mirror of
https://github.com/neovim/neovim.git
synced 2025-09-05 19:08:15 +00:00
Compare commits
70 Commits
v0.11.3
...
1063aff643
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1063aff643 | ||
![]() |
e415fae42e | ||
![]() |
7b099d2b2b | ||
![]() |
cec0ecabd8 | ||
![]() |
edfb447ff8 | ||
![]() |
b032c2b53f | ||
![]() |
09f702bc13 | ||
![]() |
c4845f3a12 | ||
![]() |
a2603016ce | ||
![]() |
d70d469c04 | ||
![]() |
99817471d7 | ||
![]() |
4c5cb950c6 | ||
![]() |
ec8900f1e6 | ||
![]() |
a3590afba5 | ||
![]() |
e841d653af | ||
![]() |
6fd8ba05a6 | ||
![]() |
e6ea97a691 | ||
![]() |
3cf9dac2ba | ||
![]() |
8dd88056f1 | ||
![]() |
1bea812953 | ||
![]() |
fa64f2d09b | ||
![]() |
3ab06d5188 | ||
![]() |
37b2d42459 | ||
![]() |
53db7fc3ef | ||
![]() |
3343ee971b | ||
![]() |
abfbd155da | ||
![]() |
39ae9a9971 | ||
![]() |
5ec7d98857 | ||
![]() |
744d96bd76 | ||
![]() |
30b801eff2 | ||
![]() |
27282696fe | ||
![]() |
4b957a4d18 | ||
![]() |
819e545c28 | ||
![]() |
d21db345ef | ||
![]() |
bd4b45dd1b | ||
![]() |
6c2f06b537 | ||
![]() |
fb6c677d57 | ||
![]() |
e68d3ef886 | ||
![]() |
35a66f74c7 | ||
![]() |
ced4eed733 | ||
![]() |
e299430ff5 | ||
![]() |
53a0d99702 | ||
![]() |
a65c4be2de | ||
![]() |
30db74de66 | ||
![]() |
6fd842a4fd | ||
![]() |
7f1e112a32 | ||
![]() |
54c2ea142a | ||
![]() |
f4b4c27a35 | ||
![]() |
5551da79c1 | ||
![]() |
8f2d6f7ce2 | ||
![]() |
09b0003d38 | ||
![]() |
41fa343484 | ||
![]() |
2e4baa3679 | ||
![]() |
6b820258cd | ||
![]() |
990b320592 | ||
![]() |
a05b70baa6 | ||
![]() |
359d65c902 | ||
![]() |
62aae1084f | ||
![]() |
e534afa5ab | ||
![]() |
2124146164 | ||
![]() |
64afa93187 | ||
![]() |
e6a0f0ee71 | ||
![]() |
685302682a | ||
![]() |
5e7021eb1b | ||
![]() |
44b8255fa2 | ||
![]() |
407fc0bb16 | ||
![]() |
657540945c | ||
![]() |
9261aef2f3 | ||
![]() |
d185057bc7 | ||
![]() |
6cfaa9c204 |
20
.github/scripts/env.ps1
vendored
20
.github/scripts/env.ps1
vendored
@@ -1,9 +1,17 @@
|
||||
# This script enables Developer Command Prompt
|
||||
# See https://github.com/microsoft/vswhere/wiki/Start-Developer-Command-Prompt#using-powershell
|
||||
$installationPath = vswhere.exe -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
|
||||
if ($installationPath -and (Test-Path "$installationPath\Common7\Tools\vsdevcmd.bat")) {
|
||||
& "${env:COMSPEC}" /s /c "`"$installationPath\Common7\Tools\vsdevcmd.bat`" -arch=x64 -no_logo && set" | ForEach-Object {
|
||||
$name, $value = $_ -split '=', 2
|
||||
"$name=$value" >> $env:GITHUB_ENV
|
||||
}
|
||||
if ($env:BUILD_ARCH -eq "arm64") {
|
||||
$arch = "arm64"
|
||||
$installationPath = vswhere.exe -latest -requires Microsoft.VisualStudio.Component.VC.Tools.arm64 -property installationPath
|
||||
} else {
|
||||
$arch = "x64"
|
||||
$installationPath = vswhere.exe -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
|
||||
}
|
||||
|
||||
if ($installationPath) {
|
||||
& "${env:COMSPEC}" /s /c "`"$installationPath\Common7\Tools\vsdevcmd.bat`" -arch=$arch -no_logo && set" |
|
||||
ForEach-Object {
|
||||
$name, $value = $_ -split '=', 2
|
||||
"$name=$value" >> $env:GITHUB_ENV
|
||||
}
|
||||
}
|
||||
|
13
.github/workflows/notes.md
vendored
13
.github/workflows/notes.md
vendored
@@ -2,21 +2,26 @@
|
||||
${NVIM_VERSION}
|
||||
```
|
||||
|
||||
## Release notes
|
||||
|
||||
- [Changelog](https://github.com/neovim/neovim/commit/${NVIM_COMMIT}) (fixes + features)
|
||||
- [News](./runtime/doc/news.txt) (`:help news` in Nvim)
|
||||
|
||||
## Install
|
||||
|
||||
### Windows
|
||||
|
||||
#### Zip
|
||||
|
||||
1. Download **nvim-win64.zip**
|
||||
1. Download **nvim-win64.zip** (or **nvim-win-arm64.zip** for ARM)
|
||||
2. Extract the zip
|
||||
3. Run `nvim.exe` on your CLI of choice
|
||||
3. Run `nvim.exe` in your terminal
|
||||
|
||||
#### MSI
|
||||
|
||||
1. Download **nvim-win64.msi**
|
||||
1. Download **nvim-win64.msi** (or **nvim-win-arm64.msi** for ARM)
|
||||
2. Run the MSI
|
||||
3. Run `nvim.exe` on your CLI of choice
|
||||
3. Run `nvim.exe` in your terminal
|
||||
|
||||
Note: On Windows "Server" you may need to [install vcruntime140.dll](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170).
|
||||
|
||||
|
34
.github/workflows/release.yml
vendored
34
.github/workflows/release.yml
vendored
@@ -132,27 +132,44 @@ jobs:
|
||||
|
||||
windows:
|
||||
needs: setup
|
||||
runs-on: windows-2022
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- runner: windows-2022
|
||||
arch: x86_64
|
||||
archive_name: nvim-win64
|
||||
- runner: windows-11-arm
|
||||
arch: arm64
|
||||
archive_name: nvim-win-arm64
|
||||
runs-on: ${{ matrix.runner }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# Perform a full checkout #13471
|
||||
fetch-depth: 0
|
||||
- run: .github/scripts/env.ps1
|
||||
env:
|
||||
BUILD_ARCH: ${{ matrix.arch }}
|
||||
- name: Install Wix
|
||||
run: |
|
||||
Invoke-WebRequest -Uri "https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314-binaries.zip" -OutFile "wix314-binaries.zip"
|
||||
Expand-Archive -Path "wix314-binaries.zip" -DestinationPath "C:/wix"
|
||||
echo "C:\wix" >> $env:GITHUB_PATH
|
||||
- name: Build deps
|
||||
run: |
|
||||
cmake -S cmake.deps -B .deps -G Ninja -DCMAKE_BUILD_TYPE=${{ needs.setup.outputs.build_type }}
|
||||
cmake --build .deps
|
||||
- name: build package
|
||||
- name: Build package
|
||||
run: |
|
||||
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ needs.setup.outputs.build_type }}
|
||||
cmake --build build --target package
|
||||
- uses: actions/upload-artifact@v4
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: nvim-win64
|
||||
name: nvim-win-${{ matrix.arch }}
|
||||
path: |
|
||||
build/nvim-win64.msi
|
||||
build/nvim-win64.zip
|
||||
build/${{ matrix.archive_name }}.zip
|
||||
build/${{ matrix.archive_name }}.msi
|
||||
retention-days: 1
|
||||
|
||||
publish:
|
||||
@@ -196,10 +213,11 @@ jobs:
|
||||
- name: Publish release
|
||||
env:
|
||||
NVIM_VERSION: ${{ needs.linux.outputs.version }}
|
||||
NVIM_COMMIT: ${{ github.sha }}
|
||||
DEBUG: api
|
||||
run: |
|
||||
envsubst < "$GITHUB_WORKSPACE/.github/workflows/notes.md" > "$RUNNER_TEMP/notes.md"
|
||||
if [ "$TAG_NAME" != "nightly" ]; then
|
||||
gh release create stable $PRERELEASE --notes-file "$RUNNER_TEMP/notes.md" --title "$SUBJECT" --target $GITHUB_SHA nvim-macos-x86_64/* nvim-macos-arm64/* nvim-linux-x86_64/* nvim-linux-arm64/* nvim-appimage-x86_64/* nvim-appimage-arm64/* nvim-win64/*
|
||||
gh release create stable $PRERELEASE --notes-file "$RUNNER_TEMP/notes.md" --title "$SUBJECT" --target $GITHUB_SHA nvim-macos-x86_64/* nvim-macos-arm64/* nvim-linux-x86_64/* nvim-linux-arm64/* nvim-appimage-x86_64/* nvim-appimage-arm64/* nvim-win-x86_64/* nvim-win-arm64/*
|
||||
fi
|
||||
gh release create $TAG_NAME $PRERELEASE --notes-file "$RUNNER_TEMP/notes.md" --title "$SUBJECT" --target $GITHUB_SHA nvim-macos-x86_64/* nvim-macos-arm64/* nvim-linux-x86_64/* nvim-linux-arm64/* nvim-appimage-x86_64/* nvim-appimage-arm64/* nvim-win64/*
|
||||
gh release create $TAG_NAME $PRERELEASE --notes-file "$RUNNER_TEMP/notes.md" --title "$SUBJECT" --target $GITHUB_SHA nvim-macos-x86_64/* nvim-macos-arm64/* nvim-linux-x86_64/* nvim-linux-arm64/* nvim-appimage-x86_64/* nvim-appimage-arm64/* nvim-win-x86_64/* nvim-win-arm64/*
|
||||
|
@@ -141,8 +141,8 @@ endif()
|
||||
# version string, else they are combined with the result of `git describe`.
|
||||
set(NVIM_VERSION_MAJOR 0)
|
||||
set(NVIM_VERSION_MINOR 11)
|
||||
set(NVIM_VERSION_PATCH 3)
|
||||
set(NVIM_VERSION_PRERELEASE "") # for package maintainers
|
||||
set(NVIM_VERSION_PATCH 5)
|
||||
set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers
|
||||
|
||||
# API level
|
||||
set(NVIM_API_LEVEL 13) # Bump this after any API/stdlib change.
|
||||
|
10
INSTALL.md
10
INSTALL.md
@@ -66,11 +66,13 @@ Several Neovim GUIs are available from scoop (extras): [scoop.sh/#/apps?q=neovim
|
||||
You can then copy your spell files over (for English, located
|
||||
[here](https://github.com/vim/vim/blob/master/runtime/spell/en.utf-8.spl) and
|
||||
[here](https://github.com/vim/vim/blob/master/runtime/spell/en.utf-8.sug));
|
||||
- For Python plugins you need the `pynvim` module. "Virtual envs" are recommended. After activating the virtual env do `pip install pynvim` (in *both*). Edit your `init.vim` so that it contains the path to the env's Python executable:
|
||||
```vim
|
||||
let g:python3_host_prog='C:/Users/foo/Envs/neovim3/Scripts/python.exe'
|
||||
- For Python plugins you need the `pynvim` module. Installation via uv
|
||||
(https://docs.astral.sh/uv/) is recommended; the `--upgrade` switch ensures
|
||||
installation of the latest version:
|
||||
```
|
||||
- Run `:checkhealth` and read `:help provider-python`.
|
||||
uv tool install --upgrade pynvim
|
||||
```
|
||||
- Run `:checkhealth` and read `:help provider-python` for more details.
|
||||
- **init.vim ("vimrc"):** If you already have Vim installed you can copy `%userprofile%\_vimrc` to `%userprofile%\AppData\Local\nvim\init.vim` to use your Vim config with Neovim.
|
||||
|
||||
|
||||
|
@@ -73,7 +73,7 @@ if(HAS_OG_FLAG)
|
||||
set(DEFAULT_MAKE_CFLAGS CFLAGS+=-Og ${DEFAULT_MAKE_CFLAGS})
|
||||
endif()
|
||||
|
||||
set(DEPS_INCLUDE_FLAGS "-I${DEPS_INSTALL_DIR}/include -I${DEPS_INSTALL_DIR}/include/luajit-2.1")
|
||||
set(DEPS_INCLUDE_FLAGS "-I\"${DEPS_INSTALL_DIR}/include\" -I\"${DEPS_INSTALL_DIR}/include/luajit-2.1\"")
|
||||
|
||||
# If the macOS deployment target is not set manually (via $MACOSX_DEPLOYMENT_TARGET),
|
||||
# fall back to local system version. Needs to be done here and in top-level CMakeLists.txt.
|
||||
@@ -96,10 +96,10 @@ else()
|
||||
find_package(Lua 5.1 EXACT)
|
||||
if(LUAJIT_FOUND)
|
||||
set(LUA_ENGINE LuaJit)
|
||||
string(APPEND DEPS_INCLUDE_FLAGS " -I${LUAJIT_INCLUDE_DIR}")
|
||||
string(APPEND DEPS_INCLUDE_FLAGS " -I\"${LUAJIT_INCLUDE_DIR}\"")
|
||||
elseif(LUA_FOUND)
|
||||
set(LUA_ENGINE Lua)
|
||||
string(APPEND DEPS_INCLUDE_FLAGS " -I${LUA_INCLUDE_DIR}")
|
||||
string(APPEND DEPS_INCLUDE_FLAGS " -I\"${LUA_INCLUDE_DIR}\"")
|
||||
else()
|
||||
message(FATAL_ERROR "Could not find system lua or luajit")
|
||||
endif()
|
||||
|
@@ -1,4 +1,4 @@
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|ARM64|aarch64)$")
|
||||
set(CMAKE_SYSTEM_PROCESSOR arm64)
|
||||
endif()
|
||||
|
||||
@@ -27,9 +27,13 @@ set(CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR}/README.md)
|
||||
|
||||
|
||||
if(WIN32)
|
||||
set(CPACK_PACKAGE_FILE_NAME "nvim-win64")
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
|
||||
set(CPACK_PACKAGE_FILE_NAME "nvim-win-arm64")
|
||||
else()
|
||||
set(CPACK_PACKAGE_FILE_NAME "nvim-win64")
|
||||
endif()
|
||||
|
||||
set(CPACK_GENERATOR ZIP WIX)
|
||||
|
||||
# WIX
|
||||
# CPACK_WIX_UPGRADE_GUID should be set, but should never change.
|
||||
# CPACK_WIX_PRODUCT_GUID should not be set (leave as default to auto-generate).
|
||||
|
@@ -77,46 +77,6 @@ function! tutor#TutorFolds()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Marks: {{{1
|
||||
|
||||
function! tutor#ApplyMarks()
|
||||
hi! link tutorExpect Special
|
||||
if exists('b:tutor_metadata') && has_key(b:tutor_metadata, 'expect')
|
||||
let b:tutor_sign_id = 1
|
||||
for expct in keys(b:tutor_metadata['expect'])
|
||||
let lnum = eval(expct)
|
||||
call matchaddpos('tutorExpect', [lnum])
|
||||
call tutor#CheckLine(lnum)
|
||||
endfor
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! tutor#ApplyMarksOnChanged()
|
||||
if exists('b:tutor_metadata') && has_key(b:tutor_metadata, 'expect')
|
||||
let lnum = line('.')
|
||||
if index(keys(b:tutor_metadata['expect']), string(lnum)) > -1
|
||||
call tutor#CheckLine(lnum)
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! tutor#CheckLine(line)
|
||||
if exists('b:tutor_metadata') && has_key(b:tutor_metadata, 'expect')
|
||||
let bufn = bufnr('%')
|
||||
let ctext = getline(a:line)
|
||||
let signs = sign_getplaced(bufn, {'lnum': a:line})[0].signs
|
||||
if !empty(signs)
|
||||
call sign_unplace('', {'id': signs[0].id})
|
||||
endif
|
||||
if b:tutor_metadata['expect'][string(a:line)] == -1 || ctext ==# b:tutor_metadata['expect'][string(a:line)]
|
||||
exe "sign place ".b:tutor_sign_id." line=".a:line." name=tutorok buffer=".bufn
|
||||
else
|
||||
exe "sign place ".b:tutor_sign_id." line=".a:line." name=tutorbad buffer=".bufn
|
||||
endif
|
||||
let b:tutor_sign_id+=1
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Tutor Cmd: {{{1
|
||||
|
||||
function! s:Locale()
|
||||
@@ -243,9 +203,9 @@ function! tutor#EnableInteractive(enable)
|
||||
setlocal buftype=nofile
|
||||
setlocal concealcursor+=inv
|
||||
setlocal conceallevel=2
|
||||
call tutor#ApplyMarks()
|
||||
lua require('nvim.tutor').apply_marks()
|
||||
augroup tutor_interactive
|
||||
autocmd! TextChanged,TextChangedI <buffer> call tutor#ApplyMarksOnChanged()
|
||||
autocmd! TextChanged,TextChangedI <buffer> lua require('nvim.tutor').apply_marks_on_changed()
|
||||
augroup END
|
||||
else
|
||||
setlocal buftype<
|
||||
|
@@ -3536,7 +3536,8 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()*
|
||||
the duration of the call.
|
||||
• fixed: If true when anchor is NW or SW, the float window
|
||||
would be kept fixed even if the window would be truncated.
|
||||
• hide: If true the floating window will be hidden.
|
||||
• hide: If true the floating window will be hidden and the
|
||||
cursor will be invisible when focused on it.
|
||||
• vertical: Split vertically |:vertical|.
|
||||
• split: Split direction: "left", "right", "above", "below".
|
||||
|
||||
|
@@ -187,20 +187,19 @@ Run |:checkhealth| in Nvim for automatic diagnosis.
|
||||
|
||||
Other hints:
|
||||
|
||||
- The python `neovim` module was renamed to `pynvim` (long ago).
|
||||
- If you're using pyenv or virtualenv for the `pynvim` module
|
||||
https://pypi.org/project/pynvim/, you must set `g:python3_host_prog` to
|
||||
the virtualenv's interpreter path.
|
||||
- Read |provider-python|.
|
||||
- Read |provider-python| to learn how to install `pynvim`.
|
||||
- Be sure you have the latest version of the `pynvim` Python module: >bash
|
||||
|
||||
python -m pip install setuptools
|
||||
python -m pip install --upgrade pynvim
|
||||
python3 -m pip install --upgrade pynvim
|
||||
uv tool install --upgrade pynvim
|
||||
<
|
||||
See |provider-python| for other installation options.
|
||||
- If you're manually creating a Python virtual environment for the `pynvim` module
|
||||
https://pypi.org/project/pynvim/, you must set `g:python3_host_prog` to
|
||||
the virtualenv's interpreter path.
|
||||
- Try with `nvim -u NORC` to make sure your config (|init.vim|) isn't causing a
|
||||
problem. If you get `E117: Unknown function`, that means there's a runtime
|
||||
issue: |faq-runtime|.
|
||||
- The python `neovim` module was renamed to `pynvim` (long ago).
|
||||
|
||||
|
||||
:CHECKHEALTH REPORTS E5009: INVALID $VIMRUNTIME ~
|
||||
|
@@ -373,11 +373,17 @@ To quote a block of ex-commands verbatim, place a greater than (>) character
|
||||
at the end of the line before the block and a less than (<) character as the
|
||||
first non-blank on a line following the block. Any line starting in column 1
|
||||
also implicitly stops the block of ex-commands before it. E.g. >
|
||||
function Example_Func()
|
||||
echo "Example"
|
||||
endfunction
|
||||
function Example_Func()
|
||||
echo "Example"
|
||||
endfunction
|
||||
<
|
||||
|
||||
To enable syntax highlighting for a block of code, place a language name
|
||||
annotation (e.g. "vim") after a greater than (>) character. E.g. >vim
|
||||
function Example_Func()
|
||||
echo "Example"
|
||||
endfunction
|
||||
<
|
||||
*help-notation*
|
||||
The following are highlighted differently in a Vim help file:
|
||||
- a special key name expressed either in <> notation as in <PageDown>, or
|
||||
as a Ctrl character as in CTRL-X
|
||||
|
@@ -135,7 +135,7 @@ following (in increasing priority):
|
||||
|
||||
1. Configuration defined for the `'*'` name.
|
||||
2. Configuration from the result of merging all tables returned by
|
||||
`lsp/<name>.lua` files in 'runtimepath' for a server of name `name`.
|
||||
`lsp/<config>.lua` files in 'runtimepath' for the config named `<config>`.
|
||||
3. Configurations defined anywhere else.
|
||||
|
||||
Note: The merge semantics of configurations follow the behaviour of
|
||||
@@ -256,6 +256,12 @@ FAQ *lsp-faq*
|
||||
" (async = false is the default for format)
|
||||
autocmd BufWritePre *.rs lua vim.lsp.buf.format({ async = false })
|
||||
<
|
||||
|
||||
- Q: How to avoid my own lsp/ folder being overridden?
|
||||
- A: Place your configs under "after/lsp/". Files in "after/lsp/" are loaded
|
||||
after those in "nvim/lsp/", so your settings will take precedence over
|
||||
the defaults provided by nvim-lspconfig. See also: |after-directory|
|
||||
|
||||
*lsp-vs-treesitter*
|
||||
- Q: How do LSP, Treesitter and Ctags compare?
|
||||
- A: LSP requires a client and language server. The language server uses
|
||||
|
@@ -356,6 +356,11 @@ PLUGINS
|
||||
• Customize :checkhealth by handling a `FileType checkhealth` event.
|
||||
|health-usage|
|
||||
|
||||
• Simplify Python provider setup to a single step: `uv tool install pynvim`
|
||||
Nvim will detect the plugin's location without user configuration, even if
|
||||
unrelated Python virtual environments are activated.
|
||||
|provider-python|
|
||||
|
||||
STARTUP
|
||||
|
||||
• |-es| ("script mode") disables shada by default.
|
||||
|
@@ -36,21 +36,18 @@ itself).
|
||||
|
||||
For Python 3 plugins:
|
||||
1. Make sure Python 3.9+ is available in your $PATH.
|
||||
2. Install the module (try "python" if "python3" is missing): >bash
|
||||
python3 -m pip install --user --upgrade pynvim
|
||||
2. Install either uv (https://docs.astral.sh/uv/) or pipx
|
||||
(https://pipx.pypa.io/stable/).
|
||||
3. Install the module: >bash
|
||||
uv tool install --upgrade pynvim
|
||||
# or:
|
||||
pipx install --upgrade pynvim
|
||||
|
||||
The pip `--upgrade` flag ensures that you get the latest version even if
|
||||
The `--upgrade` flag ensures that you get the latest version even if
|
||||
a previous version was already installed.
|
||||
|
||||
See also |python-virtualenv|.
|
||||
|
||||
Note: The old "neovim" module was renamed to "pynvim".
|
||||
https://github.com/neovim/neovim/wiki/Following-HEAD#20181118
|
||||
If you run into problems, uninstall _both_ then install "pynvim" again: >bash
|
||||
python -m pip uninstall neovim pynvim
|
||||
python -m pip install --user --upgrade pynvim
|
||||
|
||||
|
||||
PYTHON PROVIDER CONFIGURATION ~
|
||||
*g:python3_host_prog*
|
||||
Command to start Python 3 (executable, not directory). Setting this makes
|
||||
@@ -65,20 +62,18 @@ To disable Python 3 support: >vim
|
||||
|
||||
PYTHON VIRTUALENVS ~
|
||||
*python-virtualenv*
|
||||
If you plan to use per-project virtualenvs often, you should assign one
|
||||
virtualenv for Nvim and hard-code the interpreter path via
|
||||
|g:python3_host_prog| so that the "pynvim" package is not required
|
||||
for each virtualenv.
|
||||
|
||||
Example using pyenv: >bash
|
||||
pyenv install 3.4.4
|
||||
pyenv virtualenv 3.4.4 py3nvim
|
||||
pyenv activate py3nvim
|
||||
python3 -m pip install pynvim
|
||||
pyenv which python # Note the path
|
||||
The last command reports the interpreter path, add it to your init.vim: >vim
|
||||
let g:python3_host_prog = '/path/to/py3nvim/bin/python'
|
||||
Using pynvim 0.6.0+ installed via uv or pipx, Nvim will automatically detect
|
||||
pynvim even if other Python virtual environments are activated (technical
|
||||
note: via the "pynvim-python" global python tool). For older pynvim (or older
|
||||
Neovim), where detection involved finding the first Python interpreter and
|
||||
checking if it could import pynvim, automatic detection would fail when
|
||||
another virtual environment is active. Upgrading to the latest pynvim is the
|
||||
recommended solution to this; but if that's not an option, then you can set
|
||||
the variable |g:python3_host_prog| in `init.vim` to point to the full path to
|
||||
the Python interpreter where `pynvim` is installed, e.g.: >vim
|
||||
|
||||
let g:python3_host_prog = '/path/to/pynvim-venv/bin/python'
|
||||
<
|
||||
See also: https://github.com/zchee/deoplete-jedi/wiki/Setting-up-Python-for-Neovim
|
||||
|
||||
==============================================================================
|
||||
|
@@ -25,9 +25,9 @@ Table of contents: |usr_toc.txt|
|
||||
==============================================================================
|
||||
*02.1* Running Vim for the First Time
|
||||
|
||||
To start Vim, enter this command: >
|
||||
To start Nvim, enter this command: >
|
||||
|
||||
gvim file.txt
|
||||
nvim file.txt
|
||||
|
||||
On Unix you can type this at any command prompt. If you are running Microsoft
|
||||
Windows, open a Command Prompt and enter the command. In either case, Vim
|
||||
@@ -50,20 +50,6 @@ screen, a message line indicates the file is named file.txt and shows that you
|
||||
are creating a new file. The message information is temporary and other
|
||||
information overwrites it.
|
||||
|
||||
|
||||
THE VIM COMMAND
|
||||
|
||||
The gvim command causes the editor to create a new window for editing. If you
|
||||
use this command: >
|
||||
|
||||
vim file.txt
|
||||
|
||||
the editing occurs inside your command window. In other words, if you are
|
||||
running inside an xterm, the editor uses your xterm window. If you are using
|
||||
the command prompt under Microsoft Windows, the editing occurs inside this
|
||||
window. The text in the window will look the same for both versions, but with
|
||||
gvim you have extra features, like a menu bar. More about that later.
|
||||
|
||||
==============================================================================
|
||||
*02.2* Inserting text
|
||||
|
||||
@@ -580,7 +566,7 @@ Summary: *help-summary* >
|
||||
:help quote:
|
||||
|
||||
13) Vim Script is available at >
|
||||
:help eval.txt
|
||||
:help vimeval.txt
|
||||
< Certain aspects of the language are available at :h expr-X where "X" is a
|
||||
single letter. E.g. >
|
||||
:help expr-!
|
||||
@@ -660,10 +646,13 @@ Summary: *help-summary* >
|
||||
command switch of Vim use: >
|
||||
:help -f
|
||||
|
||||
24) Optional features always start with "+". To find out about the
|
||||
conceal feature use: >
|
||||
:help +conceal
|
||||
|
||||
24) Lua language and Nvim's Lua standard library are available at >vim
|
||||
:help lua.txt
|
||||
< Guide to using Lua in Nvim is available at >vim
|
||||
:help lua-guide.txt
|
||||
< Lua 5.1 reference manual is available at >vim
|
||||
:help luaref.txt
|
||||
<
|
||||
25) Documentation for included filetype specific functionality is usually
|
||||
available in the form ft-<filetype>-<functionality>. So >
|
||||
:help ft-c-syntax
|
||||
|
@@ -3,6 +3,7 @@
|
||||
" Maintainer: Romain Lafourcade <romainlafourcade@gmail.com>
|
||||
" Last Change: 2024 Apr 21
|
||||
" 2024 May 24 by Riley Bruins <ribru17@gmail.com> ('commentstring')
|
||||
" 2025 Aug 29 by Vim project, add try/catch around json_decode(), #18141
|
||||
|
||||
if exists("b:did_ftplugin")
|
||||
finish
|
||||
@@ -52,13 +53,19 @@ function! s:CollectPathsFromConfig() abort
|
||||
endif
|
||||
endif
|
||||
|
||||
let paths_from_config = config_json
|
||||
try
|
||||
let paths_from_config = config_json
|
||||
\ ->readfile()
|
||||
\ ->filter({ _, val -> val =~ '^\s*[\[\]{}"0-9]' })
|
||||
\ ->join()
|
||||
\ ->json_decode()
|
||||
\ ->get('compilerOptions', {})
|
||||
\ ->get('paths', {})
|
||||
catch /^Vim\%((\a\+)\)\=:E491:/ " invalid json
|
||||
let paths_from_config = {}
|
||||
catch /^Vim\%((\a\+)\)\=:E474:/ " invalid json in Nvim
|
||||
let paths_from_config = {}
|
||||
endtry
|
||||
|
||||
if !empty(paths_from_config)
|
||||
let b:astro_paths = paths_from_config
|
||||
|
@@ -1,7 +1,8 @@
|
||||
" Vim filetype plugin file
|
||||
" Language: gpg(1) configuration file
|
||||
" Maintainer: This runtime file is looking for a new maintainer.
|
||||
" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
|
||||
" Latest Revision: 2024-09-19 (simplify keywordprg #15696)
|
||||
" Latest Revision: 2025-07-22 (use :hor term #17822)
|
||||
|
||||
if exists("b:did_ftplugin")
|
||||
finish
|
||||
@@ -17,7 +18,7 @@ setlocal comments=:# commentstring=#\ %s formatoptions-=t formatoptions+=croql
|
||||
|
||||
if has('unix') && executable('less') && exists(':terminal') == 2
|
||||
command -buffer -nargs=1 GpgKeywordPrg
|
||||
\ silent exe ':term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s+--' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . 'gpg'
|
||||
\ silent exe ':hor term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s+--' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . 'gpg'
|
||||
setlocal iskeyword+=-
|
||||
setlocal keywordprg=:GpgKeywordPrg
|
||||
let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer GpgKeywordPrg'
|
||||
|
@@ -2,7 +2,7 @@
|
||||
" Language: modules.conf(5) configuration file
|
||||
" Maintainer: This runtime file is looking for a new maintainer.
|
||||
" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
|
||||
" Latest Revision: 2024-09-20 (remove erroneous endif)
|
||||
" Latest Revision: 2025-07-22 (use :hor term #17822)
|
||||
|
||||
if exists("b:did_ftplugin")
|
||||
finish
|
||||
@@ -19,7 +19,7 @@ setlocal formatoptions-=t formatoptions+=croql
|
||||
|
||||
if has('unix') && executable('less') && exists(':terminal') == 2
|
||||
command -buffer -nargs=1 ModconfKeywordPrg
|
||||
\ silent exe ':term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s{,8}' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . 'modprobe.d'
|
||||
\ silent exe ':hor term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s{,8}' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . 'modprobe.d'
|
||||
setlocal iskeyword+=-
|
||||
setlocal keywordprg=:ModconfKeywordPrg
|
||||
let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer ModconfKeywordPrg'
|
||||
|
@@ -1,7 +1,8 @@
|
||||
" Vim filetype plugin file
|
||||
" Language: mutt RC File
|
||||
" Maintainer: This runtime file is looking for a new maintainer.
|
||||
" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
|
||||
" Latest Revision: 2024-09-19 (simplify keywordprg #15696)
|
||||
" Latest Revision: 2025-07-22 (use :hor term #17822)
|
||||
|
||||
if exists("b:did_ftplugin")
|
||||
finish
|
||||
@@ -20,7 +21,7 @@ let &l:include = '^\s*source\>'
|
||||
|
||||
if has('unix') && executable('less') && exists(':terminal') == 2
|
||||
command -buffer -nargs=1 MuttrcKeywordPrg
|
||||
\ silent exe 'term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s+' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . 'muttrc'
|
||||
\ silent exe 'hor term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s+' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . 'muttrc'
|
||||
setlocal iskeyword+=-
|
||||
setlocal keywordprg=:MuttrcKeywordPrg
|
||||
let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer MuttrcKeywordPrg'
|
||||
|
@@ -5,6 +5,7 @@
|
||||
" 2024 Jan 14 by Vim Project (browsefilter)
|
||||
" 2024 May 23 by Riley Bruins <ribru17@gmail.com> ('commentstring')
|
||||
" 2024 Sep 19 by Konfekt (simplify keywordprg #15696)
|
||||
" 2025 Jul 22 by phanium (use :hor term #17822)
|
||||
|
||||
" Only do this when not done yet for this buffer
|
||||
if exists("b:did_ftplugin") | finish | endif
|
||||
@@ -51,7 +52,7 @@ endif
|
||||
|
||||
if exists('s:pwsh_cmd')
|
||||
if exists(':terminal') == 2
|
||||
command! -buffer -nargs=1 GetHelp silent exe 'term ' . s:pwsh_cmd . ' -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -Command Get-Help -Full "<args>"' . (executable('less') ? ' | less' : '')
|
||||
command! -buffer -nargs=1 GetHelp silent exe 'hor term ' . s:pwsh_cmd . ' -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -Command Get-Help -Full "<args>"' . (executable('less') ? ' | less' : '')
|
||||
else
|
||||
command! -buffer -nargs=1 GetHelp echo system(s:pwsh_cmd . ' -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -Command Get-Help -Full <args>')
|
||||
endif
|
||||
|
@@ -3,6 +3,7 @@
|
||||
" Maintainer: Doug Kearns <dougkearns@gmail.com>
|
||||
" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
|
||||
" Last Change: 2024 Sep 19 (simplify keywordprg #15696)
|
||||
" 2024 Jul 22 by Vim project (use :hor term #17822)
|
||||
|
||||
if exists("b:did_ftplugin")
|
||||
finish
|
||||
@@ -36,7 +37,7 @@ endif
|
||||
|
||||
if has('unix') && executable('less') && exists(':terminal') == 2
|
||||
command -buffer -nargs=1 ReadlineKeywordPrg
|
||||
\ silent exe 'term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s+' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . '3 readline'
|
||||
\ silent exe 'hor term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s+' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . '3 readline'
|
||||
setlocal iskeyword+=-
|
||||
setlocal keywordprg=:ReadlineKeywordPrg
|
||||
let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer ReadlineKeywordPrg'
|
||||
|
@@ -7,6 +7,7 @@
|
||||
" Last Change: 2024 Sep 19 by Vim Project (compiler shellcheck)
|
||||
" 2024 Dec 29 by Vim Project (improve setting shellcheck compiler)
|
||||
" 2025 Mar 09 by Vim Project (set b:match_skip)
|
||||
" 2025 Jul 22 by phanium (use :hor term #17822)
|
||||
|
||||
if exists("b:did_ftplugin")
|
||||
finish
|
||||
@@ -53,7 +54,7 @@ let s:is_kornshell = get(b:, "is_kornshell", get(g:, "is_kornshell", 0))
|
||||
|
||||
if s:is_bash
|
||||
if exists(':terminal') == 2
|
||||
command! -buffer -nargs=1 ShKeywordPrg silent exe ':term bash -c "help "<args>" 2>/dev/null || man "<args>""'
|
||||
command! -buffer -nargs=1 ShKeywordPrg silent exe ':hor term bash -c "help "<args>" 2>/dev/null || man "<args>""'
|
||||
else
|
||||
command! -buffer -nargs=1 ShKeywordPrg echo system('bash -c "help <args>" 2>/dev/null || MANPAGER= man "<args>"')
|
||||
endif
|
||||
|
@@ -2,7 +2,7 @@
|
||||
" Language: OpenSSH client configuration file
|
||||
" Maintainer: This runtime file is looking for a new maintainer.
|
||||
" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
|
||||
" Latest Revision: 2024-09-19 (simplify keywordprg #15696)
|
||||
" Latest Revision: 2025-07-22 (use :hor term #17822)
|
||||
|
||||
if exists("b:did_ftplugin")
|
||||
finish
|
||||
@@ -17,7 +17,7 @@ let b:undo_ftplugin = 'setlocal com< cms< fo<'
|
||||
|
||||
if has('unix') && executable('less') && exists(':terminal') == 2
|
||||
command -buffer -nargs=1 SshconfigKeywordPrg
|
||||
\ silent exe 'term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s+' . <q-args> . '$', '\') . ''' --hilite-search" man ' . 'ssh_config'
|
||||
\ silent exe 'hor term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s+' . <q-args> . '$', '\') . ''' --hilite-search" man ' . 'ssh_config'
|
||||
setlocal iskeyword+=-
|
||||
setlocal keywordprg=:SshconfigKeywordPrg
|
||||
let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer SshconfigKeywordPrg'
|
||||
|
@@ -2,7 +2,7 @@
|
||||
" Language: sudoers(5) configuration files
|
||||
" Maintainer: This runtime file is looking for a new maintainer.
|
||||
" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
|
||||
" Latest Revision: 2024-09-19 (simplify keywordprg #15696)
|
||||
" Latest Revision: 2025-07-22 (use :hor term #17822)
|
||||
|
||||
if exists("b:did_ftplugin")
|
||||
finish
|
||||
@@ -18,7 +18,7 @@ setlocal comments=:# commentstring=#\ %s formatoptions-=t formatoptions+=croql
|
||||
|
||||
if has('unix') && executable('less') && exists(':terminal') == 2
|
||||
command -buffer -nargs=1 SudoersKeywordPrg
|
||||
\ silent exe ':term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('\b' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . 'sudoers'
|
||||
\ silent exe ':hor term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('\b' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . 'sudoers'
|
||||
setlocal iskeyword+=-
|
||||
setlocal keywordprg=:SudoersKeywordPrg
|
||||
let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer SudoersKeywordPrg'
|
||||
|
@@ -2,7 +2,7 @@
|
||||
" Language: udev(8) rules file
|
||||
" Maintainer: This runtime file is looking for a new maintainer.
|
||||
" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
|
||||
" Latest Revision: 2024-09-19 (simplify keywordprg #15696)
|
||||
" Latest Revision: 2025-07-22 (use :hor term #17822)
|
||||
|
||||
if exists("b:did_ftplugin")
|
||||
finish
|
||||
@@ -18,7 +18,7 @@ setlocal comments=:# commentstring=#\ %s formatoptions-=t formatoptions+=croql
|
||||
|
||||
if has('unix') && executable('less') && exists(':terminal') == 2
|
||||
command -buffer -nargs=1 UdevrulesKeywordPrg
|
||||
\ silent exe ':term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s{,8}' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . 'udev'
|
||||
\ silent exe ':hor term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s{,8}' . <q-args> . '\b', '\') . ''' --hilite-search" man ' . 'udev'
|
||||
setlocal iskeyword+=-
|
||||
setlocal keywordprg=:UdevrulesKeywordPrg
|
||||
let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer UdevrulesKeywordPrg'
|
||||
|
@@ -5,6 +5,8 @@
|
||||
" Latest Revision: 2024 Sep 19
|
||||
" License: Vim (see :h license)
|
||||
" Repository: https://github.com/chrisbra/vim-zsh
|
||||
" Last Change:
|
||||
" 2025 Jul 23 by Vim Project (use :hor term #17822)
|
||||
|
||||
if exists("b:did_ftplugin")
|
||||
finish
|
||||
@@ -20,7 +22,7 @@ let b:undo_ftplugin = "setl com< cms< fo< "
|
||||
|
||||
if executable('zsh') && &shell !~# '/\%(nologin\|false\)$'
|
||||
if exists(':terminal') == 2
|
||||
command! -buffer -nargs=1 ZshKeywordPrg silent exe ':term zsh -c "autoload -Uz run-help; run-help <args>"'
|
||||
command! -buffer -nargs=1 ZshKeywordPrg silent exe ':hor term zsh -c "autoload -Uz run-help; run-help <args>"'
|
||||
else
|
||||
command! -buffer -nargs=1 ZshKeywordPrg echo system('MANPAGER= zsh -c "autoload -Uz run-help; run-help <args> 2>/dev/null"')
|
||||
endif
|
||||
|
86
runtime/lua/nvim/tutor.lua
Normal file
86
runtime/lua/nvim/tutor.lua
Normal file
@@ -0,0 +1,86 @@
|
||||
---@class nvim.TutorMetadata
|
||||
---@field expect table<string, string|-1>
|
||||
|
||||
---@alias nvim.TutorExtmarks table<string, string>
|
||||
|
||||
---@type nvim.TutorExtmarks?
|
||||
vim.b.tutor_extmarks = vim.b.tutor_extmarks
|
||||
|
||||
---@type nvim.TutorMetadata?
|
||||
vim.b.tutor_metadata = vim.b.tutor_metadata
|
||||
|
||||
local sign_text_correct = '✓'
|
||||
local sign_text_incorrect = '✗'
|
||||
local tutor_mark_ns = vim.api.nvim_create_namespace('nvim.tutor.mark')
|
||||
local tutor_hl_ns = vim.api.nvim_create_namespace('nvim.tutor.hl')
|
||||
|
||||
local M = {}
|
||||
|
||||
---@param line integer 1-based
|
||||
local function check_line(line)
|
||||
if vim.b.tutor_metadata and vim.b.tutor_metadata.expect and vim.b.tutor_extmarks then
|
||||
local ctext = vim.fn.getline(line)
|
||||
|
||||
---@type vim.api.keyset.get_extmark_item[]
|
||||
local extmarks = vim
|
||||
.iter(vim.api.nvim_buf_get_extmarks(
|
||||
0,
|
||||
tutor_mark_ns,
|
||||
{ line - 1, 0 },
|
||||
{ line - 1, -1 }, -- the extmark can move to col > 0 if users insert text there
|
||||
{ details = true }
|
||||
))
|
||||
:filter(function(extmark)
|
||||
return not extmark[4].invalid
|
||||
end)
|
||||
:totable()
|
||||
|
||||
for _, extmark in ipairs(extmarks) do
|
||||
local mark_id = extmark[1]
|
||||
local expct = vim.b.tutor_extmarks[tostring(mark_id)]
|
||||
local expect = vim.b.tutor_metadata.expect[expct]
|
||||
local is_correct = expect == -1 or ctext == expect
|
||||
|
||||
vim.api.nvim_buf_set_extmark(0, tutor_mark_ns, line - 1, 0, {
|
||||
id = mark_id,
|
||||
sign_text = is_correct and sign_text_correct or sign_text_incorrect,
|
||||
sign_hl_group = is_correct and 'tutorOK' or 'tutorX',
|
||||
invalidate = true,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M.apply_marks()
|
||||
vim.cmd [[hi! link tutorExpect Special]]
|
||||
if vim.b.tutor_metadata and vim.b.tutor_metadata.expect then
|
||||
vim.b.tutor_extmarks = {}
|
||||
for expct, _ in pairs(vim.b.tutor_metadata.expect) do
|
||||
---@diagnostic disable-next-line: assign-type-mismatch
|
||||
local lnum = tonumber(expct) ---@type integer
|
||||
vim.api.nvim_buf_set_extmark(0, tutor_hl_ns, lnum - 1, 0, {
|
||||
line_hl_group = 'tutorExpect',
|
||||
invalidate = true,
|
||||
})
|
||||
|
||||
local mark_id = vim.api.nvim_buf_set_extmark(0, tutor_mark_ns, lnum - 1, 0, {})
|
||||
|
||||
-- Cannot edit field of a Vimscript dictionary from Lua directly, see `:h lua-vim-variables`
|
||||
---@type nvim.TutorExtmarks
|
||||
local tutor_extmarks = vim.b.tutor_extmarks
|
||||
tutor_extmarks[tostring(mark_id)] = expct
|
||||
vim.b.tutor_extmarks = tutor_extmarks
|
||||
|
||||
check_line(lnum)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M.apply_marks_on_changed()
|
||||
if vim.b.tutor_metadata and vim.b.tutor_metadata.expect and vim.b.tutor_extmarks then
|
||||
local lnum = vim.fn.line('.')
|
||||
check_line(lnum)
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
@@ -200,7 +200,9 @@ function vim.show_pos(bufnr, row, col, filter)
|
||||
capture,
|
||||
string.format(
|
||||
'priority: %d language: %s',
|
||||
capture.metadata.priority or vim.hl.priorities.treesitter,
|
||||
capture.metadata.priority
|
||||
or (capture.metadata[capture.id] and capture.metadata[capture.id].priority)
|
||||
or vim.hl.priorities.treesitter,
|
||||
capture.lang
|
||||
)
|
||||
)
|
||||
|
3
runtime/lua/vim/_meta/api.lua
generated
3
runtime/lua/vim/_meta/api.lua
generated
@@ -1840,7 +1840,8 @@ function vim.api.nvim_open_term(buffer, opts) end
|
||||
--- the call.
|
||||
--- - fixed: If true when anchor is NW or SW, the float window
|
||||
--- would be kept fixed even if the window would be truncated.
|
||||
--- - hide: If true the floating window will be hidden.
|
||||
--- - hide: If true the floating window will be hidden and the cursor will be invisible when
|
||||
--- focused on it.
|
||||
--- - vertical: Split vertically `:vertical`.
|
||||
--- - split: Split direction: "left", "right", "above", "below".
|
||||
--- @return integer # |window-ID|, or 0 on error
|
||||
|
@@ -318,9 +318,11 @@ local function check_tmux()
|
||||
'$TERM differs from the tmux `default-terminal` setting. Colors might look wrong.',
|
||||
{ '$TERM may have been set by some rc (.bashrc, .zshrc, ...).' }
|
||||
)
|
||||
elseif not vim.regex([[\v(tmux-256color|screen-256color)]]):match_str(vim.env.TERM) then
|
||||
elseif
|
||||
not vim.regex([[\v(tmux-256color|tmux-direct|screen-256color)]]):match_str(vim.env.TERM)
|
||||
then
|
||||
health.error(
|
||||
'$TERM should be "screen-256color" or "tmux-256color" in tmux. Colors might look wrong.',
|
||||
'$TERM should be "screen-256color", "tmux-256color", or "tmux-direct" in tmux. Colors might look wrong.',
|
||||
{
|
||||
'Set default-terminal in ~/.tmux.conf:\nset-option -g default-terminal "screen-256color"',
|
||||
suggest_faq,
|
||||
|
@@ -957,6 +957,9 @@ end
|
||||
|
||||
---@private
|
||||
function ArrayIter:last()
|
||||
if self._head >= self._tail then
|
||||
return nil
|
||||
end
|
||||
local inc = self._head < self._tail and 1 or -1
|
||||
local v = self._table[self._tail - inc]
|
||||
self._head = self._tail
|
||||
|
@@ -352,6 +352,7 @@ function M.signature_help(config)
|
||||
|
||||
config = config and vim.deepcopy(config) or {}
|
||||
config.focus_id = method
|
||||
local user_title = config.title
|
||||
|
||||
lsp.buf_request_all(0, method, client_positional_params(), function(results, ctx)
|
||||
if api.nvim_get_current_buf() ~= ctx.bufnr then
|
||||
@@ -386,17 +387,19 @@ function M.signature_help(config)
|
||||
return
|
||||
end
|
||||
|
||||
local sfx = total > 1
|
||||
and string.format(' (%d/%d)%s', idx, total, can_cycle and ' (<C-s> to cycle)' or '')
|
||||
or ''
|
||||
local title = string.format('Signature Help: %s%s', client.name, sfx)
|
||||
if config.border then
|
||||
config.title = title
|
||||
else
|
||||
table.insert(lines, 1, '# ' .. title)
|
||||
if hl then
|
||||
hl[1] = hl[1] + 1
|
||||
hl[3] = hl[3] + 1
|
||||
-- Show title only if there are multiple clients or multiple signatures.
|
||||
if total > 1 then
|
||||
local sfx = total > 1
|
||||
and string.format(' (%d/%d)%s', idx, total, can_cycle and ' (<C-s> to cycle)' or '')
|
||||
or ''
|
||||
config.title = user_title or string.format('Signature Help: %s%s', client.name, sfx)
|
||||
-- If no border is set, render title inside the window.
|
||||
if not (config.border or vim.o.winborder ~= '') then
|
||||
table.insert(lines, 1, '# ' .. config.title)
|
||||
if hl then
|
||||
hl[1] = hl[1] + 1
|
||||
hl[3] = hl[3] + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@@ -198,7 +198,7 @@ local function check_enabled_configs()
|
||||
local v_str --- @type string?
|
||||
if k == 'name' then
|
||||
v_str = nil
|
||||
elseif k == 'filetypes' or k == 'root_markers' then
|
||||
elseif k == 'filetypes' then
|
||||
v_str = table.concat(v, ', ')
|
||||
elseif type(v) == 'function' then
|
||||
v_str = func_tostring(v)
|
||||
|
@@ -745,6 +745,10 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers
|
||||
if type(doc) == 'string' then
|
||||
signature.documentation = { kind = 'plaintext', value = doc }
|
||||
end
|
||||
-- Add delimiter if there is documentation to display
|
||||
if signature.documentation.value ~= '' then
|
||||
contents[#contents + 1] = '---'
|
||||
end
|
||||
M.convert_input_to_markdown_lines(signature.documentation, contents)
|
||||
end
|
||||
if signature.parameters and #signature.parameters > 0 then
|
||||
@@ -861,7 +865,7 @@ function M.make_floating_popup_options(width, height, opts)
|
||||
col = 1
|
||||
end
|
||||
|
||||
local title = (opts.border and opts.title) and opts.title or nil
|
||||
local title = ((opts.border or vim.o.winborder ~= '') and opts.title) and opts.title or nil
|
||||
local title_pos --- @type 'left'|'center'|'right'?
|
||||
|
||||
if title then
|
||||
@@ -1340,9 +1344,10 @@ end
|
||||
---
|
||||
---@param events table list of events
|
||||
---@param winnr integer window id of preview window
|
||||
---@param bufnrs table list of buffers where the preview window will remain visible
|
||||
---@param floating_bufnr integer floating preview buffer
|
||||
---@param bufnr integer buffer that opened the floating preview buffer
|
||||
---@see autocmd-events
|
||||
local function close_preview_autocmd(events, winnr, bufnrs)
|
||||
local function close_preview_autocmd(events, winnr, floating_bufnr, bufnr)
|
||||
local augroup = api.nvim_create_augroup('nvim.preview_window_' .. winnr, {
|
||||
clear = true,
|
||||
})
|
||||
@@ -1351,13 +1356,13 @@ local function close_preview_autocmd(events, winnr, bufnrs)
|
||||
-- the floating window buffer or the buffer that spawned it
|
||||
api.nvim_create_autocmd('BufLeave', {
|
||||
group = augroup,
|
||||
buffer = bufnrs[1],
|
||||
buffer = bufnr,
|
||||
callback = function()
|
||||
vim.schedule(function()
|
||||
-- When jumping to the quickfix window from the preview window,
|
||||
-- do not close the preview window.
|
||||
if api.nvim_get_option_value('filetype', { buf = 0 }) ~= 'qf' then
|
||||
close_preview_window(winnr, bufnrs)
|
||||
close_preview_window(winnr, { floating_bufnr, bufnr })
|
||||
end
|
||||
end)
|
||||
end,
|
||||
@@ -1366,7 +1371,7 @@ local function close_preview_autocmd(events, winnr, bufnrs)
|
||||
if #events > 0 then
|
||||
api.nvim_create_autocmd(events, {
|
||||
group = augroup,
|
||||
buffer = bufnrs[2],
|
||||
buffer = bufnr,
|
||||
callback = function()
|
||||
close_preview_window(winnr)
|
||||
end,
|
||||
@@ -1613,7 +1618,7 @@ function M.open_floating_preview(contents, syntax, opts)
|
||||
'<cmd>bdelete<cr>',
|
||||
{ silent = true, noremap = true, nowait = true }
|
||||
)
|
||||
close_preview_autocmd(opts.close_events, floating_winnr, { floating_bufnr, bufnr })
|
||||
close_preview_autocmd(opts.close_events, floating_winnr, floating_bufnr, bufnr)
|
||||
|
||||
-- save focus_id
|
||||
if opts.focus_id then
|
||||
|
@@ -724,10 +724,9 @@ local function python()
|
||||
local message = 'Detected pip upgrade failure: Python executable can import "pynvim" but not "neovim": '
|
||||
.. pynvim_exe
|
||||
local advice = {
|
||||
'Use that Python version to reinstall "pynvim" and optionally "neovim".',
|
||||
'Use that Python version to uninstall any "pynvim" or "neovim", e.g.:',
|
||||
pynvim_exe .. ' -m pip uninstall pynvim neovim',
|
||||
pynvim_exe .. ' -m pip install pynvim',
|
||||
pynvim_exe .. ' -m pip install neovim # only if needed by third-party software',
|
||||
'Then see :help provider-python for "pynvim" installation steps.',
|
||||
}
|
||||
health.error(message, advice)
|
||||
end
|
||||
@@ -753,7 +752,7 @@ local function python()
|
||||
if is_bad_response(current) then
|
||||
health.error(
|
||||
'pynvim is not installed.\nError: ' .. current,
|
||||
'Run in shell: ' .. python_exe .. ' -m pip install pynvim'
|
||||
'See :help provider-python for "pynvim" installation steps.'
|
||||
)
|
||||
end
|
||||
|
||||
|
@@ -83,6 +83,10 @@ function M.detect_by_module(module)
|
||||
return vim.fn.exepath(vim.fn.expand(python_exe, true)), nil
|
||||
end
|
||||
|
||||
if vim.fn.executable('pynvim-python') == 1 then
|
||||
return 'pynvim-python'
|
||||
end
|
||||
|
||||
local errors = {}
|
||||
for _, exe in ipairs(python_candidates) do
|
||||
local error = check_for_module(exe, module)
|
||||
|
@@ -119,7 +119,7 @@ local Tabstop = {}
|
||||
function Tabstop.new(index, bufnr, range, choices)
|
||||
local extmark_id = vim.api.nvim_buf_set_extmark(bufnr, snippet_ns, range[1], range[2], {
|
||||
right_gravity = true,
|
||||
end_right_gravity = true,
|
||||
end_right_gravity = false,
|
||||
end_line = range[3],
|
||||
end_col = range[4],
|
||||
hl_group = hl_group,
|
||||
@@ -170,7 +170,7 @@ function Tabstop:set_right_gravity(right_gravity)
|
||||
local range = self:get_range()
|
||||
self.extmark_id = vim.api.nvim_buf_set_extmark(self.bufnr, snippet_ns, range[1], range[2], {
|
||||
right_gravity = right_gravity,
|
||||
end_right_gravity = true,
|
||||
end_right_gravity = not right_gravity,
|
||||
end_line = range[3],
|
||||
end_col = range[4],
|
||||
hl_group = hl_group,
|
||||
@@ -257,10 +257,21 @@ local M = { session = nil }
|
||||
local function display_choices(tabstop)
|
||||
assert(tabstop.choices, 'Tabstop has no choices')
|
||||
|
||||
local text = tabstop:get_text()
|
||||
local found_text = false
|
||||
|
||||
local start_col = tabstop:get_range()[2] + 1
|
||||
local matches = {} --- @type table[]
|
||||
for _, choice in ipairs(tabstop.choices) do
|
||||
matches[#matches + 1] = { word = choice }
|
||||
if choice ~= text then
|
||||
matches[#matches + 1] = { word = choice }
|
||||
else
|
||||
found_text = true
|
||||
end
|
||||
end
|
||||
|
||||
if found_text then
|
||||
table.insert(matches, 1, text)
|
||||
end
|
||||
|
||||
vim.defer_fn(function()
|
||||
@@ -298,6 +309,7 @@ local function select_tabstop(tabstop)
|
||||
vim.cmd.startinsert({ bang = range[4] >= #vim.api.nvim_get_current_line() })
|
||||
end
|
||||
if tabstop.choices then
|
||||
vim.fn.cursor(range[3] + 1, range[4] + 1)
|
||||
display_choices(tabstop)
|
||||
end
|
||||
else
|
||||
|
@@ -23,23 +23,24 @@ function M.check()
|
||||
---@class ParserEntry
|
||||
---@field name string
|
||||
---@field path string
|
||||
---@field index integer runtime path index (unique)
|
||||
|
||||
local sorted_parsers = {} ---@type ParserEntry[]
|
||||
|
||||
for _, parser in ipairs(parsers) do
|
||||
for i, parser in ipairs(parsers) do
|
||||
local parsername = vim.fn.fnamemodify(parser, ':t:r')
|
||||
table.insert(sorted_parsers, { name = parsername, path = parser })
|
||||
table.insert(sorted_parsers, { name = parsername, path = parser, index = i })
|
||||
end
|
||||
|
||||
table.sort(sorted_parsers, function(a, b)
|
||||
if a.name == b.name then
|
||||
return a.path < b.path
|
||||
return a.index < b.index -- if names are the same sort by rtpath index (unique)
|
||||
else
|
||||
return a.name < b.name
|
||||
end
|
||||
end)
|
||||
|
||||
for _, parser in ipairs(sorted_parsers) do
|
||||
for i, parser in ipairs(sorted_parsers) do
|
||||
local is_loadable, err_or_nil = pcall(ts.language.add, parser.name)
|
||||
|
||||
if not is_loadable then
|
||||
@@ -51,10 +52,15 @@ function M.check()
|
||||
err_or_nil or '?'
|
||||
)
|
||||
)
|
||||
elseif i > 1 and sorted_parsers[i - 1].name == parser.name then
|
||||
-- Sorted by runtime path order (load order), thus, if the previous parser has the same name,
|
||||
-- the current parser will not be loaded and `ts.language.inspect(parser.name)` with have
|
||||
-- incorrect information.
|
||||
health.ok(string.format('Parser: %-20s (not loaded), path: %s', parser.name, parser.path))
|
||||
else
|
||||
local lang = ts.language.inspect(parser.name)
|
||||
health.ok(
|
||||
string.format('Parser: %-20s ABI: %d, path: %s', parser.name, lang.abi_version, parser.path)
|
||||
string.format('Parser: %-25s ABI: %d, path: %s', parser.name, lang.abi_version, parser.path)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@@ -52,11 +52,14 @@ function TSHighlighterQuery:query()
|
||||
return self._query
|
||||
end
|
||||
|
||||
---@alias MarkInfo { start_line: integer, start_col: integer, opts: vim.api.keyset.set_extmark }
|
||||
|
||||
---@class (private) vim.treesitter.highlighter.State
|
||||
---@field tstree TSTree
|
||||
---@field next_row integer
|
||||
---@field iter vim.treesitter.highlighter.Iter?
|
||||
---@field highlighter_query vim.treesitter.highlighter.Query
|
||||
---@field prev_marks MarkInfo[]
|
||||
|
||||
---@nodoc
|
||||
---@class vim.treesitter.highlighter
|
||||
@@ -179,10 +182,13 @@ function TSHighlighter:destroy()
|
||||
vim.b[self.bufnr].ts_highlight = nil
|
||||
api.nvim_buf_clear_namespace(self.bufnr, ns, 0, -1)
|
||||
if vim.g.syntax_on == 1 then
|
||||
api.nvim_exec_autocmds(
|
||||
'FileType',
|
||||
{ group = 'syntaxset', buffer = self.bufnr, modeline = false }
|
||||
)
|
||||
-- FileType autocmds commonly assume curbuf is the target buffer, so nvim_buf_call.
|
||||
api.nvim_buf_call(self.bufnr, function()
|
||||
api.nvim_exec_autocmds(
|
||||
'FileType',
|
||||
{ group = 'syntaxset', buffer = self.bufnr, modeline = false }
|
||||
)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -220,6 +226,7 @@ function TSHighlighter:prepare_highlight_states(win, srow, erow)
|
||||
next_row = 0,
|
||||
iter = nil,
|
||||
highlighter_query = hl_query,
|
||||
prev_marks = {},
|
||||
})
|
||||
end)
|
||||
end
|
||||
@@ -311,6 +318,35 @@ local function get_spell(capture_name)
|
||||
return nil, 0
|
||||
end
|
||||
|
||||
---Adds the mark to the buffer, clipped by the line.
|
||||
---Queues the remainder if the mark continues after the line.
|
||||
---@param m MarkInfo
|
||||
---@param buf integer
|
||||
---@param line integer
|
||||
---@param next_marks MarkInfo[]
|
||||
local function add_mark(m, buf, line, next_marks)
|
||||
local cur_start_l = m.start_line
|
||||
local cur_start_c = m.start_col
|
||||
if cur_start_l < line then
|
||||
cur_start_l = line
|
||||
cur_start_c = 0
|
||||
end
|
||||
|
||||
local cur_opts = m.opts
|
||||
if cur_opts.end_line >= line + 1 then
|
||||
cur_opts = vim.deepcopy(cur_opts, true)
|
||||
cur_opts.end_line = line + 1
|
||||
cur_opts.end_col = 0
|
||||
table.insert(next_marks, m)
|
||||
end
|
||||
|
||||
local empty = cur_opts.end_line < cur_start_l
|
||||
or (cur_opts.end_line == cur_start_l and cur_opts.end_col <= cur_start_c)
|
||||
if cur_start_l <= line and not empty then
|
||||
api.nvim_buf_set_extmark(buf, ns, cur_start_l, cur_start_c, cur_opts)
|
||||
end
|
||||
end
|
||||
|
||||
---@param self vim.treesitter.highlighter
|
||||
---@param win integer
|
||||
---@param buf integer
|
||||
@@ -328,6 +364,12 @@ local function on_line_impl(self, win, buf, line, on_spell, on_conceal)
|
||||
return
|
||||
end
|
||||
|
||||
local next_marks = {}
|
||||
|
||||
for _, mark in ipairs(state.prev_marks) do
|
||||
add_mark(mark, buf, line, next_marks)
|
||||
end
|
||||
|
||||
if state.iter == nil or state.next_row < line then
|
||||
-- Mainly used to skip over folds
|
||||
|
||||
@@ -367,7 +409,7 @@ local function on_line_impl(self, win, buf, line, on_spell, on_conceal)
|
||||
local url = get_url(match, buf, capture, metadata)
|
||||
|
||||
if hl and end_row >= line and not on_conceal and (not on_spell or spell ~= nil) then
|
||||
api.nvim_buf_set_extmark(buf, ns, start_row, start_col, {
|
||||
local opts = {
|
||||
end_line = end_row,
|
||||
end_col = end_col,
|
||||
hl_group = hl,
|
||||
@@ -376,7 +418,9 @@ local function on_line_impl(self, win, buf, line, on_spell, on_conceal)
|
||||
conceal = conceal,
|
||||
spell = spell,
|
||||
url = url,
|
||||
})
|
||||
}
|
||||
local mark = { start_line = start_row, start_col = start_col, opts = opts }
|
||||
add_mark(mark, buf, line, next_marks)
|
||||
end
|
||||
|
||||
if
|
||||
@@ -395,6 +439,8 @@ local function on_line_impl(self, win, buf, line, on_spell, on_conceal)
|
||||
state.next_row = start_row
|
||||
end
|
||||
end
|
||||
|
||||
state.prev_marks = next_marks
|
||||
end)
|
||||
end
|
||||
|
||||
|
@@ -26,6 +26,7 @@
|
||||
</screenshots>
|
||||
|
||||
<releases>
|
||||
<release date="2025-08-31" version="0.11.4"/>
|
||||
<release date="2025-07-12" version="0.11.3"/>
|
||||
<release date="2025-05-30" version="0.11.2"/>
|
||||
<release date="2025-04-26" version="0.11.1"/>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
" Vim syntax file
|
||||
" Language: Vim help file
|
||||
" Maintainer: The Vim Project <https://github.com/vim/vim>
|
||||
" Last Change: 2024 Oct 16
|
||||
" Last Change: 2024 Dec 15
|
||||
" Former Maintainer: Bram Moolenaar <Bram@vim.org>
|
||||
|
||||
" Quit when a (custom) syntax file was already loaded
|
||||
@@ -12,15 +12,36 @@ endif
|
||||
let s:cpo_save = &cpo
|
||||
set cpo&vim
|
||||
|
||||
if !exists('g:help_example_languages')
|
||||
let g:help_example_languages = #{ vim: 'vim' }
|
||||
endif
|
||||
|
||||
syn match helpHeadline "^[A-Z.][-A-Z0-9 .,()_']*?\=\ze\(\s\+\*\|$\)"
|
||||
syn match helpSectionDelim "^===.*===$"
|
||||
syn match helpSectionDelim "^---.*--$"
|
||||
" Nvim: support language annotation in codeblocks
|
||||
|
||||
if has("conceal")
|
||||
syn region helpExample matchgroup=helpIgnore start=" >[a-z0-9]*$" start="^>[a-z0-9]*$" end="^[^ \t]"me=e-1 end="^<" concealends
|
||||
syn region helpExample matchgroup=helpIgnore
|
||||
\ start="\%(^\| \)>[a-z0-9]*$" end="^[^ \t]"me=e-1 end="^<" concealends
|
||||
else
|
||||
syn region helpExample matchgroup=helpIgnore start=" >[a-z0-9]*$" start="^>[a-z0-9]*$" end="^[^ \t]"me=e-1 end="^<"
|
||||
syn region helpExample matchgroup=helpIgnore
|
||||
\ start="\%(^\| \)>[a-z0-9]*$" end="^[^ \t]"me=e-1 end="^<"
|
||||
endif
|
||||
|
||||
for [s:lang, s:syntax] in g:help_example_languages->items()
|
||||
unlet! b:current_syntax
|
||||
" silent! to prevent E403
|
||||
execute 'silent! syn include' $'@helpExampleHighlight_{s:lang}'
|
||||
\ $'syntax/{s:syntax}.vim'
|
||||
|
||||
execute $'syn region helpExampleHighlight_{s:lang} matchgroup=helpIgnore'
|
||||
\ $'start=/\%(^\| \)>{s:lang}$/'
|
||||
\ 'end=/^[^ \t]/me=e-1 end=/^</'
|
||||
\ (has("conceal") ? 'concealends' : '')
|
||||
\ $'contains=@helpExampleHighlight_{s:lang} keepend'
|
||||
endfor
|
||||
unlet! s:lang s:syntax
|
||||
|
||||
syn match helpHyperTextJump "\\\@<!|[#-)!+-~]\+|" contains=helpBar
|
||||
syn match helpHyperTextEntry "\*[#-)!+-~]\+\*\s"he=e-1 contains=helpStar
|
||||
syn match helpHyperTextEntry "\*[#-)!+-~]\+\*$" contains=helpStar
|
||||
|
3
runtime/syntax/tutor.lua
Normal file
3
runtime/syntax/tutor.lua
Normal file
@@ -0,0 +1,3 @@
|
||||
vim.cmd [[
|
||||
syntax match tutorExpect /^--->.*$/
|
||||
]]
|
@@ -304,7 +304,7 @@ it would be easier to simply type two d's to delete a line.
|
||||
|
||||
3. Now move to the fourth line.
|
||||
|
||||
4. Type `2dd`{normal} to delete two lines, then press `u`{normal} twice to undo all three lines.
|
||||
4. Type `2dd`{normal} to delete two lines.
|
||||
|
||||
1) Roses are red,
|
||||
2) Mud is fun,
|
||||
@@ -368,7 +368,7 @@ Fiix the errors oon thhis line and reeplace them witth undo.
|
||||
|
||||
** Type `p`{normal} to put previously deleted text after the cursor. **
|
||||
|
||||
1. Move the cursor to the first ✓ line below.
|
||||
1. Move the cursor to the first `--->` line below.
|
||||
|
||||
2. Type `dd`{normal} to delete the line and store it in a Neovim register.
|
||||
|
||||
@@ -378,10 +378,10 @@ Fiix the errors oon thhis line and reeplace them witth undo.
|
||||
|
||||
5. Repeat steps 2 through 4 to put all the lines in correct order.
|
||||
|
||||
d) Can you learn too?
|
||||
b) Violets are blue,
|
||||
c) Intelligence is learned,
|
||||
a) Roses are red,
|
||||
---> d) Can you learn too?
|
||||
---> b) Violets are blue,
|
||||
---> c) Intelligence is learned,
|
||||
---> a) Roses are red,
|
||||
|
||||
NOTE: You can also put the text before the cursor with `P`{normal} (capital P).
|
||||
|
||||
|
@@ -12,17 +12,13 @@
|
||||
"273": -1,
|
||||
"292": "This line of words is cleaned up.",
|
||||
"309": "1) Roses are red,",
|
||||
"310": "3) Violets are blue,",
|
||||
"311": "6) Sugar is sweet",
|
||||
"312": "7) And so are you.",
|
||||
"313": "7) And so are you.",
|
||||
"314": "7) And so are you.",
|
||||
"310": "",
|
||||
"311": "3) Violets are blue,",
|
||||
"312": "",
|
||||
"313": "",
|
||||
"314": "6) Sugar is sweet",
|
||||
"315": "7) And so are you.",
|
||||
"335": "Fix the errors on this line and replace them with undo.",
|
||||
"381": -1,
|
||||
"382": -1,
|
||||
"383": -1,
|
||||
"384": -1,
|
||||
"400": "When this line was typed in, someone pressed some wrong keys!",
|
||||
"401": "When this line was typed in, someone pressed some wrong keys!",
|
||||
"421": "This line has a few words that need changing using the change operator.",
|
||||
|
@@ -368,7 +368,7 @@ This is just a line with words you can move around in.
|
||||
|
||||
** 最後に削除された行をカーソルの後に貼り付ける(Put)には `p`{normal} をタイプします。 **
|
||||
|
||||
1. ✓ と示された以下の最初の行にカーソルを移動しましょう。
|
||||
1. `--->` と示された以下の最初の行にカーソルを移動しましょう。
|
||||
|
||||
2. `dd`{normal} とタイプして行を削除し、Neovim のレジスタに格納しましょう。
|
||||
|
||||
@@ -378,10 +378,10 @@ This is just a line with words you can move around in.
|
||||
|
||||
5. 順番が正しくなる様にステップ 2 から 4 を繰り返しましょう。
|
||||
|
||||
d) 貴方も学ぶことができるか?
|
||||
b) 菫は青く
|
||||
c) 知恵とは学ぶもの
|
||||
a) 薔薇は赤く
|
||||
---> d) 貴方も学ぶことができるか?
|
||||
---> b) 菫は青く
|
||||
---> c) 知恵とは学ぶもの
|
||||
---> a) 薔薇は赤く
|
||||
|
||||
NOTE: `P`{normal} (大文字 P)とタイプすることで、カーソルの前に貼り付ける事もできます。
|
||||
|
||||
|
@@ -11,18 +11,14 @@
|
||||
"233": "誰かがこの行の最後を2度タイプしました。",
|
||||
"272": -1,
|
||||
"291": "この行の単語は綺麗になった。",
|
||||
"308": -1,
|
||||
"309": -1,
|
||||
"310": -1,
|
||||
"311": -1,
|
||||
"312": -1,
|
||||
"313": -1,
|
||||
"314": -1,
|
||||
"308": "1) 薔薇は赤く",
|
||||
"309": "",
|
||||
"310": "3) 菫は青く",
|
||||
"311": "",
|
||||
"312": "",
|
||||
"313": "6) 砂糖は甘く",
|
||||
"314": "7) そして貴方も",
|
||||
"335": "この行の間違いを修正し、後でそれらの修正を取り消します。",
|
||||
"381": -1,
|
||||
"382": -1,
|
||||
"383": -1,
|
||||
"384": -1,
|
||||
"400": "この行を入力した時に、その人は幾つか間違ったキーを押しました!",
|
||||
"401": "この行を入力した時に、その人は幾つか間違ったキーを押しました!",
|
||||
"421": "This line has a few words that need changing using the change operator.",
|
||||
|
967
runtime/tutor/zh/vim-01-beginner.tutor
Normal file
967
runtime/tutor/zh/vim-01-beginner.tutor
Normal file
@@ -0,0 +1,967 @@
|
||||
# 欢迎来到 Neovim 教程
|
||||
|
||||
# 第 1 章
|
||||
|
||||
Neovim 是一个功能非常强大的编辑器,它的命令多到无法在这篇教程里一一讲解。本教程
|
||||
旨在介绍足够多的基本命令,让你能轻松地将 Neovim 作为通用编辑器来使用。
|
||||
|
||||
请务必记住,本教程是为“在实践中学习”而设计的。这意味着你需要亲手完成这些练习才能
|
||||
真正掌握它们。如果你只看不练,很快就会忘记最重要的内容!
|
||||
|
||||
现在,请确保你的大写锁定键(Caps-Lock)是关闭状态,然后多次按下 `j`{normal} 键,
|
||||
直到光标移动到第 0 课完全充满屏幕为止。
|
||||
|
||||
# 第 0 课
|
||||
|
||||
NOTE: 课程中的命令会修改本文,但这些更改不会被保存。不用担心会搞乱什么;只要记住
|
||||
按 [<Esc>](<Esc>) 键,再按 [u](u) 键,就可以撤销最近的更改。
|
||||
|
||||
本教程是交互式的,有几件事你需要知道。
|
||||
- 在 [这样的](holy-grail ) 链接上按 [<Enter>](<Enter>) 键可以打开链接的帮助文档。
|
||||
- 或者,你也可以在任意单词上按 [K](K) 键来查找它的文档!
|
||||
- 你可以用 `:q`{vim} `<Enter>`{normal} 来关闭这个帮助窗口。
|
||||
|
||||
当左边出现 ✗ 标志时,你需要去修改文本。当你正确地完成修改后,左边的 ✗ 标志就会变
|
||||
成 ✓。我想你已经能体会到 Neovim 的强大之处了。
|
||||
|
||||
其他时候,你会被提示运行一个命令(稍后会对此进行解释):
|
||||
|
||||
`:help`{vim} `<Enter>`{normal}
|
||||
|
||||
或者按下一系列按键:
|
||||
~~~ normal
|
||||
<Esc>0f<Space>d3wP$P
|
||||
~~~
|
||||
尖括号(<>)里的文本(如 `<Enter>`{normal})代表你需要按下的键,而不是要输入的文本。
|
||||
|
||||
现在,移动到下一课(使用 `j`{normal} 键向下滚动)。
|
||||
|
||||
# 第 1.1 课:移动光标
|
||||
|
||||
** 按 `h`、`j`、`k`、`l` 键来移动光标,如下所示。 **
|
||||
|
||||
↑
|
||||
k 提示:`h`{normal} 键在左边,向左移动。
|
||||
← h l → `l`{normal} 键在右边,向右移动。
|
||||
j `j`{normal} 键看起来像一个向下的箭头。
|
||||
↓
|
||||
|
||||
1. 在屏幕上四处移动光标,直到你习惯这种操作。
|
||||
|
||||
2. 按住向下键(`j`{normal})直到光标开始连续向下移动。
|
||||
现在你知道如何移动到下一课了。
|
||||
|
||||
3. 使用下移键,移动到第 1.2 课。
|
||||
|
||||
NOTE: 如果你不确定自己输入了什么,随时可以按 <Esc> 键回到普通模式(Normal mode)。
|
||||
然后重新输入你想要的命令。
|
||||
|
||||
NOTE: 键盘上的方向键通常也能用。但一旦你习惯了,使用 hjkl 可以让你移动得更快。
|
||||
|
||||
# 第 1.2 课:退出 Neovim
|
||||
|
||||
!! NOTE: 在执行以下任何步骤之前,请先阅读完本课的全部内容 !!
|
||||
|
||||
1. 按下 <Esc> 键(确保你处于普通模式)。
|
||||
|
||||
2. 输入:
|
||||
|
||||
`:q!`{vim} `<Enter>`{normal}
|
||||
|
||||
这个命令会退出编辑器,并“丢弃”你所做的所有更改。
|
||||
|
||||
3. 重新打开 Neovim,并通过执行带你进入本教程的命令回到这里。这个命令也许是:
|
||||
|
||||
`:Tutor`{vim} `<Enter>`{normal}
|
||||
|
||||
4. 如果你已经记住了这些步骤并充满信心,请执行第 1 到 3 步来退出并重新进入编辑器。
|
||||
|
||||
NOTE: [:q!](:q) `<Enter>`{normal} 会丢弃你做的任何更改。在接下来的几课中,你将学习如何
|
||||
将更改保存到文件中。
|
||||
|
||||
5. 向下移动光标到第 1.3 课。
|
||||
|
||||
# 第 1.3 课:文本编辑——删除(Deletion)
|
||||
|
||||
** 按 `x`{normal} 键可以删除光标下的单个字符。 **
|
||||
|
||||
1. 将光标移动到下面标有 ✗ 的那一行。
|
||||
|
||||
2. 为了修正错误,请将光标移动到需要被删除的字符上。
|
||||
|
||||
3. 按下 [x 键](x) 来删除那个多余的字符。
|
||||
|
||||
4. 重复第 2 到 4 步,直到句子正确为止。
|
||||
|
||||
The ccow jumpedd ovverr thhe mooon.
|
||||
The cow jumped over the moon.
|
||||
|
||||
5. 现在句子已经正确了,请继续学习第 1.4 课。
|
||||
|
||||
NOTE: 在学习本教程时,不必试图记住所有内容,你的 Neovim 词汇量会随着使用而增长。
|
||||
可以考虑定期回到本教程进行复习。
|
||||
|
||||
# 第 1.4 课:文本编辑——插入(Insertion)
|
||||
|
||||
** 按 `i`{normal} 键可以插入文本。 **
|
||||
|
||||
1. 将光标移动到下面标有 ✗ 的第一行。
|
||||
|
||||
2. 为了让第一行和第二行变得一样,请将光标移动到需要插入文本位置“之后”的那个字符上。
|
||||
|
||||
3. 按下 `i`{normal} 键,然后输入需要补充的内容。
|
||||
|
||||
4. 每个错误修正后,按 `<Esc>`{normal} 键回到普通模式。
|
||||
重复第 2 到 4 步来修正整个句子。
|
||||
|
||||
There is text misng this .
|
||||
There is some text missing from this line.
|
||||
|
||||
5. 当你熟练掌握插入文本后,请继续学习第 1.5 课。
|
||||
|
||||
# 第 1.5 课:文本编辑——追加(Appending)
|
||||
|
||||
** 按 `A`{normal} 键可以在行末追加文本。 **
|
||||
|
||||
1. 将光标移动到下面标有 ✗ 的第一行。
|
||||
光标在该行的哪个字符上并不重要。
|
||||
|
||||
2. 按下 [A](A) 键,然后输入需要补充的内容。
|
||||
|
||||
3. 文本追加完成后,按 `<Esc>`{normal} 键回到普通模式。
|
||||
|
||||
4. 将光标移动到标有 ✗ 的第二行,并重复第 2 和第 3 步来修正这个句子。
|
||||
|
||||
There is some text missing from th
|
||||
There is some text missing from this line.
|
||||
There is also some text miss
|
||||
There is also some text missing here.
|
||||
|
||||
5. 当你熟练掌握追加文本后,请继续学习第 1.6 课。
|
||||
|
||||
# 第 1.6 课:编辑文件
|
||||
|
||||
** 使用 `:wq`{vim} 来写入(保存)文件并退出。 **
|
||||
|
||||
!! NOTE: 在执行以下任何步骤之前,请先阅读完本课的全部内容 !!
|
||||
|
||||
1. 像第 1.2 课那样退出本教程:`:q!`{vim}
|
||||
或者,如果你能打开另一个终端,就在那个终端里执行以下操作。
|
||||
|
||||
2. 在 shell 提示符下输入这个命令:
|
||||
~~~ sh
|
||||
$ nvim tutor
|
||||
~~~
|
||||
'nvim' 是启动 Neovim 编辑器的命令,'tutor' 是你希望编辑的文件名。
|
||||
请使用一个可以被修改的文件。
|
||||
|
||||
3. 像在前面课程中学到的那样,插入和删除一些文本。
|
||||
|
||||
4. 保存更改并退出 Neovim:
|
||||
~~~ cmd
|
||||
:wq
|
||||
~~~
|
||||
注意,你需要按 `<Enter>` 键来执行该命令。
|
||||
|
||||
5. 如果你在第 1 步退出了本教程,请重启教程并移动到下面的总结部分。
|
||||
|
||||
6. 在阅读并理解了以上步骤后,亲手操作一遍。
|
||||
|
||||
# 第 1 课总结
|
||||
|
||||
1. 移动光标可以使用方向键,也可以使用 hjkl 键。
|
||||
h (左) j (下) k (上) l (右)
|
||||
|
||||
2. 从 shell 提示符启动 Neovim,输入:
|
||||
~~~ sh
|
||||
$ nvim 文件名
|
||||
~~~
|
||||
3. 退出 Neovim,输入:`<Esc>`{normal} `:q!`{vim} `<Enter>`{normal} 放弃所有更改。
|
||||
或者输入:`<Esc>`{normal} `:wq`{vim} `<Enter>`{normal} 保存所有更改。
|
||||
|
||||
4. 删除光标下的字符,输入:`x`{normal}
|
||||
|
||||
5. 插入或追加文本,输入:
|
||||
`i`{normal} 插入文本 `<Esc>`{normal} 在光标前插入。
|
||||
`A`{normal} 追加文本 `<Esc>`{normal} 在当前行末尾追加。
|
||||
|
||||
NOTE: 按 `<Esc>`{normal} 键会让你进入普通模式,或者取消一个不想继续输入的、未完成的命令。
|
||||
|
||||
现在,请继续学习第 2 课。
|
||||
|
||||
# 第 2.1 课:删除类命令(Deletion)
|
||||
|
||||
** 输入 `dw`{normal} 来删除一个单词(word)。 **
|
||||
|
||||
1. 按 `<Esc>`{normal} 键确保你处于普通模式。
|
||||
|
||||
2. 将光标移动到下面标有 ✗ 的那一行。
|
||||
|
||||
3. 将光标移动到需要被删除的单词的开头。
|
||||
|
||||
4. 输入 [d](d)[w](w) 让这个单词消失。
|
||||
|
||||
There are a some words fun that don't belong paper in this sentence.
|
||||
There are some words that don't belong in this sentence.
|
||||
|
||||
5. 重复第 3 和第 4 步,直到句子正确,然后继续学习第 2.2 课。
|
||||
|
||||
# 第 2.2 课:更多删除类命令
|
||||
|
||||
** 输入 `d$`{normal} 来删除从光标到行尾的内容。 **
|
||||
|
||||
1. 按 `<Esc>`{normal} 键确保你处于普通模式。
|
||||
|
||||
2. 将光标移动到下面标有 ✗ 的那一行。
|
||||
|
||||
3. 将光标移动到正确句子的末尾(在第一个 . 之后)。
|
||||
|
||||
4. 输入 `d$`{normal} 来删除到行尾的所有内容。
|
||||
|
||||
Somebody typed the end of this line twice. end of this line twice.
|
||||
|
||||
5. 继续学习第 2.3 课,来理解这背后的原理。
|
||||
|
||||
# 第 2.3 课:关于操作符(operator)和移动(motion)
|
||||
|
||||
许多修改文本的命令都由一个 [操作符](operator) 和一个 [移动](navigation) 组成。
|
||||
使用 [d](d) 删除操作符的命令格式如下:
|
||||
|
||||
d 移动
|
||||
|
||||
其中:
|
||||
d - 是删除操作符。
|
||||
移动 - 是操作符将要作用的范围(如下所列)。
|
||||
|
||||
一些常用的移动:
|
||||
[w](w) - 到下一个单词的开头,但不包括其第一个字符。
|
||||
[e](e) - 到当前单词的末尾,并包括最后一个字符。
|
||||
[$]($) - 到当前行的末尾,并包括最后一个字符。
|
||||
|
||||
因此,输入 `de`{normal} 将会删除从光标到当前单词末尾的内容。
|
||||
|
||||
NOTE: 在普通模式下,不带操作符、只按下移动键,将会像预期的那样移动光标。
|
||||
|
||||
# 第 2.4 课:为移动增加计数
|
||||
|
||||
** 在移动命令前输入一个数字,可以将其重复执行那么多次。 **
|
||||
|
||||
1. 将光标移动到下面标有 ✓ 的那行的开头。
|
||||
|
||||
2. 输入 `2w`{normal},光标会向前移动两个单词。
|
||||
|
||||
3. 输入 `3e`{normal},光标会移动到前方第三个单词的末尾。
|
||||
|
||||
4. 输入 `0`{normal}([零](0))可以移动到行首。
|
||||
|
||||
5. 用不同的数字重复第 2 和第 3 步。
|
||||
|
||||
This is just a line with words you can move around in.
|
||||
|
||||
6. 继续学习第 2.5 课。
|
||||
|
||||
# 第 2.5 课:使用计数删除更多内容
|
||||
|
||||
** 在操作符和移动之间加上一个数字,可以将其重复执行那么多次。 **
|
||||
|
||||
在前面提到的“删除操作符 + 移动”组合中,你可以在移动前插入一个计数来删除更多内容:
|
||||
d 数字 移动
|
||||
|
||||
1. 将光标移动到下面标有 ✗ 的那一行中第一个全大写单词上。
|
||||
|
||||
2. 输入 `d2w`{normal} 来删除两个全大写单词。
|
||||
|
||||
3. 重复第 1 和第 2 步,但使用不同的计数,用一个命令删除剩余连续的全大写单词。
|
||||
|
||||
This ABC DE line FGHI JK LMN OP of words is Q RS TUV cleaned up.
|
||||
|
||||
# 第 2.6 课:行操作
|
||||
|
||||
** 输入 `dd`{normal} 来删除一整行。 **
|
||||
|
||||
由于删除整行的操作非常频繁,Vi 的设计者们决定,输入两个 d 来删除一行会更方便。
|
||||
|
||||
1. 将光标移动到下面短语的第 2 行。
|
||||
|
||||
2. 输入 [dd](dd) 来删除该行。
|
||||
|
||||
3. 现在移动到第 4 行。
|
||||
|
||||
4. 输入 `2dd`{normal} 来删除两行。
|
||||
|
||||
1) Roses are red,
|
||||
2) Mud is fun,
|
||||
3) Violets are blue,
|
||||
4) I have a car,
|
||||
5) Clocks tell time,
|
||||
6) Sugar is sweet
|
||||
7) And so are you.
|
||||
|
||||
# 第 2.7 课:撤销命令(Undo)
|
||||
|
||||
** 按 `u`{normal} 撤销上一个命令,按 `U`{normal} 恢复一整行。 **
|
||||
|
||||
1. 将光标移动到下面标有 ✗ 的那一行,并把它放在第一个错误上。
|
||||
|
||||
2. 输入 `x`{normal} 来删除第一个多余的字符。
|
||||
|
||||
3. 现在输入 `u`{normal} 来撤销上一次执行的命令。
|
||||
|
||||
4. 这次,使用 `x`{normal} 命令修正该行所有的错误。
|
||||
|
||||
5. 现在输入大写的 `U`{normal},将该行恢复到它最初始的状态。
|
||||
|
||||
6. 现在多次输入 `u`{normal},来撤销 `U`{normal} 以及之前的命令。
|
||||
|
||||
7. 现在多次输入 `<C-r>`{normal}(Ctrl + R),来重做(redo)那些被撤销的命令。
|
||||
|
||||
Fiix the errors oon thhis line and reeplace them witth undo.
|
||||
Fix the errors on this line and replace them with undo.
|
||||
|
||||
8. 这些都是非常有用的命令。现在请继续学习第 2 课的总结。
|
||||
|
||||
# 第 2 课总结
|
||||
|
||||
1. 删除从光标到下一个单词开头的内容,输入: `dw`{normal}
|
||||
|
||||
2. 删除从光标到行尾的内容,输入: `d$`{normal}
|
||||
|
||||
3. 删除一整行,输入: `dd`{normal}
|
||||
|
||||
4. 重复一个移动,在它前面加上数字: `2w`{normal}
|
||||
|
||||
5. 修改类命令的格式是:
|
||||
|
||||
操作符 [数字] 移动
|
||||
|
||||
其中:
|
||||
|
||||
操作符 - 是要做什么,比如 [d](d) 代表删除。
|
||||
[数字] - 是一个可选的计数,用来重复移动。
|
||||
移动 - 定义了操作符要作用的文本范围,例如:
|
||||
[w](w)(单词),
|
||||
[$]($)(到行尾),等等。
|
||||
|
||||
6. 移动到行首,使用零:[0](0)
|
||||
|
||||
7. 撤销之前的操作,输入: `u`{normal}(小写 u)
|
||||
撤销对一整行的所有更改,输入: `U`{normal}(大写 U)
|
||||
重做被撤销的操作,输入: `<C-r>`{normal}
|
||||
|
||||
# 第 3.1 课:粘贴命令(Put)
|
||||
|
||||
** 输入 `p`{normal} 可以将之前删除的文本粘贴到光标之后。 **
|
||||
|
||||
1. 将光标移动到下面第一个标有 `--->` 的行。
|
||||
|
||||
2. 输入 `dd`{normal} 来删除该行,并将其存入 Neovim 的一个寄存器中。
|
||||
|
||||
3. 将光标移动到 c) 行,也就是被删除那一行的“上一行”。
|
||||
|
||||
4. 输入 `p`{normal},将之前删除的行粘贴到光标下方。
|
||||
|
||||
5. 重复第 2 到 4 步,将所有行按正确的顺序(abcd)排列。
|
||||
|
||||
---> d) Can you learn too?
|
||||
---> b) Violets are blue,
|
||||
---> c) Intelligence is learned,
|
||||
---> a) Roses are red,
|
||||
|
||||
NOTE: 你也可以用 `P`{normal}(大写 P)将文本粘贴到光标之前。
|
||||
|
||||
# 第 3.2 课:替换命令(Replace)
|
||||
|
||||
** 输入 `rx`{normal} 可以将光标下的字符替换为 x。 **
|
||||
|
||||
1. 将光标移动到下面标有 ✗ 的第一行。
|
||||
|
||||
2. 将光标移动到第一个错误字符上。
|
||||
|
||||
3. 输入 `r`{normal},然后输入正确的字符。
|
||||
|
||||
4. 重复第 2 和第 3 步,直到第一行和第二行完全一样。
|
||||
|
||||
Whan this lime was tuoed in, someone presswd some wrojg keys!
|
||||
When this line was typed in, someone pressed some wrong keys!
|
||||
|
||||
5. 现在请继续学习第 3.3 课。
|
||||
|
||||
NOTE: 请记住,你应该通过实践来学习,而不是死记硬背。
|
||||
|
||||
# 第 3.3 课:更改操作符(Change)
|
||||
|
||||
** 要更改到单词末尾,输入 `ce`{normal}。 **
|
||||
|
||||
1. 将光标移动到下面标有 ✗ 的第一行。
|
||||
|
||||
2. 将光标放在 "lubw" 的 "u" 上。
|
||||
|
||||
3. 输入 `ce`{normal},然后输入正确的单词(在这种情况是输入 "ine")。
|
||||
|
||||
4. 按下 `<Esc>`{normal},然后移动到下一个需要修改的字符。
|
||||
|
||||
5. 重复第 3 和第 4 步,直到第一句和第二句完全一样。
|
||||
|
||||
This lubw has a few wptfd that mrrf changing usf the change operator.
|
||||
This line has a few words that need changing using the change operator.
|
||||
|
||||
请注意,[c](c)e 会删除单词并让你进入插入模式(Insert mode)。
|
||||
|
||||
# 第 3.4 课:使用 `c`{normal} 进行更多更改
|
||||
|
||||
** 更改操作符可以和删除操作符使用相同的移动。 **
|
||||
|
||||
1. 更改操作符的工作方式和删除操作符一样。格式是:
|
||||
|
||||
c [数字] 移动
|
||||
|
||||
2. 移动也是一样的,比如 `w`{normal}(单词)和 `$`{normal}(到行尾)。
|
||||
|
||||
3. 移动到下面标有 ✗ 的第一行。
|
||||
|
||||
4. 将光标移动到第一个错误处。
|
||||
|
||||
5. 输入 `c$`{normal},然后输入该行余下正确的内容(参照第二行),最后按 `<Esc>`{normal}。
|
||||
|
||||
The end of this line needs some help to make it like the second.
|
||||
The end of this line needs to be corrected using the c$ command.
|
||||
|
||||
NOTE: 在输入时,你可以使用退格键(Backspace)来修正错误。
|
||||
|
||||
# 第 3 课总结
|
||||
|
||||
1. 要粘贴刚刚删除的文本,输入 [p](p)。这会把删除的文本放在光标“之后”
|
||||
(如果删除的是一整行,它会被粘贴到光标所在行的下一行)。
|
||||
|
||||
2. 要替换光标下的单个字符,输入 [r](r) 然后输入你想要的那个字符。
|
||||
|
||||
3. [更改操作符](c) 允许你更改从光标开始到一个移动命令结束位置的文本。
|
||||
输入 `ce`{normal} 来更改到单词末尾,`c$`{normal} 来更改到行尾,等等。
|
||||
|
||||
4. 更改命令的格式是:
|
||||
|
||||
c [数字] 移动
|
||||
|
||||
现在请继续学习下一课。
|
||||
|
||||
# 第 4.1 课:光标位置和文件状态
|
||||
|
||||
** 输入 `<C-g>`{normal} 来显示你在文件中的位置和文件状态。
|
||||
输入 `{count}G`{normal} 来移动到文件中的第 {count} 行。 **
|
||||
|
||||
NOTE: 在执行任何步骤之前,请先阅读完本课的全部内容!!
|
||||
|
||||
1. 按住 `<Ctrl>`{normal} 键并按下 `g`{normal}。我们称这个操作为 `<C-g>`{normal}。
|
||||
屏幕底部会出现一条消息,包含文件名和你在文件中的位置。
|
||||
请记住行号,第 3 步会用到。
|
||||
|
||||
NOTE: 你可能会在屏幕右下角看到光标位置。这是因为设置了 ['ruler']('ruler') 选项。
|
||||
|
||||
2. 按 [G](G) 移动到文件的末尾。
|
||||
输入 [gg](gg) 移动到文件的开头。
|
||||
|
||||
3. 输入你之前记住的行号,然后按 `G`{normal}。这会让你回到你第一次按 `<C-g>`{normal} 时所在的行。
|
||||
|
||||
4. 如果你觉得没问题,就执行第 1 到 3 步。
|
||||
|
||||
# 第 4.2 课:搜索命令
|
||||
|
||||
** 输入 `/`{normal} 后面跟一个短语,来搜索这个短语。 **
|
||||
|
||||
1. 在普通模式下,输入 `/`{normal} 字符。注意它和光标会出现在屏幕底部,
|
||||
就像 `:`{normal} 命令一样。
|
||||
|
||||
2. 现在输入 errroor `<Enter>`{normal}。这是你想要搜索的词。
|
||||
|
||||
3. 要再次搜索同一个短语,只需输入 [n](n)。
|
||||
要反向搜索同一个短语,输入 [N](N)。
|
||||
|
||||
4. 要反向搜索一个短语,使用 [?](?) 而不是 `/`{normal}。
|
||||
|
||||
5. 要回到你之前的位置,按 `<C-o>`{normal}。(按住 `<Ctrl>`{normal} 键的同时按字母 `o`{normal})。
|
||||
重复按可以回到更早的位置。`<C-i>`{normal} 则会前进。
|
||||
|
||||
"errroor" is not the way to spell error; errroor is an error.
|
||||
|
||||
NOTE: 当搜索到达文件末尾时,它会从头开始继续搜索,除非 ['wrapscan']('wrapscan') 选项被关闭了。
|
||||
|
||||
# 第 4.3 课:括号匹配搜索
|
||||
|
||||
** 输入 `%`{normal} 来查找匹配的 ), ], }。 **
|
||||
|
||||
1. 将光标放在下面标有 ✓ 的那一行中的任意一个 (, [, { 上。
|
||||
|
||||
2. 现在输入 [%](%) 字符。
|
||||
|
||||
3. 光标会移动到与之匹配的括号上。
|
||||
|
||||
4. 再次输入 `%`{normal},光标会移回另一半匹配的括号。
|
||||
|
||||
5. 将光标移动到另一个 (, ), [, ], {, } 上,看看 `%`{normal} 的效果。
|
||||
|
||||
This ( is a test line with ('s, ['s, ] and {'s } in it. ))
|
||||
|
||||
NOTE: 这在调试程序中不匹配的括号时非常有用!
|
||||
|
||||
# 第 4.4 课:替换命令(Substitute)
|
||||
|
||||
** 输入 `:s/old/new/g` 来用 "new" 替换 "old"。 **
|
||||
|
||||
1. 将光标移动到下面标有 ✗ 的那一行。
|
||||
|
||||
2. 输入
|
||||
~~~ cmd
|
||||
:s/thee/the/
|
||||
~~~
|
||||
NOTE: [:s](:s) 命令只改变了行中第一个匹配的 "thee"。
|
||||
|
||||
3. 现在输入
|
||||
~~~ cmd
|
||||
:s/thee/the/g
|
||||
~~~
|
||||
加上 g [标志](:s_flags) 意味着在行内进行全局替换,也就是改变该行中所有出现的 "thee"。
|
||||
|
||||
Usually thee best time to see thee flowers is in thee spring.
|
||||
|
||||
4. 要在两行之间替换一个字符串的所有出现,输入
|
||||
~~~ cmd
|
||||
:#,#s/old/new/g
|
||||
~~~
|
||||
其中 # 是要进行替换的行号范围(例如,`1,3` 表示从第 1 行到第 3 行,包含这两行)。
|
||||
|
||||
输入
|
||||
~~~ cmd
|
||||
:%s/old/new/g
|
||||
~~~
|
||||
可以在整个文件中进行替换。
|
||||
|
||||
输入
|
||||
~~~ cmd
|
||||
:%s/old/new/gc
|
||||
~~~
|
||||
可以在整个文件中查找所有出现,并对每一次替换进行确认。
|
||||
|
||||
NOTE: 你也可以先用可视模式(Visual mode)选中你想替换的行。这在后面的课程中会详细解释。
|
||||
|
||||
# 第 4 课总结
|
||||
|
||||
1. `<C-g>`{normal} 显示你的位置和文件状态。
|
||||
`G`{normal} 移动到文件末尾。
|
||||
数字 `G`{normal} 移动到指定的行号。
|
||||
`gg`{normal} 移动到第一行。
|
||||
|
||||
2. 输入 `/`{normal} 后跟一个短语,会“向前”搜索该短语。
|
||||
输入 `?`{normal} 后跟一个短语,会“向后”搜索该短语。
|
||||
搜索后,输入 `n`{normal} 查找下一个(同方向),或 `N`{normal} 查找下一个(反方向)。
|
||||
`<C-o>`{normal} 带你回到旧的光标位置,`<C-i>`{normal} 则去往新的位置。
|
||||
|
||||
3. 当光标在 (, ), [, ], {, } 上时,输入 `%`{normal} 会跳转到其匹配的另一半。
|
||||
|
||||
4. 将行内第一个 old 替换为 new,输入
|
||||
~~~ cmd
|
||||
:s/old/new
|
||||
~~~
|
||||
将行内所有 old 替换为 new,输入
|
||||
~~~ cmd
|
||||
:s/old/new/g
|
||||
~~~
|
||||
在两行 # 之间进行替换,输入
|
||||
~~~ cmd
|
||||
:#,#s/old/new/g
|
||||
~~~
|
||||
在整个文件中进行替换,输入
|
||||
~~~ cmd
|
||||
:%s/old/new/g
|
||||
~~~
|
||||
每次替换前进行确认,添加 'c' 标志
|
||||
~~~ cmd
|
||||
:%s/old/new/gc
|
||||
~~~
|
||||
|
||||
# 第 5.1 课:如何执行外部命令
|
||||
|
||||
** 输入 `:!`{vim} 后面跟一个外部命令,来执行该命令。 **
|
||||
|
||||
1. 输入你熟悉的命令 `:`{normal},让光标定位到屏幕底部。这允许你输入一个
|
||||
命令行命令。
|
||||
|
||||
2. 现在输入 [!](!cmd)(感叹号)字符。这允许你执行任何外部的 shell 命令。
|
||||
|
||||
3. 举个例子,在 "!" 后面输入 "ls",然后按 `<Enter>`{normal}。
|
||||
这会显示你的目录列表,就像你在 shell 提示符下一样。
|
||||
|
||||
NOTE: 你可以用这种方式执行任何外部命令,并且可以带参数。
|
||||
|
||||
NOTE: 所有 `:`{vim} 命令都在你按下 `<Enter>`{normal} 后执行。
|
||||
|
||||
# 第 5.2 课:更多关于写入文件的知识
|
||||
|
||||
** 要保存对文本的更改,输入 `:w`{vim} 文件名。 **
|
||||
|
||||
1. 输入 `:!{unix:(ls),win:(dir)}`{vim} 来获取你的目录列表。
|
||||
你已经知道在这之后必须按 `<Enter>`{normal}。
|
||||
|
||||
2. 选择一个还不存在的文件名,比如 TEST。
|
||||
|
||||
3. 现在输入:
|
||||
~~~ cmd
|
||||
:w TEST
|
||||
~~~
|
||||
(这里的 TEST 是你选择的文件名。)
|
||||
|
||||
4. 这会将当前文件以 TEST 的名字保存。
|
||||
要验证这一点,再次输入 `:!{unix:(ls),win:(dir)}`{vim} 来查看你的目录。
|
||||
|
||||
NOTE: 如果你退出 Neovim,然后用 `nvim TEST` 再次启动它,这个文件将会是你保存时教程
|
||||
内容的一个精确副本。
|
||||
|
||||
5. 现在通过输入以下命令来删除该文件:
|
||||
~~~ cmd
|
||||
:!{unix:(rm),win:(del)} TEST
|
||||
~~~
|
||||
|
||||
# 第 5.3 课:选择要写入的文本
|
||||
|
||||
** 要保存文件的一部分,输入 `v`{normal} 移动 `:w 文件名`{vim}。 **
|
||||
|
||||
1. 将光标移动到这一行。
|
||||
|
||||
2. 按下 [v](v) 并将光标移动到下面的第五项。注意文本被高亮了。
|
||||
|
||||
3. 按下 `:`{normal} 字符。在屏幕底部会出现:
|
||||
|
||||
`:'<,'>`{vim}
|
||||
|
||||
4. 输入
|
||||
|
||||
`w TEST`{vim}
|
||||
|
||||
这里的 TEST 是一个还不存在的文件名。在按 `<Enter>`{normal} 之前,
|
||||
确认你看到的是:
|
||||
|
||||
`:'<,'>w TEST`{vim}
|
||||
|
||||
5. Neovim 会将被选中的行写入文件 TEST。使用 `:!{unix:(ls),win:(dir)}`{vim} 来查看它。
|
||||
先不要删除它!我们将在下一课用到它。
|
||||
|
||||
NOTE: 按下 [v](v) 会启动 [可视选择(Visual selection)](visual-mode)。你可以移动光标来扩大或缩小选择范围。
|
||||
然后你可以使用一个操作符来对选中的文本
|
||||
做些什么。例如,`d`{normal} 会删除选中的文本。
|
||||
|
||||
# 第 5.4 课:读取和合并文件
|
||||
|
||||
** 要读取一个文件的内容,输入 `:r 文件名`{vim}。 **
|
||||
|
||||
1. 将光标放在这一行的正上方。
|
||||
|
||||
NOTE: 执行第 2 步后,你会看到第 5.3 课的文本。然后向下移动
|
||||
才能再次看到本课内容。完成后按 `u`{normal} 撤销。
|
||||
|
||||
2. 现在使用以下命令读取你的 TEST 文件:
|
||||
|
||||
`:r TEST`{vim}
|
||||
|
||||
这里的 TEST 是你之前使用的文件名。
|
||||
你读取的文件内容会被放在光标所在行的下方。
|
||||
|
||||
3. 要验证文件是否被读取,向上移动光标,你会注意到现在有两份第 5.3 课的内容,
|
||||
一份是原始的,一份是读取进来的。
|
||||
|
||||
NOTE: 你也可以读取一个外部命令的输出。例如:
|
||||
|
||||
`:r !{unix:(ls),win:(dir)}`{vim}
|
||||
|
||||
会读取 `ls` 命令的输出,并将其放在光标下方。
|
||||
|
||||
# 第 5 课总结
|
||||
|
||||
1. [:!command](:!cmd) 执行一个外部命令。
|
||||
|
||||
一些有用的例子:
|
||||
`:!{unix:(ls ),win:(dir)}`{vim} - 显示目录列表
|
||||
`:!{unix:(rm ),win:(del)} 文件`{vim} - 删除文件
|
||||
|
||||
2. [:w](:w) 文件名 将当前 Neovim 文件以“文件名”这个名字写入磁盘。
|
||||
|
||||
3. [v](v) 移动 :w 文件名 将可视模式选中的行保存到文件“文件名”中。
|
||||
|
||||
4. [:r](:r) 文件名 读取磁盘文件“文件名”的内容,并将其放在光标位置的下方。
|
||||
|
||||
5. {unix:([:r !ls](:r!) ),win:([:r !dir](:r!))} 读取 {unix:(ls),win:(dir)} 命令的输出,并将其放在光标位置的下方。
|
||||
|
||||
# 第 6.1 课:开启新行命令(Open)
|
||||
|
||||
** 输入 `o`{normal} 可以在光标下方开启一个新行,并进入插入模式。 **
|
||||
|
||||
1. 将光标移动到下面标有 ✓ 的那一行。
|
||||
|
||||
2. 输入小写字母 `o`{normal},在光标“下方”[开启](o)一个新行,并让你进入插入模式。
|
||||
|
||||
3. 现在输入一些文本,然后按 `<Esc>`{normal} 退出插入模式。完成后请删除你开启的行。
|
||||
|
||||
After typing `o`{normal} the cursor is placed on the open line in Insert mode.
|
||||
|
||||
4. 要在光标“上方”开启一个新行,只需输入[大写 O](O),而不是小写 `o`{normal}。
|
||||
在下面这行上试试。完成后请删除你开启的行。
|
||||
|
||||
Open up a line above this by typing O while the cursor is on this line.
|
||||
|
||||
# 第 6.2 课:追加命令(Append)
|
||||
|
||||
** 输入 `a`{normal} 可以在光标“之后”插入文本。 **
|
||||
|
||||
1. 将光标移动到下面标有 ✗ 的那行的开头。
|
||||
|
||||
2. 按 `e`{normal} 直到光标移动到 "li" 的末尾。
|
||||
|
||||
3. 输入小写字母 `a`{normal},在光标“之后”[追加](a)文本。
|
||||
|
||||
4. 参照下一行,补全这个单词。按 `<Esc>`{normal} 退出插入模式。
|
||||
|
||||
5. 使用 `e`{normal} 移动到下一个不完整的单词,并重复第 3 和第 4 步。
|
||||
|
||||
This li will allow you to pract appendi text to a line.
|
||||
This line will allow you to practice appending text to a line.
|
||||
|
||||
NOTE: [a](a)、[i](i) 和 [A](A) 都会进入同一个插入模式,唯一的区别是字符被插入的位置不同。
|
||||
|
||||
# 第 6.3 课:另一种替换方式
|
||||
|
||||
** 输入大写 `R`{normal} 可以替换多个字符。 **
|
||||
|
||||
1. 将光标移动到下面标有 ✗ 的第一行。将光标移动到第一个 "xxx" 的开头。
|
||||
|
||||
2. 现在按下 `R`{normal}([大写 R](R))并输入第二行中对应的数字,用它来替换 "xxx"。
|
||||
|
||||
3. 按下 `<Esc>`{normal} 离开[替换模式(Replace mode)](mode-replace)。注意该行余下的部分保持不变。
|
||||
|
||||
4. 重复以上步骤来替换剩下的 "xxx"。
|
||||
|
||||
Adding 123 to xxx gives you xxx.
|
||||
Adding 123 to 456 gives you 579.
|
||||
|
||||
NOTE: 替换模式很像插入模式,但你输入的每个字符都会替换掉一个已有的字符。
|
||||
|
||||
# 第 6.4 课:复制和粘贴文本
|
||||
|
||||
** 使用 `y`{normal} 操作符来复制(yank)文本,用 `p`{normal} 来粘贴(put)它。 **
|
||||
|
||||
1. 去到下面标有 ✓ 的那一行,并将光标放在 "a)" 之后。
|
||||
|
||||
2. 用 `v`{normal} 启动可视模式,并将光标移动到 "first" 之前。
|
||||
|
||||
3. 输入 `y`{normal} 来 [复制(yank)](yank) 高亮的文本。
|
||||
|
||||
4. 将光标移动到下一行的末尾:`j$`{normal}
|
||||
|
||||
5. 输入 `p`{normal} 来 [粘贴(put)](put) 文本。
|
||||
|
||||
6. 按下 `a`{normal} 然后输入 "second"。按 `<Esc>`{normal} 离开
|
||||
插入模式。
|
||||
|
||||
7. 使用可视模式选中 "item.",用 `y`{normal} 复制它,用 `j$`{normal} 移动到
|
||||
下一行的末尾,然后用 `p`{normal} 在那里粘贴文本。
|
||||
|
||||
a) This is the first item.
|
||||
b)
|
||||
|
||||
NOTE: 你可以把 `y`{normal} 当作一个操作符来使用:`yw`{normal} 会复制一个单词。
|
||||
|
||||
NOTE: 你可以用 `P`{normal} 在光标前粘贴,而不是在光标后。
|
||||
|
||||
# 第 6.5 课:设置选项(Set)
|
||||
|
||||
** 设置一个选项,让搜索和替换命令忽略大小写。 **
|
||||
|
||||
Neovim 中有许多设置,你可以配置它们来满足你的需求。
|
||||
|
||||
1. 通过输入 `/ignore` 来搜索 'ignore'。
|
||||
多按几次 `n`{normal} 来重复搜索。
|
||||
|
||||
2. 通过输入以下命令来设置 'ic'(Ignore case,即忽略大小写) 选项:
|
||||
~~~ cmd
|
||||
:set ic
|
||||
~~~
|
||||
3. 现在再次按 `n`{normal} 搜索 'ignore'。
|
||||
注意 Ignore 和 IGNORE 现在也能被找到了。
|
||||
|
||||
4. 设置 'hlsearch' 和 'incsearch' 选项:
|
||||
~~~ cmd
|
||||
:set hls is
|
||||
~~~
|
||||
5. 现在再次输入搜索命令,看看会发生什么:/ignore <Enter>
|
||||
|
||||
6. 要禁用忽略大小写,输入:
|
||||
~~~ cmd
|
||||
:set noic
|
||||
~~~
|
||||
7. 要反转一个设置的值,在它前面加上 "inv":
|
||||
~~~ cmd
|
||||
:set invic
|
||||
~~~
|
||||
NOTE: 要移除匹配项的高亮,输入:
|
||||
~~~ cmd
|
||||
:nohlsearch
|
||||
~~~
|
||||
NOTE: 如果你只想在某一次搜索命令中忽略大小写,在短语中使用 [\c](/\c):
|
||||
/ignore\c <Enter>
|
||||
|
||||
# 第 6 课总结
|
||||
|
||||
1. 输入 `o`{normal} 在光标“下方”开启一个新行并进入插入模式。
|
||||
输入 `O`{normal} 在光标“上方”开启一个新行。
|
||||
|
||||
2. 输入 `a`{normal} 在光标“之后”插入文本。
|
||||
输入 `A`{normal} 在行尾之后插入文本。
|
||||
|
||||
3. `e`{normal} 命令移动到一个单词的末尾。
|
||||
|
||||
4. `y`{normal} 操作符复制文本,`p`{normal} 粘贴它。
|
||||
|
||||
5. 输入大写 `R`{normal} 会进入替换模式,直到按下 `<Esc>`{normal}。
|
||||
|
||||
6. 输入 "[:set](:set) xxx" 可以设置选项 "xxx"。一些有用的选项是:
|
||||
|
||||
'ic' 'ignorecase' 搜索时忽略大小写
|
||||
'is' 'incsearch' 实时显示搜索短语的部分匹配
|
||||
'hls' 'hlsearch' 高亮所有匹配的短语
|
||||
|
||||
你可以使用长选项名或短选项名。
|
||||
|
||||
7. 在选项前加上 "no" 来关闭一个选项:
|
||||
~~~ cmd
|
||||
:set noic
|
||||
~~~
|
||||
8. 在选项前加上 "inv" 来反转一个选项的值:
|
||||
~~~ cmd
|
||||
:set invic
|
||||
~~~
|
||||
# 第 7.1 课:获取帮助
|
||||
|
||||
** 使用在线帮助系统。 **
|
||||
|
||||
Neovim 有一个全面的在线帮助系统。
|
||||
|
||||
要开始使用,试试下面两种方法之一:
|
||||
|
||||
- 按下 `<F1>`{normal} 键(如果你有的话)
|
||||
- 输入 `:help`{vim}
|
||||
|
||||
阅读帮助窗口中的文本,了解帮助系统是如何工作的。
|
||||
输入 `<C-w><C-w>`{normal} 可以在窗口之间跳转。
|
||||
输入 `:q`{vim} 关闭帮助窗口。
|
||||
|
||||
你几乎可以找到任何主题的帮助,只需给 ":help" 命令传递一个参数。
|
||||
试试这些(别忘了按 <Enter>):
|
||||
~~~ cmd
|
||||
:help w
|
||||
:help c_CTRL-D
|
||||
:help insert-index
|
||||
:help user-manual
|
||||
~~~
|
||||
|
||||
# 第 7.2 课:补全功能
|
||||
|
||||
** 使用 `<C-d>`{normal} 和 `<Tab>`{normal} 进行命令行补全。 **
|
||||
|
||||
1. 列出当前目录的内容:`:!{unix:(ls),win:(dir)}`{vim}
|
||||
|
||||
2. 输入一个命令的开头:`:e`{vim}
|
||||
|
||||
3. 按下 `<C-d>`{normal},Neovim 会显示一个以 "e" 开头的命令列表。
|
||||
|
||||
4. 按下 `<Tab>`{normal},Neovim 会显示一个包含可能补全项的菜单
|
||||
(或者如果输入的命令是唯一的,则直接补全,例如 ":ed`<Tab>`{normal}" 会被补全为 ":edit")。
|
||||
|
||||
5. 使用 `<Tab>`{normal} 或 `<C-n>`{normal} 移动到下一个匹配项。或者
|
||||
使用 `<S-Tab>`{normal} 或 `<C-p>`{normal} 移动到上一个匹配项。
|
||||
|
||||
6. 选择 `edit`{vim} 条目。现在你可以看到 `edit`{vim} 这个词已经被自动插入到命令行了。
|
||||
|
||||
7. 现在加上一个空格和一个已存在文件名的开头:`:edit FIL`{vim}
|
||||
|
||||
8. 按下 `<Tab>`{normal}。Vim 会显示一个补全菜单,列出以 `FIL` 开头的文件名。
|
||||
|
||||
NOTE: 补全功能对许多命令都有效。它对 `:help`{vim} 命令尤其有用。
|
||||
|
||||
# 第 7.3 课:配置 NVIM
|
||||
|
||||
Neovim 是一个高度可配置的编辑器。你可以根据自己的需求进行定制。要开始使用更多功能,
|
||||
可以创建一个 vimrc 文件,如果你想用 Lua,文件名可以是 "init.lua",如果你想用
|
||||
Vimscript,文件名可以是 "init.vim"。在本课中,我们将使用 "init.lua"。
|
||||
|
||||
1. 开始编辑 "init.lua" 文件。
|
||||
|
||||
`:exe 'edit' stdpath('config')..'/init.lua'`{vim}
|
||||
|
||||
2. 将 Lua 的示例配置复制到你的 "init.lua" 文件中。
|
||||
|
||||
`:read $VIMRUNTIME/example_init.lua`{vim}
|
||||
|
||||
3. 写入文件(这也会创建任何缺失的父目录):
|
||||
|
||||
`:w ++p`{vim}
|
||||
|
||||
4. 下次你启动 Neovim 时,可以用以下命令快速打开这个 vimrc 文件:
|
||||
|
||||
`:e $MYVIMRC`{vim}
|
||||
|
||||
# 第 7 课总结
|
||||
|
||||
1. 输入 `:help`{vim}
|
||||
或按下 `<F1>`{normal} 或 `<Help>`{normal} 来打开一个帮助窗口。
|
||||
|
||||
2. 输入 `:help 主题`{vim} 来查找关于“主题”的帮助。
|
||||
|
||||
3. 输入 `<C-w><C-w>`{normal} 来跳转到另一个窗口。
|
||||
|
||||
4. 输入 `:q`{vim} 来关闭帮助窗口。
|
||||
|
||||
5. 在命令模式下,按 `<C-d>`{normal} 查看可能的补全。按 `<Tab>`{normal} 使用补全菜单并选择一个匹配项。
|
||||
|
||||
6. 创建你的配置文件来保存你的偏好设置。你可以用 `:e $MYVIMRC`{vim} 再次访问它。
|
||||
|
||||
# 接下来呢?
|
||||
|
||||
运行 `:help nvim-quickstart`{vim} 获取更多关于扩展 Nvim 的信息。
|
||||
|
||||
# 结语
|
||||
|
||||
这就是 Vim 教程第一章的全部内容。你可以继续学习[第二章](@tutor:vim-02-beginner)。
|
||||
|
||||
本教程旨在简要介绍 Neovim 编辑器,内容刚好足够让你能比较轻松地使用它。它远非完
|
||||
整,因为 Neovim 还有许许多多其他的命令。请经常查阅帮助文档。网上也有无数优秀的
|
||||
教程和视频可供学习。这里有一些推荐:
|
||||
|
||||
- *Learn Vim Progressively*:
|
||||
https://yannesposito.com/Scratch/en/blog/Learn-Vim-Progressively/
|
||||
- *Learning Vim in 2014*:
|
||||
https://benmccormick.org/learning-vim-in-2014/
|
||||
- *Vimcasts*:
|
||||
http://vimcasts.org/
|
||||
- *Vim Video-Tutorials by Derek Wyatt*:
|
||||
http://derekwyatt.org/vim/tutorials/
|
||||
- *Learn Vimscript the Hard Way*:
|
||||
https://learnvimscriptthehardway.stevelosh.com/
|
||||
- *7 Habits of Effective Text Editing*:
|
||||
https://www.moolenaar.net/habits.html
|
||||
- *vim-galore*:
|
||||
https://github.com/mhinz/vim-galore
|
||||
|
||||
如果你更喜欢书籍,Drew Neil 的 *Practical Vim* 经常被推荐
|
||||
(其续作 *Modern Vim* 包含了 Neovim 的特有内容)。
|
||||
|
||||
本教程由 Michael C. Pierce 和 Robert K. Ware(科罗拉多矿业大学)编写,采用了
|
||||
Charles Smith(科罗拉多州立大学)提供的想法。电子邮件: bware@mines.colorado.edu。
|
||||
|
||||
由 Bram Moolenaar 为 Vim 修改。
|
||||
由 Felipe Morales 为 vim-tutor-mode 修改。
|
||||
由 Rory Nesbitt 为 Neovim 修改。
|
||||
|
||||
Neovim Tutor 简体中文翻译版由 PilgrimLyieu <pilgrimlyieu@outlook.com> 译制并校对。
|
||||
|
||||
变更记录:
|
||||
- 2025-07-06 PilgrimLyieu <pilgrimlyieu@outlook.com>
|
||||
译制并校对
|
||||
|
||||
// vim: nowrap
|
43
runtime/tutor/zh/vim-01-beginner.tutor.json
Normal file
43
runtime/tutor/zh/vim-01-beginner.tutor.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"expect": {
|
||||
"96": "The cow jumped over the moon.",
|
||||
"97": "The cow jumped over the moon.",
|
||||
"117": "There is some text missing from this line.",
|
||||
"118": "There is some text missing from this line.",
|
||||
"135": "There is some text missing from this line.",
|
||||
"136": "There is some text missing from this line.",
|
||||
"137": "There is also some text missing here.",
|
||||
"138": "There is also some text missing here.",
|
||||
"204": "There are some words that don't belong in this sentence.",
|
||||
"205": "There are some words that don't belong in this sentence.",
|
||||
"221": "Somebody typed the end of this line twice.",
|
||||
"259": -1,
|
||||
"276": "This line of words is cleaned up.",
|
||||
"292": "1) Roses are red,",
|
||||
"293": "",
|
||||
"294": "3) Violets are blue,",
|
||||
"295": "",
|
||||
"296": "",
|
||||
"297": "6) Sugar is sweet",
|
||||
"298": "7) And so are you.",
|
||||
"318": "Fix the errors on this line and replace them with undo.",
|
||||
"319": "Fix the errors on this line and replace them with undo.",
|
||||
"384": "When this line was typed in, someone pressed some wrong keys!",
|
||||
"385": "When this line was typed in, someone pressed some wrong keys!",
|
||||
"405": "This line has a few words that need changing using the change operator.",
|
||||
"406": "This line has a few words that need changing using the change operator.",
|
||||
"426": "The end of this line needs to be corrected using the c$ command.",
|
||||
"427": "The end of this line needs to be corrected using the c$ command.",
|
||||
"484": -1,
|
||||
"502": -1,
|
||||
"524": "Usually the best time to see the flowers is in the spring.",
|
||||
"702": -1,
|
||||
"707": -1,
|
||||
"723": "This line will allow you to practice appending text to a line.",
|
||||
"724": "This line will allow you to practice appending text to a line.",
|
||||
"740": "Adding 123 to 456 gives you 579.",
|
||||
"741": "Adding 123 to 456 gives you 579.",
|
||||
"765": "a) This is the first item.",
|
||||
"766": "b) This is the second item."
|
||||
}
|
||||
}
|
189
runtime/tutor/zh/vim-02-beginner.tutor
Normal file
189
runtime/tutor/zh/vim-02-beginner.tutor
Normal file
@@ -0,0 +1,189 @@
|
||||
# 欢迎来到 Neovim 教程
|
||||
|
||||
# 第 2 章
|
||||
|
||||
此处有龙(拉丁语 Hic Sunt Dracones,表示有危险):如果这是您第一次接触 vim,并
|
||||
且您希望从入门章节开始,请在 Vim 编辑器的命令行中输入:
|
||||
~~~ cmd
|
||||
:Tutor vim-01-beginner
|
||||
~~~
|
||||
或者直接点击链接打开教程的[第一章](@tutor:vim-01-beginner)。
|
||||
|
||||
完成本章大约需要 8-10 分钟,具体取决于您在实践探索上花费的时间。
|
||||
|
||||
|
||||
# 第 2.1.1 课:命名寄存器
|
||||
|
||||
** 同时复制两个单词,然后分别粘贴它们 **
|
||||
|
||||
1. 将光标移动到下面标有 ✓ 的那一行。
|
||||
|
||||
2. 导航到 'Edward' 单词的任意位置,然后输入 `"ayiw`{normal}
|
||||
|
||||
**助记**:*将 (i)nner (w)ord(内部单词)(y)ank(复制)到名为 (a) 的寄存器(")中*
|
||||
|
||||
3. 向前导航到 'cookie' 单词(可以使用 `fk`{normal} 或 `2fc`{normal}
|
||||
或 `$2b`{normal} 或 `/co`{normal} `<Enter>`{normal}),然后输入 `"byiw`{normal}
|
||||
|
||||
4. 导航到 'Vince' 单词的任意位置,然后输入 `ciw<CTRL-r>a<ESC>`{normal}
|
||||
|
||||
**助记**:*用名为 (a) 的寄存器(<contents of (r)egister>)的内容 (c)hange (i)
|
||||
nner (w)ord(修改内部单词)*
|
||||
|
||||
5. 导航到 'cake' 单词的任意位置,然后输入 `ciw<CTRL-r>b<ESC>`{normal}
|
||||
|
||||
a) Edward will henceforth be in charge of the cookie rations
|
||||
b) In this capacity, Vince will have sole cake discretionary powers
|
||||
|
||||
NOTE: 删除操作同样可以存入寄存器,例如 `"sdiw`{normal} 会将被光标下的单词删除并存入寄存器 s。
|
||||
|
||||
参考:[寄存器](registers)
|
||||
[命名寄存器](quotea)
|
||||
[移动与文本对象](text-objects)
|
||||
[CTRL-R](i_CTRL-R)
|
||||
|
||||
|
||||
# 第 2.1.2 课:表达式寄存器
|
||||
|
||||
** 即时插入计算结果 **
|
||||
|
||||
1. 将光标移动到下面标有 ✗ 的那一行。
|
||||
|
||||
2. 导航到所给数字的任意位置。
|
||||
|
||||
3. 输入 `ciw<CTRL-r>=`{normal}60\*60\*24 `<Enter>`{normal}
|
||||
|
||||
4. 在下一行,进入插入模式,并使用
|
||||
`<CTRL-r>=`{normal}`system('date')`{vim} `<Enter>`{normal} 来添加今天的日期。
|
||||
|
||||
NOTE: 所有对 `system` 的调用都依赖于操作系统,例如在 Windows 上应使用
|
||||
`system('date /t')`{vim} 或 `:r!date /t`{vim}
|
||||
|
||||
I have forgotten the exact number of seconds in a day, is it 84600?
|
||||
Today's date is:
|
||||
|
||||
NOTE: 同样效果也可以通过 `:pu=`{normal}`system('date')`{vim} 实现,
|
||||
或者用更少的按键 `:r!date`{vim}
|
||||
|
||||
参考:[表达式寄存器](quote=)
|
||||
|
||||
|
||||
# 第 2.1.3 课:数字寄存器
|
||||
|
||||
** 按下 `yy`{normal} 和 `dd`{normal} 来观察它们对寄存器的影响 **
|
||||
|
||||
1. 将光标移动到下面标有 ✓ 的那一行。
|
||||
|
||||
2. 复制(yank)第 0 行,然后用 `:reg`{vim} `<Enter>`{normal} 查看寄存器。
|
||||
|
||||
3. 用 `"cdd`{normal} 删除第 0 行,然后再次查看寄存器。
|
||||
(你觉得第 0 行的内容会出现在哪里?)
|
||||
|
||||
4. 继续删除后续的每一行,并在每次删除后用 `:reg`{vim} 查看寄存器。
|
||||
|
||||
NOTE: 你应该会发现,当新的整行删除内容被添加进来时,之前删除的内容会在寄存器列
|
||||
表中依次下移。
|
||||
|
||||
5. 现在,按顺序 (p)aste(粘贴)以下寄存器中的内容:c, 7, 4, 8, 2。例如,使用 `"7p`{normal}
|
||||
|
||||
0. This
|
||||
9. wobble
|
||||
8. secret
|
||||
7. is
|
||||
6. on
|
||||
5. axis
|
||||
4. a
|
||||
3. war
|
||||
2. message
|
||||
1. tribute
|
||||
|
||||
|
||||
NOTE: 在数字寄存器中,整行删除(`dd`{normal})的内容比整行复制或涉及更小范围移动
|
||||
的删除操作“存活”得更久。
|
||||
|
||||
参考:[数字寄存器](quote0)
|
||||
|
||||
|
||||
# 第 2.1.4 课:标记之美
|
||||
|
||||
** 避免“码农式”的行号计算 **
|
||||
|
||||
NOTE: 在写代码时,一个常见的难题是移动大块的代码。
|
||||
下面的技巧可以帮助你避免进行行号计算,比如 `"a147d`{normal} 或 `:945,1091d a`{vim},
|
||||
甚至是更麻烦的先用 `i<CTRL-r>=`{normal}1091-945 `<Enter>`{normal} 计算行数。
|
||||
|
||||
1. 将光标移动到下面标有 ✓ 的那一行。
|
||||
|
||||
2. 跳转到函数的第一行,并用 `ma`{normal} 将其标记为 a。
|
||||
|
||||
NOTE: 光标在该行的确切位置并不重要!
|
||||
|
||||
3. 使用 `$%`{normal} 导航到行尾,然后再到代码块的末尾。
|
||||
|
||||
4. 使用 `"ad'a`{normal} 将该代码块删除并存入寄存器 a。
|
||||
|
||||
**助记**:*将从光标位置到包含标记(')(a) 的那一行的内容 (d)elete(删除)到名为 (a) 的寄存器(")中*
|
||||
|
||||
5. 在 BBB 和 CCC 之间用 `"ap`{normal} 粘贴该代码块。
|
||||
|
||||
NOTE: 多次练习这个操作以达到熟练:`ma$%"ad'a`{normal}
|
||||
|
||||
~~~ cmd
|
||||
AAA
|
||||
function itGotRealBigRealFast() {
|
||||
if ( somethingIsTrue ) {
|
||||
doIt()
|
||||
}
|
||||
// the taxonomy of our function has changed and it
|
||||
// no longer makes alphabetical sense in its current position
|
||||
|
||||
// imagine hundreds of lines of code
|
||||
|
||||
// naively you could navigate to the start and end and record or
|
||||
// remember each line number
|
||||
}
|
||||
BBB
|
||||
CCC
|
||||
~~~
|
||||
|
||||
NOTE: 标记和寄存器不共享命名空间,因此寄存器 a 和标记 a 是完全独立的。
|
||||
但寄存器和宏并非如此。
|
||||
|
||||
参考:[标记](marks)
|
||||
[标记移动](mark-motions)(' 和 \` 的区别)
|
||||
|
||||
|
||||
# 第 2.1 课总结
|
||||
|
||||
1. 将文本 存储(复制、删除)到 26 个寄存器(a-z)中,并从中提取(粘贴)出来。
|
||||
2. 从单词内的任意位置复制整个单词:`yiw`{normal}
|
||||
3. 从单词内的任意位置更改整个单词:`ciw`{normal}
|
||||
4. 在插入模式下直接从寄存器插入文本:`<CTRL-r>a`{normal}
|
||||
|
||||
5. 插入简单算术运算的结果:
|
||||
在插入模式下使用 `<CTRL-r>=`{normal}60\*60 `<Enter>`{normal}
|
||||
6. 插入系统调用的结果:
|
||||
在插入模式下使用 `<CTRL-r>=`{normal}`system('ls -1')`{vim}
|
||||
|
||||
7. 使用 `:reg`{vim} 查看寄存器。
|
||||
8. 了解整行删除 (`dd`{normal}) 的最终去向:在数字寄存器中,即从寄存器 1 到 9
|
||||
依次向下存放。要理解整行删除的内容在数字寄存器中比任何其他操作都保存得更久。
|
||||
9. 了解所有复制操作在数字寄存器中的最终去向,以及它们是多么“短暂易逝”。
|
||||
|
||||
10. 在普通模式下设置标记:`m[a-zA-Z0-9]`{normal}
|
||||
11. 按行移动到标记位置:`'`{normal}
|
||||
|
||||
|
||||
# 结语
|
||||
|
||||
Neovim 教程第二章到此结束。本教程仍在不断完善中。
|
||||
|
||||
本章由 Paul D. Parker 编写。
|
||||
|
||||
由 Restorer 为 vim-tutor-mode 修改。
|
||||
|
||||
简体中文翻译版由 PilgrimLyieu <pilgrimlyieu@outlook.com> 译制并校对。
|
||||
|
||||
变更记录:
|
||||
- 2025-07-07 PilgrimLyieu <pilgrimlyieu@outlook.com>
|
||||
译制并校对
|
10
runtime/tutor/zh/vim-02-beginner.tutor.json
Normal file
10
runtime/tutor/zh/vim-02-beginner.tutor.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"expect": {
|
||||
"35": -1,
|
||||
"36": "b) In this capacity, Edward will have sole cookie discretionary powers",
|
||||
"62": "I have forgotten the exact number of seconds in a day, is it 86400",
|
||||
"63": -1,
|
||||
"89": -1,
|
||||
"132": -1
|
||||
}
|
||||
}
|
@@ -636,6 +636,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Arena
|
||||
|
||||
garray_T capture_local;
|
||||
const int save_msg_silent = msg_silent;
|
||||
const bool save_redir_off = redir_off;
|
||||
garray_T * const save_capture_ga = capture_ga;
|
||||
const int save_msg_col = msg_col;
|
||||
|
||||
@@ -647,6 +648,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Arena
|
||||
TRY_WRAP(err, {
|
||||
if (opts->output) {
|
||||
msg_silent++;
|
||||
redir_off = false;
|
||||
msg_col = 0; // prevent leading spaces
|
||||
}
|
||||
|
||||
@@ -657,6 +659,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Arena
|
||||
if (opts->output) {
|
||||
capture_ga = save_capture_ga;
|
||||
msg_silent = save_msg_silent;
|
||||
redir_off = save_redir_off;
|
||||
// Put msg_col back where it was, since nothing should have been written.
|
||||
msg_col = save_msg_col;
|
||||
}
|
||||
@@ -1073,6 +1076,7 @@ void create_user_command(uint64_t channel_id, String name, Object command, Dict(
|
||||
goto err;
|
||||
});
|
||||
|
||||
argt |= EX_RANGE;
|
||||
if (addr_type_arg != ADDR_LINES) {
|
||||
argt |= EX_ZEROR;
|
||||
}
|
||||
|
@@ -72,6 +72,7 @@ Dict nvim_exec2(uint64_t channel_id, String src, Dict(exec_opts) *opts, Error *e
|
||||
String exec_impl(uint64_t channel_id, String src, Dict(exec_opts) *opts, Error *err)
|
||||
{
|
||||
const int save_msg_silent = msg_silent;
|
||||
const bool save_redir_off = redir_off;
|
||||
garray_T *const save_capture_ga = capture_ga;
|
||||
const int save_msg_col = msg_col;
|
||||
garray_T capture_local;
|
||||
@@ -83,6 +84,7 @@ String exec_impl(uint64_t channel_id, String src, Dict(exec_opts) *opts, Error *
|
||||
TRY_WRAP(err, {
|
||||
if (opts->output) {
|
||||
msg_silent++;
|
||||
redir_off = false;
|
||||
msg_col = 0; // prevent leading spaces
|
||||
}
|
||||
|
||||
@@ -92,6 +94,7 @@ String exec_impl(uint64_t channel_id, String src, Dict(exec_opts) *opts, Error *
|
||||
if (opts->output) {
|
||||
capture_ga = save_capture_ga;
|
||||
msg_silent = save_msg_silent;
|
||||
redir_off = save_redir_off;
|
||||
// Put msg_col back where it was, since nothing should have been written.
|
||||
msg_col = save_msg_col;
|
||||
}
|
||||
|
@@ -202,7 +202,8 @@
|
||||
/// the call.
|
||||
/// - fixed: If true when anchor is NW or SW, the float window
|
||||
/// would be kept fixed even if the window would be truncated.
|
||||
/// - hide: If true the floating window will be hidden.
|
||||
/// - hide: If true the floating window will be hidden and the cursor will be invisible when
|
||||
/// focused on it.
|
||||
/// - vertical: Split vertically |:vertical|.
|
||||
/// - split: Split direction: "left", "right", "above", "below".
|
||||
///
|
||||
|
@@ -679,10 +679,6 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool i
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disable buffer-updates for the current buffer.
|
||||
// No need to check `unload_buf`: in that case the function returned above.
|
||||
buf_updates_unload(buf, false);
|
||||
|
||||
if (win != NULL // Avoid bogus clang warning.
|
||||
&& win_valid_any_tab(win)
|
||||
&& win->w_buffer == buf) {
|
||||
@@ -789,6 +785,11 @@ void buf_freeall(buf_T *buf, int flags)
|
||||
bufref_T bufref;
|
||||
set_bufref(&bufref, buf);
|
||||
|
||||
buf_updates_unload(buf, false);
|
||||
if (!bufref_valid(&bufref)) {
|
||||
// on_detach callback deleted the buffer.
|
||||
return;
|
||||
}
|
||||
if ((buf->b_ml.ml_mfp != NULL)
|
||||
&& apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname, false, buf)
|
||||
&& !bufref_valid(&bufref)) {
|
||||
@@ -935,7 +936,7 @@ static void free_buffer_stuff(buf_T *buf, int free_flags)
|
||||
map_clear_mode(buf, MAP_ALL_MODES, true, true); // clear local abbrevs
|
||||
XFREE_CLEAR(buf->b_start_fenc);
|
||||
|
||||
buf_updates_unload(buf, false);
|
||||
buf_free_callbacks(buf);
|
||||
}
|
||||
|
||||
/// Go to another buffer. Handles the result of the ATTENTION dialog.
|
||||
@@ -1255,8 +1256,11 @@ static int do_buffer_ext(int action, int start, int dir, int count, int flags)
|
||||
buf = buf->b_next;
|
||||
}
|
||||
} else {
|
||||
const bool help_only = (flags & DOBUF_SKIPHELP) != 0 && buf->b_help;
|
||||
|
||||
bp = NULL;
|
||||
while (count > 0 || (!unload && !buf->b_p_bl && bp != buf)) {
|
||||
while (count > 0 || (bp != buf && !unload
|
||||
&& !(help_only ? buf->b_help : buf->b_p_bl))) {
|
||||
// remember the buffer where we start, we come back there when all
|
||||
// buffers are unlisted.
|
||||
if (bp == NULL) {
|
||||
@@ -1264,12 +1268,13 @@ static int do_buffer_ext(int action, int start, int dir, int count, int flags)
|
||||
}
|
||||
buf = dir == FORWARD ? (buf->b_next != NULL ? buf->b_next : firstbuf)
|
||||
: (buf->b_prev != NULL ? buf->b_prev : lastbuf);
|
||||
// Avoid non-help buffers if the starting point was a help buffer
|
||||
// and vice-versa.
|
||||
// Don't count unlisted buffers.
|
||||
// Avoid non-help buffers if the starting point was a non-help buffer and
|
||||
// vice-versa.
|
||||
if (unload
|
||||
|| (buf->b_p_bl
|
||||
&& ((flags & DOBUF_SKIPHELP) == 0 || buf->b_help == bp->b_help))) {
|
||||
|| (help_only
|
||||
? buf->b_help
|
||||
: (buf->b_p_bl && ((flags & DOBUF_SKIPHELP) == 0 || !buf->b_help)))) {
|
||||
count--;
|
||||
bp = NULL; // use this buffer as new starting point
|
||||
}
|
||||
|
@@ -740,23 +740,24 @@ static int buf_write_make_backup(char *fname, bool append, FileInfo *file_info_o
|
||||
// the ones from the original file.
|
||||
// First find a file name that doesn't exist yet (use some
|
||||
// arbitrary numbers).
|
||||
xstrlcpy(IObuff, fname, IOSIZE);
|
||||
size_t dirlen = (size_t)(path_tail(fname) - fname);
|
||||
assert(dirlen < MAXPATHL);
|
||||
char tmp_fname[MAXPATHL];
|
||||
xmemcpyz(tmp_fname, fname, dirlen);
|
||||
for (int i = 4913;; i += 123) {
|
||||
char *tail = path_tail(IObuff);
|
||||
size_t size = (size_t)(tail - IObuff);
|
||||
snprintf(tail, IOSIZE - size, "%d", i);
|
||||
if (!os_fileinfo_link(IObuff, &file_info)) {
|
||||
snprintf(tmp_fname + dirlen, sizeof(tmp_fname) - dirlen, "%d", i);
|
||||
if (!os_fileinfo_link(tmp_fname, &file_info)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
int fd = os_open(IObuff,
|
||||
int fd = os_open(tmp_fname,
|
||||
O_CREAT|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
|
||||
if (fd < 0) { // can't write in directory
|
||||
*backup_copyp = true;
|
||||
} else {
|
||||
#ifdef UNIX
|
||||
os_fchown(fd, (uv_uid_t)file_info_old->stat.st_uid, (uv_gid_t)file_info_old->stat.st_gid);
|
||||
if (!os_fileinfo(IObuff, &file_info)
|
||||
if (!os_fileinfo(tmp_fname, &file_info)
|
||||
|| file_info.stat.st_uid != file_info_old->stat.st_uid
|
||||
|| file_info.stat.st_gid != file_info_old->stat.st_gid
|
||||
|| (int)file_info.stat.st_mode != perm) {
|
||||
@@ -766,7 +767,7 @@ static int buf_write_make_backup(char *fname, bool append, FileInfo *file_info_o
|
||||
// Close the file before removing it, on MS-Windows we
|
||||
// can't delete an open file.
|
||||
close(fd);
|
||||
os_remove(IObuff);
|
||||
os_remove(tmp_fname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1887,6 +1887,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, expa
|
||||
case CMD_tab:
|
||||
case CMD_tabdo:
|
||||
case CMD_topleft:
|
||||
case CMD_unsilent:
|
||||
case CMD_verbose:
|
||||
case CMD_vertical:
|
||||
case CMD_windo:
|
||||
@@ -2152,7 +2153,6 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, expa
|
||||
break;
|
||||
case CMD_checkhealth:
|
||||
xp->xp_context = EXPAND_CHECKHEALTH;
|
||||
xp->xp_pattern = (char *)arg;
|
||||
break;
|
||||
|
||||
case CMD_messages:
|
||||
|
@@ -87,10 +87,10 @@ static bool diff_need_update = false; // ex_diffupdate needs to be called
|
||||
#define DIFF_FOLLOWWRAP 0x800 // follow the wrap option
|
||||
#define DIFF_LINEMATCH 0x1000 // match most similar lines within diff
|
||||
#define ALL_WHITE_DIFF (DIFF_IWHITE | DIFF_IWHITEALL | DIFF_IWHITEEOL)
|
||||
static int diff_flags = DIFF_INTERNAL | DIFF_FILLER | DIFF_CLOSE_OFF;
|
||||
static int diff_flags = DIFF_INTERNAL | DIFF_FILLER | DIFF_CLOSE_OFF | DIFF_LINEMATCH;
|
||||
|
||||
static int diff_algorithm = 0;
|
||||
static int linematch_lines = 0;
|
||||
static int linematch_lines = 40;
|
||||
|
||||
#define LBUFLEN 50 // length of line in diff file
|
||||
|
||||
|
@@ -272,7 +272,7 @@ void screenclear(void)
|
||||
/// to be re-emitted: avoid clearing the prompt from the message grid.
|
||||
static bool cmdline_number_prompt(void)
|
||||
{
|
||||
return !ui_has(kUIMessages) && State == MODE_CMDLINE && get_cmdline_info()->mouse_used != NULL;
|
||||
return !ui_has(kUIMessages) && (State & MODE_CMDLINE) && get_cmdline_info()->mouse_used != NULL;
|
||||
}
|
||||
|
||||
/// Set dimensions of the Nvim application "screen".
|
||||
@@ -374,8 +374,8 @@ void screen_resize(int width, int height)
|
||||
// - in Ex mode, don't redraw anything.
|
||||
// - Otherwise, redraw right now, and position the cursor.
|
||||
if (State == MODE_ASKMORE || State == MODE_EXTERNCMD || exmode_active
|
||||
|| (State == MODE_CMDLINE && get_cmdline_info()->one_key)) {
|
||||
if (State == MODE_CMDLINE) {
|
||||
|| ((State & MODE_CMDLINE) && get_cmdline_info()->one_key)) {
|
||||
if (State & MODE_CMDLINE) {
|
||||
update_screen();
|
||||
}
|
||||
if (msg_grid.chars) {
|
||||
|
@@ -2552,7 +2552,6 @@ void clear_evalarg(evalarg_T *evalarg, exarg_T *eap)
|
||||
/// Handle zero level expression.
|
||||
/// This calls eval1() and handles error message and nextcmd.
|
||||
/// Put the result in "rettv" when returning OK and "evaluate" is true.
|
||||
/// Note: "rettv.v_lock" is not set.
|
||||
///
|
||||
/// @param evalarg can be NULL, &EVALARG_EVALUATE or a pointer.
|
||||
///
|
||||
@@ -2649,11 +2648,11 @@ int eval0_simple_funccal(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *co
|
||||
/// "arg" must point to the first non-white of the expression.
|
||||
/// "arg" is advanced to the next non-white after the recognized expression.
|
||||
///
|
||||
/// Note: "rettv.v_lock" is not set.
|
||||
///
|
||||
/// @return OK or FAIL.
|
||||
int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg)
|
||||
{
|
||||
CLEAR_POINTER(rettv);
|
||||
|
||||
// Get the first variable.
|
||||
if (eval2(arg, rettv, evalarg) == FAIL) {
|
||||
return FAIL;
|
||||
|
@@ -2408,19 +2408,14 @@ static int get_function_body(exarg_T *eap, garray_T *newlines, char *line_arg_in
|
||||
}
|
||||
|
||||
// Check for ":append", ":change", ":insert".
|
||||
p = skip_range(p, NULL);
|
||||
if ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p'))
|
||||
|| (p[0] == 'c'
|
||||
&& (!ASCII_ISALPHA(p[1])
|
||||
|| (p[1] == 'h' && (!ASCII_ISALPHA(p[2])
|
||||
|| (p[2] == 'a'
|
||||
&& (strncmp(&p[3], "nge", 3) != 0
|
||||
|| !ASCII_ISALPHA(p[6])))))))
|
||||
|| (p[0] == 'i'
|
||||
&& (!ASCII_ISALPHA(p[1]) || (p[1] == 'n'
|
||||
&& (!ASCII_ISALPHA(p[2])
|
||||
|| (p[2] == 's')))))) {
|
||||
char *const tp = p = skip_range(p, NULL);
|
||||
if ((checkforcmd(&p, "append", 1)
|
||||
|| checkforcmd(&p, "change", 1)
|
||||
|| checkforcmd(&p, "insert", 1))
|
||||
&& (*p == '!' || *p == '|' || ascii_iswhite_nl_or_nul(*p))) {
|
||||
skip_until = xmemdupz(".", 1);
|
||||
} else {
|
||||
p = tp;
|
||||
}
|
||||
|
||||
// heredoc: Check for ":python <<EOF", ":lua <<EOF", etc.
|
||||
|
@@ -2512,13 +2512,11 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
|
||||
goto theend;
|
||||
}
|
||||
u_unchanged(curbuf);
|
||||
buf_updates_unload(curbuf, false);
|
||||
buf_freeall(curbuf, BFA_KEEP_UNDO);
|
||||
|
||||
// Tell readfile() not to clear or reload undo info.
|
||||
readfile_flags = READ_KEEP_UNDO;
|
||||
} else {
|
||||
buf_updates_unload(curbuf, false);
|
||||
buf_freeall(curbuf, 0); // Free all things for buffer.
|
||||
}
|
||||
// If autocommands deleted the buffer we were going to re-edit, give
|
||||
|
@@ -3023,13 +3023,16 @@ static void append_command(const char *cmd)
|
||||
}
|
||||
|
||||
/// Return true and set "*idx" if "p" points to a one letter command.
|
||||
/// - The 'k' command can directly be followed by any character.
|
||||
/// - The 'k' command can directly be followed by any character
|
||||
/// but :keepa[lt] is another command, as are :keepj[umps],
|
||||
/// :kee[pmarks] and :keepp[atterns].
|
||||
/// - The 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r'
|
||||
/// but :sre[wind] is another command, as are :scr[iptnames],
|
||||
/// :scs[cope], :sim[alt], :sig[ns] and :sil[ent].
|
||||
static int one_letter_cmd(const char *p, cmdidx_T *idx)
|
||||
{
|
||||
if (*p == 'k') {
|
||||
if (p[0] == 'k'
|
||||
&& (p[1] != 'e' || (p[1] == 'e' && p[2] != 'e'))) {
|
||||
*idx = CMD_k;
|
||||
return true;
|
||||
}
|
||||
@@ -3062,6 +3065,9 @@ char *find_ex_command(exarg_T *eap, int *full)
|
||||
char *p = eap->cmd;
|
||||
if (one_letter_cmd(p, &eap->cmdidx)) {
|
||||
p++;
|
||||
if (full != NULL) {
|
||||
*full = true;
|
||||
}
|
||||
} else {
|
||||
while (ASCII_ISALPHA(*p)) {
|
||||
p++;
|
||||
|
@@ -1470,6 +1470,17 @@ static int may_do_command_line_next_incsearch(int firstc, int count, incsearch_s
|
||||
pat = ccline.cmdbuff + skiplen;
|
||||
}
|
||||
|
||||
bool bslsh = false;
|
||||
// do not search for the search end delimiter,
|
||||
// unless it is part of the pattern
|
||||
if (patlen > 2 && firstc == pat[patlen - 1]) {
|
||||
patlen--;
|
||||
if (pat[patlen - 1] == '\\') {
|
||||
pat[patlen - 1] = (char)(uint8_t)firstc;
|
||||
bslsh = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (next_match) {
|
||||
t = s->match_end;
|
||||
if (lt(s->match_start, s->match_end)) {
|
||||
@@ -1493,6 +1504,9 @@ static int may_do_command_line_next_incsearch(int firstc, int count, incsearch_s
|
||||
RE_SEARCH, NULL);
|
||||
emsg_off--;
|
||||
pat[patlen] = save;
|
||||
if (bslsh) {
|
||||
pat[patlen - 1] = '\\';
|
||||
}
|
||||
ui_busy_stop();
|
||||
if (found) {
|
||||
s->search_start = s->match_start;
|
||||
|
@@ -1493,7 +1493,7 @@ int typval_exec_lua_callable(LuaRef lua_cb, int argcount, typval_T *argvars, typ
|
||||
PUSH_ALL_TYPVALS(lstate, argvars, argcount, false);
|
||||
|
||||
if (nlua_pcall(lstate, argcount, 1)) {
|
||||
nlua_print(lstate);
|
||||
nlua_error(lstate, _("Lua callback: %.*s"));
|
||||
return FCERR_OTHER;
|
||||
}
|
||||
|
||||
|
@@ -565,7 +565,7 @@ static int nlua_foldupdate(lua_State *lstate)
|
||||
}
|
||||
// input is zero-based end-exclusive range
|
||||
linenr_T top = (linenr_T)luaL_checkinteger(lstate, 2) + 1;
|
||||
if (top < 1 || top > win->w_buffer->b_ml.ml_line_count) {
|
||||
if (top < 1) {
|
||||
return luaL_error(lstate, "invalid top");
|
||||
}
|
||||
linenr_T bot = (linenr_T)luaL_checkinteger(lstate, 3);
|
||||
|
@@ -3062,7 +3062,7 @@ void repeat_message(void)
|
||||
if (State == MODE_ASKMORE) {
|
||||
msg_moremsg(true); // display --more-- message again
|
||||
msg_row = Rows - 1;
|
||||
} else if (State == MODE_CMDLINE && confirm_msg != NULL) {
|
||||
} else if ((State & MODE_CMDLINE) && confirm_msg != NULL) {
|
||||
display_confirm_msg(); // display ":confirm" message again
|
||||
msg_row = Rows - 1;
|
||||
} else if (State == MODE_EXTERNCMD) {
|
||||
|
@@ -6615,10 +6615,17 @@ void finish_yankreg_from_object(yankreg_T *reg, bool clipboard_adjust)
|
||||
}
|
||||
}
|
||||
|
||||
update_yankreg_width(reg);
|
||||
}
|
||||
|
||||
/// Updates the "y_width" of a blockwise register based on its contents.
|
||||
/// Do nothing on a non-blockwise register.
|
||||
static void update_yankreg_width(yankreg_T *reg)
|
||||
{
|
||||
if (reg->y_type == kMTBlockWise) {
|
||||
size_t maxlen = 0;
|
||||
for (size_t i = 0; i < reg->y_size; i++) {
|
||||
size_t rowlen = reg->y_array[i].size;
|
||||
size_t rowlen = mb_string2cells_len(reg->y_array[i].data, reg->y_array[i].size);
|
||||
maxlen = MAX(maxlen, rowlen);
|
||||
}
|
||||
assert(maxlen <= INT_MAX);
|
||||
@@ -6720,15 +6727,7 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
|
||||
}
|
||||
}
|
||||
|
||||
if (reg->y_type == kMTBlockWise) {
|
||||
size_t maxlen = 0;
|
||||
for (size_t i = 0; i < reg->y_size; i++) {
|
||||
size_t rowlen = reg->y_array[i].size;
|
||||
maxlen = MAX(maxlen, rowlen);
|
||||
}
|
||||
assert(maxlen <= INT_MAX);
|
||||
reg->y_width = (int)maxlen - 1;
|
||||
}
|
||||
update_yankreg_width(reg);
|
||||
|
||||
*target = reg;
|
||||
return true;
|
||||
|
@@ -139,10 +139,10 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
|
||||
// To keep the code simple, we only allow changing the
|
||||
// draw mode when the popup menu is not being displayed
|
||||
pum_external = ui_has(kUIPopupmenu)
|
||||
|| (State == MODE_CMDLINE && ui_has(kUIWildmenu));
|
||||
|| ((State & MODE_CMDLINE) && ui_has(kUIWildmenu));
|
||||
}
|
||||
|
||||
pum_rl = State != MODE_CMDLINE && curwin->w_p_rl;
|
||||
pum_rl = (State & MODE_CMDLINE) == 0 && curwin->w_p_rl;
|
||||
|
||||
do {
|
||||
// Mark the pum as visible already here,
|
||||
@@ -154,7 +154,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
|
||||
int below_row = cmdline_row;
|
||||
|
||||
// wildoptions=pum
|
||||
if (State == MODE_CMDLINE) {
|
||||
if (State & MODE_CMDLINE) {
|
||||
pum_win_row = ui_has(kUICmdline) ? 0 : cmdline_row;
|
||||
cursor_col = cmd_startcol;
|
||||
pum_anchor_grid = ui_has(kUICmdline) ? -1 : DEFAULT_GRID_HANDLE;
|
||||
@@ -244,7 +244,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
|
||||
// pum above "pum_win_row"
|
||||
pum_above = true;
|
||||
|
||||
if (State == MODE_CMDLINE) {
|
||||
if (State & MODE_CMDLINE) {
|
||||
// for cmdline pum, no need for context lines
|
||||
context_lines = 0;
|
||||
} else {
|
||||
@@ -268,7 +268,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
|
||||
// pum below "pum_win_row"
|
||||
pum_above = false;
|
||||
|
||||
if (State == MODE_CMDLINE) {
|
||||
if (State & MODE_CMDLINE) {
|
||||
// for cmdline pum, no need for context lines
|
||||
context_lines = 0;
|
||||
} else {
|
||||
@@ -404,7 +404,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
|
||||
// room the window size will keep changing.
|
||||
} while (pum_set_selected(selected, redo_count) && ++redo_count <= 2);
|
||||
|
||||
pum_grid.zindex = (State == MODE_CMDLINE) ? kZIndexCmdlinePopupMenu : kZIndexPopupMenu;
|
||||
pum_grid.zindex = (State & MODE_CMDLINE) ? kZIndexCmdlinePopupMenu : kZIndexPopupMenu;
|
||||
pum_redraw();
|
||||
}
|
||||
|
||||
@@ -418,15 +418,15 @@ static int *pum_compute_text_attrs(char *text, hlf_T hlf, int user_hlattr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *leader = State == MODE_CMDLINE ? cmdline_compl_pattern()
|
||||
: ins_compl_leader();
|
||||
char *leader = (State & MODE_CMDLINE) ? cmdline_compl_pattern()
|
||||
: ins_compl_leader();
|
||||
if (leader == NULL || *leader == NUL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int *attrs = xmalloc(sizeof(int) * (size_t)vim_strsize(text));
|
||||
bool in_fuzzy = State == MODE_CMDLINE ? cmdline_compl_is_fuzzy()
|
||||
: (get_cot_flags() & kOptCotFlagFuzzy) != 0;
|
||||
bool in_fuzzy = (State & MODE_CMDLINE) ? cmdline_compl_is_fuzzy()
|
||||
: (get_cot_flags() & kOptCotFlagFuzzy) != 0;
|
||||
size_t leader_len = strlen(leader);
|
||||
|
||||
garray_T *ga = NULL;
|
||||
|
@@ -1582,6 +1582,7 @@ static void reg_nextline(void)
|
||||
// Returns RA_FAIL, RA_NOMATCH or RA_MATCH.
|
||||
// If "bytelen" is not NULL, it is set to the byte length of the match in the
|
||||
// last line.
|
||||
// Optional: ignore case if rex.reg_ic is set.
|
||||
static int match_with_backref(linenr_T start_lnum, colnr_T start_col, linenr_T end_lnum,
|
||||
colnr_T end_col, int *bytelen)
|
||||
{
|
||||
@@ -1619,7 +1620,8 @@ static int match_with_backref(linenr_T start_lnum, colnr_T start_col, linenr_T e
|
||||
len = reg_getline_len(clnum) - ccol;
|
||||
}
|
||||
|
||||
if (cstrncmp(p + ccol, (char *)rex.input, &len) != 0) {
|
||||
if ((!rex.reg_ic && cstrncmp(p + ccol, (char *)rex.input, &len) != 0)
|
||||
|| (rex.reg_ic && mb_strnicmp(p + ccol, (char *)rex.input, (size_t)len) != 0)) {
|
||||
return RA_NOMATCH; // doesn't match
|
||||
}
|
||||
if (bytelen != NULL) {
|
||||
@@ -1727,7 +1729,8 @@ static void mb_decompose(int c, int *c1, int *c2, int *c3)
|
||||
*c3 = d.c;
|
||||
} else {
|
||||
*c1 = c;
|
||||
*c2 = *c3 = 0;
|
||||
*c2 = 0;
|
||||
*c3 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1213,13 +1213,10 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, size_t patlen
|
||||
} else if (!spats[0].off.line) {
|
||||
off_buf[off_len++] = 's';
|
||||
}
|
||||
if (spats[0].off.off > 0 || spats[0].off.line) {
|
||||
off_buf[off_len++] = '+';
|
||||
}
|
||||
off_buf[off_len] = NUL;
|
||||
if (spats[0].off.off != 0 || spats[0].off.line) {
|
||||
off_len += (size_t)snprintf(off_buf + off_len, sizeof(off_buf) - off_len,
|
||||
"%" PRId64, spats[0].off.off);
|
||||
"%+" PRId64, spats[0].off.off);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -185,11 +185,11 @@ void get_mode(char *buf)
|
||||
int i = 0;
|
||||
|
||||
if (State == MODE_HITRETURN || State == MODE_ASKMORE || State == MODE_SETWSIZE
|
||||
|| (State == MODE_CMDLINE && get_cmdline_info()->one_key)) {
|
||||
|| ((State & MODE_CMDLINE) && get_cmdline_info()->one_key)) {
|
||||
buf[i++] = 'r';
|
||||
if (State == MODE_ASKMORE) {
|
||||
buf[i++] = 'm';
|
||||
} else if (State == MODE_CMDLINE) {
|
||||
} else if (State & MODE_CMDLINE) {
|
||||
buf[i++] = '?';
|
||||
}
|
||||
} else if (State == MODE_EXTERNCMD) {
|
||||
|
@@ -113,7 +113,15 @@ struct TUIData {
|
||||
bool set_cursor_color_as_str;
|
||||
bool cursor_has_color;
|
||||
bool is_starting;
|
||||
bool did_set_grapheme_cluster_mode;
|
||||
bool resize_events_enabled;
|
||||
|
||||
// Terminal modes that Nvim enabled that it must disable on exit
|
||||
struct {
|
||||
bool grapheme_clusters : 1;
|
||||
bool theme_updates : 1;
|
||||
bool resize_events : 1;
|
||||
} modes;
|
||||
|
||||
FILE *screenshot;
|
||||
cursorentry_T cursor_shapes[SHAPE_IDX_COUNT];
|
||||
HlAttrs clear_attrs;
|
||||
@@ -246,15 +254,25 @@ void tui_handle_term_mode(TUIData *tui, TermMode mode, TermModeState state)
|
||||
case kTermModeGraphemeClusters:
|
||||
if (!is_set) {
|
||||
tui_set_term_mode(tui, mode, true);
|
||||
tui->did_set_grapheme_cluster_mode = true;
|
||||
tui->modes.grapheme_clusters = true;
|
||||
}
|
||||
break;
|
||||
case kTermModeThemeUpdates:
|
||||
tui_set_term_mode(tui, mode, true);
|
||||
if (!is_set) {
|
||||
tui_set_term_mode(tui, mode, true);
|
||||
tui->modes.theme_updates = true;
|
||||
}
|
||||
break;
|
||||
case kTermModeResizeEvents:
|
||||
signal_watcher_stop(&tui->winch_handle);
|
||||
tui_set_term_mode(tui, mode, true);
|
||||
if (!is_set) {
|
||||
tui_set_term_mode(tui, mode, true);
|
||||
tui->modes.resize_events = true;
|
||||
}
|
||||
|
||||
// We track both whether the mode is enabled AND if Nvim was the one to enable it
|
||||
tui->resize_events_enabled = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -358,7 +376,10 @@ static void terminfo_start(TUIData *tui)
|
||||
tui->overflow = false;
|
||||
tui->set_cursor_color_as_str = false;
|
||||
tui->cursor_has_color = false;
|
||||
tui->did_set_grapheme_cluster_mode = false;
|
||||
tui->resize_events_enabled = false;
|
||||
tui->modes.grapheme_clusters = false;
|
||||
tui->modes.resize_events = false;
|
||||
tui->modes.theme_updates = false;
|
||||
tui->showing_mode = SHAPE_IDX_N;
|
||||
tui->unibi_ext.enable_mouse = -1;
|
||||
tui->unibi_ext.disable_mouse = -1;
|
||||
@@ -519,7 +540,9 @@ static void terminfo_disable(TUIData *tui)
|
||||
{
|
||||
// Disable theme update notifications. We do this first to avoid getting any
|
||||
// more notifications after we reset the cursor and any color palette changes.
|
||||
tui_set_term_mode(tui, kTermModeThemeUpdates, false);
|
||||
if (tui->modes.theme_updates) {
|
||||
tui_set_term_mode(tui, kTermModeThemeUpdates, false);
|
||||
}
|
||||
|
||||
// Destroy output stuff
|
||||
tui_mode_change(tui, NULL_STRING, SHAPE_IDX_N);
|
||||
@@ -532,9 +555,12 @@ static void terminfo_disable(TUIData *tui)
|
||||
// Reset the key encoding
|
||||
tui_reset_key_encoding(tui);
|
||||
|
||||
// Disable resize events
|
||||
tui_set_term_mode(tui, kTermModeResizeEvents, false);
|
||||
if (tui->did_set_grapheme_cluster_mode) {
|
||||
// Disable terminal modes that we enabled
|
||||
if (tui->modes.resize_events) {
|
||||
tui_set_term_mode(tui, kTermModeResizeEvents, false);
|
||||
}
|
||||
|
||||
if (tui->modes.grapheme_clusters) {
|
||||
tui_set_term_mode(tui, kTermModeGraphemeClusters, false);
|
||||
}
|
||||
|
||||
@@ -680,7 +706,7 @@ static void sigwinch_cb(SignalWatcher *watcher, int signum, void *cbdata)
|
||||
{
|
||||
got_winch++;
|
||||
TUIData *tui = cbdata;
|
||||
if (tui_is_stopped(tui)) {
|
||||
if (tui_is_stopped(tui) || tui->resize_events_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -536,7 +536,21 @@ void ui_flush(void)
|
||||
if (!ui_active()) {
|
||||
return;
|
||||
}
|
||||
|
||||
static bool was_busy = false;
|
||||
|
||||
cmdline_ui_flush();
|
||||
|
||||
if (!(State & MODE_CMDLINE) && curwin->w_floating && curwin->w_config.hide) {
|
||||
if (!was_busy) {
|
||||
ui_call_busy_start();
|
||||
was_busy = true;
|
||||
}
|
||||
} else if (was_busy) {
|
||||
ui_call_busy_stop();
|
||||
was_busy = false;
|
||||
}
|
||||
|
||||
win_ui_flush(false);
|
||||
msg_ext_ui_flush();
|
||||
msg_scroll_flush();
|
||||
|
@@ -782,6 +782,13 @@ describe('nvim_create_user_command', function()
|
||||
eq(5, #api.nvim_list_tabpages())
|
||||
eq(1, fn.tabpagenr())
|
||||
end)
|
||||
|
||||
it('"addr" flag allows ranges without explicit "range" flag', function()
|
||||
exec_lua([[
|
||||
vim.api.nvim_create_user_command('Test2', 'echo "hello <line1><line2>"', { addr = 'other' })
|
||||
]])
|
||||
eq('hello 12', n.exec_capture('1,2Test2'))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('nvim_del_user_command', function()
|
||||
|
@@ -385,6 +385,9 @@ describe('API', function()
|
||||
)
|
||||
eq({ output = '' }, api.nvim_exec2('echo', { output = true }))
|
||||
eq({ output = 'foo 42' }, api.nvim_exec2('echo "foo" 42', { output = true }))
|
||||
-- Returns output in cmdline mode #35321
|
||||
feed(':')
|
||||
eq({ output = 'foo 42' }, api.nvim_exec2('echo "foo" 42', { output = true }))
|
||||
end)
|
||||
|
||||
it('displays messages when opts.output=false', function()
|
||||
@@ -1586,6 +1589,17 @@ describe('API', function()
|
||||
eq("Invalid 'type': 'bx'", pcall_err(api.nvim_put, { 'xxx', 'yyy' }, 'bx', false, true))
|
||||
eq("Invalid 'type': 'b3x'", pcall_err(api.nvim_put, { 'xxx', 'yyy' }, 'b3x', false, true))
|
||||
end)
|
||||
|
||||
it('computes block width correctly when not specified #35034', function()
|
||||
api.nvim_put({ 'line 1', 'line 2', 'line 3' }, 'l', false, false)
|
||||
-- block width should be 4
|
||||
api.nvim_put({ 'あい', 'xxx', 'xx' }, 'b', false, false)
|
||||
expect([[
|
||||
あいline 1
|
||||
xxx line 2
|
||||
xx line 3
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('nvim_strwidth', function()
|
||||
@@ -4887,6 +4901,9 @@ describe('API', function()
|
||||
|
||||
it('captures output', function()
|
||||
eq('foo', api.nvim_cmd({ cmd = 'echo', args = { '"foo"' } }, { output = true }))
|
||||
-- Returns output in cmdline mode #35321
|
||||
feed(':')
|
||||
eq('foo', api.nvim_cmd({ cmd = 'echo', args = { '"foo"' } }, { output = true }))
|
||||
end)
|
||||
|
||||
it('sets correct script context', function()
|
||||
|
@@ -814,6 +814,42 @@ describe('startup', function()
|
||||
]])
|
||||
end)
|
||||
|
||||
it("default 'diffopt' is applied with -d", function()
|
||||
clear({
|
||||
args = {
|
||||
'-d',
|
||||
'test/functional/fixtures/diff/startup_old.txt',
|
||||
'test/functional/fixtures/diff/startup_new.txt',
|
||||
'--cmd',
|
||||
'set laststatus=0',
|
||||
},
|
||||
})
|
||||
local screen = Screen.new(80, 24)
|
||||
screen:expect([[
|
||||
{7:+ }{13:^+-- 15 lines: package main············}│{7:+ }{13:+-- 15 lines: package main···········}|
|
||||
{7: } │{7: } |
|
||||
{7: }func printCharacters(str string) strin│{7: }func printCharacters(str string) stri|
|
||||
{7: } return str │{7: } return str |
|
||||
{7: }} │{7: }} |
|
||||
{7: } │{7: } |
|
||||
{7: }func main() { │{7: }func main() { |
|
||||
{7: }{23:--------------------------------------}│{7: }{22: hello := "Hello, World!" }|
|
||||
{7: }{23:--------------------------------------}│{7: }{22: }|
|
||||
{7: }{4: fmt.Print}{27:Ln}{4:(compressString("aa}│{7: }{4: fmt.Print(compressString("aaa}|
|
||||
{7: }{4: fmt.Print}{27:Ln}{4:(compressString("go}│{7: }{4: fmt.Print(compressString("gol}|
|
||||
{7: }{4: fmt.Print}{27:Ln}{4:(removeDuplicate("a}│{7: }{4: fmt.Print(removeDuplicate("aa}|
|
||||
{7: }{4: fmt.Print}{27:Ln}{4:(reverseAndDouble("}│{7: }{4: fmt.Print(reverseAndDouble("a}|
|
||||
{7: }{4: fmt.Print}{27:Ln}{4:(printCharacters("g}│{7: }{4: fmt.Print(printCharacters("go}|
|
||||
{7: }{23:--------------------------------------}│{7: }{22: }|
|
||||
{7: }{23:--------------------------------------}│{7: }{22: fmt.Println(hello) }|
|
||||
{7: }} │{7: }} |
|
||||
{1:~ }│{1:~ }|*6
|
||||
|
|
||||
]])
|
||||
command('let &diffopt = &diffopt')
|
||||
screen:expect_unchanged()
|
||||
end)
|
||||
|
||||
it('does not crash if --embed is given twice', function()
|
||||
clear { args = { '--embed' } }
|
||||
assert_alive()
|
||||
|
31
test/functional/fixtures/diff/startup_new.txt
Normal file
31
test/functional/fixtures/diff/startup_new.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func compressString(str string) string {
|
||||
return str
|
||||
}
|
||||
|
||||
func removeDuplicate(str string) string {
|
||||
return str
|
||||
}
|
||||
|
||||
func reverseAndDouble(str string) string {
|
||||
return str
|
||||
}
|
||||
|
||||
func printCharacters(str string) string {
|
||||
return str
|
||||
}
|
||||
|
||||
func main() {
|
||||
hello := "Hello, World!"
|
||||
|
||||
fmt.Print(compressString("aaaabbccgh"))
|
||||
fmt.Print(compressString("golang"))
|
||||
fmt.Print(removeDuplicate("aabccchbbccaaa"))
|
||||
fmt.Print(reverseAndDouble("abcdfgh"))
|
||||
fmt.Print(printCharacters("golang"))
|
||||
|
||||
fmt.Println(hello)
|
||||
}
|
27
test/functional/fixtures/diff/startup_old.txt
Normal file
27
test/functional/fixtures/diff/startup_old.txt
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func compressString(str string) string {
|
||||
return str
|
||||
}
|
||||
|
||||
func removeDuplicate(str string) string {
|
||||
return str
|
||||
}
|
||||
|
||||
func reverseAndDouble(str string) string {
|
||||
return str
|
||||
}
|
||||
|
||||
func printCharacters(str string) string {
|
||||
return str
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.PrintLn(compressString("aaaabbccgh"))
|
||||
fmt.PrintLn(compressString("golang"))
|
||||
fmt.PrintLn(removeDuplicate("aabccchbbccaaa"))
|
||||
fmt.PrintLn(reverseAndDouble("abcdfgh"))
|
||||
fmt.PrintLn(printCharacters("golang"))
|
||||
}
|
@@ -731,6 +731,111 @@ describe('search cmdline', function()
|
||||
feed('<Esc>')
|
||||
screen:expect(s)
|
||||
end)
|
||||
|
||||
-- oldtest: Test_incsearch_delimiter_ctrlg()
|
||||
it('incsearch does not include trailing delimiter', function()
|
||||
screen:try_resize(20, 6)
|
||||
exec([[
|
||||
set incsearch hls
|
||||
call setline(1, ["1 vim inc", "2 vim /", "3 vim /", "4 vim ?", "5 vim ?"])
|
||||
normal gg
|
||||
redraw
|
||||
]])
|
||||
|
||||
feed('/')
|
||||
poke_eventloop()
|
||||
feed('v')
|
||||
poke_eventloop()
|
||||
feed('i')
|
||||
poke_eventloop()
|
||||
feed('m')
|
||||
poke_eventloop()
|
||||
feed(' ')
|
||||
poke_eventloop()
|
||||
feed('/')
|
||||
poke_eventloop()
|
||||
feed('<C-G>')
|
||||
screen:expect([[
|
||||
1 {10:vim }inc |
|
||||
2 {2:vim }/ |
|
||||
3 {10:vim }/ |
|
||||
4 {10:vim }? |
|
||||
5 {10:vim }? |
|
||||
/vim /^ |
|
||||
]])
|
||||
feed('<Esc>')
|
||||
|
||||
command('5')
|
||||
feed('?')
|
||||
poke_eventloop()
|
||||
feed('v')
|
||||
poke_eventloop()
|
||||
feed('i')
|
||||
poke_eventloop()
|
||||
feed('m')
|
||||
poke_eventloop()
|
||||
feed(' ')
|
||||
poke_eventloop()
|
||||
feed('?')
|
||||
poke_eventloop()
|
||||
feed('<C-T>')
|
||||
screen:expect([[
|
||||
1 {10:vim }inc |
|
||||
2 {10:vim }/ |
|
||||
3 {2:vim }/ |
|
||||
4 {10:vim }? |
|
||||
5 {10:vim }? |
|
||||
?vim ?^ |
|
||||
]])
|
||||
feed('<Esc>')
|
||||
|
||||
feed('/')
|
||||
poke_eventloop()
|
||||
feed('v')
|
||||
poke_eventloop()
|
||||
feed('i')
|
||||
poke_eventloop()
|
||||
feed('m')
|
||||
poke_eventloop()
|
||||
feed(' ')
|
||||
poke_eventloop()
|
||||
feed('\\/')
|
||||
poke_eventloop()
|
||||
feed('<C-G>')
|
||||
screen:expect([[
|
||||
1 vim inc |
|
||||
2 {10:vim /} |
|
||||
3 {2:vim /} |
|
||||
4 vim ? |
|
||||
5 vim ? |
|
||||
/vim \/^ |
|
||||
]])
|
||||
feed('<Esc>')
|
||||
|
||||
command('5')
|
||||
feed('?')
|
||||
poke_eventloop()
|
||||
feed('v')
|
||||
poke_eventloop()
|
||||
feed('i')
|
||||
poke_eventloop()
|
||||
feed('m')
|
||||
poke_eventloop()
|
||||
feed(' ')
|
||||
poke_eventloop()
|
||||
feed('\\?')
|
||||
poke_eventloop()
|
||||
feed('<C-T>')
|
||||
screen:expect([[
|
||||
1 vim inc |
|
||||
2 vim / |
|
||||
3 vim / |
|
||||
4 {10:vim ?} |
|
||||
5 {2:vim ?} |
|
||||
?vim \?^ |
|
||||
]])
|
||||
feed('<Esc>')
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('Search highlight', function()
|
||||
|
@@ -14,6 +14,8 @@ local feed = n.feed
|
||||
local expect_events = t.expect_events
|
||||
local write_file = t.write_file
|
||||
local dedent = t.dedent
|
||||
local matches = t.matches
|
||||
local pcall_err = t.pcall_err
|
||||
|
||||
local origlines = {
|
||||
'original line 1',
|
||||
@@ -1384,3 +1386,140 @@ describe('lua: nvim_buf_attach on_bytes', function()
|
||||
do_both(false)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('nvim_buf_attach on_detach', function()
|
||||
it('called before buf_freeall autocommands', function()
|
||||
exec_lua(function()
|
||||
vim.api.nvim_create_autocmd({ 'BufUnload', 'BufDelete', 'BufWipeout' }, {
|
||||
callback = function(args)
|
||||
table.insert(
|
||||
_G.events,
|
||||
('%s: %d %s'):format(
|
||||
args.event,
|
||||
args.buf,
|
||||
tostring(vim.api.nvim_buf_is_loaded(args.buf))
|
||||
)
|
||||
)
|
||||
end,
|
||||
})
|
||||
function _G.on_detach(_, b)
|
||||
table.insert(
|
||||
_G.events,
|
||||
('on_detach: %d %s'):format(b, tostring(vim.api.nvim_buf_is_loaded(b)))
|
||||
)
|
||||
end
|
||||
_G.events = {}
|
||||
vim.cmd 'new'
|
||||
vim.bo.bufhidden = 'wipe'
|
||||
vim.api.nvim_buf_attach(0, false, { on_detach = _G.on_detach })
|
||||
vim.cmd 'quit!'
|
||||
end)
|
||||
|
||||
eq(
|
||||
{ 'on_detach: 2 true', 'BufUnload: 2 true', 'BufDelete: 2 true', 'BufWipeout: 2 true' },
|
||||
exec_lua('return _G.events')
|
||||
)
|
||||
eq(false, api.nvim_buf_is_valid(2))
|
||||
|
||||
exec_lua(function()
|
||||
_G.events = {}
|
||||
local buf = vim.api.nvim_create_buf(false, true)
|
||||
vim.api.nvim_buf_attach(buf, false, { on_detach = _G.on_detach })
|
||||
vim.api.nvim_buf_delete(buf, { force = true })
|
||||
end)
|
||||
|
||||
-- Was unlisted, so no BufDelete.
|
||||
eq(
|
||||
{ 'on_detach: 3 true', 'BufUnload: 3 true', 'BufWipeout: 3 true' },
|
||||
exec_lua('return _G.events')
|
||||
)
|
||||
eq(false, api.nvim_buf_is_valid(3))
|
||||
|
||||
exec_lua(function()
|
||||
_G.events = {}
|
||||
vim.api.nvim_buf_attach(1, false, { on_detach = _G.on_detach })
|
||||
vim.api.nvim_create_autocmd('BufUnload', {
|
||||
buffer = 1,
|
||||
once = true,
|
||||
callback = function()
|
||||
vim.api.nvim_buf_attach(1, false, {
|
||||
on_detach = function(...)
|
||||
vim.fn.bufload(1) -- Leaks the memfile it were to run inside free_buffer_stuff.
|
||||
return _G.on_detach(...)
|
||||
end,
|
||||
})
|
||||
table.insert(_G.events, 'local BufUnload')
|
||||
end,
|
||||
})
|
||||
vim.cmd 'edit asdf' -- Reuses buffer 1.
|
||||
end)
|
||||
|
||||
-- on_detach shouldn't run after autocommands when reusing a buffer (in free_buffer_stuff), even
|
||||
-- if those autocommands registered it, as curbuf may be in a semi-unloaded state at that point.
|
||||
eq({
|
||||
'on_detach: 1 true',
|
||||
'BufUnload: 1 true',
|
||||
'local BufUnload',
|
||||
'BufDelete: 1 true',
|
||||
'BufWipeout: 1 true',
|
||||
}, exec_lua('return _G.events'))
|
||||
|
||||
exec_lua(function()
|
||||
_G.events = {}
|
||||
vim.api.nvim_buf_attach(0, false, { on_detach = _G.on_detach })
|
||||
vim.cmd 'edit'
|
||||
end)
|
||||
|
||||
-- Re-edit buffer; on_detach is called.
|
||||
eq({ 'on_detach: 1 true', 'BufUnload: 1 true' }, exec_lua('return _G.events'))
|
||||
eq(true, api.nvim_buf_is_valid(1))
|
||||
|
||||
exec_lua(function()
|
||||
vim.cmd '%bwipeout!'
|
||||
vim.bo.modified = true
|
||||
_G.events = {}
|
||||
vim.api.nvim_buf_attach(0, false, { on_detach = _G.on_detach })
|
||||
vim.api.nvim_buf_delete(0, { force = true })
|
||||
end)
|
||||
|
||||
-- on_detach must still be first when wiping the last buffer if it's listed and non-reusable.
|
||||
-- Previously: BufUnload → BufDelete → on_detach → BufWipeout.
|
||||
eq(
|
||||
{ 'on_detach: 4 true', 'BufUnload: 4 true', 'BufDelete: 4 true', 'BufWipeout: 4 false' },
|
||||
exec_lua('return _G.events')
|
||||
)
|
||||
end)
|
||||
|
||||
it('disallows splitting', function()
|
||||
command('new | setlocal bufhidden=wipe')
|
||||
local buf = api.nvim_get_current_buf()
|
||||
exec_lua(function()
|
||||
vim.api.nvim_buf_attach(0, false, {
|
||||
on_detach = function()
|
||||
-- Used to allow opening more views into a closing buffer, resulting in open windows to an
|
||||
-- unloaded buffer.
|
||||
vim.cmd [=[execute "normal! \<C-W>s"]=]
|
||||
end,
|
||||
})
|
||||
end)
|
||||
matches('E1159: Cannot split a window when closing the buffer$', pcall_err(command, 'quit!'))
|
||||
eq({}, fn.win_findbuf(buf))
|
||||
eq(false, api.nvim_buf_is_valid(buf))
|
||||
end)
|
||||
end)
|
||||
|
||||
it('nvim_buf_attach from buf_freeall autocommands does not leak', function()
|
||||
exec_lua(function()
|
||||
local b = vim.api.nvim_create_buf(true, true)
|
||||
vim.api.nvim_create_autocmd('BufWipeout', {
|
||||
buffer = b,
|
||||
once = true,
|
||||
callback = function()
|
||||
vim.api.nvim_buf_attach(b, false, {})
|
||||
_G.autocmd_fired = true
|
||||
end,
|
||||
})
|
||||
vim.api.nvim_buf_delete(b, { force = true })
|
||||
end)
|
||||
eq(true, exec_lua('return _G.autocmd_fired'))
|
||||
end)
|
||||
|
@@ -339,6 +339,15 @@ describe('vim.iter', function()
|
||||
local s = 'abcdefghijklmnopqrstuvwxyz'
|
||||
eq('z', vim.iter(vim.split(s, '')):last())
|
||||
eq('z', vim.iter(vim.gsplit(s, '')):last())
|
||||
eq(
|
||||
nil,
|
||||
vim
|
||||
.iter({ 1, 2, 3, 4, 5 })
|
||||
:filter(function()
|
||||
return false
|
||||
end)
|
||||
:last()
|
||||
)
|
||||
end)
|
||||
|
||||
it('enumerate()', function()
|
||||
|
@@ -16,6 +16,7 @@ local feed = n.feed
|
||||
local assert_alive = n.assert_alive
|
||||
local NIL = vim.NIL
|
||||
local eq = t.eq
|
||||
local matches = t.matches
|
||||
|
||||
before_each(clear)
|
||||
|
||||
@@ -266,6 +267,8 @@ describe('luaeval()', function()
|
||||
|
||||
return true
|
||||
]])
|
||||
-- v:errmsg is set properly #35554
|
||||
matches(': dead function\n', api.nvim_get_vvar('errmsg'))
|
||||
end)
|
||||
|
||||
it('should handle passing functions around', function()
|
||||
|
@@ -240,6 +240,23 @@ describe('vim.snippet', function()
|
||||
eq({ 'public function foo() {', '\t', '}' }, buf_lines(0))
|
||||
end)
|
||||
|
||||
it('does not change the chosen text when jumping back to a choice tabstop', function()
|
||||
test_expand_success(
|
||||
{ '${1|public,protected,private|} function ${2:name}() {', '\t$0', '}' },
|
||||
{ ' function name() {', '\t', '}' }
|
||||
)
|
||||
wait_for_pum()
|
||||
feed('<C-n><C-y><Tab>')
|
||||
poke_eventloop()
|
||||
feed('<S-Tab>')
|
||||
poke_eventloop()
|
||||
wait_for_pum()
|
||||
feed('<Tab>')
|
||||
poke_eventloop()
|
||||
feed('foo')
|
||||
eq({ 'protected function foo() {', '\t', '}' }, buf_lines(0))
|
||||
end)
|
||||
|
||||
it('jumps through adjacent tabstops', function()
|
||||
test_expand_success(
|
||||
{ 'for i=1,${1:to}${2:,step} do\n\t$3\nend' },
|
||||
@@ -249,7 +266,11 @@ describe('vim.snippet', function()
|
||||
feed('<Tab>')
|
||||
poke_eventloop()
|
||||
feed(',2')
|
||||
eq({ 'for i=1,10,2 do', '\t', 'end' }, buf_lines(0))
|
||||
-- Make sure changes on previous tabstops does not change following ones
|
||||
feed('<S-Tab>')
|
||||
poke_eventloop()
|
||||
feed('20')
|
||||
eq({ 'for i=1,20,2 do', '\t', 'end' }, buf_lines(0))
|
||||
end)
|
||||
|
||||
it('updates snippet state when built-in completion menu is visible', function()
|
||||
|
@@ -67,6 +67,14 @@ describe(':checkhealth', function()
|
||||
assert_alive()
|
||||
end)
|
||||
|
||||
it('cmdline completion works with multiple args #35054', function()
|
||||
clear()
|
||||
n.feed(':checkhealth vim.ls<Tab>')
|
||||
eq('checkhealth vim.lsp', fn.getcmdline())
|
||||
n.feed(' vim.prov<Tab>')
|
||||
eq('checkhealth vim.lsp vim.provider', fn.getcmdline())
|
||||
end)
|
||||
|
||||
it('vim.g.health', function()
|
||||
clear {
|
||||
args_rm = { '-u' },
|
||||
|
@@ -267,6 +267,14 @@ describe('vim.lsp.util', function()
|
||||
|
||||
eq(56, opts.height)
|
||||
end)
|
||||
|
||||
it('title with winborder option #35179', function()
|
||||
local opts = exec_lua(function()
|
||||
vim.o.winborder = 'single'
|
||||
return vim.lsp.util.make_floating_popup_options(100, 100, { title = 'Title' })
|
||||
end)
|
||||
eq('Title', opts.title)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
|
@@ -593,32 +593,35 @@ describe('LSP', function()
|
||||
exec_lua(create_server_definition)
|
||||
local result = exec_lua(function()
|
||||
local server = _G._create_server()
|
||||
local bufnr = vim.api.nvim_create_buf(false, true)
|
||||
vim.api.nvim_set_current_buf(bufnr)
|
||||
local detach_called = false
|
||||
local bufnr1 = vim.api.nvim_create_buf(false, true)
|
||||
local bufnr2 = vim.api.nvim_create_buf(false, true)
|
||||
local detach_called1 = false
|
||||
local detach_called2 = false
|
||||
vim.api.nvim_create_autocmd('LspDetach', {
|
||||
buffer = bufnr1,
|
||||
callback = function()
|
||||
detach_called = true
|
||||
detach_called1 = true
|
||||
end,
|
||||
})
|
||||
local client_id = vim.lsp.start({ name = 'detach-dummy', cmd = server.cmd })
|
||||
assert(client_id, 'lsp.start must return client_id')
|
||||
vim.api.nvim_create_autocmd('LspDetach', {
|
||||
buffer = bufnr2,
|
||||
callback = function()
|
||||
detach_called2 = true
|
||||
end,
|
||||
})
|
||||
vim.api.nvim_set_current_buf(bufnr1)
|
||||
local client_id = assert(vim.lsp.start({ name = 'detach-dummy', cmd = server.cmd }))
|
||||
local client = assert(vim.lsp.get_client_by_id(client_id))
|
||||
local num_attached_before = vim.tbl_count(client.attached_buffers)
|
||||
vim.api.nvim_buf_delete(bufnr, { force = true })
|
||||
local num_attached_after = vim.tbl_count(client.attached_buffers)
|
||||
return {
|
||||
bufnr = bufnr,
|
||||
client_id = client_id,
|
||||
num_attached_before = num_attached_before,
|
||||
num_attached_after = num_attached_after,
|
||||
detach_called = detach_called,
|
||||
}
|
||||
vim.api.nvim_set_current_buf(bufnr2)
|
||||
vim.lsp.start({ name = 'detach-dummy', cmd = server.cmd })
|
||||
assert(vim.tbl_count(client.attached_buffers) == 2)
|
||||
vim.api.nvim_buf_delete(bufnr1, { force = true })
|
||||
assert(vim.tbl_count(client.attached_buffers) == 1)
|
||||
vim.api.nvim_buf_delete(bufnr2, { force = true })
|
||||
assert(vim.tbl_count(client.attached_buffers) == 0)
|
||||
return detach_called1 and detach_called2
|
||||
end)
|
||||
eq(true, result ~= nil, 'exec_lua must return result')
|
||||
eq(1, result.num_attached_before)
|
||||
eq(0, result.num_attached_after)
|
||||
eq(true, result.detach_called)
|
||||
eq(true, result)
|
||||
end)
|
||||
|
||||
it('should not re-attach buffer if it was deleted in on_init #28575', function()
|
||||
@@ -3657,7 +3660,7 @@ describe('LSP', function()
|
||||
}
|
||||
return vim.lsp.util.convert_signature_help_to_markdown_lines(signature_help, 'cs', { ',' })
|
||||
end)
|
||||
local expected = { '```cs', 'TestEntity.TestEntity()', '```', 'some doc' }
|
||||
local expected = { '```cs', 'TestEntity.TestEntity()', '```', '---', 'some doc' }
|
||||
eq(expected, result)
|
||||
end)
|
||||
|
||||
|
@@ -16,16 +16,21 @@ describe(':Tutor', function()
|
||||
command('Tutor')
|
||||
screen = Screen.new(81, 30)
|
||||
screen:set_default_attr_ids({
|
||||
[0] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.Gray },
|
||||
[0] = { foreground = Screen.colors.Blue4, background = Screen.colors.Grey },
|
||||
[1] = { bold = true },
|
||||
[2] = { underline = true, foreground = tonumber('0x0088ff') },
|
||||
[3] = { foreground = Screen.colors.SlateBlue },
|
||||
[4] = { bold = true, foreground = Screen.colors.Brown },
|
||||
[5] = { bold = true, foreground = Screen.colors.Magenta1 },
|
||||
[6] = { italic = true },
|
||||
[7] = { foreground = tonumber('0x00ff88'), bold = true, background = Screen.colors.Grey },
|
||||
[8] = { bold = true, foreground = Screen.colors.Blue },
|
||||
[9] = { foreground = Screen.colors.Magenta1 },
|
||||
[10] = { foreground = tonumber('0xff2000'), bold = true },
|
||||
[11] = { foreground = tonumber('0xff2000'), bold = true, background = Screen.colors.Grey },
|
||||
[12] = { foreground = tonumber('0x6a0dad') },
|
||||
})
|
||||
end)
|
||||
|
||||
it('applies {unix:…,win:…} transform', function()
|
||||
local expected = is_os('win')
|
||||
and [[
|
||||
@@ -134,62 +139,150 @@ describe(':Tutor', function()
|
||||
feed(':983<CR>zt')
|
||||
screen:expect(expected)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe(':Tutor tutor', function()
|
||||
local screen --- @type test.functional.ui.screen
|
||||
|
||||
before_each(function()
|
||||
clear({ args = { '--clean' } })
|
||||
command('set cmdheight=0')
|
||||
command('Tutor tutor')
|
||||
screen = Screen.new(81, 30)
|
||||
screen:set_default_attr_ids({
|
||||
[0] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.Gray },
|
||||
[1] = { bold = true },
|
||||
[2] = { underline = true, foreground = tonumber('0x0088ff') },
|
||||
[3] = { foreground = Screen.colors.SlateBlue },
|
||||
[4] = { bold = true, foreground = Screen.colors.Brown },
|
||||
[5] = { bold = true, foreground = Screen.colors.Magenta1 },
|
||||
[6] = { italic = true },
|
||||
[7] = { foreground = tonumber('0x00ff88'), bold = true, background = Screen.colors.Grey },
|
||||
[8] = { bold = true, foreground = Screen.colors.Blue1 },
|
||||
})
|
||||
end)
|
||||
|
||||
it('applies interactive marks', function()
|
||||
feed(':216<CR>zt')
|
||||
it("removing a line doesn't affect highlight/mark of other lines", function()
|
||||
-- Do lesson 2.6
|
||||
feed(':294<CR>zt')
|
||||
screen:expect([[
|
||||
{0: }{3:^###}{5: expect } |
|
||||
{0: } |
|
||||
{0: }"expect" lines check that the contents of the line are identical to some preset|
|
||||
{0: } text |
|
||||
{0: }(like in the exercises above). |
|
||||
{0: } |
|
||||
{0: }These elements are specified in separate JSON files like this |
|
||||
{0: } |
|
||||
{0: }{3:~~~ json} |
|
||||
{0: }{ |
|
||||
{0: } "expect": { |
|
||||
{0: } "1": "This is how this line should look.", |
|
||||
{0: } "2": "This is how this line should look.", |
|
||||
{0: } "3": -1 |
|
||||
{0: } } |
|
||||
{0: }} |
|
||||
{0: }{3:~~~} |
|
||||
{0: } |
|
||||
{0: }These files contain an "expect" dictionary, for which the keys are line numbers|
|
||||
{0: } and |
|
||||
{0: }the values are the expected text. A value of -1 means that the condition for th|
|
||||
{0: }e line |
|
||||
{0: }will always be satisfied, no matter what (this is useful for letting the user p|
|
||||
{0: }lay a bit). |
|
||||
{0: } |
|
||||
{7:✓ }{3:This is an "expect" line that is always satisfied. Try changing it.} |
|
||||
{0: } |
|
||||
{0: }These files conventionally have the same name as the tutorial document with the|
|
||||
{0: } .json |
|
||||
{0: }extension appended (for a full example, see the file that corresponds to thi{8:@@@}|
|
||||
{0: }{3:^#}{5: Lesson 2.6: OPERATING ON LINES} |
|
||||
{0: } |
|
||||
{0: }{1: Type }{4:dd}{1: to delete a whole line. } |
|
||||
{0: } |
|
||||
{0: }Due to the frequency of whole line deletion, the designers of Vi decided |
|
||||
{0: }it would be easier to simply type two d's to delete a line. |
|
||||
{0: } |
|
||||
{0: } 1. Move the cursor to the second line in the phrase below. |
|
||||
{0: } |
|
||||
{0: } 2. Type {2:dd} to delete the line. |
|
||||
{0: } |
|
||||
{0: } 3. Now move to the fourth line. |
|
||||
{0: } |
|
||||
{0: } 4. Type {9:2}{4:dd} to delete two lines. |
|
||||
{0: } |
|
||||
{7:✓ }{3:1) Roses are red, }|
|
||||
{11:✗ }{3:2) Mud is fun, }|
|
||||
{7:✓ }{3:3) Violets are blue, }|
|
||||
{11:✗ }{3:4) I have a car, }|
|
||||
{11:✗ }{3:5) Clocks tell time, }|
|
||||
{7:✓ }{3:6) Sugar is sweet }|
|
||||
{7:✓ }{3:7) And so are you. }|
|
||||
{0: } |
|
||||
{0: }{3:#}{5: Lesson 2.7: THE UNDO COMMAND} |
|
||||
{0: } |
|
||||
{0: }{1: Press }{4:u}{1: to undo the last commands, }{4:U}{1: to fix a whole line. } |
|
||||
{0: } |
|
||||
{0: } 1. Move the cursor to the line below marked {10:✗} and place it on the first error.|
|
||||
{0: } |
|
||||
{0: } 2. Type {4:x} to delete the first unwanted character. |
|
||||
]])
|
||||
|
||||
feed('<Cmd>310<CR>dd<Cmd>311<CR>2dd')
|
||||
screen:expect([[
|
||||
{0: }{3:#}{5: Lesson 2.6: OPERATING ON LINES} |
|
||||
{0: } |
|
||||
{0: }{1: Type }{4:dd}{1: to delete a whole line. } |
|
||||
{0: } |
|
||||
{0: }Due to the frequency of whole line deletion, the designers of Vi decided |
|
||||
{0: }it would be easier to simply type two d's to delete a line. |
|
||||
{0: } |
|
||||
{0: } 1. Move the cursor to the second line in the phrase below. |
|
||||
{0: } |
|
||||
{0: } 2. Type {2:dd} to delete the line. |
|
||||
{0: } |
|
||||
{0: } 3. Now move to the fourth line. |
|
||||
{0: } |
|
||||
{0: } 4. Type {9:2}{4:dd} to delete two lines. |
|
||||
{0: } |
|
||||
{7:✓ }{3:1) Roses are red, }|
|
||||
{7:✓ }{3:3) Violets are blue, }|
|
||||
{7:✓ }{3:^6) Sugar is sweet }|
|
||||
{7:✓ }{3:7) And so are you. }|
|
||||
{0: } |
|
||||
{0: }{3:#}{5: Lesson 2.7: THE UNDO COMMAND} |
|
||||
{0: } |
|
||||
{0: }{1: Press }{4:u}{1: to undo the last commands, }{4:U}{1: to fix a whole line. } |
|
||||
{0: } |
|
||||
{0: } 1. Move the cursor to the line below marked {10:✗} and place it on the first error.|
|
||||
{0: } |
|
||||
{0: } 2. Type {4:x} to delete the first unwanted character. |
|
||||
{0: } |
|
||||
{0: } 3. Now type {4:u} to undo the last command executed. |
|
||||
{0: } |
|
||||
]])
|
||||
end)
|
||||
|
||||
it("inserting text at start of line doesn't affect highlight/sign", function()
|
||||
-- Go to lesson 1.3 and make it top line in the window
|
||||
feed('<Cmd>92<CR>zt')
|
||||
screen:expect([[
|
||||
{0: }{3:^#}{5: Lesson 1.3: TEXT EDITING: DELETION} |
|
||||
{0: } |
|
||||
{0: }{1: Press }{4:x}{1: to delete the character under the cursor. } |
|
||||
{0: } |
|
||||
{0: } 1. Move the cursor to the line below marked {10:✗}. |
|
||||
{0: } |
|
||||
{0: } 2. To fix the errors, move the cursor until it is on top of the |
|
||||
{0: } character to be deleted. |
|
||||
{0: } |
|
||||
{0: } 3. Press {2:the x key} to delete the unwanted character. |
|
||||
{0: } |
|
||||
{0: } 4. Repeat steps 2 through 4 until the sentence is correct. |
|
||||
{0: } |
|
||||
{11:✗ }{3:The ccow jumpedd ovverr thhe mooon. }|
|
||||
{0: } |
|
||||
{0: } 5. Now that the line is correct, go on to Lesson 1.4. |
|
||||
{0: } |
|
||||
{0: }{1:NOTE}: As you go through this tutorial, do not try to memorize everything, |
|
||||
{0: } your Neovim vocabulary will expand with usage. Consider returning to |
|
||||
{0: } this tutorial periodically for a refresher. |
|
||||
{0: } |
|
||||
{0: }{3:#}{5: Lesson 1.4: TEXT EDITING: INSERTION} |
|
||||
{0: } |
|
||||
{0: }{1: Press }{12:i}{1: to insert text. } |
|
||||
{0: } |
|
||||
{0: } 1. Move the cursor to the first line below marked {10:✗}. |
|
||||
{0: } |
|
||||
{0: } 2. To make the first line the same as the second, move the cursor on top |
|
||||
{0: } of the first character AFTER where the text is to be inserted. |
|
||||
{0: } |
|
||||
]])
|
||||
-- Go to the test line and insert text at the start of the line
|
||||
feed('<Cmd>105<CR>iThe <Esc>')
|
||||
-- Remove redundant characters
|
||||
feed('fcxfdxfvxfrxfhxfox')
|
||||
-- Remove the original "The " text (not the just-inserted one)
|
||||
feed('^4ldw^')
|
||||
screen:expect([[
|
||||
{0: }{3:#}{5: Lesson 1.3: TEXT EDITING: DELETION} |
|
||||
{0: } |
|
||||
{0: }{1: Press }{4:x}{1: to delete the character under the cursor. } |
|
||||
{0: } |
|
||||
{0: } 1. Move the cursor to the line below marked {10:✗}. |
|
||||
{0: } |
|
||||
{0: } 2. To fix the errors, move the cursor until it is on top of the |
|
||||
{0: } character to be deleted. |
|
||||
{0: } |
|
||||
{0: } 3. Press {2:the x key} to delete the unwanted character. |
|
||||
{0: } |
|
||||
{0: } 4. Repeat steps 2 through 4 until the sentence is correct. |
|
||||
{0: } |
|
||||
{7:✓ }{3:^The cow jumped over the moon. }|
|
||||
{0: } |
|
||||
{0: } 5. Now that the line is correct, go on to Lesson 1.4. |
|
||||
{0: } |
|
||||
{0: }{1:NOTE}: As you go through this tutorial, do not try to memorize everything, |
|
||||
{0: } your Neovim vocabulary will expand with usage. Consider returning to |
|
||||
{0: } this tutorial periodically for a refresher. |
|
||||
{0: } |
|
||||
{0: }{3:#}{5: Lesson 1.4: TEXT EDITING: INSERTION} |
|
||||
{0: } |
|
||||
{0: }{1: Press }{12:i}{1: to insert text. } |
|
||||
{0: } |
|
||||
{0: } 1. Move the cursor to the first line below marked {10:✗}. |
|
||||
{0: } |
|
||||
{0: } 2. To make the first line the same as the second, move the cursor on top |
|
||||
{0: } of the first character AFTER where the text is to be inserted. |
|
||||
{0: } |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
|
@@ -537,7 +537,7 @@ describe('clipboard (with fake clipboard.vim)', function()
|
||||
eq('textstar', api.nvim_get_current_line())
|
||||
end)
|
||||
|
||||
it('Block paste works correctly', function()
|
||||
it('block paste works correctly', function()
|
||||
insert([[
|
||||
aabbcc
|
||||
ddeeff
|
||||
@@ -550,6 +550,20 @@ describe('clipboard (with fake clipboard.vim)', function()
|
||||
ddeeddeeff
|
||||
]])
|
||||
end)
|
||||
|
||||
it('block paste computes block width correctly #35034', function()
|
||||
insert('あいうえお')
|
||||
feed('0<C-V>ly')
|
||||
feed('P')
|
||||
expect('あいあいうえお')
|
||||
feed('A\nxxx\nxx<Esc>')
|
||||
feed('0<C-V>kkly')
|
||||
feed('P')
|
||||
expect([[
|
||||
あいあいあいうえお
|
||||
xxx xxx
|
||||
xx xx]])
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('clipboard=unnamedplus', function()
|
||||
|
@@ -853,4 +853,16 @@ t2]])
|
||||
command('set ft=c')
|
||||
eq(foldlevels, get_fold_levels())
|
||||
end)
|
||||
|
||||
it('no error when deleting lines at end of buffer with fml=0', function()
|
||||
local screen = Screen.new(40, 2)
|
||||
insert('hello')
|
||||
parse('markdown')
|
||||
command('set foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr() foldminlines=0')
|
||||
feed('o<Esc>dd')
|
||||
screen:expect([[
|
||||
^hello |
|
||||
|
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
|
@@ -5,6 +5,7 @@ local Screen = require('test.functional.ui.screen')
|
||||
local clear = n.clear
|
||||
local insert = n.insert
|
||||
local exec_lua = n.exec_lua
|
||||
local eval = n.eval
|
||||
local feed = n.feed
|
||||
local command = n.command
|
||||
local api = n.api
|
||||
@@ -197,6 +198,14 @@ describe('treesitter highlighting (C)', function()
|
||||
end)
|
||||
-- legacy syntax highlighting is used
|
||||
screen:expect(hl_grid_legacy_c)
|
||||
|
||||
exec_lua(function()
|
||||
vim.cmd 'new | wincmd p'
|
||||
vim.treesitter.start()
|
||||
vim.cmd 'bdelete!'
|
||||
end)
|
||||
-- Does not change &syntax of the other, unrelated buffer.
|
||||
eq('', eval('&syntax'))
|
||||
end)
|
||||
|
||||
it('is updated with edits', function()
|
||||
|
@@ -1141,6 +1141,16 @@ describe('cmdline redraw', function()
|
||||
]])
|
||||
command('redraw')
|
||||
screen:expect_unchanged()
|
||||
|
||||
command('set keymap=dvorak')
|
||||
feed('<C-^>')
|
||||
command('redraw')
|
||||
screen:expect_unchanged()
|
||||
|
||||
feed('<C-^>')
|
||||
command('set keymap&')
|
||||
command('redraw')
|
||||
screen:expect_unchanged()
|
||||
end)
|
||||
|
||||
it('substitute confirm prompt does not scroll', function()
|
||||
|
@@ -1249,6 +1249,7 @@ end)
|
||||
it('diff updates line numbers below filler lines', function()
|
||||
local screen = Screen.new(40, 14)
|
||||
exec([[
|
||||
set diffopt=internal,filler,closeoff
|
||||
call setline(1, ['a', 'a', 'a', 'y', 'b', 'b', 'b', 'b', 'b'])
|
||||
vnew
|
||||
call setline(1, ['a', 'a', 'a', 'x', 'x', 'x', 'b', 'b', 'b', 'b', 'b'])
|
||||
@@ -1384,6 +1385,7 @@ end)
|
||||
|
||||
it("'relativenumber' doesn't draw beyond end of window in diff mode #29403", function()
|
||||
local screen = Screen.new(60, 12)
|
||||
command('set diffopt=internal,filler,closeoff')
|
||||
command('set relativenumber')
|
||||
feed('10aa<CR><Esc>gg')
|
||||
command('vnew')
|
||||
|
@@ -9492,6 +9492,220 @@ describe('float window', function()
|
||||
|
|
||||
]])
|
||||
end
|
||||
|
||||
--
|
||||
-- Cursor visibility:
|
||||
--
|
||||
-- Cursor is not visible in a hide=true floating window.
|
||||
api.nvim_set_current_win(win)
|
||||
if multigrid then
|
||||
screen:expect({
|
||||
grid = [[
|
||||
## grid 1
|
||||
[2:----------------------------------------]|*6
|
||||
[3:----------------------------------------]|
|
||||
## grid 2
|
||||
|
|
||||
{0:~ }|*5
|
||||
## grid 3
|
||||
|
|
||||
## grid 4 (hidden)
|
||||
{1: }|
|
||||
{2:~ }|
|
||||
]], win_viewport={
|
||||
[2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
|
||||
[4] = {win = 1001, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
|
||||
}, win_viewport_margins={
|
||||
[2] = {
|
||||
bottom = 0,
|
||||
left = 0,
|
||||
right = 0,
|
||||
top = 0,
|
||||
win = 1000
|
||||
},
|
||||
[4] = {
|
||||
bottom = 0,
|
||||
left = 0,
|
||||
right = 0,
|
||||
top = 0,
|
||||
win = 1001
|
||||
}
|
||||
}
|
||||
})
|
||||
else
|
||||
screen:expect({
|
||||
grid = [[
|
||||
|
|
||||
{0:~ }|*5
|
||||
|
|
||||
]]
|
||||
})
|
||||
end
|
||||
|
||||
-- Show cursor if cmdline is entered while curwin is a hide=true floating window.
|
||||
feed(':')
|
||||
if multigrid then
|
||||
screen:expect({
|
||||
grid = [[
|
||||
## grid 1
|
||||
[2:----------------------------------------]|*6
|
||||
[3:----------------------------------------]|
|
||||
## grid 2
|
||||
|
|
||||
{0:~ }|*5
|
||||
## grid 3
|
||||
:^ |
|
||||
## grid 4 (hidden)
|
||||
{1: }|
|
||||
{2:~ }|
|
||||
]], win_viewport={
|
||||
[2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
|
||||
[4] = {win = 1001, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
|
||||
}, win_viewport_margins={
|
||||
[2] = {
|
||||
bottom = 0,
|
||||
left = 0,
|
||||
right = 0,
|
||||
top = 0,
|
||||
win = 1000
|
||||
},
|
||||
[4] = {
|
||||
bottom = 0,
|
||||
left = 0,
|
||||
right = 0,
|
||||
top = 0,
|
||||
win = 1001
|
||||
}
|
||||
}
|
||||
})
|
||||
else
|
||||
screen:expect({
|
||||
grid = [[
|
||||
|
|
||||
{0:~ }|*5
|
||||
:^ |
|
||||
]]
|
||||
})
|
||||
end
|
||||
|
||||
command('set keymap=dvorak')
|
||||
feed('<C-^>')
|
||||
screen:expect_unchanged()
|
||||
|
||||
feed('<C-^>')
|
||||
command('set keymap&')
|
||||
screen:expect_unchanged()
|
||||
|
||||
feed('<ESC>')
|
||||
|
||||
-- Show cursor after switching to a normal window (hide=false).
|
||||
api.nvim_set_current_win(cwin)
|
||||
if multigrid then
|
||||
screen:expect({
|
||||
grid = [[
|
||||
## grid 1
|
||||
[2:----------------------------------------]|*6
|
||||
[3:----------------------------------------]|
|
||||
## grid 2
|
||||
^ |
|
||||
{0:~ }|*5
|
||||
## grid 3
|
||||
|
|
||||
## grid 4 (hidden)
|
||||
{1: }|
|
||||
{2:~ }|
|
||||
]], win_viewport={
|
||||
[2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
|
||||
[4] = {win = 1001, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
|
||||
}, win_viewport_margins={
|
||||
[2] = {
|
||||
bottom = 0,
|
||||
left = 0,
|
||||
right = 0,
|
||||
top = 0,
|
||||
win = 1000
|
||||
},
|
||||
[4] = {
|
||||
bottom = 0,
|
||||
left = 0,
|
||||
right = 0,
|
||||
top = 0,
|
||||
win = 1001
|
||||
}
|
||||
}
|
||||
})
|
||||
else
|
||||
screen:expect({
|
||||
grid = [[
|
||||
^ |
|
||||
{0:~ }|*5
|
||||
|
|
||||
]]
|
||||
})
|
||||
end
|
||||
api.nvim_set_current_win(win)
|
||||
local win1 = api.nvim_open_win(buf, false, {relative='editor', width=4, height=4, row=1, col=2})
|
||||
api.nvim_set_current_win(win1)
|
||||
if multigrid then
|
||||
screen:expect({
|
||||
grid = [[
|
||||
## grid 1
|
||||
[2:----------------------------------------]|*6
|
||||
[3:----------------------------------------]|
|
||||
## grid 2
|
||||
|
|
||||
{0:~ }|*5
|
||||
## grid 3
|
||||
|
|
||||
## grid 4 (hidden)
|
||||
{1: }|
|
||||
{2:~ }|
|
||||
## grid 5
|
||||
{1:^ }|
|
||||
{2:~ }|*3
|
||||
]], float_pos={
|
||||
[5] = {1002, "NW", 1, 1, 2, true, 50};
|
||||
}, win_viewport={
|
||||
[2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
|
||||
[4] = {win = 1001, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
|
||||
[5] = {win = 1002, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
|
||||
}, win_viewport_margins={
|
||||
[2] = {
|
||||
bottom = 0,
|
||||
left = 0,
|
||||
right = 0,
|
||||
top = 0,
|
||||
win = 1000
|
||||
},
|
||||
[4] = {
|
||||
bottom = 0,
|
||||
left = 0,
|
||||
right = 0,
|
||||
top = 0,
|
||||
win = 1001
|
||||
},
|
||||
[5] = {
|
||||
bottom = 0,
|
||||
left = 0,
|
||||
right = 0,
|
||||
top = 0,
|
||||
win = 1002
|
||||
}
|
||||
}
|
||||
})
|
||||
else
|
||||
screen:expect({
|
||||
grid = [[
|
||||
|
|
||||
{0:~ }{1:^ }{0: }|
|
||||
{0:~ }{2:~ }{0: }|*3
|
||||
{0:~ }|
|
||||
|
|
||||
]]
|
||||
})
|
||||
end
|
||||
api.nvim_win_close(win1, true)
|
||||
|
||||
-- check window jump with hide
|
||||
feed('<C-W><C-W>')
|
||||
-- should keep on current window
|
||||
|
@@ -4130,7 +4130,7 @@ describe('builtin popupmenu', function()
|
||||
]])
|
||||
end
|
||||
|
||||
-- not rightleft on the cmdline
|
||||
-- oldtest: Test_wildmenu_pum_rightleft()
|
||||
feed('<esc>:sign ')
|
||||
if multigrid then
|
||||
screen:expect({
|
||||
@@ -4154,9 +4154,8 @@ describe('builtin popupmenu', function()
|
||||
]],
|
||||
}
|
||||
end
|
||||
|
||||
-- oldtest: Test_wildmenu_pum_rightleft()
|
||||
feed('<tab>')
|
||||
-- Not rightleft on the cmdline.
|
||||
feed('<Tab>')
|
||||
if multigrid then
|
||||
screen:expect({
|
||||
grid = [[
|
||||
@@ -4195,6 +4194,15 @@ describe('builtin popupmenu', function()
|
||||
]],
|
||||
}
|
||||
end
|
||||
|
||||
-- Behavior is the same when using 'keymap'.
|
||||
feed('<Esc>')
|
||||
command('set keymap=dvorak')
|
||||
-- ";gul" -> "sign" when using Dvorak keymap.
|
||||
feed(':<C-^>;gul <Tab>')
|
||||
screen:expect_unchanged(true)
|
||||
feed('<Esc>')
|
||||
command('set keymap&')
|
||||
end)
|
||||
|
||||
it('with rightleft vsplits', function()
|
||||
|
@@ -365,7 +365,7 @@ for _, v in ipairs(ext_keys) do
|
||||
expect_keys[v] = true
|
||||
end
|
||||
|
||||
--- @class test.function.ui.screen.Expect
|
||||
--- @class test.functional.ui.screen.Expect
|
||||
---
|
||||
--- Expected screen state (string). Each line represents a screen
|
||||
--- row. Last character of each row (typically "|") is stripped.
|
||||
@@ -460,7 +460,7 @@ end
|
||||
--- or keyword args (supports more options):
|
||||
--- screen:expect({ grid=[[...]], cmdline={...}, condition=function() ... end })
|
||||
---
|
||||
--- @param expected string|function|test.function.ui.screen.Expect
|
||||
--- @param expected string|function|test.functional.ui.screen.Expect
|
||||
--- @param attr_ids? table<integer,table<string,any>>
|
||||
function Screen:expect(expected, attr_ids, ...)
|
||||
--- @type string, fun()
|
||||
|
@@ -169,6 +169,17 @@ func Test_bnext_bprev_help()
|
||||
b XHelp1
|
||||
blast | call assert_equal(b4, bufnr())
|
||||
|
||||
" Cycling through help buffers works even if they aren't listed.
|
||||
b XHelp1
|
||||
setlocal nobuflisted
|
||||
bnext | call assert_equal(b3, bufnr())
|
||||
bnext | call assert_equal(b1, bufnr())
|
||||
bprev | call assert_equal(b3, bufnr())
|
||||
setlocal nobuflisted
|
||||
bprev | call assert_equal(b1, bufnr())
|
||||
bprev | call assert_equal(b3, bufnr())
|
||||
bnext | call assert_equal(b1, bufnr())
|
||||
|
||||
%bwipe!
|
||||
endfunc
|
||||
|
||||
@@ -598,4 +609,31 @@ func Test_closed_buffer_still_in_window()
|
||||
%bw!
|
||||
endfunc
|
||||
|
||||
" Cursor position should be restored when switching to a buffer previously
|
||||
" viewed in a window, regardless of whether it's visible in another one.
|
||||
func Test_switch_to_previously_viewed_buffer()
|
||||
set nostartofline
|
||||
new Xviewbuf
|
||||
call setline(1, range(1, 200))
|
||||
let oldwin = win_getid()
|
||||
vsplit
|
||||
|
||||
call cursor(100, 3)
|
||||
edit Xotherbuf
|
||||
buffer Xviewbuf
|
||||
call assert_equal([0, 100, 3, 0], getpos('.'))
|
||||
|
||||
exe win_id2win(oldwin) .. 'close'
|
||||
setlocal bufhidden=hide
|
||||
|
||||
call cursor(200, 3)
|
||||
edit Xotherbuf
|
||||
buffer Xviewbuf
|
||||
call assert_equal([0, 200, 3, 0], getpos('.'))
|
||||
|
||||
bwipe! Xotherbuf
|
||||
bwipe! Xviewbuf
|
||||
set startofline&
|
||||
endfunc
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab
|
||||
|
@@ -158,6 +158,33 @@ func Test_appendbufline()
|
||||
exe "bwipe! " . b
|
||||
endfunc
|
||||
|
||||
func Test_appendbufline_no_E315()
|
||||
let after = [
|
||||
\ 'set stl=%f ls=2',
|
||||
\ 'new',
|
||||
\ 'let buf = bufnr("%")',
|
||||
\ 'quit',
|
||||
\ 'vsp',
|
||||
\ 'exec "buffer" buf',
|
||||
\ 'wincmd w',
|
||||
\ 'call appendbufline(buf, 0, "abc")',
|
||||
\ 'redraw',
|
||||
\ 'while getbufline(buf, 1)[0] =~ "^\\s*$"',
|
||||
\ ' sleep 10m',
|
||||
\ 'endwhile',
|
||||
\ 'au VimLeavePre * call writefile([v:errmsg], "Xerror")',
|
||||
\ 'au VimLeavePre * call writefile(["done"], "Xdone")',
|
||||
\ 'qall!',
|
||||
\ ]
|
||||
if !RunVim([], after, '--clean')
|
||||
return
|
||||
endif
|
||||
call assert_notmatch("^E315:", readfile("Xerror")[0])
|
||||
call assert_equal("done", readfile("Xdone")[0])
|
||||
call delete("Xerror")
|
||||
call delete("Xdone")
|
||||
endfunc
|
||||
|
||||
func Test_deletebufline()
|
||||
new
|
||||
let b = bufnr('%')
|
||||
|
@@ -1204,6 +1204,10 @@ func Test_cmdline_complete_various()
|
||||
call feedkeys(":ka\<C-A>\<C-B>\"\<CR>", 'xt')
|
||||
call assert_equal("\"ka\<C-A>", @:)
|
||||
|
||||
" completion for :keepmarks command
|
||||
call feedkeys(":kee edi\<C-A>\<C-B>\"\<CR>", 'xt')
|
||||
call assert_equal("\"kee edit", @:)
|
||||
|
||||
" completion for short version of the :s command
|
||||
call feedkeys(":sI \<C-A>\<C-B>\"\<CR>", 'xt')
|
||||
call assert_equal("\"sI \<C-A>", @:)
|
||||
@@ -4276,6 +4280,28 @@ func Test_term_option()
|
||||
let &cpo = _cpo
|
||||
endfunc
|
||||
|
||||
func Test_ex_command_completion()
|
||||
" required for :*
|
||||
" set cpo+=*
|
||||
let list = filter(getcompletion('', 'command'), 'exists(":" . v:val) == 0')
|
||||
" :++ and :-- are only valid in Vim9 script context, so they can be ignored
|
||||
" call assert_equal(['++', '--'], sort(list))
|
||||
call assert_equal([], sort(list))
|
||||
call assert_equal(2, exists(':k'))
|
||||
call assert_equal(0, exists(':ke'))
|
||||
call assert_equal(1, exists(':kee'))
|
||||
call assert_equal(1, exists(':keep'))
|
||||
call assert_equal(1, exists(':keepm'))
|
||||
call assert_equal(1, exists(':keepma'))
|
||||
call assert_equal(1, exists(':keepmar'))
|
||||
call assert_equal(1, exists(':keepmark'))
|
||||
call assert_equal(2, exists(':keepmarks'))
|
||||
call assert_equal(2, exists(':keepalt'))
|
||||
call assert_equal(2, exists(':keepjumps'))
|
||||
call assert_equal(2, exists(':keeppatterns'))
|
||||
set cpo-=*
|
||||
endfunc
|
||||
|
||||
func Test_cd_bslash_completion_windows()
|
||||
CheckMSWindows
|
||||
let save_shellslash = &shellslash
|
||||
|
59
test/old/testdir/test_cmdmods.vim
Normal file
59
test/old/testdir/test_cmdmods.vim
Normal file
@@ -0,0 +1,59 @@
|
||||
" Test for all command modifiers in
|
||||
|
||||
let s:luaeval_cmdmods =<< trim END
|
||||
vim.iter(loadfile('../../../src/nvim/ex_cmds.lua')()):map(function(cmd)
|
||||
if cmd.func == 'ex_wrongmodifier' or cmd.command == 'hide' then
|
||||
return cmd.command
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end):totable()
|
||||
END
|
||||
let s:cmdmods = []
|
||||
|
||||
func s:get_cmdmods()
|
||||
if empty(s:cmdmods)
|
||||
let s:cmdmods = luaeval(s:luaeval_cmdmods->join("\n"))
|
||||
endif
|
||||
return s:cmdmods
|
||||
endfunc
|
||||
|
||||
func Test_keep_cmdmods_names()
|
||||
call assert_equal('k', fullcommand(':k'))
|
||||
call assert_equal('k', fullcommand(':ke'))
|
||||
call assert_equal('keepmarks', fullcommand(':kee'))
|
||||
call assert_equal('keepmarks', fullcommand(':keep'))
|
||||
call assert_equal('keepmarks', fullcommand(':keepm'))
|
||||
call assert_equal('keepmarks', fullcommand(':keepma'))
|
||||
call assert_equal('keepmarks', fullcommand(':keepmar'))
|
||||
call assert_equal('keepmarks', fullcommand(':keepmark'))
|
||||
call assert_equal('keepmarks', fullcommand(':keepmarks'))
|
||||
call assert_equal('keepalt', fullcommand(':keepa'))
|
||||
call assert_equal('keepalt', fullcommand(':keepal'))
|
||||
call assert_equal('keepalt', fullcommand(':keepalt'))
|
||||
call assert_equal('keepjumps', fullcommand(':keepj'))
|
||||
call assert_equal('keepjumps', fullcommand(':keepju'))
|
||||
call assert_equal('keepjumps', fullcommand(':keepjum'))
|
||||
call assert_equal('keepjumps', fullcommand(':keepjump'))
|
||||
call assert_equal('keepjumps', fullcommand(':keepjumps'))
|
||||
call assert_equal('keeppatterns', fullcommand(':keepp'))
|
||||
call assert_equal('keeppatterns', fullcommand(':keeppa'))
|
||||
call assert_equal('keeppatterns', fullcommand(':keeppat'))
|
||||
call assert_equal('keeppatterns', fullcommand(':keeppatt'))
|
||||
call assert_equal('keeppatterns', fullcommand(':keeppatte'))
|
||||
call assert_equal('keeppatterns', fullcommand(':keeppatter'))
|
||||
call assert_equal('keeppatterns', fullcommand(':keeppattern'))
|
||||
call assert_equal('keeppatterns', fullcommand(':keeppatterns'))
|
||||
endfunc
|
||||
|
||||
func Test_cmdmod_completion()
|
||||
for mod in s:get_cmdmods()
|
||||
let cmd = $'{mod} ed'
|
||||
if mod == 'filter'
|
||||
let cmd = $'{mod} /pattern/ ed'
|
||||
endif
|
||||
call assert_equal('edit', getcompletion(cmd, 'cmdline')[0])
|
||||
endfor
|
||||
endfunc
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab
|
@@ -105,6 +105,13 @@ func Test_exists()
|
||||
" Internal command with a count
|
||||
call assert_equal(0, exists(':3buffer'))
|
||||
|
||||
" Valid internal command (full match)
|
||||
call assert_equal(2, exists(':k'))
|
||||
" Non-existing internal command (':k' with arg 'e')
|
||||
call assert_equal(0, exists(':ke'))
|
||||
" Valid internal command (partial match)
|
||||
call assert_equal(1, exists(':kee'))
|
||||
|
||||
" User defined command (full match)
|
||||
command! MyCmd :echo 'My command'
|
||||
call assert_equal(2, exists(':MyCmd'))
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user