diff options
114 files changed, 4936 insertions, 1715 deletions
diff --git a/.editorconfig b/.editorconfig index b117784d13..bb6a1423ef 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,6 +3,7 @@ root = true [*] indent_style = space indent_size = 2 +tab_width = 8 end_of_line = lf insert_final_newline = true charset = utf-8 diff --git a/.travis.yml b/.travis.yml index c704d29f10..e6b9516b6e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -127,6 +127,7 @@ addons: branches: only: - master + - /^release-\d+\.\d+$/ cache: apt: true diff --git a/CMakeLists.txt b/CMakeLists.txt index 629f70dc6e..8272a1a469 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,14 +111,14 @@ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY # If not in a git repo (e.g., a tarball) these tokens define the complete # version string, else they are combined with the result of `git describe`. set(NVIM_VERSION_MAJOR 0) -set(NVIM_VERSION_MINOR 3) -set(NVIM_VERSION_PATCH 2) -set(NVIM_VERSION_PRERELEASE "") # for package maintainers +set(NVIM_VERSION_MINOR 4) +set(NVIM_VERSION_PATCH 0) +set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers # API level -set(NVIM_API_LEVEL 5) # Bump this after any API change. +set(NVIM_API_LEVEL 6) # Bump this after any API change. set(NVIM_API_LEVEL_COMPAT 0) # Adjust this after a _breaking_ API change. -set(NVIM_API_PRERELEASE false) +set(NVIM_API_PRERELEASE true) file(TO_CMAKE_PATH ${CMAKE_CURRENT_LIST_DIR}/.git FORCED_GIT_DIR) include(GetGitRevisionDescription) @@ -174,7 +174,7 @@ endif() # Enable -Wconversion. if(NOT MSVC) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wconversion") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wshadow -Wconversion") endif() # gcc 4.0+ sets _FORTIFY_SOURCE=2 automatically. This currently @@ -248,6 +248,16 @@ int main(void) } " HAVE_EXECINFO_BACKTRACE) +check_c_source_compiles(" +int main(void) +{ + int a; + __builtin_add_overflow(a, a, &a); + __builtin_sub_overflow(a, a, &a); + return 0; +} +" HAVE_BUILTIN_ADD_OVERFLOW) + if(MSVC) # XXX: /W4 gives too many warnings. #3241 add_definitions(/W3 -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1b73c60773..b7392eaf0c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,13 +8,13 @@ low-risk/isolated tasks: - [Merge a Vim patch]. - Try a [complexity:low] issue. -- Fix bugs found by [clang scan-build](#clang-scan-build), - [coverity](#coverity), and [PVS](#pvs-studio). +- Fix bugs found by [Clang](#clang-scan-build), [PVS](#pvs-studio) or + [Coverity](#coverity). Developer guidelines -------------------- -- Nvim contributors should read `:help dev` (especially `:help dev-api`). +- Nvim contributors should read `:help dev`. - External UI developers should read `:help dev-ui`. - API client developers should read `:help dev-api-client`. - Nvim developers are _strongly encouraged_ to install `ninja` for faster builds. @@ -27,14 +27,14 @@ Developer guidelines Reporting problems ------------------ -- Check the [**FAQ**][wiki-faq]. -- Search [existing issues][github-issues] (including closed!) +- [Check the FAQ][wiki-faq]. +- [Search existing issues][github-issues] (including closed!) - Update Neovim to the latest version to see if your problem persists. - Disable plugins incrementally, to narrow down the cause of the issue. - When reporting a crash, [include a stacktrace](https://github.com/neovim/neovim/wiki/Development-tips#backtrace-linux). - [Bisect][git-bisect] to the cause of a regression, if you are able. This is _extremely_ helpful. - Check `$NVIM_LOG_FILE`, if it exists. -- Include `cmake --system-information` for **build** issues. +- Include `cmake --system-information` for build-related issues. Pull requests ("PRs") --------------------- @@ -86,14 +86,14 @@ the VCS/git logs more valuable. - Try to keep the first line under 72 characters. - **Prefix the commit subject with a _scope_:** `doc:`, `test:`, `foo.c:`, `runtime:`, ... - - For commits that contain only style/lint changes, a single-word subject - line is preferred: `style` or `lint`. + - Subject line for commits with only style/lint changes can be a single + word: `style` or `lint`. - A blank line must separate the subject from the description. - Use the _imperative voice_: "Fix bug" rather than "Fixed bug" or "Fixes bug." ### Automated builds (CI) -Each pull request must pass the automated builds on [travis CI], [quickbuild] +Each pull request must pass the automated builds on [Travis CI], [QuickBuild] and [AppVeyor]. - CI builds are compiled with [`-Werror`][gcc-warnings], so compiler warnings @@ -108,8 +108,6 @@ and [AppVeyor]. - The `lint` build ([#3174][3174]) checks modified lines _and their immediate neighbors_. This is to encourage incrementally updating the legacy style to meet our style guidelines. - - A single word (`lint` or `style`) is sufficient as the subject line of - a commit that contains only style changes. - [How to investigate QuickBuild failures](https://github.com/neovim/neovim/pull/4718#issuecomment-217631350) QuickBuild uses this invocation: @@ -123,29 +121,48 @@ QuickBuild uses this invocation: ### Clang scan-build -The auto-generated [clang-scan] report presents walk-throughs of bugs found by -Clang's [scan-build](https://clang-analyzer.llvm.org/scan-build.html) static -analyzer. To verify a fix locally, run `scan-build` like this: +View the [Clang report] to see potential bugs found by the Clang +[scan-build](https://clang-analyzer.llvm.org/scan-build.html) analyzer. - rm -rf build/ - scan-build --use-analyzer=/usr/bin/clang make - -### Coverity +- Search the Neovim commit history to find examples: + ``` + git log --oneline --no-merges --grep clang + ``` +- To verify a fix locally, run `scan-build` like this: + ``` + rm -rf build/ + scan-build --use-analyzer=/usr/bin/clang make + ``` -[Coverity](https://scan.coverity.com/projects/neovim-neovim) runs against the -master build. To view the defects, just request access; you will be approved. +### PVS-Studio -Use this commit-message format for coverity fixes: +View the [PVS report](https://neovim.io/doc/reports/pvs/PVS-studio.html.d/) to +see potential bugs found by [PVS Studio](https://www.viva64.com/en/pvs-studio/). - coverity/<id>: <description of what fixed the defect> +- Use this format for commit messages (where `{id}` is the PVS warning-id)): + ``` + PVS/V{id}: {description} + ``` +- Search the Neovim commit history to find examples: + ``` + git log --oneline --no-merges --grep PVS + ``` +- Try `./scripts/pvscheck.sh` to run PVS locally. -where `<id>` is the Coverity ID (CID). For example see [#804](https://github.com/neovim/neovim/pull/804). +### Coverity -### PVS-Studio +[Coverity](https://scan.coverity.com/projects/neovim-neovim) runs against the +master build. To view the defects, just request access; you will be approved. -View the [PVS analysis report](https://neovim.io/doc/reports/pvs/) to see bugs -found by [PVS Studio](https://www.viva64.com/en/pvs-studio/). -You can run `scripts/pvscheck.sh` locally to run PVS on your machine. +- Use this format for commit messages (where `{id}` is the CID (Coverity ID); + ([example](https://github.com/neovim/neovim/pull/804))): + ``` + coverity/{id}: {description} + ``` +- Search the Neovim commit history to find examples: + ``` + git log --oneline --no-merges --grep coverity + ``` Reviewing --------- @@ -179,9 +196,9 @@ as context, use the `-W` argument as well. [wiki-faq]: https://github.com/neovim/neovim/wiki/FAQ [review-checklist]: https://github.com/neovim/neovim/wiki/Code-review-checklist [3174]: https://github.com/neovim/neovim/issues/3174 -[travis CI]: https://travis-ci.org/neovim/neovim -[quickbuild]: http://neovim-qb.szakmeister.net/dashboard +[Travis CI]: https://travis-ci.org/neovim/neovim +[QuickBuild]: http://neovim-qb.szakmeister.net/dashboard [AppVeyor]: https://ci.appveyor.com/project/neovim/neovim [Merge a Vim patch]: https://github.com/neovim/neovim/wiki/Merging-patches-from-upstream-Vim -[clang-scan]: https://neovim.io/doc/reports/clang/ +[Clang report]: https://neovim.io/doc/reports/clang/ [complexity:low]: https://github.com/neovim/neovim/issues?q=is%3Aopen+is%3Aissue+label%3Acomplexity%3Alow @@ -101,7 +101,7 @@ else endif helptags: | nvim - +$(BUILD_CMD) -C build helptags + +$(BUILD_CMD) -C build runtime/doc/tags # Builds help HTML _and_ checks for invalid help tags. helphtml: | nvim helptags diff --git a/cmake/GenerateHelptags.cmake.in b/cmake/GenerateHelptags.cmake.in deleted file mode 100644 index db6f15b1be..0000000000 --- a/cmake/GenerateHelptags.cmake.in +++ /dev/null @@ -1,39 +0,0 @@ -if(DEFINED ENV{DESTDIR}) - file(TO_CMAKE_PATH - $ENV{DESTDIR}/@CMAKE_INSTALL_FULL_DATADIR@/nvim/runtime/doc - HELPTAGS_WORKING_DIRECTORY) -else() - file(TO_CMAKE_PATH - @CMAKE_INSTALL_FULL_DATADIR@/nvim/runtime/doc - HELPTAGS_WORKING_DIRECTORY) -endif() - -message(STATUS "Generating helptags in ${HELPTAGS_WORKING_DIRECTORY}") -if(EXISTS ${HELPTAGS_WORKING_DIRECTORY}/) - message(STATUS "${HELPTAGS_WORKING_DIRECTORY} already exists") - # If the doc directory already exists, helptags could fail due to duplicate - # tags. Tell the user to remove the directory and try again. - set(TROUBLESHOOTING "\nRemove \"${HELPTAGS_WORKING_DIRECTORY}\" and try again.") -endif() - -# Workaround for hanging "yes | nvim -c 'helptags ++t .'" -# and therefore hanging "yes | make install": -# Set INPUT_FILE to an empty file, causing execute_process -# to disregard other standard input (such as "yes |"). -set(EMPTY_FILE ${CMAKE_CURRENT_BINARY_DIR}/.GenerateHelptags) -file(WRITE ${EMPTY_FILE} "") -execute_process( - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/bin/nvim - -u NONE - -es - -c "helptags ++t ." - -c quit - WORKING_DIRECTORY ${HELPTAGS_WORKING_DIRECTORY} - INPUT_FILE ${EMPTY_FILE} - OUTPUT_VARIABLE err - ERROR_VARIABLE err - RESULT_VARIABLE res) - -if(NOT res EQUAL 0) - message(FATAL_ERROR "Generating helptags failed: ${err} - ${res}${TROUBLESHOOTING}") -endif() diff --git a/man/nvim.1 b/man/nvim.1 index b9f47cc851..4d338321c4 100644 --- a/man/nvim.1 +++ b/man/nvim.1 @@ -56,6 +56,7 @@ Reads text from standard input until .Dv EOF , then opens a buffer with that text. User input is read from standard error, which should be a terminal. +.El .Sh OPTIONS .Bl -tag -width Fl .It Fl t Ar tag diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index ad706af087..d66e04f5f2 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -29,21 +29,16 @@ foreach(PACKAGE ${PACKAGES}) file(GLOB "${PACKNAME}_DOC_FILES" ${PACKAGE}/doc/*.txt) if(${PACKNAME}_DOC_FILES) file(MAKE_DIRECTORY ${GENERATED_PACKAGE_DIR}/${PACKNAME}) - add_custom_target("${PACKNAME}-tags" + add_custom_command(OUTPUT "${GENERATED_PACKAGE_DIR}/${PACKNAME}/doc/tags" COMMAND ${CMAKE_COMMAND} -E copy_directory ${PACKAGE} ${GENERATED_PACKAGE_DIR}/${PACKNAME} COMMAND "${PROJECT_BINARY_DIR}/bin/nvim" -u NONE -i NONE -e --headless -c "helptags doc" -c quit DEPENDS nvim + nvim_runtime_deps WORKING_DIRECTORY "${GENERATED_PACKAGE_DIR}/${PACKNAME}" ) - add_dependencies(${PACKNAME}-tags nvim_runtime_deps) - - add_custom_command(OUTPUT "${GENERATED_PACKAGE_DIR}/${PACKNAME}/doc/tags" - DEPENDS - "${PACKNAME}-tags" - ) set("${PACKNAME}_DOC_NAMES") foreach(DF "${${PACKNAME}_DOC_FILES}") @@ -67,7 +62,7 @@ foreach(DF ${DOCFILES}) list(APPEND BUILDDOCFILES ${GENERATED_RUNTIME_DIR}/doc/${F}) endforeach() -add_custom_target(helptags +add_custom_command(OUTPUT ${GENERATED_HELP_TAGS} COMMAND ${CMAKE_COMMAND} -E remove doc/* COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/runtime/doc doc @@ -75,19 +70,15 @@ add_custom_target(helptags -u NONE -i NONE -e --headless -c "helptags ++t doc" -c quit DEPENDS nvim + nvim_runtime_deps WORKING_DIRECTORY "${GENERATED_RUNTIME_DIR}" ) -add_dependencies(helptags nvim_runtime_deps) -add_custom_command(OUTPUT ${GENERATED_HELP_TAGS} - DEPENDS - helptags -) add_custom_target(doc_html COMMAND make html DEPENDS - helptags + ${GENERATED_HELP_TAGS} WORKING_DIRECTORY "${GENERATED_RUNTIME_DIR}/doc" ) diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim index fd32e03666..2fb9d74d8d 100644 --- a/runtime/autoload/provider/clipboard.vim +++ b/runtime/autoload/provider/clipboard.vim @@ -65,7 +65,7 @@ function! provider#clipboard#Executable() abort let s:paste = get(g:clipboard, 'paste', { '+': v:null, '*': v:null }) let s:cache_enabled = get(g:clipboard, 'cache_enabled', 0) return get(g:clipboard, 'name', 'g:clipboard') - elseif has('mac') && executable('pbpaste') && s:cmd_ok('pbpaste') + elseif has('mac') let s:copy['+'] = 'pbcopy' let s:paste['+'] = 'pbpaste' let s:copy['*'] = s:copy['+'] diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index e9579d5c86..425dcede8b 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2209,6 +2209,7 @@ printf({fmt}, {expr1}...) String format text pumvisible() Number whether popup menu is visible pyeval({expr}) any evaluate |Python| expression py3eval({expr}) any evaluate |python3| expression +pyxeval({expr}) any evaluate |python_x| expression range({expr} [, {max} [, {stride}]]) List items from {expr} to {max} readfile({fname} [, {binary} [, {max}]]) @@ -5740,29 +5741,35 @@ mode([expr]) Return a string that indicates the current mode. a non-empty String (|non-zero-arg|), then the full mode is returned, otherwise only the first letter is returned. - n Normal - no Operator-pending - v Visual by character - V Visual by line - CTRL-V Visual blockwise - s Select by character - S Select by line - CTRL-S Select blockwise - i Insert - ic Insert mode completion |compl-generic| - ix Insert mode |i_CTRL-X| completion - R Replace |R| - Rc Replace mode completion |compl-generic| - Rv Virtual Replace |gR| - Rx Replace mode |i_CTRL-X| completion - c Command-line editing - cv Vim Ex mode |gQ| - ce Normal Ex mode |Q| - r Hit-enter prompt - rm The -- more -- prompt - r? A |:confirm| query of some sort - ! Shell or external command is executing - t Terminal mode: keys go to the job + n Normal + no Operator-pending + nov Operator-pending (forced characterwise |o_v|) + noV Operator-pending (forced linewise |o_V|) + noCTRL-V Operator-pending (forced blockwise |o_CTRL-V|) + niI Normal using |i_CTRL-O| in |Insert-mode| + niR Normal using |i_CTRL-O| in |Replace-mode| + niV Normal using |i_CTRL-O| in |Virtual-Replace-mode| + v Visual by character + V Visual by line + CTRL-V Visual blockwise + s Select by character + S Select by line + CTRL-S Select blockwise + i Insert + ic Insert mode completion |compl-generic| + ix Insert mode |i_CTRL-X| completion + R Replace |R| + Rc Replace mode completion |compl-generic| + Rv Virtual Replace |gR| + Rx Replace mode |i_CTRL-X| completion + c Command-line editing + cv Vim Ex mode |gQ| + ce Normal Ex mode |Q| + r Hit-enter prompt + rm The -- more -- prompt + r? |:confirm| query of some sort + ! Shell or external command is executing + t Terminal mode: keys go to the job This is useful in the 'statusline' option or when used with |remote_expr()| In most other places it always returns "c" or "n". @@ -6146,6 +6153,12 @@ pyeval({expr}) *pyeval()* non-string keys result in error. {only available when compiled with the |+python| feature} +pyxeval({expr}) *pyxeval()* + Evaluate Python expression {expr} and return its result + converted to Vim data structures. + Uses Python 2 or 3, see |python_x| and 'pyxversion'. + See also: |pyeval()|, |py3eval()| + *E726* *E727* range({expr} [, {max} [, {stride}]]) *range()* Returns a |List| with Numbers: @@ -8517,6 +8530,7 @@ printer Compiled with |:hardcopy| support. profile Compiled with |:profile| support. python Legacy Vim Python 2.x API is available. |has-python| python3 Legacy Vim Python 3.x API is available. |has-python| +pythonx Compiled with |python_x| interface. |has-pythonx| quickfix Compiled with |quickfix| support. reltime Compiled with |reltime()| support. rightleft Compiled with 'rightleft' support. diff --git a/runtime/doc/if_pyth.txt b/runtime/doc/if_pyth.txt index 2da2e5147b..df4b54ef76 100644 --- a/runtime/doc/if_pyth.txt +++ b/runtime/doc/if_pyth.txt @@ -692,6 +692,7 @@ vim.Function object *python-Function* 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* @@ -723,4 +724,57 @@ You can test what Python version is available with: > endif ============================================================================== +10. 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 +same as the Python 2 and 3 variants, but select the Python version using the +'pyxversion' setting. + +Set 'pyxversion' in your |vimrc| to prefer Python 2 or Python 3 for Python +commands. Changing this setting at runtime risks losing the state of plugins +(such as initialization). + +If you want to use a module, you can put it in the {rtp}/pythonx directory. +See |pythonx-directory|. + + *:pyx* *:pythonx* +`:pyx` and `:pythonx` work similar to `:python`. To check if `:pyx` works: > + :pyx print("Hello") + +To see what version of Python is being used: > + :pyx import sys + :pyx print(sys.version) +< + *:pyxfile* *python_x-special-comments* +`:pyxfile` works similar to `:pyfile`. But you can add a "shebang" comment to +force Vim to use `:pyfile` or `:py3file`: > + #!/any string/python2 " Shebang. Must be the first line of the file. + #!/any string/python3 " Shebang. Must be the first line of the file. + # requires python 2.x " Maximum lines depend on 'modelines'. + # requires python 3.x " Maximum lines depend on 'modelines'. +Unlike normal modelines, the bottom of the file is not checked. +If none of them are found, the 'pyxversion' option is used. + *W20* *W21* +If Vim does not support the selected Python version a silent message will be +printed. Use `:messages` to read them. + + *:pyxdo* +`:pyxdo` works similar to `:pydo`. + + *has-pythonx* +To check if pyx* functions and commands are available: > + if has('pythonx') + echo 'pyx* commands are available. (Python ' . &pyx . ')' + endif + +If you prefer Python 2 and want to fallback to Python 3, set 'pyxversion' +explicitly in your |.vimrc|. Example: > + if has('python') + set pyx=2 + elseif has('python3') + set pyx=3 + endif + +============================================================================== vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index 7024a57333..63ded3152f 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1418,6 +1418,10 @@ tag command action ~ |:python| :py[thon] execute Python command |:pydo| :pyd[o] execute Python command for each line |:pyfile| :pyf[ile] execute Python script file +|:pyx| :pyx execute |python_x| command +|:pythonx| :pythonx same as :pyx +|:pyxdo| :pyxd[o] execute |python_x| command for each line +|:pyxfile| :pyxf[ile] execute |python_x| script file |:quit| :q[uit] quit current window (when one window quit Vim) |:quitall| :quita[ll] quit Vim |:qall| :qa[ll] quit Vim diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 534b2025cd..bcefa1f56b 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -4471,6 +4471,30 @@ 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|. + *'pyxversion'* *'pyx'* +'pyxversion' 'pyx' number (default depends on the build) + global + Specifies the python version used for pyx* functions and commands + |python_x|. The default value is as follows: + + |provider| installed Default ~ + |+python| and |+python3| 0 + only |+python| 2 + only |+python3| 3 + + Available values are 0, 2 and 3. + If 'pyxversion' is 0, it is set to 2 or 3 after the first execution of + any python2/3 commands or functions. E.g. `:py` sets to 2, and `:py3` + sets to 3. `:pyx` sets it to 3 if Python 3 is available, otherwise sets + to 2 if Python 2 is available. + See also: |has-pythonx| + + If only |+python| or |+python3| are available, + 'pyxversion' has no effect. The pyx* functions and commands are + always the same as the installed version. + + This option cannot be set from a |modeline| or in the |sandbox|, for + security reasons. *'quoteescape'* *'qe'* 'quoteescape' 'qe' string (default "\") diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt index cf0efd7a75..98a5244345 100644 --- a/runtime/doc/quickref.txt +++ b/runtime/doc/quickref.txt @@ -813,6 +813,7 @@ Short explanation of each option: *option-list* 'pumwidth' 'pw' minimum width of the popup menu 'pythondll' name of the Python 2 dynamic library 'pythonthreedll' name of the Python 3 dynamic library +'pyxversion' 'pyx' Python version used for pyx* commands 'quoteescape' 'qe' escape characters used in a string 'readonly' 'ro' disallow writing the buffer 'redrawtime' 'rdt' timeout for 'hlsearch' and |:match| highlighting diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index c021f236c8..9acd8b2c5a 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -12,9 +12,10 @@ Nvim UI protocol *ui* UI Events *ui-events* GUIs can be implemented as external processes communicating with Nvim over the -RPC API. The UI model consists of a terminal-like grid with a single, -monospace font size. Some elements (UI "widgets") can be drawn separately from -the grid ("externalized"). +RPC API. The default UI model consists of a terminal-like grid with a single, +monospace font size. The UI can opt-in to have windows drawn on separate +grids, as well as to have some elements (UI "widgets") be drawn by the UI +itself rather than by nvim ("externalized"). *ui-options* @@ -32,6 +33,7 @@ a dictionary with these (optional) keys: `ext_cmdline` Externalize the cmdline. |ui-cmdline| `ext_wildmenu` Externalize the wildmenu. |ui-wildmenu| `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| Specifying a non-existent option is an error. UIs can check the |api-metadata| @@ -46,7 +48,7 @@ Each update event is itself an array whose first element is the event name and remaining elements are event-parameter tuples. This allows multiple events of the same kind to be sent in a row without the event name being repeated. This batching is mostly used for "grid_line", because each "grid_line" event puts -contents in one screen line, but clients must be prepared for multiple argument +contents in one grid line, but clients must be prepared for multiple argument sets being batched for all event kinds. Events must be handled in-order. A "flush" event is sent when nvim is done @@ -61,10 +63,12 @@ By default, Nvim sends |ui-global| and |ui-grid-old| events; these suffice to implement a terminal-like interface. However there are two revisions of the grid part of the protocol. The newer revision |ui-linegrid|, enabled by `ext_linegrid` option, has a more effecient representation of text (especially -highlighted text), and room for futher enhancements that will use -multiple grids. The older revision is available and used by default only for -backwards compatibility reasons. New UIs are strongly recommended to use -|ui-linegrid|, as further protocol extensions will require it. +highlighted text), and allows extensions that use multiple grids. + +The older revision is available and used by default only for backwards +compatibility reasons. New UIs are strongly recommended to use |ui-linegrid|, +as further protocol extensions require it. The |ui-multigrid| extension +enables |ui-linegrid| implicitly. Nvim optionally sends screen elements "semantically" as structured events instead of raw grid-lines, controlled by |ui-ext-options|. The UI must present @@ -220,7 +224,8 @@ Most of these events take a `grid` index as first parameter. Grid 1 is the global grid used by default for the entire editor screen state. Grids other than that will be defined by future extensions. Just activating the `ext_linegrid` option by itself will never cause any additional grids to be -created. +created. To enable per-window grids, `ext_multigrid` option should be set (see +|ui-multigrid|). Highlight attribute groups are predefined. UIs should maintain a table to map numerical highlight `id`:s to the actual attributes. @@ -476,18 +481,65 @@ highlight group is cleared, so `ui_name` can always be used to reliably identify screen elements, even if no attributes have been applied. ============================================================================== +Multigrid Events *ui-multigrid* + +Only sent if `ext_multigrid` option is set in |ui-options|. Enables the +`ext_linegrid` extension implicitly. + +The multigrid extension gives the UIs more control over how windows are +displayed. The UIs receive updates on a separate grid for each window. The UIs +can set the grid size independently of how much space the window occupies on +the global layout. This enables the UIs to set a different font size for each +window if the UI so desires. The UI can also reserve space around the border +of the window for its own elements, for instance scrollbars from the UI +toolkit. + +By default, the grid size is handled by nvim and set to the outer grid size +(i.e. the size of the window frame in nvim) whenever the split is created. +Once a UI sets a grid size, nvim does not handle the size for that grid and +the UI must change the grid size whenever the outer size is changed. To +delegate the handling of grid size back to nvim, the UIs should request the +size (0, 0). + +A window can be hidden and redisplayed without its grid being deallocated. +This can happen multiple times for the same window, for instance when switching +tabs. + +["win_pos", grid, win, start_row, start_col, width, height] + Set the position and size of the grid in nvim (i.e. the outer grid + size). If the window was previously hidden, it should now be shown + again. + +["win_hide", grid] + Stop displaying the window. + +["win_scroll_over_start"] + Hint that following `grid_scroll` on the default grid should + scroll over windows. This is a temporary workaround to allow + UIs to use the builtin message drawing. Later on, messages will be + drawn on a dedicated grid. + +["win_scroll_over_reset"] + Hint that scrolled over windows should be redrawn again, and not be + overdrawn by default grid scrolling anymore. + +See |ui-linegrid| for grid events. +See |nvim_ui_try_resize_grid| in |api-ui| to request changing the grid size. + +============================================================================== Popupmenu Events *ui-popupmenu* Only sent if `ext_popupmenu` option is set in |ui-options| -["popupmenu_show", items, selected, row, col] +["popupmenu_show", items, selected, row, col, grid] Show |popupmenu-completion|. `items` is an array of completion items to show; each item is an array of the form [word, kind, menu, info] as defined at |complete-items|, except that `word` is replaced by `abbr` if present. `selected` is the initially-selected item, a zero-based index into the array of items (-1 if no item is selected). `row` and `col` give the anchor position, where the first character of the - completed word will be. + completed word will be. When |ui-multigrid| is used, `grid` is the + grid for the anchor position. ["popupmenu_select", selected] Select an item in the current popupmenu. `selected` is a zero-based diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 3915763eed..db856ceb65 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -357,6 +357,9 @@ TUI: and has a 'ttybuiltin' setting to control how that combination works. Nvim uses one or the other, it does not attempt to merge the two. +UI/Display: + |Visual| selection highlights the character at cursor. |visual-use| + VimL (Vim script) compatibility: `count` does not alias to |v:count| `errmsg` does not alias to |v:errmsg| diff --git a/runtime/optwin.vim b/runtime/optwin.vim index 1742c64c3e..2e9f4723dd 100644 --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -905,7 +905,7 @@ if has("folding") call append("$", "foldmarker\tmarkers used when 'foldmethod' is \"marker\"") call append("$", "\t(local to window)") call <SID>OptionL("fmr") - call append("$", "foldnestmax\tmaximum fold depth for when 'foldmethod is \"indent\" or \"syntax\"") + call append("$", "foldnestmax\tmaximum fold depth for when 'foldmethod' is \"indent\" or \"syntax\"") call append("$", "\t(local to window)") call <SID>OptionL("fdn") endif @@ -1295,6 +1295,10 @@ if exists("&mzschemedll") call append("$", "mzschemegcdll\tname of the Tcl GC dynamic library") call <SID>OptionG("mzschemegcdll", &mzschemegcdll) endif +if has('pythonx') + call append("$", "pyxversion\twhether to use Python 2 or 3") + call append("$", " \tset pyx=" . &wd) +endif " Install autocommands to enable mappings in option-window noremap <silent> <buffer> <CR> <C-\><C-N>:call <SID>CR()<CR> diff --git a/scripts/git-log-pretty-since.sh b/scripts/git-log-pretty-since.sh index 6281127542..a0aa4354b6 100755 --- a/scripts/git-log-pretty-since.sh +++ b/scripts/git-log-pretty-since.sh @@ -1,6 +1,8 @@ #!/usr/bin/env bash -# Shows a log with changes grouped next to their merge-commit. +# Prints a nicely-formatted commit history. +# - Commits are grouped below their merge-commit. +# - Issue numbers are moved next to the commit-id. # # Parameters: # $1 "since" commit @@ -19,14 +21,32 @@ is_merge_commit() { git log $1^2 >/dev/null 2>&1 && return 0 || return 1 } +# Removes parens from issue/ticket/PR numbers. +# +# Example: +# in: 3340e08becbf foo (#9423) +# out: 3340e08becbf foo #9423 +_deparen() { + sed 's/(\(\#[0-9]\{3,\}\))/\1/g' +} + +# Cleans up issue/ticket/PR numbers in the commit descriptions. +# +# Example: +# in: 3340e08becbf foo (#9423) +# out: 3340e08becbf #9423 foo +_format_ticketnums() { + nvim -Es +'g/\v(#[0-9]{3,})/norm! ngEldE0ep' +'%p' | _deparen +} + for commit in $(git log --format='%H' --first-parent "$__SINCE"..HEAD); do if is_merge_commit ${commit} ; then if [ -z "$__INVMATCH" ] || ! git log --oneline ${commit}^1..${commit}^2 \ - | grep -E "$__INVMATCH" >/dev/null 2>&1 ; then + | >/dev/null 2>&1 grep -E "$__INVMATCH" ; then git log -1 --oneline ${commit} git log --format=' %h %s' ${commit}^1..${commit}^2 fi else git log -1 --oneline ${commit} fi -done +done | _format_ticketnums diff --git a/scripts/pvscheck.sh b/scripts/pvscheck.sh index 11b672c515..bf523b023a 100755 --- a/scripts/pvscheck.sh +++ b/scripts/pvscheck.sh @@ -8,6 +8,10 @@ set -e # arguments provided. test -z "$POSH_VERSION" && set -u +log_info() { + >&2 printf "pvscheck.sh: %s\n" "$@" +} + get_jobs_num() { if [ -n "${TRAVIS:-}" ] ; then # HACK: /proc/cpuinfo on Travis CI is misleading, so hardcode 1. @@ -261,6 +265,11 @@ install_pvs() {( cd "$tgt" + if test -d pvs-studio ; then + log_info 'install_pvs: "pvs-studio" directory already exists, skipping install' + return 0 + fi + mkdir pvs-studio cd pvs-studio diff --git a/scripts/release.sh b/scripts/release.sh index 66ffd3e6fe..1645a10452 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -67,7 +67,7 @@ _do_release_commit() { if ! test "$ARG1" = '--use-current-commit' ; then echo "Building changelog since ${__LAST_TAG}..." - __CHANGELOG="$(./scripts/git-log-pretty-since.sh "$__LAST_TAG" 'vim-patch:\S')" + __CHANGELOG="$(./scripts/git-log-pretty-since.sh "$__LAST_TAG" 'vim-patch:[^[:space:]]')" git add CMakeLists.txt git commit --edit -m "${__RELEASE_MSG} ${__CHANGELOG}" diff --git a/scripts/windows.ti b/scripts/windows.ti index da643350db..4f4832b786 100644 --- a/scripts/windows.ti +++ b/scripts/windows.ti @@ -27,7 +27,7 @@ win32con|ANSI emulation for libuv on legacy console, conemu|ANIS X3.64 and Xterm 256 colors for ConEmu with libuv, ccc@, mc5i@, xenl@, acsc@, rmacs@, smacs@, blink@, cbt@, - cvvis@, cnorm=\E[?25h, dim@, flash@, hts@, initc@, invis@, is2@, + cvvis@, cnorm=\E[?25h, cud1=\E[B, dim@, flash@, hts@, initc@, invis@, is2@, kf46@, kf47@, kf48@, kf49@, kf50@, kf51@, kf52@, kf53@, kf54@, kf55@, kf56@, kf57@, kf58@, kf59@, kf60@, kf61@, kf62@, kf63@, kmous@, mc0@, mc4@, mc5@, meml@, memu@, oc@, rmam@, rmcup=\E[?1049l, @@ -49,7 +49,7 @@ conemu|ANIS X3.64 and Xterm 256 colors for ConEmu with libuv, vtpcon|ANIS emulation for console virtual terminal sequence with libuv, ccc@, mc5i@, xenl@, blink@, acsc=jjkkllmmnnqqttuuvvwwxx, - cvvis@, dim@, flash@, + cvvis@, cud1=\E[B, dim@, flash@, initc=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E, invis@, is2=\E[!p\E[?3l, kf46@, kf47@, kf48@, kf49@, kf50@, kf51@, kf52@, kf53@, kf54@, diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index 390b7e8363..feca140547 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -6,6 +6,7 @@ #include <string.h> #include "nvim/func_attr.h" +#include "nvim/types.h" #define ARRAY_DICT_INIT {.size = 0, .capacity = 0, .items = NULL} #define STRING_INIT {.data = NULL, .size = 0} @@ -20,8 +21,6 @@ # define DictionaryOf(...) Dictionary #endif -typedef int handle_T; - // Basic types typedef enum { kErrorTypeNone = -1, diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 01f8c9f71c..7ba5251c60 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -126,7 +126,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, } } - if (ui->ui_ext[kUIHlState]) { + if (ui->ui_ext[kUIHlState] || ui->ui_ext[kUIMultigrid]) { ui->ui_ext[kUILinegrid] = true; } @@ -245,6 +245,28 @@ 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. +/// +/// On invalid grid handle, fails with error. +/// +/// @param grid The handle of the grid to be changed. +/// @param width The new requested width. +/// @param height The new requested height. +void nvim_ui_try_resize_grid(uint64_t channel_id, Integer grid, Integer width, + Integer height, Error *error) + FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY +{ + if (!pmap_has(uint64_t)(connected_uis, channel_id)) { + api_set_error(error, kErrorTypeException, + "UI not attached to channel: %" PRId64, channel_id); + return; + } + + ui_grid_resize((handle_T)grid, (int)width, (int)height, error); +} + /// Pushes data into UI.UIData, to be consumed later by remote_ui_flush(). static void push_call(UI *ui, const char *name, Array args) { diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index 10331fd5c2..59a7780651 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -81,8 +81,21 @@ void grid_line(Integer grid, Integer row, Integer col_start, Array data) 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; +void grid_destroy(Integer grid) + FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; -void popupmenu_show(Array items, Integer selected, Integer row, Integer col) +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; +void win_scroll_over_reset(void) + FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; + +void popupmenu_show(Array items, Integer selected, + Integer row, Integer col, Integer grid) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; void popupmenu_hide(void) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index ecfff1ea8f..24e76ecf88 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -28,6 +28,7 @@ #include "nvim/screen.h" #include "nvim/memory.h" #include "nvim/message.h" +#include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/option.h" @@ -75,7 +76,6 @@ void nvim_command(String command, Error *err) { try_start(); do_cmdline_cmd(command.data); - update_screen(VALID); try_end(err); } @@ -1916,17 +1916,46 @@ Object nvim_get_proc(Integer pid, Error *err) return rvobj; } +/// 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. +/// +/// @param item Index of the item to select, starting with zero. Pass in "-1" +/// to select no item (restore 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`. +void nvim_select_popupmenu_item(Integer item, Boolean insert, Boolean finish, + Dictionary opts, Error *err) + FUNC_API_SINCE(6) +{ + if (opts.size > 0) { + api_set_error(err, kErrorTypeValidation, "opts dict isn't empty"); + return; + } + + if (finish) { + insert = true; + } + + pum_ext_select_item((int)item, insert, finish); +} + /// NB: if your UI doesn't use hlstate, this will not return hlstate first time Array nvim__inspect_cell(Integer row, Integer col, Error *err) { Array ret = ARRAY_DICT_INIT; - if (row < 0 || row >= screen_Rows - || col < 0 || col >= screen_Columns) { + if (row < 0 || row >= default_grid.Rows + || col < 0 || col >= default_grid.Columns) { return ret; } - size_t off = LineOffset[(size_t)row] + (size_t)col; - ADD(ret, STRING_OBJ(cstr_to_string((char *)ScreenLines[off]))); - int attr = ScreenAttrs[off]; + size_t off = default_grid.line_offset[(size_t)row] + (size_t)col; + ADD(ret, STRING_OBJ(cstr_to_string((char *)default_grid.chars[off]))); + int attr = default_grid.attrs[off]; ADD(ret, DICTIONARY_OBJ(hl_get_attr_by_id(attr, true, err))); // will not work first time if (!highlight_use_hlstate()) { diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index cf1d1f5e45..33857f95b7 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -135,7 +135,7 @@ void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err) // make sure cursor is in visible range even if win != curwin update_topline_win(win); - update_screen(VALID); + redraw_win_later(win, VALID); } /// Gets the window height diff --git a/src/nvim/assert.h b/src/nvim/assert.h index 761636305e..29195a49dc 100644 --- a/src/nvim/assert.h +++ b/src/nvim/assert.h @@ -121,4 +121,34 @@ ((enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(!!(e)) }) 0) #endif +/// @def STRICT_ADD +/// @brief Adds (a + b) and stores result in `c`. Aborts on overflow. +/// +/// Requires GCC 5+ and Clang 3.8+ +/// https://clang.llvm.org/docs/LanguageExtensions.html +/// https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html +/// +/// Alternative for compilers without __builtin_xx_overflow ? +/// https://stackoverflow.com/a/44830670/152142 +/// +/// @param MAX Maximum value of the narrowest type of operand. +/// Not used if compiler supports __builtin_add_overflow. +#if HAVE_BUILTIN_ADD_OVERFLOW +# define STRICT_ADD(a, b, c, t) \ + do { if (__builtin_add_overflow(a, b, c)) { abort(); } } while (0) +#else +# define STRICT_ADD(a, b, c, t) \ + 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 +# define STRICT_SUB(a, b, c, t) \ + do { if (__builtin_sub_overflow(a, b, c)) { abort(); } } while (0) +#else +# define STRICT_SUB(a, b, c, t) \ + do { *(c) = (t)(a - b); } while (0) +#endif + #endif // NVIM_ASSERT_H diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 8b107041b1..22c8a2bf05 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -5347,8 +5347,7 @@ int bufhl_add_hl(buf_T *buf, hlentry->stop = col_end; if (0 < lnum && lnum <= buf->b_ml.ml_line_count) { - changed_lines_buf(buf, lnum, lnum+1, 0); - redraw_buf_later(buf, VALID); + redraw_buf_line_later(buf, lnum); } return src_id; } @@ -5414,8 +5413,7 @@ int bufhl_add_virt_text(buf_T *buf, } if (0 < lnum && lnum <= buf->b_ml.ml_line_count) { - changed_lines_buf(buf, lnum, lnum+1, 0); - redraw_buf_later(buf, VALID); + redraw_buf_line_later(buf, lnum); } return src_id; } @@ -5440,8 +5438,6 @@ void bufhl_clear_line_range(buf_T *buf, linenr_T line_start, linenr_T line_end) { - linenr_T first_changed = MAXLNUM, last_changed = -1; - kbitr_t(bufhl) itr; BufhlLine *l, t = BUFHLLINE_INIT(line_start); if (!kb_itr_get(bufhl, &buf->b_bufhl_info, &t, &itr)) { @@ -5456,12 +5452,7 @@ void bufhl_clear_line_range(buf_T *buf, if (line_start <= line) { BufhlLineStatus status = bufhl_clear_line(l, src_id, line); if (status != kBLSUnchanged) { - if (line > last_changed) { - last_changed = line; - } - if (line < first_changed) { - first_changed = line; - } + redraw_buf_line_later(buf, line); } if (status == kBLSDeleted) { kb_del_itr(bufhl, &buf->b_bufhl_info, &itr); @@ -5469,11 +5460,6 @@ void bufhl_clear_line_range(buf_T *buf, } } } - - if (last_changed != -1) { - changed_lines_buf(buf, first_changed, last_changed+1, 0); - redraw_buf_later(buf, VALID); - } } /// Clear bufhl highlights from a given source group and given line diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index d270714611..05688790c2 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -18,6 +18,8 @@ typedef struct { // for garray_T #include "nvim/garray.h" +// for ScreenGrid +#include "nvim/grid_defs.h" // for HLF_COUNT #include "nvim/highlight_defs.h" // for pos_T, lpos_T and linenr_T @@ -1183,6 +1185,9 @@ struct window_S { int w_tagstackidx; /* idx just below active entry */ int w_tagstacklen; /* number of tags on stack */ + ScreenGrid w_grid; // the grid specific to the window + bool w_pos_changed; // true if window position changed + /* * w_fraction is the fractional row of the cursor within the window, from * 0 at the top row to FRACTION_MULT at the last row. diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 4e8bb3b0d7..c220c4e347 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -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_width != 0)) { + && (wp->w_grid.Columns != 0)) { // Count all characters from first non-blank after a blank up to next // non-blank after a blank. numberextra = win_col_off(wp); col2 = col; - colmax = (colnr_T)(wp->w_width - numberextra - col_adj); + colmax = (colnr_T)(wp->w_grid.Columns - 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_width) { - col -= wp->w_width; - numberextra = wp->w_width - (numberextra - win_col_off2(wp)); + 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 >= numberextra && numberextra > 0) { col %= numberextra; } @@ -1097,16 +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_width)) { + if (col == 0 || (col + size + sbrlen > (colnr_T)wp->w_grid.Columns)) { added = 0; if (*p_sbr != NUL) { - if (size + sbrlen + numberwidth > (colnr_T)wp->w_width) { + if (size + sbrlen + numberwidth > (colnr_T)wp->w_grid.Columns) { // Calculate effective window width. - int width = (colnr_T)wp->w_width - sbrlen - numberwidth; - int prev_width = col ? ((colnr_T)wp->w_width - (sbrlen + col)) : 0; + int width = (colnr_T)wp->w_grid.Columns - sbrlen - numberwidth; + int prev_width = col ? ((colnr_T)wp->w_grid.Columns - (sbrlen + col)) + : 0; if (width == 0) { - width = (colnr_T)wp->w_width; + width = (colnr_T)wp->w_grid.Columns; } added += ((size - prev_width) / width) * vim_strsize(p_sbr); if ((size - prev_width) % width) { @@ -1175,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_width == 0) { + if (wp->w_grid.Columns == 0) { // there is no border return false; } - width1 = wp->w_width - win_col_off(wp); + width1 = wp->w_grid.Columns - win_col_off(wp); if ((int)vcol < width1 - 1) { return false; diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 0fda941a51..409eb653a0 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -4,6 +4,7 @@ #include <stdbool.h> #include <inttypes.h> +#include "nvim/assert.h" #include "nvim/cursor.h" #include "nvim/charset.h" #include "nvim/fold.h" @@ -120,11 +121,11 @@ static int coladvance2( --curwin->w_curswant; } } else { - int width = curwin->w_width - win_col_off(curwin); + int width = curwin->w_grid.Columns - win_col_off(curwin); if (finetune && curwin->w_p_wrap - && curwin->w_width != 0 + && curwin->w_grid.Columns != 0 && wcol >= (colnr_T)width) { csize = linetabsize(line); if (csize > 0) @@ -170,7 +171,9 @@ static int coladvance2( if (line[idx] == NUL) { /* Append spaces */ int correct = wcol - col; - char_u *newline = xmallocz((size_t)(idx + correct)); + size_t newline_size; + STRICT_ADD(idx, correct, &newline_size, size_t); + char_u *newline = xmallocz(newline_size); memcpy(newline, line, (size_t)idx); memset(newline + idx, ' ', (size_t)correct); @@ -187,14 +190,17 @@ static int coladvance2( if (-correct > csize) return FAIL; - newline = xmallocz((size_t)(linelen - 1 + csize)); + size_t n; + STRICT_ADD(linelen - 1, csize, &n, size_t); + newline = xmallocz(n); // Copy first idx chars memcpy(newline, line, (size_t)idx); // Replace idx'th char with csize spaces memset(newline + idx, ' ', (size_t)csize); // Copy the rest of the line - memcpy(newline + idx + csize, line + idx + 1, - (size_t)(linelen - idx - 1)); + STRICT_SUB(linelen, idx, &n, size_t); + STRICT_SUB(n, 1, &n, size_t); + memcpy(newline + idx + csize, line + idx + 1, n); ml_replace(pos->lnum, newline, false); changed_bytes(pos->lnum, idx); @@ -223,9 +229,10 @@ static int coladvance2( } else { int b = (int)wcol - (int)col; - /* The difference between wcol and col is used to set coladd. */ - if (b > 0 && b < (MAXCOL - 2 * curwin->w_width)) + // The difference between wcol and col is used to set coladd. + if (b > 0 && b < (MAXCOL - 2 * curwin->w_grid.Columns)) { pos->coladd = b; + } col += b; } @@ -436,7 +443,7 @@ bool leftcol_changed(void) bool retval = false; changed_cline_bef_curs(); - lastcol = curwin->w_leftcol + curwin->w_width - curwin_col_off() - 1; + lastcol = curwin->w_leftcol + curwin->w_grid.Columns - curwin_col_off() - 1; validate_virtcol(); /* diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index cf79005a37..5551576c6d 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -254,6 +254,16 @@ char_u *parse_shape_opt(int what) return NULL; } +/// Returns true if the cursor is non-blinking "block" shape during +/// visual selection. +/// +/// @param exclusive If 'selection' option is "exclusive". +bool cursor_is_block_during_visual(bool exclusive) +{ + int mode_idx = exclusive ? SHAPE_IDX_VE : SHAPE_IDX_V; + return (SHAPE_BLOCK == shape_table[mode_idx].shape + && 0 == shape_table[mode_idx].blinkon); +} /// Map cursor mode from string to integer /// diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 2e0b198c13..866161e5cf 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -695,7 +695,7 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din) for (lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { len += (long)STRLEN(ml_get_buf(buf, lnum, false)) + 1; } - ptr = xmalloc(len); + ptr = try_malloc(len); if (ptr == NULL) { // Allocating memory failed. This can happen, because we try to read // the whole buffer text into memory. Set the failed flag, the diff @@ -1088,9 +1088,6 @@ static int diff_file(diffio_T *dio) const size_t len = (strlen(tmp_orig) + strlen(tmp_new) + strlen(tmp_diff) + STRLEN(p_srr) + 27); char *const cmd = xmalloc(len); - if (cmd == NULL) { - return FAIL; - } // We don't want $DIFF_OPTIONS to get in the way. if (os_getenv("DIFF_OPTIONS")) { @@ -1545,9 +1542,9 @@ static void diff_read(int idx_orig, int idx_new, diffout_T *dout) } else if ((STRNCMP(line, "@@ ", 3) == 0)) { diffstyle = DIFF_UNIFIED; } else if ((STRNCMP(line, "--- ", 4) == 0) - && (vim_fgets(linebuf, LBUFLEN, fd) == 0) + && (vim_fgets(linebuf, LBUFLEN, fd) == 0) // -V501 && (STRNCMP(line, "+++ ", 4) == 0) - && (vim_fgets(linebuf, LBUFLEN, fd) == 0) + && (vim_fgets(linebuf, LBUFLEN, fd) == 0) // -V501 && (STRNCMP(line, "@@ ", 3) == 0)) { diffstyle = DIFF_UNIFIED; } else { @@ -1565,7 +1562,8 @@ static void diff_read(int idx_orig, int idx_new, diffout_T *dout) &lnum_new, &count_new) == FAIL) { continue; } - } else if (diffstyle == DIFF_UNIFIED) { + } else { + assert(diffstyle == DIFF_UNIFIED); if (STRNCMP(line, "@@ ", 3) != 0) { continue; // not the start of a diff block } @@ -1573,9 +1571,6 @@ static void diff_read(int idx_orig, int idx_new, diffout_T *dout) &lnum_new, &count_new) == FAIL) { continue; } - } else { - EMSG(_("E959: Invalid diff format.")); - break; } // Go over blocks before the change, for which orig and new are equal. diff --git a/src/nvim/edit.c b/src/nvim/edit.c index c04190eaba..50c28dbaad 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -184,6 +184,16 @@ static expand_T compl_xp; static int compl_opt_refresh_always = FALSE; +static int pum_selected_item = -1; + +/// state for pum_ext_select_item. +struct { + bool active; + int item; + bool insert; + bool finish; +} pum_want; + typedef struct insert_state { VimState state; cmdarg_T *ca; @@ -560,7 +570,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_height - 1 - p_so + + curwin->w_grid.Rows - 1 - p_so && (curwin->w_cursor.lnum != curwin->w_topline || curwin->w_topfill > 0)) { if (curwin->w_topfill > 0) { @@ -976,10 +986,25 @@ static int insert_handle_key(InsertState *s) case K_EVENT: // some event multiqueue_process_events(main_loop.events); - break; + goto check_pum; case K_COMMAND: // some command do_cmdline(NULL, getcmdkeycmd, NULL, 0); + +check_pum: + // TODO(bfredl): Not entirely sure this indirection is necessary + // but doing like this ensures using nvim_select_popupmenu_item is + // equivalent to selecting the item with a typed key. + if (pum_want.active) { + if (pum_visible()) { + insert_do_complete(s); + if (pum_want.finish) { + // accept the item and stop completion + ins_compl_prep(Ctrl_Y); + } + } + pum_want.active = false; + } break; case K_HOME: // <Home> @@ -1494,40 +1519,41 @@ void edit_putchar(int c, int highlight) { int attr; - if (ScreenLines != NULL) { - update_topline(); /* just in case w_topline isn't valid */ + if (curwin->w_grid.chars != NULL || default_grid.chars != NULL) { + update_topline(); // just in case w_topline isn't valid validate_cursor(); if (highlight) { attr = HL_ATTR(HLF_8); } else { attr = 0; } - pc_row = curwin->w_winrow + curwin->w_wrow; - pc_col = curwin->w_wincol; + pc_row = curwin->w_wrow; + pc_col = 0; pc_status = PC_STATUS_UNSET; if (curwin->w_p_rl) { - pc_col += curwin->w_width - 1 - curwin->w_wcol; + pc_col += curwin->w_grid.Columns - 1 - curwin->w_wcol; if (has_mbyte) { - int fix_col = mb_fix_col(pc_col, pc_row); + int fix_col = grid_fix_col(&curwin->w_grid, pc_col, pc_row); if (fix_col != pc_col) { - screen_putchar(' ', pc_row, fix_col, attr); - --curwin->w_wcol; + grid_putchar(&curwin->w_grid, ' ', pc_row, fix_col, attr); + curwin->w_wcol--; pc_status = PC_STATUS_RIGHT; } } } else { pc_col += curwin->w_wcol; - if (mb_lefthalve(pc_row, pc_col)) + if (grid_lefthalve(&curwin->w_grid, pc_row, pc_col)) { pc_status = PC_STATUS_LEFT; + } } /* save the character to be able to put it back */ if (pc_status == PC_STATUS_UNSET) { - screen_getbytes(pc_row, pc_col, pc_bytes, &pc_attr); + grid_getbytes(&curwin->w_grid, pc_row, pc_col, pc_bytes, &pc_attr); pc_status = PC_STATUS_SET; } - screen_putchar(c, pc_row, pc_col, attr); + grid_putchar(&curwin->w_grid, c, pc_row, pc_col, attr); } } @@ -1541,9 +1567,10 @@ void edit_unputchar(void) curwin->w_wcol++; } if (pc_status == PC_STATUS_RIGHT || pc_status == PC_STATUS_LEFT) { - redrawWinline(curwin, curwin->w_cursor.lnum, false); + redrawWinline(curwin, curwin->w_cursor.lnum); } else { - screen_puts(pc_bytes, pc_row - msg_scrolled, pc_col, pc_attr); + grid_puts(&curwin->w_grid, pc_bytes, pc_row - msg_scrolled, pc_col, + pc_attr); } } } @@ -1566,8 +1593,8 @@ void display_dollar(colnr_T col) char_u *p = get_cursor_line_ptr(); curwin->w_cursor.col -= utf_head_off(p, p + col); curs_columns(false); // Recompute w_wrow and w_wcol - if (curwin->w_wcol < curwin->w_width) { - edit_putchar('$', FALSE); + if (curwin->w_wcol < curwin->w_grid.Columns) { + edit_putchar('$', false); dollar_vcol = curwin->w_virtcol; } curwin->w_cursor.col = save_col; @@ -1581,7 +1608,7 @@ static void undisplay_dollar(void) { if (dollar_vcol >= 0) { dollar_vcol = -1; - redrawWinline(curwin, curwin->w_cursor.lnum, false); + redrawWinline(curwin, curwin->w_cursor.lnum); } } @@ -2664,6 +2691,7 @@ void ins_compl_show_pum(void) // Use the cursor to get all wrapping and other settings right. col = curwin->w_cursor.col; curwin->w_cursor.col = compl_col; + pum_selected_item = cur; pum_display(compl_match_array, compl_match_arraysize, cur, array_changed); curwin->w_cursor.col = col; } @@ -3518,6 +3546,7 @@ expand_by_function ( win_T *curwin_save; buf_T *curbuf_save; typval_T rettv; + const int save_State = State; funcname = (type == CTRL_X_FUNCTION) ? curbuf->b_p_cfu : curbuf->b_p_ofu; if (*funcname == NUL) @@ -3563,6 +3592,9 @@ expand_by_function ( ins_compl_add_dict(matchdict); theend: + // Restore State, it might have been changed. + State = save_State; + if (matchdict != NULL) { tv_dict_unref(matchdict); } @@ -4340,6 +4372,17 @@ ins_compl_next ( return num_matches; } +void pum_ext_select_item(int item, bool insert, bool finish) +{ + if (!pum_visible() || item < -1 || item >= compl_match_arraysize) { + return; + } + pum_want.active = true; + pum_want.item = item; + pum_want.insert = insert; + pum_want.finish = finish; +} + // Call this while finding completions, to check whether the user has hit a key // that should change the currently displayed completion, or exit completion // mode. Also, when compl_pending is not zero, show a completion as soon as @@ -4400,6 +4443,9 @@ void ins_compl_check_keys(int frequency, int in_compl_func) */ static int ins_compl_key2dir(int c) { + if (c == K_EVENT || c == K_COMMAND) { + return pum_want.item < pum_selected_item ? BACKWARD : FORWARD; + } if (c == Ctrl_P || c == Ctrl_L || c == K_PAGEUP || c == K_KPAGEUP || c == K_S_UP || c == K_UP) { @@ -4427,6 +4473,11 @@ static int ins_compl_key2count(int c) { int h; + if (c == K_EVENT || c == K_COMMAND) { + int offset = pum_want.item - pum_selected_item; + return abs(offset); + } + if (ins_compl_pum_key(c) && c != K_UP && c != K_DOWN) { h = pum_get_height(); if (h > 3) @@ -4453,6 +4504,9 @@ static bool ins_compl_use_match(int c) case K_KPAGEUP: case K_S_UP: return false; + case K_EVENT: + case K_COMMAND: + return pum_want.active && pum_want.insert; } return true; } @@ -4674,6 +4728,7 @@ static int ins_complete(int c, bool enable_pum) pos_T pos; win_T *curwin_save; buf_T *curbuf_save; + const int save_State = State; /* Call 'completefunc' or 'omnifunc' and get pattern length as a * string */ @@ -4691,7 +4746,9 @@ static int ins_complete(int c, bool enable_pum) pos = curwin->w_cursor; curwin_save = curwin; curbuf_save = curbuf; - col = call_func_retnr(funcname, 2, args, FALSE); + col = call_func_retnr(funcname, 2, args, false); + + State = save_State; if (curwin_save != curwin || curbuf_save != curbuf) { EMSG(_(e_complwin)); return FAIL; @@ -5825,7 +5882,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_width - 'wrapmargin' + * else if 'wrapmargin' option is set, use curwin->w_grid.Columns-'wrapmargin' * if invalid value, use 0. * Set default to window width (maximum 79) for "gq" operator. */ @@ -5840,9 +5897,10 @@ 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_width - curbuf->b_p_wm; - if (cmdwin_type != 0) + textwidth = curwin->w_grid.Columns - curbuf->b_p_wm; + if (cmdwin_type != 0) { textwidth -= 1; + } textwidth -= curwin->w_p_fdc; if (signcolumn_on(curwin)) { @@ -5855,9 +5913,10 @@ comp_textwidth ( if (textwidth < 0) textwidth = 0; if (ff && textwidth == 0) { - textwidth = curwin->w_width - 1; - if (textwidth > 79) + textwidth = curwin->w_grid.Columns - 1; + if (textwidth > 79) { textwidth = 79; + } } return textwidth; } @@ -5921,7 +5980,7 @@ static void check_spell_redraw(void) linenr_T lnum = spell_redraw_lnum; spell_redraw_lnum = 0; - redrawWinline(curwin, lnum, false); + redrawWinline(curwin, lnum); } } @@ -8663,6 +8722,7 @@ static colnr_T get_nolist_virtcol(void) static char_u *do_insert_char_pre(int c) { char buf[MB_MAXBYTES + 1]; + const int save_State = State; // Return quickly when there is nothing to do. if (!has_event(EVENT_INSERTCHARPRE)) { @@ -8687,6 +8747,9 @@ static char_u *do_insert_char_pre(int c) set_vim_var_string(VV_CHAR, NULL, -1); textlock--; + // Restore the State, it may have been changed. + State = save_State; + return res; } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d67818aa81..58d42b49a7 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2380,7 +2380,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int ll_n1 = lp->ll_n1; // Check whether any of the list items is locked - for (listitem_T *ri = tv_list_first(rettv->vval.v_list); + for (ri = tv_list_first(rettv->vval.v_list); ri != NULL && ll_li != NULL; ) { if (tv_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, (const char *)lp->ll_name, @@ -2476,9 +2476,9 @@ notify: assert(lp->ll_newkey != NULL); tv_dict_watcher_notify(dict, (char *)lp->ll_newkey, lp->ll_tv, NULL); } else { - dictitem_T *di = lp->ll_di; - assert(di->di_key != NULL); - tv_dict_watcher_notify(dict, (char *)di->di_key, lp->ll_tv, &oldtv); + dictitem_T *di_ = lp->ll_di; + assert(di_->di_key != NULL); + tv_dict_watcher_notify(dict, (char *)di_->di_key, lp->ll_tv, &oldtv); tv_clear(&oldtv); } } @@ -5234,8 +5234,6 @@ bool garbage_collect(bool testing) /// @return true, if something was freed. static int free_unref_items(int copyID) { - dict_T *dd, *dd_next; - list_T *ll, *ll_next; bool did_free = false; // Let all "free" functions know that we are here. This means no @@ -5273,14 +5271,16 @@ static int free_unref_items(int copyID) } // PASS 2: free the items themselves. - for (dd = gc_first_dict; dd != NULL; dd = dd_next) { + dict_T *dd_next; + for (dict_T *dd = gc_first_dict; dd != NULL; dd = dd_next) { dd_next = dd->dv_used_next; if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) { tv_dict_free_dict(dd); } } - for (ll = gc_first_list; ll != NULL; ll = ll_next) { + list_T *ll_next; + for (list_T *ll = gc_first_list; ll != NULL; ll = ll_next) { ll_next = ll->lv_used_next; if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK) && !tv_list_has_watchers(ll)) { @@ -7103,7 +7103,7 @@ static void f_bufloaded(typval_T *argvars, typval_T *rettv, FunPtr fptr) /* * Get buffer by number or pattern. */ -static buf_T *get_buf_tv(typval_T *tv, int curtab_only) +static buf_T *tv_get_buf(typval_T *tv, int curtab_only) { char_u *name = tv->vval.v_string; int save_magic; @@ -7149,7 +7149,7 @@ static void f_bufname(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } emsg_off++; - const buf_T *const buf = get_buf_tv(&argvars[0], false); + const buf_T *const buf = tv_get_buf(&argvars[0], false); emsg_off--; if (buf != NULL && buf->b_fname != NULL) { rettv->vval.v_string = (char_u *)xstrdup((char *)buf->b_fname); @@ -7168,7 +7168,7 @@ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } emsg_off++; - const buf_T *buf = get_buf_tv(&argvars[0], false); + const buf_T *buf = tv_get_buf(&argvars[0], false); emsg_off--; // If the buffer isn't found and the second argument is not zero create a @@ -7195,7 +7195,7 @@ static void buf_win_common(typval_T *argvars, typval_T *rettv, bool get_nr) } emsg_off++; - buf_T *buf = get_buf_tv(&argvars[0], true); + buf_T *buf = tv_get_buf(&argvars[0], true); if (buf == NULL) { // no need to search if buffer was not found rettv->vval.v_number = -1; goto end; @@ -9280,7 +9280,7 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Information about one buffer. Argument specifies the buffer if (tv_check_num(&argvars[0])) { // issue errmsg if type error emsg_off++; - argbuf = get_buf_tv(&argvars[0], false); + argbuf = tv_get_buf(&argvars[0], false); emsg_off--; if (argbuf == NULL) { return; @@ -9376,7 +9376,7 @@ static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (tv_check_str_or_nr(&argvars[0])) { emsg_off++; - buf = get_buf_tv(&argvars[0], false); + buf = tv_get_buf(&argvars[0], false); emsg_off--; } @@ -9404,7 +9404,7 @@ static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *varname = tv_get_string_chk(&argvars[1]); emsg_off++; - buf_T *const buf = get_buf_tv(&argvars[0], false); + buf_T *const buf = tv_get_buf(&argvars[0], false); if (buf != NULL && varname != NULL) { // set curbuf to be our buf, temporarily @@ -10704,6 +10704,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) "postscript", "printer", "profile", + "pythonx", "reltime", "quickfix", "rightleft", @@ -11507,23 +11508,23 @@ static void dict_list(typval_T *const tv, typval_T *const rettv, tv_list_alloc_ret(rettv, tv_dict_len(tv->vval.v_dict)); TV_DICT_ITER(tv->vval.v_dict, di, { - typval_T tv = { .v_lock = VAR_UNLOCKED }; + typval_T tv_item = { .v_lock = VAR_UNLOCKED }; switch (what) { case kDictListKeys: { - tv.v_type = VAR_STRING; - tv.vval.v_string = vim_strsave(di->di_key); + tv_item.v_type = VAR_STRING; + tv_item.vval.v_string = vim_strsave(di->di_key); break; } case kDictListValues: { - tv_copy(&di->di_tv, &tv); + tv_copy(&di->di_tv, &tv_item); break; } case kDictListItems: { // items() list_T *const sub_l = tv_list_alloc(2); - tv.v_type = VAR_LIST; - tv.vval.v_list = sub_l; + tv_item.v_type = VAR_LIST; + tv_item.vval.v_list = sub_l; tv_list_ref(sub_l); tv_list_append_owned_tv(sub_l, (typval_T) { @@ -11538,7 +11539,7 @@ static void dict_list(typval_T *const tv, typval_T *const rettv, } } - tv_list_append_owned_tv(rettv->vval.v_list, tv); + tv_list_append_owned_tv(rettv->vval.v_list, tv_item); }); } @@ -13026,6 +13027,10 @@ static void f_pumvisible(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_pyeval(typval_T *argvars, typval_T *rettv, FunPtr fptr) { + if (p_pyx == 0) { + p_pyx = 2; + } + script_host_eval("python", argvars, rettv); } @@ -13034,9 +13039,24 @@ static void f_pyeval(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_py3eval(typval_T *argvars, typval_T *rettv, FunPtr fptr) { + if (p_pyx == 0) { + p_pyx = 3; + } + script_host_eval("python3", argvars, rettv); } +// "pyxeval()" function +static void f_pyxeval(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + init_pyxversion(); + if (p_pyx == 2) { + f_pyeval(argvars, rettv, NULL); + } else { + f_py3eval(argvars, rettv, NULL); + } +} + /* * "range()" function */ @@ -14023,11 +14043,11 @@ static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) const int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; const int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; - if (row < 0 || row >= screen_Rows - || col < 0 || col >= screen_Columns) { + if (row < 0 || row >= default_grid.Rows + || col < 0 || col >= default_grid.Columns) { c = -1; } else { - c = ScreenAttrs[LineOffset[row] + col]; + c = default_grid.attrs[default_grid.line_offset[row] + col]; } rettv->vval.v_number = c; } @@ -14042,12 +14062,12 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) const int row = tv_get_number_chk(&argvars[0], NULL) - 1; const int col = tv_get_number_chk(&argvars[1], NULL) - 1; - if (row < 0 || row >= screen_Rows - || col < 0 || col >= screen_Columns) { + if (row < 0 || row >= default_grid.Rows + || col < 0 || col >= default_grid.Columns) { c = -1; } else { - off = LineOffset[row] + col; - c = utf_ptr2char(ScreenLines[off]); + off = default_grid.line_offset[row] + col; + c = utf_ptr2char(default_grid.chars[off]); } rettv->vval.v_number = c; } @@ -14471,7 +14491,7 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } const char *varname = tv_get_string_chk(&argvars[1]); - buf_T *const buf = get_buf_tv(&argvars[0], false); + buf_T *const buf = tv_get_buf(&argvars[0], false); typval_T *varp = &argvars[2]; if (buf != NULL && varname != NULL) { @@ -14765,12 +14785,12 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *const l = argvars[0].vval.v_list; // To some extent make sure that we are dealing with a list from // "getmatches()". - int i = 0; + int li_idx = 0; TV_LIST_ITER_CONST(l, li, { if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT || (d = TV_LIST_ITEM_TV(li)->vval.v_dict) == NULL) { emsgf(_("E474: List item %d is either not a dictionary " - "or an empty one"), i); + "or an empty one"), li_idx); return; } if (!(tv_dict_find(d, S_LEN("group")) != NULL @@ -14778,10 +14798,11 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) || tv_dict_find(d, S_LEN("pos1")) != NULL) && tv_dict_find(d, S_LEN("priority")) != NULL && tv_dict_find(d, S_LEN("id")) != NULL)) { - emsgf(_("E474: List item %d is missing one of the required keys"), i); + emsgf(_("E474: List item %d is missing one of the required keys"), + li_idx); return; } - i++; + li_idx++; }); clear_matches(curwin); @@ -16774,10 +16795,10 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - uint16_t term_width = MAX(0, curwin->w_width - win_col_off(curwin)); + uint16_t term_width = MAX(0, curwin->w_grid.Columns - win_col_off(curwin)); Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, true, false, false, cwd, - term_width, curwin->w_height, + term_width, curwin->w_grid.Rows, xstrdup("xterm-256color"), &rettv->vval.v_number); if (rettv->vval.v_number <= 0) { @@ -20133,7 +20154,9 @@ void ex_function(exarg_T *eap) arg = skipwhite(skiptowhite(p)); if (arg[0] == '<' && arg[1] =='<' && ((p[0] == 'p' && p[1] == 'y' - && (!ASCII_ISALPHA(p[2]) || p[2] == 't')) + && (!ASCII_ISALNUM(p[2]) || p[2] == 't' + || ((p[2] == '3' || p[2] == 'x') + && !ASCII_ISALPHA(p[3])))) || (p[0] == 'p' && p[1] == 'e' && (!ASCII_ISALPHA(p[2]) || p[2] == 'r')) || (p[0] == 't' && p[1] == 'c' diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index e72bb7b870..78cac4878d 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -226,6 +226,7 @@ return { pumvisible={}, py3eval={args=1}, pyeval={args=1}, + pyxeval={args=1}, range={args={1, 3}}, readfile={args={1, 3}}, reltime={args={0, 2}}, diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index e9ab6cd3e2..64658b52d9 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -302,11 +302,11 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const size_t len_ = (len); \ ga_grow(gap, (int) (2 + len_ + memcnt(buf_, '\'', len_))); \ ga_append(gap, '\''); \ - for (size_t i = 0; i < len_; i++) { \ - if (buf_[i] == '\'') { \ + for (size_t i_ = 0; i_ < len_; i_++) { \ + if (buf_[i_] == '\'') { \ ga_append(gap, '\''); \ } \ - ga_append(gap, buf_[i]); \ + ga_append(gap, buf_[i_]); \ } \ ga_append(gap, '\''); \ } \ diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 36c4e333cf..85844c37bd 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -256,10 +256,12 @@ 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_width - curbuf->b_p_wm; - if (width <= 0) + if (width == 0 && curbuf->b_p_wm > 0) { + width = curwin->w_grid.Columns - curbuf->b_p_wm; + } + if (width <= 0) { width = 80; + } } if (u_save((linenr_T)(eap->line1 - 1), (linenr_T)(eap->line2 + 1)) == FAIL) @@ -2870,11 +2872,11 @@ void ex_z(exarg_T *eap) // Vi compatible: ":z!" uses display height, without a count uses // 'scroll' if (eap->forceit) { - bigness = curwin->w_height; + bigness = curwin->w_grid.Rows; } else if (ONE_WINDOW) { bigness = curwin->w_p_scr * 2; } else { - bigness = curwin->w_height - 3; + bigness = curwin->w_grid.Rows - 3; } if (bigness < 1) { bigness = 1; @@ -5757,7 +5759,7 @@ void ex_sign(exarg_T *eap) id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum); if (id > 0) { buf_delsign(curwin->w_buffer, id); - update_debug_sign(curwin->w_buffer, curwin->w_cursor.lnum); + redraw_buf_line_later(curwin->w_buffer, curwin->w_cursor.lnum); } else { EMSG(_("E159: Missing sign number")); } @@ -5786,7 +5788,7 @@ void ex_sign(exarg_T *eap) // ":sign unplace {id}": remove placed sign by number FOR_ALL_BUFFERS(buf) { if ((lnum = buf_delsign(buf, id)) != 0) { - update_debug_sign(buf, lnum); + redraw_buf_line_later(buf, lnum); } } return; @@ -5886,7 +5888,7 @@ void ex_sign(exarg_T *eap) } else { // ":sign unplace {id} file={fname}" lnum = buf_delsign(buf, id); - update_debug_sign(buf, lnum); + redraw_buf_line_later(buf, lnum); } } else if (sign_name != NULL) { // idx == SIGNCMD_PLACE @@ -5908,7 +5910,7 @@ void ex_sign(exarg_T *eap) lnum = buf_change_sign_type(buf, id, sp->sn_typenr); } if (lnum > 0) { - update_debug_sign(buf, lnum); + redraw_buf_line_later(buf, lnum); } else { EMSG2(_("E885: Not possible to change sign %s"), sign_name); } diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index 79ca5363e0..4806eff1b4 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -2067,6 +2067,30 @@ return { func='ex_py3file', }, { + command='pyx', + flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), + addr_type=ADDR_LINES, + func='ex_pyx', + }, + { + command='pyxdo', + flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN), + addr_type=ADDR_LINES, + func='ex_pyxdo', + }, + { + command='pythonx', + flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), + addr_type=ADDR_LINES, + func='ex_pyx', + }, + { + command='pyxfile', + flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN), + addr_type=ADDR_LINES, + func='ex_pyxfile', + }, + { command='quit', flags=bit.bor(BANG, RANGE, COUNT, NOTADR, TRLBAR, CMDWIN), addr_type=ADDR_WINDOWS, diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 1ffcf67ef7..484c911a8b 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -1772,7 +1772,7 @@ void ex_args(exarg_T *eap) } } - if (!ends_excmd(*eap->arg)) { + if (*eap->arg != NUL) { // ":args file ..": define new argument list, handle like ":next" // Also for ":argslocal file .." and ":argsglobal file ..". ex_next(eap); @@ -2130,9 +2130,9 @@ void ex_listdo(exarg_T *eap) // Remember the number of the next listed buffer, in case // ":bwipe" is used or autocommands do something strange. next_fnum = -1; - for (buf_T *buf = curbuf->b_next; buf != NULL; buf = buf->b_next) { - if (buf->b_p_bl) { - next_fnum = buf->b_fnum; + for (buf_T *bp = curbuf->b_next; bp != NULL; bp = bp->b_next) { + if (bp->b_p_bl) { + next_fnum = bp->b_fnum; break; } } @@ -2809,6 +2809,126 @@ void ex_options(exarg_T *eap) cmd_source((char_u *)SYS_OPTWIN_FILE, NULL); } +// Detect Python 3 or 2, and initialize 'pyxversion'. +void init_pyxversion(void) +{ + if (p_pyx == 0) { + if (!eval_has_provider("python3")) { + p_pyx = 3; + } else if (!eval_has_provider("python")) { + p_pyx = 2; + } + } +} + +// Does a file contain one of the following strings at the beginning of any +// line? +// "#!(any string)python2" => returns 2 +// "#!(any string)python3" => returns 3 +// "# requires python 2.x" => returns 2 +// "# requires python 3.x" => returns 3 +// otherwise return 0. +static int requires_py_version(char_u *filename) +{ + FILE *file; + int requires_py_version = 0; + int i, lines; + + lines = (int)p_mls; + if (lines < 0) { + lines = 5; + } + + file = mch_fopen((char *)filename, "r"); + if (file != NULL) { + for (i = 0; i < lines; i++) { + if (vim_fgets(IObuff, IOSIZE, file)) { + break; + } + if (i == 0 && IObuff[0] == '#' && IObuff[1] == '!') { + // Check shebang. + if (strstr((char *)IObuff + 2, "python2") != NULL) { + requires_py_version = 2; + break; + } + if (strstr((char *)IObuff + 2, "python3") != NULL) { + requires_py_version = 3; + break; + } + } + IObuff[21] = '\0'; + if (STRCMP("# requires python 2.x", IObuff) == 0) { + requires_py_version = 2; + break; + } + if (STRCMP("# requires python 3.x", IObuff) == 0) { + requires_py_version = 3; + break; + } + } + fclose(file); + } + return requires_py_version; +} + + +// Source a python file using the requested python version. +static void source_pyx_file(exarg_T *eap, char_u *fname) +{ + exarg_T ex; + long int v = requires_py_version(fname); + + init_pyxversion(); + if (v == 0) { + // user didn't choose a preference, 'pyx' is used + v = p_pyx; + } + + // now source, if required python version is not supported show + // unobtrusive message. + if (eap == NULL) { + memset(&ex, 0, sizeof(ex)); + } else { + ex = *eap; + } + ex.arg = fname; + ex.cmd = (char_u *)(v == 2 ? "pyfile" : "pyfile3"); + + if (v == 2) { + ex_pyfile(&ex); + } else { + ex_py3file(&ex); + } +} + +// ":pyxfile {fname}" +void ex_pyxfile(exarg_T *eap) +{ + source_pyx_file(eap, eap->arg); +} + +// ":pyx" +void ex_pyx(exarg_T *eap) +{ + init_pyxversion(); + if (p_pyx == 2) { + ex_python(eap); + } else { + ex_python3(eap); + } +} + +// ":pyxdo" +void ex_pyxdo(exarg_T *eap) +{ + init_pyxversion(); + if (p_pyx == 2) { + ex_pydo(eap); + } else { + ex_pydo3(eap); + } +} + /// ":source {fname}" void ex_source(exarg_T *eap) { diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 4ef332186e..ffa913efcf 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -2158,6 +2158,10 @@ static char_u * do_one_cmd(char_u **cmdlinep, case CMD_python: case CMD_py3: case CMD_python3: + case CMD_pythonx: + case CMD_pyx: + case CMD_pyxdo: + case CMD_pyxfile: case CMD_return: case CMD_rightbelow: case CMD_ruby: @@ -9194,10 +9198,10 @@ static int ses_winsizes(FILE *fd, int restore_size, win_T *tab_firstwin) /* 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_height, - (int64_t)(Rows / 2), (int64_t)Rows) < 0 + "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)) return FAIL; @@ -9459,8 +9463,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_height / 2), - (int64_t)wp->w_height) < 0 + (int64_t)(wp->w_grid.Rows / 2), + (int64_t)wp->w_grid.Rows) < 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 diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index bfc32887ca..4ea25117d9 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -6197,9 +6197,6 @@ static int open_cmdwin(void) stuffReadbuff(p); stuffcharReadbuff(CAR); } - } else if (cmdwin_result == K_XF2) { /* :qa typed */ - ccline.cmdbuff = vim_strsave((char_u *)"qa"); - cmdwin_result = CAR; } else if (cmdwin_result == Ctrl_C) { /* :q or :close, don't execute any command * and don't modify the cmd window. */ diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index fe12a69801..c631057e81 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -5997,9 +5997,8 @@ void do_autocmd(char_u *arg_in, int forceit) char_u *pat; char_u *envpat = NULL; char_u *cmd; - event_T event; - int need_free = FALSE; - int nested = FALSE; + int need_free = false; + int nested = false; int group; if (*arg == '|') { @@ -6082,7 +6081,7 @@ void do_autocmd(char_u *arg_in, int forceit) last_event = (event_T)-1; // for listing the event name last_group = AUGROUP_ERROR; // for listing the group name if (*arg == '*' || *arg == NUL || *arg == '|') { - for (event = (event_T)0; (int)event < (int)NUM_EVENTS; + for (event_T event = (event_T)0; (int)event < (int)NUM_EVENTS; event = (event_T)((int)event + 1)) { if (do_autocmd_event(event, pat, nested, cmd, forceit, group) == FAIL) { break; diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 94702a9a3a..53e9846c2d 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -2120,8 +2120,8 @@ static int vgetorpeek(int advance) ++col; } curwin->w_wrow = curwin->w_cline_row - + curwin->w_wcol / curwin->w_width; - curwin->w_wcol %= curwin->w_width; + + curwin->w_wcol / curwin->w_grid.Columns; + curwin->w_wcol %= curwin->w_grid.Columns; curwin->w_wcol += curwin_col_off(); col = 0; /* no correction needed */ } else { @@ -2129,8 +2129,8 @@ static int vgetorpeek(int advance) col = curwin->w_cursor.col - 1; } } else if (curwin->w_p_wrap && curwin->w_wrow) { - --curwin->w_wrow; - curwin->w_wcol = curwin->w_width - 1; + curwin->w_wrow--; + curwin->w_wcol = curwin->w_grid.Columns - 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 04ff1320ce..f749c119cd 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -91,9 +91,10 @@ EXTERN struct nvim_stats_s { /* * Number of Rows and Columns in the screen. * Must be long to be able to use them as options in option.c. - * Note: Use screen_Rows and screen_Columns to access items in ScreenLines[]. - * They may have different values when the screen wasn't (re)allocated yet - * after setting Rows or Columns (e.g., when starting up). + * Note: Use default_grid.Rows and default_grid.Columns to access items in + * default_grid.chars[]. They may have different values when the screen + * wasn't (re)allocated yet after setting Rows or Columns (e.g., when starting + * up). */ #define DFLT_COLS 80 // default value for 'columns' #define DFLT_ROWS 24 // default value for 'lines' @@ -129,45 +130,6 @@ typedef off_t off_T; #endif /* - * The characters and attributes cached for the screen. - */ -typedef char_u schar_T[(MAX_MCO+1) * 4 + 1]; -typedef int16_t sattr_T; - -/// ScreenLines[] contains a copy of the whole screen, as it currently is -/// displayed. It is a single block of screen cells, the size of the screen -/// plus one line. The extra line used as a buffer while redrawing a window -/// line, so it can be compared with the previous state of that line. This way -/// we can avoid sending bigger updates than neccessary to the Ul layer. -/// -/// Screen cells are stored as NUL-terminated UTF-8 strings, and a cell can -/// contain up to MAX_MCO composing characters after the base character. -/// The composing characters are to be drawn on top of the original character. -/// The content after the NUL is not defined (so comparison must be done a -/// single cell at a time). Double-width characters are stored in the left cell, -/// and the right cell should only contain the empty string. When a part of the -/// screen is cleared, the cells should be filled with a single whitespace char. -/// -/// ScreenAttrs[] contains the highlighting attribute for each cell. -/// LineOffset[n] is the offset from ScreenLines[] and ScreenAttrs[] for the -/// start of line 'n'. These offsets are in general not linear, as full screen -/// scrolling is implemented by rotating the offsets in the LineOffset array. -/// LineWraps[] is an array of boolean flags indicating if the screen line wraps -/// to the next line. It can only be true if a window occupies the entire screen -/// width. -/// -/// -/// Note: before the screen is initialized and when out of memory these can be -/// NULL. -EXTERN schar_T *ScreenLines INIT(= NULL); -EXTERN sattr_T *ScreenAttrs INIT(= NULL); -EXTERN unsigned *LineOffset INIT(= NULL); -EXTERN char_u *LineWraps INIT(= NULL); /* line wraps to next line */ - -EXTERN int screen_Rows INIT(= 0); /* actual size of ScreenLines[] */ -EXTERN int screen_Columns INIT(= 0); /* actual size of ScreenLines[] */ - -/* * When vgetc() is called, it sets mod_mask to the set of modifiers that are * held down based on the MOD_MASK_* symbols that are read first. */ @@ -228,11 +190,8 @@ EXTERN int compl_cont_status INIT(= 0); # define CONT_LOCAL 32 /* for ctrl_x_mode 0, ^X^P/^X^N do a local * expansion, (eg use complete=.) */ -/* - * Functions for putting characters in the command line, - * while keeping ScreenLines[] updated. - */ -EXTERN int cmdmsg_rl INIT(= FALSE); /* cmdline is drawn right to left */ +// state for putting characters in the message area +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 @@ -702,11 +661,13 @@ EXTERN int* (*iconv_errno)(void); /// Visual_mode: When State is NORMAL or INSERT. /// finish_op : When State is NORMAL, after typing the operator and /// before typing the motion command. +/// motion_force: Last motion_force from do_pending_operator() EXTERN int State INIT(= NORMAL); // This is the current state of the // command interpreter. EXTERN bool finish_op INIT(= false); // true while an operator is pending EXTERN long opcount INIT(= 0); // count for pending operator +EXTERN int motion_force INIT(=0); // motion force for pending operator // Ex Mode (Q) state EXTERN int exmode_active INIT(= 0); // Zero, EXMODE_NORMAL or EXMODE_VIM. diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h new file mode 100644 index 0000000000..37d85ead0c --- /dev/null +++ b/src/nvim/grid_defs.h @@ -0,0 +1,59 @@ +#ifndef NVIM_GRID_DEFS_H +#define NVIM_GRID_DEFS_H + +#include <stdint.h> + +#include "nvim/types.h" + +#define MAX_MCO 6 // maximum value for 'maxcombine' + +// The characters and attributes drawn on grids. +typedef char_u schar_T[(MAX_MCO+1) * 4 + 1]; +typedef int16_t sattr_T; + +/// ScreenGrid represents a resizable rectuangular grid displayed by UI clients. +/// +/// chars[] contains the UTF-8 text that is currently displayed on the grid. +/// It is stored as a single block of cells. When redrawing a part of the grid, +/// the new state can be compared with the existing state of the grid. This way +/// we can avoid sending bigger updates than neccessary to the Ul layer. +/// +/// Screen cells are stored as NUL-terminated UTF-8 strings, and a cell can +/// contain up to MAX_MCO composing characters after the base character. +/// The composing characters are to be drawn on top of the original character. +/// The content after the NUL is not defined (so comparison must be done a +/// single cell at a time). Double-width characters are stored in the left cell, +/// and the right cell should only contain the empty string. When a part of the +/// screen is cleared, the cells should be filled with a single whitespace char. +/// +/// attrs[] contains the highlighting attribute for each cell. +/// line_offset[n] is the offset from chars[] and attrs[] for the +/// start of line 'n'. These offsets are in general not linear, as full screen +/// scrolling is implemented by rotating the offsets in the line_offset array. +/// line_wraps[] is an array of boolean flags indicating if the screen line +/// wraps to the next line. It can only be true if a window occupies the entire +/// screen width. +typedef struct { + handle_T handle; + + schar_T *chars; + sattr_T *attrs; + unsigned *line_offset; + char_u *line_wraps; + + // the size of the allocated grid. + int Rows; + int Columns; + + // offsets for the grid relative to the global screen + 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; +} ScreenGrid; + +#endif // NVIM_GRID_DEFS_H diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index a8731d5bd7..983dbb7bbe 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -582,8 +582,9 @@ static void prt_header(prt_settings_T *const psettings, const int pagenum, */ static void prt_message(char_u *s) { - screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0); - screen_puts(s, (int)Rows - 1, 0, HL_ATTR(HLF_R)); + grid_fill(&default_grid, (int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', + 0); + grid_puts(&default_grid, s, (int)Rows - 1, 0, HL_ATTR(HLF_R)); ui_flush(); } diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index 5a9727f46e..41d60fa3ea 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -98,7 +98,7 @@ static int get_attr_entry(HlEntry entry) return id; } -/// When a UI connects, we need to send it the table of higlights used so far. +/// When a UI connects, we need to send it the table of highlights used so far. void ui_send_all_hls(UI *ui) { for (size_t i = 1; i < kv_size(attr_entries); i++) { @@ -212,13 +212,7 @@ void clear_hl_tables(bool reinit) map_clear(int, int)(combine_attr_entries); highlight_attr_set_all(); highlight_changed(); - redraw_all_later(NOT_VALID); - if (ScreenAttrs) { - // the meaning of 0 doesn't change anyway - // but the rest must be retransmitted - memset(ScreenAttrs, 0, - sizeof(*ScreenAttrs) * (size_t)(screen_Rows * screen_Columns)); - } + screen_invalidate_highlights(); } else { kv_destroy(attr_entries); map_free(HlEntry, int)(attr_entry_ids); diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 3cc64e0033..13534ac1a9 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -6,6 +6,7 @@ #include <stdbool.h> #include "nvim/ascii.h" +#include "nvim/assert.h" #include "nvim/indent.h" #include "nvim/eval.h" #include "nvim/charset.h" @@ -204,7 +205,12 @@ int set_indent(int size, int flags) // after the if (!curbuf->b_p_et) below. if (orig_char_len != -1) { assert(orig_char_len + size - ind_done + line_len >= 0); - newline = xmalloc((size_t)(orig_char_len + size - ind_done + line_len)); + 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); todo = size - ind_done; // Set total length of indent in characters, which may have been @@ -226,7 +232,9 @@ int set_indent(int size, int flags) } else { todo = size; assert(ind_len + line_len >= 0); - newline = xmalloc((size_t)(ind_len + line_len)); + size_t newline_size; + STRICT_ADD(ind_len, line_len, &newline_size, size_t); + newline = xmalloc(newline_size); s = newline; } @@ -392,7 +400,9 @@ int copy_indent(int size, char_u *src) // and the rest of the line. line_len = (int)STRLEN(get_cursor_line_ptr()) + 1; assert(ind_len + line_len >= 0); - line = xmalloc((size_t)(ind_len + line_len)); + size_t line_size; + STRICT_ADD(ind_len, line_len, &line_size, size_t); + line = xmalloc(line_size); p = line; } } @@ -463,8 +473,8 @@ int get_breakindent_win(win_T *wp, char_u *line) static char_u *prev_line = NULL; // cached pointer to 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_width + // window width minus window margin space, i.e. what rests for text + const int eff_wwidth = wp->w_grid.Columns - ((wp->w_p_nu || wp->w_p_rnu) && (vim_strchr(p_cpo, CPO_NUMCOL) == NULL) ? number_width(wp) + 1 : 0); @@ -544,10 +554,8 @@ int get_expr_indent(void) // Need to make a copy, the 'indentexpr' option could be changed while // evaluating it. char_u *inde_copy = vim_strsave(curbuf->b_p_inde); - if (inde_copy != NULL) { - indent = (int)eval_to_number(inde_copy); - xfree(inde_copy); - } + indent = (int)eval_to_number(inde_copy); + xfree(inde_copy); if (use_sandbox) { sandbox--; diff --git a/src/nvim/main.c b/src/nvim/main.c index 8a40577e8f..17f3a894d4 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -781,7 +781,6 @@ static void command_line_scan(mparm_T *parmp) bool had_minmin = false; // found "--" argument int want_argument; // option argument with argument int c; - char_u *p = NULL; long n; argc--; @@ -1227,7 +1226,7 @@ scripterror: // Add the file to the global argument list. ga_grow(&global_alist.al_ga, 1); - p = vim_strsave((char_u *)argv[0]); + char_u *p = vim_strsave((char_u *)argv[0]); if (parmp->diff_mode && os_isdir(p) && GARGCOUNT > 0 && !os_isdir(alist_name(&GARGLIST[0]))) { diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 0ee8e2bd85..ead6b4405d 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -556,13 +556,6 @@ size_t mb_string2cells(const char_u *str) return clen; } -/// Return number of display cells for char at ScreenLines[off]. -/// We make sure that the offset used is less than "max_off". -int utf_off2cells(unsigned off, unsigned max_off) -{ - return (off + 1 < max_off && ScreenLines[off + 1][0] == 0) ? 2 : 1; -} - /// Convert a UTF-8 byte sequence to a wide character /// /// If the sequence is illegal or truncated by a NUL then the first byte is @@ -1822,32 +1815,6 @@ const char *mb_unescape(const char **const pp) return NULL; } -/* - * Return true if the character at "row"/"col" on the screen is the left side - * of a double-width character. - * Caller must make sure "row" and "col" are not invalid! - */ -bool mb_lefthalve(int row, int col) -{ - return utf_off2cells(LineOffset[row] + col, - LineOffset[row] + screen_Columns) > 1; -} - -/* - * Correct a position on the screen, if it's the right half of a double-wide - * char move it to the left half. Returns the corrected column. - */ -int mb_fix_col(int col, int row) -{ - col = check_col(col); - row = check_row(row); - if (ScreenLines != NULL && col > 0 - && ScreenLines[LineOffset[row] + col][0] == 0) { - return col - 1; - } - return col; -} - /* * Skip the Vim specific head of a 'encoding' name. @@ -2524,23 +2491,3 @@ char_u * string_convert_ext(const vimconv_T *const vcp, char_u *ptr, return retval; } - -// Check bounds for column number -static int check_col(int col) -{ - if (col < 0) - return 0; - if (col >= screen_Columns) - return screen_Columns - 1; - return col; -} - -// Check bounds for row number -static int check_row(int row) -{ - if (row < 0) - return 0; - if (row >= screen_Rows) - return screen_Rows - 1; - return row; -} diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h index 70073a8540..ed48705c6d 100644 --- a/src/nvim/mbyte.h +++ b/src/nvim/mbyte.h @@ -19,13 +19,6 @@ #define MB_BYTE2LEN(b) utf8len_tab[b] #define MB_BYTE2LEN_CHECK(b) (((b) < 0 || (b) > 255) ? 1 : utf8len_tab[b]) -/// Maximum value for 'maxcombine' -/// -/// At most that number of composing characters may be attached to the leading -/// character by various `utfc_*` functions. Note that some functions do not -/// have this limit. -enum { MAX_MCO = 6 }; - // max length of an unicode char #define MB_MAXCHAR 6 diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 6b96a3b070..d38079ca72 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -698,8 +698,8 @@ void free_all_mem(void) buf = bufref_valid(&bufref) ? nextbuf : firstbuf; } - /* screenlines (can't display anything now!) */ - free_screenlines(); + // free screenlines (can't display anything now!) + screen_free_all_mem(); clear_hl_tables(false); list_free_log(); diff --git a/src/nvim/message.c b/src/nvim/message.c index 6de81a8aaf..37e40c3cc1 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1546,10 +1546,8 @@ void msg_prt_line(char_u *s, int list) msg_clr_eos(); } -/* - * Use screen_puts() to output one multi-byte character. - * Return the pointer "s" advanced to the next character. - */ +// Use grid_puts() to output one multi-byte character. +// Return the pointer "s" advanced to the next character. static char_u *screen_puts_mbyte(char_u *s, int l, int attr) { int cw; @@ -1563,7 +1561,7 @@ static char_u *screen_puts_mbyte(char_u *s, int l, int attr) return s; } - screen_puts_len(s, l, msg_row, msg_col, attr); + grid_puts_len(&default_grid, s, l, msg_row, msg_col, attr); if (cmdmsg_rl) { msg_col -= cw; if (msg_col == 0) { @@ -1886,19 +1884,22 @@ int msg_scrollsize(void) */ static void msg_scroll_up(void) { + if (msg_scrolled == 0) { + ui_call_win_scroll_over_start(); + } if (dy_flags & DY_MSGSEP) { if (msg_scrolled == 0) { - screen_fill(Rows-p_ch-1, Rows-p_ch, 0, (int)Columns, - fill_msgsep, fill_msgsep, HL_ATTR(HLF_MSGSEP)); + grid_fill(&default_grid, Rows-p_ch-1, Rows-p_ch, 0, (int)Columns, + fill_msgsep, fill_msgsep, HL_ATTR(HLF_MSGSEP)); } int nscroll = MIN(msg_scrollsize()+1, Rows); - screen_del_lines(Rows-nscroll, 1, Rows, 0, Columns); + grid_del_lines(&default_grid, Rows-nscroll, 1, Rows, 0, Columns); } else { - screen_del_lines(0, 1, (int)Rows, 0, Columns); + grid_del_lines(&default_grid, 0, 1, (int)Rows, 0, Columns); } // TODO(bfredl): when msgsep display is properly batched, this fill should be // eliminated. - screen_fill(Rows-1, Rows, 0, (int)Columns, ' ', ' ', 0); + grid_fill(&default_grid, Rows-1, Rows, 0, (int)Columns, ' ', ' ', 0); } /* @@ -2097,7 +2098,8 @@ static void t_puts(int *t_col, const char_u *t_s, const char_u *s, int attr) { // Output postponed text. msg_didout = true; // Remember that line is not empty. - screen_puts_len((char_u *)t_s, (int)(s - t_s), msg_row, msg_col, attr); + grid_puts_len(&default_grid, (char_u *)t_s, (int)(s - t_s), msg_row, msg_col, + attr); msg_col += *t_col; *t_col = 0; /* If the string starts with a composing character don't increment the @@ -2313,8 +2315,9 @@ static int do_more_prompt(int typed_char) } if (toscroll == -1 - && screen_ins_lines(0, 1, (int)Rows, 0, (int)Columns) == OK) { - screen_fill(0, 1, 0, (int)Columns, ' ', ' ', 0); + && grid_ins_lines(&default_grid, 0, 1, (int)Rows, + 0, (int)Columns) == OK) { + grid_fill(&default_grid, 0, 1, 0, (int)Columns, ' ', ' ', 0); // display line at top (void)disp_sb_line(0, mp); } else { @@ -2333,18 +2336,18 @@ static int do_more_prompt(int typed_char) /* scroll up, display line at bottom */ msg_scroll_up(); inc_msg_scrolled(); - screen_fill((int)Rows - 2, (int)Rows - 1, 0, - (int)Columns, ' ', ' ', 0); + grid_fill(&default_grid, (int)Rows - 2, (int)Rows - 1, 0, + (int)Columns, ' ', ' ', 0); mp_last = disp_sb_line((int)Rows - 2, mp_last); --toscroll; } } if (toscroll <= 0) { - /* displayed the requested text, more prompt again */ - screen_fill((int)Rows - 1, (int)Rows, 0, - (int)Columns, ' ', ' ', 0); - msg_moremsg(FALSE); + // displayed the requested text, more prompt again + grid_fill(&default_grid, (int)Rows - 1, (int)Rows, 0, + (int)Columns, ' ', ' ', 0); + msg_moremsg(false); continue; } @@ -2355,8 +2358,9 @@ static int do_more_prompt(int typed_char) break; } - /* clear the --more-- message */ - screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0); + // clear the --more-- message + grid_fill(&default_grid, (int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', + 0); State = oldState; setmouse(); if (quit_more) { @@ -2452,8 +2456,8 @@ void mch_msg(char *str) */ static void msg_screen_putchar(int c, int attr) { - msg_didout = TRUE; /* remember that line is not empty */ - screen_putchar(c, msg_row, msg_col, attr); + msg_didout = true; // remember that line is not empty + grid_putchar(&default_grid, c, msg_row, msg_col, attr); if (cmdmsg_rl) { if (--msg_col == 0) { msg_col = Columns; @@ -2473,11 +2477,12 @@ void msg_moremsg(int full) char_u *s = (char_u *)_("-- More --"); attr = HL_ATTR(HLF_M); - screen_puts(s, (int)Rows - 1, 0, attr); - if (full) - screen_puts((char_u *) - _(" SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "), - (int)Rows - 1, vim_strsize(s), attr); + grid_puts(&default_grid, s, (int)Rows - 1, 0, attr); + if (full) { + grid_puts(&default_grid, (char_u *) + _(" SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "), + (int)Rows - 1, vim_strsize(s), attr); + } } /* @@ -2525,13 +2530,13 @@ void msg_clr_eos(void) */ void msg_clr_eos_force(void) { - if (cmdmsg_rl) { - screen_fill(msg_row, msg_row + 1, 0, msg_col + 1, ' ', ' ', 0); - screen_fill(msg_row + 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0); - } else { - screen_fill(msg_row, msg_row + 1, msg_col, (int)Columns, ' ', ' ', 0); - screen_fill(msg_row + 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0); - } + int msg_startcol = (cmdmsg_rl) ? 0 : msg_col; + int msg_endcol = (cmdmsg_rl) ? msg_col + 1 : (int)Columns; + + grid_fill(&default_grid, msg_row, msg_row + 1, msg_startcol, msg_endcol, ' ', + ' ', 0); + grid_fill(&default_grid, msg_row + 1, (int)Rows, 0, (int)Columns, ' ', ' ', + 0); } /* diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index ffe2d11f08..a66ded13f1 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -203,7 +203,7 @@ open_line ( char_u *ptr; char_u last_char; - pos_T old_cursor = curwin->w_cursor; + old_cursor = curwin->w_cursor; ptr = saved_line; if (flags & OPENLINE_DO_COM) lead_len = get_leader_len(ptr, NULL, FALSE, TRUE); @@ -1251,7 +1251,7 @@ int plines_win_nofill( return 1; } - if (wp->w_width == 0) { + if (wp->w_grid.Columns == 0) { return 1; } @@ -1261,8 +1261,8 @@ int plines_win_nofill( } const int lines = plines_win_nofold(wp, lnum); - if (winheight && lines > wp->w_height) { - return wp->w_height; + if (winheight && lines > wp->w_grid.Rows) { + return wp->w_grid.Rows; } return lines; } @@ -1292,7 +1292,7 @@ int plines_win_nofold(win_T *wp, linenr_T lnum) /* * Add column offset for 'number', 'relativenumber' and 'foldcolumn'. */ - width = wp->w_width - win_col_off(wp); + width = wp->w_grid.Columns - win_col_off(wp); if (width <= 0 || col > 32000) { return 32000; // bigger than the number of screen columns } @@ -1318,8 +1318,9 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column) if (!wp->w_p_wrap) return lines + 1; - if (wp->w_width == 0) + if (wp->w_grid.Columns == 0) { return lines + 1; + } char_u *line = ml_get_buf(wp->w_buffer, lnum, false); char_u *s = line; @@ -1340,7 +1341,7 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column) } // Add column offset for 'number', 'relativenumber', 'foldcolumn', etc. - int width = wp->w_width - win_col_off(wp); + int width = wp->w_grid.Columns - win_col_off(wp); if (width <= 0) { return 9999; } diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 3c792326a4..bf71e6a479 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -110,8 +110,9 @@ retnomove: // Remember the character under the mouse, it might be a '-' or '+' in the // fold column. NB: only works for ASCII chars! if (row >= 0 && row < Rows && col >= 0 && col <= Columns - && ScreenLines != NULL) { - mouse_char = ScreenLines[LineOffset[row] + (unsigned)col][0]; + && default_grid.chars != NULL) { + mouse_char = default_grid.chars[default_grid.line_offset[row] + + (unsigned)col][0]; } else { mouse_char = ' '; } @@ -318,7 +319,6 @@ retnomove: // Start Visual mode before coladvance(), for when 'sel' != "old" if ((flags & MOUSE_MAY_VIS) && !VIsual_active) { - check_visual_highlight(); VIsual = old_cursor; VIsual_active = true; VIsual_reselect = true; diff --git a/src/nvim/move.c b/src/nvim/move.c index 3a29851ee6..f2c8996050 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -83,8 +83,9 @@ static void comp_botline(win_T *wp) redraw_for_cursorline(wp); wp->w_valid |= (VALID_CROW|VALID_CHEIGHT); } - if (done + n > wp->w_height) + if (done + n > wp->w_grid.Rows) { break; + } done += n; lnum = last; } @@ -116,8 +117,8 @@ static void redraw_for_cursorline(win_T *wp) // "w_last_cursorline" may be outdated, worst case we redraw // too much. This is optimized for moving the cursor around in // the current window. - redrawWinline(wp, wp->w_last_cursorline, false); - redrawWinline(wp, wp->w_cursor.lnum, false); + redrawWinline(wp, wp->w_last_cursorline); + redrawWinline(wp, wp->w_cursor.lnum); redraw_win_later(wp, VALID); } else { redraw_win_later(wp, SOME_VALID); @@ -149,9 +150,12 @@ 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_height == 0) { + if (!screen_valid(true) || curwin->w_grid.Rows == 0) { curwin->w_topline = curwin->w_cursor.lnum; curwin->w_botline = curwin->w_topline; curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; @@ -200,9 +204,10 @@ void update_topline(void) check_topline = true; if (check_topline) { - int halfheight = curwin->w_height / 2 - 1; - if (halfheight < 2) + int halfheight = curwin->w_grid.Rows / 2 - 1; + if (halfheight < 2) { halfheight = 2; + } long n; if (hasAnyFolding(curwin)) { /* Count the number of logical lines between the cursor and @@ -289,20 +294,22 @@ void update_topline(void) * botline - p_so (approximation of how much will be * scrolled). */ for (linenr_T lnum = curwin->w_cursor.lnum; - 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_height + 1) + 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) { break; + } (void)hasFolding(lnum, &lnum, NULL); } } else line_count = curwin->w_cursor.lnum - curwin->w_botline + 1 + p_so; - if (line_count <= curwin->w_height + 1) + if (line_count <= curwin->w_grid.Rows + 1) { scroll_cursor_bot(scrolljump_value(), false); - else + } else { scroll_cursor_halfway(false); + } } } } @@ -346,7 +353,7 @@ void update_topline_win(win_T* win) */ static int scrolljump_value(void) { - long result = p_sj >= 0 ? p_sj : (curwin->w_height * -p_sj) / 100; + long result = p_sj >= 0 ? p_sj : (curwin->w_grid.Rows * -p_sj) / 100; assert(result <= INT_MAX); return (int)result; } @@ -522,6 +529,7 @@ 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); @@ -660,18 +668,19 @@ void validate_cursor_col(void) colnr_T col = curwin->w_virtcol; colnr_T off = curwin_col_off(); col += off; - int width = curwin->w_width - off + curwin_col_off2(); - - /* long line wrapping, adjust curwin->w_wrow */ - if (curwin->w_p_wrap - && col >= (colnr_T)curwin->w_width - && width > 0) - /* use same formula as what is used in curs_columns() */ - col -= ((col - curwin->w_width) / width + 1) * width; - if (col > (int)curwin->w_leftcol) + int width = curwin->w_grid.Columns - off + curwin_col_off2(); + + // long line wrapping, adjust curwin->w_wrow + if (curwin->w_p_wrap && col >= (colnr_T)curwin->w_grid.Columns + && width > 0) { + // use same formula as what is used in curs_columns() + col -= ((col - curwin->w_grid.Columns) / width + 1) * width; + } + if (col > (int)curwin->w_leftcol) { col -= curwin->w_leftcol; - else + } else { col = 0; + } curwin->w_wcol = col; curwin->w_valid |= VALID_WCOL; @@ -760,20 +769,20 @@ void curs_columns( */ curwin->w_wrow = curwin->w_cline_row; - int textwidth = curwin->w_width - extra; + int textwidth = curwin->w_grid.Columns - extra; if (textwidth <= 0) { - /* No room for text, put cursor in last char of window. */ - curwin->w_wcol = curwin->w_width - 1; - curwin->w_wrow = curwin->w_height - 1; + // 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; } else if (curwin->w_p_wrap - && curwin->w_width != 0 + && curwin->w_grid.Columns != 0 ) { width = textwidth + curwin_col_off2(); - /* long line wrapping, adjust curwin->w_wrow */ - if (curwin->w_wcol >= curwin->w_width) { - /* this same formula is used in validate_cursor_col() */ - n = (curwin->w_wcol - curwin->w_width) / width + 1; + // long line wrapping, adjust curwin->w_wrow + if (curwin->w_wcol >= curwin->w_grid.Columns) { + // this same formula is used in validate_cursor_col() + n = (curwin->w_wcol - curwin->w_grid.Columns) / width + 1; curwin->w_wcol -= n * width; curwin->w_wrow += n; @@ -800,7 +809,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_width + (int)p_siso + 1; + endcol - curwin->w_leftcol - curwin->w_grid.Columns + (int)p_siso + 1; if (off_left < 0 || off_right > 0) { int diff = (off_left < 0) ? -off_left: off_right; @@ -843,17 +852,16 @@ void curs_columns( prev_skipcol = curwin->w_skipcol; int p_lines = 0; - if ((curwin->w_wrow >= curwin->w_height + if ((curwin->w_wrow >= curwin->w_grid.Rows || ((prev_skipcol > 0 - || curwin->w_wrow + p_so >= curwin->w_height) + || curwin->w_wrow + p_so >= curwin->w_grid.Rows) && (p_lines = - plines_win_nofill - (curwin, curwin->w_cursor.lnum, false)) - - 1 >= curwin->w_height)) - && curwin->w_height != 0 + plines_win_nofill(curwin, curwin->w_cursor.lnum, false)) - 1 + >= curwin->w_grid.Rows)) + && curwin->w_grid.Rows != 0 && curwin->w_cursor.lnum == curwin->w_topline && width > 0 - && curwin->w_width != 0 + && curwin->w_grid.Columns != 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 @@ -875,19 +883,22 @@ void curs_columns( } else n = p_lines; - if ((colnr_T)n >= curwin->w_height + curwin->w_skipcol / width) + if ((colnr_T)n >= curwin->w_grid.Rows + curwin->w_skipcol / width) { extra += 2; + } if (extra == 3 || p_lines < p_so * 2) { - /* not enough room for 'scrolloff', put cursor in the middle */ + // not enough room for 'scrolloff', put cursor in the middle n = curwin->w_virtcol / width; - if (n > curwin->w_height / 2) - n -= curwin->w_height / 2; - else + if (n > curwin->w_grid.Rows / 2) { + n -= curwin->w_grid.Rows / 2; + } else { n = 0; - /* don't skip more than necessary */ - if (n > p_lines - curwin->w_height + 1) - n = p_lines - curwin->w_height + 1; + } + // don't skip more than necessary + if (n > p_lines - curwin->w_grid.Rows + 1) { + n = p_lines - curwin->w_grid.Rows + 1; + } curwin->w_skipcol = n * width; } else if (extra == 1) { /* less then 'scrolloff' lines above, decrease skipcol */ @@ -900,18 +911,20 @@ void curs_columns( curwin->w_skipcol -= extra * width; } } else if (extra == 2) { - /* less then 'scrolloff' lines below, increase skipcol */ - endcol = (n - curwin->w_height + 1) * width; - while (endcol > curwin->w_virtcol) + // less then 'scrolloff' lines below, increase skipcol + endcol = (n - curwin->w_grid.Rows + 1) * width; + while (endcol > curwin->w_virtcol) { endcol -= width; - if (endcol > curwin->w_skipcol) + } + if (endcol > curwin->w_skipcol) { curwin->w_skipcol = endcol; + } } curwin->w_wrow -= curwin->w_skipcol / width; - if (curwin->w_wrow >= curwin->w_height) { - /* small window, make sure cursor is in it */ - extra = curwin->w_wrow - curwin->w_height + 1; + if (curwin->w_wrow >= curwin->w_grid.Rows) { + // small window, make sure cursor is in it + extra = curwin->w_wrow - curwin->w_grid.Rows + 1; curwin->w_skipcol += extra * width; curwin->w_wrow -= extra; } @@ -953,9 +966,9 @@ 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_height - 1) { - ++curwin->w_topfill; - ++done; + && curwin->w_topfill < curwin->w_grid.Rows - 1) { + curwin->w_topfill++; + done++; } else { if (curwin->w_topline == 1) break; @@ -988,15 +1001,15 @@ scrolldown ( */ int wrow = curwin->w_wrow; if (curwin->w_p_wrap - && curwin->w_width != 0 + && curwin->w_grid.Columns != 0 ) { validate_virtcol(); validate_cheight(); wrow += curwin->w_cline_height - 1 - - curwin->w_virtcol / curwin->w_width; + curwin->w_virtcol / curwin->w_grid.Columns; } bool moved = false; - while (wrow >= curwin->w_height && curwin->w_cursor.lnum > 1) { + while (wrow >= curwin->w_grid.Rows && curwin->w_cursor.lnum > 1) { linenr_T first; if (hasFolding(curwin->w_cursor.lnum, &first, NULL)) { --wrow; @@ -1081,14 +1094,15 @@ check_topfill ( { if (wp->w_topfill > 0) { int n = plines_win_nofill(wp, wp->w_topline, true); - if (wp->w_topfill + n > wp->w_height) { + if (wp->w_topfill + n > wp->w_grid.Rows) { if (down && wp->w_topline > 1) { --wp->w_topline; wp->w_topfill = 0; } else { - wp->w_topfill = wp->w_height - n; - if (wp->w_topfill < 0) + wp->w_topfill = wp->w_grid.Rows - n; + if (wp->w_topfill < 0) { wp->w_topfill = 0; + } } } } @@ -1101,12 +1115,13 @@ check_topfill ( static void max_topfill(void) { int n = plines_nofill(curwin->w_topline); - if (n >= curwin->w_height) + if (n >= curwin->w_grid.Rows) { curwin->w_topfill = 0; - else { + } else { curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline); - if (curwin->w_topfill + n > curwin->w_height) - curwin->w_topfill = curwin->w_height - n; + if (curwin->w_topfill + n > curwin->w_grid.Rows) { + curwin->w_topfill = curwin->w_grid.Rows - n; + } } } @@ -1137,14 +1152,14 @@ void scrolldown_clamp(void) else end_row += plines_nofill(curwin->w_topline - 1); if (curwin->w_p_wrap - && curwin->w_width != 0 + && curwin->w_grid.Columns != 0 ) { validate_cheight(); validate_virtcol(); end_row += curwin->w_cline_height - 1 - - curwin->w_virtcol / curwin->w_width; + curwin->w_virtcol / curwin->w_grid.Columns; } - if (end_row < curwin->w_height - p_so) { + if (end_row < curwin->w_grid.Rows - p_so) { if (can_fill) { ++curwin->w_topfill; check_topfill(curwin, true); @@ -1179,10 +1194,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_width != 0 + && curwin->w_grid.Columns != 0 ) { validate_virtcol(); - start_row -= curwin->w_virtcol / curwin->w_width; + start_row -= curwin->w_virtcol / curwin->w_grid.Columns; } if (start_row >= p_so) { if (curwin->w_topfill > 0) @@ -1336,10 +1351,12 @@ void scroll_cursor_top(int min_scroll, int always) else used += plines(bot); } - if (used > curwin->w_height) + if (used > curwin->w_grid.Rows) { break; - if (top < curwin->w_topline) + } + if (top < curwin->w_topline) { scrolled += i; + } /* * If scrolling is needed, scroll at least 'sj' lines. @@ -1359,7 +1376,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_height) { + if (used > curwin->w_grid.Rows) { scroll_cursor_halfway(false); } else { /* @@ -1393,10 +1410,10 @@ void scroll_cursor_top(int min_scroll, int always) void set_empty_rows(win_T *wp, int used) { wp->w_filler_rows = 0; - if (used == 0) - wp->w_empty_rows = 0; /* single line that doesn't fit */ - else { - wp->w_empty_rows = wp->w_height - 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; 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) @@ -1439,8 +1456,9 @@ 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_height) + if (loff.height == MAXCOL || used + loff.height > curwin->w_grid.Rows) { break; + } used += loff.height; curwin->w_topfill = loff.fill; } @@ -1497,12 +1515,14 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) /* Add one line above */ topline_back(&loff); - if (loff.height == MAXCOL) + if (loff.height == MAXCOL) { used = MAXCOL; - else + } else { used += loff.height; - if (used > curwin->w_height) + } + if (used > curwin->w_grid.Rows) { break; + } if (loff.lnum >= curwin->w_botline && (loff.lnum > curwin->w_botline || loff.fill <= fill_below_window) @@ -1519,8 +1539,9 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) /* Add one line below */ botline_forw(&boff); used += boff.height; - if (used > curwin->w_height) + if (used > curwin->w_grid.Rows) { break; + } if (extra < ( mouse_dragging > 0 ? mouse_dragging - 1 : p_so) || scrolled < min_scroll) { @@ -1541,14 +1562,14 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) } linenr_T line_count; - /* curwin->w_empty_rows is larger, no need to scroll */ - if (scrolled <= 0) + // curwin->w_empty_rows is larger, no need to scroll + if (scrolled <= 0) { line_count = 0; - /* more than a screenfull, don't scroll but redraw */ - else if (used > curwin->w_height) + // more than a screenfull, don't scroll but redraw + } else if (used > curwin->w_grid.Rows) { line_count = used; - /* scroll minimal number of lines */ - else { + // scroll minimal number of lines + } else { line_count = 0; boff.fill = curwin->w_topfill; boff.lnum = curwin->w_topline - 1; @@ -1566,10 +1587,11 @@ 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_height && line_count > min_scroll) + if (line_count >= curwin->w_grid.Rows && line_count > min_scroll) { scroll_cursor_halfway(false); - else + } else { scrollup(line_count, true); + } /* * If topline didn't change we need to restore w_botline and w_empty_rows @@ -1608,8 +1630,9 @@ 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_height) + if (used > curwin->w_grid.Rows) { break; + } below += boff.height; } else { ++below; /* count a "~" line */ @@ -1624,8 +1647,9 @@ void scroll_cursor_halfway(int atend) used = MAXCOL; else used += loff.height; - if (used > curwin->w_height) + if (used > curwin->w_grid.Rows) { break; + } above += loff.height; topline = loff.lnum; topfill = loff.fill; @@ -1634,8 +1658,9 @@ 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_height) + if (old_topline > curwin->w_topline + curwin->w_grid.Rows) { curwin->w_botfill = false; + } check_topfill(curwin, false); curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); curwin->w_valid |= VALID_TOPLINE; @@ -1662,18 +1687,20 @@ void cursor_correct(void) } if (curwin->w_topline == 1) { above_wanted = 0; - int max_off = curwin->w_height / 2; - if (below_wanted > max_off) + int max_off = curwin->w_grid.Rows / 2; + if (below_wanted > max_off) { below_wanted = max_off; + } } validate_botline(); if (curwin->w_botline == curbuf->b_ml.ml_line_count + 1 && mouse_dragging == 0 ) { below_wanted = 0; - int max_off = (curwin->w_height - 1) / 2; - if (above_wanted > max_off) + int max_off = (curwin->w_grid.Rows - 1) / 2; + if (above_wanted > max_off) { above_wanted = max_off; + } } /* @@ -1844,7 +1871,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_height && loff.lnum >= 1) { + while (n <= curwin->w_grid.Rows && loff.lnum >= 1) { topline_back(&loff); if (loff.height == MAXCOL) n = MAXCOL; @@ -1935,7 +1962,7 @@ int onepage(Direction dir, long count) */ static void get_scroll_overlap(lineoff_T *lp, int dir) { - int min_height = curwin->w_height - 2; + int min_height = curwin->w_grid.Rows - 2; if (lp->fill > 0) lp->height = 1; @@ -1989,12 +2016,13 @@ void halfpage(bool flag, linenr_T Prenum) long scrolled = 0; int i; - if (Prenum) - curwin->w_p_scr = (Prenum > curwin->w_height) ? - curwin->w_height : Prenum; + if (Prenum) { + curwin->w_p_scr = (Prenum > curwin->w_grid.Rows) ? curwin->w_grid.Rows + : Prenum; + } assert(curwin->w_p_scr <= INT_MAX); - int n = curwin->w_p_scr <= curwin->w_height ? (int)curwin->w_p_scr - : curwin->w_height; + int n = curwin->w_p_scr <= curwin->w_grid.Rows ? (int)curwin->w_p_scr + : curwin->w_grid.Rows; update_topline(); validate_botline(); diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 46a9e95b91..76fbe407c2 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -495,8 +495,8 @@ static void broadcast_event(const char *name, Array args) kv_size(subscribed)); for (size_t i = 0; i < kv_size(subscribed); i++) { - Channel *channel = kv_A(subscribed, i); - channel_write(channel, buffer); + Channel *c = kv_A(subscribed, i); + channel_write(c, buffer); } end: diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 38ee0936aa..29c5d27258 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1,11 +1,11 @@ // 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 -/* - * normal.c: Contains the main routine for processing characters in command - * mode. Communicates closely with the code in ops.c to handle - * the operators. - */ +// +// normal.c: Contains the main routine for processing characters in command +// mode. Communicates closely with the code in ops.c to handle +// the operators. +// #include <assert.h> #include <inttypes.h> @@ -1445,8 +1445,10 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) oap->motion_type = kMTCharWise; } else if (oap->motion_force == Ctrl_V) { // Change line- or characterwise motion into Visual block mode. - VIsual_active = true; - VIsual = oap->start; + if (!VIsual_active) { + VIsual_active = true; + VIsual = oap->start; + } VIsual_mode = Ctrl_V; VIsual_select = false; VIsual_reselect = false; @@ -2039,6 +2041,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) curwin->w_cursor = old_cursor; } clearop(oap); + motion_force = NUL; } curwin->w_p_lbr = lbr_saved; } @@ -2761,10 +2764,9 @@ do_mouse ( } else if ((mod_mask & MOD_MASK_MULTI_CLICK) && (State & (NORMAL | INSERT)) && mouse_has(MOUSE_VISUAL)) { if (is_click || !VIsual_active) { - if (VIsual_active) + if (VIsual_active) { orig_cursor = VIsual; - else { - check_visual_highlight(); + } else { VIsual = curwin->w_cursor; orig_cursor = VIsual; VIsual_active = true; @@ -2935,22 +2937,6 @@ static int get_mouse_class(char_u *p) } /* - * Check if highlighting for visual mode is possible, give a warning message - * if not. - */ -void check_visual_highlight(void) -{ - static bool did_check = false; - - if (full_screen) { - if (!did_check && HL_ATTR(HLF_V) == 0) { - MSG(_("Warning: terminal cannot highlight")); - } - did_check = true; - } -} - -/* * End Visual mode. * This function should ALWAYS be called to end Visual mode, except from * do_pending_operator(). @@ -3466,10 +3452,10 @@ static void display_showcmd(void) int len; len = (int)STRLEN(showcmd_buf); - if (len == 0) + if (len == 0) { showcmd_is_clear = true; - else { - screen_puts(showcmd_buf, (int)Rows - 1, sc_col, 0); + } else { + grid_puts(&default_grid, showcmd_buf, (int)Rows - 1, sc_col, 0); showcmd_is_clear = false; } @@ -3477,7 +3463,8 @@ static void display_showcmd(void) * clear the rest of an old message by outputting up to SHOWCMD_COLS * spaces */ - screen_puts((char_u *)" " + len, (int)Rows - 1, sc_col + len, 0); + grid_puts(&default_grid, (char_u *)" " + len, (int)Rows - 1, + sc_col + len, 0); setcursor(); /* put cursor back where it belongs */ } @@ -3905,18 +3892,16 @@ 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_width - col_off1; - width2 = curwin->w_width - col_off2; + width1 = curwin->w_grid.Columns - col_off1; + width2 = curwin->w_grid.Columns - col_off2; if (width2 == 0) { width2 = 1; // Avoid divide by zero. } - if (curwin->w_width != 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_grid.Columns != 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) { atend = true; validate_virtcol(); @@ -3957,9 +3942,11 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) (void)hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL); linelen = linetabsize(get_cursor_line_ptr()); - if (linelen > width1) - curwin->w_curswant += (((linelen - width1 - 1) / width2) - + 1) * width2; + if (linelen > width1) { + int w = (((linelen - width1 - 1) / width2) + 1) * width2; + assert(curwin->w_curswant <= INT_MAX - w); + curwin->w_curswant += w; + } } } else { /* dir == FORWARD */ if (linelen > width1) @@ -4255,7 +4242,7 @@ dozet: /* "zH" - scroll screen right half-page */ case 'H': - cap->count1 *= curwin->w_width / 2; + cap->count1 *= curwin->w_grid.Columns / 2; FALLTHROUGH; /* "zh" - scroll screen to the right */ @@ -4270,8 +4257,8 @@ dozet: } break; - /* "zL" - scroll screen left half-page */ - case 'L': cap->count1 *= curwin->w_width / 2; + // "zL" - scroll screen left half-page + case 'L': cap->count1 *= curwin->w_grid.Columns / 2; FALLTHROUGH; /* "zl" - scroll screen to the left */ @@ -4307,11 +4294,12 @@ dozet: col = 0; /* like the cursor is in col 0 */ else getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col); - n = curwin->w_width - curwin_col_off(); - if (col + l_p_siso < n) + n = curwin->w_grid.Columns - curwin_col_off(); + if (col + l_p_siso < n) { col = 0; - else + } else { col = col + l_p_siso - n + 1; + } if (curwin->w_leftcol != col) { curwin->w_leftcol = col; redraw_later(NOT_VALID); @@ -5016,11 +5004,11 @@ static void nv_scroll(cmdarg_T *cap) /* Don't count filler lines above the window. */ used -= diff_check_fill(curwin, curwin->w_topline) - curwin->w_topfill; - validate_botline(); /* make sure w_empty_rows is valid */ - half = (curwin->w_height - 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". */ + validate_botline(); // make sure w_empty_rows is valid + half = (curwin->w_grid.Rows - 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". if (n > 0 && used + diff_check_fill(curwin, curwin->w_topline + n) / 2 >= half) { --n; @@ -5032,9 +5020,10 @@ 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_height) - --n; - } else { /* (cap->cmdchar == 'H') */ + if (n > 0 && used > curwin->w_grid.Rows) { + n--; + } + } else { // (cap->cmdchar == 'H') n = cap->count1 - 1; if (hasAnyFolding(curwin)) { /* Count a fold for one screen line. */ @@ -6403,8 +6392,8 @@ static void nv_visual(cmdarg_T *cap) /* 'v', 'V' and CTRL-V can be used while an operator is pending to make it * characterwise, linewise, or blockwise. */ if (cap->oap->op_type != OP_NOP) { - cap->oap->motion_force = cap->cmdchar; - finish_op = false; /* operator doesn't finish now but later */ + motion_force = cap->oap->motion_force = cap->cmdchar; + finish_op = false; // operator doesn't finish now but later return; } @@ -6417,9 +6406,8 @@ static void nv_visual(cmdarg_T *cap) VIsual_mode = cap->cmdchar; showmode(); } - redraw_curbuf_later(INVERTED); /* update the inversion */ - } else { /* start Visual mode */ - check_visual_highlight(); + redraw_curbuf_later(INVERTED); // update the inversion + } else { // start Visual mode if (cap->count0 > 0 && resel_VIsual_mode != NUL) { /* use previously selected part */ VIsual = curwin->w_cursor; @@ -6747,9 +6735,9 @@ static void nv_g_cmd(cmdarg_T *cap) oap->motion_type = kMTCharWise; oap->inclusive = false; if (curwin->w_p_wrap - && curwin->w_width != 0 + && curwin->w_grid.Columns != 0 ) { - int width1 = curwin->w_width - curwin_col_off(); + int width1 = curwin->w_grid.Columns - curwin_col_off(); int width2 = width1 + curwin_col_off2(); validate_virtcol(); @@ -6761,10 +6749,11 @@ static void nv_g_cmd(cmdarg_T *cap) /* Go to the middle of the screen line. When 'number' or * 'relativenumber' is on and lines are wrapping the middle can be more * to the left. */ - if (cap->nchar == 'm') - i += (curwin->w_width - curwin_col_off() + if (cap->nchar == 'm') { + i += (curwin->w_grid.Columns - curwin_col_off() + ((curwin->w_p_wrap && i > 0) ? curwin_col_off2() : 0)) / 2; + } coladvance((colnr_T)i); if (flag) { do @@ -6808,11 +6797,11 @@ static void nv_g_cmd(cmdarg_T *cap) oap->motion_type = kMTCharWise; oap->inclusive = true; if (curwin->w_p_wrap - && curwin->w_width != 0 + && curwin->w_grid.Columns != 0 ) { curwin->w_curswant = MAXCOL; /* so we stay at the end */ if (cap->count1 == 1) { - int width1 = curwin->w_width - col_off; + int width1 = curwin->w_grid.Columns - col_off; int width2 = width1 + curwin_col_off2(); validate_virtcol(); @@ -6838,7 +6827,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_width - col_off - 1; + i = curwin->w_leftcol + curwin->w_grid.Columns - col_off - 1; coladvance((colnr_T)i); /* Make sure we stick in this column. */ @@ -7953,7 +7942,7 @@ static void get_op_vcol( colnr_T end; if (VIsual_mode != Ctrl_V - || (!initial && oap->end.col < curwin->w_width)) { + || (!initial && oap->end.col < curwin->w_grid.Columns)) { return; } diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 5a6e56299d..e9cb480647 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2670,7 +2670,6 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) colnr_T vcol; int delcount; int incr = 0; - long j; struct block_def bd; char_u **y_array = NULL; long nr_lines = 0; @@ -2840,16 +2839,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } if (curbuf->terminal) { - for (int i = 0; i < count; i++) { // -V756 - // feed the lines to the terminal - for (size_t j = 0; j < y_size; j++) { - if (j) { - // terminate the previous line - terminal_send(curbuf->terminal, "\n", 1); - } - terminal_send(curbuf->terminal, (char *)y_array[j], STRLEN(y_array[j])); - } - } + terminal_paste(count, y_array, y_size); return; } @@ -3035,12 +3025,14 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) yanklen = (int)STRLEN(y_array[i]); - /* calculate number of spaces required to fill right side of block*/ + // calculate number of spaces required to fill right side of block spaces = y_width + 1; - for (j = 0; j < yanklen; j++) + for (long j = 0; j < yanklen; j++) { spaces -= lbr_chartabsize(NULL, &y_array[i][j], 0); - if (spaces < 0) + } + if (spaces < 0) { spaces = 0; + } // insert the new text totlen = (size_t)(count * (yanklen + spaces) @@ -3050,21 +3042,21 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) ptr = newp; memmove(ptr, oldp, (size_t)bd.textcol); ptr += bd.textcol; - /* may insert some spaces before the new text */ + // may insert some spaces before the new text memset(ptr, ' ', (size_t)bd.startspaces); ptr += bd.startspaces; - /* insert the new text */ - for (j = 0; j < count; ++j) { + // insert the new text + for (long j = 0; j < count; j++) { memmove(ptr, y_array[i], (size_t)yanklen); ptr += yanklen; - /* insert block's trailing spaces only if there's text behind */ + // insert block's trailing spaces only if there's text behind if ((j < count - 1 || !shortline) && spaces) { memset(ptr, ' ', (size_t)spaces); ptr += spaces; } } - /* may insert some spaces after the new text */ + // may insert some spaces after the new text memset(ptr, ' ', (size_t)bd.endspaces); ptr += bd.endspaces; // move the text after the cursor to the end of the line. @@ -5697,12 +5689,12 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) // Timestamp is not saved for clipboard registers because clipboard registers // are not saved in the ShaDa file. - int i = 0; + size_t tv_idx = 0; TV_LIST_ITER_CONST(lines, li, { if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) { goto err; } - reg->y_array[i++] = (char_u *)xstrdupnul( + reg->y_array[tv_idx++] = (char_u *)xstrdupnul( (const char *)TV_LIST_ITEM_TV(li)->vval.v_string); }); diff --git a/src/nvim/option.c b/src/nvim/option.c index 8c692f9f42..6b1c5c998f 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -4322,6 +4322,10 @@ 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_pyx) { + if (p_pyx != 0 && p_pyx != 2 && p_pyx != 3) { + errmsg = e_invarg; + } } else if (pp == &p_ul || pp == &curbuf->b_p_ul) { // sync undo before 'undolevels' changes // use the old value, otherwise u_sync() may not work properly diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 6a0d0e32e0..7b99b6f266 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -516,6 +516,7 @@ EXTERN char_u *p_pex; // 'patchexpr' EXTERN char_u *p_pm; // 'patchmode' EXTERN char_u *p_path; // 'path' EXTERN char_u *p_cdpath; // 'cdpath' +EXTERN long p_pyx; // 'pyxversion' EXTERN long p_rdt; // 'redrawtime' EXTERN int p_remap; // 'remap' EXTERN long p_re; // 'regexpengine' diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 0cc6f58c5f..aba8f8b893 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -1810,6 +1810,14 @@ return { defaults={if_true={vi=0}} }, { + full_name='pyxversion', abbreviation='pyx', + type='number', scope={'global'}, + secure=true, + vi_def=true, + varname='p_pyx', + defaults={if_true={vi=0}} + }, + { full_name='quoteescape', abbreviation='qe', type='string', scope={'buffer'}, vi_def=true, diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index 68c8967b91..056770f2c0 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -68,12 +68,12 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed) int kind_width; int extra_width; int i; - int row; int context_lines; - int col; int above_row; int below_row; int redo_count = 0; + int row; + int col; if (!pum_is_visible) { // To keep the code simple, we only allow changing the @@ -90,11 +90,18 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed) below_row = cmdline_row; // anchor position: the start of the completed word - row = curwin->w_wrow + curwin->w_winrow; + row = curwin->w_wrow; if (curwin->w_p_rl) { - col = curwin->w_wincol + curwin->w_width - curwin->w_wcol - 1; + col = curwin->w_width - curwin->w_wcol - 1; } else { - col = curwin->w_wincol + curwin->w_wcol; + col = curwin->w_wcol; + } + + int grid = (int)curwin->w_grid.handle; + if (!ui_is_external(kUIMultigrid)) { + grid = (int)default_grid.handle; + row += curwin->w_winrow; + col += curwin->w_wincol; } if (pum_external) { @@ -108,7 +115,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed) ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_info))); ADD(arr, ARRAY_OBJ(item)); } - ui_call_popupmenu_show(arr, selected, row, col); + ui_call_popupmenu_show(arr, selected, row, col, grid); } else { ui_call_popupmenu_select(selected); } @@ -351,10 +358,10 @@ void pum_redraw(void) // prepend a space if there is room if (curwin->w_p_rl) { if (pum_col < curwin->w_wincol + curwin->w_width - 1) { - screen_putchar(' ', row, pum_col + 1, attr); + grid_putchar(&default_grid, ' ', row, pum_col + 1, attr); } } else if (pum_col > 0) { - screen_putchar(' ', row, pum_col - 1, attr); + grid_putchar(&default_grid, ' ', row, pum_col - 1, attr); } // Display each entry, use two spaces for a Tab. @@ -416,12 +423,13 @@ void pum_redraw(void) size++; } } - screen_puts_len(rt, (int)STRLEN(rt), row, col - size + 1, attr); + grid_puts_len(&default_grid, rt, (int)STRLEN(rt), row, + col - size + 1, attr); xfree(rt_start); xfree(st); col -= width; } else { - screen_puts_len(st, (int)STRLEN(st), row, col, attr); + grid_puts_len(&default_grid, st, (int)STRLEN(st), row, col, attr); xfree(st); col += width; } @@ -432,10 +440,11 @@ void pum_redraw(void) // Display two spaces for a Tab. if (curwin->w_p_rl) { - screen_puts_len((char_u *)" ", 2, row, col - 1, attr); + grid_puts_len(&default_grid, (char_u *)" ", 2, row, col - 1, + attr); col -= 2; } else { - screen_puts_len((char_u *)" ", 2, row, col, attr); + grid_puts_len(&default_grid, (char_u *)" ", 2, row, col, attr); col += 2; } totwidth += 2; @@ -466,36 +475,37 @@ void pum_redraw(void) } if (curwin->w_p_rl) { - screen_fill(row, row + 1, pum_col - pum_base_width - n + 1, - col + 1, ' ', ' ', attr); + grid_fill(&default_grid, row, row + 1, pum_col - pum_base_width - n + 1, + col + 1, ' ', ' ', attr); col = pum_col - pum_base_width - n + 1; } else { - screen_fill(row, row + 1, col, pum_col + pum_base_width + n, - ' ', ' ', attr); + grid_fill(&default_grid, row, row + 1, col, + pum_col + pum_base_width + n, ' ', ' ', attr); col = pum_col + pum_base_width + n; } totwidth = pum_base_width + n; } if (curwin->w_p_rl) { - screen_fill(row, row + 1, pum_col - pum_width + 1, col + 1, ' ', ' ', - attr); + grid_fill(&default_grid, row, row + 1, pum_col - pum_width + 1, col + 1, + ' ', ' ', attr); } else { - screen_fill(row, row + 1, col, pum_col + pum_width, ' ', ' ', attr); + grid_fill(&default_grid, row, row + 1, col, pum_col + pum_width, ' ', ' ', + attr); } if (pum_scrollbar > 0) { if (curwin->w_p_rl) { - screen_putchar(' ', row, pum_col - pum_width, - i >= thumb_pos && i < thumb_pos + thumb_heigth - ? attr_thumb : attr_scroll); + grid_putchar(&default_grid, ' ', row, pum_col - pum_width, + i >= thumb_pos && i < thumb_pos + thumb_heigth + ? attr_thumb : attr_scroll); } else { - screen_putchar(' ', row, pum_col + pum_width, - i >= thumb_pos && i < thumb_pos + thumb_heigth - ? attr_thumb : attr_scroll); + grid_putchar(&default_grid, ' ', row, pum_col + pum_width, + i >= thumb_pos && i < thumb_pos + thumb_heigth + ? attr_thumb : attr_scroll); } } - screen_puts_line_flush(false); + grid_puts_line_flush(&default_grid, false); row++; } } diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index d9e307bb71..4eeddf1d5a 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -925,11 +925,11 @@ restofline: return QF_FAIL; } if (*fields->errmsg) { - size_t len = STRLEN(qfprev->qf_text); + size_t textlen = STRLEN(qfprev->qf_text); qfprev->qf_text = xrealloc(qfprev->qf_text, - len + STRLEN(fields->errmsg) + 2); - qfprev->qf_text[len] = '\n'; - STRCPY(qfprev->qf_text + len + 1, fields->errmsg); + textlen + STRLEN(fields->errmsg) + 2); + qfprev->qf_text[textlen] = '\n'; + STRCPY(qfprev->qf_text + textlen + 1, fields->errmsg); } if (qfprev->qf_nr == -1) { qfprev->qf_nr = fields->enr; diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index d62a009fbc..0b9e1cfdec 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -4920,7 +4920,7 @@ regmatch ( } } else { const char_u *const line = - reg_getline(behind_pos.rs_u.pos.lnum); + reg_getline(rp->rs_un.regsave.rs_u.pos.lnum); rp->rs_un.regsave.rs_u.pos.col -= utf_head_off(line, diff --git a/src/nvim/screen.c b/src/nvim/screen.c index d095bdb7c8..513f8dac9a 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -7,8 +7,7 @@ // by remembering what is already on the screen, and only updating the parts // that changed. // -// The screen_*() functions write to the screen and handle updating -// ScreenLines[]. +// The grid_*() functions write to the screen and handle updating grid->lines[]. // // update_screen() is the function that updates all windows and status lines. // It is called from the main loop when must_redraw is non-zero. It may be @@ -74,6 +73,7 @@ #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/cursor.h" +#include "nvim/cursor_shape.h" #include "nvim/diff.h" #include "nvim/eval.h" #include "nvim/ex_cmds.h" @@ -119,17 +119,21 @@ #define MB_FILLER_CHAR '<' /* character used when a double-width character * doesn't fit. */ #define W_ENDCOL(wp) (wp->w_wincol + wp->w_width) +#define W_ENDROW(wp) (wp->w_winrow + wp->w_height) + + +// temporary buffer for rendering a single screenline, so it can be +// comparared with previous contents to calulate smallest delta. +static size_t linebuf_size = 0; +static schar_T *linebuf_char = NULL; +static sattr_T *linebuf_attr = NULL; static match_T search_hl; /* used for 'hlsearch' highlight matching */ static foldinfo_T win_foldinfo; /* info for 'foldcolumn' */ -/* - * Buffer for one screen line (characters and attributes). - */ -static schar_T *current_ScreenLine; - StlClickDefinition *tab_page_click_defs = NULL; + long tab_page_click_defs_size = 0; // for line_putchar. Contains the state that needs to be remembered from @@ -141,6 +145,12 @@ typedef struct { } LineState; #define LINE_STATE(p) { p, 0, 0 } +/// Whether to call "ui_call_grid_resize" in win_grid_alloc +static bool send_grid_resize = false; + +/// Highlight ids are no longer valid. Force retransmission +static bool highlights_invalid = false; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "screen.c.generated.h" #endif @@ -175,6 +185,16 @@ void redraw_all_later(int type) FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { redraw_win_later(wp, type); } + // This may be needed when switching tabs. + if (must_redraw < type) { + must_redraw = type; + } +} + +void screen_invalidate_highlights(void) +{ + redraw_all_later(NOT_VALID); + highlights_invalid = true; } /* @@ -194,6 +214,15 @@ void redraw_buf_later(buf_T *buf, int type) } } +void redraw_buf_line_later(buf_T *buf, linenr_T line) +{ + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->w_buffer == buf) { + redrawWinline(wp, line); + } + } +} + /* * Changed something in the current window, at buffer line "lnum", that * requires that line and possibly other lines to be redrawn. @@ -205,12 +234,11 @@ void redraw_buf_later(buf_T *buf, int type) void redrawWinline( win_T *wp, - linenr_T lnum, - int invalid /* window line height is invalid now */ + linenr_T lnum ) { - int i; - + if (lnum >= wp->w_topline + && lnum < wp->w_botline) { if (wp->w_redraw_top == 0 || wp->w_redraw_top > lnum) { wp->w_redraw_top = lnum; } @@ -218,13 +246,6 @@ redrawWinline( wp->w_redraw_bot = lnum; } redraw_win_later(wp, VALID); - - if (invalid) { - // A w_lines[] entry for this lnum has become invalid. - i = find_wl_entry(wp, lnum); - if (i >= 0) { - wp->w_lines[i].wl_valid = false; - } } } @@ -237,12 +258,12 @@ void update_curbuf(int type) update_screen(type); } -/* - * update_screen() - * - * Based on the current value of curwin->w_topline, transfer a screenfull - * of stuff from Filemem to ScreenLines[], and update curwin->w_botline. - */ +/// Redraw the parts of the screen that is marked for redraw. +/// +/// Most code shouldn't call this directly, rather use redraw_later() and +/// and redraw_all_later() to mark parts of the screen as needing a redraw. +/// +/// @param type set to a NOT_VALID to force redraw of entire screen void update_screen(int type) { static int did_intro = FALSE; @@ -285,6 +306,7 @@ void update_screen(int type) * if the screen was scrolled up when displaying a message, scroll it down */ if (msg_scrolled) { + ui_call_win_scroll_over_reset(); clear_cmdline = true; if (dy_flags & DY_MSGSEP) { int valid = MAX(Rows - msg_scrollsize(), 0); @@ -292,25 +314,25 @@ void update_screen(int type) redraw_tabline = true; } FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_winrow + wp->w_height > valid) { + if (W_ENDROW(wp) > valid) { wp->w_redr_type = NOT_VALID; wp->w_lines_valid = 0; } - if (wp->w_winrow + wp->w_height + wp->w_status_height > valid) { + if (W_ENDROW(wp) + wp->w_status_height > valid) { wp->w_redr_status = true; } } - } else if (msg_scrolled > Rows - 5) { // clearing is faster + } else if (msg_scrolled > default_grid.Rows - 5) { // clearing is faster type = CLEAR; } else if (type != CLEAR) { check_for_delay(false); - if (screen_ins_lines(0, msg_scrolled, (int)Rows, 0, (int)Columns) - == FAIL) { + if (grid_ins_lines(&default_grid, 0, msg_scrolled, (int)Rows, + 0, (int)Columns) == FAIL) { type = CLEAR; } FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_winrow < msg_scrolled) { - if (wp->w_winrow + wp->w_height > msg_scrolled + if (W_ENDROW(wp) > msg_scrolled && wp->w_redr_type < REDRAW_TOP && wp->w_lines_valid > 0 && wp->w_topline == wp->w_lines[0].wl_lnum) { @@ -318,7 +340,7 @@ void update_screen(int type) wp->w_redr_type = REDRAW_TOP; } else { wp->w_redr_type = NOT_VALID; - if (wp->w_winrow + wp->w_height + wp->w_status_height + if (W_ENDROW(wp) + wp->w_status_height <= msg_scrolled) { wp->w_redr_status = TRUE; } @@ -345,6 +367,8 @@ void update_screen(int type) type = NOT_VALID; // must_redraw may be set indirectly, avoid another redraw later must_redraw = 0; + } else if (highlights_invalid) { + grid_invalidate(&default_grid); } if (clear_cmdline) /* going to clear cmdline (done below) */ @@ -430,6 +454,8 @@ void update_screen(int type) win_redr_status(wp, true); // any popup menu will be redrawn below } } + send_grid_resize = false; + highlights_invalid = false; end_search_hl(); // May need to redraw the popup menu. if (pum_drawn()) { @@ -456,6 +482,25 @@ void update_screen(int type) } +// Prepare for updating one or more windows. +// Caller must check for "updating_screen" already set to avoid recursiveness. +static void update_prepare(void) +{ + updating_screen = true; + start_search_hl(); +} + +// Finish updating one or more windows. +static void update_finish(void) +{ + if (redraw_cmdline) { + showmode(); + } + + end_search_hl(); + updating_screen = false; +} + /* * Return TRUE if the cursor line in window "wp" may be concealed, according * to the 'concealcursor' option. @@ -498,107 +543,33 @@ void update_single_line(win_T *wp, linenr_T lnum) int j; // Don't do anything if the screen structures are (not yet) valid. - if (!screen_valid(true) || updating_screen) { + if (linebuf_char == NULL || updating_screen) { return; } - updating_screen = true; if (lnum >= wp->w_topline && lnum < wp->w_botline && foldedCount(wp, lnum, &win_foldinfo) == 0) { + update_prepare(); + row = 0; for (j = 0; j < wp->w_lines_valid; ++j) { if (lnum == wp->w_lines[j].wl_lnum) { init_search_hl(wp); - start_search_hl(); prepare_search_hl(wp, lnum); update_window_hl(wp, false); + // allocate window grid if not already + win_grid_alloc(wp); win_line(wp, lnum, row, row + wp->w_lines[j].wl_size, false, false); - end_search_hl(); break; } row += wp->w_lines[j].wl_size; } + + update_finish(); } need_cursor_line_redraw = false; - updating_screen = false; -} - - -/* - * Prepare for updating one or more windows. - * Caller must check for "updating_screen" already set to avoid recursiveness. - */ -static void update_prepare(void) -{ - updating_screen = TRUE; - start_search_hl(); } -/* - * Finish updating one or more windows. - */ -static void update_finish(void) -{ - if (redraw_cmdline) { - showmode(); - } - - end_search_hl(); - updating_screen = FALSE; -} - -void update_debug_sign(const buf_T *const buf, const linenr_T lnum) -{ - bool doit = false; - win_foldinfo.fi_level = 0; - - // update/delete a specific mark - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (buf != NULL && lnum > 0) { - if (wp->w_buffer == buf && lnum >= wp->w_topline - && lnum < wp->w_botline) { - if (wp->w_redraw_top == 0 || wp->w_redraw_top > lnum) { - wp->w_redraw_top = lnum; - } - if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum) { - wp->w_redraw_bot = lnum; - } - redraw_win_later(wp, VALID); - } - } else { - redraw_win_later(wp, VALID); - } - if (wp->w_redr_type != 0) { - doit = true; - } - } - - // Return when there is nothing to do, screen updating is already - // happening (recursive call), messages on the screen or still starting up. - if (!doit - || updating_screen - || State == ASKMORE - || State == HITRETURN - || msg_scrolled - || starting) { - return; - } - - // update all windows that need updating - update_prepare(); - - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_redr_type != 0) { - update_window_hl(wp, wp->w_redr_type >= NOT_VALID); - win_update(wp); - } - if (wp->w_redr_status) { - win_redr_status(wp, false); - } - } - - update_finish(); -} /* * Update a single window. @@ -656,11 +627,10 @@ static void win_update(win_T *wp) static int recursive = FALSE; /* being called recursively */ int old_botline = wp->w_botline; long fold_count; - /* remember what happened to the previous line, to know if - * check_visual_highlight() can be used */ -#define DID_NONE 1 /* didn't update a line */ -#define DID_LINE 2 /* updated a normal line */ -#define DID_FOLD 3 /* updated a folded line */ + // Remember what happened to the previous line. +#define DID_NONE 1 // didn't update a line +#define DID_LINE 2 // updated a normal line +#define DID_FOLD 3 // updated a folded line int did_update = DID_NONE; linenr_T syntax_last_parsed = 0; /* last parsed text line */ linenr_T mod_top = 0; @@ -669,20 +639,22 @@ static void win_update(win_T *wp) type = wp->w_redr_type; + win_grid_alloc(wp); + if (type >= NOT_VALID) { wp->w_redr_status = true; wp->w_lines_valid = 0; } - /* Window is zero-height: nothing to draw. */ - if (wp->w_height == 0) { + // Window is zero-height: nothing to draw. + if (wp->w_grid.Rows == 0) { wp->w_redr_type = 0; return; } - /* Window is zero-width: Only need to draw the separator. */ - if (wp->w_width == 0) { - /* draw the vertical separator right of this window */ + // Window is zero-width: Only need to draw the separator. + if (wp->w_grid.Columns == 0) { + // draw the vertical separator right of this window draw_vsep_win(wp, 0); wp->w_redr_type = 0; return; @@ -699,8 +671,8 @@ static void win_update(win_T *wp) if (buf->terminal) { terminal_resize(buf->terminal, - (uint16_t)(MAX(0, wp->w_width - win_col_off(wp))), - (uint16_t)wp->w_height); + (uint16_t)(MAX(0, wp->w_grid.Columns - win_col_off(wp))), + (uint16_t)wp->w_grid.Rows); } } else if (buf->b_mod_set && buf->b_mod_xlines != 0 @@ -870,21 +842,22 @@ static void win_update(win_T *wp) /* count the number of lines we are off, counting a sequence * of folded lines as one */ j = 0; - for (ln = wp->w_topline; ln < wp->w_lines[0].wl_lnum; ++ln) { - ++j; - if (j >= wp->w_height - 2) + for (ln = wp->w_topline; ln < wp->w_lines[0].wl_lnum; ln++) { + j++; + if (j >= wp->w_grid.Rows - 2) { break; + } (void)hasFoldingWin(wp, ln, NULL, &ln, true, NULL); } } else j = wp->w_lines[0].wl_lnum - wp->w_topline; - if (j < wp->w_height - 2) { /* not too far off */ + if (j < wp->w_grid.Rows - 2) { // not too far off i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1); /* insert extra lines for previously invisible filler lines */ if (wp->w_lines[0].wl_lnum != wp->w_topline) i += diff_check_fill(wp, wp->w_lines[0].wl_lnum) - wp->w_old_topfill; - if (i < wp->w_height - 2) { // less than a screen off + if (i < wp->w_grid.Rows - 2) { // less than a screen off // Try to insert the correct number of lines. // If not the last window, delete the lines at the bottom. // win_ins_lines may fail when the terminal can't do it. @@ -897,12 +870,15 @@ static void win_update(win_T *wp) /* Move the entries that were scrolled, disable * the entries for the lines to be redrawn. */ - if ((wp->w_lines_valid += j) > wp->w_height) - wp->w_lines_valid = wp->w_height; - for (idx = wp->w_lines_valid; idx - j >= 0; idx--) + 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; + } + while (idx >= 0) { + wp->w_lines[idx--].wl_valid = false; + } } } else mid_start = 0; /* redraw all lines */ @@ -947,7 +923,7 @@ static void win_update(win_T *wp) row -= wp->w_topfill; if (row > 0) { if (win_del_lines(wp, 0, row) == OK) { - bot_start = wp->w_height - row; + bot_start = wp->w_grid.Rows - row; } else { mid_start = 0; // redraw all lines } @@ -966,7 +942,7 @@ static void win_update(win_T *wp) /* stop at line that didn't fit, unless it is still * valid (no lines deleted) */ if (row > 0 && bot_start + row - + (int)wp->w_lines[j].wl_size > wp->w_height) { + + (int)wp->w_lines[j].wl_size > wp->w_grid.Rows) { wp->w_lines_valid = idx + 1; break; } @@ -990,18 +966,18 @@ static void win_update(win_T *wp) // When starting redraw in the first line, redraw all lines. if (mid_start == 0) { - mid_end = wp->w_height; + mid_end = wp->w_grid.Rows; } } else { /* Not VALID or INVERTED: redraw all lines. */ mid_start = 0; - mid_end = wp->w_height; + mid_end = wp->w_grid.Rows; } if (type == SOME_VALID) { /* SOME_VALID: redraw all lines. */ mid_start = 0; - mid_end = wp->w_height; + mid_end = wp->w_grid.Rows; type = NOT_VALID; } @@ -1146,8 +1122,8 @@ static void win_update(win_T *wp) ++lnum; } srow += mid_start; - mid_end = wp->w_height; - for (; idx < wp->w_lines_valid; ++idx) { /* find end */ + mid_end = wp->w_grid.Rows; + for (; idx < wp->w_lines_valid; idx++) { // find end if (wp->w_lines[idx].wl_valid && wp->w_lines[idx].wl_lnum >= to + 1) { /* Only update until first row of this line */ @@ -1187,8 +1163,8 @@ static void win_update(win_T *wp) for (;; ) { /* stop updating when reached the end of the window (check for _past_ * the end of the window is at the end of the loop) */ - if (row == wp->w_height) { - didline = TRUE; + if (row == wp->w_grid.Rows) { + didline = true; break; } @@ -1286,8 +1262,8 @@ static void win_update(win_T *wp) new_rows += plines_win(wp, l, true); } j++; - if (new_rows > wp->w_height - row - 2) { - /* it's getting too much, must redraw the rest */ + if (new_rows > wp->w_grid.Rows - row - 2) { + // it's getting too much, must redraw the rest new_rows = 9999; break; } @@ -1298,20 +1274,20 @@ static void win_update(win_T *wp) * remaining text or scrolling fails, must redraw the * rest. If scrolling works, must redraw the text * below the scrolled text. */ - if (row - xtra_rows >= wp->w_height - 2) { + 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_height + xtra_rows; + bot_start = wp->w_grid.Rows + xtra_rows; } } } else if (xtra_rows > 0) { /* May scroll text down. If there is not enough * remaining text of scrolling fails, must redraw the * rest. */ - if (row + xtra_rows >= wp->w_height - 2) { + if (row + xtra_rows >= wp->w_grid.Rows - 2) { mod_bot = MAXLNUM; } else { if (win_ins_lines(wp, row + old_rows, xtra_rows) == FAIL) { @@ -1340,7 +1316,7 @@ static void win_update(win_T *wp) wp->w_lines[j] = wp->w_lines[i]; /* stop at a line that won't fit */ if (x + (int)wp->w_lines[j].wl_size - > wp->w_height) { + > wp->w_grid.Rows) { wp->w_lines_valid = j + 1; break; } @@ -1353,10 +1329,12 @@ static void win_update(win_T *wp) /* move entries in w_lines[] downwards */ j -= i; wp->w_lines_valid += j; - if (wp->w_lines_valid > wp->w_height) - wp->w_lines_valid = wp->w_height; - for (i = wp->w_lines_valid; i - j >= idx; --i) + if (wp->w_lines_valid > wp->w_grid.Rows) { + wp->w_lines_valid = wp->w_grid.Rows; + } + for (i = wp->w_lines_valid; i - j >= idx; i--) { wp->w_lines[i] = wp->w_lines[i - j]; + } /* The w_lines[] entries for inserted lines are * now invalid, but wl_size may be used above. @@ -1388,12 +1366,12 @@ static void win_update(win_T *wp) && wp->w_lines[idx].wl_lnum == lnum && lnum > wp->w_topline && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE)) - && srow + wp->w_lines[idx].wl_size > wp->w_height + && srow + wp->w_lines[idx].wl_size > wp->w_grid.Rows && diff_check_fill(wp, lnum) == 0 ) { /* This line is not going to fit. Don't draw anything here, * will draw "@ " lines below. */ - row = wp->w_height + 1; + row = wp->w_grid.Rows + 1; } else { prepare_search_hl(wp, lnum); /* Let the syntax stuff know we skipped a few lines. */ @@ -1404,7 +1382,7 @@ static void win_update(win_T *wp) /* * Display one line. */ - row = win_line(wp, lnum, srow, wp->w_height, mod_top == 0, false); + row = win_line(wp, lnum, srow, wp->w_grid.Rows, mod_top == 0, false); wp->w_lines[idx].wl_folded = FALSE; wp->w_lines[idx].wl_lastlnum = lnum; @@ -1415,10 +1393,7 @@ static void win_update(win_T *wp) wp->w_lines[idx].wl_lnum = lnum; wp->w_lines[idx].wl_valid = true; - // Past end of the window or end of the screen. Note that after - // resizing wp->w_height may be end up too big. That's a problem - // elsewhere, but prevent a crash here. - if (row > wp->w_height || row + wp->w_winrow >= Rows) { + if (row > wp->w_grid.Rows) { // past end of grid // we may need the size of that too long line later on if (dollar_vcol == -1) { wp->w_lines[idx].wl_size = plines_win(wp, lnum, true); @@ -1434,13 +1409,19 @@ static void win_update(win_T *wp) if (wp->w_p_rnu) { // 'relativenumber' set: The text doesn't need to be drawn, but // the number column nearly always does. - (void)win_line(wp, lnum, srow, wp->w_height, true, true); + fold_count = foldedCount(wp, lnum, &win_foldinfo); + if (fold_count != 0) { + fold_line(wp, fold_count, &win_foldinfo, lnum, row); + } else { + (void)win_line(wp, lnum, srow, wp->w_grid.Rows, true, true); + } } // This line does not need to be drawn, advance to the next one. row += wp->w_lines[idx++].wl_size; - if (row > wp->w_height) /* past end of screen */ + if (row > wp->w_grid.Rows) { // past end of screen break; + } lnum = wp->w_lines[idx - 1].wl_lastlnum + 1; did_update = DID_NONE; } @@ -1479,31 +1460,28 @@ static void win_update(win_T *wp) * Don't overwrite it, it can be edited. */ wp->w_botline = lnum + 1; - } else if (diff_check_fill(wp, lnum) >= wp->w_height - srow) { - /* Window ends in filler lines. */ + } else if (diff_check_fill(wp, lnum) >= wp->w_grid.Rows - srow) { + // Window ends in filler lines. wp->w_botline = lnum; - wp->w_filler_rows = wp->w_height - srow; + wp->w_filler_rows = wp->w_grid.Rows - srow; } else if (dy_flags & DY_TRUNCATE) { // 'display' has "truncate" - int scr_row = wp->w_winrow + wp->w_height - 1; + int scr_row = wp->w_grid.Rows - 1; // Last line isn't finished: Display "@@@" in the last screen line. - screen_puts_len((char_u *)"@@", 2, scr_row, wp->w_wincol, at_attr); + grid_puts_len(&wp->w_grid, (char_u *)"@@", 2, scr_row, 0, at_attr); - screen_fill(scr_row, scr_row + 1, - (int)wp->w_wincol + 2, (int)W_ENDCOL(wp), - '@', ' ', at_attr); + grid_fill(&wp->w_grid, scr_row, scr_row + 1, 2, (int)wp->w_grid.Columns, + '@', ' ', at_attr); set_empty_rows(wp, srow); wp->w_botline = lnum; } else if (dy_flags & DY_LASTLINE) { // 'display' has "lastline" // Last line isn't finished: Display "@@@" at the end. - screen_fill(wp->w_winrow + wp->w_height - 1, - wp->w_winrow + wp->w_height, - W_ENDCOL(wp) - 3, W_ENDCOL(wp), - '@', '@', at_attr); + grid_fill(&wp->w_grid, wp->w_grid.Rows - 1, wp->w_grid.Rows, + wp->w_grid.Columns - 3, wp->w_grid.Columns, '@', '@', at_attr); set_empty_rows(wp, srow); wp->w_botline = lnum; } else { - win_draw_end(wp, '@', ' ', srow, wp->w_height, HLF_AT); + win_draw_end(wp, '@', ' ', srow, wp->w_grid.Rows, HLF_AT); wp->w_botline = lnum; } } else { @@ -1518,8 +1496,9 @@ static void win_update(win_T *wp) i = '-'; else i = fill_diff; - if (row + j > wp->w_height) - j = wp->w_height - row; + if (row + j > wp->w_grid.Rows) { + j = wp->w_grid.Rows - row; + } win_draw_end(wp, i, i, row, row + (int)j, HLF_DED); row += j; } @@ -1528,7 +1507,7 @@ static void win_update(win_T *wp) // 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_height, HLF_EOB); + win_draw_end(wp, fill_eob, ' ', row, wp->w_grid.Rows, HLF_EOB); } if (wp->w_redr_type >= REDRAW_TOP) { @@ -1607,71 +1586,67 @@ static void win_draw_end(win_T *wp, int c1, int c2, int row, int endrow, hlf_T h n = fdc; if (n > 0) { - /* draw the fold column at the right */ - if (n > wp->w_width) - n = wp->w_width; - screen_fill(wp->w_winrow + row, wp->w_winrow + endrow, - W_ENDCOL(wp) - n, W_ENDCOL(wp), - ' ', ' ', win_hl_attr(wp, HLF_FC)); + // draw the fold column at the right + if (n > wp->w_grid.Columns) { + n = wp->w_grid.Columns; + } + grid_fill(&wp->w_grid, row, endrow, wp->w_grid.Columns - n, + wp->w_grid.Columns, ' ', ' ', win_hl_attr(wp, HLF_FC)); } if (signcolumn_on(wp)) { int nn = n + win_signcol_width(wp); - /* draw the sign column left of the fold column */ - if (nn > wp->w_width) { - nn = wp->w_width; + // draw the sign column left of the fold column + if (nn > wp->w_grid.Columns) { + nn = wp->w_grid.Columns; } - screen_fill(wp->w_winrow + row, wp->w_winrow + endrow, - W_ENDCOL(wp) - nn, W_ENDCOL(wp) - n, - ' ', ' ', win_hl_attr(wp, HLF_SC)); + grid_fill(&wp->w_grid, row, endrow, wp->w_grid.Columns - nn, + wp->w_grid.Columns - n, ' ', ' ', win_hl_attr(wp, HLF_SC)); n = nn; } - screen_fill(wp->w_winrow + row, wp->w_winrow + endrow, - wp->w_wincol, W_ENDCOL(wp) - 1 - FDC_OFF, - c2, c2, attr); - screen_fill(wp->w_winrow + row, wp->w_winrow + endrow, - W_ENDCOL(wp) - 1 - FDC_OFF, W_ENDCOL(wp) - FDC_OFF, - c1, c2, attr); + grid_fill(&wp->w_grid, row, endrow, 0, wp->w_grid.Columns - 1 - FDC_OFF, + c2, c2, attr); + grid_fill(&wp->w_grid, row, endrow, + wp->w_grid.Columns - 1 - FDC_OFF, wp->w_grid.Columns - FDC_OFF, + c1, c2, attr); } else { if (cmdwin_type != 0 && wp == curwin) { /* draw the cmdline character in the leftmost column */ n = 1; - if (n > wp->w_width) - n = wp->w_width; - screen_fill(wp->w_winrow + row, wp->w_winrow + endrow, - wp->w_wincol, wp->w_wincol + n, - cmdwin_type, ' ', win_hl_attr(wp, HLF_AT)); + if (n > wp->w_grid.Columns) { + n = wp->w_grid.Columns; + } + grid_fill(&wp->w_grid, row, endrow, 0, n, cmdwin_type, ' ', + win_hl_attr(wp, HLF_AT)); } if (fdc > 0) { int nn = n + fdc; - /* draw the fold column at the left */ - if (nn > wp->w_width) - nn = wp->w_width; - screen_fill(wp->w_winrow + row, wp->w_winrow + endrow, - wp->w_wincol + n, wp->w_wincol + nn, - ' ', ' ', win_hl_attr(wp, HLF_FC)); + // draw the fold column at the left + if (nn > wp->w_grid.Columns) { + nn = wp->w_grid.Columns; + } + grid_fill(&wp->w_grid, row, endrow, n, nn, ' ', ' ', + win_hl_attr(wp, HLF_FC)); n = nn; } if (signcolumn_on(wp)) { int nn = n + win_signcol_width(wp); - /* draw the sign column after the fold column */ - if (nn > wp->w_width) { - nn = wp->w_width; + // draw the sign column after the fold column + if (nn > wp->w_grid.Columns) { + nn = wp->w_grid.Columns; } - screen_fill(wp->w_winrow + row, wp->w_winrow + endrow, - wp->w_wincol + n, wp->w_wincol + nn, - ' ', ' ', win_hl_attr(wp, HLF_SC)); + grid_fill(&wp->w_grid, row, endrow, n, nn, ' ', ' ', + win_hl_attr(wp, HLF_SC)); n = nn; } - screen_fill(wp->w_winrow + row, wp->w_winrow + endrow, - wp->w_wincol + FDC_OFF, W_ENDCOL(wp), - c1, c2, attr); + grid_fill(&wp->w_grid, row, endrow, FDC_OFF, wp->w_grid.Columns, c1, c2, + attr); } set_empty_rows(wp, row); } @@ -1693,7 +1668,7 @@ static int compute_foldcolumn(win_T *wp, int col) { int fdc = wp->w_p_fdc; int wmw = wp == curwin && p_wmw == 0 ? 1 : p_wmw; - int wwidth = wp->w_width; + int wwidth = wp->w_grid.Columns; if (fdc > wwidth - (col + wmw)) { fdc = wwidth - (col + wmw); @@ -1764,7 +1739,7 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T int fdc; int col; int txtcol; - int off = (int)(current_ScreenLine - ScreenLines); + int off; int ri; /* Build the fold line: @@ -1776,14 +1751,15 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T * 6. set highlighting for the Visual area an other text */ col = 0; + off = 0; /* * 1. Add the cmdwin_type for the command-line window * Ignores 'rightleft', this window is never right-left. */ if (cmdwin_type != 0 && wp == curwin) { - schar_from_ascii(ScreenLines[off], cmdwin_type); - ScreenAttrs[off] = win_hl_attr(wp, HLF_AT); + schar_from_ascii(linebuf_char[off], cmdwin_type); + linebuf_attr[off] = win_hl_attr(wp, HLF_AT); col++; } @@ -1795,11 +1771,12 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T if (wp->w_p_rl) { int i; - copy_text_attr(off + wp->w_width - fdc - col, buf, fdc, + copy_text_attr(off + wp->w_grid.Columns - fdc - col, buf, fdc, win_hl_attr(wp, HLF_FC)); // reverse the fold column for (i = 0; i < fdc; i++) { - schar_from_ascii(ScreenLines[off + wp->w_width - i - 1 - col], buf[i]); + schar_from_ascii(linebuf_char[off + wp->w_grid.Columns - i - 1 - col], + buf[i]); } } else { copy_text_attr(off + col, buf, fdc, win_hl_attr(wp, HLF_FC)); @@ -1807,20 +1784,23 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T col += fdc; } -# define RL_MEMSET(p, v, l) if (wp->w_p_rl) \ - for (ri = 0; ri < l; ++ri) \ - ScreenAttrs[off + (wp->w_width - (p) - (l)) + ri] = v; \ - else \ - for (ri = 0; ri < l; ++ri) \ - ScreenAttrs[off + (p) + ri] = v +# define RL_MEMSET(p, v, l) if (wp->w_p_rl) { \ + for (ri = 0; ri < l; ri++) { \ + linebuf_attr[off + (wp->w_grid.Columns - (p) - (l)) + ri] = v; \ + } \ + } else { \ + for (ri = 0; ri < l; ri++) { \ + linebuf_attr[off + (p) + ri] = v; \ + } \ + } /* Set all attributes of the 'number' or 'relativenumber' column and the * text */ - RL_MEMSET(col, win_hl_attr(wp, HLF_FL), wp->w_width - col); + RL_MEMSET(col, win_hl_attr(wp, HLF_FL), wp->w_grid.Columns - col); // If signs are being displayed, add spaces. if (signcolumn_on(wp)) { - len = wp->w_width - col; + len = wp->w_grid.Columns - col; if (len > 0) { int len_max = win_signcol_width(wp); if (len > len_max) { @@ -1836,7 +1816,7 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T * 3. Add the 'number' or 'relativenumber' column */ if (wp->w_p_nu || wp->w_p_rnu) { - len = wp->w_width - col; + len = wp->w_grid.Columns - col; if (len > 0) { int w = number_width(wp); long num; @@ -1862,7 +1842,7 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T snprintf((char *)buf, FOLD_TEXT_LEN, fmt, w, num); if (wp->w_p_rl) { // the line number isn't reversed - copy_text_attr(off + wp->w_width - len - col, buf, len, + copy_text_attr(off + wp->w_grid.Columns - len - col, buf, len, win_hl_attr(wp, HLF_FL)); } else { copy_text_attr(off + col, buf, len, win_hl_attr(wp, HLF_FL)); @@ -1878,11 +1858,9 @@ 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 current_ScreenLine. Fill up with "fill_fold". - * Right-left text is put in columns 0 - number-col, normal text is put - * in columns number-col - window-width. - */ + // 5. move the text to linebuf_char[off]. Fill up with "fill_fold". + // Right-left text is put in columns 0 - number-col, normal text is put + // in columns number-col - window-width. int idx; if (wp->w_p_rl) { @@ -1898,8 +1876,8 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T // if(col + cells > wp->w_width - (wp->w_p_rl ? col : 0)) { break; } // This is obvious wrong. If Vim ever fixes this, solve for "cells" again // in the correct condition. - int maxcells = wp->w_width - col - (wp->w_p_rl ? col : 0); - int cells = line_putchar(&s, &ScreenLines[idx], maxcells, wp->w_p_rl); + int maxcells = wp->w_grid.Columns - col - (wp->w_p_rl ? col : 0); + int cells = line_putchar(&s, &linebuf_char[idx], maxcells, wp->w_p_rl); if (cells == -1) { break; } @@ -1913,10 +1891,10 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T schar_T sc; schar_from_char(sc, fill_fold); - while (col < wp->w_width + while (col < wp->w_grid.Columns - (wp->w_p_rl ? txtcol : 0) ) { - schar_copy(ScreenLines[off+col++], sc); + schar_copy(linebuf_char[off+col++], sc); } if (text != buf) @@ -1948,20 +1926,21 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T >= (colnr_T)STRLEN(ml_get_buf(wp->w_buffer, lnume, FALSE))))))) { if (VIsual_mode == Ctrl_V) { - /* Visual block mode: highlight the chars part of the block */ - if (wp->w_old_cursor_fcol + txtcol < (colnr_T)wp->w_width) { + // Visual block mode: highlight the chars part of the block + if (wp->w_old_cursor_fcol + txtcol < (colnr_T)wp->w_grid.Columns) { if (wp->w_old_cursor_lcol != MAXCOL && wp->w_old_cursor_lcol + txtcol - < (colnr_T)wp->w_width) + < (colnr_T)wp->w_grid.Columns) { len = wp->w_old_cursor_lcol; - else - len = wp->w_width - txtcol; + } else { + len = wp->w_grid.Columns - txtcol; + } RL_MEMSET(wp->w_old_cursor_fcol + txtcol, win_hl_attr(wp, HLF_V), len - (int)wp->w_old_cursor_fcol); } } else { // Set all attributes of the text - RL_MEMSET(txtcol, win_hl_attr(wp, HLF_V), wp->w_width - txtcol); + RL_MEMSET(txtcol, win_hl_attr(wp, HLF_V), wp->w_grid.Columns - txtcol); } } } @@ -1979,9 +1958,9 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T } else { txtcol -= wp->w_leftcol; } - if (txtcol >= 0 && txtcol < wp->w_width) { - ScreenAttrs[off + txtcol] = - hl_combine_attr(ScreenAttrs[off + txtcol], win_hl_attr(wp, HLF_MC)); + if (txtcol >= 0 && txtcol < wp->w_grid.Columns) { + linebuf_attr[off + txtcol] = + hl_combine_attr(linebuf_attr[off + txtcol], win_hl_attr(wp, HLF_MC)); } txtcol = old_txtcol; j = wp->w_p_cc_cols[++i]; @@ -1995,13 +1974,14 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T txtcol -= wp->w_skipcol; else txtcol -= wp->w_leftcol; - if (txtcol >= 0 && txtcol < wp->w_width) - ScreenAttrs[off + txtcol] = hl_combine_attr( - ScreenAttrs[off + txtcol], win_hl_attr(wp, HLF_CUC)); + if (txtcol >= 0 && txtcol < wp->w_grid.Columns) { + linebuf_attr[off + txtcol] = hl_combine_attr( + linebuf_attr[off + txtcol], win_hl_attr(wp, HLF_CUC)); + } } - screen_line(row + wp->w_winrow, wp->w_wincol, wp->w_width, - wp->w_width, false, wp, wp->w_hl_attr_normal, false); + grid_put_linebuf(&wp->w_grid, row, 0, wp->w_grid.Columns, wp->w_grid.Columns, + false, wp, wp->w_hl_attr_normal, false); /* * Update w_cline_height and w_cline_folded if the cursor line was @@ -2018,7 +1998,7 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T } -/// Copy "buf[len]" to ScreenLines["off"] and set attributes to "attr". +/// Copy "buf[len]" to linebuf_char["off"] and set attributes to "attr". /// /// Only works for ASCII text! static void copy_text_attr(int off, char_u *buf, int len, int attr) @@ -2026,8 +2006,8 @@ static void copy_text_attr(int off, char_u *buf, int len, int attr) int i; for (i = 0; i < len; i++) { - schar_from_ascii(ScreenLines[off + i], buf[i]); - ScreenAttrs[off + i] = attr; + schar_from_ascii(linebuf_char[off + i], buf[i]); + linebuf_attr[off + i] = attr; } } @@ -2102,7 +2082,6 @@ win_line ( bool number_only // only update the number column ) { - unsigned off; // offset in ScreenLines/ScreenAttrs int c = 0; // init for GCC long vcol = 0; // virtual column (for tabs) long vcol_sbr = -1; // virtual column after showbreak @@ -2110,7 +2089,7 @@ win_line ( char_u *line; // current line char_u *ptr; // current position in "line" int row; // row in the window, excl w_winrow - int screen_row; // row on the screen, incl 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 */ @@ -2155,10 +2134,10 @@ win_line ( int syntax_attr = 0; /* attributes desired by syntax */ int has_syntax = FALSE; /* this buffer has syntax highl. */ int save_did_emsg; - int eol_hl_off = 0; /* 1 if highlighted char after EOL */ - int draw_color_col = FALSE; /* highlight colorcolumn */ - int *color_cols = NULL; /* pointer to according columns array */ - bool has_spell = false; /* this buffer has spell checking */ + int eol_hl_off = 0; // 1 if highlighted char after EOL + int draw_color_col = false; // highlight colorcolumn + int *color_cols = NULL; // pointer to according columns array + bool has_spell = false; // this buffer has spell checking # define SPWORDLEN 150 char_u nextline[SPWORDLEN * 2]; /* text with start of the next line */ int nextlinecol = 0; /* column where nextline[] starts */ @@ -2240,7 +2219,6 @@ win_line ( return startrow; row = startrow; - screen_row = row + wp->w_winrow; if (!number_only) { // To speed up the loop below, set extra_check when there is linebreak, @@ -2365,8 +2343,9 @@ win_line ( } } - // Check if the character under the cursor should not be inverted - if (!highlight_match && lnum == curwin->w_cursor.lnum && wp == curwin) { + // Check if the char under the cursor should be inverted (highlighted). + if (!highlight_match && lnum == curwin->w_cursor.lnum && wp == curwin + && cursor_is_block_during_visual(*p_sel == 'e')) { noinvcur = true; } @@ -2677,13 +2656,13 @@ win_line ( cur = cur->next; } - off = (unsigned)(current_ScreenLine - ScreenLines); + unsigned off = 0; // Offset relative start of line int col = 0; // Visual column on screen. if (wp->w_p_rl) { // Rightleft window: process the text in the normal direction, but put - // it in current_ScreenLine[] from right to left. Start at the + // it in linebuf_char[off] from right to left. Start at the // rightmost column of the window. - col = wp->w_width - 1; + col = grid->Columns - 1; off += col; } @@ -2858,10 +2837,11 @@ win_line ( c_extra = '-'; else c_extra = fill_diff; - if (wp->w_p_rl) + if (wp->w_p_rl) { n_extra = col + 1; - else - n_extra = wp->w_width - col; + } else { + n_extra = grid->Columns - col; + } char_attr = win_hl_attr(wp, HLF_DED); } if (*p_sbr != NUL && need_showbreak) { @@ -2902,20 +2882,20 @@ win_line ( && lnum == wp->w_cursor.lnum && vcol >= (long)wp->w_virtcol && filler_todo <= 0) || (number_only && draw_state > WL_NR)) { - screen_line(screen_row, wp->w_wincol, col, -wp->w_width, wp->w_p_rl, wp, - wp->w_hl_attr_normal, false); + grid_put_linebuf(grid, row, 0, col, -grid->Columns, wp->w_p_rl, wp, + wp->w_hl_attr_normal, false); // Pretend we have finished updating the window. Except when // 'cursorcolumn' is set. if (wp->w_p_cuc) { row = wp->w_cline_row + wp->w_cline_height; } else { - row = wp->w_height; + row = grid->Rows; } break; } - if (draw_state == WL_LINE && area_highlighting) { - /* handle Visual or match highlighting in this line */ + if (draw_state == WL_LINE && (area_highlighting || has_spell)) { + // handle Visual or match highlighting in this line if (vcol == fromcol || (vcol + 1 == fromcol && n_extra == 0 && utf_ptr2cells(ptr) > 1) @@ -3130,9 +3110,8 @@ win_line ( /* If a double-width char doesn't fit display a '>' in the * last column. */ - if (( - wp->w_p_rl ? (col <= 0) : - (col >= wp->w_width - 1)) + if ((wp->w_p_rl ? (col <= 0) : + (col >= grid->Columns - 1)) && (*mb_char2cells)(mb_c) == 2) { c = '>'; mb_c = c; @@ -3272,9 +3251,8 @@ win_line ( /* If a double-width char doesn't fit display a '>' in the * last column; the character is displayed at the start of the * next line. */ - if (( - wp->w_p_rl ? (col <= 0) : - (col >= wp->w_width - 1)) + if ((wp->w_p_rl ? (col <= 0) : + (col >= grid->Columns - 1)) && (*mb_char2cells)(mb_c) == 2) { c = '>'; mb_c = c; @@ -3448,7 +3426,7 @@ win_line ( char_u *p = ptr - (mb_off + 1); // TODO: is passing p for start of the line OK? n_extra = win_lbr_chartabsize(wp, line, p, (colnr_T)vcol, NULL) - 1; - if (c == TAB && n_extra + col > wp->w_width) { + if (c == TAB && n_extra + col > grid->Columns) { n_extra = (int)wp->w_buffer->b_p_ts - vcol % (int)wp->w_buffer->b_p_ts - 1; } @@ -3504,8 +3482,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 - // into "ScreenLines". + // turn it into something else on the way to putting it on the screen. if (c == TAB && (!wp->w_p_list || lcs_tab1)) { int tab_len = 0; long vcol_adjusted = vcol; // removed showbreak length @@ -3605,9 +3582,7 @@ win_line ( || ((fromcol >= 0 || fromcol_prev >= 0) && tocol > vcol && VIsual_mode != Ctrl_V - && ( - wp->w_p_rl ? (col >= 0) : - (col < wp->w_width)) + && (wp->w_p_rl ? (col >= 0) : (col < grid->Columns)) && !(noinvcur && lnum == wp->w_cursor.lnum && (colnr_T)vcol == wp->w_virtcol))) @@ -3672,14 +3647,11 @@ win_line ( saved_attr2 = char_attr; // save current attr mb_utf8 = false; // don't draw as UTF-8 } else if (VIsual_active - && (VIsual_mode == Ctrl_V - || VIsual_mode == 'v') + && (VIsual_mode == Ctrl_V || VIsual_mode == 'v') && virtual_active() && tocol != MAXCOL && vcol < tocol - && ( - wp->w_p_rl ? (col >= 0) : - (col < wp->w_width))) { + && (wp->w_p_rl ? (col >= 0) : (col < grid->Columns))) { c = ' '; ptr--; // put it back at the NUL } @@ -3749,7 +3721,7 @@ win_line ( && conceal_cursor_line(wp) && (int)wp->w_virtcol <= vcol + n_skip) { if (wp->w_p_rl) { - wp->w_wcol = wp->w_width - col + boguscols - 1; + wp->w_wcol = grid->Columns - col + boguscols - 1; } else { wp->w_wcol = col - boguscols; } @@ -3837,8 +3809,9 @@ win_line ( if (col < 0) n = 1; } else { - if (col >= wp->w_width) + if (col >= grid->Columns) { n = -1; + } } if (n != 0) { /* At the window boundary, highlight the last character @@ -3847,7 +3820,7 @@ win_line ( col += n; } else { // Add a blank character to highlight. - schar_from_ascii(ScreenLines[off], ' '); + schar_from_ascii(linebuf_char[off], ' '); } if (area_attr == 0) { /* Use attributes from match with highest priority among @@ -3878,7 +3851,7 @@ win_line ( if (wp->w_p_cul && lnum == wp->w_cursor.lnum) { eol_attr = hl_combine_attr(win_hl_attr(wp, HLF_CUL), eol_attr); } - ScreenAttrs[off] = eol_attr; + linebuf_attr[off] = eol_attr; if (wp->w_p_rl) { --col; --off; @@ -3910,7 +3883,7 @@ win_line ( if (((wp->w_p_cuc && (int)wp->w_virtcol >= VCOL_HLC - eol_hl_off && (int)wp->w_virtcol < - wp->w_width * (row - startrow + 1) + v + grid->Columns * (row - startrow + 1) + v && lnum != wp->w_cursor.lnum) || draw_color_col || line_attr_lowprio || line_attr || diff_hlf != (hlf_T)0 || do_virttext)) { @@ -3958,7 +3931,7 @@ win_line ( int col_stride = wp->w_p_rl ? -1 : 1; - while (wp->w_p_rl ? col >= 0 : col < wp->w_width) { + while (wp->w_p_rl ? col >= 0 : col < grid->Columns) { int cells = -1; if (do_virttext && !delay_virttext) { if (*s.p == NUL) { @@ -3972,14 +3945,14 @@ win_line ( } } if (*s.p != NUL) { - cells = line_putchar(&s, &ScreenLines[off], wp->w_width - col, + cells = line_putchar(&s, &linebuf_char[off], grid->Columns - col, false); } } delay_virttext = false; if (cells == -1) { - schar_from_ascii(ScreenLines[off], ' '); + schar_from_ascii(linebuf_char[off], ' '); cells = 1; } col += cells * col_stride; @@ -3987,23 +3960,23 @@ win_line ( draw_color_col = advance_color_col(VCOL_HLC, &color_cols); } - int attr = base_attr; + int col_attr = base_attr; if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol) { - attr = cuc_attr; + col_attr = cuc_attr; } else if (draw_color_col && VCOL_HLC == *color_cols) { - attr = mc_attr; + col_attr = mc_attr; } if (do_virttext) { - attr = hl_combine_attr(attr, virt_attr); + col_attr = hl_combine_attr(col_attr, virt_attr); } - attr = hl_combine_attr(attr, line_attr); + col_attr = hl_combine_attr(col_attr, line_attr); - ScreenAttrs[off] = attr; + linebuf_attr[off] = col_attr; if (cells == 2) { - ScreenAttrs[off+1] = attr; + linebuf_attr[off+1] = col_attr; } off += cells * col_stride; @@ -4020,14 +3993,14 @@ win_line ( if (wp->w_buffer->terminal) { // terminal buffers may need to highlight beyond the end of the // logical line - while (col < wp->w_width) { - schar_from_ascii(ScreenLines[off], ' '); - ScreenAttrs[off++] = term_attrs[vcol++]; + while (col < grid->Columns) { + schar_from_ascii(linebuf_char[off], ' '); + linebuf_attr[off++] = term_attrs[vcol++]; col++; } } - screen_line(screen_row, wp->w_wincol, col, wp->w_width, wp->w_p_rl, wp, - wp->w_hl_attr_normal, false); + grid_put_linebuf(grid, row, 0, col, grid->Columns, wp->w_p_rl, wp, + wp->w_hl_attr_normal, false); row++; /* @@ -4048,9 +4021,7 @@ win_line ( if (lcs_ext && !wp->w_p_wrap && filler_todo <= 0 - && ( - wp->w_p_rl ? col == 0 : - col == wp->w_width - 1) + && (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)))) { @@ -4107,22 +4078,23 @@ win_line ( --col; } if (mb_utf8) { - schar_from_cc(ScreenLines[off], mb_c, u8cc); + schar_from_cc(linebuf_char[off], mb_c, u8cc); } else { - schar_from_ascii(ScreenLines[off], c); + schar_from_ascii(linebuf_char[off], c); } if (multi_attr) { - ScreenAttrs[off] = multi_attr; + linebuf_attr[off] = multi_attr; multi_attr = 0; - } else - ScreenAttrs[off] = char_attr; + } else { + linebuf_attr[off] = char_attr; + } if (has_mbyte && (*mb_char2cells)(mb_c) > 1) { // Need to fill two screen columns. off++; col++; // UTF-8: Put a 0 in the second screen char. - ScreenLines[off][0] = 0; + linebuf_char[off][0] = 0; if (draw_state > WL_NR && filler_todo <= 0) { vcol++; } @@ -4228,33 +4200,35 @@ win_line ( * At end of screen line and there is more to come: Display the line * so far. If there is no more to display it is caught above. */ - if (( - wp->w_p_rl ? (col < 0) : - (col >= wp->w_width)) + 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) || (n_extra != 0 && (c_extra != NUL || *p_extra != NUL))) ) { - bool wrap = wp->w_p_wrap // Wrapping enabled. - && filler_todo <= 0 // Not drawing diff filler lines. - && lcs_eol_one != -1 // Haven't printed the lcs_eol character. - && row != endrow - 1 // Not the last line being displayed. - && wp->w_width == Columns // Window spans the width of the screen. - && !wp->w_p_rl; // Not right-to-left. - screen_line(screen_row, wp->w_wincol, col - boguscols, - wp->w_width, wp->w_p_rl, wp, wp->w_hl_attr_normal, wrap); + bool wrap = wp->w_p_wrap // Wrapping enabled. + && filler_todo <= 0 // Not drawing diff filler lines. + && lcs_eol_one != -1 // Haven't printed the lcs_eol character. + && row != endrow - 1 // Not the last line being displayed. + && (grid->Columns == Columns // Window spans the width of the screen, + || ui_is_external(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); if (wrap) { + ScreenGrid *current_grid = grid; + int current_row = row, dummy_col = 0; // dummy_col unused + screen_adjust_grid(¤t_grid, ¤t_row, &dummy_col); + // Force a redraw of the first column of the next line. - ScreenAttrs[LineOffset[screen_row + 1]] = -1; + current_grid->attrs[current_grid->line_offset[current_row+1]] = -1; // Remember that the line wraps, used for modeless copy. - LineWraps[screen_row] = true; + current_grid->line_wraps[current_row] = true; } boguscols = 0; - ++row; - ++screen_row; + row++; /* When not wrapping and finished diff lines, or when displayed * '$' and highlighting until last column, break here. */ @@ -4267,7 +4241,7 @@ win_line ( if (draw_state != WL_LINE && filler_todo <= 0 ) { - win_draw_end(wp, '@', ' ', row, wp->w_height, HLF_AT); + win_draw_end(wp, '@', ' ', row, wp->w_grid.Rows, HLF_AT); row = endrow; } @@ -4278,9 +4252,9 @@ win_line ( } col = 0; - off = (unsigned)(current_ScreenLine - ScreenLines); + off = 0; if (wp->w_p_rl) { - col = wp->w_width - 1; /* col is not used if breaking! */ + col = grid->Columns - 1; // col is not used if breaking! off += col; } @@ -4313,6 +4287,22 @@ win_line ( return row; } +/// Determine if dedicated window grid should be used or the default_grid +/// +/// If UI did not request multigrid support, draw all windows on the +/// default_grid. +/// +/// If the default_grid is used, adjust window relative positions to global +/// screen positions. +static void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off) +{ + if (!ui_is_external(kUIMultigrid) && *grid != &default_grid) { + *row_off += (*grid)->row_offset; + *col_off += (*grid)->col_offset; + *grid = &default_grid; + } +} + /* * Check whether the given character needs redrawing: @@ -4321,32 +4311,32 @@ win_line ( * - the character is multi-byte and the next byte is different * - the character is two cells wide and the second cell differs. */ -static int char_needs_redraw(int off_from, int off_to, int cols) +static int grid_char_needs_redraw(ScreenGrid *grid, int off_from, int off_to, + int cols) { return (cols > 0 - && ((schar_cmp(ScreenLines[off_from], ScreenLines[off_to]) - || ScreenAttrs[off_from] != ScreenAttrs[off_to] - || (utf_off2cells(off_from, off_from + cols) > 1 - && schar_cmp(ScreenLines[off_from + 1], - ScreenLines[off_to + 1]))) + && ((schar_cmp(linebuf_char[off_from], grid->chars[off_to]) + || linebuf_attr[off_from] != grid->attrs[off_to] + || (line_off2cells(linebuf_char, off_from, off_from + cols) > 1 + && schar_cmp(linebuf_char[off_from + 1], + grid->chars[off_to + 1]))) || p_wd < 0)); } -/* - * Move one "cooked" screen line to the screen, but only the characters that - * have actually changed. Handle insert/delete character. - * "coloff" gives the first column on the screen for this line. - * "endcol" gives the columns where valid characters are. - * "clear_width" is the width of the window. It's > 0 if the rest of the line - * needs to be cleared, negative otherwise. - * "rlflag" is TRUE in a rightleft window: - * When TRUE and "clear_width" > 0, clear columns 0 to "endcol" - * When FALSE and "clear_width" > 0, clear columns "endcol" to "clear_width" - * If "wrap" is true, then hint to the UI that "row" contains a line - * which has wrapped into the next row. - */ -static void screen_line(int row, int coloff, int endcol, int clear_width, - int rlflag, win_T *wp, int bg_attr, bool wrap) +/// Move one buffered line to the window grid, but only the characters that +/// have actually changed. Handle insert/delete character. +/// "coloff" gives the first column on the grid for this line. +/// "endcol" gives the columns where valid characters are. +/// "clear_width" is the width of the window. It's > 0 if the rest of the line +/// needs to be cleared, negative otherwise. +/// "rlflag" is TRUE in a rightleft window: +/// When TRUE and "clear_width" > 0, clear columns 0 to "endcol" +/// When FALSE and "clear_width" > 0, clear columns "endcol" to "clear_width" +/// If "wrap" is true, then hint to the UI that "row" contains a line +/// which has wrapped into the next row. +static void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, + int clear_width, int rlflag, win_T *wp, + int bg_attr, bool wrap) { unsigned off_from; unsigned off_to; @@ -4360,56 +4350,61 @@ static void screen_line(int row, int coloff, int endcol, int clear_width, // 2: occupies two display cells int start_dirty = -1, end_dirty = 0; - /* Check for illegal row and col, just in case. */ - if (row >= Rows) - row = Rows - 1; - if (endcol > Columns) - endcol = Columns; + // TODO(bfredl): check all callsites and eliminate + // Check for illegal row and col, just in case + if (row >= grid->Rows) { + row = grid->Rows - 1; + } + if (endcol > grid->Columns) { + endcol = grid->Columns; + } + + screen_adjust_grid(&grid, &row, &coloff); - off_from = (unsigned)(current_ScreenLine - ScreenLines); - off_to = LineOffset[row] + coloff; - max_off_from = off_from + screen_Columns; - max_off_to = LineOffset[row] + screen_Columns; + off_from = 0; + off_to = grid->line_offset[row] + coloff; + max_off_from = linebuf_size; + max_off_to = grid->line_offset[row] + grid->Columns; if (rlflag) { /* Clear rest first, because it's left of the text. */ if (clear_width > 0) { - while (col <= endcol && ScreenLines[off_to][0] == ' ' - && ScreenLines[off_to][1] == NUL - && ScreenAttrs[off_to] == bg_attr + while (col <= endcol && grid->chars[off_to][0] == ' ' + && grid->chars[off_to][1] == NUL + && grid->attrs[off_to] == bg_attr ) { ++off_to; ++col; } if (col <= endcol) { - screen_fill(row, row + 1, col + coloff, endcol + coloff + 1, ' ', ' ', - bg_attr); + grid_fill(grid, row, row + 1, col + coloff, endcol + coloff + 1, + ' ', ' ', bg_attr); } } col = endcol + 1; - off_to = LineOffset[row] + col + coloff; + off_to = grid->line_offset[row] + col + coloff; off_from += col; endcol = (clear_width > 0 ? clear_width : -clear_width); } if (bg_attr) { for (int c = col; c < endcol; c++) { - ScreenAttrs[off_from+c] = hl_combine_attr(bg_attr, - ScreenAttrs[off_from+c]); + linebuf_attr[off_from+c] = + hl_combine_attr(bg_attr, linebuf_attr[off_from+c]); } } - redraw_next = char_needs_redraw(off_from, off_to, endcol - col); + redraw_next = grid_char_needs_redraw(grid, off_from, off_to, endcol - col); while (col < endcol) { char_cells = 1; if (col + 1 < endcol) { - char_cells = utf_off2cells(off_from, max_off_from); + char_cells = line_off2cells(linebuf_char, off_from, max_off_from); } redraw_this = redraw_next; - redraw_next = char_needs_redraw(off_from + char_cells, - off_to + char_cells, - endcol - col - char_cells); + redraw_next = grid_char_needs_redraw(grid, off_from + char_cells, + off_to + char_cells, + endcol - col - char_cells); if (redraw_this) { if (start_dirty == -1) { @@ -4423,23 +4418,23 @@ static void screen_line(int row, int coloff, int endcol, int clear_width, // char over the left halve of an existing one if (col + char_cells == endcol && ((char_cells == 1 - && utf_off2cells(off_to, max_off_to) > 1) + && grid_off2cells(grid, off_to, max_off_to) > 1) || (char_cells == 2 - && utf_off2cells(off_to, max_off_to) == 1 - && utf_off2cells(off_to + 1, max_off_to) > 1))) { + && grid_off2cells(grid, off_to, max_off_to) == 1 + && grid_off2cells(grid, off_to + 1, max_off_to) > 1))) { clear_next = true; } - schar_copy(ScreenLines[off_to], ScreenLines[off_from]); + schar_copy(grid->chars[off_to], linebuf_char[off_from]); if (char_cells == 2) { - schar_copy(ScreenLines[off_to+1], ScreenLines[off_from+1]); + schar_copy(grid->chars[off_to+1], linebuf_char[off_from+1]); } - ScreenAttrs[off_to] = ScreenAttrs[off_from]; + grid->attrs[off_to] = linebuf_attr[off_from]; // For simplicity set the attributes of second half of a // double-wide character equal to the first half. if (char_cells == 2) { - ScreenAttrs[off_to + 1] = ScreenAttrs[off_from]; + grid->attrs[off_to + 1] = linebuf_attr[off_from]; } } @@ -4451,7 +4446,7 @@ static void screen_line(int row, int coloff, int endcol, int clear_width, if (clear_next) { /* Clear the second half of a double-wide character of which the left * half was overwritten with a single-wide character. */ - schar_from_ascii(ScreenLines[off_to], ' '); + schar_from_ascii(grid->chars[off_to], ' '); end_dirty++; } @@ -4460,28 +4455,29 @@ static void screen_line(int row, int coloff, int endcol, int clear_width, // blank out the rest of the line // TODO(bfredl): we could cache winline widths while (col < clear_width) { - if (ScreenLines[off_to][0] != ' ' || ScreenLines[off_to][1] != NUL - || ScreenAttrs[off_to] != bg_attr) { - ScreenLines[off_to][0] = ' '; - ScreenLines[off_to][1] = NUL; - ScreenAttrs[off_to] = bg_attr; - if (start_dirty == -1) { - start_dirty = col; - end_dirty = col; - } else if (clear_end == -1) { - end_dirty = endcol; - } - clear_end = col+1; + if (grid->chars[off_to][0] != ' ' + || grid->chars[off_to][1] != NUL + || grid->attrs[off_to] != bg_attr) { + grid->chars[off_to][0] = ' '; + grid->chars[off_to][1] = NUL; + grid->attrs[off_to] = bg_attr; + if (start_dirty == -1) { + start_dirty = col; + end_dirty = col; + } else if (clear_end == -1) { + end_dirty = endcol; } - col++; - off_to++; + clear_end = col+1; + } + col++; + off_to++; } } - if (clear_width > 0 || wp->w_width != Columns) { + if (clear_width > 0 || wp->w_width != grid->Columns) { // If we cleared after the end of the line, it did not wrap. // For vsplit, line wrapping is not possible. - LineWraps[row] = false; + grid->line_wraps[row] = false; } if (clear_end < end_dirty) { @@ -4491,7 +4487,7 @@ static void screen_line(int row, int coloff, int endcol, int clear_width, start_dirty = end_dirty; } if (clear_end > start_dirty) { - ui_line(row, coloff+start_dirty, coloff+end_dirty, coloff+clear_end, + ui_line(grid, row, coloff+start_dirty, coloff+end_dirty, coloff+clear_end, bg_attr, wrap); } } @@ -4586,9 +4582,8 @@ static void draw_vsep_win(win_T *wp, int row) if (wp->w_vsep_width) { // draw the vertical separator right of this window c = fillchar_vsep(wp, &hl); - screen_fill(wp->w_winrow + row, wp->w_winrow + wp->w_height, - W_ENDCOL(wp), W_ENDCOL(wp) + 1, - c, ' ', hl); + grid_fill(&default_grid, wp->w_winrow + row, W_ENDROW(wp), + W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, ' ', hl); } } @@ -4673,7 +4668,8 @@ win_redr_status_matches ( if (matches == NULL) /* interrupted completion? */ return; - buf = xmalloc(has_mbyte ? Columns * MB_MAXBYTES + 1 : Columns + 1); + buf = xmalloc(has_mbyte ? default_grid.Columns * MB_MAXBYTES + 1 + : default_grid.Columns + 1); if (match == -1) { /* don't show match but original text */ match = 0; @@ -4693,15 +4689,16 @@ win_redr_status_matches ( clen += status_match_len(xp, L_MATCH(i)) + 2; if (first_match > 0) clen += 2; - /* jumping right, put match at the left */ - if ((long)clen > Columns) { + // jumping right, put match at the left + if ((long)clen > default_grid.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 >= Columns) + if ((long)clen >= default_grid.Columns) { break; + } } if (i == num_matches) add_left = TRUE; @@ -4710,9 +4707,10 @@ 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 >= Columns) + if ((long)clen >= default_grid.Columns) { break; - --first_match; + } + first_match--; } fillchar = fillchar_status(&attr, curwin); @@ -4727,7 +4725,8 @@ win_redr_status_matches ( clen = len; i = first_match; - while ((long)(clen + status_match_len(xp, L_MATCH(i)) + 2) < Columns) { + while ((long)(clen + status_match_len(xp, L_MATCH(i)) + 2) + < default_grid.Columns) { if (i == match) { selstart = buf + len; selstart_col = clen; @@ -4778,8 +4777,8 @@ 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 == Rows - 1) { - screen_del_lines(0, 1, (int)Rows, 0, (int)Columns); + if (cmdline_row == default_grid.Rows - 1) { + grid_del_lines(&default_grid, 0, 1, (int)Rows, 0, (int)Columns); msg_scrolled++; } else { cmdline_row++; @@ -4801,13 +4800,14 @@ win_redr_status_matches ( } } - screen_puts(buf, row, 0, attr); + grid_puts(&default_grid, buf, row, 0, attr); if (selstart != NULL && highlight) { *selend = NUL; - screen_puts(selstart, row, selstart_col, HL_ATTR(HLF_WM)); + grid_puts(&default_grid, selstart, row, selstart_col, HL_ATTR(HLF_WM)); } - screen_fill(row, row + 1, clen, (int)Columns, fillchar, fillchar, attr); + grid_fill(&default_grid, row, row + 1, clen, (int)default_grid.Columns, + fillchar, fillchar, attr); } win_redraw_last_status(topframe); @@ -4905,15 +4905,15 @@ static void win_redr_status(win_T *wp, int ignore_pum) } } - row = wp->w_winrow + wp->w_height; - screen_puts(p, row, wp->w_wincol, attr); - screen_fill(row, row + 1, len + wp->w_wincol, - this_ru_col + wp->w_wincol, fillchar, fillchar, attr); + row = W_ENDROW(wp); + grid_puts(&default_grid, p, row, wp->w_wincol, attr); + grid_fill(&default_grid, row, row + 1, len + wp->w_wincol, + this_ru_col + wp->w_wincol, fillchar, fillchar, attr); if (get_keymap_str(wp, (char_u *)"<%s>", NameBuff, MAXPATHL) && this_ru_col - len > (int)(STRLEN(NameBuff) + 1)) - screen_puts(NameBuff, row, (int)(this_ru_col - STRLEN(NameBuff) - - 1 + wp->w_wincol), attr); + grid_puts(&default_grid, NameBuff, row, + (int)(this_ru_col - STRLEN(NameBuff) - 1), attr); win_redr_ruler(wp, TRUE); } @@ -4927,8 +4927,7 @@ static void win_redr_status(win_T *wp, int ignore_pum) } else { fillchar = fillchar_vsep(wp, &attr); } - screen_putchar(fillchar, wp->w_winrow + wp->w_height, - W_ENDCOL(wp), attr); + grid_putchar(&default_grid, fillchar, W_ENDROW(wp), W_ENDCOL(wp), attr); } busy = FALSE; } @@ -5072,10 +5071,10 @@ win_redr_custom ( row = 0; fillchar = ' '; attr = HL_ATTR(HLF_TPF); - maxwidth = Columns; + maxwidth = default_grid.Columns; use_sandbox = was_set_insecurely((char_u *)"tabline", 0); } else { - row = wp->w_winrow + wp->w_height; + row = W_ENDROW(wp); fillchar = fillchar_status(&attr, wp); maxwidth = wp->w_width; @@ -5091,13 +5090,14 @@ win_redr_custom ( if (*stl++ != '(') stl = p_ruf; } - col = ru_col - (Columns - wp->w_width); - if (col < (wp->w_width + 1) / 2) + col = ru_col - (default_grid.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 = Rows - 1; - --maxwidth; /* writing in last column may cause scrolling */ + row = default_grid.Rows - 1; + maxwidth--; // writing in last column may cause scrolling fillchar = ' '; attr = 0; } @@ -5154,9 +5154,9 @@ win_redr_custom ( curattr = attr; p = buf; for (n = 0; hltab[n].start != NULL; n++) { - int len = (int)(hltab[n].start - p); - screen_puts_len(p, len, row, col, curattr); - col += vim_strnsize(p, len); + int textlen = (int)(hltab[n].start - p); + grid_puts_len(&default_grid, p, textlen, row, col, curattr); + col += vim_strnsize(p, textlen); p = hltab[n].start; if (hltab[n].userhl == 0) @@ -5169,9 +5169,10 @@ win_redr_custom ( curattr = highlight_user[hltab[n].userhl - 1]; } // Make sure to use an empty string instead of p, if p is beyond buf + len. - screen_puts(p >= buf + len ? (char_u *)"" : p, row, col, curattr); + grid_puts(&default_grid, p >= buf + len ? (char_u *)"" : p, row, col, + curattr); - screen_puts_line_flush(false); + grid_puts_line_flush(&default_grid, false); if (wp == NULL) { // Fill the tab_page_click_defs array for clicking in the tab pages line. @@ -5189,7 +5190,7 @@ win_redr_custom ( p = (char_u *) tabtab[n].start; cur_click_def = tabtab[n].def; } - while (col < Columns) { + while (col < default_grid.Columns) { tab_page_click_defs[col++] = cur_click_def; } } @@ -5242,43 +5243,80 @@ static void schar_copy(char_u *sc1, char_u *sc2) STRLCPY(sc1, sc2, sizeof(schar_T)); } +static int line_off2cells(schar_T *line, size_t off, size_t max_off) +{ + return (off + 1 < max_off && line[off + 1][0] == 0) ? 2 : 1; +} -/* - * Output a single character directly to the screen and update ScreenLines. - */ -void screen_putchar(int c, int row, int col, int attr) +/// Return number of display cells for char at grid->chars[off]. +/// We make sure that the offset used is less than "max_off". +static int grid_off2cells(ScreenGrid *grid, size_t off, size_t max_off) +{ + return line_off2cells(grid->chars, off, max_off); +} + +/// Return true if the character at "row"/"col" on the screen is the left side +/// of a double-width character. +/// +/// Caller must make sure "row" and "col" are not invalid! +bool grid_lefthalve(ScreenGrid *grid, int row, int col) +{ + screen_adjust_grid(&grid, &row, &col); + + return grid_off2cells(grid, grid->line_offset[row] + col, + grid->line_offset[row] + grid->Columns) > 1; +} + +/// Correct a position on the screen, if it's the right half of a double-wide +/// char move it to the left half. Returns the corrected column. +int grid_fix_col(ScreenGrid *grid, int col, int row) +{ + int coloff = 0; + screen_adjust_grid(&grid, &row, &coloff); + + col += coloff; + if (grid->chars != NULL && col > 0 + && grid->chars[grid->line_offset[row] + col][0] == 0) { + return col - 1 - coloff; + } + return col - coloff; +} + +/// output a single character directly to the grid +void grid_putchar(ScreenGrid *grid, int c, int row, int col, int attr) { char_u buf[MB_MAXBYTES + 1]; buf[utf_char2bytes(c, buf)] = NUL; - screen_puts(buf, row, col, attr); + grid_puts(grid, buf, row, col, attr); } -/* - * Get a single character directly from ScreenLines into "bytes[]". - * Also return its attribute in *attrp; - */ -void screen_getbytes(int row, int col, char_u *bytes, int *attrp) +/// get a single character directly from grid.chars into "bytes[]". +/// Also return its attribute in *attrp; +void grid_getbytes(ScreenGrid *grid, int row, int col, char_u *bytes, + int *attrp) { unsigned off; - /* safety check */ - if (ScreenLines != NULL && row < screen_Rows && col < screen_Columns) { - off = LineOffset[row] + col; - *attrp = ScreenAttrs[off]; - schar_copy(bytes, ScreenLines[off]); + screen_adjust_grid(&grid, &row, &col); + + + // safety check + if (grid->chars != NULL && row < grid->Rows && col < grid->Columns) { + off = grid->line_offset[row] + col; + *attrp = grid->attrs[off]; + schar_copy(bytes, grid->chars[off]); } } -/* - * Put string '*text' on the screen at position 'row' and 'col', with - * attributes 'attr', and update ScreenLines[] and ScreenAttrs[]. - * Note: only outputs within one row, message is truncated at screen boundary! - * Note: if ScreenLines[], row and/or col is invalid, nothing is done. - */ -void screen_puts(char_u *text, int row, int col, int attr) + +/// put string '*text' on the window grid at position 'row' and 'col', with +/// attributes 'attr', and update chars[] and attrs[]. +/// Note: only outputs within one row, message is truncated at grid boundary! +/// Note: if grid, row and/or col is invalid, nothing is done. +void grid_puts(ScreenGrid *grid, char_u *text, int row, int col, int attr) { - screen_puts_len(text, -1, row, col, attr); + grid_puts_len(grid, text, -1, row, col, attr); } static int put_dirty_row = -1; @@ -5295,11 +5333,10 @@ void screen_puts_line_start(int row) put_dirty_row = row; } -/* - * Like screen_puts(), but output "text[len]". When "len" is -1 output up to - * a NUL. - */ -void screen_puts_len(char_u *text, int textlen, int row, int col, int attr) +/// like grid_puts(), but output "text[len]". When "len" is -1 output up to +/// a NUL. +void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, + int col, int attr) { unsigned off; char_u *ptr = text; @@ -5317,8 +5354,15 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr) int force_redraw_this; int force_redraw_next = FALSE; int need_redraw; - bool do_flush = false; + + screen_adjust_grid(&grid, &row, &col); + + // safety check + if (grid->chars == NULL || row >= grid->Rows || col >= grid->Columns) { + return; + } + if (put_dirty_row == -1) { screen_puts_line_start(row); do_flush = true; @@ -5327,16 +5371,13 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr) abort(); } } - - if (ScreenLines == NULL || row >= screen_Rows) /* safety check */ - return; - off = LineOffset[row] + col; + off = grid->line_offset[row] + col; /* 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 < screen_Columns && mb_fix_col(col, row) != col) { - schar_from_ascii(ScreenLines[off - 1], ' '); - ScreenAttrs[off - 1] = 0; + 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; // redraw the previous cell, make it empty if (put_dirty_first == -1) { put_dirty_first = col-1; @@ -5346,8 +5387,8 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr) force_redraw_next = true; } - max_off = LineOffset[row] + screen_Columns; - while (col < screen_Columns + max_off = grid->line_offset[row] + grid->Columns; + while (col < grid->Columns && (len < 0 || (int)(ptr - text) < len) && *ptr != NUL) { c = *ptr; @@ -5380,7 +5421,7 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr) } else { prev_c = u8c; } - if (col + mbyte_cells > screen_Columns) { + if (col + mbyte_cells > grid->Columns) { // Only 1 cell left, but character requires 2 cells: // display a '>' in the last column to avoid wrapping. */ c = '>'; @@ -5394,9 +5435,9 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr) force_redraw_this = force_redraw_next; force_redraw_next = FALSE; - need_redraw = schar_cmp(ScreenLines[off], buf) - || (mbyte_cells == 2 && ScreenLines[off + 1][0] != 0) - || ScreenAttrs[off] != attr + 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) { @@ -5409,18 +5450,19 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr) clear_next_cell = false; } else if ((len < 0 ? ptr[mbyte_blen] == NUL : ptr + mbyte_blen >= text + len) - && ((mbyte_cells == 1 && utf_off2cells(off, max_off) > 1) + && ((mbyte_cells == 1 + && grid_off2cells(grid, off, max_off) > 1) || (mbyte_cells == 2 - && utf_off2cells(off, max_off) == 1 - && utf_off2cells(off + 1, max_off) > 1))) { + && grid_off2cells(grid, off, max_off) == 1 + && grid_off2cells(grid, off + 1, max_off) > 1))) { clear_next_cell = true; } - schar_copy(ScreenLines[off], buf); - ScreenAttrs[off] = attr; + schar_copy(grid->chars[off], buf); + grid->attrs[off] = attr; if (mbyte_cells == 2) { - ScreenLines[off + 1][0] = 0; - ScreenAttrs[off + 1] = attr; + grid->chars[off + 1][0] = 0; + grid->attrs[off + 1] = attr; } if (put_dirty_first == -1) { put_dirty_first = col; @@ -5439,25 +5481,27 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr) } if (do_flush) { - screen_puts_line_flush(true); + grid_puts_line_flush(grid, true); } } /// End a group of screen_puts_len calls and send the screen buffer to the UI /// layer. /// +/// @param grid The grid which contains the buffer. /// @param set_cursor Move the visible cursor to the end of the changed region. /// This is a workaround for not yet refactored code paths /// and shouldn't be used in new code. -void screen_puts_line_flush(bool set_cursor) +void grid_puts_line_flush(ScreenGrid *grid, bool set_cursor) { assert(put_dirty_row != -1); if (put_dirty_first != -1) { if (set_cursor) { - ui_cursor_goto(put_dirty_row, put_dirty_last); + ui_grid_cursor_goto(grid->handle, put_dirty_row, + MIN(put_dirty_last, grid->Columns-1)); } - ui_line(put_dirty_row, put_dirty_first, put_dirty_last, put_dirty_last, 0, - false); + ui_line(grid, put_dirty_row, put_dirty_first, put_dirty_last, + put_dirty_last, 0, false); put_dirty_first = -1; put_dirty_last = 0; } @@ -5760,23 +5804,33 @@ next_search_hl_pos( } -/* - * Fill the screen from 'start_row' to 'end_row', from 'start_col' to 'end_col' - * with character 'c1' in first column followed by 'c2' in the other columns. - * Use attributes 'attr'. - */ -void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1, int c2, int attr) +/// Fill the grid from 'start_row' to 'end_row', from 'start_col' to 'end_col' +/// with character 'c1' in first column followed by 'c2' in the other columns. +/// Use attributes 'attr'. +void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, + int end_col, int c1, int c2, int attr) { schar_T sc; - if (end_row > screen_Rows) /* safety check */ - end_row = screen_Rows; - if (end_col > screen_Columns) /* safety check */ - end_col = screen_Columns; - if (ScreenLines == NULL - || start_row >= end_row - || start_col >= end_col) /* nothing to do */ + int row_off = 0, col_off = 0; + screen_adjust_grid(&grid, &row_off, &col_off); + start_row += row_off; + end_row += row_off; + start_col += col_off; + end_col += col_off; + + // safety check + if (end_row > grid->Rows) { + end_row = grid->Rows; + } + if (end_col > grid->Columns) { + end_col = grid->Columns; + } + + // nothing to do + if (grid->chars == NULL || start_row >= end_row || start_col >= end_col) { return; + } for (int row = start_row; row < end_row; row++) { if (has_mbyte) { @@ -5784,24 +5838,30 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1, // out the left halve. When drawing over the left halve of a // double wide-char clear out the right halve. Only needed in a // terminal. - if (start_col > 0 && mb_fix_col(start_col, row) != start_col) { - screen_puts_len((char_u *)" ", 1, row, start_col - 1, 0); + if (start_col > 0 && grid_fix_col(grid, start_col, row) != start_col) { + grid_puts_len(grid, (char_u *)" ", 1, row, start_col - 1, 0); } - if (end_col < screen_Columns && mb_fix_col(end_col, row) != end_col) { - screen_puts_len((char_u *)" ", 1, row, end_col, 0); + if (end_col < grid->Columns + && grid_fix_col(grid, end_col, row) != end_col) { + grid_puts_len(grid, (char_u *)" ", 1, row, end_col, 0); } } + // if grid was resized (in ext_multigrid mode), the UI has no redraw updates + // for the newly resized grid. It is better mark everything as dirty and + // send all the updates. int dirty_first = INT_MAX; int dirty_last = 0; + int col = start_col; schar_from_char(sc, c1); - int lineoff = LineOffset[row]; + int lineoff = grid->line_offset[row]; for (col = start_col; col < end_col; col++) { int off = lineoff + col; - if (schar_cmp(ScreenLines[off], sc) || ScreenAttrs[off] != attr) { - schar_copy(ScreenLines[off], sc); - ScreenAttrs[off] = attr; + if (schar_cmp(grid->chars[off], sc) + || grid->attrs[off] != attr) { + schar_copy(grid->chars[off], sc); + grid->attrs[off] = attr; if (dirty_first == INT_MAX) { dirty_first = col; } @@ -5820,16 +5880,16 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1, put_dirty_last = MAX(put_dirty_last, dirty_last); } else { int last = c2 != ' ' ? dirty_last : dirty_first + (c1 != ' '); - ui_line(row, dirty_first, last, dirty_last, attr, false); + ui_line(grid, row, dirty_first, last, dirty_last, attr, false); } } - if (end_col == Columns) { - LineWraps[row] = false; + if (end_col == grid->Columns) { + grid->line_wraps[row] = false; } // TODO(bfredl): The relevant caller should do this - if (row == Rows - 1) { // overwritten the command line + if (row == default_grid.Rows - 1) { // overwritten the command line redraw_cmdline = true; if (start_col == 0 && end_col == Columns && c1 == ' ' && c2 == ' ' && attr == 0) { @@ -5867,24 +5927,90 @@ void check_for_delay(int check_msg_scroll) */ int screen_valid(int doclear) { - screenalloc(doclear); /* allocate screen buffers if size changed */ - return ScreenLines != NULL; + screenalloc(doclear); // allocate screen buffers if size changed + return default_grid.chars != NULL; } -/* - * Resize the shell to Rows and Columns. - * Allocate ScreenLines[] and associated items. - * - * There may be some time between setting Rows and Columns and (re)allocating - * ScreenLines[]. This happens when starting up and when (manually) changing - * the shell size. Always use screen_Rows and screen_Columns to access items - * in ScreenLines[]. Use Rows and Columns for positioning text etc. where the - * final size of the shell is needed. - */ +/// (Re)allocates a window grid if size changed while in ext_multigrid mode. +/// Updates size, offsets and handle for the grid regardless. +/// +/// If "doclear" is true, don't try to copy from the old grid rather clear the +/// resized grid. +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; + } + + // TODO(bfredl): floating windows should force this to true + bool want_allocation = ui_is_external(kUIMultigrid); + bool has_allocation = (grid->chars != NULL); + + if (want_allocation && has_allocation && highlights_invalid) { + grid_invalidate(grid); + } + + int was_resized = false; + if ((has_allocation != want_allocation) + || grid->Rows != rows + || grid->Columns != columns) { + if (want_allocation) { + grid_alloc(grid, rows, columns, true); + win_free_lsize(wp); + win_alloc_lines(wp); + } 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; + } + was_resized = true; + } + + grid->row_offset = wp->w_winrow; + grid->col_offset = wp->w_wincol; + + // send grid resize event if: + // - 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)) { + ui_call_grid_resize(grid->handle, grid->Columns, grid->Rows); + } +} + +/// assign a handle to the grid. The grid need not be allocated. +void grid_assign_handle(ScreenGrid *grid) +{ + static int last_grid_handle = DEFAULT_GRID_HANDLE; + + // only assign a grid handle if not already + if (grid->handle == 0) { + grid->handle = ++last_grid_handle; + } +} + +/// Resize the screen to Rows and Columns. +/// +/// Allocate default_grid.chars[] and other grid arrays. +/// +/// There may be some time between setting Rows and Columns and (re)allocating +/// default_grid arrays. This happens when starting up and when +/// (manually) changing the shell size. Always use default_grid.Rows and +/// 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) { - int new_row, old_row; - int len; static bool entered = false; // avoid recursiveness int retry_count = 0; @@ -5892,13 +6018,13 @@ retry: // Allocation of the screen buffers is done only when the size changes and // when Rows and Columns have been set and we have started doing full // screen stuff. - if ((ScreenLines != NULL - && Rows == screen_Rows - && Columns == screen_Columns + if ((default_grid.chars != NULL + && Rows == default_grid.Rows + && Columns == default_grid.Columns ) || Rows == 0 || Columns == 0 - || (!full_screen && ScreenLines == NULL)) { + || (!full_screen && default_grid.chars == NULL)) { return; } @@ -5921,30 +6047,22 @@ retry: comp_col(); /* recompute columns for shown command and ruler */ - /* - * We're changing the size of the screen. - * - Allocate new arrays for ScreenLines and ScreenAttrs. - * - Move lines from the old arrays into the new arrays, clear extra - * lines (unless the screen is going to be cleared). - * - Free the old arrays. - * - * If anything fails, make ScreenLines NULL, so we don't do anything! - * Continuing with the old ScreenLines may result in a crash, because the - * size is wrong. - */ + // We're changing the size of the screen. + // - Allocate new arrays for default_grid + // - Move lines from the old arrays into the new arrays, clear extra + // lines (unless the screen is going to be cleared). + // - Free the old arrays. + // + // 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); - // Allocate space for an extra row as scratch space, so that a redrawn - // line can be compared with the previous screen line state. - size_t ncells = (size_t)((Rows+1) * Columns); - schar_T *new_ScreenLines = xmalloc(ncells * sizeof(*new_ScreenLines)); - sattr_T *new_ScreenAttrs = xmalloc(ncells * sizeof(*new_ScreenAttrs)); - unsigned *new_LineOffset = xmalloc((size_t)(Rows * sizeof(*new_LineOffset))); - char_u *new_LineWraps = xmalloc((size_t)(Rows * sizeof(*new_LineWraps))); + grid_alloc(&default_grid, Rows, Columns, !doclear); StlClickDefinition *new_tab_page_click_defs = xcalloc( (size_t)Columns, sizeof(*new_tab_page_click_defs)); @@ -5955,53 +6073,15 @@ retry: win_alloc_lines(aucmd_win); } - for (new_row = 0; new_row < Rows; new_row++) { - new_LineOffset[new_row] = new_row * Columns; - new_LineWraps[new_row] = false; - - // If the screen is not going to be cleared, copy as much as - // possible from the old screen to the new one and clear the rest - // (used when resizing the window at the "--more--" prompt or when - // executing an external command, for the GUI). - if (!doclear) { - for (int col = 0; col < Columns; col++) { - schar_from_ascii(new_ScreenLines[new_row * Columns + col], ' '); - } - memset(new_ScreenAttrs + new_row * Columns, - 0, (size_t)Columns * sizeof(*new_ScreenAttrs)); - old_row = new_row + (screen_Rows - Rows); - if (old_row >= 0 && ScreenLines != NULL) { - if (screen_Columns < Columns) { - len = screen_Columns; - } else { - len = Columns; - } - - memmove(new_ScreenLines + new_LineOffset[new_row], - ScreenLines + LineOffset[old_row], - (size_t)len * sizeof(schar_T)); - memmove(new_ScreenAttrs + new_LineOffset[new_row], - ScreenAttrs + LineOffset[old_row], - (size_t)len * sizeof(new_ScreenAttrs[0])); - } - } - } - // Use the last line of the screen for the current line. - current_ScreenLine = new_ScreenLines + Rows * Columns; - - free_screenlines(); + clear_tab_page_click_defs(tab_page_click_defs, tab_page_click_defs_size); + xfree(tab_page_click_defs); - ScreenLines = new_ScreenLines; - ScreenAttrs = new_ScreenAttrs; - LineOffset = new_LineOffset; - LineWraps = new_LineWraps; tab_page_click_defs = new_tab_page_click_defs; - tab_page_click_defs_size = Columns; + tab_page_click_defs_size = default_grid.Columns; - /* It's important that screen_Rows and screen_Columns reflect the actual - * size of ScreenLines[]. Set them before calling anything. */ - screen_Rows = Rows; - screen_Columns = 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) @@ -6023,14 +6103,75 @@ retry: } } -void free_screenlines(void) +void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy) { - xfree(ScreenLines); - xfree(ScreenAttrs); - xfree(LineOffset); - xfree(LineWraps); - clear_tab_page_click_defs(tab_page_click_defs, tab_page_click_defs_size); - xfree(tab_page_click_defs); + int new_row; + ScreenGrid new = *grid; + + size_t ncells = (size_t)((rows+1) * columns); + new.chars = xmalloc(ncells * sizeof(schar_T)); + new.attrs = xmalloc(ncells * sizeof(sattr_T)); + new.line_offset = xmalloc((size_t)(rows * sizeof(unsigned))); + new.line_wraps = xmalloc((size_t)(rows * sizeof(char_u))); + + new.Rows = rows; + new.Columns = columns; + + for (new_row = 0; new_row < new.Rows; new_row++) { + 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); + + if (copy) { + // If the screen is not going to be cleared, copy as much as + // possible from the old screen to the new one and clear the rest + // (used when resizing the window at the "--more--" prompt or when + // executing an external command, for the GUI). + if (new_row < grid->Rows && grid->chars != NULL) { + int len = MIN(grid->Columns, new.Columns); + memmove(new.chars + new.line_offset[new_row], + grid->chars + grid->line_offset[new_row], + (size_t)len * sizeof(schar_T)); + memmove(new.attrs + new.line_offset[new_row], + grid->attrs + grid->line_offset[new_row], + (size_t)len * sizeof(sattr_T)); + } + } + } + grid_free(grid); + *grid = new; + + // Share a single scratch buffer for all grids, by + // ensuring it is as wide as the widest grid. + if (linebuf_size < (size_t)columns) { + xfree(linebuf_char); + xfree(linebuf_attr); + linebuf_char = xmalloc(columns * sizeof(schar_T)); + linebuf_attr = xmalloc(columns * sizeof(sattr_T)); + linebuf_size = columns; + } +} + +void grid_free(ScreenGrid *grid) +{ + xfree(grid->chars); + xfree(grid->attrs); + xfree(grid->line_offset); + xfree(grid->line_wraps); + + grid->chars = NULL; + grid->attrs = NULL; + grid->line_offset = NULL; + grid->line_wraps = NULL; +} + +/// Doesn't allow reinit, so must only be called by free_all_mem! +void screen_free_all_mem(void) +{ + grid_free(&default_grid); + xfree(linebuf_char); + xfree(linebuf_attr); } /// Clear tab_page_click_defs table @@ -6061,14 +6202,15 @@ static void screenclear2(void) { int i; - if (starting == NO_SCREEN || ScreenLines == NULL) { + if (starting == NO_SCREEN || default_grid.chars == NULL) { return; } - // blank out ScreenLines - for (i = 0; i < Rows; i++) { - lineclear(LineOffset[i], (int)Columns, true); - LineWraps[i] = false; + // blank out the default grid + for (i = 0; i < default_grid.Rows; i++) { + grid_clear_line(&default_grid, default_grid.line_offset[i], + (int)default_grid.Columns, true); + default_grid.line_wraps[i] = false; } ui_call_grid_clear(1); // clear the display @@ -6089,27 +6231,33 @@ static void screenclear2(void) msg_didout = false; } -/* - * Clear one line in ScreenLines. - */ -static void lineclear(unsigned off, int width, bool valid) +/// clear a line in the grid starting at "off" until "width" characters +/// are cleared. +static void grid_clear_line(ScreenGrid *grid, unsigned off, int width, + bool valid) { for (int col = 0; col < width; col++) { - schar_from_ascii(ScreenLines[off + col], ' '); + schar_from_ascii(grid->chars[off + col], ' '); } int fill = valid ? 0 : -1; - (void)memset(ScreenAttrs + off, fill, (size_t)width * sizeof(sattr_T)); + (void)memset(grid->attrs + off, fill, (size_t)width * sizeof(sattr_T)); } -/// Copy part of a Screenline for vertically split window. -static void linecopy(int to, int from, int col, int width) +static void grid_invalidate(ScreenGrid *grid) { - unsigned off_to = LineOffset[to] + col; - unsigned off_from = LineOffset[from] + col; + (void)memset(grid->attrs, -1, grid->Rows * grid->Columns * sizeof(sattr_T)); +} + - memmove(ScreenLines + off_to, ScreenLines + off_from, +/// Copy part of a grid line for vertically split window. +static void linecopy(ScreenGrid *grid, int to, int from, int col, int width) +{ + unsigned off_to = grid->line_offset[to] + col; + unsigned off_from = grid->line_offset[from] + col; + + memmove(grid->chars + off_to, grid->chars + off_from, width * sizeof(schar_T)); - memmove(ScreenAttrs + off_to, ScreenAttrs + off_from, + memmove(grid->attrs + off_to, grid->attrs + off_from, width * sizeof(sattr_T)); } @@ -6120,16 +6268,20 @@ void setcursor(void) { if (redrawing()) { validate_cursor(); - int left_offset = curwin->w_wcol; + + ScreenGrid *grid = &curwin->w_grid; + int row = curwin->w_wrow; + int col = curwin->w_wcol; if (curwin->w_p_rl) { // With 'rightleft' set and the cursor on a double-wide character, // position it on the leftmost column. - left_offset = curwin->w_width - curwin->w_wcol + col = curwin->w_grid.Columns - curwin->w_wcol - ((utf_ptr2cells(get_cursor_pos_ptr()) == 2 && vim_isprintc(gchar_cursor())) ? 2 : 1); } - ui_cursor_goto(curwin->w_winrow + curwin->w_wrow, - curwin->w_wincol + left_offset); + + screen_adjust_grid(&grid, &row, &col); + ui_grid_cursor_goto(grid->handle, row, col); } } @@ -6156,7 +6308,7 @@ static int win_do_lines(win_T *wp, int row, int line_count, int del) } // No lines are being moved, just draw over the entire area - if (row + line_count >= wp->w_height) { + if (row + line_count >= wp->w_grid.Rows) { return OK; } @@ -6165,15 +6317,12 @@ static int win_do_lines(win_T *wp, int row, int line_count, int del) check_for_delay(false); clear_cmdline = true; int retval; - if (del) { - retval = screen_del_lines(wp->w_winrow + row, line_count, - wp->w_winrow + wp->w_height, - wp->w_wincol, wp->w_width); + retval = grid_del_lines(&wp->w_grid, row, line_count, + wp->w_grid.Rows, 0, wp->w_grid.Columns); } else { - retval = screen_ins_lines(wp->w_winrow + row, line_count, - wp->w_winrow + wp->w_height, - wp->w_wincol, wp->w_width); + retval = grid_ins_lines(&wp->w_grid, row, line_count, + wp->w_grid.Rows, 0, wp->w_grid.Columns); } return retval; } @@ -6189,95 +6338,109 @@ static int win_do_lines(win_T *wp, int row, int line_count, int del) */ -/// insert lines on the screen and update ScreenLines[] +/// insert lines on the screen and move the existing lines down +/// 'line_count' is the number of lines to be inserted. /// '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. +/// '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 screen_ins_lines(int row, int line_count, int end, int col, int width) +int grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col, + int width) { int i; int j; unsigned temp; + int row_off = 0; + screen_adjust_grid(&grid, &row_off, &col); + row += row_off; + end += row_off; + if (!screen_valid(TRUE) || line_count <= 0) { return FAIL; } - // Shift LineOffset[] line_count down to reflect the inserted lines. - // Clear the inserted lines in ScreenLines[]. + // Shift line_offset[] line_count down to reflect the inserted lines. + // Clear the inserted lines. for (i = 0; i < line_count; i++) { - if (width != Columns) { + if (width != grid->Columns) { // need to copy part of a line j = end - 1 - i; while ((j -= line_count) >= row) { - linecopy(j + line_count, j, col, width); + linecopy(grid, j + line_count, j, col, width); } j += line_count; - lineclear(LineOffset[j] + col, width, false); - LineWraps[j] = false; + grid_clear_line(grid, grid->line_offset[j] + col, width, false); + grid->line_wraps[j] = false; } else { j = end - 1 - i; - temp = LineOffset[j]; + temp = grid->line_offset[j]; while ((j -= line_count) >= row) { - LineOffset[j + line_count] = LineOffset[j]; - LineWraps[j + line_count] = LineWraps[j]; + grid->line_offset[j + line_count] = grid->line_offset[j]; + grid->line_wraps[j + line_count] = grid->line_wraps[j]; } - LineOffset[j + line_count] = temp; - LineWraps[j + line_count] = false; - lineclear(temp, (int)Columns, false); + grid->line_offset[j + line_count] = temp; + grid->line_wraps[j + line_count] = false; + grid_clear_line(grid, temp, (int)grid->Columns, false); } } - ui_call_grid_scroll(1, row, end, col, col+width, -line_count, 0); + ui_call_grid_scroll(grid->handle, row, end, col, col+width, -line_count, 0); return OK; } -/// delete lines on the screen and update ScreenLines[] +/// 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 screen_del_lines(int row, int line_count, int end, int col, int width) +int grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col, + int width) { int j; int i; unsigned temp; + int row_off = 0; + screen_adjust_grid(&grid, &row_off, &col); + row += row_off; + end += row_off; + if (!screen_valid(TRUE) || line_count <= 0) { return FAIL; } - // Now shift LineOffset[] line_count up to reflect the deleted lines. - // Clear the inserted lines in ScreenLines[]. + // Now shift line_offset[] line_count up to reflect the deleted lines. + // Clear the inserted lines. for (i = 0; i < line_count; i++) { - if (width != Columns) { + if (width != grid->Columns) { // need to copy part of a line j = row + i; while ((j += line_count) <= end - 1) { - linecopy(j - line_count, j, col, width); + linecopy(grid, j - line_count, j, col, width); } j -= line_count; - lineclear(LineOffset[j] + col, width, false); - LineWraps[j] = false; + grid_clear_line(grid, grid->line_offset[j] + col, width, false); + grid->line_wraps[j] = false; } else { // whole width, moving the line pointers is faster j = row + i; - temp = LineOffset[j]; + temp = grid->line_offset[j]; while ((j += line_count) <= end - 1) { - LineOffset[j - line_count] = LineOffset[j]; - LineWraps[j - line_count] = LineWraps[j]; + grid->line_offset[j - line_count] = grid->line_offset[j]; + grid->line_wraps[j - line_count] = grid->line_wraps[j]; } - LineOffset[j - line_count] = temp; - LineWraps[j - line_count] = false; - lineclear(temp, (int)Columns, false); + grid->line_offset[j - line_count] = temp; + grid->line_wraps[j - line_count] = false; + grid_clear_line(grid, temp, (int)grid->Columns, false); } } - ui_call_grid_scroll(1, row, end, col, col+width, line_count, 0); + ui_call_grid_scroll(grid->handle, row, end, col, col+width, line_count, 0); return OK; } @@ -6324,8 +6487,9 @@ int showmode(void) /* if the cmdline is more than one line high, erase top lines */ need_clear = clear_cmdline; - if (clear_cmdline && cmdline_row < Rows - 1) - msg_clr_cmdline(); /* will reset clear_cmdline */ + if (clear_cmdline && cmdline_row < default_grid.Rows - 1) { + msg_clr_cmdline(); // will reset clear_cmdline + } /* Position on the last line in the window, column 0 */ msg_pos_mode(); @@ -6343,9 +6507,10 @@ int showmode(void) 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 = (Rows - msg_row) * Columns - 3; - if (edit_submode_extra != NULL) + length = (default_grid.Rows - msg_row) * default_grid.Columns - 3; + if (edit_submode_extra != NULL) { length -= vim_strsize(edit_submode_extra); + } if (length > 0) { if (edit_submode_pre != NULL) length -= vim_strsize(edit_submode_pre); @@ -6466,7 +6631,7 @@ int showmode(void) static void msg_pos_mode(void) { msg_col = 0; - msg_row = Rows - 1; + msg_row = default_grid.Rows - 1; } /// Delete mode message. Used when ESC is typed which is expected to end @@ -6525,7 +6690,7 @@ static void draw_tabline(void) int use_sep_chars = (t_colors < 8 ); - if (ScreenLines == NULL) { + if (default_grid.chars == NULL) { return; } redraw_tabline = false; @@ -6540,7 +6705,7 @@ static void draw_tabline(void) // Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect. - assert(Columns == tab_page_click_defs_size); + assert(default_grid.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. */ @@ -6562,7 +6727,7 @@ static void draw_tabline(void) } if (tabcount > 0) { - tabwidth = (Columns - 1 + tabcount / 2) / tabcount; + tabwidth = (default_grid.Columns - 1 + tabcount / 2) / tabcount; } if (tabwidth < 6) { @@ -6573,7 +6738,7 @@ static void draw_tabline(void) tabcount = 0; FOR_ALL_TABS(tp) { - if (col >= Columns - 4) { + if (col >= default_grid.Columns - 4) { break; } @@ -6592,14 +6757,14 @@ static void draw_tabline(void) attr = win_hl_attr(cwp, HLF_TPS); } if (use_sep_chars && col > 0) { - screen_putchar('|', 0, col++, attr); + grid_putchar(&default_grid, '|', 0, col++, attr); } if (tp->tp_topframe != topframe) { attr = win_hl_attr(cwp, HLF_TP); } - screen_putchar(' ', 0, col++, attr); + grid_putchar(&default_grid, ' ', 0, col++, attr); modified = false; @@ -6614,15 +6779,17 @@ static void draw_tabline(void) if (wincount > 1) { vim_snprintf((char *)NameBuff, MAXPATHL, "%d", wincount); len = (int)STRLEN(NameBuff); - if (col + len >= Columns - 3) + if (col + len >= default_grid.Columns - 3) { break; - screen_puts_len(NameBuff, len, 0, col, - hl_combine_attr(attr, win_hl_attr(cwp, HLF_T))); + } + grid_puts_len(&default_grid, NameBuff, len, 0, col, + hl_combine_attr(attr, win_hl_attr(cwp, HLF_T))); col += len; } - if (modified) - screen_puts_len((char_u *)"+", 1, 0, col++, attr); - screen_putchar(' ', 0, col++, attr); + if (modified) { + grid_puts_len(&default_grid, (char_u *)"+", 1, 0, col++, attr); + } + grid_putchar(&default_grid, ' ', 0, col++, attr); } room = scol - col + tabwidth - 1; @@ -6641,13 +6808,14 @@ static void draw_tabline(void) p += len - room; len = room; } - if (len > Columns - col - 1) - len = Columns - col - 1; + if (len > default_grid.Columns - col - 1) { + len = default_grid.Columns - col - 1; + } - screen_puts_len(p, (int)STRLEN(p), 0, col, attr); + grid_puts_len(&default_grid, p, (int)STRLEN(p), 0, col, attr); col += len; } - screen_putchar(' ', 0, col++, attr); + grid_putchar(&default_grid, ' ', 0, col++, attr); // Store the tab page number in tab_page_click_defs[], so that // jump_to_mouse() knows where each one is. @@ -6665,12 +6833,14 @@ static void draw_tabline(void) c = '_'; else c = ' '; - screen_fill(0, 1, col, (int)Columns, c, c, attr_fill); + grid_fill(&default_grid, 0, 1, col, (int)default_grid.Columns, c, c, + attr_fill); /* Put an "X" for closing the current tab if there are several. */ if (first_tabpage->tp_next != NULL) { - screen_putchar('X', 0, (int)Columns - 1, attr_nosel); - tab_page_click_defs[Columns - 1] = (StlClickDefinition) { + grid_putchar(&default_grid, 'X', 0, (int)default_grid.Columns - 1, + attr_nosel); + tab_page_click_defs[default_grid.Columns - 1] = (StlClickDefinition) { .type = kStlClickTabClose, .tabnr = 999, .func = NULL, @@ -6861,15 +7031,15 @@ static void win_redr_ruler(win_T *wp, int always) int off; if (wp->w_status_height) { - row = wp->w_winrow + wp->w_height; + row = W_ENDROW(wp); fillchar = fillchar_status(&attr, wp); off = wp->w_wincol; width = wp->w_width; } else { - row = Rows - 1; + row = default_grid.Rows - 1; fillchar = ' '; attr = 0; - width = Columns; + width = default_grid.Columns; off = 0; } @@ -6904,15 +7074,18 @@ static void win_redr_ruler(win_T *wp, int always) int i = (int)STRLEN(buffer); get_rel_pos(wp, buffer + i + 1, RULER_BUF_LEN - i - 1); int o = i + vim_strsize(buffer + i + 1); - if (wp->w_status_height == 0) /* can't use last char of screen */ - ++o; - int this_ru_col = ru_col - (Columns - width); - if (this_ru_col < 0) + if (wp->w_status_height == 0) { // can't use last char of screen + o++; + } + int this_ru_col = ru_col - (default_grid.Columns - width); + if (this_ru_col < 0) { this_ru_col = 0; - /* Never use more than half the window/screen width, leave the other - * half for the filename. */ - if (this_ru_col < (width + 1) / 2) + } + // Never use more than half the window/screen width, leave the other half + // for the filename. + if (this_ru_col < (width + 1) / 2) { this_ru_col = (width + 1) / 2; + } if (this_ru_col + o < width) { // Need at least 3 chars left for get_rel_pos() + NUL. while (this_ru_col + o < width && RULER_BUF_LEN > i + 4) { @@ -6931,13 +7104,12 @@ static void win_redr_ruler(win_T *wp, int always) } } - screen_puts(buffer, row, this_ru_col + off, attr); + grid_puts(&default_grid, buffer, row, this_ru_col + off, attr); i = redraw_cmdline; - screen_fill(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 */ + 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; @@ -6958,12 +7130,13 @@ int number_width(win_T *wp) int n; linenr_T lnum; - if (wp->w_p_rnu && !wp->w_p_nu) - /* cursor line shows "0" */ - lnum = wp->w_height; - else - /* cursor line shows absolute line number */ + if (wp->w_p_rnu && !wp->w_p_nu) { + // cursor line shows "0" + lnum = wp->w_grid.Rows; + } else { + // cursor line shows absolute line number lnum = wp->w_buffer->b_ml.ml_line_count; + } if (lnum == wp->w_nrwidth_line_count) return wp->w_nrwidth_width; @@ -7019,6 +7192,8 @@ void screen_resize(int width, int height) width = Columns; ui_resize(width, height); + send_grid_resize = true; + /* The window layout used to be adjusted here, but it now happens in * screenalloc() (also invoked from screenclear()). That is because the * "busy" check above may skip this, but not screenalloc(). */ @@ -7040,7 +7215,7 @@ void screen_resize(int width, int height) * - in Ex mode, don't redraw anything. * - Otherwise, redraw right now, and position the cursor. * Always need to call update_screen() or screenalloc(), to make - * sure Rows/Columns and the size of ScreenLines[] is correct! + * sure Rows/Columns and the size of the screen is correct! */ if (State == ASKMORE || State == EXTERNCMD || State == CONFIRM || exmode_active) { @@ -7112,3 +7287,13 @@ void win_new_shellsize(void) shell_new_columns(); // update window sizes } } + +win_T *get_win_by_grid_handle(handle_T handle) +{ + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->w_grid.handle == handle) { + return wp; + } + } + return NULL; +} diff --git a/src/nvim/screen.h b/src/nvim/screen.h index 17515d4253..109541ef07 100644 --- a/src/nvim/screen.h +++ b/src/nvim/screen.h @@ -5,6 +5,7 @@ #include "nvim/types.h" #include "nvim/buffer_defs.h" +#include "nvim/grid_defs.h" #include "nvim/pos.h" /* @@ -20,6 +21,18 @@ #define NOT_VALID 40 /* buffer needs complete redraw */ #define CLEAR 50 /* screen messed up, clear it */ +/// By default, all widows are draw on a single rectangular grid, represented by +/// this ScreenGrid instance. In multigrid mode each window will have its own +/// grid, then this is only used for global screen elements that hasn't been +/// externalized. +/// +/// 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 }); + +#define DEFAULT_GRID_HANDLE 1 // handle for the default_grid + /// Status line click definition typedef struct { enum { diff --git a/src/nvim/search.c b/src/nvim/search.c index f31ec7170b..cf0f1ea287 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -2166,9 +2166,10 @@ showmatch( if (!curwin->w_p_wrap) { getvcol(curwin, lpos, NULL, &vcol, NULL); } - if (curwin->w_p_wrap || (vcol >= curwin->w_leftcol - && vcol < curwin->w_leftcol + curwin->w_width)) { - mpos = *lpos; /* save the pos, update_screen() may change it */ + if (curwin->w_p_wrap + || (vcol >= curwin->w_leftcol + && vcol < curwin->w_leftcol + curwin->w_grid.Columns)) { + mpos = *lpos; // save the pos, update_screen() may change it save_cursor = curwin->w_cursor; save_so = p_so; save_siso = p_siso; diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 11da7195bf..36570e0ded 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -3475,53 +3475,55 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, } \ tgt = proc(obj.via.attr); \ } while (0) -#define CHECK_KEY_IS_STR(entry_name) \ - if (unpacked.data.via.map.ptr[i].key.type != MSGPACK_OBJECT_STR) { \ +#define CHECK_KEY_IS_STR(un, entry_name) \ + if (un.data.via.map.ptr[i].key.type != MSGPACK_OBJECT_STR) { \ emsgf(_(READERR(entry_name, "has key which is not a string")), \ initial_fpos); \ CLEAR_GA_AND_ERROR_OUT(ad_ga); \ - } else if (unpacked.data.via.map.ptr[i].key.via.str.size == 0) { \ + } else if (un.data.via.map.ptr[i].key.via.str.size == 0) { \ emsgf(_(READERR(entry_name, "has empty key")), initial_fpos); \ CLEAR_GA_AND_ERROR_OUT(ad_ga); \ } -#define CHECKED_KEY(entry_name, name, error_desc, tgt, condition, attr, proc) \ +#define CHECKED_KEY(un, entry_name, name, error_desc, tgt, condition, attr, \ + proc) \ else if (CHECK_KEY( /* NOLINT(readability/braces) */ \ - unpacked.data.via.map.ptr[i].key, name)) { \ + un.data.via.map.ptr[i].key, name)) { \ CHECKED_ENTRY( \ condition, "has " name " key value " error_desc, \ - entry_name, unpacked.data.via.map.ptr[i].val, \ + entry_name, un.data.via.map.ptr[i].val, \ tgt, attr, proc); \ } -#define TYPED_KEY(entry_name, name, type_name, tgt, objtype, attr, proc) \ +#define TYPED_KEY(un, entry_name, name, type_name, tgt, objtype, attr, proc) \ CHECKED_KEY( \ - entry_name, name, "which is not " type_name, tgt, \ - unpacked.data.via.map.ptr[i].val.type == MSGPACK_OBJECT_##objtype, \ + un, entry_name, name, "which is not " type_name, tgt, \ + un.data.via.map.ptr[i].val.type == MSGPACK_OBJECT_##objtype, \ attr, proc) -#define BOOLEAN_KEY(entry_name, name, tgt) \ - TYPED_KEY(entry_name, name, "a boolean", tgt, BOOLEAN, boolean, ID) -#define STRING_KEY(entry_name, name, tgt) \ - TYPED_KEY(entry_name, name, "a binary", tgt, BIN, bin, BINDUP) -#define CONVERTED_STRING_KEY(entry_name, name, tgt) \ - TYPED_KEY(entry_name, name, "a binary", tgt, BIN, bin, BIN_CONVERTED) -#define INT_KEY(entry_name, name, tgt, proc) \ +#define BOOLEAN_KEY(un, entry_name, name, tgt) \ + TYPED_KEY(un, entry_name, name, "a boolean", tgt, BOOLEAN, boolean, ID) +#define STRING_KEY(un, entry_name, name, tgt) \ + TYPED_KEY(un, entry_name, name, "a binary", tgt, BIN, bin, BINDUP) +#define CONVERTED_STRING_KEY(un, entry_name, name, tgt) \ + TYPED_KEY(un, entry_name, name, "a binary", tgt, BIN, bin, \ + BIN_CONVERTED) +#define INT_KEY(un, entry_name, name, tgt, proc) \ CHECKED_KEY( \ - entry_name, name, "which is not an integer", tgt, \ - ((unpacked.data.via.map.ptr[i].val.type \ + un, entry_name, name, "which is not an integer", tgt, \ + ((un.data.via.map.ptr[i].val.type \ == MSGPACK_OBJECT_POSITIVE_INTEGER) \ - || (unpacked.data.via.map.ptr[i].val.type \ + || (un.data.via.map.ptr[i].val.type \ == MSGPACK_OBJECT_NEGATIVE_INTEGER)), \ i64, proc) -#define INTEGER_KEY(entry_name, name, tgt) \ - INT_KEY(entry_name, name, tgt, TOINT) -#define LONG_KEY(entry_name, name, tgt) \ - INT_KEY(entry_name, name, tgt, TOLONG) -#define ADDITIONAL_KEY \ +#define INTEGER_KEY(un, entry_name, name, tgt) \ + INT_KEY(un, entry_name, name, tgt, TOINT) +#define LONG_KEY(un, entry_name, name, tgt) \ + INT_KEY(un, entry_name, name, tgt, TOLONG) +#define ADDITIONAL_KEY(un) \ else { /* NOLINT(readability/braces) */ \ ga_grow(&ad_ga, 1); \ - memcpy(((char *)ad_ga.ga_data) + ((size_t) ad_ga.ga_len \ - * sizeof(*unpacked.data.via.map.ptr)), \ - unpacked.data.via.map.ptr + i, \ - sizeof(*unpacked.data.via.map.ptr)); \ + memcpy(((char *)ad_ga.ga_data) + ((size_t)ad_ga.ga_len \ + * sizeof(*un.data.via.map.ptr)), \ + un.data.via.map.ptr + i, \ + sizeof(*un.data.via.map.ptr)); \ ad_ga.ga_len++; \ } #define CONVERTED(str, len) (xmemdupz((str), (len))) @@ -3726,28 +3728,29 @@ shada_read_next_item_start: garray_T ad_ga; ga_init(&ad_ga, sizeof(*(unpacked.data.via.map.ptr)), 1); for (size_t i = 0; i < unpacked.data.via.map.size; i++) { - CHECK_KEY_IS_STR("search pattern") - BOOLEAN_KEY("search pattern", SEARCH_KEY_MAGIC, + CHECK_KEY_IS_STR(unpacked, "search pattern") + BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_MAGIC, entry->data.search_pattern.magic) - BOOLEAN_KEY("search pattern", SEARCH_KEY_SMARTCASE, + BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_SMARTCASE, entry->data.search_pattern.smartcase) - BOOLEAN_KEY("search pattern", SEARCH_KEY_HAS_LINE_OFFSET, + BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_HAS_LINE_OFFSET, entry->data.search_pattern.has_line_offset) - BOOLEAN_KEY("search pattern", SEARCH_KEY_PLACE_CURSOR_AT_END, + BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_PLACE_CURSOR_AT_END, entry->data.search_pattern.place_cursor_at_end) - BOOLEAN_KEY("search pattern", SEARCH_KEY_IS_LAST_USED, + BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_IS_LAST_USED, entry->data.search_pattern.is_last_used) - BOOLEAN_KEY("search pattern", SEARCH_KEY_IS_SUBSTITUTE_PATTERN, + BOOLEAN_KEY(unpacked, "search pattern", + SEARCH_KEY_IS_SUBSTITUTE_PATTERN, entry->data.search_pattern.is_substitute_pattern) - BOOLEAN_KEY("search pattern", SEARCH_KEY_HIGHLIGHTED, + BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_HIGHLIGHTED, entry->data.search_pattern.highlighted) - BOOLEAN_KEY("search pattern", SEARCH_KEY_BACKWARD, + BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_BACKWARD, entry->data.search_pattern.search_backward) - INTEGER_KEY("search pattern", SEARCH_KEY_OFFSET, + INTEGER_KEY(unpacked, "search pattern", SEARCH_KEY_OFFSET, entry->data.search_pattern.offset) - CONVERTED_STRING_KEY("search pattern", SEARCH_KEY_PAT, + CONVERTED_STRING_KEY(unpacked, "search pattern", SEARCH_KEY_PAT, entry->data.search_pattern.pat) - ADDITIONAL_KEY + ADDITIONAL_KEY(unpacked) } if (entry->data.search_pattern.pat == NULL) { emsgf(_(READERR("search pattern", "has no pattern")), initial_fpos); @@ -3768,7 +3771,7 @@ shada_read_next_item_start: garray_T ad_ga; ga_init(&ad_ga, sizeof(*(unpacked.data.via.map.ptr)), 1); for (size_t i = 0; i < unpacked.data.via.map.size; i++) { - CHECK_KEY_IS_STR("mark") + CHECK_KEY_IS_STR(unpacked, "mark") if (CHECK_KEY(unpacked.data.via.map.ptr[i].key, KEY_NAME_CHAR)) { if (type_u64 == kSDItemJump || type_u64 == kSDItemChange) { emsgf(_(READERR("mark", "has n key which is only valid for " @@ -3782,10 +3785,10 @@ shada_read_next_item_start: "mark", unpacked.data.via.map.ptr[i].val, entry->data.filemark.name, u64, TOCHAR); } - LONG_KEY("mark", KEY_LNUM, entry->data.filemark.mark.lnum) - INTEGER_KEY("mark", KEY_COL, entry->data.filemark.mark.col) - STRING_KEY("mark", KEY_FILE, entry->data.filemark.fname) - ADDITIONAL_KEY + LONG_KEY(unpacked, "mark", KEY_LNUM, entry->data.filemark.mark.lnum) + INTEGER_KEY(unpacked, "mark", KEY_COL, entry->data.filemark.mark.col) + STRING_KEY(unpacked, "mark", KEY_FILE, entry->data.filemark.fname) + ADDITIONAL_KEY(unpacked) } if (entry->data.filemark.fname == NULL) { emsgf(_(READERR("mark", "is missing file name")), initial_fpos); @@ -3810,7 +3813,7 @@ shada_read_next_item_start: garray_T ad_ga; ga_init(&ad_ga, sizeof(*(unpacked.data.via.map.ptr)), 1); for (size_t i = 0; i < unpacked.data.via.map.size; i++) { - CHECK_KEY_IS_STR("register") + CHECK_KEY_IS_STR(unpacked, "register") if (CHECK_KEY(unpacked.data.via.map.ptr[i].key, REG_KEY_CONTENTS)) { if (unpacked.data.via.map.ptr[i].val.type != MSGPACK_OBJECT_ARRAY) { @@ -3828,8 +3831,8 @@ shada_read_next_item_start: } const msgpack_object_array arr = unpacked.data.via.map.ptr[i].val.via.array; - for (size_t i = 0; i < arr.size; i++) { - if (arr.ptr[i].type != MSGPACK_OBJECT_BIN) { + for (size_t j = 0; j < arr.size; j++) { + if (arr.ptr[j].type != MSGPACK_OBJECT_BIN) { emsgf(_(READERR("register", "has " REG_KEY_CONTENTS " array " "with non-binary value")), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); @@ -3837,18 +3840,19 @@ shada_read_next_item_start: } entry->data.reg.contents_size = arr.size; entry->data.reg.contents = xmalloc(arr.size * sizeof(char *)); - for (size_t i = 0; i < arr.size; i++) { - entry->data.reg.contents[i] = BIN_CONVERTED(arr.ptr[i].via.bin); + for (size_t j = 0; j < arr.size; j++) { + entry->data.reg.contents[j] = BIN_CONVERTED(arr.ptr[j].via.bin); } } - BOOLEAN_KEY("register", REG_KEY_UNNAMED, entry->data.reg.is_unnamed) - TYPED_KEY("register", REG_KEY_TYPE, "an unsigned integer", + BOOLEAN_KEY(unpacked, "register", REG_KEY_UNNAMED, + entry->data.reg.is_unnamed) + TYPED_KEY(unpacked, "register", REG_KEY_TYPE, "an unsigned integer", entry->data.reg.type, POSITIVE_INTEGER, u64, TOU8) - TYPED_KEY("register", KEY_NAME_CHAR, "an unsigned integer", + TYPED_KEY(unpacked, "register", KEY_NAME_CHAR, "an unsigned integer", entry->data.reg.name, POSITIVE_INTEGER, u64, TOCHAR) - TYPED_KEY("register", REG_KEY_WIDTH, "an unsigned integer", + TYPED_KEY(unpacked, "register", REG_KEY_WIDTH, "an unsigned integer", entry->data.reg.width, POSITIVE_INTEGER, u64, TOSIZE) - ADDITIONAL_KEY + ADDITIONAL_KEY(unpacked) } if (entry->data.reg.contents == NULL) { emsgf(_(READERR("register", "has missing " REG_KEY_CONTENTS " array")), @@ -3990,8 +3994,7 @@ shada_read_next_item_start: .data = unpacked.data.via.array.ptr[i], }; { - msgpack_unpacked unpacked = unpacked_2; - if (unpacked.data.type != MSGPACK_OBJECT_MAP) { + if (unpacked_2.data.type != MSGPACK_OBJECT_MAP) { emsgf(_(RERR "Error while reading ShaDa file: " "buffer list at position %" PRIu64 " " "contains entry that is not a dictionary"), @@ -4000,21 +4003,23 @@ shada_read_next_item_start: } entry->data.buffer_list.buffers[i].pos = default_pos; garray_T ad_ga; - ga_init(&ad_ga, sizeof(*(unpacked.data.via.map.ptr)), 1); + ga_init(&ad_ga, sizeof(*(unpacked_2.data.via.map.ptr)), 1); { + // XXX: Temporarily reassign `i` because the macros depend on it. const size_t j = i; { - for (size_t i = 0; i < unpacked.data.via.map.size; i++) { - CHECK_KEY_IS_STR("buffer list entry") - LONG_KEY("buffer list entry", KEY_LNUM, + for (i = 0; i < unpacked_2.data.via.map.size; i++) { // -V535 + CHECK_KEY_IS_STR(unpacked_2, "buffer list entry") + LONG_KEY(unpacked_2, "buffer list entry", KEY_LNUM, entry->data.buffer_list.buffers[j].pos.lnum) - INTEGER_KEY("buffer list entry", KEY_COL, + INTEGER_KEY(unpacked_2, "buffer list entry", KEY_COL, entry->data.buffer_list.buffers[j].pos.col) - STRING_KEY("buffer list entry", KEY_FILE, + STRING_KEY(unpacked_2, "buffer list entry", KEY_FILE, entry->data.buffer_list.buffers[j].fname) - ADDITIONAL_KEY + ADDITIONAL_KEY(unpacked_2) } } + i = j; // XXX: Restore `i`. } if (entry->data.buffer_list.buffers[i].pos.lnum <= 0) { emsgf(_(RERR "Error while reading ShaDa file: " diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 518bd0922e..a9330e792a 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -2112,9 +2112,9 @@ char_u *did_set_spelllang(win_T *wp) } if (region_mask != 0) { - langp_T *p = GA_APPEND_VIA_PTR(langp_T, &ga); - p->lp_slang = slang; - p->lp_region = region_mask; + langp_T *p_ = GA_APPEND_VIA_PTR(langp_T, &ga); + p_->lp_slang = slang; + p_->lp_region = region_mask; use_midword(slang, wp); if (slang->sl_nobreak) @@ -2190,11 +2190,11 @@ char_u *did_set_spelllang(win_T *wp) } if (region_mask != 0) { - langp_T *p = GA_APPEND_VIA_PTR(langp_T, &ga); - p->lp_slang = slang; - p->lp_sallang = NULL; - p->lp_replang = NULL; - p->lp_region = region_mask; + langp_T *p_ = GA_APPEND_VIA_PTR(langp_T, &ga); + p_->lp_slang = slang; + p_->lp_sallang = NULL; + p_->lp_replang = NULL; + p_->lp_region = region_mask; use_midword(slang, wp); } diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 8be8d24b9f..7a6f2fce39 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -4311,8 +4311,8 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname) qsort(gap->ga_data, (size_t)gap->ga_len, sizeof(fromto_T), rep_compare); - int i = round == 1 ? SN_REP : (round == 2 ? SN_SAL : SN_REPSAL); - putc(i, fd); // <sectionID> + int sect_id = round == 1 ? SN_REP : (round == 2 ? SN_SAL : SN_REPSAL); + putc(sect_id, fd); // <sectionID> // This is for making suggestions, section is not required. putc(0, fd); // <sectionflags> diff --git a/src/nvim/state.c b/src/nvim/state.c index d75f4038ae..bfd73050c3 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -113,7 +113,7 @@ int get_real_state(void) /// @returns[allocated] mode string char *get_mode(void) { - char *buf = xcalloc(3, sizeof(char)); + char *buf = xcalloc(4, sizeof(char)); if (VIsual_active) { if (VIsual_select) { @@ -160,6 +160,8 @@ char *get_mode(void) buf[0] = 'n'; if (finish_op) { buf[1] = 'o'; + // to be able to detect force-linewise/blockwise/characterwise operations + buf[2] = (char)motion_force; } } diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 973d09d065..81c78ca6a9 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -6463,7 +6463,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) // If no argument, list current highlighting. if (ends_excmd((uint8_t)(*line))) { - for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) { + for (i = 1; i <= highlight_ga.ga_len && !got_int; i++) { // TODO(brammool): only call when the group has attributes set highlight_list_one(i); } @@ -6569,8 +6569,8 @@ void do_highlight(const char *line, const bool forceit, const bool init) restore_cterm_colors(); // Clear all default highlight groups and load the defaults. - for (int idx = 0; idx < highlight_ga.ga_len; idx++) { - highlight_clear(idx); + for (int j = 0; j < highlight_ga.ga_len; j++) { + highlight_clear(j); } init_highlight(true, true); highlight_changed(); diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index f715344689..51bf22b31c 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -529,6 +529,20 @@ void terminal_send(Terminal *term, char *data, size_t size) term->opts.write_cb(data, size, term->opts.data); } +void terminal_paste(long count, char_u **y_array, size_t y_size) +{ + for (int i = 0; i < count; i++) { // -V756 + // feed the lines to the terminal + for (size_t j = 0; j < y_size; j++) { + if (j) { + // terminate the previous line + terminal_send(curbuf->terminal, "\n", 1); + } + terminal_send(curbuf->terminal, (char *)y_array[j], STRLEN(y_array[j])); + } + } +} + void terminal_flush_output(Terminal *term) { size_t len = vterm_output_read(term->vt, term->textbuf, diff --git a/src/nvim/testdir/pyxfile/py2_magic.py b/src/nvim/testdir/pyxfile/py2_magic.py new file mode 100644 index 0000000000..819892fd16 --- /dev/null +++ b/src/nvim/testdir/pyxfile/py2_magic.py @@ -0,0 +1,4 @@ +# requires python 2.x + +import sys +print(sys.version) diff --git a/src/nvim/testdir/pyxfile/py2_shebang.py b/src/nvim/testdir/pyxfile/py2_shebang.py new file mode 100644 index 0000000000..13bfc491ec --- /dev/null +++ b/src/nvim/testdir/pyxfile/py2_shebang.py @@ -0,0 +1,4 @@ +#!/usr/bin/python2 + +import sys +print(sys.version) diff --git a/src/nvim/testdir/pyxfile/py3_magic.py b/src/nvim/testdir/pyxfile/py3_magic.py new file mode 100644 index 0000000000..d4b7ee0071 --- /dev/null +++ b/src/nvim/testdir/pyxfile/py3_magic.py @@ -0,0 +1,4 @@ +# requires python 3.x + +import sys +print(sys.version) diff --git a/src/nvim/testdir/pyxfile/py3_shebang.py b/src/nvim/testdir/pyxfile/py3_shebang.py new file mode 100644 index 0000000000..ec05808ca4 --- /dev/null +++ b/src/nvim/testdir/pyxfile/py3_shebang.py @@ -0,0 +1,4 @@ +#!/usr/bin/python3 + +import sys +print(sys.version) diff --git a/src/nvim/testdir/pyxfile/pyx.py b/src/nvim/testdir/pyxfile/pyx.py new file mode 100644 index 0000000000..261a6512c0 --- /dev/null +++ b/src/nvim/testdir/pyxfile/pyx.py @@ -0,0 +1,2 @@ +import sys +print(sys.version) diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index 4fe7db135b..bfd6240f0c 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -26,7 +26,7 @@ " It will be called after each Test_ function. " " When debugging a test it can be useful to add messages to v:errors: -" call add(v:errors, "this happened") +" call add(v:errors, "this happened") " Check that the screen size is at least 24 x 80 characters. @@ -130,6 +130,10 @@ func RunTheTest(test) endtry endif + " In case 'insertmode' was set and something went wrong, make sure it is + " reset to avoid trouble with anything else. + set noinsertmode + if exists("*TearDown") try call TearDown() diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim index 20171bb599..368fc9810d 100644 --- a/src/nvim/testdir/test_arglist.vim +++ b/src/nvim/testdir/test_arglist.vim @@ -170,6 +170,15 @@ func Test_argument() call assert_fails('argument', 'E163:') endfunc +func Test_args_with_quote() + " Only on Unix can a file name include a double quote. + if has('unix') + args \"foobar + call assert_equal('"foobar', argv(0)) + %argdelete + endif +endfunc + " Test for 0argadd and 0argedit " Ported from the test_argument_0count.in test script func Test_zero_argadd() diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim index 0b4b5d1922..df3d691d85 100644 --- a/src/nvim/testdir/test_fold.vim +++ b/src/nvim/testdir/test_fold.vim @@ -704,6 +704,26 @@ func Test_fold_last_line_with_pagedown() enew! endfunc +func Test_folds_with_rnu() + if !CanRunVimInTerminal() + return + endif + + call writefile([ + \ 'set fdm=marker rnu foldcolumn=2', + \ 'call setline(1, ["{{{1", "nline 1", "{{{1", "line 2"])', + \ ], 'Xtest_folds_with_rnu') + let buf = RunVimInTerminal('-S Xtest_folds_with_rnu', {}) + + call VerifyScreenDump(buf, 'Test_folds_with_rnu_01', {}) + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_folds_with_rnu_02', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('Xtest_folds_with_rnu') +endfunc + func Test_folds_marker_in_comment2() new call setline(1, ['Lorem ipsum dolor sit', 'Lorem ipsum dolor sit', 'Lorem ipsum dolor sit']) diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim index 247c01c98d..32593a423a 100644 --- a/src/nvim/testdir/test_mapping.vim +++ b/src/nvim/testdir/test_mapping.vim @@ -230,3 +230,57 @@ func Test_abbreviation_CR() delfunc Eatchar bw! endfunc + +func Test_motionforce_omap() + func GetCommand() + let g:m=mode(1) + let [g:lnum1, g:col1] = searchpos('-', 'Wb') + if g:lnum1 == 0 + return "\<Esc>" + endif + let [g:lnum2, g:col2] = searchpos('-', 'W') + if g:lnum2 == 0 + return "\<Esc>" + endif + return ":call Select()\<CR>" + endfunc + func Select() + call cursor([g:lnum1, g:col1]) + exe "normal! 1 ". (strlen(g:m) == 2 ? 'v' : g:m[2]) + call cursor([g:lnum2, g:col2]) + execute "normal! \<BS>" + endfunc + new + onoremap <buffer><expr> i- GetCommand() + " 1) default omap mapping + %d_ + call setline(1, ['aaa - bbb', 'x', 'ddd - eee']) + call cursor(2, 1) + norm di- + call assert_equal('no', g:m) + call assert_equal(['aaa -- eee'], getline(1, '$')) + " 2) forced characterwise operation + %d_ + call setline(1, ['aaa - bbb', 'x', 'ddd - eee']) + call cursor(2, 1) + norm dvi- + call assert_equal('nov', g:m) + call assert_equal(['aaa -- eee'], getline(1, '$')) + " 3) forced linewise operation + %d_ + call setline(1, ['aaa - bbb', 'x', 'ddd - eee']) + call cursor(2, 1) + norm dVi- + call assert_equal('noV', g:m) + call assert_equal([''], getline(1, '$')) + " 4) forced blockwise operation + %d_ + call setline(1, ['aaa - bbb', 'x', 'ddd - eee']) + call cursor(2, 1) + exe "norm d\<C-V>i-" + call assert_equal("no\<C-V>", g:m) + call assert_equal(['aaabbb', 'x', 'dddeee'], getline(1, '$')) + bwipe! + delfunc Select + delfunc GetCommand +endfunc diff --git a/src/nvim/testdir/test_pyx2.vim b/src/nvim/testdir/test_pyx2.vim new file mode 100644 index 0000000000..50e57c3bfb --- /dev/null +++ b/src/nvim/testdir/test_pyx2.vim @@ -0,0 +1,74 @@ +" Test for pyx* commands and functions with Python 2. + +set pyx=2 +if !has('python') + finish +endif + +let s:py2pattern = '^2\.[0-7]\.\d\+' +let s:py3pattern = '^3\.\d\+\.\d\+' + + +func Test_has_pythonx() + call assert_true(has('pythonx')) +endfunc + + +func Test_pyx() + redir => var + pyx << EOF +import sys +print(sys.version) +EOF + redir END + call assert_match(s:py2pattern, split(var)[0]) +endfunc + + +func Test_pyxdo() + pyx import sys + enew + pyxdo return sys.version.split("\n")[0] + call assert_match(s:py2pattern, split(getline('.'))[0]) +endfunc + + +func Test_pyxeval() + pyx import sys + call assert_match(s:py2pattern, split(pyxeval('sys.version'))[0]) +endfunc + + +func Test_pyxfile() + " No special comments nor shebangs + redir => var + pyxfile pyxfile/pyx.py + redir END + call assert_match(s:py2pattern, split(var)[0]) + + " Python 2 special comment + redir => var + pyxfile pyxfile/py2_magic.py + redir END + call assert_match(s:py2pattern, split(var)[0]) + + " Python 2 shebang + redir => var + pyxfile pyxfile/py2_shebang.py + redir END + call assert_match(s:py2pattern, split(var)[0]) + + if has('python3') + " Python 3 special comment + redir => var + pyxfile pyxfile/py3_magic.py + redir END + call assert_match(s:py3pattern, split(var)[0]) + + " Python 3 shebang + redir => var + pyxfile pyxfile/py3_shebang.py + redir END + call assert_match(s:py3pattern, split(var)[0]) + endif +endfunc diff --git a/src/nvim/testdir/test_pyx3.vim b/src/nvim/testdir/test_pyx3.vim new file mode 100644 index 0000000000..64546b4688 --- /dev/null +++ b/src/nvim/testdir/test_pyx3.vim @@ -0,0 +1,74 @@ +" Test for pyx* commands and functions with Python 3. + +set pyx=3 +if !has('python3') + finish +endif + +let s:py2pattern = '^2\.[0-7]\.\d\+' +let s:py3pattern = '^3\.\d\+\.\d\+' + + +func Test_has_pythonx() + call assert_true(has('pythonx')) +endfunc + + +func Test_pyx() + redir => var + pyx << EOF +import sys +print(sys.version) +EOF + redir END + call assert_match(s:py3pattern, split(var)[0]) +endfunc + + +func Test_pyxdo() + pyx import sys + enew + pyxdo return sys.version.split("\n")[0] + call assert_match(s:py3pattern, split(getline('.'))[0]) +endfunc + + +func Test_pyxeval() + pyx import sys + call assert_match(s:py3pattern, split(pyxeval('sys.version'))[0]) +endfunc + + +func Test_pyxfile() + " No special comments nor shebangs + redir => var + pyxfile pyxfile/pyx.py + redir END + call assert_match(s:py3pattern, split(var)[0]) + + " Python 3 special comment + redir => var + pyxfile pyxfile/py3_magic.py + redir END + call assert_match(s:py3pattern, split(var)[0]) + + " Python 3 shebang + redir => var + pyxfile pyxfile/py3_shebang.py + redir END + call assert_match(s:py3pattern, split(var)[0]) + + if has('python') + " Python 2 special comment + redir => var + pyxfile pyxfile/py2_magic.py + redir END + call assert_match(s:py2pattern, split(var)[0]) + + " Python 2 shebang + redir => var + pyxfile pyxfile/py2_shebang.py + redir END + call assert_match(s:py2pattern, split(var)[0]) + endif +endfunc diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index 756a455ebd..4a143d665d 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -277,3 +277,44 @@ func Test_visual_mode_reset() set belloff& endfunc + +func Test_Visual_sentence_textobject() + new + call setline(1, ['First sentence. Second sentence. Third', 'sentence. Fouth sentence']) + + " When start and end of visual area are identical, 'as' or 'is' select + " the whole sentence. + norm! 1gofdvasy + call assert_equal('Second sentence. ', @") + norm! 1gofdvisy + call assert_equal('Second sentence.', @") + + " When start and end of visual area are not identical, 'as' or 'is' + " extend the sentence in direction of the end of the visual area. + norm! 1gofdvlasy + call assert_equal('d sentence. ', @") + norm! gvasy + call assert_equal("d sentence. Third\nsentence. ", @") + + norm! 1gofdvlisy + call assert_equal('d sentence.', @") + norm! gvisy + call assert_equal('d sentence. ', @") + norm! gvisy + call assert_equal("d sentence. Third\nsentence.", @") + + " Extend visual area in opposite direction. + norm! 1gofdvhasy + call assert_equal(' Second', @") + norm! gvasy + call assert_equal("First sentence. Second", @") + + norm! 1gofdvhisy + call assert_equal('Second', @") + norm! gvisy + call assert_equal(' Second', @") + norm! gvisy + call assert_equal('First sentence. Second', @") + + bwipe! +endfunc diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 0362820687..a0df80a8e8 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -352,8 +352,8 @@ static bool handle_forced_escape(TermInput *input) return false; } -static void read_cb(Stream *stream, RBuffer *buf, size_t c, void *data, - bool eof) +static void read_cb(Stream *stream, RBuffer *buf, size_t count_, void *data, + bool eof) { TermInput *input = data; diff --git a/src/nvim/tui/terminfo.c b/src/nvim/tui/terminfo.c index 27792655c9..14023ce2cb 100644 --- a/src/nvim/tui/terminfo.c +++ b/src/nvim/tui/terminfo.c @@ -137,9 +137,6 @@ unibi_term *terminfo_from_builtin(const char *term, char **termname) if (*termname == NULL) { *termname = xstrdup("builtin_?"); } - // Disable BCE by default (for built-in terminfos). #7624 - // https://github.com/kovidgoyal/kitty/issues/160#issuecomment-346470545 - unibi_set_bool(ut, unibi_back_color_erase, false); return ut; } diff --git a/src/nvim/tui/terminfo_defs.h b/src/nvim/tui/terminfo_defs.h index 82fab415a2..8d77dc7225 100644 --- a/src/nvim/tui/terminfo_defs.h +++ b/src/nvim/tui/terminfo_defs.h @@ -122,7 +122,7 @@ static const int8_t ansi_terminfo[] = { // clr_eos=\E[J, // column_address=\E[%i%p1%dG, // cursor_address=\E[%i%p1%d;%p2%dH, -// cursor_down=\n, +// cursor_down=\E[B, // cursor_home=\E[H, // cursor_invisible=\E[?25l, // cursor_left=^H, @@ -284,7 +284,7 @@ static const int8_t ansi_terminfo[] = { // user8@, // user9@, static const int8_t conemu_terminfo[] = { - 30,2,61,0,38,0,15,0,-99,1,29,3,99,111,110,101,109,117,124,65,78,73,83,32,88,51,46,54,52,32,97,110,100,32,88,116,101,114,109,32,50,53,54,32,99,111,108,111,114,115,32,102,111,114,32,67,111,110,69,109,117,32,119,105,116,104,32,108,105,98,117,118,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,-2,-1,0,0,2,0,4,0,-2,-1,21,0,29,0,33,0,37,0,-1,-1,48,0,65,0,67,0,71,0,78,0,-1,-1,80,0,87,0,-1,-1,91,0,-2,-1,95,0,99,0,-1,-1,-1,-1,-2,-1,-2,-1,103,0,108,0,-1,-1,-2,-1,-2,-1,-2,-1,-1,-1,117,0,122,0,127,0,-124,0,-2,-1,-115,0,-110,0,-1,-1,-2,-1,-101,0,-95,0,-2,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-89,0,-1,-1,-85,0,-1,-1,-1,-1,-1,-1,-83,0,-1,-1,-78,0,-1,-1,-1,-1,-1,-1,-1,-1,-74,0,-69,0,-63,0,-58,0,-53,0,-48,0,-43,0,-37,0,-31,0,-25,0,-19,0,-14,0,-1,-1,-9,0,-1,-1,-5,0,0,1,5,1,9,1,16,1,-1,-1,23,1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,27,1,36,1,45,1,54,1,63,1,72,1,81,1,90,1,99,1,108,1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,117,1,-2,-1,-2,-1,-1,-1,-1,-1,-119,1,-116,1,-105,1,-102,1,-100,1,-97,1,-2,-1,-1,-1,-54,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-52,1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-48,1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-44,1,-39,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-35,1,-1,-1,-1,-1,-28,1,-1,-1,-1,-1,-1,-1,-1,-1,-21,1,-14,1,-7,1,-1,-1,-1,-1,0,2,-1,-1,7,2,-1,-1,-1,-1,-1,-1,14,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,21,2,27,2,33,2,39,2,45,2,51,2,57,2,63,2,69,2,75,2,81,2,87,2,93,2,99,2,105,2,111,2,117,2,123,2,-127,2,-121,2,-115,2,-109,2,-103,2,-97,2,-91,2,-85,2,-79,2,-73,2,-67,2,-61,2,-54,2,-48,2,-42,2,-36,2,-30,2,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-24,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-19,2,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-10,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-5,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,1,3,15,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,91,65,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,54,37,116,59,49,37,59,109,0,9,0,27,91,71,0,27,91,90,0,27,91,52,126,0,27,79,77,0,27,91,51,59,50,126,0,27,91,52,59,50,126,0,27,91,49,59,50,126,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,49,59,54,83,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,51,57,59,52,57,109,0,27,91,51,109,0,27,91,50,51,109,0,27,91,51,56,59,53,59,37,112,49,37,100,109,0,27,91,52,56,59,53,59,37,112,49,37,100,109,0,0,3,0,1,0,73,0,-106,0,-101,1,1,0,1,0,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,0,0,-2,-1,-1,-1,5,0,11,0,-1,-1,-2,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,21,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,30,0,33,0,36,0,39,0,42,0,48,0,54,0,59,0,64,0,69,0,74,0,79,0,83,0,88,0,93,0,98,0,103,0,108,0,114,0,120,0,126,0,-124,0,-118,0,-112,0,-106,0,-100,0,-94,0,-88,0,-82,0,-76,0,-71,0,-66,0,-61,0,-56,0,-51,0,-45,0,-39,0,-33,0,-27,0,-21,0,-15,0,-9,0,-3,0,3,1,9,1,15,1,21,1,27,1,33,1,39,1,45,1,51,1,57,1,63,1,69,1,73,1,78,1,83,1,88,1,93,1,98,1,102,1,106,1,110,1,114,1,119,1,124,1,27,91,51,74,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,91,49,59,50,65,0,65,88,0,71,48,0,88,84,0,85,56,0,67,114,0,67,115,0,69,48,0,69,51,0,77,115,0,83,48,0,83,101,0,83,115,0,84,83,0,88,77,0,103,114,98,111,109,0,103,115,98,111,109,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,69,78,68,56,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,72,79,77,56,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,114,109,120,120,0,115,109,120,120,0,120,109,0 // NOLINT + 30,2,61,0,38,0,15,0,-99,1,31,3,99,111,110,101,109,117,124,65,78,73,83,32,88,51,46,54,52,32,97,110,100,32,88,116,101,114,109,32,50,53,54,32,99,111,108,111,114,115,32,102,111,114,32,67,111,110,69,109,117,32,119,105,116,104,32,108,105,98,117,118,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,-2,-1,0,0,2,0,4,0,-2,-1,21,0,29,0,33,0,37,0,-1,-1,48,0,65,0,69,0,73,0,80,0,-1,-1,82,0,89,0,-1,-1,93,0,-2,-1,97,0,101,0,-1,-1,-1,-1,-2,-1,-2,-1,105,0,110,0,-1,-1,-2,-1,-2,-1,-2,-1,-1,-1,119,0,124,0,-127,0,-122,0,-2,-1,-113,0,-108,0,-1,-1,-2,-1,-99,0,-93,0,-2,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-87,0,-1,-1,-83,0,-1,-1,-1,-1,-1,-1,-81,0,-1,-1,-76,0,-1,-1,-1,-1,-1,-1,-1,-1,-72,0,-67,0,-61,0,-56,0,-51,0,-46,0,-41,0,-35,0,-29,0,-23,0,-17,0,-12,0,-1,-1,-7,0,-1,-1,-3,0,2,1,7,1,11,1,18,1,-1,-1,25,1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,29,1,38,1,47,1,56,1,65,1,74,1,83,1,92,1,101,1,110,1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,119,1,-2,-1,-2,-1,-1,-1,-1,-1,-117,1,-114,1,-103,1,-100,1,-98,1,-95,1,-2,-1,-1,-1,-52,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-50,1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-46,1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-42,1,-37,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-33,1,-1,-1,-1,-1,-26,1,-1,-1,-1,-1,-1,-1,-1,-1,-19,1,-12,1,-5,1,-1,-1,-1,-1,2,2,-1,-1,9,2,-1,-1,-1,-1,-1,-1,16,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,23,2,29,2,35,2,41,2,47,2,53,2,59,2,65,2,71,2,77,2,83,2,89,2,95,2,101,2,107,2,113,2,119,2,125,2,-125,2,-119,2,-113,2,-107,2,-101,2,-95,2,-89,2,-83,2,-77,2,-71,2,-65,2,-59,2,-52,2,-46,2,-40,2,-34,2,-28,2,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-22,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-17,2,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-8,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-3,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,3,3,17,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,91,65,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,54,37,116,59,49,37,59,109,0,9,0,27,91,71,0,27,91,90,0,27,91,52,126,0,27,79,77,0,27,91,51,59,50,126,0,27,91,52,59,50,126,0,27,91,49,59,50,126,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,49,59,54,83,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,51,57,59,52,57,109,0,27,91,51,109,0,27,91,50,51,109,0,27,91,51,56,59,53,59,37,112,49,37,100,109,0,27,91,52,56,59,53,59,37,112,49,37,100,109,0,0,3,0,1,0,74,0,-104,0,-95,1,1,0,1,0,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,0,0,-2,-1,-1,-1,5,0,-1,-1,11,0,-1,-1,-2,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,21,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,30,0,33,0,39,0,42,0,45,0,48,0,54,0,60,0,65,0,70,0,75,0,80,0,85,0,89,0,94,0,99,0,104,0,109,0,114,0,120,0,126,0,-124,0,-118,0,-112,0,-106,0,-100,0,-94,0,-88,0,-82,0,-76,0,-70,0,-65,0,-60,0,-55,0,-50,0,-45,0,-39,0,-33,0,-27,0,-21,0,-15,0,-9,0,-3,0,3,1,9,1,15,1,21,1,27,1,33,1,39,1,45,1,51,1,57,1,63,1,69,1,75,1,79,1,84,1,89,1,94,1,99,1,104,1,108,1,112,1,116,1,120,1,125,1,-126,1,27,91,51,74,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,91,49,59,50,65,0,65,88,0,71,48,0,88,84,0,85,56,0,67,114,0,67,115,0,69,48,0,69,51,0,77,115,0,83,48,0,83,101,0,83,109,117,108,120,0,83,115,0,84,83,0,88,77,0,103,114,98,111,109,0,103,115,98,111,109,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,69,78,68,56,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,72,79,77,56,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,114,109,120,120,0,115,109,120,120,0,120,109,0 // NOLINT }; // cygwin|ANSI emulation for Cygwin, @@ -1780,7 +1780,7 @@ static const int8_t vte_256colour_terminfo[] = { // clr_eos=\E[J, // column_address=\E[%i%p1%dG, // cursor_address=\E[%i%p1%d;%p2%dH, -// cursor_down=\n, +// cursor_down=\E[B, // cursor_home=\E[H, // cursor_invisible=\E[?25l, // cursor_left=^H, @@ -1942,7 +1942,7 @@ static const int8_t vte_256colour_terminfo[] = { // user8@, // user9@, static const int8_t vtpcon_terminfo[] = { - 30,2,71,0,38,0,15,0,-99,1,19,4,118,116,112,99,111,110,124,65,78,73,83,32,101,109,117,108,97,116,105,111,110,32,102,111,114,32,99,111,110,115,111,108,101,32,118,105,114,116,117,97,108,32,116,101,114,109,105,110,97,108,32,115,101,113,117,101,110,99,101,32,119,105,116,104,32,108,105,98,117,118,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,-2,-1,25,0,33,0,37,0,41,0,-1,-1,52,0,69,0,71,0,75,0,82,0,-1,-1,84,0,97,0,-1,-1,101,0,-2,-1,105,0,109,0,-1,-1,-1,-1,113,0,-2,-1,117,0,122,0,-1,-1,-2,-1,-2,-1,-2,-1,-1,-1,-125,0,-120,0,-115,0,-110,0,-101,0,-97,0,-92,0,-1,-1,-2,-1,-83,0,-77,0,-2,-1,-1,-1,-1,-1,-1,-1,-71,0,-1,-1,-1,-1,-1,-1,-61,0,-1,-1,-57,0,-1,-1,-1,-1,-1,-1,-55,0,-1,-1,-50,0,-1,-1,-1,-1,-1,-1,-1,-1,-46,0,-41,0,-35,0,-30,0,-25,0,-20,0,-15,0,-9,0,-3,0,3,1,9,1,14,1,-1,-1,19,1,-1,-1,23,1,28,1,33,1,37,1,44,1,-1,-1,51,1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,55,1,64,1,73,1,82,1,91,1,100,1,109,1,118,1,127,1,-120,1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-111,1,-2,-1,-2,-1,-1,-1,-1,-1,-91,1,-88,1,-77,1,-74,1,-72,1,-69,1,-26,1,-1,-1,-23,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-21,1,-1,-1,-1,-1,-1,-1,-1,-1,-17,1,-1,-1,6,2,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,10,2,15,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,19,2,-1,-1,-1,-1,26,2,-1,-1,-1,-1,-1,-1,-1,-1,33,2,40,2,47,2,-1,-1,-1,-1,54,2,-1,-1,61,2,-1,-1,-1,-1,-1,-1,68,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,75,2,81,2,87,2,93,2,99,2,105,2,111,2,117,2,123,2,-127,2,-121,2,-115,2,-109,2,-103,2,-97,2,-91,2,-85,2,-79,2,-73,2,-67,2,-61,2,-55,2,-49,2,-43,2,-37,2,-31,2,-25,2,-19,2,-13,2,-7,2,0,3,6,3,12,3,18,3,24,3,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,30,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,35,3,-2,-1,44,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-119,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-114,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-108,3,-45,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,49,50,108,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,40,48,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,40,66,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,33,112,27,91,63,51,108,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,91,65,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,54,37,116,59,49,37,59,109,0,27,72,0,9,0,27,91,71,0,106,106,107,107,108,108,109,109,110,110,113,113,116,116,117,117,118,118,119,119,120,120,0,27,91,90,0,27,91,52,126,0,27,79,77,0,27,91,51,59,50,126,0,27,91,52,59,50,126,0,27,91,49,59,50,126,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,49,59,54,83,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,51,57,59,52,57,109,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,0,27,91,51,109,0,27,91,50,51,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,0,3,0,1,0,73,0,-106,0,-101,1,1,0,1,0,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,0,0,-2,-1,-1,-1,5,0,11,0,-1,-1,-2,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,21,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,30,0,33,0,36,0,39,0,42,0,48,0,54,0,59,0,64,0,69,0,74,0,79,0,83,0,88,0,93,0,98,0,103,0,108,0,114,0,120,0,126,0,-124,0,-118,0,-112,0,-106,0,-100,0,-94,0,-88,0,-82,0,-76,0,-71,0,-66,0,-61,0,-56,0,-51,0,-45,0,-39,0,-33,0,-27,0,-21,0,-15,0,-9,0,-3,0,3,1,9,1,15,1,21,1,27,1,33,1,39,1,45,1,51,1,57,1,63,1,69,1,73,1,78,1,83,1,88,1,93,1,98,1,102,1,106,1,110,1,114,1,119,1,124,1,27,91,51,74,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,91,49,59,50,65,0,65,88,0,71,48,0,88,84,0,85,56,0,67,114,0,67,115,0,69,48,0,69,51,0,77,115,0,83,48,0,83,101,0,83,115,0,84,83,0,88,77,0,103,114,98,111,109,0,103,115,98,111,109,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,69,78,68,56,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,72,79,77,56,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,114,109,120,120,0,115,109,120,120,0,120,109,0 // NOLINT + 30,2,71,0,38,0,15,0,-99,1,21,4,118,116,112,99,111,110,124,65,78,73,83,32,101,109,117,108,97,116,105,111,110,32,102,111,114,32,99,111,110,115,111,108,101,32,118,105,114,116,117,97,108,32,116,101,114,109,105,110,97,108,32,115,101,113,117,101,110,99,101,32,119,105,116,104,32,108,105,98,117,118,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,-2,-1,25,0,33,0,37,0,41,0,-1,-1,52,0,69,0,73,0,77,0,84,0,-1,-1,86,0,99,0,-1,-1,103,0,-2,-1,107,0,111,0,-1,-1,-1,-1,115,0,-2,-1,119,0,124,0,-1,-1,-2,-1,-2,-1,-2,-1,-1,-1,-123,0,-118,0,-113,0,-108,0,-99,0,-95,0,-90,0,-1,-1,-2,-1,-81,0,-75,0,-2,-1,-1,-1,-1,-1,-1,-1,-69,0,-1,-1,-1,-1,-1,-1,-59,0,-1,-1,-55,0,-1,-1,-1,-1,-1,-1,-53,0,-1,-1,-48,0,-1,-1,-1,-1,-1,-1,-1,-1,-44,0,-39,0,-33,0,-28,0,-23,0,-18,0,-13,0,-7,0,-1,0,5,1,11,1,16,1,-1,-1,21,1,-1,-1,25,1,30,1,35,1,39,1,46,1,-1,-1,53,1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,57,1,66,1,75,1,84,1,93,1,102,1,111,1,120,1,-127,1,-118,1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-109,1,-2,-1,-2,-1,-1,-1,-1,-1,-89,1,-86,1,-75,1,-72,1,-70,1,-67,1,-24,1,-1,-1,-21,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-19,1,-1,-1,-1,-1,-1,-1,-1,-1,-15,1,-1,-1,8,2,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,12,2,17,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,21,2,-1,-1,-1,-1,28,2,-1,-1,-1,-1,-1,-1,-1,-1,35,2,42,2,49,2,-1,-1,-1,-1,56,2,-1,-1,63,2,-1,-1,-1,-1,-1,-1,70,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,77,2,83,2,89,2,95,2,101,2,107,2,113,2,119,2,125,2,-125,2,-119,2,-113,2,-107,2,-101,2,-95,2,-89,2,-83,2,-77,2,-71,2,-65,2,-59,2,-53,2,-47,2,-41,2,-35,2,-29,2,-23,2,-17,2,-11,2,-5,2,2,3,8,3,14,3,20,3,26,3,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,32,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,37,3,-2,-1,46,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-117,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-112,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-106,3,-43,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,49,50,108,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,40,48,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,40,66,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,33,112,27,91,63,51,108,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,91,65,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,54,37,116,59,49,37,59,109,0,27,72,0,9,0,27,91,71,0,106,106,107,107,108,108,109,109,110,110,113,113,116,116,117,117,118,118,119,119,120,120,0,27,91,90,0,27,91,52,126,0,27,79,77,0,27,91,51,59,50,126,0,27,91,52,59,50,126,0,27,91,49,59,50,126,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,49,59,54,83,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,51,57,59,52,57,109,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,0,27,91,51,109,0,27,91,50,51,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,0,3,0,1,0,74,0,-104,0,-95,1,1,0,1,0,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,0,0,-2,-1,-1,-1,5,0,-1,-1,11,0,-1,-1,-2,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,21,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,30,0,33,0,39,0,42,0,45,0,48,0,54,0,60,0,65,0,70,0,75,0,80,0,85,0,89,0,94,0,99,0,104,0,109,0,114,0,120,0,126,0,-124,0,-118,0,-112,0,-106,0,-100,0,-94,0,-88,0,-82,0,-76,0,-70,0,-65,0,-60,0,-55,0,-50,0,-45,0,-39,0,-33,0,-27,0,-21,0,-15,0,-9,0,-3,0,3,1,9,1,15,1,21,1,27,1,33,1,39,1,45,1,51,1,57,1,63,1,69,1,75,1,79,1,84,1,89,1,94,1,99,1,104,1,108,1,112,1,116,1,120,1,125,1,-126,1,27,91,51,74,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,91,49,59,50,65,0,65,88,0,71,48,0,88,84,0,85,56,0,67,114,0,67,115,0,69,48,0,69,51,0,77,115,0,83,48,0,83,101,0,83,109,117,108,120,0,83,115,0,84,83,0,88,77,0,103,114,98,111,109,0,103,115,98,111,109,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,69,78,68,56,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,72,79,77,56,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,114,109,120,120,0,115,109,120,120,0,120,109,0 // NOLINT }; // win32con|ANSI emulation for libuv on legacy console, diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index d1e456f185..2e26a75be1 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -121,6 +121,7 @@ typedef struct { int resize_screen; int reset_scroll_region; int set_cursor_style, reset_cursor_style; + int save_title, restore_title; int enter_undercurl_mode, exit_undercurl_mode, set_underline_color; } unibi_ext; char *space_buf; @@ -271,9 +272,11 @@ static void terminfo_start(UI *ui) data->invis, sizeof data->invis); // Set 't_Co' from the result of unibilium & fix_terminfo. t_colors = unibi_get_num(data->ut, unibi_max_colors); - // Enter alternate screen and clear + // Enter alternate screen, save title, and clear. // NOTE: Do this *before* changing terminal settings. #6433 unibi_out(ui, unibi_enter_ca_mode); + // Save title/icon to the "stack". #4063 + unibi_out_ext(ui, data->unibi_ext.save_title); unibi_out(ui, unibi_keypad_xmit); unibi_out(ui, unibi_clear_screen); // Enable bracketed paste @@ -300,10 +303,12 @@ static void terminfo_stop(UI *ui) tui_mode_change(ui, (String)STRING_INIT, SHAPE_IDX_N); tui_mouse_off(ui); unibi_out(ui, unibi_exit_attribute_mode); - // cursor should be set to normal before exiting alternate screen + // Reset cursor to normal before exiting alternate screen. unibi_out(ui, unibi_cursor_normal); unibi_out(ui, unibi_keypad_local); unibi_out(ui, unibi_exit_ca_mode); + // Restore title/icon from the "stack". #4063 + unibi_out_ext(ui, data->unibi_ext.restore_title); if (data->cursor_color_changed) { unibi_out_ext(ui, data->unibi_ext.reset_cursor_color); } @@ -1047,6 +1052,7 @@ static void tui_grid_scroll(UI *ui, Integer g, Integer startrow, Integer endrow, Integer startcol, Integer endcol, Integer rows, Integer cols) { + (void)cols; // unused TUIData *data = ui->data; UGrid *grid = &data->grid; int top = (int)startrow, bot = (int)endrow-1; @@ -1169,7 +1175,7 @@ static void tui_flush(UI *ui) } UGRID_FOREACH_CELL(grid, row, r.left, clear_col, { - cursor_goto(ui, row, col); + cursor_goto(ui, row, curcol); print_cell(ui, cell); }); if (clear_col < r.right) { @@ -1278,7 +1284,7 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol, grid->cells[linerow][c].attr = attrs[c-startcol]; } UGRID_FOREACH_CELL(grid, (int)linerow, (int)startcol, (int)endcol, { - cursor_goto(ui, (int)linerow, col); + cursor_goto(ui, (int)linerow, curcol); print_cell(ui, cell); }); @@ -1589,9 +1595,9 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, unibi_set_if_empty(ut, unibi_exit_italics_mode, "\x1b[23m"); unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]2"); unibi_set_if_empty(ut, unibi_from_status_line, "\x07"); - // Enter/exit alternate screen with "title stacking". #4063 - unibi_set_str(ut, unibi_enter_ca_mode, "\x1b[?1049h\x1b[22;0;0t"); - unibi_set_str(ut, unibi_exit_ca_mode, "\x1b[?1049l\x1b[23;0;0t"); + // 2017-04 terminfo.src has older control sequences. + unibi_set_str(ut, unibi_enter_ca_mode, "\x1b[?1049h"); + unibi_set_str(ut, unibi_exit_ca_mode, "\x1b[?1049l"); } else if (screen) { // per the screen manual; 2017-04 terminfo.src lacks these. unibi_set_if_empty(ut, unibi_to_status_line, "\x1b_"); @@ -1611,9 +1617,9 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, } else if (putty) { // No bugs in the vanilla terminfo for our purposes. } else if (iterm) { - // Enter/exit alternate screen with "title stacking". #4063 - unibi_set_str(ut, unibi_enter_ca_mode, "\x1b[?1049h\x1b[22;0;0t"); - unibi_set_str(ut, unibi_exit_ca_mode, "\x1b[?1049l\x1b[23;0;0t"); + // 2017-04 terminfo.src has older control sequences. + unibi_set_str(ut, unibi_enter_ca_mode, "\x1b[?1049h"); + unibi_set_str(ut, unibi_exit_ca_mode, "\x1b[?1049l"); // 2017-04 terminfo.src lacks these. unibi_set_if_empty(ut, unibi_set_tb_margin, "\x1b[%i%p1%d;%p2%dr"); unibi_set_if_empty(ut, unibi_orig_pair, "\x1b[39;49m"); @@ -1675,8 +1681,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, } // Blacklist of terminals that cannot be trusted to report DECSCUSR support. - if (!(st || (vte_version != 0 && vte_version < 3900) - || (konsolev > 0 && konsolev < 180770))) { + if (!(st || (vte_version != 0 && vte_version < 3900) || konsolev)) { data->unibi_ext.reset_cursor_style = unibi_find_ext_str(ut, "Se"); data->unibi_ext.set_cursor_style = unibi_find_ext_str(ut, "Ss"); } @@ -1866,6 +1871,11 @@ static void augment_terminfo(TUIData *data, const char *term, ut, "ext.reset_cursor_color", "\x1b]112\x07"); } + data->unibi_ext.save_title = (int)unibi_add_ext_str( + ut, "ext.save_title", "\x1b[22;0;0t"); + data->unibi_ext.restore_title = (int)unibi_add_ext_str( + ut, "ext.restore_title", "\x1b[23;0;0t"); + /// Terminals usually ignore unrecognized private modes, and there is no /// known ambiguity with these. So we just set them unconditionally. data->unibi_ext.enable_lr_margin = (int)unibi_add_ext_str( diff --git a/src/nvim/types.h b/src/nvim/types.h index 317bead3bb..f803b45e27 100644 --- a/src/nvim/types.h +++ b/src/nvim/types.h @@ -13,6 +13,9 @@ typedef unsigned char char_u; // Can hold one decoded UTF-8 character. typedef uint32_t u8char_T; +// Opaque handle used by API clients to refer to various objects in vim +typedef int handle_T; + typedef struct expand expand_T; #endif // NVIM_TYPES_H diff --git a/src/nvim/ugrid.h b/src/nvim/ugrid.h index 33a706b8c0..19c2e99ebb 100644 --- a/src/nvim/ugrid.h +++ b/src/nvim/ugrid.h @@ -25,8 +25,8 @@ struct ugrid { #define UGRID_FOREACH_CELL(grid, row, startcol, endcol, code) \ do { \ UCell *row_cells = (grid)->cells[row]; \ - for (int col = startcol; col < endcol; col++) { \ - UCell *cell = row_cells + col; \ + for (int curcol = startcol; curcol < endcol; curcol++) { \ + UCell *cell = row_cells + curcol; \ (void)(cell); \ code; \ } \ diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 0c69e94e5d..96232ab223 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -51,12 +51,13 @@ static UI *uis[MAX_UI_COUNT]; static bool ui_ext[kUIExtCount] = { 0 }; static size_t ui_count = 0; -static int row = 0, col = 0; +static int ui_mode_idx = SHAPE_IDX_N; +static int cursor_row = 0, cursor_col = 0; static bool pending_cursor_update = false; static int busy = 0; -static int mode_idx = SHAPE_IDX_N; static bool pending_mode_info_update = false; 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, ...) @@ -188,21 +189,14 @@ void ui_refresh(void) UI *ui = uis[i]; width = MIN(ui->width, width); height = MIN(ui->height, height); - for (UIExtension i = 0; (int)i < kUIExtCount; i++) { - ext_widgets[i] &= ui->ui_ext[i]; + for (UIExtension j = 0; (int)j < kUIExtCount; j++) { + ext_widgets[j] &= ui->ui_ext[j]; } } - row = col = 0; + cursor_row = cursor_col = 0; pending_cursor_update = true; - ui_default_colors_set(); - - int save_p_lz = p_lz; - p_lz = false; // convince redrawing() to return true ... - screen_resize(width, height); - p_lz = save_p_lz; - for (UIExtension i = 0; (int)i < kUIExtCount; i++) { ui_ext[i] = ext_widgets[i]; if (i < kUIGlobalCount) { @@ -210,6 +204,14 @@ void ui_refresh(void) BOOLEAN_OBJ(ext_widgets[i])); } } + + ui_default_colors_set(); + + int save_p_lz = p_lz; + p_lz = false; // convince redrawing() to return true ... + screen_resize(width, height); + p_lz = save_p_lz; + ui_mode_info_set(); pending_mode_update = true; ui_cursor_shape(); @@ -315,30 +317,43 @@ void ui_set_ext_option(UI *ui, UIExtension ext, bool active) } } -void ui_line(int row, int startcol, int endcol, int clearcol, int clearattr, - bool wrap) +void ui_line(ScreenGrid *grid, int row, int startcol, int endcol, int clearcol, + int clearattr, bool wrap) { - size_t off = LineOffset[row]+(size_t)startcol; - UI_CALL(raw_line, 1, row, startcol, endcol, clearcol, clearattr, wrap, - (const schar_T *)ScreenLines+off, (const sattr_T *)ScreenAttrs+off); + 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); + if (p_wd) { // 'writedelay': flush & delay each time. - int old_row = row, old_col = col; - // If'writedelay is active, we set the cursor to highlight what was drawn - ui_cursor_goto(row, MIN(clearcol, (int)Columns-1)); + int old_row = cursor_row, old_col = cursor_col; + handle_T old_grid = cursor_grid_handle; + // If 'writedelay' is active, set the cursor to indicate what was drawn. + ui_grid_cursor_goto(grid->handle, row, MIN(clearcol, (int)Columns-1)); ui_flush(); uint64_t wd = (uint64_t)labs(p_wd); os_microdelay(wd * 1000u, true); - ui_cursor_goto(old_row, old_col); + ui_grid_cursor_goto(old_grid, old_row, old_col); } } void ui_cursor_goto(int new_row, int new_col) { - if (new_row == row && new_col == col) { + ui_grid_cursor_goto(DEFAULT_GRID_HANDLE, new_row, new_col); +} + +void ui_grid_cursor_goto(handle_T grid_handle, int new_row, int new_col) +{ + if (new_row == cursor_row + && new_col == cursor_col + && grid_handle == cursor_grid_handle) { return; } - row = new_row; - col = new_col; + + cursor_row = new_row; + cursor_col = new_col; + cursor_grid_handle = grid_handle; pending_cursor_update = true; } @@ -349,19 +364,20 @@ void ui_mode_info_set(void) int ui_current_row(void) { - return row; + return cursor_row; } int ui_current_col(void) { - return col; + return cursor_col; } void ui_flush(void) { cmdline_ui_flush(); + win_ui_flush(); if (pending_cursor_update) { - ui_call_grid_cursor_goto(1, row, col); + ui_call_grid_cursor_goto(cursor_grid_handle, cursor_row, cursor_col); pending_cursor_update = false; } if (pending_mode_info_update) { @@ -372,8 +388,8 @@ void ui_flush(void) pending_mode_info_update = false; } if (pending_mode_update) { - char *full_name = shape_table[mode_idx].full_name; - ui_call_mode_change(cstr_as_string(full_name), mode_idx); + char *full_name = shape_table[ui_mode_idx].full_name; + ui_call_mode_change(cstr_as_string(full_name), ui_mode_idx); pending_mode_update = false; } ui_call_flush(); @@ -389,8 +405,8 @@ void ui_cursor_shape(void) } int new_mode_idx = cursor_get_mode_idx(); - if (new_mode_idx != mode_idx) { - mode_idx = new_mode_idx; + if (new_mode_idx != ui_mode_idx) { + ui_mode_idx = new_mode_idx; pending_mode_update = true; } conceal_check_cursor_line(); @@ -421,3 +437,22 @@ Array ui_array(void) } return all_uis; } + +void ui_grid_resize(handle_T grid_handle, int width, int height, Error *error) +{ + if (grid_handle == DEFAULT_GRID_HANDLE) { + screen_resize(width, height); + return; + } + + win_T *wp = get_win_by_grid_handle(grid_handle); + if (wp == NULL) { + api_set_error(error, kErrorTypeValidation, + "No window with the given handle"); + return; + } + + wp->w_grid.requested_rows = (int)height; + wp->w_grid.requested_cols = (int)width; + redraw_win_later(wp, SOME_VALID); +} diff --git a/src/nvim/ui.h b/src/nvim/ui.h index d89ad60ce7..16237214cb 100644 --- a/src/nvim/ui.h +++ b/src/nvim/ui.h @@ -16,6 +16,7 @@ typedef enum { kUIWildmenu, #define kUIGlobalCount (kUIWildmenu+1) kUILinegrid, + kUIMultigrid, kUIHlState, kUIExtCount, } UIExtension; @@ -26,6 +27,7 @@ EXTERN const char *ui_ext_names[] INIT(= { "ext_tabline", "ext_wildmenu", "ext_linegrid", + "ext_multigrid", "ext_hlstate", }); diff --git a/src/nvim/version.c b/src/nvim/version.c index 11dc813997..9b9053d7a9 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -2271,8 +2271,8 @@ static void do_intro_line(long row, char_u *mesg, int attr) } } assert(row <= INT_MAX && col <= INT_MAX); - screen_puts_len(p, l, (int)row, (int)col, - *p == '<' ? HL_ATTR(HLF_8) : attr); + grid_puts_len(&default_grid, p, l, (int)row, (int)col, + *p == '<' ? HL_ATTR(HLF_8) : attr); col += clen; } } diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c index 1a7e55c11e..dcc64db8a0 100644 --- a/src/nvim/viml/parser/expressions.c +++ b/src/nvim/viml/parser/expressions.c @@ -2845,30 +2845,32 @@ viml_pexpr_parse_no_paren_closing_error: {} kvi_push(ast_stack, new_top_node_p); want_node = kENodeOperator; } else { - if (want_node == kENodeValue) { - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeNested); - *top_node_p = cur_node; - kvi_push(ast_stack, &cur_node->children); - HL_CUR_TOKEN(NestingParenthesis); - } else if (want_node == kENodeOperator) { - if (prev_token.type == kExprLexSpacing) { - // For some reason "function (args)" is a function call, but - // "(funcref) (args)" is not. AFAIR this somehow involves - // compatibility and Bram was commenting that this is - // intentionally inconsistent and he is not very happy with the - // situation himself. - if ((*top_node_p)->type != kExprNodePlainIdentifier - && (*top_node_p)->type != kExprNodeComplexIdentifier - && (*top_node_p)->type != kExprNodeCurlyBracesIdentifier) { - OP_MISSING; + switch (want_node) { + case kENodeValue: { + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeNested); + *top_node_p = cur_node; + kvi_push(ast_stack, &cur_node->children); + HL_CUR_TOKEN(NestingParenthesis); + break; + } + case kENodeOperator: { + if (prev_token.type == kExprLexSpacing) { + // For some reason "function (args)" is a function call, but + // "(funcref) (args)" is not. AFAIR this somehow involves + // compatibility and Bram was commenting that this is + // intentionally inconsistent and he is not very happy with the + // situation himself. + if ((*top_node_p)->type != kExprNodePlainIdentifier + && (*top_node_p)->type != kExprNodeComplexIdentifier + && (*top_node_p)->type != kExprNodeCurlyBracesIdentifier) { + OP_MISSING; + } } + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeCall); + ADD_OP_NODE(cur_node); + HL_CUR_TOKEN(CallingParenthesis); + break; } - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeCall); - ADD_OP_NODE(cur_node); - HL_CUR_TOKEN(CallingParenthesis); - } else { - // Currently it is impossible to reach this. - assert(false); } want_node = kENodeValue; } diff --git a/src/nvim/window.c b/src/nvim/window.c index 8be79f0cff..97c708ed20 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -997,6 +997,9 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) p_wh = i; } + // Send the window positions to the UI + oldwin->w_pos_changed = true; + return OK; } @@ -1341,6 +1344,9 @@ static void win_rotate(int upwards, int count) (void)win_comp_pos(); } + wp1->w_pos_changed = true; + wp2->w_pos_changed = true; + redraw_all_later(NOT_VALID); } @@ -1423,6 +1429,9 @@ void win_move_after(win_T *win1, win_T *win2) redraw_later(NOT_VALID); } win_enter(win1, false); + + win1->w_pos_changed = true; + win2->w_pos_changed = true; } /* @@ -2059,6 +2068,7 @@ int win_close(win_T *win, bool free_buf) if (help_window) restore_snapshot(SNAP_HELP_IDX, close_curwin); + curwin->w_pos_changed = true; redraw_all_later(NOT_VALID); return OK; } @@ -3108,6 +3118,10 @@ int win_new_tabpage(int after, char_u *filename) redraw_all_later(NOT_VALID); + if (ui_is_external(kUIMultigrid)) { + tabpage_check_windows(tp); + } + apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf); apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf); apply_autocmds(EVENT_TABNEW, filename, filename, false, curbuf); @@ -3299,11 +3313,16 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au int old_off = tp->tp_firstwin->w_winrow; win_T *next_prevwin = tp->tp_prevwin; + tabpage_T *old_curtab = curtab; curtab = tp; firstwin = tp->tp_firstwin; lastwin = tp->tp_lastwin; topframe = tp->tp_topframe; + if (old_curtab != curtab && ui_is_external(kUIMultigrid)) { + tabpage_check_windows(old_curtab); + } + /* We would like doing the TabEnter event first, but we don't have a * valid current window yet, which may break some commands. * This triggers autocommands, thus may make "tp" invalid. */ @@ -3336,7 +3355,20 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au } redraw_all_later(NOT_VALID); - must_redraw = NOT_VALID; +} + +/// called when changing current tabpage from old_curtab to curtab +static void tabpage_check_windows(tabpage_T *old_curtab) +{ + win_T *next_wp; + for (win_T *wp = old_curtab->tp_firstwin; wp; wp = next_wp) { + next_wp = wp->w_next; + wp->w_pos_changed = true; + } + + for (win_T *wp = firstwin; wp; wp = wp->w_next) { + wp->w_pos_changed = true; + } } /* @@ -3860,6 +3892,8 @@ static win_T *win_alloc(win_T *after, int hidden) new_wp->handle = ++last_win_id; handle_register_window(new_wp); + grid_assign_handle(&new_wp->w_grid); + // Init w: variables. new_wp->w_vars = tv_dict_alloc(); init_var_dict(new_wp->w_vars, &new_wp->w_winvar, VAR_SCOPE); @@ -3958,6 +3992,8 @@ win_free ( xfree(wp->w_p_cc_cols); + win_free_grid(wp, false); + if (wp != aucmd_win) win_remove(wp, tp); if (autocmd_busy) { @@ -3970,6 +4006,20 @@ win_free ( unblock_autocmds(); } +void win_free_grid(win_T *wp, bool reinit) +{ + if (wp->w_grid.handle != 0 && ui_is_external(kUIMultigrid)) { + ui_call_grid_destroy(wp->w_grid.handle); + wp->w_grid.handle = 0; + } + grid_free(&wp->w_grid); + if (reinit) { + // if a float is turned into a split and back into a float, the grid + // data structure will be reused + memset(&wp->w_grid, 0, sizeof(wp->w_grid)); + } +} + /* * Append window "wp" in the window list after window "after". */ @@ -4071,8 +4121,8 @@ static void frame_remove(frame_T *frp) void win_alloc_lines(win_T *wp) { wp->w_lines_valid = 0; - assert(Rows >= 0); - wp->w_lines = xcalloc(Rows, sizeof(wline_T)); + assert(wp->w_grid.Rows >= 0); + wp->w_lines = xcalloc(MAX(wp->w_grid.Rows + 1, Rows), sizeof(wline_T)); } /* @@ -4202,7 +4252,8 @@ static void frame_comp_pos(frame_T *topfrp, int *row, int *col) wp->w_winrow = *row; wp->w_wincol = *col; redraw_win_later(wp, NOT_VALID); - wp->w_redr_status = TRUE; + wp->w_redr_status = true; + wp->w_pos_changed = true; } *row += wp->w_height + wp->w_status_height; *col += wp->w_width + wp->w_vsep_width; @@ -4255,8 +4306,9 @@ void win_setheight_win(int height, win_T *win) * If there is extra space created between the last window and the command * line, clear it. */ - if (full_screen && msg_scrolled == 0 && row < cmdline_row) - screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0); + if (full_screen && msg_scrolled == 0 && row < cmdline_row) { + grid_fill(&default_grid, row, cmdline_row, 0, (int)Columns, ' ', ' ', 0); + } cmdline_row = row; msg_row = row; msg_col = 0; @@ -4339,11 +4391,11 @@ static void frame_setheight(frame_T *curfrp, int height) room_cmdline = 0; } - if (height <= room + room_cmdline) + if (height <= room + room_cmdline) { break; + } if (run == 2 || curfrp->fr_width == Columns) { - if (height > room + room_cmdline) - height = room + room_cmdline; + height = room + room_cmdline; break; } frame_setheight(curfrp->fr_parent, height @@ -4706,7 +4758,7 @@ void win_drag_status_line(win_T *dragwin, int offset) fr = fr->fr_next; } row = win_comp_pos(); - screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0); + grid_fill(&default_grid, row, cmdline_row, 0, (int)Columns, ' ', ' ', 0); cmdline_row = row; p_ch = Rows - cmdline_row; if (p_ch < 1) @@ -4866,6 +4918,8 @@ void win_new_height(win_T *wp, int height) if (!exiting) { scroll_to_fraction(wp, prev_height); } + + wp->w_pos_changed = true; } void scroll_to_fraction(win_T *wp, int prev_height) @@ -4996,6 +5050,7 @@ void win_new_width(win_T *wp, int width) 0); } } + wp->w_pos_changed = true; } void win_comp_scroll(win_T *wp) @@ -5052,10 +5107,11 @@ void command_height(void) /* Recompute window positions. */ (void)win_comp_pos(); - /* clear the lines added to cmdline */ - if (full_screen) - screen_fill(cmdline_row, (int)Rows, 0, - (int)Columns, ' ', ' ', 0); + // clear the lines added to cmdline + if (full_screen) { + grid_fill(&default_grid, cmdline_row, (int)Rows, 0, (int)Columns, ' ', + ' ', 0); + } msg_row = cmdline_row; redraw_cmdline = TRUE; return; @@ -6031,3 +6087,23 @@ void win_findbuf(typval_T *argvars, list_T *list) } } } + +void win_ui_flush(void) +{ + if (!ui_is_external(kUIMultigrid)) { + return; + } + + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (wp->w_pos_changed && wp->w_grid.chars != NULL) { + if (tp == curtab) { + ui_call_win_pos(wp->w_grid.handle, wp->handle, wp->w_winrow, + wp->w_wincol, wp->w_width, wp->w_height); + } else { + ui_call_win_hide(wp->w_grid.handle); + } + wp->w_pos_changed = false; + } + } + +} diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index ddf5575e31..52e41ca856 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -1261,6 +1261,7 @@ describe('API', function() ext_tabline = false, ext_wildmenu = false, ext_linegrid = screen._options.ext_linegrid or false, + ext_multigrid = false, ext_hlstate=false, height = 4, rgb = true, diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 272b80466c..851f3e720e 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -84,6 +84,10 @@ end local session, loop_running, last_error +local function get_session() + return session +end + local function set_session(s, keep) if session and not keep then session:close() @@ -164,34 +168,34 @@ local function expect_msg_seq(...) error(final_error) end -local function call_and_stop_on_error(...) +local function call_and_stop_on_error(lsession, ...) local status, result = copcall(...) -- luacheck: ignore if not status then - session:stop() + lsession:stop() last_error = result return '' end return result end -local function run(request_cb, notification_cb, setup_cb, timeout) +local function run_session(lsession, request_cb, notification_cb, setup_cb, timeout) local on_request, on_notification, on_setup if request_cb then function on_request(method, args) - return call_and_stop_on_error(request_cb, method, args) + return call_and_stop_on_error(lsession, request_cb, method, args) end end if notification_cb then function on_notification(method, args) - call_and_stop_on_error(notification_cb, method, args) + call_and_stop_on_error(lsession, notification_cb, method, args) end end if setup_cb then function on_setup() - call_and_stop_on_error(setup_cb) + call_and_stop_on_error(lsession, setup_cb) end end @@ -205,6 +209,10 @@ local function run(request_cb, notification_cb, setup_cb, timeout) end end +local function run(request_cb, notification_cb, setup_cb, timeout) + run_session(session, request_cb, notification_cb, setup_cb, timeout) +end + local function stop() session:stop() end @@ -677,6 +685,7 @@ local module = { buffer = buffer, bufmeths = bufmeths, call = nvim_call, + create_callindex = create_callindex, clear = clear, command = nvim_command, connect = connect, @@ -701,6 +710,7 @@ local module = { filter = filter, funcs = funcs, get_pathsep = get_pathsep, + get_session = get_session, insert = insert, iswin = iswin, map = map, @@ -732,6 +742,7 @@ local module = { retry = retry, rmdir = rmdir, run = run, + run_session = run_session, set_session = set_session, set_shell_powershell = set_shell_powershell, skip_fragile = skip_fragile, diff --git a/test/functional/legacy/051_highlight_spec.lua b/test/functional/legacy/051_highlight_spec.lua index 40f70de2ec..0c9c9621ee 100644 --- a/test/functional/legacy/051_highlight_spec.lua +++ b/test/functional/legacy/051_highlight_spec.lua @@ -3,10 +3,11 @@ local Screen = require('test.functional.ui.screen') local helpers = require('test.functional.helpers')(after_each) local clear, feed = helpers.clear, helpers.feed -local command, expect = helpers.command, helpers.expect +local expect = helpers.expect local eq = helpers.eq local wait = helpers.wait local exc_exec = helpers.exc_exec +local feed_command = helpers.feed_command describe(':highlight', function() setup(clear) @@ -15,7 +16,7 @@ describe(':highlight', function() local screen = Screen.new(35, 10) screen:attach() -- Basic test if ":highlight" doesn't crash - command('set more') + feed_command('set more') feed(':highlight<CR>') -- FIXME(tarruda): We need to be sure the prompt is displayed before -- continuing, or risk a race condition where some of the following input @@ -34,52 +35,62 @@ describe(':highlight', function() ]]) feed('q') wait() -- wait until we're back to normal - command('hi Search') - command('hi Normal') + feed_command('hi Search') + feed_command('hi Normal') -- Test setting colors. -- Test clearing one color and all doesn't generate error or warning - command('hi NewGroup cterm=italic ctermfg=DarkBlue ctermbg=Grey gui=NONE guifg=#00ff00 guibg=Cyan') - command('hi Group2 cterm=NONE') - command('hi Group3 cterm=bold') - command('redir! @a') - command('hi NewGroup') - command('hi Group2') - command('hi Group3') - command('hi clear NewGroup') - command('hi NewGroup') - command('hi Group2') - command('hi Group2 NONE') - command('hi Group2') - command('hi clear') - command('hi Group3') + feed_command('hi NewGroup cterm=italic ctermfg=DarkBlue ctermbg=Grey gui=NONE guifg=#00ff00 guibg=Cyan') + feed_command('hi Group2 cterm=NONE') + feed_command('hi Group3 cterm=bold') + feed_command('redir! @a') + feed_command('hi NewGroup') + feed_command('hi Group2') + feed_command('hi Group3') + feed_command('hi clear NewGroup') + feed_command('hi NewGroup') + feed_command('hi Group2') + feed_command('hi Group2 NONE') + feed_command('hi Group2') + feed_command('hi clear') + feed_command('hi Group3') + feed('<cr>') eq('Vim(highlight):E475: Invalid argument: cterm=\'asdf', exc_exec([[hi Crash cterm='asdf]])) - command('redir END') + feed_command('redir END') -- Filter ctermfg and ctermbg, the numbers depend on the terminal - command('0put a') - command([[%s/ctermfg=\d*/ctermfg=2/]]) - command([[%s/ctermbg=\d*/ctermbg=3/]]) + feed_command('0put a') + feed_command([[%s/ctermfg=\d*/ctermfg=2/]]) + feed_command([[%s/ctermbg=\d*/ctermbg=3/]]) -- Fix the fileformat - command('set ff&') - command('$d') + feed_command('set ff&') + feed_command('$d') -- Assert buffer contents. expect([[ + NewGroup xxx cterm=italic ctermfg=2 ctermbg=3 guifg=#00ff00 guibg=Cyan + Group2 xxx cleared + Group3 xxx cterm=bold + + NewGroup xxx cleared + Group2 xxx cleared + + Group2 xxx cleared + + Group3 xxx cleared]]) - screen:detach() end) end) diff --git a/test/functional/terminal/helpers.lua b/test/functional/terminal/helpers.lua index ae8d4704e4..7de0152de0 100644 --- a/test/functional/terminal/helpers.lua +++ b/test/functional/terminal/helpers.lua @@ -72,10 +72,10 @@ local function screen_setup(extra_rows, command, cols) if command == default_command 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 + 1) + local empty_line = (' '):rep(cols) local expected = { - 'tty ready'..(' '):rep(cols - 8), - '{1: }' ..(' '):rep(cols), + 'tty ready'..(' '):rep(cols - 9), + '{1: }' ..(' '):rep(cols - 1), empty_line, empty_line, empty_line, @@ -85,8 +85,8 @@ local function screen_setup(extra_rows, command, cols) table.insert(expected, empty_line) end - table.insert(expected, '{3:-- TERMINAL --}' .. ((' '):rep(cols - 13))) - screen:expect(table.concat(expected, '\n')) + table.insert(expected, '{3:-- TERMINAL --}' .. ((' '):rep(cols - 14))) + screen:expect(table.concat(expected, '|\n')..'|') else -- This eval also acts as a wait(). if 0 == nvim('eval', "exists('b:terminal_job_id')") then diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 0b47314be7..68652bfb7f 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -259,10 +259,10 @@ describe('tui', function() feed_data(':echo map(nvim_list_uis(), {k,v -> sort(items(v))})\013') screen:expect([=[ [[['ext_cmdline', v:false], ['ext_hlstate', v:fals| - e], ['ext_linegrid', v:true], ['ext_popupmenu', v:| - false], ['ext_tabline', v:false], ['ext_wildmenu',| - v:false], ['height', 6], ['rgb', v:false], ['widt| - h', 50]]] | + 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]]] | {10:Press ENTER or type command to continue}{1: } | {3:-- TERMINAL --} | ]=]) @@ -401,7 +401,7 @@ describe('tui FocusGained/FocusLost', function() -- Exit cmdline-mode. Redraws from timers/events are blocked during -- cmdline-mode, so the buffer won't be updated until we exit cmdline-mode. feed_data('\n') - screen:expect{any='lost'..(' '):rep(46)..'\ngained'} + screen:expect{any='lost'..(' '):rep(46)..'|\ngained'} end) end) diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua index 39a5c10bb7..943cbcef56 100644 --- a/test/functional/ui/fold_spec.lua +++ b/test/functional/ui/fold_spec.lua @@ -1,8 +1,10 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear, feed, eq = helpers.clear, helpers.feed, helpers.eq +local command = helpers.command local feed_command = helpers.feed_command local insert = helpers.insert +local funcs = helpers.funcs local meths = helpers.meths describe("folded lines", function() @@ -26,6 +28,26 @@ describe("folded lines", function() screen:detach() end) + it("highlighting with relative line numbers", function() + command("set relativenumber foldmethod=marker") + feed_command("set foldcolumn=2") + funcs.setline(1, '{{{1') + funcs.setline(2, 'line 1') + funcs.setline(3, '{{{1') + funcs.setline(4, 'line 2') + feed("j") + screen:expect([[ + {7:+ }{5: 1 +-- 2 lines: ·························}| + {7:+ }{5: 0 ^+-- 2 lines: ·························}| + {7: }{1:~ }| + {7: }{1:~ }| + {7: }{1:~ }| + {7: }{1:~ }| + {7: }{1:~ }| + :set foldcolumn=2 | + ]]) + end) + it("works with multibyte text", function() -- Currently the only allowed value of 'maxcombine' eq(6, meths.get_option('maxcombine')) diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index 96f6b43320..39170337d7 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -322,6 +322,40 @@ describe('highlight', function() screen:attach() end) + it('visual', function() + screen:detach() + screen = Screen.new(20,4) + screen:attach() + screen:set_default_attr_ids({ + [1] = {background = Screen.colors.LightGrey}, + [2] = {bold = true, foreground = Screen.colors.Blue1}, + [3] = {bold = true}, + }) + insert([[ + line1 foo bar + ]]) + + -- Non-blinking block cursor: does NOT highlight char-at-cursor. + command('set guicursor=a:block-blinkon0') + feed('gg$vhhh') + screen:expect([[ + line1 foo^ {1:bar} | + | + {2:~ }| + {3:-- VISUAL --} | + ]]) + + -- Vertical cursor: highlights char-at-cursor. #8983 + command('set guicursor=a:block-blinkon175') + feed('<esc>gg$vhhh') + screen:expect([[ + line1 foo{1:^ bar} | + | + {2:~ }| + {3:-- VISUAL --} | + ]]) + end) + it('cterm=standout gui=standout', function() screen:detach() screen = Screen.new(20,5) diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index bb6cb543ed..3228d6b7fc 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -2512,6 +2512,9 @@ describe(":substitute", function() end) it(':substitute with inccommand during :terminal activity', function() + if helpers.skip_fragile(pending) then + return + end retry(2, 40000, function() local screen = Screen.new(30,15) clear() diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua index 7f9cd190ee..850efed282 100644 --- a/test/functional/ui/input_spec.lua +++ b/test/functional/ui/input_spec.lua @@ -61,6 +61,9 @@ describe('feeding large chunks of input with <Paste>', function() end) it('ok', function() + if helpers.skip_fragile(pending) then + return + end local t = {} for i = 1, 20000 do t[i] = 'item ' .. tostring(i) diff --git a/test/functional/ui/multigrid_spec.lua b/test/functional/ui/multigrid_spec.lua new file mode 100644 index 0000000000..a5d4e34000 --- /dev/null +++ b/test/functional/ui/multigrid_spec.lua @@ -0,0 +1,1524 @@ +local helpers = require('test.functional.helpers')(after_each) +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 + + +describe('multigrid screen', function() + local screen + + before_each(function() + clear{headless=false, args={'--cmd', 'set laststatus=2'}} + screen = Screen.new(53,14) + screen:attach({ext_multigrid=true}) + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {foreground = Screen.colors.Magenta}, + [3] = {foreground = Screen.colors.Brown, bold = true}, + [4] = {foreground = Screen.colors.SlateBlue}, + [5] = {bold = true, foreground = Screen.colors.SlateBlue}, + [6] = {foreground = Screen.colors.Cyan4}, + [7] = {bold = true}, + [8] = {underline = true, bold = true, foreground = Screen.colors.SlateBlue}, + [9] = {foreground = Screen.colors.SlateBlue, underline = true}, + [10] = {foreground = Screen.colors.Red}, + [11] = {bold = true, reverse = true}, + [12] = {reverse = true}, + [13] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey}, + [14] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, + [15] = {bold = true, foreground = Screen.colors.SeaGreen4}, + [16] = {background = Screen.colors.LightGrey, underline = true}, + [17] = {background = Screen.colors.LightGrey, underline = true, bold = true, foreground = Screen.colors.Magenta}, + [18] = {bold = true, foreground = Screen.colors.Magenta}, + [19] = {foreground = Screen.colors.Brown}, + }) + end) + + after_each(function() + screen:detach() + end) + + it('default initial screen', function() + screen:expect([[ + ## grid 1 + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11:[No Name] }| + | + ## grid 2 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + end) + + it('positions windows correctly', function() + command('vsplit') + screen:expect([[ + ## grid 1 + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + {11:[No Name] }{12:[No Name] }| + | + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]], nil, nil, function() + eq({ + [2] = { win = 1000, startrow = 0, startcol = 27, width = 26, height = 12 }, + [3] = { win = 1001, startrow = 0, startcol = 0, width = 26, height = 12 } + }, screen.win_position) + end) + command('wincmd l') + command('split') + screen:expect([[ + ## grid 1 + [3:--------------------------]{12:│}[4:--------------------------]| + [3:--------------------------]{12:│}[4:--------------------------]| + [3:--------------------------]{12:│}[4:--------------------------]| + [3:--------------------------]{12:│}[4:--------------------------]| + [3:--------------------------]{12:│}[4:--------------------------]| + [3:--------------------------]{12:│}[4:--------------------------]| + [3:--------------------------]{12:│}{11:[No Name] }| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + {12:[No Name] [No Name] }| + | + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 4 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]], nil, nil, function() + eq({ + [2] = { win = 1000, startrow = 7, startcol = 27, width = 26, height = 5 }, + [3] = { win = 1001, startrow = 0, startcol = 0, width = 26, height = 12 }, + [4] = { win = 1002, startrow = 0, startcol = 27, width = 26, height = 6 } + }, screen.win_position) + end) + command('wincmd h') + command('q') + screen:expect([[ + ## grid 1 + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + {11:[No Name] }| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {12:[No Name] }| + | + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 4 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]], nil, nil, function() + eq({ + [2] = { win = 1000, startrow = 7, startcol = 0, width = 53, height = 5 }, + [4] = { win = 1002, startrow = 0, startcol = 0, width = 53, height = 6 } + }, screen.win_position) + end) + end) + + describe('split', function () + describe('horizontally', function () + it('allocates grids', function () + command('sp') + screen:expect([[ + ## grid 1 + [3:-----------------------------------------------------]| + [3:-----------------------------------------------------]| + [3:-----------------------------------------------------]| + [3:-----------------------------------------------------]| + [3:-----------------------------------------------------]| + [3:-----------------------------------------------------]| + {11:[No Name] }| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {12:[No Name] }| + | + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + end) + + it('resizes grids', function () + command('sp') + command('resize 8') + screen:expect([[ + ## grid 1 + [3:-----------------------------------------------------]| + [3:-----------------------------------------------------]| + [3:-----------------------------------------------------]| + [3:-----------------------------------------------------]| + [3:-----------------------------------------------------]| + [3:-----------------------------------------------------]| + [3:-----------------------------------------------------]| + [3:-----------------------------------------------------]| + {11:[No Name] }| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {12:[No Name] }| + | + ## grid 2 + | + {1:~ }| + {1:~ }| + ## grid 3 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + end) + + it('splits vertically', function() + command('sp') + command('vsp') + command('vsp') + screen:expect([[ + ## grid 1 + [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]| + [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]| + [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]| + [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]| + [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]| + [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]| + {11:[No Name] }{12:[No Name] [No Name] }| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {12:[No Name] }| + | + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 4 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 5 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + insert('hello') + screen:expect([[ + ## grid 1 + [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]| + [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]| + [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]| + [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]| + [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]| + [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]| + {11:[No Name] [+] }{12:[No Name] [+] [No Name] [+] }| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {12:[No Name] [+] }| + | + ## grid 2 + hello | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + hello | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 4 + hello | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 5 + hell^o | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + end) + it('closes splits', function () + command('sp') + screen:expect([[ + ## grid 1 + [3:-----------------------------------------------------]| + [3:-----------------------------------------------------]| + [3:-----------------------------------------------------]| + [3:-----------------------------------------------------]| + [3:-----------------------------------------------------]| + [3:-----------------------------------------------------]| + {11:[No Name] }| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {12:[No Name] }| + | + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + command('q') + screen:expect([[ + ## grid 1 + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11:[No Name] }| + | + ## grid 2 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + end) + end) + + describe('vertically', function () + it('allocates grids', function () + command('vsp') + screen:expect([[ + ## grid 1 + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + {11:[No Name] }{12:[No Name] }| + | + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + end) + it('resizes grids', function () + command('vsp') + command('vertical resize 10') + -- see "Note 1" for info about why there are two vseps + screen:expect([[ + ## grid 1 + [3:----------]{12:│}[2:------------------------------------------]| + [3:----------]{12:│}[2:------------------------------------------]| + [3:----------]{12:│}[2:------------------------------------------]| + [3:----------]{12:│}[2:------------------------------------------]| + [3:----------]{12:│}[2:------------------------------------------]| + [3:----------]{12:│}[2:------------------------------------------]| + [3:----------]{12:│}[2:------------------------------------------]| + [3:----------]{12:│}[2:------------------------------------------]| + [3:----------]{12:│}[2:------------------------------------------]| + [3:----------]{12:│}[2:------------------------------------------]| + [3:----------]{12:│}[2:------------------------------------------]| + [3:----------]{12:│}[2:------------------------------------------]| + {11:<No Name] }{12:[No Name] }| + | + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + end) + it('splits horizontally', function () + command('vsp') + command('sp') + screen:expect([[ + ## grid 1 + [4:--------------------------]{12:│}[2:--------------------------]| + [4:--------------------------]{12:│}[2:--------------------------]| + [4:--------------------------]{12:│}[2:--------------------------]| + [4:--------------------------]{12:│}[2:--------------------------]| + [4:--------------------------]{12:│}[2:--------------------------]| + [4:--------------------------]{12:│}[2:--------------------------]| + {11:[No Name] }{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + {12:[No Name] [No Name] }| + | + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 4 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + insert('hello') + screen:expect([[ + ## grid 1 + [4:--------------------------]{12:│}[2:--------------------------]| + [4:--------------------------]{12:│}[2:--------------------------]| + [4:--------------------------]{12:│}[2:--------------------------]| + [4:--------------------------]{12:│}[2:--------------------------]| + [4:--------------------------]{12:│}[2:--------------------------]| + [4:--------------------------]{12:│}[2:--------------------------]| + {11:[No Name] [+] }{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + {12:[No Name] [+] [No Name] [+] }| + | + ## grid 2 + hello | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + hello | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 4 + hell^o | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + end) + it('closes splits', function () + command('vsp') + screen:expect([[ + ## grid 1 + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + {11:[No Name] }{12:[No Name] }| + | + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + command('q') + screen:expect([[ + ## grid 1 + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11:[No Name] }| + | + ## grid 2 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + end) + end) + end) + + describe('on resize', function () + it('rebuilds all grids', function () + screen:try_resize(25, 6) + screen:expect([[ + ## grid 1 + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + {11:[No Name] }| + | + ## grid 2 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + end) + + it('has minimum width/height values', function() + screen:try_resize(1, 1) + screen:expect([[ + ## grid 1 + [2:------------]| + {11:[No Name] }| + | + ## grid 2 + ^ | + ]]) + + feed('<esc>:ls') + screen:expect([[ + ## grid 1 + [2:------------]| + {11:[No Name] }| + :ls^ | + ## grid 2 + | + ]]) + end) + end) + + describe('grid of smaller inner size', function() + it('is rendered correctly', function() + screen:try_resize_grid(2, 8, 5) + screen:expect([[ + ## grid 1 + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11:[No Name] }| + | + ## grid 2 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + end) + end) + + describe('grid of bigger inner size', function() + it('is rendered correctly', function() + screen:try_resize_grid(2, 80, 20) + screen:expect([[ + ## grid 1 + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11:[No Name] }| + | + ## grid 2 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + end) + end) + + + describe('with resized grid', function() + before_each(function() + screen:try_resize_grid(2, 60, 20) + end) + it('gets written till grid width', function() + insert(('a'):rep(60).."\n") + screen:expect([[ + ## grid 1 + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11:[No Name] [+] }| + | + ## grid 2 + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + end) + + it('wraps with grid width', function() + insert(('b'):rep(80).."\n") + screen:expect([[ + ## grid 1 + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11:[No Name] [+] }| + | + ## grid 2 + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb| + bbbbbbbbbbbbbbbbbbbb | + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + end) + + it('displays messages with default grid width', function() + command('echomsg "this is a very very very very very very very very'.. + ' long message"') + screen:expect([[ + ## grid 1 + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11:[No Name] }| + this is a very very very...ry very very long message | + ## grid 2 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + end) + + it('creates folds with grid width', function() + insert('this is a fold\nthis is inside fold\nthis is outside fold') + feed('kzfgg') + screen:expect([[ + ## grid 1 + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11:[No Name] [+] }| + | + ## grid 2 + {13:^+-- 2 lines: this is a fold································}| + this is outside fold | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + end) + end) + + it('multiline messages scroll over windows', function() + command('sp') + command('vsp') + screen:expect([[ + ## grid 1 + [4:--------------------------]{12:│}[3:--------------------------]| + [4:--------------------------]{12:│}[3:--------------------------]| + [4:--------------------------]{12:│}[3:--------------------------]| + [4:--------------------------]{12:│}[3:--------------------------]| + [4:--------------------------]{12:│}[3:--------------------------]| + [4:--------------------------]{12:│}[3:--------------------------]| + {11:[No Name] }{12:[No Name] }| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {12:[No Name] }| + | + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 4 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + + feed(":echoerr 'very' | echoerr 'much' | echoerr 'fail'<cr>") + screen:expect([[ + ## grid 1 + [4:--------------------------]{12:│}[3:--------------------------]| + [4:--------------------------]{12:│}[3:--------------------------]| + [4:--------------------------]{12:│}[3:--------------------------]| + [4:--------------------------]{12:│}[3:--------------------------]| + [4:--------------------------]{12:│}[3:--------------------------]| + [4:--------------------------]{12:│}[3:--------------------------]| + {11:[No Name] }{12:[No Name] }| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {11: }| + {14:very} | + {14:much} | + {14:fail} | + {15:Press ENTER or type command to continue}^ | + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 4 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + + feed('<cr>') + screen:expect([[ + ## grid 1 + [4:--------------------------]{12:│}[3:--------------------------]| + [4:--------------------------]{12:│}[3:--------------------------]| + [4:--------------------------]{12:│}[3:--------------------------]| + [4:--------------------------]{12:│}[3:--------------------------]| + [4:--------------------------]{12:│}[3:--------------------------]| + [4:--------------------------]{12:│}[3:--------------------------]| + {11:[No Name] }{12:[No Name] }| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {12:[No Name] }| + | + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 4 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + + command([[ + func! ErrMsg() + for i in range(11) + echoerr "error ".i + endfor + endfunc]]) + feed(":call ErrMsg()<cr>") + screen:expect([[ + ## grid 1 + {14:Error detected while processing function ErrMsg:} | + {19:line 2:} | + {14:error 0} | + {14:error 1} | + {14:error 2} | + {14:error 3} | + {14:error 4} | + {14:error 5} | + {14:error 6} | + {14:error 7} | + {14:error 8} | + {14:error 9} | + {14:error 10} | + {15:Press ENTER or type command to continue}^ | + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 4 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + + feed("<c-c>") + screen:expect([[ + ## grid 1 + [4:--------------------------]{12:│}[3:--------------------------]| + [4:--------------------------]{12:│}[3:--------------------------]| + [4:--------------------------]{12:│}[3:--------------------------]| + [4:--------------------------]{12:│}[3:--------------------------]| + [4:--------------------------]{12:│}[3:--------------------------]| + [4:--------------------------]{12:│}[3:--------------------------]| + {11:[No Name] }{12:[No Name] }| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + [2:-----------------------------------------------------]| + {12:[No Name] }| + | + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 4 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + end) + + it('handles switich tabs', function() + command('vsp') + screen:expect([[ + ## grid 1 + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + {11:[No Name] }{12:[No Name] }| + | + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + + + command('tabnew') + -- note the old grids aren't resized yet + screen:expect([[ + ## grid 1 + {16: }{17:2}{16: [No Name] }{7: [No Name] }{12: }{16:X}| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + {11:[No Name] }| + | + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 4 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + + command('sp') + screen:expect([[ + ## grid 1 + {16: }{17:2}{16: [No Name] }{7: }{18:2}{7: [No Name] }{12: }{16:X}| + [5:-----------------------------------------------------]| + [5:-----------------------------------------------------]| + [5:-----------------------------------------------------]| + [5:-----------------------------------------------------]| + [5:-----------------------------------------------------]| + {11:[No Name] }| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + {12:[No Name] }| + | + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 4 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 5 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + + command('tabnext') + screen:expect([[ + ## grid 1 + {7: }{18:2}{7: [No Name] }{16: }{17:2}{16: [No Name] }{12: }{16:X}| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + {11:[No Name] }{12:[No Name] }| + | + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 4 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 5 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + + command('tabnext') + screen:expect([[ + ## grid 1 + {16: }{17:2}{16: [No Name] }{7: }{18:2}{7: [No Name] }{12: }{16:X}| + [5:-----------------------------------------------------]| + [5:-----------------------------------------------------]| + [5:-----------------------------------------------------]| + [5:-----------------------------------------------------]| + [5:-----------------------------------------------------]| + {11:[No Name] }| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + [4:-----------------------------------------------------]| + {12:[No Name] }| + | + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 4 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 5 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + + command('tabclose') + screen:expect([[ + ## grid 1 + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + [3:--------------------------]{12:│}[2:--------------------------]| + {11:[No Name] }{12:[No Name] }| + | + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]) + end) +end) diff --git a/test/functional/ui/options_spec.lua b/test/functional/ui/options_spec.lua index 32e8faf7d3..c26fa5e29b 100644 --- a/test/functional/ui/options_spec.lua +++ b/test/functional/ui/options_spec.lua @@ -26,6 +26,7 @@ describe('ui receives option updates', function() ext_wildmenu=false, ext_linegrid=false, ext_hlstate=false, + ext_multigrid=false, } clear(...) diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua index aa99499ec6..87b489fd71 100644 --- a/test/functional/ui/output_spec.lua +++ b/test/functional/ui/output_spec.lua @@ -51,6 +51,9 @@ describe("shell command :!", function() end) it("throttles shell-command output greater than ~10KB", function() + if helpers.skip_fragile(pending) then + return + end child_session.feed_data( ":!for i in $(seq 2 30000); do echo XXXXXXXXXX $i; done\n") diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index 606c7c1e26..9424931de4 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -2,6 +2,9 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear, feed = helpers.clear, helpers.feed local source = helpers.source +local insert = helpers.insert +local meths = helpers.meths +local command = helpers.command describe('ui/ext_popupmenu', function() local screen @@ -14,22 +17,25 @@ describe('ui/ext_popupmenu', 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] = {background = Screen.colors.WebGray}, + [7] = {background = Screen.colors.LightMagenta}, }) - end) - - it('works', function() source([[ function! TestComplete() abort - call complete(1, ['foo', 'bar', 'spam']) + call complete(1, [{'word':'foo', 'abbr':'fo', 'menu':'the foo', 'info':'foo-y', 'kind':'x'}, 'bar', 'spam']) return '' endfunction ]]) - local expected = { - {'foo', '', '', ''}, - {'bar', '', '', ''}, - {'spam', '', '', ''}, - } + end) + + local expected = { + {'fo', 'x', 'the foo', 'foo-y'}, + {'bar', '', '', ''}, + {'spam', '', '', ''}, + } + + it('works', function() feed('o<C-r>=TestComplete()<CR>') screen:expect{grid=[[ | @@ -91,8 +97,277 @@ describe('ui/ext_popupmenu', function() {2:-- INSERT --} | ]]} end) + + it('can be controlled by API', function() + feed('o<C-r>=TestComplete()<CR>') + screen:expect{grid=[[ + | + foo^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]], popupmenu={ + items=expected, + pos=0, + anchor={1,0}, + }} + + meths.select_popupmenu_item(1,false,false,{}) + screen:expect{grid=[[ + | + foo^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]], popupmenu={ + items=expected, + pos=1, + anchor={1,0}, + }} + + meths.select_popupmenu_item(2,true,false,{}) + screen:expect{grid=[[ + | + spam^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]], popupmenu={ + items=expected, + pos=2, + anchor={1,0}, + }} + + meths.select_popupmenu_item(0,true,true,{}) + screen:expect([[ + | + foo^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]]) + + + feed('<c-w><C-r>=TestComplete()<CR>') + screen:expect{grid=[[ + | + foo^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]], popupmenu={ + items=expected, + pos=0, + anchor={1,0}, + }} + + meths.select_popupmenu_item(-1,false,false,{}) + screen:expect{grid=[[ + | + foo^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]], popupmenu={ + items=expected, + pos=-1, + anchor={1,0}, + }} + + meths.select_popupmenu_item(1,true,false,{}) + screen:expect{grid=[[ + | + bar^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]], popupmenu={ + items=expected, + pos=1, + anchor={1,0}, + }} + + meths.select_popupmenu_item(-1,true,false,{}) + screen:expect{grid=[[ + | + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]], popupmenu={ + items=expected, + pos=-1, + anchor={1,0}, + }} + + meths.select_popupmenu_item(0,true,false,{}) + screen:expect{grid=[[ + | + foo^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]], popupmenu={ + items=expected, + pos=0, + anchor={1,0}, + }} + + meths.select_popupmenu_item(-1,true,true,{}) + screen:expect([[ + | + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]]) + + command('imap <f1> <cmd>call nvim_select_popupmenu_item(2,v:true,v:false,{})<cr>') + command('imap <f2> <cmd>call nvim_select_popupmenu_item(-1,v:false,v:false,{})<cr>') + command('imap <f3> <cmd>call nvim_select_popupmenu_item(1,v:false,v:true,{})<cr>') + feed('<C-r>=TestComplete()<CR>') + screen:expect{grid=[[ + | + foo^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]], popupmenu={ + items=expected, + pos=0, + anchor={1,0}, + }} + + feed('<f1>') + screen:expect{grid=[[ + | + spam^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]], popupmenu={ + items=expected, + pos=2, + anchor={1,0}, + }} + + feed('<f2>') + screen:expect{grid=[[ + | + spam^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]], popupmenu={ + items=expected, + pos=-1, + anchor={1,0}, + }} + + feed('<f3>') + screen:expect([[ + | + bar^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]]) + + -- also should work for builtin popupmenu + screen:set_option('ext_popupmenu', false) + feed('<C-r>=TestComplete()<CR>') + screen:expect([[ + | + foo^ | + {6:fo x the foo }{1: }| + {7:bar }{1: }| + {7:spam }{1: }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]]) + + feed('<f1>') + screen:expect([[ + | + spam^ | + {7:fo x the foo }{1: }| + {7:bar }{1: }| + {6:spam }{1: }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]]) + + feed('<f2>') + screen:expect([[ + | + spam^ | + {7:fo x the foo }{1: }| + {7:bar }{1: }| + {7:spam }{1: }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]]) + + feed('<f3>') + screen:expect([[ + | + bar^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]]) + end) end) + describe('popup placement', function() local screen before_each(function() @@ -257,4 +532,79 @@ describe('popup placement', function() {2:-- }{5:match 1 of 10} | ]]) end) + + it('works with vsplits', function() + insert('aaa aab aac\n') + feed(':vsplit<cr>') + screen:expect([[ + aaa aab aac {3:│}aaa aab aac| + ^ {3:│} | + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {4:[No Name] [+] }{3:<Name] [+] }| + :vsplit | + ]]) + + feed('ibbb a<c-x><c-n>') + screen:expect([[ + aaa aab aac {3:│}aaa aab aac| + bbb aaa^ {3:│}bbb aaa | + {1:~ }{s: aaa }{1: }{3:│}{1:~ }| + {1:~ }{n: aab }{1: }{3:│}{1:~ }| + {1:~ }{n: aac }{1: }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {4:[No Name] [+] }{3:<Name] [+] }| + {2:-- }{5:match 1 of 3} | + ]]) + + feed('<esc><c-w><c-w>oc a<c-x><c-n>') + screen:expect([[ + aaa aab aac{3:│}aaa aab aac | + bbb aaa {3:│}bbb aaa | + c aaa {3:│}c aaa^ | + {1:~ }{3:│}{1:~}{s: aaa }{1: }| + {1:~ }{3:│}{1:~}{n: aab }{1: }| + {1:~ }{3:│}{1:~}{n: aac }{1: }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {1:~ }{3:│}{1:~ }| + {3:<Name] [+] }{4:[No Name] [+] }| + {2:-- }{5:match 1 of 3} | + ]]) + end) end) diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index 75eb5bb4e3..69f4a44dd8 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -75,9 +75,11 @@ local global_helpers = require('test.helpers') local deepcopy = global_helpers.deepcopy local shallowcopy = global_helpers.shallowcopy local helpers = require('test.functional.helpers')(nil) -local request, run, uimeths = helpers.request, helpers.run, helpers.uimeths +local request, run_session = helpers.request, helpers.run_session local eq = helpers.eq local dedent = helpers.dedent +local get_session = helpers.get_session +local create_callindex = helpers.create_callindex local inspect = require('inspect') @@ -155,6 +157,8 @@ function Screen.new(width, height) cmdline_block = {}, wildmenu_items = nil, wildmenu_selected = nil, + win_position = {}, + _session = nil, _default_attr_ids = nil, _default_attr_ignore = nil, _mouse_enabled = true, @@ -165,11 +169,19 @@ function Screen.new(width, height) _new_attrs = false, _width = width, _height = height, + _grids = {}, _cursor = { - row = 1, col = 1 + grid = 1, row = 1, col = 1 }, - _busy = false + _busy = false, }, Screen) + local function ui(method, ...) + local status, rv = self._session:request('nvim_ui_'..method, ...) + if not status then + error(rv[2]) + end + end + self.uimeths = create_callindex(ui) return self end @@ -189,34 +201,50 @@ function Screen:set_hlstate_cterm(val) self._hlstate_cterm = val end -function Screen:attach(options) +function Screen:attach(options, session) + if session == nil then + session = get_session() + end if options == nil then options = {} end if options.ext_linegrid == nil then options.ext_linegrid = true end + + self._session = session self._options = options self._clear_attrs = (options.ext_linegrid and {{},{}}) or {} self:_handle_resize(self._width, self._height) - uimeths.attach(self._width, self._height, options) + self.uimeths.attach(self._width, self._height, options) if self._options.rgb == nil then -- nvim defaults to rgb=true internally, -- simplify test code by doing the same. self._options.rgb = true end + if self._options.ext_multigrid then + self._options.ext_linegrid = true + end + self._session = session end function Screen:detach() - uimeths.detach() + self.uimeths.detach() + self._session = nil end function Screen:try_resize(columns, rows) - uimeths.try_resize(columns, rows) + self._width = columns + self._height = rows + self.uimeths.try_resize(columns, rows) +end + +function Screen:try_resize_grid(grid, columns, rows) + self.uimeths.try_resize_grid(grid, columns, rows) end function Screen:set_option(option, value) - uimeths.set_option(option, value) + self.uimeths.set_option(option, value) self._options[option] = value end @@ -321,7 +349,6 @@ function Screen:expect(expected, attr_ids, attr_ignore) -- value. grid = dedent(grid:gsub('\n[ ]+$', ''), 0) for row in grid:gmatch('[^\n]+') do - row = row:sub(1, #row - 1) -- Last char must be the screen delimiter. table.insert(expected_rows, row) end end @@ -341,19 +368,11 @@ function Screen:expect(expected, attr_ids, attr_ignore) end end - if grid ~= nil and self._height ~= #expected_rows then - return ("Expected screen state's row count(" .. #expected_rows - .. ') differs from configured height(' .. self._height .. ') of Screen.') - end - if self._options.ext_hlstate and self._new_attrs then attr_state.id_to_index = self:hlstate_check_attrs(attr_state.ids or {}) end - local actual_rows = {} - for i = 1, self._height do - actual_rows[i] = self:_row_repr(self._rows[i], attr_state) - end + local actual_rows = self:render(not expected.any, attr_state) if expected.any ~= nil then -- Search for `any` anywhere in the screen lines. @@ -362,13 +381,17 @@ function Screen:expect(expected, attr_ids, attr_ignore) return ( 'Failed to match any screen lines.\n' .. 'Expected (anywhere): "' .. expected.any .. '"\n' - .. 'Actual:\n |' .. table.concat(actual_rows, '|\n |') .. '|\n\n') + .. 'Actual:\n |' .. table.concat(actual_rows, '\n |') .. '\n\n') end end if grid ~= nil then -- `expected` must match the screen lines exactly. - for i = 1, self._height do + if #actual_rows ~= #expected_rows then + return "Expected screen state's row count(" .. #expected_rows + .. ') differs from configured height(' .. #actual_rows .. ') of Screen.' + end + for i = 1, #actual_rows do if expected_rows[i] ~= actual_rows[i] then local msg_expected_rows = {} for j = 1, #expected_rows do @@ -378,8 +401,8 @@ function Screen:expect(expected, attr_ids, attr_ignore) actual_rows[i] = '*' .. actual_rows[i] return ( 'Row ' .. tostring(i) .. ' did not match.\n' - ..'Expected:\n |'..table.concat(msg_expected_rows, '|\n |')..'|\n' - ..'Actual:\n |'..table.concat(actual_rows, '|\n |')..'|\n\n'..[[ + ..'Expected:\n |'..table.concat(msg_expected_rows, '\n |')..'\n' + ..'Actual:\n |'..table.concat(actual_rows, '\n |')..'\n\n'..[[ To print the expect() call that would assert the current screen state, use screen:snapshot_util(). In case of non-deterministic failures, use screen:redraw_debug() to show all intermediate screen states. ]]) @@ -465,7 +488,7 @@ function Screen:_wait(check, flags) if not err then success_seen = true if did_miminal_timeout then - helpers.stop() + self._session:stop() end elseif success_seen and #args > 0 then failure_after_success = true @@ -474,7 +497,7 @@ function Screen:_wait(check, flags) return true end - run(nil, notification_cb, nil, minimal_timeout) + run_session(self._session, nil, notification_cb, nil, minimal_timeout) if not did_flush then err = "no flush received" elseif not checked then @@ -487,7 +510,7 @@ function Screen:_wait(check, flags) if not success_seen then did_miminal_timeout = true - run(nil, notification_cb, nil, timeout-minimal_timeout) + run_session(self._session, nil, notification_cb, nil, timeout-minimal_timeout) end local did_warn = false @@ -547,7 +570,7 @@ function Screen:sleep(ms) assert(method == 'redraw') self:_redraw(args) end - run(nil, notification_cb, nil, ms) + run_session(self._session, nil, notification_cb, nil, ms) end function Screen:_redraw(updates) @@ -579,6 +602,22 @@ function Screen:set_on_event_handler(callback) end function Screen:_handle_resize(width, height) + self:_handle_grid_resize(1, width, height) + self._scroll_region = { + top = 1, bot = height, left = 1, right = width + } + self._grid = self._grids[1] +end + +local function min(x,y) + if x < y then + return x + else + return y + end +end + +function Screen:_handle_grid_resize(grid, width, height) local rows = {} for _ = 1, height do local cols = {} @@ -587,22 +626,36 @@ function Screen:_handle_resize(width, height) end table.insert(rows, cols) end - self._cursor.row = 1 - self._cursor.col = 1 - self._rows = rows - self._width = width - self._height = height - self._scroll_region = { - top = 1, bot = height, left = 1, right = width + if grid > 1 and self._grids[grid] ~= nil then + local old = self._grids[grid] + for i = 1, min(height,old.height) do + for j = 1, min(width,old.width) do + rows[i][j] = old.rows[i][j] + end + end + end + + if self._cursor.grid == grid then + self._cursor.row = 1 + self._cursor.col = 1 + end + self._grids[grid] = { + rows=rows, + width=width, + height=height, } end -function Screen:_handle_flush() +function Screen:_handle_win_scroll_over_start() + self.scroll_over = true + self.scroll_over_pos = self._grids[1].height end -function Screen:_handle_grid_resize(grid, width, height) - assert(grid == 1) - self:_handle_resize(width, height) +function Screen:_handle_win_scroll_over_reset() + self.scroll_over = false +end + +function Screen:_handle_flush() end function Screen:_reset() @@ -641,20 +694,27 @@ function Screen:_handle_clear() -- newer clients, to check we remain compatible with both kind of clients, -- ensure the scroll region is in a reset state. local expected_region = { - top = 1, bot = self._height, left = 1, right = self._width + top = 1, bot = self._grid.height, left = 1, right = self._grid.width } eq(expected_region, self._scroll_region) - self:_clear_block(1, self._height, 1, self._width) + self:_handle_grid_clear(1) end function Screen:_handle_grid_clear(grid) - assert(grid == 1) - self:_clear_block(1, self._height, 1, self._width) + self:_clear_block(self._grids[grid], 1, self._grids[grid].height, 1, self._grids[grid].width) +end + +function Screen:_handle_grid_destroy(grid) + self._grids[grid] = nil + if self._options.ext_multigrid then + assert(self.win_position[grid]) + self.win_position[grid] = nil + end end function Screen:_handle_eol_clear() local row, col = self._cursor.row, self._cursor.col - self:_clear_block(row, row, col, self._scroll_region.right) + self:_clear_block(self._grid, row, row, col, self._grid.width) end function Screen:_handle_cursor_goto(row, col) @@ -663,7 +723,7 @@ function Screen:_handle_cursor_goto(row, col) end function Screen:_handle_grid_cursor_goto(grid, row, col) - assert(grid == 1) + self._cursor.grid = grid self._cursor.row = row + 1 self._cursor.col = col + 1 end @@ -704,13 +764,18 @@ function Screen:_handle_scroll(count) self:_handle_grid_scroll(1, top-1, bot, left-1, right, count, 0) end -function Screen:_handle_grid_scroll(grid, top, bot, left, right, rows, cols) +function Screen:_handle_grid_scroll(g, top, bot, left, right, rows, cols) + if self.scroll_over and g == 1 and top < self.scroll_over_pos then + self.scroll_over_pos = top + end + top = top+1 left = left+1 - assert(grid == 1) assert(cols == 0) + local grid = self._grids[g] local start, stop, step + if rows > 0 then start = top stop = bot - rows @@ -723,8 +788,8 @@ function Screen:_handle_grid_scroll(grid, top, bot, left, right, rows, cols) -- shift scroll region for i = start, stop, step do - local target = self._rows[i] - local source = self._rows[i + rows] + local target = grid.rows[i] + local source = grid.rows[i + rows] for j = left, right do target[j].text = source[j].text target[j].attrs = source[j].attrs @@ -734,7 +799,7 @@ function Screen:_handle_grid_scroll(grid, top, bot, left, right, rows, cols) -- clear invalid rows for i = stop + step, stop + rows, step do - self:_clear_row_section(i, left, right) + self:_clear_row_section(grid, i, left, right) end end @@ -744,13 +809,35 @@ function Screen:_handle_hl_attr_define(id, rgb_attrs, cterm_attrs, info) self._new_attrs = true end +function Screen:_handle_win_pos(grid, win, startrow, startcol, width, height) + self.win_position[grid] = { + win = win, + startrow = startrow, + startcol = startcol, + width = width, + height = height + } +end + +function Screen:_handle_win_hide(grid) + self.win_position[grid] = nil +end + +function Screen:get_hl(val) + if self._options.ext_newgrid then + return self._attr_table[val][1] + else + return val + end +end + function Screen:_handle_highlight_set(attrs) self._attrs = attrs end function Screen:_handle_put(str) assert(not self._options.ext_linegrid) - local cell = self._rows[self._cursor.row][self._cursor.col] + local cell = self._grid.rows[self._cursor.row][self._cursor.col] cell.text = str cell.attrs = self._attrs cell.hl_id = -1 @@ -759,8 +846,7 @@ end function Screen:_handle_grid_line(grid, row, col, items) assert(self._options.ext_linegrid) - assert(grid == 1) - local line = self._rows[row+1] + local line = self._grids[grid].rows[row+1] local colpos = col+1 local hl = self._clear_attrs local hl_id = 0 @@ -887,45 +973,68 @@ function Screen:_handle_wildmenu_hide() self.wildmenu_items, self.wildmenu_pos = nil, nil end -function Screen:_clear_block(top, bot, left, right) +function Screen:_clear_block(grid, top, bot, left, right) for i = top, bot do - self:_clear_row_section(i, left, right) + self:_clear_row_section(grid, i, left, right) end end -function Screen:_clear_row_section(rownum, startcol, stopcol) - local row = self._rows[rownum] +function Screen:_clear_row_section(grid, rownum, startcol, stopcol) + local row = grid.rows[rownum] for i = startcol, stopcol do row[i].text = ' ' row[i].attrs = self._clear_attrs end end -function Screen:_row_repr(row, attr_state) +function Screen:_row_repr(gridnr, rownr, attr_state, cursor) local rv = {} local current_attr_id - for i = 1, self._width do - local attrs = row[i].attrs - if self._options.ext_linegrid then - attrs = attrs[(self._options.rgb and 1) or 2] - end - local attr_id = self:_get_attr_id(attr_state, attrs, row[i].hl_id) - if current_attr_id and attr_id ~= current_attr_id then - -- close current attribute bracket, add it before any whitespace - -- up to the current cell - -- table.insert(rv, backward_find_meaningful(rv, i), '}') - table.insert(rv, '}') - current_attr_id = nil - end - if not current_attr_id and attr_id then - -- open a new attribute bracket - table.insert(rv, '{' .. attr_id .. ':') - current_attr_id = attr_id + local i = 1 + local has_windows = self._options.ext_multigrid and gridnr == 1 + if self.scroll_over and self.scroll_over_pos < rownr then + has_windows = false + end + local row = self._grids[gridnr].rows[rownr] + while i <= #row do + local did_window = false + if has_windows then + for id,pos in pairs(self.win_position) do + if i-1 == pos.startcol and pos.startrow <= rownr-1 and rownr-1 < pos.startrow + pos.height then + if current_attr_id then + -- close current attribute bracket + table.insert(rv, '}') + current_attr_id = nil + end + table.insert(rv, '['..id..':'..string.rep('-',pos.width)..']') + i = i + pos.width + did_window = true + end + end end - if not self._busy and self._rows[self._cursor.row] == row and self._cursor.col == i then - table.insert(rv, '^') + + if not did_window then + local attrs = row[i].attrs + if self._options.ext_linegrid then + attrs = attrs[(self._options.rgb and 1) or 2] + end + local attr_id = self:_get_attr_id(attr_state, attrs, row[i].hl_id) + if current_attr_id and attr_id ~= current_attr_id then + -- close current attribute bracket + table.insert(rv, '}') + current_attr_id = nil + end + if not current_attr_id and attr_id then + -- open a new attribute bracket + table.insert(rv, '{' .. attr_id .. ':') + current_attr_id = attr_id + end + if not self._busy and cursor and self._cursor.col == i then + table.insert(rv, '^') + end + table.insert(rv, row[i].text) + i = i + 1 end - table.insert(rv, row[i].text) end if current_attr_id then table.insert(rv, '}') @@ -997,7 +1106,23 @@ function Screen:redraw_debug(attrs, ignore, timeout) if timeout == nil then timeout = 250 end - run(nil, notification_cb, nil, timeout) + run_session(self._session, nil, notification_cb, nil, timeout) +end + +function Screen:render(headers, attr_state, preview) + headers = headers and self._options.ext_multigrid + local rv = {} + for igrid,grid in pairs(self._grids) do + if headers then + table.insert(rv, "## grid "..igrid) + end + for i = 1, grid.height do + local cursor = self._cursor.grid == igrid and self._cursor.row == i + local prefix = (headers or preview) and " " or "" + table.insert(rv, prefix..self:_row_repr(igrid, i, attr_state, cursor).."|") + end + end + return rv end function Screen:print_snapshot(attrs, ignore) @@ -1020,10 +1145,7 @@ function Screen:print_snapshot(attrs, ignore) attr_state.id_to_index = self:hlstate_check_attrs(attr_state.ids) end - local lines = {} - for i = 1, self._height do - table.insert(lines, " "..self:_row_repr(self._rows[i], attr_state).."|") - end + local lines = self:render(true, attr_state, true) local ext_state = self:_extstate_repr(attr_state) local keys = false diff --git a/test/functional/ui/wildmode_spec.lua b/test/functional/ui/wildmode_spec.lua index 8931d9245b..ffe71cfadf 100644 --- a/test/functional/ui/wildmode_spec.lua +++ b/test/functional/ui/wildmode_spec.lua @@ -165,7 +165,7 @@ describe("'wildmenu'", function() feed([[:<Tab>]]) -- Invoke wildmenu. -- Check only the last 2 lines, because the shell output is -- system-dependent. - expect_stay_unchanged{any='! # & < = > @ > \n:!^'} + expect_stay_unchanged{any='! # & < = > @ > |\n:!^'} end) end) diff --git a/test/helpers.lua b/test/helpers.lua index 6ef7a456f4..a6ed312213 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -49,8 +49,8 @@ local check_logs_useless_lines = { local function eq(expected, actual, ctx) return assert.are.same(expected, actual, ctx) end -local function neq(expected, actual) - return assert.are_not.same(expected, actual) +local function neq(expected, actual, context) + return assert.are_not.same(expected, actual, context) end local function ok(res) return assert.is_true(res) |