mirror of
https://github.com/neovim/neovim.git
synced 2025-09-05 19:08:15 +00:00
Compare commits
57 Commits
a33284c2c0
...
nightly
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9898327bd7 | ||
![]() |
0bf4b7898c | ||
![]() |
7fcdb0541f | ||
![]() |
e6cdb76665 | ||
![]() |
e5ba875a95 | ||
![]() |
2415d8f424 | ||
![]() |
2b421d518f | ||
![]() |
2affb8373f | ||
![]() |
9c3099f0cf | ||
![]() |
de950f8272 | ||
![]() |
17da1ad8f4 | ||
![]() |
f2536aa795 | ||
![]() |
bf5f7c1591 | ||
![]() |
1f7d6c3876 | ||
![]() |
967d226f96 | ||
![]() |
c4c9daf7e2 | ||
![]() |
f9ce939bf5 | ||
![]() |
1ae09bf545 | ||
![]() |
431004dda2 | ||
![]() |
79bfeecdb4 | ||
![]() |
b4d21f141c | ||
![]() |
4f374bf938 | ||
![]() |
c4c69c5012 | ||
![]() |
a5e7ccc329 | ||
![]() |
28ab656122 | ||
![]() |
8a12a01466 | ||
![]() |
4cda52a5d1 | ||
![]() |
d8a8825679 | ||
![]() |
6888f65be1 | ||
![]() |
06df337617 | ||
![]() |
8fc1db043a | ||
![]() |
d4f789fd78 | ||
![]() |
3c601d02dc | ||
![]() |
c6bfc203f1 | ||
![]() |
77e3efecee | ||
![]() |
f311c96973 | ||
![]() |
582e0714b5 | ||
![]() |
6a330f893b | ||
![]() |
1b3abfa688 | ||
![]() |
c333d64663 | ||
![]() |
b3d29f396d | ||
![]() |
0c0ef489f9 | ||
![]() |
4edeaaa6e2 | ||
![]() |
6eebf30a39 | ||
![]() |
8a2587be23 | ||
![]() |
834e1181da | ||
![]() |
68a2e0ef78 | ||
![]() |
61712cbc3a | ||
![]() |
8a1afac653 | ||
![]() |
dab31a3637 | ||
![]() |
772f1966a3 | ||
![]() |
19f2e5c3eb | ||
![]() |
7a71235399 | ||
![]() |
bc6737250d | ||
![]() |
5edbabdbec | ||
![]() |
118e7e7111 | ||
![]() |
c10e36fc01 |
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@v5
|
||||
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/*
|
||||
|
@@ -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).
|
||||
|
@@ -825,8 +825,9 @@ will be set to zero, but can be changed and used for the replacing cmdline or
|
||||
message window. Cmdline state is emitted as |ui-cmdline| events, which the UI
|
||||
must handle.
|
||||
|
||||
["msg_show", kind, content, replace_last, history, append, msg_id, progress] ~
|
||||
Display a message to the user.
|
||||
["msg_show", kind, content, replace_last, history, append, msg_id] ~
|
||||
Display a message to the user. Update (replace) any existing message
|
||||
matching `msg_id`.
|
||||
|
||||
kind
|
||||
Name indicating the message kind:
|
||||
@@ -886,18 +887,6 @@ must handle.
|
||||
Unique identifier for the message. It can either be an integer or
|
||||
string. When message of same id appears it should replace the older message.
|
||||
|
||||
progress
|
||||
Progress-message properties:
|
||||
• title: Title string of the progress message.
|
||||
• status: Status of the progress message. Can contain one of
|
||||
the following values
|
||||
• success: The progress item completed successfully
|
||||
• running: The progress is ongoing
|
||||
• failed: The progress item failed
|
||||
• cancel: The progressing process should be canceled.
|
||||
• percent: How much progress is done on the progress
|
||||
message
|
||||
|
||||
["msg_clear"] ~
|
||||
Clear all messages currently displayed by "msg_show", emitted after
|
||||
clearing the screen (messages sent by other "msg_" events below should
|
||||
|
@@ -254,19 +254,6 @@ The idea is "versionless evolution", in the words of Rich Hickey:
|
||||
- Relaxing a requirement should be a compatible change.
|
||||
- Strengthening a promise should be a compatible change.
|
||||
|
||||
==============================================================================
|
||||
Global events *api-global-events*
|
||||
|
||||
When a client invokes an API request as an async notification, it is not
|
||||
possible for Nvim to send an error response. Instead, in case of error, the
|
||||
following notification will be sent to the client:
|
||||
|
||||
*nvim_error_event*
|
||||
nvim_error_event[{type}, {message}]
|
||||
|
||||
{type} is a numeric id as defined by `api_info().error_types`, and {message} is
|
||||
a string with the error message.
|
||||
|
||||
==============================================================================
|
||||
Buffer update events *api-buffer-updates*
|
||||
|
||||
@@ -553,6 +540,38 @@ Extmark positions changed by an edit will be restored on undo/redo. Creating
|
||||
and deleting extmarks is not a buffer change, thus new undo states are not
|
||||
created for extmark changes.
|
||||
|
||||
==============================================================================
|
||||
Global Events *api-events*
|
||||
|
||||
nvim_error_event({type}, {msg}) *nvim_error_event*
|
||||
Emitted on the client channel if an async API request responds with an
|
||||
error.
|
||||
|
||||
Attributes: ~
|
||||
|RPC| only
|
||||
|
||||
Parameters: ~
|
||||
• {type} (`integer`) Error type id as defined by
|
||||
`api_info().error_types`.
|
||||
• {msg} (`string`) Error message.
|
||||
|
||||
nvim_ui_term_event({event}, {value}) *nvim_ui_term_event*
|
||||
Emitted by the TUI client to signal when a host-terminal event occurred.
|
||||
|
||||
Supports these events:
|
||||
• "termresponse": The host-terminal sent a DA1, OSC, DCS, or APC response
|
||||
sequence to Nvim. The payload is the received response. Sets
|
||||
|v:termresponse| and fires |TermResponse|.
|
||||
|
||||
Attributes: ~
|
||||
|RPC| only
|
||||
Since: 0.10.0
|
||||
|
||||
Parameters: ~
|
||||
• {event} (`string`) Event name
|
||||
• {value} (`any`) Event payload
|
||||
|
||||
|
||||
==============================================================================
|
||||
Global Functions *api-global*
|
||||
|
||||
@@ -1385,9 +1404,9 @@ nvim_set_client_info({name}, {version}, {type}, {methods}, {attributes})
|
||||
debugging and orchestration. (Note: Something is better than nothing!
|
||||
Fields are optional, but at least set `name`.)
|
||||
|
||||
Can be called more than once; the caller should merge old info if
|
||||
appropriate. Example: library first identifies the channel, then a plugin
|
||||
using that library later identifies itself.
|
||||
Can be called more than once; caller should merge old info if appropriate.
|
||||
Example: a library first identifies the channel, then a plugin using that
|
||||
library later identifies itself.
|
||||
|
||||
Attributes: ~
|
||||
|RPC| only
|
||||
@@ -3290,11 +3309,12 @@ nvim_set_decoration_provider({ns_id}, {opts})
|
||||
Note: this function should not be called often. Rather, the callbacks
|
||||
themselves can be used to throttle unneeded callbacks. the `on_start`
|
||||
callback can return `false` to disable the provider until the next redraw.
|
||||
Similarly, return `false` in `on_win` will skip the `on_line` calls for
|
||||
that window (but any extmarks set in `on_win` will still be used). A
|
||||
plugin managing multiple sources of decoration should ideally only set one
|
||||
provider, and merge the sources internally. You can use multiple `ns_id`
|
||||
for the extmarks set/modified inside the callback anyway.
|
||||
Similarly, return `false` in `on_win` will skip the `on_line` and
|
||||
`on_range` calls for that window (but any extmarks set in `on_win` will
|
||||
still be used). A plugin managing multiple sources of decoration should
|
||||
ideally only set one provider, and merge the sources internally. You can
|
||||
use multiple `ns_id` for the extmarks set/modified inside the callback
|
||||
anyway.
|
||||
|
||||
Note: doing anything other than setting extmarks is considered
|
||||
experimental. Doing things like changing options are not explicitly
|
||||
@@ -3302,8 +3322,8 @@ nvim_set_decoration_provider({ns_id}, {opts})
|
||||
consumption). Doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is
|
||||
quite dubious for the moment.
|
||||
|
||||
Note: It is not allowed to remove or update extmarks in `on_line`
|
||||
callbacks.
|
||||
Note: It is not allowed to remove or update extmarks in `on_line` or
|
||||
`on_range` callbacks.
|
||||
|
||||
Attributes: ~
|
||||
Lua |vim.api| only
|
||||
@@ -3323,9 +3343,16 @@ nvim_set_decoration_provider({ns_id}, {opts})
|
||||
• on_win: called when starting to redraw a specific window. >
|
||||
["win", winid, bufnr, toprow, botrow]
|
||||
<
|
||||
• on_line: called for each buffer line being redrawn. (The
|
||||
interaction with fold lines is subject to change) >
|
||||
• on_line: (deprecated, use on_range instead) >
|
||||
["line", winid, bufnr, row]
|
||||
<
|
||||
• on_range: called for each buffer range being redrawn. Range
|
||||
is end-exclusive and may span multiple lines. Range bounds
|
||||
point to the first byte of a character. An end position of
|
||||
the form (lnum, 0), including (number of lines, 0), is
|
||||
valid and indicates that EOL of the preceding line is
|
||||
included. >
|
||||
["range", winid, bufnr, begin_row, begin_col, end_row, end_col]
|
||||
<
|
||||
• on_end: called at the end of a redraw cycle >
|
||||
["end", tick]
|
||||
@@ -3652,22 +3679,6 @@ nvim_ui_set_option({name}, {value}) *nvim_ui_set_option()*
|
||||
• {name} (`string`)
|
||||
• {value} (`any`)
|
||||
|
||||
nvim_ui_term_event({event}, {value}) *nvim_ui_term_event()*
|
||||
Tells Nvim when a host-terminal event occurred.
|
||||
|
||||
Supports these events:
|
||||
• "termresponse": The host-terminal sent a DA1, OSC, DCS, or APC response
|
||||
sequence to Nvim. The payload is the received response. Sets
|
||||
|v:termresponse| and fires |TermResponse|.
|
||||
|
||||
Attributes: ~
|
||||
|RPC| only
|
||||
Since: 0.10.0
|
||||
|
||||
Parameters: ~
|
||||
• {event} (`string`) Event name
|
||||
• {value} (`any`) Event payload
|
||||
|
||||
nvim_ui_try_resize({width}, {height}) *nvim_ui_try_resize()*
|
||||
|
||||
Attributes: ~
|
||||
|
@@ -1153,23 +1153,23 @@ first word). You can use the |']| or |`]| command after the put command to
|
||||
move the cursor to the end of the inserted text, or use |'[| or |`[| to move
|
||||
the cursor to the start.
|
||||
|
||||
*put-Visual-mode* *v_p* *v_P*
|
||||
*put-Visual-mode*
|
||||
When using a put command like |p| or |P| in Visual mode, Vim will try to
|
||||
replace the selected text with the contents of the register. Whether this
|
||||
works well depends on the type of selection and the type of the text in the
|
||||
register. With blockwise selection it also depends on the size of the block
|
||||
and whether the corners are on an existing character. (Implementation detail:
|
||||
it actually works by first putting the register after the selection and then
|
||||
deleting the selection.)
|
||||
With |p| the previously selected text is put in the unnamed register (and
|
||||
possibly the selection and/or clipboard). This is useful if you want to put
|
||||
that text somewhere else. But you cannot repeat the same change.
|
||||
With |P| the unnamed register is not changed (and neither the selection or
|
||||
clipboard), you can repeat the same change. But the deleted text cannot be
|
||||
used. If you do need it you can use |p| with another register. E.g., yank
|
||||
the text to copy, Visually select the text to replace and use "0p . You can
|
||||
repeat this as many times as you like, and the unnamed register will be
|
||||
changed each time.
|
||||
replace the selected text with the contents of the register. How this
|
||||
works depends on the type of selection and the text. With blockwise selection
|
||||
it also depends on the size of the block and whether the corners are on an
|
||||
existing character. (Implementation detail: it actually works by first
|
||||
putting the register after the selection and then deleting the selection.)
|
||||
*v_p*
|
||||
|p| in Visual mode puts text and sets the default register (unnamed,
|
||||
selection, or clipboard) to the previously-selected text. Useful if you want
|
||||
to put that text somewhere else. But you cannot repeat the same change.
|
||||
*v_P*
|
||||
|P| in Visual mode puts text without setting the default register. You can
|
||||
repeat the change, but the deleted text cannot be used. If you do need it you
|
||||
can use |p| with another register. E.g., yank the text to copy, Visually
|
||||
select the text to replace and use "0p . You can repeat this as many times as
|
||||
you like, and the unnamed register will be changed each time.
|
||||
*blockwise-put*
|
||||
When a register contains text from one line (characterwise), using a
|
||||
blockwise Visual selection, putting that register will paste that text
|
||||
|
@@ -142,13 +142,16 @@ CTRL-R {register} *c_CTRL-R* *c_<C-R>*
|
||||
typing CTRL-R and the second character '"' will be displayed
|
||||
to indicate that you are expected to enter the name of a
|
||||
register.
|
||||
The text is inserted as if you typed it, but mappings and
|
||||
abbreviations are not used. Command-line completion through
|
||||
'wildchar' is not triggered though. And characters that end
|
||||
the command line are inserted literally (<Esc>, <CR>, <NL>,
|
||||
<C-C>). A <BS> or CTRL-W could still end the command line
|
||||
though, and remaining characters will then be interpreted in
|
||||
another mode, which might not be what you intended.
|
||||
When used with named or clipboard registers (A-Z,a-z,0-9,+)
|
||||
text is inserted literally like pasting with "p". For other
|
||||
registers, the text is inserted as if you typed it, but
|
||||
mappings and abbreviations are not used. Command-line
|
||||
completion through 'wildchar' is not triggered though. And
|
||||
characters that end the command line are inserted literally
|
||||
(<Esc>, <CR>, <NL>, <C-C>). A <BS> or CTRL-W could still end
|
||||
the command line though, and remaining characters will then be
|
||||
interpreted in another mode, which might not be what you
|
||||
intended.
|
||||
Special registers:
|
||||
'"' the unnamed register, containing the text of
|
||||
the last delete or yank
|
||||
@@ -176,7 +179,9 @@ CTRL-R {register} *c_CTRL-R* *c_<C-R>*
|
||||
sure the expression evaluates to an empty
|
||||
string. E.g.: >
|
||||
<C-R><C-R>=setcmdpos(2)[-1]<CR>
|
||||
< See |registers| about registers.
|
||||
< You can use this to insert a register as
|
||||
typed with CTRL-R =@reg.
|
||||
See |registers| about registers.
|
||||
Implementation detail: When using the |expression| register
|
||||
and invoking setcmdpos(), this sets the position before
|
||||
inserting the resulting string. Use CTRL-R CTRL-R to set the
|
||||
|
@@ -451,6 +451,8 @@ Use existing common {verb} names (actions) if possible:
|
||||
a handler. |dev-name-events|
|
||||
- open: Opens something (a buffer, window, …)
|
||||
- parse: Parses something into a structured form
|
||||
- request: Calls a remote (network, RPC) operation.
|
||||
- send: Writes data or a message to a channel.
|
||||
- set: Sets a thing (or group of things)
|
||||
- start: Spin up a long-lived process. Prefer "enable" except when
|
||||
"start" is obviously more appropriate.
|
||||
@@ -461,7 +463,7 @@ Do NOT use these deprecated verbs:
|
||||
- contains: Prefer "has".
|
||||
- disable: Prefer `enable(enable: boolean)`.
|
||||
- exit: Prefer "cancel" (or "stop" if appropriate).
|
||||
- is_disabled: Prefer `is_enabled()`.
|
||||
- is_disabled: Prefer `!is_enabled()`.
|
||||
- list: Redundant with "get"
|
||||
- notify: Redundant with "print", "echo"
|
||||
- show: Redundant with "print", "echo"
|
||||
|
@@ -101,13 +101,15 @@ CTRL-N Find next keyword (see |i_CTRL-N|).
|
||||
CTRL-P Find previous keyword (see |i_CTRL-P|).
|
||||
|
||||
CTRL-R {register} *i_CTRL-R*
|
||||
Insert the contents of a register. Between typing CTRL-R and
|
||||
Insert the contents of a register. Between typing CTRL-R and
|
||||
the second character, '"' will be displayed to indicate that
|
||||
you are expected to enter the name of a register.
|
||||
The text is inserted as if you typed it, but mappings and
|
||||
you are expected to enter the name of a register. When used
|
||||
with named or clipboard registers (A-Z,a-z,0-9,+) text is
|
||||
inserted literally like pasting with "p". For other registers,
|
||||
the text is inserted as if you typed it, but mappings and
|
||||
abbreviations are not used. If you have options like
|
||||
'textwidth', 'formatoptions', or 'autoindent' set, this will
|
||||
influence what will be inserted. This is different from what
|
||||
influence what will be inserted. This is different from what
|
||||
happens with the "p" command and pasting with the mouse.
|
||||
Special registers:
|
||||
'"' the unnamed register, containing the text of
|
||||
@@ -131,6 +133,8 @@ CTRL-R {register} *i_CTRL-R*
|
||||
special keys. E.g., you can use this to move
|
||||
the cursor up:
|
||||
CTRL-R ="\<Up>"
|
||||
you can use this to insert a register as
|
||||
typed with CTRL-R =@reg.
|
||||
Use CTRL-R CTRL-R to insert text literally.
|
||||
When the result is a |List| the items are used
|
||||
as lines. They can have line breaks inside
|
||||
|
@@ -162,7 +162,7 @@ order of 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.
|
||||
|
||||
Example: given the following configs... >lua
|
||||
@@ -280,6 +280,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
|
||||
@@ -2223,6 +2229,47 @@ is_enabled({filter}) *vim.lsp.inlay_hint.is_enabled()*
|
||||
==============================================================================
|
||||
Lua module: vim.lsp.inline_completion *lsp-inline_completion*
|
||||
|
||||
This module provides the LSP "inline completion" feature, for completing
|
||||
multiline text (e.g., whole methods) instead of just a word or line, which may
|
||||
result in "syntactically or semantically incorrect" code. Unlike regular
|
||||
completion, this is typically presented as overlay text instead of a menu of
|
||||
completion candidates.
|
||||
|
||||
LSP spec:
|
||||
https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_inlineCompletion
|
||||
|
||||
To try it out, here is a quickstart example using Copilot: *lsp-copilot*
|
||||
1. Install Copilot: >sh
|
||||
npm install --global @github/copilot-language-server
|
||||
<
|
||||
2. Define a config, (or copy `lsp/copilot.lua` from
|
||||
https://github.com/neovim/nvim-lspconfig): >lua
|
||||
vim.lsp.config('copilot', {
|
||||
cmd = { 'copilot-language-server', '--stdio', },
|
||||
root_markers = { '.git' },
|
||||
})
|
||||
<
|
||||
3. Activate the config: >lua
|
||||
vim.lsp.enable('copilot')
|
||||
<
|
||||
4. Sign in to Copilot, or use the `:LspCopilotSignIn` command from
|
||||
https://github.com/neovim/nvim-lspconfig
|
||||
5. Enable inline completion: >lua
|
||||
vim.lsp.inline_completion.enable()
|
||||
<
|
||||
6. Set a keymap for `vim.lsp.inline_completion.get()` and invoke the keymap.
|
||||
|
||||
|
||||
*vim.lsp.inline_completion.Item*
|
||||
|
||||
Fields: ~
|
||||
• {client_id} (`integer`) Client ID
|
||||
• {insert_text} (`string|lsp.StringValue`) The text to be inserted, can
|
||||
be a snippet.
|
||||
• {range}? (`vim.Range`) Which range it be applied.
|
||||
• {command}? (`lsp.Command`) Corresponding server command.
|
||||
|
||||
|
||||
enable({enable}, {filter}) *vim.lsp.inline_completion.enable()*
|
||||
Enables or disables inline completion for the {filter}ed scope, inline
|
||||
completion will automatically be refreshed when you are in insert mode.
|
||||
@@ -2240,9 +2287,9 @@ enable({enable}, {filter}) *vim.lsp.inline_completion.enable()*
|
||||
for all.
|
||||
|
||||
get({opts}) *vim.lsp.inline_completion.get()*
|
||||
Apply the currently displayed completion candidate to the buffer.
|
||||
Accept the currently displayed completion candidate to the buffer.
|
||||
|
||||
It returns false when no candidate can be applied, so you can use the
|
||||
It returns false when no candidate can be accepted, so you can use the
|
||||
return value to implement a fallback: >lua
|
||||
vim.keymap.set('i', '<Tab>', function()
|
||||
if not vim.lsp.inline_completion.get() then
|
||||
@@ -2259,6 +2306,10 @@ get({opts}) *vim.lsp.inline_completion.get()*
|
||||
• {opts} (`table?`) A table with the following fields:
|
||||
• {bufnr}? (`integer`, default: 0) Buffer handle, or 0 for
|
||||
current.
|
||||
• {on_accept}? (`fun(item: vim.lsp.inline_completion.Item)`)
|
||||
Accept handler, called with the accepted item. If not
|
||||
provided, the default handler is used, which applies changes
|
||||
to the buffer based on the completion item.
|
||||
|
||||
Return: ~
|
||||
(`boolean`) `true` if a completion was applied, else `false`.
|
||||
@@ -2371,6 +2422,33 @@ set_level({level}) *vim.lsp.log.set_level()*
|
||||
• {level} (`string|integer`) One of |vim.log.levels|
|
||||
|
||||
|
||||
==============================================================================
|
||||
Lua module: vim.lsp.on_type_formatting *lsp-on_type_formatting*
|
||||
|
||||
enable({enable}, {filter}) *vim.lsp.on_type_formatting.enable()*
|
||||
Enables/disables on-type formatting globally or for the {filter}ed scope.
|
||||
The following are some practical usage examples: >lua
|
||||
-- Enable for all clients
|
||||
vim.lsp.on_type_formatting.enable()
|
||||
|
||||
-- Enable for a specific client
|
||||
vim.api.nvim_create_autocmd('LspAttach', {
|
||||
callback = function(args)
|
||||
local client_id = args.data.client_id
|
||||
local client = assert(vim.lsp.get_client_by_id(client_id))
|
||||
if client.name == 'rust-analyzer' then
|
||||
vim.lsp.on_type_formatting.enable(true, { client_id = client_id })
|
||||
end
|
||||
end,
|
||||
})
|
||||
<
|
||||
|
||||
Parameters: ~
|
||||
• {enable} (`boolean?`) true/nil to enable, false to disable.
|
||||
• {filter} (`table?`) Optional filters |kwargs|:
|
||||
• {client_id} (`integer?`) Client ID, or `nil` for all.
|
||||
|
||||
|
||||
==============================================================================
|
||||
Lua module: vim.lsp.rpc *lsp-rpc*
|
||||
|
||||
|
@@ -10,19 +10,18 @@
|
||||
==============================================================================
|
||||
Introduction *lua-guide*
|
||||
|
||||
This guide will go through the basics of using Lua in Nvim. It is not meant
|
||||
to be a comprehensive encyclopedia of all available features, nor will it
|
||||
detail all intricacies. Think of it as a survival kit -- the bare minimum
|
||||
needed to know to comfortably get started on using Lua in Nvim.
|
||||
|
||||
An important thing to note is that this isn't a guide to the Lua language
|
||||
itself. Rather, this is a guide on how to configure and modify Nvim through
|
||||
the Lua language and the functions we provide to help with this. Take a look
|
||||
at |luaref| and |lua-concepts| if you'd like to learn more about Lua itself.
|
||||
Similarly, this guide assumes some familiarity with the basics of Nvim
|
||||
(commands, options, mappings, autocommands), which are covered in the
|
||||
This guide introduces the basics of everyday Lua usage for configuring and
|
||||
controlling Nvim. It assumes some familiarity with the (non-Lua) basics of
|
||||
Nvim (commands, options, mappings, autocommands), which are covered in the
|
||||
|user-manual|.
|
||||
|
||||
This is not a comprehensive encyclopedia of all available features. Think of
|
||||
it as a survival kit: the bare minimum needed to comfortably get started on
|
||||
using Lua in Nvim.
|
||||
|
||||
See |lua-plugin| for guidance on developing Lua plugins.
|
||||
See |luaref| and |lua-concepts| for details on the Lua programming language.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
Some words on the API *lua-guide-api*
|
||||
|
||||
|
313
runtime/doc/lua-plugin.txt
Normal file
313
runtime/doc/lua-plugin.txt
Normal file
@@ -0,0 +1,313 @@
|
||||
*lua-plugin.txt* Nvim
|
||||
|
||||
NVIM REFERENCE MANUAL
|
||||
|
||||
Guide to developing Lua plugins for Nvim
|
||||
|
||||
|
||||
Type |gO| to see the table of contents.
|
||||
|
||||
==============================================================================
|
||||
Introduction *lua-plugin*
|
||||
|
||||
This document provides guidance for developing Nvim Lua plugins.
|
||||
|
||||
See |lua-guide| for guidance on using Lua to configure and operate Nvim.
|
||||
See |luaref| and |lua-concepts| for details on the Lua programming language.
|
||||
|
||||
==============================================================================
|
||||
Creating your first plugin *lua-plugin-new*
|
||||
|
||||
Any Vimscript or Lua code file that lives in the right directory,
|
||||
automatically is a "plugin". There's no manifest or "registration" step.
|
||||
|
||||
You can try it right now:
|
||||
|
||||
1. Visit your config directory: >
|
||||
:exe 'edit' stdpath('config')
|
||||
<
|
||||
2. Create a `plugin/foo.lua` file in there.
|
||||
3. Add something to it, like: >lua
|
||||
vim.print('Hello World')
|
||||
<
|
||||
4. Start `nvim` and notice that it prints "Hello World" in the messages area.
|
||||
Check `:messages` if you don't see it.
|
||||
|
||||
Besides `plugin/foo.lua`, which is always run at startup, you can define Lua
|
||||
modules in the `lua/` directory. Those modules aren't loaded until your
|
||||
`plugin/foo.lua`, or the user, calls `require(…)`.
|
||||
|
||||
==============================================================================
|
||||
Type safety *lua-plugin-type-safety*
|
||||
|
||||
Lua, as a dynamically typed language, is great for configuration. It provides
|
||||
virtually immediate feedback.
|
||||
But for larger projects, this can be a double-edged sword, leaving your plugin
|
||||
susceptible to unexpected bugs at the wrong time.
|
||||
|
||||
You can leverage LuaCATS or "emmylua" annotations https://luals.github.io/wiki/annotations/
|
||||
along with lua-language-server ("LuaLS") https://luals.github.io/ to catch
|
||||
potential bugs in your CI before your plugin's users do. The Nvim codebase
|
||||
uses these annotations extensively.
|
||||
|
||||
TOOLS
|
||||
|
||||
- lua-typecheck-action https://github.com/marketplace/actions/lua-typecheck-action
|
||||
- lua-language-server https://luals.github.io
|
||||
|
||||
==============================================================================
|
||||
Keymaps *lua-plugin-keymaps*
|
||||
|
||||
Avoid creating excessive keymaps automatically. Doing so can conflict with
|
||||
user |mapping|s.
|
||||
|
||||
NOTE: An example for uncontroversial keymaps are buffer-local |mapping|s for
|
||||
specific file types or floating windows, or <Plug> mappings.
|
||||
|
||||
A common approach to allow keymap configuration is to define a declarative DSL
|
||||
https://en.wikipedia.org/wiki/Domain-specific_language via a `setup` function.
|
||||
|
||||
However, doing so means that
|
||||
|
||||
- You will have to implement and document it yourself.
|
||||
- Users will likely face inconsistencies if another plugin has a slightly
|
||||
different DSL.
|
||||
- |init.lua| scripts that call such a `setup` function may throw an error if
|
||||
the plugin is not installed or disabled.
|
||||
|
||||
As an alternative, you can provide |<Plug>| mappings to allow users to define
|
||||
their own keymaps with |vim.keymap.set()|.
|
||||
|
||||
- This requires one line of code in user configs.
|
||||
- Even if your plugin is not installed or disabled, creating the keymap won't
|
||||
throw an error.
|
||||
|
||||
Another option is to simply expose a Lua function or |user-commands|.
|
||||
|
||||
Some benefits of |<Plug>| mappings are that you can
|
||||
|
||||
- Enforce options like `expr = true`.
|
||||
- Use |vim.keymap|'s built-in mode handling to expose functionality only for
|
||||
specific |map-modes|.
|
||||
- Handle different |map-modes| differently with a single mapping, without
|
||||
adding mode checks to the underlying implementation.
|
||||
- Detect user-defined mappings through |hasmapto()| before creating defaults.
|
||||
|
||||
Some benefits of exposing a Lua function are:
|
||||
|
||||
- Extensibility, if the function takes an options table as an argument.
|
||||
- A cleaner UX, if there are many options and enumerating all combinations
|
||||
of options would result in a lot of |<Plug>| mappings.
|
||||
|
||||
NOTE: If your function takes an options table, users may still benefit
|
||||
from |<Plug>| mappings for the most common combinations.
|
||||
|
||||
KEYMAP EXAMPLE
|
||||
|
||||
In your plugin:
|
||||
>lua
|
||||
vim.keymap.set('n', '<Plug>(SayHello)', function()
|
||||
print('Hello from normal mode')
|
||||
end)
|
||||
|
||||
vim.keymap.set('v', '<Plug>(SayHello)', function()
|
||||
print('Hello from visual mode')
|
||||
end)
|
||||
<
|
||||
In the user's config:
|
||||
>lua
|
||||
vim.keymap.set({'n', 'v'}, '<leader>h', '<Plug>(SayHello)')
|
||||
<
|
||||
==============================================================================
|
||||
Initialization *lua-plugin-init*
|
||||
|
||||
Newcomers to Lua plugin development will often put all initialization logic in
|
||||
a single `setup` function, which takes a table of options.
|
||||
If you do this, users will be forced to call this function in order to use
|
||||
your plugin, even if they are happy with the default configuration.
|
||||
|
||||
Strictly separated configuration and smart initialization allow your plugin to
|
||||
work out of the box.
|
||||
|
||||
NOTE: A well designed plugin has minimal impact on startup time. See also
|
||||
|lua-plugin-lazy|.
|
||||
|
||||
Common approaches to a strictly separated configuration are:
|
||||
|
||||
- A Lua function, e.g. `setup(opts)` or `configure(opts)`, which only overrides the
|
||||
default configuration and does not contain any initialization logic.
|
||||
- A Vimscript compatible table (e.g. in the |vim.g| or |vim.b| namespace) that your
|
||||
plugin reads from and validates at initialization time.
|
||||
See also |lua-vim-variables|.
|
||||
|
||||
Typically, automatic initialization logic is done in a |plugin| or |ftplugin|
|
||||
script. See also |'runtimepath'|.
|
||||
|
||||
==============================================================================
|
||||
Lazy loading *lua-plugin-lazy*
|
||||
|
||||
Some users like to micro-manage "lazy loading" of plugins by explicitly
|
||||
configuring which commands and key mappings load the plugin.
|
||||
|
||||
Your plugin should not depend on every user micro-managing their configuration
|
||||
in such a way. Nvim has a mechanism for every plugin to do its own implicit
|
||||
lazy-loading (in Vimscript it's called |autoload|), via `autoload/`
|
||||
(Vimscript) and `lua/` (Lua). Plugin authors can provide "lazy loading" by
|
||||
providing a `plugin/<name>.lua` file which defines their commands and
|
||||
keymappings. This file should be small, and should not eagerly `require()` the
|
||||
rest of your plugin. Commands and mappings should do the `require()`.
|
||||
|
||||
Guidance:
|
||||
|
||||
- Plugins should arrange their "lazy" behavior once, instead of expecting every user to micromanage it.
|
||||
- Keep `plugin/<name>.lua` small, avoid eagerly calling `require()` on modules
|
||||
until a command or mapping is actually used.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
Defer require() calls *lua-plugin-defer-require*
|
||||
|
||||
`plugin/<name>.lua` scripts (|plugin|) are eagerly run at startup; this is
|
||||
intentional, so that plugins can setup the (minimal) commands and keymappings
|
||||
that users will use to invoke the plugin. This also means these "plugin/"
|
||||
files should NOT eagerly `require` Lua modules.
|
||||
|
||||
For example, instead of:
|
||||
>lua
|
||||
local foo = require('foo')
|
||||
vim.api.nvim_create_user_command('MyCommand', function()
|
||||
foo.do_something()
|
||||
end, {
|
||||
-- ...
|
||||
})
|
||||
<
|
||||
which calls `require('foo')` as soon as the module is loaded, you can
|
||||
lazy-load it by moving the `require` into the command's implementation:
|
||||
>lua
|
||||
vim.api.nvim_create_user_command('MyCommand', function()
|
||||
local foo = require('foo')
|
||||
foo.do_something()
|
||||
end, {
|
||||
-- ...
|
||||
})
|
||||
<
|
||||
Likewise, if a plugin uses a Lua module as an entrypoint, it should
|
||||
defer `require` calls too.
|
||||
|
||||
NOTE: For a Vimscript alternative to `require`, see |autoload|.
|
||||
|
||||
NOTE: If you are worried about eagerly creating user commands, autocommands or
|
||||
keymaps at startup: Plugin managers that provide abstractions for lazy-loading
|
||||
plugins on such events do the same amount of work. There is no performance
|
||||
benefit for users to define lazy-loading entrypoints in their configuration
|
||||
instead of plugins defining it in `plugin/<name>.lua`.
|
||||
|
||||
NOTE: You can use |--startuptime| to |profile| the impact a plugin has on
|
||||
startup time.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
Filetype-specific functionality *lua-plugin-filetype*
|
||||
|
||||
Consider making use of 'filetype' for any functionality that is specific to
|
||||
a filetype, by putting the initialization logic in a `ftplugin/{filetype}.lua`
|
||||
script.
|
||||
|
||||
FILETYPE EXAMPLE
|
||||
|
||||
A plugin tailored to Rust development might have initialization in
|
||||
`ftplugin/rust.lua`:
|
||||
>lua
|
||||
if not vim.g.loaded_my_rust_plugin then
|
||||
-- Initialize
|
||||
end
|
||||
-- NOTE: Using `vim.g.loaded_` prevents the plugin from initializing twice
|
||||
-- and allows users to prevent plugins from loading
|
||||
-- (in both Lua and Vimscript).
|
||||
vim.g.loaded_my_rust_plugin = true
|
||||
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
-- do something specific to this buffer,
|
||||
-- e.g. add a |<Plug>| mapping or create a command
|
||||
vim.keymap.set('n', '<Plug>(MyPluginBufferAction)', function()
|
||||
print('Hello')
|
||||
end, { buffer = bufnr, })
|
||||
<
|
||||
==============================================================================
|
||||
Configuration *lua-plugin-config*
|
||||
|
||||
Once you have merged the default configuration with the user's config, you
|
||||
should validate configs.
|
||||
|
||||
Validations could include:
|
||||
|
||||
- Correct types, see |vim.validate()|
|
||||
- Unknown fields in the user config (e.g. due to typos).
|
||||
This can be tricky to implement, and may be better suited for a |health|
|
||||
check, to reduce overhead.
|
||||
|
||||
==============================================================================
|
||||
Troubleshooting *lua-plugin-troubleshooting*
|
||||
|
||||
While developing a plugin, you can use the |:restart| command to see the
|
||||
result of code changes in your plugin.
|
||||
|
||||
HEALTH
|
||||
|
||||
Nvim's "health" framework gives plugins a simple way to report status checks
|
||||
to users. See |health-dev| for an example.
|
||||
|
||||
Basically, this just means your plugin will have a `lua/{plugin}/health.lua`
|
||||
file. |:checkhealth| will automatically find this file when it runs.
|
||||
|
||||
Some things to validate:
|
||||
|
||||
- User configuration
|
||||
- Proper initialization
|
||||
- Presence of Lua dependencies (e.g. other plugins)
|
||||
- Presence of external dependencies
|
||||
|
||||
MINIMAL CONFIG TEMPLATE
|
||||
|
||||
It can be useful to provide a template for a minimal configuration, along with
|
||||
a guide on how to use it to reproduce issues.
|
||||
|
||||
==============================================================================
|
||||
Versioning and releases *lua-plugin-versioning*
|
||||
|
||||
Consider:
|
||||
|
||||
- Use |vim.deprecate()| or a `---@deprecate` annotation when you need to
|
||||
communicate a (future) breaking change or discouraged practice.
|
||||
- Using SemVer https://semver.org/ tags and releases to properly communicate
|
||||
bug fixes, new features, and breaking changes.
|
||||
- Automating versioning and releases in CI.
|
||||
- Publishing to luarocks https://luarocks.org, especially if your plugin
|
||||
has dependencies or components that need to be built; or if it could be a
|
||||
dependency for another plugin.
|
||||
|
||||
FURTHER READING
|
||||
|
||||
- Luarocks ❤️ Nvim https://github.com/nvim-neorocks/sample-luarocks-plugin
|
||||
|
||||
VERSIONING TOOLS
|
||||
|
||||
- luarocks-tag-release
|
||||
https://github.com/marketplace/actions/luarocks-tag-release
|
||||
- release-please-action
|
||||
https://github.com/marketplace/actions/release-please-action
|
||||
- semantic-release
|
||||
https://github.com/semantic-release/semantic-release
|
||||
|
||||
==============================================================================
|
||||
Documentation *lua-plugin-doc*
|
||||
|
||||
Provide vimdoc (see |help-writing|), so that users can read your plugin's
|
||||
documentation in Nvim, by entering `:h {plugin}` in |command-mode|. The
|
||||
help-tags (the right-aligned "search keywords" in the help documents) are
|
||||
regenerated using the |:helptags| command.
|
||||
|
||||
DOCUMENTATION TOOLS
|
||||
|
||||
- panvimdoc https://github.com/kdheepak/panvimdoc
|
||||
|
||||
|
||||
vim:tw=78:ts=8:sw=4:sts=4:et:ft=help:norl:
|
@@ -843,32 +843,29 @@ vim.ui_detach({ns}) *vim.ui_detach()*
|
||||
• {ns} (`integer`) Namespace ID
|
||||
|
||||
vim.wait({time}, {callback}, {interval}, {fast_only}) *vim.wait()*
|
||||
Wait for {time} in milliseconds until {callback} returns `true`.
|
||||
Waits up to `time` milliseconds, until `callback` returns `true`
|
||||
(success). Executes `callback` immediately, then at intervals of
|
||||
approximately `interval` milliseconds (default 200). Returns all
|
||||
`callback` results on success.
|
||||
|
||||
Executes {callback} immediately and at approximately {interval}
|
||||
milliseconds (default 200). Nvim still processes other events during this
|
||||
time.
|
||||
|
||||
Cannot be called while in an |api-fast| event.
|
||||
Nvim processes other events while waiting. Cannot be called during an
|
||||
|api-fast| event.
|
||||
|
||||
Examples: >lua
|
||||
---
|
||||
-- Wait for 100 ms, allowing other events to process
|
||||
vim.wait(100, function() end)
|
||||
-- Wait for 100 ms, allowing other events to process.
|
||||
vim.wait(100)
|
||||
|
||||
---
|
||||
-- Wait for 100 ms or until global variable set.
|
||||
vim.wait(100, function() return vim.g.waiting_for_var end)
|
||||
-- Wait up to 1000 ms or until `vim.g.foo` is true, at intervals of ~500 ms.
|
||||
vim.wait(1000, function() return vim.g.foo end, 500)
|
||||
|
||||
---
|
||||
-- Wait for 1 second or until global variable set, checking every ~500 ms
|
||||
vim.wait(1000, function() return vim.g.waiting_for_var end, 500)
|
||||
-- Wait up to 100 ms or until `vim.g.foo` is true, and get the callback results.
|
||||
local ok, rv1, rv2, rv3 = vim.wait(100, function()
|
||||
return vim.g.foo, 'a', 42, { ok = { 'yes' } }
|
||||
end)
|
||||
|
||||
---
|
||||
-- Schedule a function to set a value in 100ms
|
||||
-- Schedule a function to set a value in 100ms. This would wait 10s if blocked, but actually
|
||||
-- only waits 100ms because `vim.wait` processes other events while waiting.
|
||||
vim.defer_fn(function() vim.g.timer_result = true end, 100)
|
||||
|
||||
-- Would wait ten seconds if results blocked. Actually only waits 100 ms
|
||||
if vim.wait(10000, function() return vim.g.timer_result end) then
|
||||
print('Only waiting a little bit of time!')
|
||||
end
|
||||
@@ -886,10 +883,10 @@ vim.wait({time}, {callback}, {interval}, {fast_only}) *vim.wait()*
|
||||
Return (multiple): ~
|
||||
(`boolean`)
|
||||
(`-1|-2?`)
|
||||
• If {callback} returns `true` during the {time}: `true, nil`
|
||||
• If {callback} never returns `true` during the {time}: `false, -1`
|
||||
• If {callback} is interrupted during the {time}: `false, -2`
|
||||
• If {callback} errors, the error is raised.
|
||||
• If callback returns `true` before timeout: `true, nil, ...`
|
||||
• On timeout: `false, -1`
|
||||
• On interrupt: `false, -2`
|
||||
• On error: the error is raised.
|
||||
|
||||
|
||||
==============================================================================
|
||||
|
@@ -848,25 +848,32 @@ prompt.
|
||||
==============================================================================
|
||||
4. PROGRESS MESSAGE *progress-message*
|
||||
|
||||
Nvim can emit progress-message, which are a special kind of |ui-messages|
|
||||
used to report the state of long-running tasks.
|
||||
Plugins and core Nvim features can emit "progress" |ui-messages| to report the
|
||||
state of long-running tasks.
|
||||
|
||||
Progress messages are created or updated using |nvim_echo()| with `kind='progress'`
|
||||
and the related options. Each message has a unique `msg_id`. A subsequent
|
||||
message with the same `msg_id` replaces the older one.
|
||||
Create or update a progress-message by calling |nvim_echo()| with
|
||||
`kind='progress'`. Each message has a unique message-id, returned by
|
||||
`nvim_echo`. You can specify the id when calling `nvim_echo` to update an
|
||||
existing progress-message.
|
||||
|
||||
Events: ~
|
||||
• msg_show |ui-messages| event is fired for ext-ui upon creation/update of a
|
||||
progress-message
|
||||
• Updating or creating a progress message also triggers the |Progress| autocommand.
|
||||
|
||||
Example: >
|
||||
local id = vim.api.nvim_echo({{'searching...'}}, true,
|
||||
{kind='progress', status='running', percent=10, title="term"})
|
||||
vim.api.nvim_echo({{'searching'}}, true,
|
||||
{id=id, kind='progress', status='running', percent=50, title="term"})
|
||||
vim.api.nvim_echo({{'done'}}, true,
|
||||
{id=id, kind='progress', status='success', percent=100, title="term"})
|
||||
Example: >lua
|
||||
local progress = {
|
||||
kind = 'progress',
|
||||
status = 'running',
|
||||
percent = 10,
|
||||
title = 'term',
|
||||
}
|
||||
progress.id = vim.api.nvim_echo({ { 'searching...' } }, true, progress)
|
||||
progress.percent = 50
|
||||
vim.api.nvim_echo({ { 'searching' } }, true, progress)
|
||||
progress.status = 'success'
|
||||
progress.percent = 100
|
||||
vim.api.nvim_echo({ { 'done' } }, true, progress)
|
||||
<
|
||||
See also: |nvim_echo()| |ui-messages| |Progress|
|
||||
|
||||
|
@@ -186,7 +186,7 @@ The following new APIs or features were added.
|
||||
Additionally |TSNode:range()| now takes an optional {include_bytes} argument.
|
||||
|
||||
• Treesitter injection queries now use the format described at
|
||||
https://tree-sitter.github.io/tree-sitter/syntax-highlighting#language-injection .
|
||||
https://tree-sitter.github.io/tree-sitter/3-syntax-highlighting.html#language-injection .
|
||||
Support for the previous format will be removed in a future release.
|
||||
|
||||
• Added |nvim_get_hl()| for getting highlight group definitions in a format
|
||||
|
@@ -39,6 +39,11 @@ TREESITTER
|
||||
|
||||
• todo
|
||||
|
||||
UI
|
||||
|
||||
• `progress` attribute removed form |ui-messages| msg_show event
|
||||
|
||||
|
||||
==============================================================================
|
||||
BREAKING CHANGES *news-breaking*
|
||||
|
||||
@@ -46,7 +51,7 @@ These changes may require adaptations in your config or plugins.
|
||||
|
||||
API
|
||||
|
||||
• todo
|
||||
• Decoration provider has `on_range()` callback.
|
||||
|
||||
BUILD
|
||||
|
||||
@@ -63,7 +68,9 @@ DIAGNOSTICS
|
||||
|
||||
EDITOR
|
||||
|
||||
• todo
|
||||
- |i_CTRL-R| inserts named/clipboard registers (A-Z,a-z,0-9+) literally, like
|
||||
pasting instead of like user input. Improves performance, avoids broken
|
||||
formatting. To get the old behavior you can use `<C-R>=@x`.
|
||||
|
||||
EVENTS
|
||||
|
||||
@@ -113,6 +120,7 @@ TREESITTER
|
||||
`metadata[capture_id].offset`. The offset will be applied in
|
||||
|vim.treesitter.get_range()|, which should be preferred over reading
|
||||
metadata directly for retrieving node ranges.
|
||||
• |Query:iter_captures()| supports specifying starting and ending columns.
|
||||
|
||||
TUI
|
||||
|
||||
@@ -190,13 +198,13 @@ EVENTS
|
||||
|
||||
• |CmdlineLeavePre| triggered before preparing to leave the command line.
|
||||
• New `append` paremeter for |ui-messages| `msg_show` event.
|
||||
• New `msg_id` and `progress` paremeter for |ui-messages| `msg_show` event.
|
||||
• New `msg_id` paremeter for |ui-messages| `msg_show` event.
|
||||
• Creating or updating a progress message with |nvim_echo()| triggers a |Progress| event.
|
||||
|
||||
HIGHLIGHTS
|
||||
|
||||
• |hl-DiffTextAdd| highlights added text within a changed line.
|
||||
• |hl-StderrMsg| |hl-StdoutMsg|
|
||||
• |hl-OkMsg| |hl-StderrMsg| |hl-StdoutMsg|
|
||||
• |hl-SnippetTabstopActive| highlights the currently active tabstop.
|
||||
|
||||
LSP
|
||||
@@ -236,9 +244,13 @@ LSP
|
||||
• |vim.lsp.buf.signature_help()| supports "noActiveParameterSupport".
|
||||
• Support for `textDocument/inlineCompletion` |lsp-inline_completion|
|
||||
https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_inlineCompletion
|
||||
See |lsp-inline_completion| for quickstart instructions.
|
||||
• Support for `textDocument/onTypeFormatting`: |lsp-on_type_formatting|
|
||||
https://microsoft.github.io/language-server-protocol/specification/#textDocument_onTypeFormatting
|
||||
|
||||
LUA
|
||||
|
||||
• |vim.wait()| returns the callback results.
|
||||
• Lua type annotations for `vim.uv`.
|
||||
• |vim.hl.range()| now allows multiple timed highlights.
|
||||
• |vim.tbl_extend()| and |vim.tbl_deep_extend()| now accept a function behavior argument.
|
||||
@@ -280,6 +292,7 @@ PERFORMANCE
|
||||
support for nested braces and follows LSP 3.17 specification with
|
||||
additional constraints for improved correctness and resistance to
|
||||
backtracking edge cases.
|
||||
- |i_CTRL-R| inserts named/clipboard registers literally, 10x speedup.
|
||||
|
||||
PLUGINS
|
||||
|
||||
|
@@ -649,11 +649,11 @@ ask you where to write the file (there must be a writable directory in
|
||||
'runtimepath' for this).
|
||||
|
||||
The plugin has a default place where to look for spell files, on the Vim ftp
|
||||
server. The protocol used is SSL (https://) for security. If you want to use
|
||||
another location or another protocol, set the g:spellfile_URL variable to the
|
||||
directory that holds the spell files. You can use http:// or ftp://, but you
|
||||
are taking a security risk then. The |netrw| plugin is used for getting the
|
||||
file, look there for the specific syntax of the URL. Example: >
|
||||
server. The protocol used is TLS (`https://`) for security. If you want to
|
||||
use another location or another protocol, set the g:spellfile_URL variable to
|
||||
the directory that holds the spell files. You can use `http://` or `ftp://`,
|
||||
but you are taking a security risk then. The |netrw| plugin is used for
|
||||
getting the file, look there for the specific syntax of the URL. Example: >
|
||||
let g:spellfile_URL = 'https://ftp.nluug.nl/vim/runtime/spell'
|
||||
You may need to escape special characters.
|
||||
|
||||
|
@@ -5263,8 +5263,12 @@ EndOfBuffer Filler lines (~) after the last line in the buffer.
|
||||
By default, this is highlighted like |hl-NonText|.
|
||||
*hl-TermCursor*
|
||||
TermCursor Cursor in a focused terminal.
|
||||
*hl-OkMsg*
|
||||
OkMsg Success messages.
|
||||
*hl-WarningMsg*
|
||||
WarningMsg Warning messages.
|
||||
*hl-ErrorMsg*
|
||||
ErrorMsg Error messages on the command line.
|
||||
ErrorMsg Error messages.
|
||||
*hl-StderrMsg*
|
||||
StderrMsg Messages in stderr from shell commands.
|
||||
*hl-StdoutMsg*
|
||||
@@ -5405,8 +5409,6 @@ Title Titles for output from ":set all", ":autocmd" etc.
|
||||
Visual Visual mode selection.
|
||||
*hl-VisualNOS*
|
||||
VisualNOS Visual mode selection when vim is "Not Owning the Selection".
|
||||
*hl-WarningMsg*
|
||||
WarningMsg Warning messages.
|
||||
*hl-Whitespace*
|
||||
Whitespace "nbsp", "space", "tab", "multispace", "lead" and "trail"
|
||||
in 'listchars'.
|
||||
|
@@ -551,7 +551,7 @@ TREESITTER LANGUAGE INJECTIONS *treesitter-language-injections*
|
||||
<
|
||||
|
||||
Note the following information is adapted from:
|
||||
https://tree-sitter.github.io/tree-sitter/syntax-highlighting#language-injection
|
||||
https://tree-sitter.github.io/tree-sitter/3-syntax-highlighting.html#language-injection
|
||||
|
||||
Some source files contain code written in multiple different languages.
|
||||
Examples include:
|
||||
@@ -1522,7 +1522,7 @@ them to parse text. See |vim.treesitter.query.parse()| for a working example.
|
||||
• {has_combined_injections} (`boolean`) whether the query contains
|
||||
combined injections
|
||||
• {query} (`TSQuery`) userdata query object
|
||||
• {iter_captures} (`fun(self: vim.treesitter.Query, node: TSNode, source: integer|string, start: integer?, stop: integer?, opts: table?): fun(end_line: integer?): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch, TSTree`)
|
||||
• {iter_captures} (`fun(self: vim.treesitter.Query, node: TSNode, source: integer|string, start_row: integer?, end_row: integer?, opts: table?): fun(end_line: integer?, end_col: integer?): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch, TSTree`)
|
||||
See |Query:iter_captures()|.
|
||||
• {iter_matches} (`fun(self: vim.treesitter.Query, node: TSNode, source: integer|string, start: integer?, stop: integer?, opts: table?): fun(): integer, table<integer, TSNode[]>, vim.treesitter.query.TSMetadata, TSTree`)
|
||||
See |Query:iter_matches()|.
|
||||
@@ -1693,7 +1693,7 @@ parse({lang}, {query}) *vim.treesitter.query.parse()*
|
||||
• |vim.treesitter.query.get()|
|
||||
|
||||
*Query:iter_captures()*
|
||||
Query:iter_captures({node}, {source}, {start}, {stop}, {opts})
|
||||
Query:iter_captures({node}, {source}, {start_row}, {end_row}, {opts})
|
||||
Iterates over all captures from all matches in {node}.
|
||||
|
||||
{source} is required if the query contains predicates; then the caller
|
||||
@@ -1725,22 +1725,26 @@ Query:iter_captures({node}, {source}, {start}, {stop}, {opts})
|
||||
contained predicates.
|
||||
|
||||
Parameters: ~
|
||||
• {node} (`TSNode`) under which the search will occur
|
||||
• {source} (`integer|string`) Source buffer or string to extract text
|
||||
from
|
||||
• {start} (`integer?`) Starting line for the search. Defaults to
|
||||
`node:start()`.
|
||||
• {stop} (`integer?`) Stopping line for the search (end-exclusive).
|
||||
Defaults to `node:end_()`.
|
||||
• {opts} (`table?`) Optional keyword arguments:
|
||||
• max_start_depth (integer) if non-zero, sets the maximum
|
||||
start depth for each match. This is used to prevent
|
||||
traversing too deep into a tree.
|
||||
• match_limit (integer) Set the maximum number of
|
||||
in-progress matches (Default: 256).
|
||||
• {node} (`TSNode`) under which the search will occur
|
||||
• {source} (`integer|string`) Source buffer or string to extract
|
||||
text from
|
||||
• {start_row} (`integer?`) Starting line for the search. Defaults to
|
||||
`node:start()`.
|
||||
• {end_row} (`integer?`) Stopping line for the search (end-inclusive,
|
||||
unless `stop_col` is provided). Defaults to
|
||||
`node:end_()`.
|
||||
• {opts} (`table?`) Optional keyword arguments:
|
||||
• max_start_depth (integer) if non-zero, sets the maximum
|
||||
start depth for each match. This is used to prevent
|
||||
traversing too deep into a tree.
|
||||
• match_limit (integer) Set the maximum number of
|
||||
in-progress matches (Default: 256).
|
||||
• start_col (integer) Starting column for the search.
|
||||
• end_col (integer) Stopping column for the search
|
||||
(end-exclusive).
|
||||
|
||||
Return: ~
|
||||
(`fun(end_line: integer?): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch, TSTree`)
|
||||
(`fun(end_line: integer?, end_col: integer?): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch, TSTree`)
|
||||
capture id, capture node, metadata, match, tree
|
||||
|
||||
*Query:iter_matches()*
|
||||
|
@@ -136,10 +136,10 @@ might want to try the manual way of downloading the file.
|
||||
Accessing files over the internet works with the netrw plugin. Currently URLs
|
||||
with these formats are recognized:
|
||||
|
||||
ftp:// uses ftp
|
||||
rcp:// uses rcp
|
||||
scp:// uses scp
|
||||
http:// uses wget (reading only)
|
||||
`ftp://` uses ftp
|
||||
`rcp://` uses rcp
|
||||
`scp://` uses scp
|
||||
`http://` uses wget (reading only)
|
||||
|
||||
Vim doesn't do the communication itself, it relies on the mentioned programs
|
||||
to be available on your computer. On most Unix systems "ftp" and "rcp" will
|
||||
@@ -147,7 +147,7 @@ be present. "scp" and "wget" might need to be installed.
|
||||
|
||||
Vim detects these URLs for each command that starts editing a new file, also
|
||||
with ":edit" and ":split", for example. Write commands also work, except for
|
||||
http://.
|
||||
`http://`.
|
||||
|
||||
For more information, also about passwords, see |netrw|.
|
||||
|
||||
|
@@ -316,6 +316,9 @@ Commands:
|
||||
Editor:
|
||||
- |prompt-buffer| supports multiline input/paste, undo/redo, and o/O normal
|
||||
commands.
|
||||
- |i_CTRL-R| inserts named/clipboard registers (A-Z,a-z,0-9+) literally, like
|
||||
pasting instead of like user input. Improves performance, avoids broken
|
||||
formatting. To get the old behavior you can use `<C-R>=@x`.
|
||||
|
||||
Events (autocommands):
|
||||
- Fixed inconsistent behavior in execution of nested autocommands #23368
|
||||
|
@@ -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
|
||||
|
75
runtime/keymap/arabic_buckwalter.vim
Normal file
75
runtime/keymap/arabic_buckwalter.vim
Normal file
@@ -0,0 +1,75 @@
|
||||
" Vim Keymap file for Arabic using Buckwalter Transliteration
|
||||
" see
|
||||
" http://qamus.org/transliteration.htm
|
||||
" https://en.wikipedia.org/wiki/Buckwalter_transliteration
|
||||
"
|
||||
" Created By: Rafael Ketsetsides <rketsetsides@gmail.com>
|
||||
" Last Update: 2025 Sep 02
|
||||
|
||||
let b:keymap_name = "arabic_buckwalter"
|
||||
|
||||
loadkeymap
|
||||
|
||||
A <char-0x0627> " ALEF
|
||||
b <char-0x0628> " BEH
|
||||
p <char-0x0629> " TEH MARBUTA
|
||||
t <char-0x062a> " TEH
|
||||
v <char-0x062b> " THEH
|
||||
j <char-0x062c> " JEEM
|
||||
H <char-0x062d> " HAH
|
||||
x <char-0x062e> " KHAH
|
||||
d <char-0x062f> " DAL
|
||||
* <char-0x0630> " THAL
|
||||
r <char-0x0631> " REH
|
||||
z <char-0x0632> " ZAIN
|
||||
s <char-0x0633> " SEEN
|
||||
$ <char-0x0634> " SHEEN
|
||||
S <char-0x0635> " SAD
|
||||
D <char-0x0636> " DAD
|
||||
T <char-0x0637> " TAH
|
||||
Z <char-0x0638> " ZAH
|
||||
E <char-0x0639> " AIN
|
||||
g <char-0x063a> " GHAIN
|
||||
_ <char-0x0640> " TATWEEL
|
||||
f <char-0x0641> " FEH
|
||||
q <char-0x0642> " QAF
|
||||
k <char-0x0643> " KAF
|
||||
l <char-0x0644> " LAM
|
||||
m <char-0x0645> " MEEM
|
||||
n <char-0x0646> " NOON
|
||||
h <char-0x0647> " HEH
|
||||
w <char-0x0648> " WAW
|
||||
Y <char-0x0649> " ALEF MAKSURA
|
||||
y <char-0x064a> " YEH
|
||||
' <char-0x0621> " HAMZA
|
||||
| <char-0x0622> " ALEF WITH MADDA ABOVE
|
||||
> <char-0x0623> " ALEF WITH HAMZA ABOVE
|
||||
& <char-0x0624> " WAW WITH HAMZA ABOVE
|
||||
< <char-0x0625> " ALEF WITH HAMZA BELOW
|
||||
} <char-0x0626> " YEH WITH HAMZA ABOVE
|
||||
F <char-0x064b> " Tanween -- FATHATAN
|
||||
N <char-0x064c> " Tanween -- DAMMATAN
|
||||
K <char-0x064d> " Tanween -- KASRATAN
|
||||
a <char-0x064e> " Tanween -- FATHA
|
||||
u <char-0x064f> " Tanween -- DAMMA
|
||||
i <char-0x0650> " Tanween -- KASRA
|
||||
~ <char-0x0651> " Tanween -- SHADDA
|
||||
o <char-0x0652> " Tanween -- SUKUN
|
||||
; <char-0x061b> " Arabic Semicolon
|
||||
, <char-0x060c> " Arabic Comma
|
||||
? <char-0x061f> " Arabic Question Mark
|
||||
{ <char-0x0671> " ALEF WASLA
|
||||
P <char-0x067E> " PEH
|
||||
J <char-0x0686> " TCHEH
|
||||
V <char-0x06A4> " VEH
|
||||
G <char-0x06AF> " GAF
|
||||
0 <char-0x06F0> " Arabic-Indic 0
|
||||
1 <char-0x06F1> " Arabic-Indic 1
|
||||
2 <char-0x06F2> " Arabic-Indic 2
|
||||
3 <char-0x06F3> " Arabic-Indic 3
|
||||
4 <char-0x06F4> " Arabic-Indic 4
|
||||
5 <char-0x06F5> " Arabic-Indic 5
|
||||
6 <char-0x06F6> " Arabic-Indic 6
|
||||
7 <char-0x06F7> " Arabic-Indic 7
|
||||
8 <char-0x06F8> " Arabic-Indic 8
|
||||
9 <char-0x06F9> " Arabic-Indic 9
|
@@ -8,7 +8,7 @@ local M = {}
|
||||
--- @param env? table<string,string|number>
|
||||
--- @return string
|
||||
local function system(cmd, silent, env)
|
||||
if vim.fn.executable(cmd[1]) == 0 then
|
||||
if fn.executable(cmd[1]) == 0 then
|
||||
error(string.format('executable not found: "%s"', cmd[1]), 0)
|
||||
end
|
||||
|
||||
@@ -651,10 +651,10 @@ function M.init_pager()
|
||||
|
||||
-- Raw manpage into (:Man!) overlooks `match('man://')` condition,
|
||||
-- so if the buffer already exists, create new with a non existing name.
|
||||
if vim.fn.bufexists(man_bufname) == 1 then
|
||||
if fn.bufexists(man_bufname) == 1 then
|
||||
local new_bufname = man_bufname
|
||||
for i = 1, 100 do
|
||||
if vim.fn.bufexists(new_bufname) == 0 then
|
||||
if fn.bufexists(new_bufname) == 0 then
|
||||
break
|
||||
end
|
||||
new_bufname = ('%s?new=%s'):format(man_bufname, i)
|
||||
|
@@ -148,7 +148,7 @@ end
|
||||
---@return fun(line: string): string
|
||||
local function make_comment_function(parts, indent)
|
||||
local prefix, nonindent_start, suffix = indent .. parts.left, indent:len() + 1, parts.right
|
||||
local blank_comment = indent .. vim.trim(parts.left) .. vim.trim(parts.right)
|
||||
local blank_comment = indent .. vim.trim(parts.left) .. vim.trim(suffix)
|
||||
|
||||
return function(line)
|
||||
if is_blank(line) then
|
||||
|
@@ -8,9 +8,10 @@ local M = {}
|
||||
--- @param listed string[] Already listed servers
|
||||
--- @return string[] # List of servers found on the current machine in stdpath("run").
|
||||
function M.serverlist(listed)
|
||||
local root = vim.fs.normalize(vim.fn.stdpath('run') .. '/..')
|
||||
local socket_paths = vim.fs.find(function(name, _)
|
||||
return name:match('nvim.*')
|
||||
end, { path = vim.fn.stdpath('run'), type = 'socket', limit = math.huge })
|
||||
end, { path = root, type = 'socket', limit = math.huge })
|
||||
|
||||
local found = {} ---@type string[]
|
||||
for _, socket in ipairs(socket_paths) do
|
||||
|
15
runtime/lua/vim/_meta/api.lua
generated
15
runtime/lua/vim/_meta/api.lua
generated
@@ -2128,7 +2128,7 @@ function vim.api.nvim_set_current_win(window) end
|
||||
--- Note: this function should not be called often. Rather, the callbacks
|
||||
--- themselves can be used to throttle unneeded callbacks. the `on_start`
|
||||
--- callback can return `false` to disable the provider until the next redraw.
|
||||
--- Similarly, return `false` in `on_win` will skip the `on_line` calls
|
||||
--- Similarly, return `false` in `on_win` will skip the `on_line` and `on_range` calls
|
||||
--- for that window (but any extmarks set in `on_win` will still be used).
|
||||
--- A plugin managing multiple sources of decoration should ideally only set
|
||||
--- one provider, and merge the sources internally. You can use multiple `ns_id`
|
||||
@@ -2140,7 +2140,7 @@ function vim.api.nvim_set_current_win(window) end
|
||||
--- Doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is quite dubious
|
||||
--- for the moment.
|
||||
---
|
||||
--- Note: It is not allowed to remove or update extmarks in `on_line` callbacks.
|
||||
--- Note: It is not allowed to remove or update extmarks in `on_line` or `on_range` callbacks.
|
||||
---
|
||||
--- @param ns_id integer Namespace id from `nvim_create_namespace()`
|
||||
--- @param opts vim.api.keyset.set_decoration_provider Table of callbacks:
|
||||
@@ -2157,11 +2157,18 @@ function vim.api.nvim_set_current_win(window) end
|
||||
--- ```
|
||||
--- ["win", winid, bufnr, toprow, botrow]
|
||||
--- ```
|
||||
--- - on_line: called for each buffer line being redrawn.
|
||||
--- (The interaction with fold lines is subject to change)
|
||||
--- - on_line: (deprecated, use on_range instead)
|
||||
--- ```
|
||||
--- ["line", winid, bufnr, row]
|
||||
--- ```
|
||||
--- - on_range: called for each buffer range being redrawn.
|
||||
--- Range is end-exclusive and may span multiple lines. Range
|
||||
--- bounds point to the first byte of a character. An end position
|
||||
--- of the form (lnum, 0), including (number of lines, 0), is valid
|
||||
--- and indicates that EOL of the preceding line is included.
|
||||
--- ```
|
||||
--- ["range", winid, bufnr, begin_row, begin_col, end_row, end_col]
|
||||
--- ```
|
||||
--- - on_end: called at the end of a redraw cycle
|
||||
--- ```
|
||||
--- ["end", tick]
|
||||
|
1
runtime/lua/vim/_meta/api_keysets.lua
generated
1
runtime/lua/vim/_meta/api_keysets.lua
generated
@@ -382,6 +382,7 @@ error('Cannot require a meta file')
|
||||
--- @field on_buf? fun(_: "buf", bufnr: integer, tick: integer)
|
||||
--- @field on_win? fun(_: "win", winid: integer, bufnr: integer, toprow: integer, botrow: integer): boolean?
|
||||
--- @field on_line? fun(_: "line", winid: integer, bufnr: integer, row: integer): boolean?
|
||||
--- @field on_range? fun(_: "range", winid: integer, bufnr: integer, start_row: integer, start_col: integer, end_row: integer, end_col: integer): boolean?
|
||||
--- @field on_end? fun(_: "end", tick: integer)
|
||||
--- @field _on_hl_def? fun(_: "hl_def")
|
||||
--- @field _on_spell_nav? fun(_: "spell_nav")
|
||||
|
@@ -179,34 +179,30 @@ function vim.iconv(str, from, to, opts) end
|
||||
--- @param fn fun()
|
||||
function vim.schedule(fn) end
|
||||
|
||||
--- Wait for {time} in milliseconds until {callback} returns `true`.
|
||||
--- Waits up to `time` milliseconds, until `callback` returns `true` (success). Executes
|
||||
--- `callback` immediately, then at intervals of approximately `interval` milliseconds (default
|
||||
--- 200). Returns all `callback` results on success.
|
||||
---
|
||||
--- Executes {callback} immediately and at approximately {interval}
|
||||
--- milliseconds (default 200). Nvim still processes other events during
|
||||
--- this time.
|
||||
---
|
||||
--- Cannot be called while in an |api-fast| event.
|
||||
--- Nvim processes other events while waiting.
|
||||
--- Cannot be called during an |api-fast| event.
|
||||
---
|
||||
--- Examples:
|
||||
---
|
||||
--- ```lua
|
||||
--- ---
|
||||
--- -- Wait for 100 ms, allowing other events to process
|
||||
--- vim.wait(100, function() end)
|
||||
--- -- Wait for 100 ms, allowing other events to process.
|
||||
--- vim.wait(100)
|
||||
---
|
||||
--- ---
|
||||
--- -- Wait for 100 ms or until global variable set.
|
||||
--- vim.wait(100, function() return vim.g.waiting_for_var end)
|
||||
--- -- Wait up to 1000 ms or until `vim.g.foo` is true, at intervals of ~500 ms.
|
||||
--- vim.wait(1000, function() return vim.g.foo end, 500)
|
||||
---
|
||||
--- ---
|
||||
--- -- Wait for 1 second or until global variable set, checking every ~500 ms
|
||||
--- vim.wait(1000, function() return vim.g.waiting_for_var end, 500)
|
||||
--- -- Wait up to 100 ms or until `vim.g.foo` is true, and get the callback results.
|
||||
--- local ok, rv1, rv2, rv3 = vim.wait(100, function()
|
||||
--- return vim.g.foo, 'a', 42, { ok = { 'yes' } }
|
||||
--- end)
|
||||
---
|
||||
--- ---
|
||||
--- -- Schedule a function to set a value in 100ms
|
||||
--- -- Schedule a function to set a value in 100ms. This would wait 10s if blocked, but actually
|
||||
--- -- only waits 100ms because `vim.wait` processes other events while waiting.
|
||||
--- vim.defer_fn(function() vim.g.timer_result = true end, 100)
|
||||
---
|
||||
--- -- Would wait ten seconds if results blocked. Actually only waits 100 ms
|
||||
--- if vim.wait(10000, function() return vim.g.timer_result end) then
|
||||
--- print('Only waiting a little bit of time!')
|
||||
--- end
|
||||
@@ -216,11 +212,11 @@ function vim.schedule(fn) end
|
||||
--- @param callback? fun(): boolean Optional callback. Waits until {callback} returns true
|
||||
--- @param interval? integer (Approximate) number of milliseconds to wait between polls
|
||||
--- @param fast_only? boolean If true, only |api-fast| events will be processed.
|
||||
--- @return boolean, nil|-1|-2
|
||||
--- - If {callback} returns `true` during the {time}: `true, nil`
|
||||
--- - If {callback} never returns `true` during the {time}: `false, -1`
|
||||
--- - If {callback} is interrupted during the {time}: `false, -2`
|
||||
--- - If {callback} errors, the error is raised.
|
||||
--- @return boolean, nil|-1|-2, ...
|
||||
--- - If callback returns `true` before timeout: `true, nil, ...`
|
||||
--- - On timeout: `false, -1`
|
||||
--- - On interrupt: `false, -2`
|
||||
--- - On error: the error is raised.
|
||||
function vim.wait(time, callback, interval, fast_only) end
|
||||
|
||||
--- Subscribe to |ui-events|, similar to |nvim_ui_attach()| but receive events in a Lua callback.
|
||||
|
@@ -1229,7 +1229,7 @@ function M.config(opts, namespace)
|
||||
if float_opts then
|
||||
float_opts = type(float_opts) == 'table' and float_opts or {}
|
||||
|
||||
opts.jump.on_jump = function(_, bufnr)
|
||||
jump_opts.on_jump = function(_, bufnr)
|
||||
M.open_float(vim.tbl_extend('keep', float_opts, {
|
||||
bufnr = bufnr,
|
||||
scope = 'cursor',
|
||||
@@ -1697,7 +1697,7 @@ M.handlers.underline = {
|
||||
bufnr = vim._resolve_bufnr(bufnr)
|
||||
opts = opts or {}
|
||||
|
||||
if not vim.api.nvim_buf_is_loaded(bufnr) then
|
||||
if not api.nvim_buf_is_loaded(bufnr) then
|
||||
return
|
||||
end
|
||||
|
||||
@@ -1724,7 +1724,7 @@ M.handlers.underline = {
|
||||
end
|
||||
|
||||
local lines =
|
||||
vim.api.nvim_buf_get_lines(diagnostic.bufnr, diagnostic.lnum, diagnostic.lnum + 1, true)
|
||||
api.nvim_buf_get_lines(diagnostic.bufnr, diagnostic.lnum, diagnostic.lnum + 1, true)
|
||||
|
||||
vim.hl.range(
|
||||
bufnr,
|
||||
@@ -1795,7 +1795,7 @@ M.handlers.virtual_text = {
|
||||
bufnr = vim._resolve_bufnr(bufnr)
|
||||
opts = opts or {}
|
||||
|
||||
if not vim.api.nvim_buf_is_loaded(bufnr) then
|
||||
if not api.nvim_buf_is_loaded(bufnr) then
|
||||
return
|
||||
end
|
||||
|
||||
@@ -2491,7 +2491,7 @@ function M.open_float(opts, ...)
|
||||
local location = info.location
|
||||
local file_name = vim.fs.basename(vim.uri_to_fname(location.uri))
|
||||
local info_suffix = ': ' .. info.message
|
||||
related_info_locations[#lines + 1] = info.location
|
||||
related_info_locations[#lines + 1] = location
|
||||
lines[#lines + 1] = string.format(
|
||||
'%s%s:%s:%s%s',
|
||||
default_pre,
|
||||
@@ -2881,11 +2881,11 @@ function M.status(bufnr)
|
||||
return result_str
|
||||
end
|
||||
|
||||
vim.api.nvim_create_autocmd('DiagnosticChanged', {
|
||||
group = vim.api.nvim_create_augroup('nvim.diagnostic.status', {}),
|
||||
api.nvim_create_autocmd('DiagnosticChanged', {
|
||||
group = api.nvim_create_augroup('nvim.diagnostic.status', {}),
|
||||
callback = function(ev)
|
||||
if vim.api.nvim_buf_is_loaded(ev.buf) then
|
||||
vim.api.nvim__redraw({ buf = ev.buf, statusline = true })
|
||||
if api.nvim_buf_is_loaded(ev.buf) then
|
||||
api.nvim__redraw({ buf = ev.buf, statusline = true })
|
||||
end
|
||||
end,
|
||||
desc = 'diagnostics component for the statusline',
|
||||
|
@@ -734,13 +734,10 @@ local extension = {
|
||||
luau = 'luau',
|
||||
lrc = 'lyrics',
|
||||
m = detect.m,
|
||||
at = 'm4',
|
||||
at = 'config',
|
||||
mc = detect.mc,
|
||||
quake = 'm3quake',
|
||||
m4 = function(path, _bufnr)
|
||||
local pathl = path:lower()
|
||||
return not (pathl:find('html%.m4$') or pathl:find('fvwm2rc')) and 'm4' or nil
|
||||
end,
|
||||
m4 = detect.m4,
|
||||
eml = 'mail',
|
||||
mk = detect.make,
|
||||
mak = detect.make,
|
||||
@@ -1395,6 +1392,8 @@ local extension = {
|
||||
yaml = 'yaml',
|
||||
eyaml = 'yaml',
|
||||
mplstyle = 'yaml',
|
||||
kyaml = 'yaml',
|
||||
kyml = 'yaml',
|
||||
grc = detect_line1('<%?xml', 'xml', 'yaml'),
|
||||
yang = 'yang',
|
||||
yuck = 'yuck',
|
||||
@@ -2237,6 +2236,7 @@ local pattern = {
|
||||
},
|
||||
['/%.'] = {
|
||||
['/%.aws/credentials$'] = 'confini',
|
||||
['/%.aws/cli/alias$'] = 'confini',
|
||||
['/%.gitconfig%.d/'] = starsetf('gitconfig'),
|
||||
['/%.gnupg/gpg%.conf$'] = 'gpg',
|
||||
['/%.gnupg/options$'] = 'gpg',
|
||||
@@ -2245,6 +2245,7 @@ local pattern = {
|
||||
['/%.pinforc$'] = 'pinfo',
|
||||
['/%.cargo/credentials$'] = 'toml',
|
||||
['/%.init/.*%.override$'] = 'upstart',
|
||||
['/%.kube/kuberc$'] = 'yaml',
|
||||
},
|
||||
['calendar/'] = {
|
||||
['/%.calendar/'] = starsetf('calendar'),
|
||||
@@ -2347,7 +2348,7 @@ local pattern = {
|
||||
},
|
||||
['require'] = {
|
||||
['%-requirements%.txt$'] = 'requirements',
|
||||
['requirements%-.*%.txt$'] = 'requirements',
|
||||
['^requirements%-.*%.txt$'] = 'requirements',
|
||||
['^requirements/.*%.txt$'] = 'requirements',
|
||||
['^requires/.*%.txt$'] = 'requirements',
|
||||
},
|
||||
@@ -2493,7 +2494,6 @@ local pattern = {
|
||||
end
|
||||
end,
|
||||
['^hg%-editor%-.*%.txt$'] = 'hgcommit',
|
||||
['%.html%.m4$'] = 'htmlm4',
|
||||
['^JAM.*%.'] = starsetf('jam'),
|
||||
['^Prl.*%.'] = starsetf('jam'),
|
||||
['^${HOME}/.*/Code/User/.*%.json$'] = 'jsonc',
|
||||
@@ -2530,6 +2530,8 @@ local pattern = {
|
||||
[',v$'] = 'rcs',
|
||||
['^svn%-commit.*%.tmp$'] = 'svn',
|
||||
['%.swift%.gyb$'] = 'swiftgyb',
|
||||
['^vivado.*%.jou$'] = 'tcl',
|
||||
['^vivado.*%.log$'] = 'tcl',
|
||||
['termcap'] = starsetf(function(_path, _bufnr)
|
||||
return require('vim.filetype.detect').printcap('term')
|
||||
end),
|
||||
|
@@ -688,10 +688,7 @@ function M.fvwm_v1(_, _)
|
||||
end
|
||||
|
||||
--- @type vim.filetype.mapfn
|
||||
function M.fvwm_v2(path, _)
|
||||
if fn.fnamemodify(path, ':e') == 'm4' then
|
||||
return 'fvwm2m4'
|
||||
end
|
||||
function M.fvwm_v2(_, _)
|
||||
return 'fvwm', function(bufnr)
|
||||
vim.b[bufnr].fvwm_version = 2
|
||||
end
|
||||
@@ -1026,6 +1023,50 @@ function M.m(_, bufnr)
|
||||
end
|
||||
end
|
||||
|
||||
--- For files ending in *.m4, distinguish:
|
||||
--- – *.html.m4 files
|
||||
--- - *fvwm2rc*.m4 files
|
||||
--- – files in the Autoconf M4 dialect
|
||||
--- – files in POSIX M4
|
||||
--- @type vim.filetype.mapfn
|
||||
function M.m4(path, bufnr)
|
||||
local fname = fn.fnamemodify(path, ':t')
|
||||
path = fn.fnamemodify(path, ':p:h')
|
||||
|
||||
if fname:find('html%.m4$') then
|
||||
return 'htmlm4'
|
||||
end
|
||||
|
||||
if fname:find('fvwm2rc') then
|
||||
return 'fvwm2m4'
|
||||
end
|
||||
|
||||
-- Canonical Autoconf file
|
||||
if fname == 'aclocal.m4' then
|
||||
return 'config'
|
||||
end
|
||||
|
||||
-- Repo heuristic for Autoconf M4 (nearby configure.ac)
|
||||
if
|
||||
fn.filereadable(path .. '/../configure.ac') ~= 0
|
||||
or fn.filereadable(path .. '/configure.ac') ~= 0
|
||||
then
|
||||
return 'config'
|
||||
end
|
||||
|
||||
-- Content heuristic for Autoconf M4 (scan first ~200 lines)
|
||||
-- Signals:
|
||||
-- - Autoconf macro prefixes: AC_/AM_/AS_/AU_/AT_
|
||||
for _, line in ipairs(getlines(bufnr, 1, 200)) do
|
||||
if line:find('^%s*A[CMSUT]_') then
|
||||
return 'config'
|
||||
end
|
||||
end
|
||||
|
||||
-- Default to POSIX M4
|
||||
return 'm4'
|
||||
end
|
||||
|
||||
--- @param contents string[]
|
||||
--- @return string?
|
||||
local function m4(contents)
|
||||
|
@@ -190,9 +190,9 @@ function M.dir(path, opts)
|
||||
if
|
||||
opts.depth
|
||||
and level < opts.depth
|
||||
and (t == 'directory' or (t == 'link' and opts.follow and (vim.uv.fs_stat(
|
||||
M.joinpath(path, f)
|
||||
) or {}).type == 'directory'))
|
||||
and (t == 'directory' or (t == 'link' and opts.follow and (
|
||||
uv.fs_stat(M.joinpath(path, f)) or {}
|
||||
).type == 'directory'))
|
||||
and (not opts.skip or opts.skip(f) ~= false)
|
||||
then
|
||||
dirs[#dirs + 1] = { f, level + 1 }
|
||||
@@ -369,7 +369,7 @@ function M.find(names, opts)
|
||||
|
||||
if
|
||||
type_ == 'directory'
|
||||
or (type_ == 'link' and opts.follow and (vim.uv.fs_stat(f) or {}).type == 'directory')
|
||||
or (type_ == 'link' and opts.follow and (uv.fs_stat(f) or {}).type == 'directory')
|
||||
then
|
||||
dirs[#dirs + 1] = f
|
||||
end
|
||||
|
@@ -72,7 +72,7 @@ function M.range(bufnr, ns, higroup, start, finish, opts)
|
||||
0,
|
||||
}
|
||||
|
||||
local buf_line_count = vim.api.nvim_buf_line_count(bufnr)
|
||||
local buf_line_count = api.nvim_buf_line_count(bufnr)
|
||||
pos1[2] = math.min(pos1[2], buf_line_count)
|
||||
pos2[2] = math.min(pos2[2], buf_line_count)
|
||||
|
||||
@@ -182,8 +182,8 @@ function M.on_yank(opts)
|
||||
|
||||
local higroup = opts.higroup or 'IncSearch'
|
||||
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local winid = vim.api.nvim_get_current_win()
|
||||
local bufnr = api.nvim_get_current_buf()
|
||||
local winid = api.nvim_get_current_win()
|
||||
|
||||
if yank_timer and not yank_timer:is_closing() then
|
||||
yank_timer:close()
|
||||
@@ -191,7 +191,7 @@ function M.on_yank(opts)
|
||||
yank_hl_clear()
|
||||
end
|
||||
|
||||
vim.api.nvim__ns_set(yank_ns, { wins = { winid } })
|
||||
api.nvim__ns_set(yank_ns, { wins = { winid } })
|
||||
yank_timer, yank_hl_clear = M.range(bufnr, yank_ns, higroup, "'[", "']", {
|
||||
regtype = event.regtype,
|
||||
inclusive = true,
|
||||
|
@@ -458,7 +458,7 @@ end
|
||||
--- @return F
|
||||
local function track(stat, f)
|
||||
return function(...)
|
||||
local start = vim.uv.hrtime()
|
||||
local start = uv.hrtime()
|
||||
local r = { f(...) }
|
||||
stats[stat] = stats[stat] or { total = 0, time = 0 }
|
||||
stats[stat].total = stats[stat].total + 1
|
||||
|
@@ -19,6 +19,7 @@ local lsp = vim._defer_require('vim.lsp', {
|
||||
inline_completion = ..., --- @module 'vim.lsp.inline_completion'
|
||||
linked_editing_range = ..., --- @module 'vim.lsp.linked_editing_range'
|
||||
log = ..., --- @module 'vim.lsp.log'
|
||||
on_type_formatting = ..., --- @module 'vim.lsp.on_type_formatting'
|
||||
protocol = ..., --- @module 'vim.lsp.protocol'
|
||||
rpc = ..., --- @module 'vim.lsp.rpc'
|
||||
semantic_tokens = ..., --- @module 'vim.lsp.semantic_tokens'
|
||||
@@ -1376,7 +1377,7 @@ function lsp.formatexpr(opts)
|
||||
local response =
|
||||
client:request_sync(ms.textDocument_rangeFormatting, params, timeout_ms, bufnr)
|
||||
if response and response.result then
|
||||
lsp.util.apply_text_edits(response.result, bufnr, client.offset_encoding)
|
||||
util.apply_text_edits(response.result, bufnr, client.offset_encoding)
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
@@ -104,7 +104,7 @@ local function incremental_changes(state, encoding, bufnr, firstline, lastline,
|
||||
|
||||
local line_ending = vim.lsp._buf_get_line_ending(bufnr)
|
||||
local incremental_change = sync.compute_diff(
|
||||
state.lines,
|
||||
prev_lines,
|
||||
curr_lines,
|
||||
firstline,
|
||||
lastline,
|
||||
|
@@ -249,7 +249,7 @@ function State:new(bufnr)
|
||||
group = self.augroup,
|
||||
pattern = 'foldexpr',
|
||||
callback = function()
|
||||
if vim.v.option_type == 'global' or vim.api.nvim_get_current_buf() == bufnr then
|
||||
if vim.v.option_type == 'global' or api.nvim_get_current_buf() == bufnr then
|
||||
vim.lsp._capability.enable('folding_range', false, { bufnr = bufnr })
|
||||
end
|
||||
end,
|
||||
|
@@ -443,7 +443,7 @@ function M.signature_help(config)
|
||||
local buf, win = util.open_floating_preview(lines, 'markdown', config)
|
||||
|
||||
if hl then
|
||||
vim.api.nvim_buf_clear_namespace(buf, sig_help_ns, 0, -1)
|
||||
api.nvim_buf_clear_namespace(buf, sig_help_ns, 0, -1)
|
||||
vim.hl.range(
|
||||
buf,
|
||||
sig_help_ns,
|
||||
@@ -1072,7 +1072,7 @@ end
|
||||
--- @param opts? vim.lsp.WorkspaceDiagnosticsOpts
|
||||
--- @see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_dagnostics
|
||||
function M.workspace_diagnostics(opts)
|
||||
vim.validate('opts', opts, 'table', true)
|
||||
validate('opts', opts, 'table', true)
|
||||
|
||||
lsp.diagnostic._workspace_diagnostics(opts or {})
|
||||
end
|
||||
@@ -1435,7 +1435,7 @@ function M.selection_range(direction)
|
||||
|
||||
lsp.buf_request(
|
||||
0,
|
||||
ms.textDocument_selectionRange,
|
||||
method,
|
||||
params,
|
||||
---@param response lsp.SelectionRange[]?
|
||||
function(err, response)
|
||||
|
@@ -211,6 +211,9 @@ local all_clients = {}
|
||||
---
|
||||
--- @field _enabled_capabilities table<vim.lsp.capability.Name, boolean?>
|
||||
---
|
||||
--- Whether on-type formatting is enabled for this client.
|
||||
--- @field _otf_enabled boolean?
|
||||
---
|
||||
--- Track this so that we can escalate automatically if we've already tried a
|
||||
--- graceful shutdown
|
||||
--- @field private _graceful_shutdown_failed true?
|
||||
@@ -871,7 +874,7 @@ function Client:stop(force)
|
||||
self._is_stopping = true
|
||||
local rpc = self.rpc
|
||||
|
||||
vim.lsp._watchfiles.cancel(self.id)
|
||||
lsp._watchfiles.cancel(self.id)
|
||||
|
||||
if force or not self.initialized or self._graceful_shutdown_failed then
|
||||
rpc.terminate()
|
||||
@@ -921,7 +924,7 @@ function Client:_register(registrations)
|
||||
for _, reg in ipairs(registrations) do
|
||||
local method = reg.method
|
||||
if method == ms.workspace_didChangeWatchedFiles then
|
||||
vim.lsp._watchfiles.register(reg, self.id)
|
||||
lsp._watchfiles.register(reg, self.id)
|
||||
elseif not self:_supports_registration(method) then
|
||||
unsupported[#unsupported + 1] = method
|
||||
end
|
||||
@@ -955,7 +958,7 @@ function Client:_unregister(unregistrations)
|
||||
self:_unregister_dynamic(unregistrations)
|
||||
for _, unreg in ipairs(unregistrations) do
|
||||
if unreg.method == ms.workspace_didChangeWatchedFiles then
|
||||
vim.lsp._watchfiles.unregister(unreg, self.id)
|
||||
lsp._watchfiles.unregister(unreg, self.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1096,10 +1099,10 @@ function Client:on_attach(bufnr)
|
||||
-- on_attach and LspAttach callbacks the ability to schedule wrap the
|
||||
-- opt-out (deleting the semanticTokensProvider from capabilities)
|
||||
vim.schedule(function()
|
||||
for _, Capability in pairs(vim.lsp._capability.all) do
|
||||
for _, Capability in pairs(lsp._capability.all) do
|
||||
if
|
||||
self:supports_method(Capability.method)
|
||||
and vim.lsp._capability.is_enabled(Capability.name, {
|
||||
and lsp._capability.is_enabled(Capability.name, {
|
||||
bufnr = bufnr,
|
||||
client_id = self.id,
|
||||
})
|
||||
@@ -1220,10 +1223,10 @@ function Client:_on_detach(bufnr)
|
||||
})
|
||||
end
|
||||
|
||||
for _, Capability in pairs(vim.lsp._capability.all) do
|
||||
for _, Capability in pairs(lsp._capability.all) do
|
||||
if
|
||||
self:supports_method(Capability.method)
|
||||
and vim.lsp._capability.is_enabled(Capability.name, {
|
||||
and lsp._capability.is_enabled(Capability.name, {
|
||||
bufnr = bufnr,
|
||||
client_id = self.id,
|
||||
})
|
||||
@@ -1266,7 +1269,7 @@ local function reset_defaults(bufnr)
|
||||
end
|
||||
vim._with({ buf = bufnr }, function()
|
||||
local keymap = vim.fn.maparg('K', 'n', false, true)
|
||||
if keymap and keymap.callback == vim.lsp.buf.hover and keymap.buffer == 1 then
|
||||
if keymap and keymap.callback == lsp.buf.hover and keymap.buffer == 1 then
|
||||
vim.keymap.del('n', 'K', { buffer = bufnr })
|
||||
end
|
||||
end)
|
||||
|
@@ -864,7 +864,7 @@ end
|
||||
--- - findstart=0: column where the completion starts, or -2 or -3
|
||||
--- - findstart=1: list of matches (actually just calls |complete()|)
|
||||
function M._omnifunc(findstart, base)
|
||||
vim.lsp.log.debug('omnifunc.findstart', { findstart = findstart, base = base })
|
||||
lsp.log.debug('omnifunc.findstart', { findstart = findstart, base = base })
|
||||
assert(base) -- silence luals
|
||||
local bufnr = api.nvim_get_current_buf()
|
||||
local clients = lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_completion })
|
||||
|
@@ -498,7 +498,7 @@ function M._workspace_diagnostics(opts)
|
||||
local function handler(error, result, ctx)
|
||||
-- Check for retrigger requests on cancellation errors.
|
||||
-- Unless `retriggerRequest` is explicitly disabled, try again.
|
||||
if error ~= nil and error.code == lsp.protocol.ErrorCodes.ServerCancelled then
|
||||
if error ~= nil and error.code == protocol.ErrorCodes.ServerCancelled then
|
||||
if error.data == nil or error.data.retriggerRequest ~= false then
|
||||
local client = assert(lsp.get_client_by_id(ctx.client_id))
|
||||
client:request(ms.workspace_diagnostic, ctx.params, handler)
|
||||
|
@@ -450,7 +450,7 @@ function M.color_presentation()
|
||||
end
|
||||
vim.list_extend(text_edits, choice.additionalTextEdits or {})
|
||||
|
||||
lsp.util.apply_text_edits(text_edits, bufnr, client.offset_encoding)
|
||||
util.apply_text_edits(text_edits, bufnr, client.offset_encoding)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
@@ -177,7 +177,7 @@ function M.get(filter)
|
||||
--- @param buf integer
|
||||
vim.tbl_map(function(buf)
|
||||
vim.list_extend(hints, M.get(vim.tbl_extend('keep', { bufnr = buf }, filter)))
|
||||
end, vim.api.nvim_list_bufs())
|
||||
end, api.nvim_list_bufs())
|
||||
return hints
|
||||
else
|
||||
bufnr = vim._resolve_bufnr(bufnr)
|
||||
|
@@ -1,3 +1,35 @@
|
||||
--- @brief
|
||||
--- This module provides the LSP "inline completion" feature, for completing multiline text (e.g.,
|
||||
--- whole methods) instead of just a word or line, which may result in "syntactically or
|
||||
--- semantically incorrect" code. Unlike regular completion, this is typically presented as overlay
|
||||
--- text instead of a menu of completion candidates.
|
||||
---
|
||||
--- LSP spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_inlineCompletion
|
||||
---
|
||||
--- To try it out, here is a quickstart example using Copilot: [lsp-copilot]()
|
||||
---
|
||||
--- 1. Install Copilot:
|
||||
--- ```sh
|
||||
--- npm install --global @github/copilot-language-server
|
||||
--- ```
|
||||
--- 2. Define a config, (or copy `lsp/copilot.lua` from https://github.com/neovim/nvim-lspconfig):
|
||||
--- ```lua
|
||||
--- vim.lsp.config('copilot', {
|
||||
--- cmd = { 'copilot-language-server', '--stdio', },
|
||||
--- root_markers = { '.git' },
|
||||
--- })
|
||||
--- ```
|
||||
--- 3. Activate the config:
|
||||
--- ```lua
|
||||
--- vim.lsp.enable('copilot')
|
||||
--- ```
|
||||
--- 4. Sign in to Copilot, or use the `:LspCopilotSignIn` command from https://github.com/neovim/nvim-lspconfig
|
||||
--- 5. Enable inline completion:
|
||||
--- ```lua
|
||||
--- vim.lsp.inline_completion.enable()
|
||||
--- ```
|
||||
--- 6. Set a keymap for `vim.lsp.inline_completion.get()` and invoke the keymap.
|
||||
|
||||
local util = require('vim.lsp.util')
|
||||
local log = require('vim.lsp.log')
|
||||
local protocol = require('vim.lsp.protocol')
|
||||
@@ -11,11 +43,11 @@ local M = {}
|
||||
|
||||
local namespace = api.nvim_create_namespace('nvim.lsp.inline_completion')
|
||||
|
||||
---@class (private) vim.lsp.inline_completion.CurrentItem
|
||||
---@field index integer The index among all items form all clients.
|
||||
---@class vim.lsp.inline_completion.Item
|
||||
---@field _index integer The index among all items form all clients.
|
||||
---@field client_id integer Client ID
|
||||
---@field insert_text string|lsp.StringValue The text to be inserted, can be a snippet.
|
||||
---@field filter_text? string
|
||||
---@field _filter_text? string
|
||||
---@field range? vim.Range Which range it be applied.
|
||||
---@field command? lsp.Command Corresponding server command.
|
||||
|
||||
@@ -25,7 +57,7 @@ local namespace = api.nvim_create_namespace('nvim.lsp.inline_completion')
|
||||
---@class (private) vim.lsp.inline_completion.Completor : vim.lsp.Capability
|
||||
---@field active table<integer, vim.lsp.inline_completion.Completor?>
|
||||
---@field timer? uv.uv_timer_t Timer for debouncing automatic requests
|
||||
---@field current? vim.lsp.inline_completion.CurrentItem Currently selected item
|
||||
---@field current? vim.lsp.inline_completion.Item Currently selected item
|
||||
---@field client_state table<integer, vim.lsp.inline_completion.ClientState>
|
||||
local Completor = {
|
||||
name = 'inline_completion',
|
||||
@@ -146,11 +178,11 @@ function Completor:select(index, show_index)
|
||||
local client = assert(vim.lsp.get_client_by_id(client_id))
|
||||
local range = item.range and vim.range.lsp(self.bufnr, item.range, client.offset_encoding)
|
||||
self.current = {
|
||||
index = index,
|
||||
_index = index,
|
||||
client_id = client_id,
|
||||
insert_text = item.insertText,
|
||||
range = range,
|
||||
filter_text = item.filterText,
|
||||
_filter_text = item.filterText,
|
||||
command = item.command,
|
||||
}
|
||||
|
||||
@@ -281,19 +313,14 @@ function Completor:abort()
|
||||
self.current = nil
|
||||
end
|
||||
|
||||
--- Apply the current completion item to the buffer.
|
||||
--- Accept the current completion item to the buffer.
|
||||
---
|
||||
---@package
|
||||
function Completor:apply()
|
||||
local current = self.current
|
||||
self:abort()
|
||||
if not current then
|
||||
return
|
||||
end
|
||||
|
||||
local insert_text = current.insert_text
|
||||
---@param item vim.lsp.inline_completion.Item
|
||||
function Completor:accept(item)
|
||||
local insert_text = item.insert_text
|
||||
if type(insert_text) == 'string' then
|
||||
local range = current.range
|
||||
local range = item.range
|
||||
if range then
|
||||
local lines = vim.split(insert_text, '\n')
|
||||
api.nvim_buf_set_text(
|
||||
@@ -304,7 +331,7 @@ function Completor:apply()
|
||||
range.end_.col,
|
||||
lines
|
||||
)
|
||||
local pos = current.range.start:to_cursor()
|
||||
local pos = item.range.start:to_cursor()
|
||||
api.nvim_win_set_cursor(vim.fn.bufwinid(self.bufnr), {
|
||||
pos[1] + #lines - 1,
|
||||
(#lines == 1 and pos[2] or 0) + #lines[#lines],
|
||||
@@ -317,9 +344,9 @@ function Completor:apply()
|
||||
end
|
||||
|
||||
-- Execute the command *after* inserting this completion.
|
||||
if current.command then
|
||||
local client = assert(vim.lsp.get_client_by_id(current.client_id))
|
||||
client:exec_cmd(current.command, { bufnr = self.bufnr })
|
||||
if item.command then
|
||||
local client = assert(vim.lsp.get_client_by_id(item.client_id))
|
||||
client:exec_cmd(item.command, { bufnr = self.bufnr })
|
||||
end
|
||||
end
|
||||
|
||||
@@ -381,7 +408,7 @@ function M.select(opts)
|
||||
end
|
||||
|
||||
local n = completor:count_items()
|
||||
local index = current.index + count
|
||||
local index = current._index + count
|
||||
if wrap then
|
||||
index = (index - 1) % n + 1
|
||||
else
|
||||
@@ -396,10 +423,15 @@ end
|
||||
--- Buffer handle, or 0 for current.
|
||||
--- (default: 0)
|
||||
---@field bufnr? integer
|
||||
|
||||
--- Apply the currently displayed completion candidate to the buffer.
|
||||
---
|
||||
--- It returns false when no candidate can be applied,
|
||||
--- Accept handler, called with the accepted item.
|
||||
--- If not provided, the default handler is used,
|
||||
--- which applies changes to the buffer based on the completion item.
|
||||
---@field on_accept? fun(item: vim.lsp.inline_completion.Item)
|
||||
|
||||
--- Accept the currently displayed completion candidate to the buffer.
|
||||
---
|
||||
--- It returns false when no candidate can be accepted,
|
||||
--- so you can use the return value to implement a fallback:
|
||||
---
|
||||
--- ```lua
|
||||
@@ -420,11 +452,23 @@ function M.get(opts)
|
||||
opts = opts or {}
|
||||
|
||||
local bufnr = vim._resolve_bufnr(opts.bufnr)
|
||||
local on_accept = opts.on_accept
|
||||
|
||||
local completor = Completor.active[bufnr]
|
||||
if completor and completor.current then
|
||||
-- Schedule apply to allow `get()` can be mapped with `<expr>`.
|
||||
vim.schedule(function()
|
||||
completor:apply()
|
||||
local item = completor.current
|
||||
completor:abort()
|
||||
if not item then
|
||||
return
|
||||
end
|
||||
|
||||
if on_accept then
|
||||
on_accept(item)
|
||||
else
|
||||
completor:accept(item)
|
||||
end
|
||||
end)
|
||||
return true
|
||||
end
|
||||
|
@@ -225,13 +225,13 @@ end
|
||||
---@param message_type lsp.MessageType
|
||||
function log._from_lsp_level(message_type)
|
||||
if message_type == protocol.MessageType.Error then
|
||||
return vim.log.levels.ERROR
|
||||
return log_levels.ERROR
|
||||
elseif message_type == protocol.MessageType.Warning then
|
||||
return vim.log.levels.WARN
|
||||
return log_levels.WARN
|
||||
elseif message_type == protocol.MessageType.Info or message_type == protocol.MessageType.Log then
|
||||
return vim.log.levels.INFO
|
||||
return log_levels.INFO
|
||||
else
|
||||
return vim.log.levels.DEBUG
|
||||
return log_levels.DEBUG
|
||||
end
|
||||
end
|
||||
|
||||
|
261
runtime/lua/vim/lsp/on_type_formatting.lua
Normal file
261
runtime/lua/vim/lsp/on_type_formatting.lua
Normal file
@@ -0,0 +1,261 @@
|
||||
local api = vim.api
|
||||
local lsp = vim.lsp
|
||||
local util = lsp.util
|
||||
local method = lsp.protocol.Methods.textDocument_onTypeFormatting
|
||||
|
||||
local schedule = vim.schedule
|
||||
local current_buf = api.nvim_get_current_buf
|
||||
local get_mode = api.nvim_get_mode
|
||||
|
||||
local ns = api.nvim_create_namespace('nvim.lsp.on_type_formatting')
|
||||
local augroup = api.nvim_create_augroup('nvim.lsp.on_type_formatting', {})
|
||||
|
||||
local M = {}
|
||||
|
||||
--- @alias vim.lsp.on_type_formatting.BufTriggers table<string, table<integer, vim.lsp.Client>>
|
||||
|
||||
--- A map from bufnr -> trigger character -> client ID -> client
|
||||
--- @type table<integer, vim.lsp.on_type_formatting.BufTriggers>
|
||||
local buf_handles = {}
|
||||
|
||||
--- |lsp-handler| for the `textDocument/onTypeFormatting` method.
|
||||
---
|
||||
--- @param err? lsp.ResponseError
|
||||
--- @param result? lsp.TextEdit[]
|
||||
--- @param ctx lsp.HandlerContext
|
||||
local function on_type_formatting(err, result, ctx)
|
||||
if err then
|
||||
lsp.log.error('on_type_formatting', err)
|
||||
return
|
||||
end
|
||||
|
||||
local bufnr = assert(ctx.bufnr)
|
||||
|
||||
-- A `null` result is equivalent to an empty `TextEdit[]` result; no work should be done.
|
||||
if not result or not api.nvim_buf_is_loaded(bufnr) or util.buf_versions[bufnr] ~= ctx.version then
|
||||
return
|
||||
end
|
||||
|
||||
local client = assert(vim.lsp.get_client_by_id(ctx.client_id))
|
||||
|
||||
util.apply_text_edits(result, ctx.bufnr, client.offset_encoding)
|
||||
end
|
||||
|
||||
---@param bufnr integer
|
||||
---@param typed string
|
||||
---@param triggered_clients vim.lsp.Client[]
|
||||
---@param idx integer?
|
||||
---@param client vim.lsp.Client?
|
||||
local function format_iter(bufnr, typed, triggered_clients, idx, client)
|
||||
if not idx or not client then
|
||||
return
|
||||
end
|
||||
---@type lsp.DocumentOnTypeFormattingParams
|
||||
local params = vim.tbl_extend(
|
||||
'keep',
|
||||
util.make_formatting_params(),
|
||||
util.make_position_params(0, client.offset_encoding),
|
||||
{ ch = typed }
|
||||
)
|
||||
client:request(method, params, function(...)
|
||||
on_type_formatting(...)
|
||||
format_iter(bufnr, typed, triggered_clients, next(triggered_clients, idx))
|
||||
end, bufnr)
|
||||
end
|
||||
|
||||
---@param typed string
|
||||
local function on_key(_, typed)
|
||||
local mode = get_mode()
|
||||
if mode.blocking or mode.mode ~= 'i' then
|
||||
return
|
||||
end
|
||||
|
||||
local bufnr = current_buf()
|
||||
|
||||
local buf_handle = buf_handles[bufnr]
|
||||
if not buf_handle then
|
||||
return
|
||||
end
|
||||
|
||||
-- LSP expects '\n' for formatting on newline
|
||||
if typed == '\r' then
|
||||
typed = '\n'
|
||||
end
|
||||
|
||||
local triggered_clients = buf_handle[typed]
|
||||
if not triggered_clients then
|
||||
return
|
||||
end
|
||||
|
||||
-- Schedule the formatting to occur *after* the LSP is aware of the inserted character
|
||||
schedule(function()
|
||||
format_iter(bufnr, typed, triggered_clients, next(triggered_clients))
|
||||
end)
|
||||
end
|
||||
|
||||
--- @param client vim.lsp.Client
|
||||
--- @param bufnr integer
|
||||
local function detach(client, bufnr)
|
||||
local buf_handle = buf_handles[bufnr]
|
||||
if not buf_handle then
|
||||
return
|
||||
end
|
||||
|
||||
local client_id = client.id
|
||||
|
||||
-- Remove this client from its associated trigger characters
|
||||
for trigger_char, attached_clients in pairs(buf_handle) do
|
||||
attached_clients[client_id] = nil
|
||||
|
||||
-- Remove the trigger character if we detached its last client.
|
||||
if not next(attached_clients) then
|
||||
buf_handle[trigger_char] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Remove the buf handle and its autocmds if we removed its last client.
|
||||
if not next(buf_handle) then
|
||||
buf_handles[bufnr] = nil
|
||||
api.nvim_clear_autocmds({ group = augroup, buffer = bufnr })
|
||||
|
||||
-- Remove the on_key callback if we removed the last buf handle.
|
||||
if not next(buf_handles) then
|
||||
vim.on_key(nil, ns)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- @param client vim.lsp.Client
|
||||
--- @param bufnr integer
|
||||
local function attach(client, bufnr)
|
||||
if not client:supports_method(method, bufnr) then
|
||||
return
|
||||
end
|
||||
|
||||
local client_id = client.id
|
||||
---@type lsp.DocumentOnTypeFormattingOptions
|
||||
local otf_capabilities =
|
||||
assert(vim.tbl_get(client.server_capabilities, 'documentOnTypeFormattingProvider'))
|
||||
|
||||
-- Set on_key callback, clearing first in case it was already registered.
|
||||
vim.on_key(nil, ns)
|
||||
vim.on_key(on_key, ns)
|
||||
|
||||
-- Populate the buf handle data. We cannot use defaulttable here because then an empty table will
|
||||
-- be created for each unique keystroke
|
||||
local buf_handle = buf_handles[bufnr] or {}
|
||||
buf_handles[bufnr] = buf_handle
|
||||
|
||||
local trigger = buf_handle[otf_capabilities.firstTriggerCharacter] or {}
|
||||
buf_handle[otf_capabilities.firstTriggerCharacter] = trigger
|
||||
trigger[client_id] = client
|
||||
|
||||
for _, char in ipairs(otf_capabilities.moreTriggerCharacter or {}) do
|
||||
trigger = buf_handle[char] or {}
|
||||
buf_handle[char] = trigger
|
||||
trigger[client_id] = client
|
||||
end
|
||||
|
||||
api.nvim_clear_autocmds({ group = augroup, buffer = bufnr })
|
||||
api.nvim_create_autocmd('LspDetach', {
|
||||
buffer = bufnr,
|
||||
desc = 'Detach on-type formatting module when the client detaches',
|
||||
group = augroup,
|
||||
callback = function(args)
|
||||
local detached_client = assert(lsp.get_client_by_id(args.data.client_id))
|
||||
detach(detached_client, bufnr)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
api.nvim_create_autocmd('LspAttach', {
|
||||
desc = 'Enable on-type formatting for all buffers with individually-enabled clients.',
|
||||
callback = function(ev)
|
||||
local buf = ev.buf
|
||||
local client = assert(lsp.get_client_by_id(ev.data.client_id))
|
||||
if client._otf_enabled then
|
||||
attach(client, buf)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
---@param enable boolean
|
||||
---@param client vim.lsp.Client
|
||||
local function toggle_for_client(enable, client)
|
||||
local handler = enable and attach or detach
|
||||
|
||||
-- Toggle for buffers already attached.
|
||||
for bufnr, _ in pairs(client.attached_buffers) do
|
||||
handler(client, bufnr)
|
||||
end
|
||||
|
||||
client._otf_enabled = enable
|
||||
end
|
||||
|
||||
---@param enable boolean
|
||||
local function toggle_globally(enable)
|
||||
-- Toggle for clients that have already attached.
|
||||
local clients = lsp.get_clients({ method = method })
|
||||
for _, client in ipairs(clients) do
|
||||
toggle_for_client(enable, client)
|
||||
end
|
||||
|
||||
-- If disabling, only clear the attachment autocmd. If enabling, create it as well.
|
||||
local group = api.nvim_create_augroup('nvim.lsp.on_type_formatting', { clear = true })
|
||||
if enable then
|
||||
api.nvim_create_autocmd('LspAttach', {
|
||||
group = group,
|
||||
desc = 'Enable on-type formatting for ALL clients by default.',
|
||||
callback = function(ev)
|
||||
local client = assert(lsp.get_client_by_id(ev.data.client_id))
|
||||
if client._otf_enabled ~= false then
|
||||
attach(client, ev.buf)
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
--- Optional filters |kwargs|:
|
||||
--- @inlinedoc
|
||||
--- @class vim.lsp.on_type_formatting.enable.Filter
|
||||
--- @field client_id integer? Client ID, or `nil` for all.
|
||||
|
||||
--- Enables/disables on-type formatting globally or for the {filter}ed scope. The following are some
|
||||
--- practical usage examples:
|
||||
---
|
||||
--- ```lua
|
||||
--- -- Enable for all clients
|
||||
--- vim.lsp.on_type_formatting.enable()
|
||||
---
|
||||
--- -- Enable for a specific client
|
||||
--- vim.api.nvim_create_autocmd('LspAttach', {
|
||||
--- callback = function(args)
|
||||
--- local client_id = args.data.client_id
|
||||
--- local client = assert(vim.lsp.get_client_by_id(client_id))
|
||||
--- if client.name == 'rust-analyzer' then
|
||||
--- vim.lsp.on_type_formatting.enable(true, { client_id = client_id })
|
||||
--- end
|
||||
--- end,
|
||||
--- })
|
||||
--- ```
|
||||
---
|
||||
--- @param enable? boolean true/nil to enable, false to disable.
|
||||
--- @param filter vim.lsp.on_type_formatting.enable.Filter?
|
||||
function M.enable(enable, filter)
|
||||
vim.validate('enable', enable, 'boolean', true)
|
||||
vim.validate('filter', filter, 'table', true)
|
||||
|
||||
enable = enable ~= false
|
||||
filter = filter or {}
|
||||
|
||||
if filter.client_id then
|
||||
local client =
|
||||
assert(lsp.get_client_by_id(filter.client_id), 'Client not found for id ' .. filter.client_id)
|
||||
toggle_for_client(enable, client)
|
||||
else
|
||||
toggle_globally(enable)
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
@@ -572,6 +572,9 @@ function protocol.make_client_capabilities()
|
||||
linkedEditingRange = {
|
||||
dynamicRegistration = false,
|
||||
},
|
||||
onTypeFormatting = {
|
||||
dynamicRegistration = false,
|
||||
},
|
||||
},
|
||||
workspace = {
|
||||
symbol = {
|
||||
|
@@ -526,11 +526,11 @@ local function merge_dispatchers(dispatchers)
|
||||
---@type vim.lsp.rpc.Dispatchers
|
||||
local merged = {
|
||||
notification = (
|
||||
dispatchers.notification and vim.schedule_wrap(dispatchers.notification)
|
||||
dispatchers.notification and schedule_wrap(dispatchers.notification)
|
||||
or default_dispatchers.notification
|
||||
),
|
||||
on_error = (
|
||||
dispatchers.on_error and vim.schedule_wrap(dispatchers.on_error)
|
||||
dispatchers.on_error and schedule_wrap(dispatchers.on_error)
|
||||
or default_dispatchers.on_error
|
||||
),
|
||||
on_exit = dispatchers.on_exit or default_dispatchers.on_exit,
|
||||
|
@@ -368,7 +368,7 @@ end
|
||||
--- @param hl_group string
|
||||
--- @param priority integer
|
||||
local function set_mark(bufnr, ns, token, hl_group, priority)
|
||||
vim.api.nvim_buf_set_extmark(bufnr, ns, token.line, token.start_col, {
|
||||
api.nvim_buf_set_extmark(bufnr, ns, token.line, token.start_col, {
|
||||
hl_group = hl_group,
|
||||
end_line = token.end_line,
|
||||
end_col = token.end_col,
|
||||
|
@@ -159,7 +159,7 @@ local function compute_start_range(
|
||||
else
|
||||
byte_idx = start_byte_idx + str_utf_start(prev_line, start_byte_idx)
|
||||
--- Convert to 0 based for input, and from 0 based for output
|
||||
char_idx = vim.str_utfindex(prev_line, position_encoding, byte_idx - 1) + 1
|
||||
char_idx = str_utfindex(prev_line, position_encoding, byte_idx - 1) + 1
|
||||
end
|
||||
|
||||
-- Return the start difference (shared for new and prev lines)
|
||||
|
@@ -595,7 +595,7 @@ function M.rename(old_fname, new_fname, opts)
|
||||
opts = opts or {}
|
||||
local skip = not opts.overwrite or opts.ignoreIfExists
|
||||
|
||||
local old_fname_full = vim.uv.fs_realpath(vim.fs.normalize(old_fname, { expand_env = false }))
|
||||
local old_fname_full = uv.fs_realpath(vim.fs.normalize(old_fname, { expand_env = false }))
|
||||
if not old_fname_full then
|
||||
vim.notify('Invalid path: ' .. old_fname, vim.log.levels.ERROR)
|
||||
return
|
||||
@@ -869,7 +869,7 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers
|
||||
active_offset = { offset - 1, offset + #parameter_label - 1 }
|
||||
break
|
||||
end
|
||||
offset = offset + #param.label + 1
|
||||
offset = offset + #plabel + 1
|
||||
end
|
||||
end
|
||||
if parameter.documentation then
|
||||
|
@@ -365,7 +365,7 @@ local function trigger_event(p, event_name, kind)
|
||||
spec.version = spec.version or (uv.fs_stat(p.path) and git_get_default_branch(p.path))
|
||||
|
||||
local data = { kind = kind, spec = spec, path = p.path }
|
||||
vim.api.nvim_exec_autocmds(event_name, { pattern = p.path, data = data })
|
||||
api.nvim_exec_autocmds(event_name, { pattern = p.path, data = data })
|
||||
end
|
||||
|
||||
--- @param title string
|
||||
|
@@ -732,10 +732,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
|
||||
@@ -761,7 +760,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
|
||||
|
||||
|
@@ -80,8 +80,6 @@ function TSQueryCursor:next_match() end
|
||||
|
||||
--- @param node TSNode
|
||||
--- @param query TSQuery
|
||||
--- @param start integer?
|
||||
--- @param stop integer?
|
||||
--- @param opts? { max_start_depth?: integer, match_limit?: integer}
|
||||
--- @param opts? { start_row: integer, start_col: integer, end_row: integer, end_col: integer, max_start_depth?: integer, match_limit?: integer }
|
||||
--- @return TSQueryCursor
|
||||
function vim._create_ts_querycursor(node, query, start, stop, opts) end
|
||||
function vim._create_ts_querycursor(node, query, opts) end
|
||||
|
@@ -343,7 +343,7 @@ function M.inspect_tree(opts)
|
||||
local win = api.nvim_get_current_win()
|
||||
local treeview, err = TSTreeView:new(buf, opts.lang)
|
||||
if err and err:match('no parser for lang') then
|
||||
vim.api.nvim_echo({ { err, 'WarningMsg' } }, true, {})
|
||||
api.nvim_echo({ { err, 'WarningMsg' } }, true, {})
|
||||
return
|
||||
elseif not treeview then
|
||||
error(err)
|
||||
@@ -627,7 +627,7 @@ function M.edit_query(lang)
|
||||
local base_buf = base_win and api.nvim_win_get_buf(base_win)
|
||||
local inspect_win = base_buf and vim.b[base_buf].dev_inspect
|
||||
if base_win and base_buf and api.nvim_win_is_valid(inspect_win) then
|
||||
vim.api.nvim_set_current_win(inspect_win)
|
||||
api.nvim_set_current_win(inspect_win)
|
||||
buf = base_buf
|
||||
win = base_win
|
||||
cmd = 'new'
|
||||
|
@@ -9,7 +9,7 @@ function M.check()
|
||||
health.info(
|
||||
string.format(
|
||||
'Treesitter ABI support: min %d, max %d',
|
||||
vim.treesitter.minimum_language_version,
|
||||
ts.minimum_language_version,
|
||||
ts.language_version
|
||||
)
|
||||
)
|
||||
|
@@ -1,10 +1,11 @@
|
||||
local api = vim.api
|
||||
local query = vim.treesitter.query
|
||||
local Range = require('vim.treesitter._range')
|
||||
local cmp_lt = Range.cmp_pos.lt
|
||||
|
||||
local ns = api.nvim_create_namespace('nvim.treesitter.highlighter')
|
||||
|
||||
---@alias vim.treesitter.highlighter.Iter fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch
|
||||
---@alias vim.treesitter.highlighter.Iter fun(end_line: integer|nil, end_col: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch, TSTree
|
||||
|
||||
---@class (private) vim.treesitter.highlighter.Query
|
||||
---@field private _query vim.treesitter.Query?
|
||||
@@ -57,6 +58,7 @@ end
|
||||
---@class (private) vim.treesitter.highlighter.State
|
||||
---@field tstree TSTree
|
||||
---@field next_row integer
|
||||
---@field next_col integer
|
||||
---@field iter vim.treesitter.highlighter.Iter?
|
||||
---@field highlighter_query vim.treesitter.highlighter.Query
|
||||
---@field prev_marks MarkInfo[]
|
||||
@@ -233,6 +235,7 @@ function TSHighlighter:prepare_highlight_states(win, srow, erow)
|
||||
table.insert(self._highlight_states[win], {
|
||||
tstree = tstree,
|
||||
next_row = 0,
|
||||
next_col = 0,
|
||||
iter = nil,
|
||||
highlighter_query = hl_query,
|
||||
prev_marks = {},
|
||||
@@ -331,27 +334,36 @@ end
|
||||
---Queues the remainder if the mark continues after the line.
|
||||
---@param m MarkInfo
|
||||
---@param buf integer
|
||||
---@param line integer
|
||||
---@param range_start_row integer
|
||||
---@param range_start_col integer
|
||||
---@param range_end_row integer
|
||||
---@param range_end_col integer
|
||||
---@param next_marks MarkInfo[]
|
||||
local function add_mark(m, buf, line, next_marks)
|
||||
local function add_mark(
|
||||
m,
|
||||
buf,
|
||||
range_start_row,
|
||||
range_start_col,
|
||||
range_end_row,
|
||||
range_end_col,
|
||||
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
|
||||
if cmp_lt(cur_start_l, cur_start_c, range_start_row, range_start_col) then
|
||||
cur_start_l = range_start_row
|
||||
cur_start_c = range_start_col
|
||||
end
|
||||
|
||||
local cur_opts = m.opts
|
||||
if cur_opts.end_line >= line + 1 then
|
||||
if cmp_lt(range_end_row, range_end_col, cur_opts.end_line, cur_opts.end_col) then
|
||||
cur_opts = vim.deepcopy(cur_opts, true)
|
||||
cur_opts.end_line = line + 1
|
||||
cur_opts.end_col = 0
|
||||
cur_opts.end_line = range_end_row
|
||||
cur_opts.end_col = range_end_col
|
||||
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
|
||||
if cmp_lt(cur_start_l, cur_start_c, cur_opts.end_line, cur_opts.end_col) then
|
||||
api.nvim_buf_set_extmark(buf, ns, cur_start_l, cur_start_c, cur_opts)
|
||||
end
|
||||
end
|
||||
@@ -359,17 +371,44 @@ end
|
||||
---@param self vim.treesitter.highlighter
|
||||
---@param win integer
|
||||
---@param buf integer
|
||||
---@param line integer
|
||||
---@param range_start_row integer
|
||||
---@param range_start_col integer
|
||||
---@param range_end_row integer
|
||||
---@param range_end_col integer
|
||||
---@param on_spell boolean
|
||||
---@param on_conceal boolean
|
||||
local function on_line_impl(self, win, buf, line, on_spell, on_conceal)
|
||||
self._conceal_checked[line] = self._conceal_line and true or nil
|
||||
local function on_range_impl(
|
||||
self,
|
||||
win,
|
||||
buf,
|
||||
range_start_row,
|
||||
range_start_col,
|
||||
range_end_row,
|
||||
range_end_col,
|
||||
on_spell,
|
||||
on_conceal
|
||||
)
|
||||
if self._conceal_line then
|
||||
range_start_col = 0
|
||||
if range_end_col ~= 0 then
|
||||
range_end_row = range_end_row + 1
|
||||
range_end_col = 0
|
||||
end
|
||||
end
|
||||
for i = range_start_row, range_end_row - 1 do
|
||||
self._conceal_checked[i] = self._conceal_line or nil
|
||||
end
|
||||
self:for_each_highlight_state(win, function(state)
|
||||
local root_node = state.tstree:root()
|
||||
local root_start_row, _, root_end_row, _ = root_node:range()
|
||||
---@type { [1]: integer, [2]: integer, [3]: integer, [4]: integer }
|
||||
local root_range = { root_node:range() }
|
||||
|
||||
-- Only consider trees that contain this line
|
||||
if root_start_row > line or root_end_row < line then
|
||||
if
|
||||
not Range.intercepts(
|
||||
root_range,
|
||||
{ range_start_row, range_start_col, range_end_row, range_end_col }
|
||||
)
|
||||
then
|
||||
return
|
||||
end
|
||||
|
||||
@@ -378,85 +417,114 @@ local function on_line_impl(self, win, buf, line, on_spell, on_conceal)
|
||||
local next_marks = {}
|
||||
|
||||
for _, mark in ipairs(state.prev_marks) do
|
||||
add_mark(mark, buf, line, next_marks)
|
||||
add_mark(
|
||||
mark,
|
||||
buf,
|
||||
range_start_row,
|
||||
range_start_col,
|
||||
range_end_row,
|
||||
range_end_col,
|
||||
next_marks
|
||||
)
|
||||
end
|
||||
|
||||
if state.iter == nil or state.next_row < line then
|
||||
local next_row = state.next_row
|
||||
local next_col = state.next_col
|
||||
|
||||
if state.iter == nil or cmp_lt(next_row, next_col, range_start_row, range_start_col) then
|
||||
-- Mainly used to skip over folds
|
||||
|
||||
-- TODO(lewis6991): Creating a new iterator loses the cached predicate results for query
|
||||
-- matches. Move this logic inside iter_captures() so we can maintain the cache.
|
||||
state.iter =
|
||||
state.highlighter_query:query():iter_captures(root_node, self.bufnr, line, root_end_row + 1)
|
||||
state.iter = state.highlighter_query:query():iter_captures(
|
||||
root_node,
|
||||
self.bufnr,
|
||||
range_start_row,
|
||||
root_range[3],
|
||||
{ start_col = range_start_col, end_col = root_range[4] }
|
||||
)
|
||||
end
|
||||
|
||||
local captures = state.highlighter_query:query().captures
|
||||
|
||||
while line >= state.next_row do
|
||||
local capture, node, metadata, match = state.iter(line)
|
||||
|
||||
local outer_range = { root_end_row + 1, 0, root_end_row + 1, 0 }
|
||||
if node then
|
||||
outer_range = vim.treesitter.get_range(node, buf, metadata and metadata[capture])
|
||||
while cmp_lt(next_row, next_col, range_end_row, range_end_col) do
|
||||
local capture, node, metadata, match = state.iter(range_end_row, range_end_col)
|
||||
if not node then
|
||||
next_row = math.huge
|
||||
next_col = math.huge
|
||||
break
|
||||
end
|
||||
|
||||
local outer_range = vim.treesitter.get_range(node, buf, metadata and metadata[capture])
|
||||
if cmp_lt(next_row, next_col, outer_range[1], outer_range[2]) then
|
||||
next_row = outer_range[1]
|
||||
next_col = outer_range[2]
|
||||
end
|
||||
|
||||
if not capture then
|
||||
break
|
||||
end
|
||||
local outer_range_start_row = outer_range[1]
|
||||
|
||||
for _, range in ipairs(tree_region) do
|
||||
local intersection = Range.intersection(range, outer_range)
|
||||
if intersection then
|
||||
local start_row, start_col, end_row, end_col = Range.unpack4(intersection)
|
||||
|
||||
if capture then
|
||||
local hl = state.highlighter_query:get_hl_from_capture(capture)
|
||||
local hl = state.highlighter_query:get_hl_from_capture(capture)
|
||||
|
||||
local capture_name = captures[capture]
|
||||
local capture_name = captures[capture]
|
||||
|
||||
local spell, spell_pri_offset = get_spell(capture_name)
|
||||
local spell, spell_pri_offset = get_spell(capture_name)
|
||||
|
||||
-- The "priority" attribute can be set at the pattern level or on a particular capture
|
||||
local priority = (
|
||||
tonumber(metadata.priority or metadata[capture] and metadata[capture].priority)
|
||||
or vim.hl.priorities.treesitter
|
||||
) + spell_pri_offset
|
||||
-- The "priority" attribute can be set at the pattern level or on a particular capture
|
||||
local priority = (
|
||||
tonumber(metadata.priority or metadata[capture] and metadata[capture].priority)
|
||||
or vim.hl.priorities.treesitter
|
||||
) + spell_pri_offset
|
||||
|
||||
-- The "conceal" attribute can be set at the pattern level or on a particular capture
|
||||
local conceal = metadata.conceal or metadata[capture] and metadata[capture].conceal
|
||||
-- The "conceal" attribute can be set at the pattern level or on a particular capture
|
||||
local conceal = metadata.conceal or metadata[capture] and metadata[capture].conceal
|
||||
|
||||
local url = get_url(match, buf, capture, metadata)
|
||||
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
|
||||
local opts = {
|
||||
end_line = end_row,
|
||||
end_col = end_col,
|
||||
hl_group = hl,
|
||||
ephemeral = true,
|
||||
priority = priority,
|
||||
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 hl and not on_conceal and (not on_spell or spell ~= nil) then
|
||||
local opts = {
|
||||
end_line = end_row,
|
||||
end_col = end_col,
|
||||
hl_group = hl,
|
||||
ephemeral = true,
|
||||
priority = priority,
|
||||
conceal = conceal,
|
||||
spell = spell,
|
||||
url = url,
|
||||
}
|
||||
local mark = { start_line = start_row, start_col = start_col, opts = opts }
|
||||
add_mark(
|
||||
mark,
|
||||
buf,
|
||||
range_start_row,
|
||||
range_start_col,
|
||||
range_end_row,
|
||||
range_end_col,
|
||||
next_marks
|
||||
)
|
||||
end
|
||||
|
||||
if
|
||||
(metadata.conceal_lines or metadata[capture] and metadata[capture].conceal_lines)
|
||||
and #api.nvim_buf_get_extmarks(buf, ns, { start_row, 0 }, { start_row, 0 }, {}) == 0
|
||||
then
|
||||
api.nvim_buf_set_extmark(buf, ns, start_row, 0, {
|
||||
end_line = end_row,
|
||||
conceal_lines = '',
|
||||
})
|
||||
end
|
||||
if
|
||||
(metadata.conceal_lines or metadata[capture] and metadata[capture].conceal_lines)
|
||||
and #api.nvim_buf_get_extmarks(buf, ns, { start_row, 0 }, { start_row, 0 }, {}) == 0
|
||||
then
|
||||
api.nvim_buf_set_extmark(buf, ns, start_row, 0, {
|
||||
end_line = end_row,
|
||||
conceal_lines = '',
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if outer_range_start_row > line then
|
||||
state.next_row = outer_range_start_row
|
||||
end
|
||||
end
|
||||
|
||||
state.next_row = next_row
|
||||
state.next_col = next_col
|
||||
state.prev_marks = next_marks
|
||||
end)
|
||||
end
|
||||
@@ -464,14 +532,17 @@ end
|
||||
---@private
|
||||
---@param win integer
|
||||
---@param buf integer
|
||||
---@param line integer
|
||||
function TSHighlighter._on_line(_, win, buf, line, _)
|
||||
---@param br integer
|
||||
---@param bc integer
|
||||
---@param er integer
|
||||
---@param ec integer
|
||||
function TSHighlighter._on_range(_, win, buf, br, bc, er, ec, _)
|
||||
local self = TSHighlighter.active[buf]
|
||||
if not self then
|
||||
return
|
||||
end
|
||||
|
||||
on_line_impl(self, win, buf, line, false, false)
|
||||
on_range_impl(self, win, buf, br, bc, er, ec, false, false)
|
||||
end
|
||||
|
||||
---@private
|
||||
@@ -490,9 +561,7 @@ function TSHighlighter._on_spell_nav(_, win, buf, srow, _, erow, _)
|
||||
local highlight_states = self._highlight_states[win]
|
||||
self:prepare_highlight_states(win, srow, erow)
|
||||
|
||||
for row = srow, erow do
|
||||
on_line_impl(self, win, buf, row, true, false)
|
||||
end
|
||||
on_range_impl(self, win, buf, srow, 0, erow, 0, true, false)
|
||||
self._highlight_states[win] = highlight_states
|
||||
end
|
||||
|
||||
@@ -510,7 +579,7 @@ function TSHighlighter._on_conceal_line(_, win, buf, row)
|
||||
local highlight_states = self._highlight_states[win]
|
||||
self.tree:parse({ row, row })
|
||||
self:prepare_highlight_states(win, row, row)
|
||||
on_line_impl(self, win, buf, row, false, true)
|
||||
on_range_impl(self, win, buf, row, 0, row + 1, 0, false, true)
|
||||
self._highlight_states[win] = highlight_states
|
||||
end
|
||||
|
||||
@@ -554,7 +623,7 @@ end
|
||||
|
||||
api.nvim_set_decoration_provider(ns, {
|
||||
on_win = TSHighlighter._on_win,
|
||||
on_line = TSHighlighter._on_line,
|
||||
on_range = TSHighlighter._on_range,
|
||||
_on_spell_nav = TSHighlighter._on_spell_nav,
|
||||
_on_conceal_line = TSHighlighter._on_conceal_line,
|
||||
})
|
||||
|
@@ -188,7 +188,7 @@ end
|
||||
|
||||
--- Returns available treesitter languages.
|
||||
function M._complete()
|
||||
local parsers = vim.api.nvim_get_runtime_file('parser/*', true)
|
||||
local parsers = api.nvim_get_runtime_file('parser/*', true)
|
||||
local parser_names_set = {} ---@type table<string, boolean>
|
||||
for _, parser in ipairs(parsers) do
|
||||
local parser_name = vim.fn.fnamemodify(parser, ':t:r')
|
||||
|
@@ -988,7 +988,7 @@ end
|
||||
|
||||
---@private
|
||||
--- Extract injections according to:
|
||||
--- https://tree-sitter.github.io/tree-sitter/syntax-highlighting#language-injection
|
||||
--- https://tree-sitter.github.io/tree-sitter/3-syntax-highlighting.html#language-injection
|
||||
---@param match table<integer,TSNode[]>
|
||||
---@param metadata vim.treesitter.query.TSMetadata
|
||||
---@return string?, boolean, Range6[]
|
||||
|
@@ -4,6 +4,7 @@
|
||||
local api = vim.api
|
||||
local language = require('vim.treesitter.language')
|
||||
local memoize = vim.func._memoize
|
||||
local cmp_ge = require('vim.treesitter._range').cmp_pos.ge
|
||||
|
||||
local MODELINE_FORMAT = '^;+%s*inherits%s*:?%s*([a-z_,()]+)%s*$'
|
||||
local EXTENDS_FORMAT = '^;+%s*extends%s*$'
|
||||
@@ -951,18 +952,20 @@ end
|
||||
---
|
||||
---@param node TSNode under which the search will occur
|
||||
---@param source (integer|string) Source buffer or string to extract text from
|
||||
---@param start? integer Starting line for the search. Defaults to `node:start()`.
|
||||
---@param stop? integer Stopping line for the search (end-exclusive). Defaults to `node:end_()`.
|
||||
---@param start_row? integer Starting line for the search. Defaults to `node:start()`.
|
||||
---@param end_row? integer Stopping line for the search (end-inclusive, unless `stop_col` is provided). Defaults to `node:end_()`.
|
||||
---@param opts? table Optional keyword arguments:
|
||||
--- - max_start_depth (integer) if non-zero, sets the maximum start depth
|
||||
--- for each match. This is used to prevent traversing too deep into a tree.
|
||||
--- - match_limit (integer) Set the maximum number of in-progress matches (Default: 256).
|
||||
--- - start_col (integer) Starting column for the search.
|
||||
--- - end_col (integer) Stopping column for the search (end-exclusive).
|
||||
---
|
||||
---@return (fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch, TSTree):
|
||||
---@return (fun(end_line: integer|nil, end_col: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch, TSTree):
|
||||
--- capture id, capture node, metadata, match, tree
|
||||
---
|
||||
---@note Captures are only returned if the query pattern of a specific capture contained predicates.
|
||||
function Query:iter_captures(node, source, start, stop, opts)
|
||||
function Query:iter_captures(node, source, start_row, end_row, opts)
|
||||
opts = opts or {}
|
||||
opts.match_limit = opts.match_limit or 256
|
||||
|
||||
@@ -970,17 +973,24 @@ function Query:iter_captures(node, source, start, stop, opts)
|
||||
source = api.nvim_get_current_buf()
|
||||
end
|
||||
|
||||
start, stop = value_or_node_range(start, stop, node)
|
||||
start_row, end_row = value_or_node_range(start_row, end_row, node)
|
||||
|
||||
local tree = node:tree()
|
||||
local cursor = vim._create_ts_querycursor(node, self.query, start, stop, opts)
|
||||
local cursor = vim._create_ts_querycursor(node, self.query, {
|
||||
start_row = start_row,
|
||||
start_col = opts.start_col or 0,
|
||||
end_row = end_row,
|
||||
end_col = opts.end_col or 0,
|
||||
max_start_depth = opts.max_start_depth,
|
||||
match_limit = opts.match_limit or 256,
|
||||
})
|
||||
|
||||
-- For faster checks that a match is not in the cache.
|
||||
local highest_cached_match_id = -1
|
||||
---@type table<integer, vim.treesitter.query.TSMetadata>
|
||||
local match_cache = {}
|
||||
|
||||
local function iter(end_line)
|
||||
local function iter(end_line, end_col)
|
||||
local capture, captured_node, match = cursor:next_capture()
|
||||
|
||||
if not capture then
|
||||
@@ -1005,9 +1015,22 @@ function Query:iter_captures(node, source, start, stop, opts)
|
||||
local predicates = processed_pattern.predicates
|
||||
if not self:_match_predicates(predicates, pattern_i, captures, source) then
|
||||
cursor:remove_match(match_id)
|
||||
if end_line and captured_node:range() > end_line then
|
||||
|
||||
local row, col = captured_node:range()
|
||||
|
||||
local outside = false
|
||||
if end_line then
|
||||
if end_col then
|
||||
outside = cmp_ge(row, col, end_line, end_col)
|
||||
else
|
||||
outside = row > end_line
|
||||
end
|
||||
end
|
||||
|
||||
if outside then
|
||||
return nil, captured_node, nil, nil
|
||||
end
|
||||
|
||||
return iter(end_line) -- tail call: try next match
|
||||
end
|
||||
|
||||
@@ -1072,7 +1095,14 @@ function Query:iter_matches(node, source, start, stop, opts)
|
||||
start, stop = value_or_node_range(start, stop, node)
|
||||
|
||||
local tree = node:tree()
|
||||
local cursor = vim._create_ts_querycursor(node, self.query, start, stop, opts)
|
||||
local cursor = vim._create_ts_querycursor(node, self.query, {
|
||||
start_row = start,
|
||||
start_col = 0,
|
||||
end_row = stop,
|
||||
end_col = 0,
|
||||
max_start_depth = opts.max_start_depth,
|
||||
match_limit = opts.match_limit or 256,
|
||||
})
|
||||
|
||||
local function iter()
|
||||
local match = cursor:next_match()
|
||||
|
@@ -3,7 +3,7 @@
|
||||
" Maintainer: Aliaksei Budavei <0x000c70 AT gmail DOT com>
|
||||
" Former Maintainer: Claudio Fleiner <claudio@fleiner.com>
|
||||
" Repository: https://github.com/zzzyxwvut/java-vim.git
|
||||
" Last Change: 2025 Aug 07
|
||||
" Last Change: 2025 Aug 30
|
||||
|
||||
" Please check ":help java.vim" for comments on some of the options
|
||||
" available.
|
||||
@@ -447,6 +447,11 @@ if !exists("g:java_ignore_javadoc") && (s:with_html || s:with_markdown) && g:mai
|
||||
unlet g:html_syntax_folding
|
||||
endif
|
||||
|
||||
if !empty(get(g:, 'markdown_fenced_languages', []))
|
||||
let s:markdown_fenced_languages_copy = g:markdown_fenced_languages
|
||||
unlet g:markdown_fenced_languages
|
||||
endif
|
||||
|
||||
syntax include @javaMarkdown syntax/markdown.vim
|
||||
|
||||
try
|
||||
@@ -464,6 +469,11 @@ if !exists("g:java_ignore_javadoc") && (s:with_html || s:with_markdown) && g:mai
|
||||
finally
|
||||
unlet! b:current_syntax
|
||||
|
||||
if exists("s:markdown_fenced_languages_copy")
|
||||
let g:markdown_fenced_languages = s:markdown_fenced_languages_copy
|
||||
unlet s:markdown_fenced_languages_copy
|
||||
endif
|
||||
|
||||
if exists("s:html_syntax_folding_copy")
|
||||
let g:html_syntax_folding = s:html_syntax_folding_copy
|
||||
unlet s:html_syntax_folding_copy
|
||||
|
@@ -1,9 +1,8 @@
|
||||
" Vim syntax file
|
||||
" Language: M4
|
||||
" Language: M4
|
||||
" Maintainer: Claudio Fleiner (claudio@fleiner.com)
|
||||
" URL: http://www.fleiner.com/vim/syntax/m4.vim
|
||||
" (outdated)
|
||||
" Last Change: 2022 Jun 12
|
||||
" 2025 Sep 2 by Vim project: fix a few syntax issues #18192
|
||||
|
||||
" This file will highlight user function calls if they use only
|
||||
" capital letters and have at least one argument (i.e. the '('
|
||||
@@ -18,6 +17,23 @@ if !exists("main_syntax")
|
||||
let main_syntax='m4'
|
||||
endif
|
||||
|
||||
" Reference: The Open Group Base Specifications, M4
|
||||
" https://pubs.opengroup.org/onlinepubs/9799919799/
|
||||
|
||||
" Quoting in M4:
|
||||
" – Quotes are nestable;
|
||||
" – The delimiters can be redefined with changequote(); here we only handle
|
||||
" the default pair: ` ... ';
|
||||
" – Quoted text in M4 is rescanned, not treated as a literal string;
|
||||
" Therefore the region is marked transparent so contained items retain
|
||||
" their normal highlighting.
|
||||
syn region m4Quoted
|
||||
\ matchgroup=m4QuoteDelim
|
||||
\ start=+`+
|
||||
\ end=+'+
|
||||
\ contains=@m4Top
|
||||
\ transparent
|
||||
|
||||
" define the m4 syntax
|
||||
syn match m4Variable contained "\$\d\+"
|
||||
syn match m4Special contained "$[@*#]"
|
||||
@@ -30,28 +46,26 @@ syn region m4Paren matchgroup=m4Delimiter start="(" end=")" contained contain
|
||||
syn region m4Command matchgroup=m4Function start="\<\(m4_\)\=\(define\|defn\|pushdef\)(" end=")" contains=@m4Top
|
||||
syn region m4Command matchgroup=m4Preproc start="\<\(m4_\)\=\(include\|sinclude\)("he=e-1 end=")" contains=@m4Top
|
||||
syn region m4Command matchgroup=m4Statement start="\<\(m4_\)\=\(syscmd\|esyscmd\|ifdef\|ifelse\|indir\|builtin\|shift\|errprint\|m4exit\|changecom\|changequote\|changeword\|m4wrap\|debugfile\|divert\|undivert\)("he=e-1 end=")" contains=@m4Top
|
||||
syn region m4Command matchgroup=m4builtin start="\<\(m4_\)\=\(len\|index\|regexp\|substr\|translit\|patsubst\|format\|incr\|decr\|eval\|maketemp\)("he=e-1 end=")" contains=@m4Top
|
||||
syn region m4Command matchgroup=m4Builtin start="\<\(m4_\)\=\(len\|index\|regexp\|substr\|translit\|patsubst\|format\|incr\|decr\|eval\|maketemp\)("he=e-1 end=")" contains=@m4Top
|
||||
syn keyword m4Statement divert undivert
|
||||
syn region m4Command matchgroup=m4Type start="\<\(m4_\)\=\(undefine\|popdef\)("he=e-1 end=")" contains=@m4Top
|
||||
syn region m4Function matchgroup=m4Type start="\<[_A-Z][_A-Z0-9]*("he=e-1 end=")" contains=@m4Top
|
||||
syn region m4String start="`" end="'" contains=SpellErrors
|
||||
syn cluster m4Top contains=m4Comment,m4Constants,m4Special,m4Variable,m4String,m4Paren,m4Command,m4Statement,m4Function
|
||||
syn cluster m4Top contains=m4Comment,m4Constants,m4Special,m4Variable,m4Paren,m4Command,m4Statement,m4Function,m4Quoted
|
||||
|
||||
" Define the default highlighting.
|
||||
" Only when an item doesn't have highlighting yet
|
||||
hi def link m4Delimiter Delimiter
|
||||
hi def link m4Comment Comment
|
||||
hi def link m4Function Function
|
||||
hi def link m4Keyword Keyword
|
||||
hi def link m4Special Special
|
||||
hi def link m4String String
|
||||
hi def link m4Statement Statement
|
||||
hi def link m4Preproc PreProc
|
||||
hi def link m4Type Type
|
||||
hi def link m4Special Special
|
||||
hi def link m4Variable Special
|
||||
hi def link m4Constants Constant
|
||||
hi def link m4Builtin Statement
|
||||
hi def link m4QuoteDelim Delimiter
|
||||
hi def link m4Delimiter Delimiter
|
||||
hi def link m4Comment Comment
|
||||
hi def link m4Function Function
|
||||
hi def link m4Keyword Keyword
|
||||
hi def link m4Special Special
|
||||
hi def link m4Statement Statement
|
||||
hi def link m4Preproc PreProc
|
||||
hi def link m4Type Type
|
||||
hi def link m4Variable Special
|
||||
hi def link m4Constants Constant
|
||||
hi def link m4Builtin Statement
|
||||
|
||||
let b:current_syntax = "m4"
|
||||
|
||||
|
@@ -1,30 +1,32 @@
|
||||
" Vim syntax file
|
||||
" Language: Debian version information
|
||||
" Maintainer: Debian Vim Maintainers
|
||||
" Last Change: 2025 Apr 24
|
||||
" Last Change: 2025 Aug 26
|
||||
" URL: https://salsa.debian.org/vim-team/vim-debian/blob/main/syntax/shared/debversions.vim
|
||||
|
||||
let s:cpo = &cpo
|
||||
set cpo-=C
|
||||
|
||||
" Version names that are upcoming or released and still within the standard support window
|
||||
let g:debSharedSupportedVersions = [
|
||||
\ 'oldstable', 'stable', 'testing', 'unstable', 'experimental', 'sid', 'rc-buggy',
|
||||
\ 'bullseye', 'bookworm', 'trixie', 'forky', 'duke',
|
||||
\ 'bookworm', 'trixie', 'forky', 'duke',
|
||||
\
|
||||
\ 'focal', 'jammy', 'noble', 'oracular', 'plucky', 'questing',
|
||||
\ 'jammy', 'noble', 'plucky', 'questing',
|
||||
\ 'devel'
|
||||
\ ]
|
||||
" Historic version names, no longer under standard support
|
||||
let g:debSharedUnsupportedVersions = [
|
||||
\ 'buzz', 'rex', 'bo', 'hamm', 'slink', 'potato',
|
||||
\ 'woody', 'sarge', 'etch', 'lenny', 'squeeze', 'wheezy',
|
||||
\ 'jessie', 'stretch', 'buster',
|
||||
\ 'jessie', 'stretch', 'buster', 'bullseye',
|
||||
\
|
||||
\ 'warty', 'hoary', 'breezy', 'dapper', 'edgy', 'feisty',
|
||||
\ 'gutsy', 'hardy', 'intrepid', 'jaunty', 'karmic', 'lucid',
|
||||
\ 'maverick', 'natty', 'oneiric', 'precise', 'quantal', 'raring', 'saucy',
|
||||
\ 'trusty', 'utopic', 'vivid', 'wily', 'xenial', 'yakkety', 'zesty',
|
||||
\ 'artful', 'bionic', 'cosmic', 'disco', 'eoan', 'groovy',
|
||||
\ 'hirsute', 'impish', 'kinetic', 'lunar', 'mantic',
|
||||
\ 'artful', 'bionic', 'cosmic', 'disco', 'eoan', 'focal', 'groovy',
|
||||
\ 'hirsute', 'impish', 'kinetic', 'lunar', 'mantic', 'oracular',
|
||||
\ ]
|
||||
|
||||
let &cpo=s:cpo
|
||||
|
@@ -232,7 +232,7 @@ syn match vimNumber '\<0z\%(\x\x\)\+\%(\.\%(\x\x\)\+\)*' skipwhite nextgroup=@vi
|
||||
syn case match
|
||||
|
||||
" All vimCommands are contained by vimIsCommand. {{{2
|
||||
syn cluster vimCmdList contains=vimAbb,vimAddress,vimAutocmd,vimAugroup,vimBehave,vimCall,vimCatch,vimCommandModifier,vimConst,vimDoautocmd,vimDebuggreedy,vimDef,vimDefFold,vimDelcommand,vimDelFunction,@vimEcho,vimElse,vimEnddef,vimEndfunction,vimEndif,vimEval,vimExecute,vimIsCommand,vimExtCmd,vimExFilter,vimExMark,vimFiletype,vimFor,vimFunction,vimFunctionFold,vimGrep,vimGrepAdd,vimGlobal,vimHelpgrep,vimHighlight,vimImport,vimLet,vimLoadkeymap,vimLockvar,vimMake,vimMap,vimMark,vimMatch,vimNotFunc,vimNormal,vimProfdel,vimProfile,vimRedir,vimSet,vimSleep,vimSort,vimSyntax,vimSynColor,vimSynLink,vimThrow,vimUniq,vimUnlet,vimUnlockvar,vimUnmap,vimUserCmd,vimVimgrep,vimVimgrepadd,vimMenu,vimMenutranslate,@vim9CmdList,@vimExUserCmdList,vimLua,vimMzScheme,vimPerl,vimPython,vimPython3,vimPythonX,vimRuby,vimTcl
|
||||
syn cluster vimCmdList contains=vimAbb,vimAddress,vimAutocmd,vimAugroup,vimBehave,vimCall,vimCatch,vimCommandModifier,vimConst,vimDoautocmd,vimDebuggreedy,vimDef,vimDefFold,vimDefer,vimDelcommand,vimDelFunction,@vimEcho,vimElse,vimEnddef,vimEndfunction,vimEndif,vimEval,vimExecute,vimIsCommand,vimExtCmd,vimExFilter,vimExMark,vimFiletype,vimFor,vimFunction,vimFunctionFold,vimGrep,vimGrepAdd,vimGlobal,vimHelpgrep,vimHighlight,vimImport,vimLet,vimLoadkeymap,vimLockvar,vimMake,vimMap,vimMark,vimMatch,vimNotFunc,vimNormal,vimProfdel,vimProfile,vimRedir,vimSet,vimSleep,vimSort,vimSyntax,vimSynColor,vimSynLink,vimThrow,vimUniq,vimUnlet,vimUnlockvar,vimUnmap,vimUserCmd,vimVimgrep,vimVimgrepadd,vimMenu,vimMenutranslate,@vim9CmdList,@vimExUserCmdList,vimLua,vimMzScheme,vimPerl,vimPython,vimPython3,vimPythonX,vimRuby,vimTcl
|
||||
syn cluster vim9CmdList contains=vim9Abstract,vim9Class,vim9Const,vim9Enum,vim9Export,vim9Final,vim9For,vim9Interface,vim9Type,vim9Var
|
||||
syn match vimCmdSep "\\\@1<!|" skipwhite nextgroup=@vimCmdList,vimSubst1,@vimFunc
|
||||
syn match vimCmdSep ":\+" skipwhite nextgroup=@vimCmdList,vimSubst1
|
||||
@@ -300,6 +300,10 @@ syn match vimCall "\<call\=\>" skipwhite nextgroup=vimVar,@vimFunc
|
||||
" TODO: special-cased until generalised range/count support is implemented
|
||||
syn match vimDebuggreedy "\<0\=debugg\%[reedy]\>" contains=vimCount
|
||||
|
||||
" Defer {{{2
|
||||
" =====
|
||||
syn match vimDefer "\<defer\=\>" skipwhite nextgroup=@vimFunc
|
||||
|
||||
" Exception Handling {{{2
|
||||
syn keyword vimThrow th[row] skipwhite nextgroup=@vimExprList
|
||||
syn keyword vimCatch cat[ch] skipwhite nextgroup=vimCatchPattern
|
||||
@@ -2240,6 +2244,7 @@ if !exists("skip_vim_syntax_inits")
|
||||
hi def link vimDef vimCommand
|
||||
hi def link vimDefBang vimBang
|
||||
hi def link vimDefComment vim9Comment
|
||||
hi def link vimDefer vimCommand
|
||||
hi def link vimDefParam vimVar
|
||||
hi def link vimDelcommand vimCommand
|
||||
hi def link vimDelcommandAttr vimUserCmdAttr
|
||||
|
@@ -4,8 +4,8 @@
|
||||
# changelog header
|
||||
header = """
|
||||
# Changelog\n
|
||||
For notable changes, see runtime/doc/news.txt (or `:help news` in Nvim).\n
|
||||
Following is a list of fixes/features commits.\n
|
||||
Following is a list of commits (fixes/features only) in this release.\n
|
||||
See `:help news` in Nvim for release notes.\n
|
||||
"""
|
||||
# template for the changelog body
|
||||
# https://github.com/Keats/tera
|
||||
|
@@ -97,5 +97,5 @@ echo "
|
||||
Next steps:
|
||||
- Run tests/CI (version_spec.lua)!
|
||||
- Push the tag:
|
||||
git push --follow-tags
|
||||
git push v${__VERSION}
|
||||
- Update website: index.html"
|
||||
|
@@ -395,6 +395,7 @@ output:write([[
|
||||
#include "nvim/api/buffer.h"
|
||||
#include "nvim/api/command.h"
|
||||
#include "nvim/api/deprecated.h"
|
||||
#include "nvim/api/events.h"
|
||||
#include "nvim/api/extmark.h"
|
||||
#include "nvim/api/options.h"
|
||||
#include "nvim/api/tabpage.h"
|
||||
@@ -421,7 +422,7 @@ for _, k in ipairs(keysets) do
|
||||
local function typename(type)
|
||||
if type == 'HLGroupID' then
|
||||
return 'kObjectTypeInteger'
|
||||
elseif not type or vim.startswith(type, 'Union') then
|
||||
elseif not type or startswith(type, 'Union') then
|
||||
return 'kObjectTypeNil'
|
||||
elseif type == 'StringArray' then
|
||||
return 'kUnpackTypeStringArray'
|
||||
|
@@ -73,6 +73,7 @@ local new_layout = {
|
||||
['intro.txt'] = true,
|
||||
['lua.txt'] = true,
|
||||
['lua-guide.txt'] = true,
|
||||
['lua-plugin.txt'] = true,
|
||||
['luaref.txt'] = true,
|
||||
['news.txt'] = true,
|
||||
['news-0.9.txt'] = true,
|
||||
|
@@ -1,5 +1,8 @@
|
||||
#!/usr/bin/env -S nvim -l
|
||||
--- Generates Nvim :help docs from Lua/C docstrings
|
||||
--- Generates Nvim :help docs from Lua/C docstrings.
|
||||
---
|
||||
--- Usage:
|
||||
--- make doc
|
||||
---
|
||||
--- The generated :help text for each function is formatted as follows:
|
||||
--- - Max width of 78 columns (`TEXT_WIDTH`).
|
||||
@@ -106,6 +109,7 @@ local config = {
|
||||
filename = 'api.txt',
|
||||
section_order = {
|
||||
-- Sections at the top, in a specific order:
|
||||
'events.c',
|
||||
'vim.c',
|
||||
'vimscript.c',
|
||||
|
||||
@@ -126,11 +130,22 @@ local config = {
|
||||
['vim.c'] = 'Global',
|
||||
},
|
||||
section_fmt = function(name)
|
||||
if name == 'Events' then
|
||||
return 'Global Events'
|
||||
end
|
||||
|
||||
return name .. ' Functions'
|
||||
end,
|
||||
helptag_fmt = function(name)
|
||||
return fmt('api-%s', name:lower())
|
||||
end,
|
||||
fn_helptag_fmt = function(fun)
|
||||
local name = fun.name
|
||||
if vim.endswith(name, '_event') then
|
||||
return name
|
||||
end
|
||||
return fn_helptag_fmt_common(fun)
|
||||
end,
|
||||
},
|
||||
lua = {
|
||||
filename = 'lua.txt',
|
||||
@@ -286,6 +301,7 @@ local config = {
|
||||
'inline_completion.lua',
|
||||
'linked_editing_range.lua',
|
||||
'log.lua',
|
||||
'on_type_formatting.lua',
|
||||
'rpc.lua',
|
||||
'semantic_tokens.lua',
|
||||
'tagfunc.lua',
|
||||
@@ -591,7 +607,7 @@ local function render_fields_or_params(xs, generics, classes, cfg)
|
||||
inline_type(p, classes)
|
||||
local nm, ty = p.name, p.type
|
||||
|
||||
local desc = p.classvar and string.format('See |%s|.', cfg.fn_helptag_fmt(p)) or p.desc
|
||||
local desc = p.classvar and fmt('See |%s|.', cfg.fn_helptag_fmt(p)) or p.desc
|
||||
|
||||
local fnm = p.kind == 'operator' and fmt('op(%s)', nm) or fmt_field_name(nm)
|
||||
local pnm = fmt(' • %-' .. indent .. 's', fnm)
|
||||
@@ -1071,7 +1087,7 @@ local function gen_target(cfg)
|
||||
for _, f in ipairs(cfg.section_order) do
|
||||
local section = sections[f]
|
||||
if section then
|
||||
print(string.format(" Rendering section: '%s'", section.title))
|
||||
print(fmt(" Rendering section: '%s'", section.title))
|
||||
local add_sep_and_header = not vim.tbl_contains(cfg.append_only or {}, f)
|
||||
docs[#docs + 1] = render_section(section, add_sep_and_header)
|
||||
end
|
||||
|
@@ -409,13 +409,18 @@ name, etc.
|
||||
|
||||
All the global variables are declared in `globals.h`.
|
||||
|
||||
### The main loop
|
||||
### The main event-loop
|
||||
|
||||
The main loop is implemented in state_enter. The basic idea is that Vim waits
|
||||
for the user to type a character and processes it until another character is
|
||||
needed. Thus there are several places where Vim waits for a character to be
|
||||
typed. The `vgetc()` function is used for this. It also handles mapping.
|
||||
|
||||
What we consider the "Nvim event loop" is actually a wrapper around `uv_run` to
|
||||
handle both the `fast_events` queue and possibly (a suitable subset of) deferred
|
||||
events. Therefore "raw" `vim.uv.run()` is often not enough to "yield" from Lua
|
||||
plugins; instead they can call `vim.wait(0)`.
|
||||
|
||||
Updating the screen is mostly postponed until a command or a sequence of
|
||||
commands has finished. The work is done by `update_screen()`, which calls
|
||||
`win_update()` for every window, which calls `win_line()` for every line.
|
||||
|
76
src/nvim/api/events.c
Normal file
76
src/nvim/api/events.c
Normal file
@@ -0,0 +1,76 @@
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "klib/kvec.h"
|
||||
#include "nvim/api/events.h"
|
||||
#include "nvim/api/private/converter.h"
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/api/private/helpers.h"
|
||||
#include "nvim/api/private/validate.h"
|
||||
#include "nvim/api/ui.h"
|
||||
#include "nvim/assert_defs.h"
|
||||
#include "nvim/autocmd.h"
|
||||
#include "nvim/autocmd_defs.h"
|
||||
#include "nvim/channel.h"
|
||||
#include "nvim/channel_defs.h"
|
||||
#include "nvim/eval.h"
|
||||
#include "nvim/globals.h"
|
||||
#include "nvim/main.h"
|
||||
#include "nvim/map_defs.h"
|
||||
#include "nvim/memory.h"
|
||||
#include "nvim/memory_defs.h"
|
||||
#include "nvim/msgpack_rpc/channel.h"
|
||||
#include "nvim/msgpack_rpc/channel_defs.h"
|
||||
#include "nvim/msgpack_rpc/packer.h"
|
||||
#include "nvim/msgpack_rpc/packer_defs.h"
|
||||
#include "nvim/types_defs.h"
|
||||
#include "nvim/ui.h"
|
||||
|
||||
/// Emitted on the client channel if an async API request responds with an error.
|
||||
///
|
||||
/// @param channel_id
|
||||
/// @param type Error type id as defined by `api_info().error_types`.
|
||||
/// @param msg Error message.
|
||||
void nvim_error_event(uint64_t channel_id, Integer type, String msg)
|
||||
FUNC_API_REMOTE_ONLY
|
||||
{
|
||||
// TODO(bfredl): consider printing message to user, as will be relevant
|
||||
// if we fork nvim processes as async workers
|
||||
ELOG("async error on channel %" PRId64 ": %s", channel_id, msg.size ? msg.data : "");
|
||||
}
|
||||
|
||||
/// Emitted by the TUI client to signal when a host-terminal event occurred.
|
||||
///
|
||||
/// Supports these events:
|
||||
///
|
||||
/// - "termresponse": The host-terminal sent a DA1, OSC, DCS, or APC response sequence to Nvim.
|
||||
/// The payload is the received response. Sets |v:termresponse| and fires
|
||||
/// |TermResponse|.
|
||||
///
|
||||
/// @param channel_id
|
||||
/// @param event Event name
|
||||
/// @param value Event payload
|
||||
/// @param[out] err Error details, if any.
|
||||
void nvim_ui_term_event(uint64_t channel_id, String event, Object value, Error *err)
|
||||
FUNC_API_SINCE(12) FUNC_API_REMOTE_ONLY
|
||||
{
|
||||
if (strequal("termresponse", event.data)) {
|
||||
if (value.type != kObjectTypeString) {
|
||||
api_set_error(err, kErrorTypeValidation, "termresponse must be a string");
|
||||
return;
|
||||
}
|
||||
|
||||
const String termresponse = value.data.string;
|
||||
set_vim_var_string(VV_TERMRESPONSE, termresponse.data, (ptrdiff_t)termresponse.size);
|
||||
|
||||
MAXSIZE_TEMP_DICT(data, 1);
|
||||
PUT_C(data, "sequence", value);
|
||||
apply_autocmds_group(EVENT_TERMRESPONSE, NULL, NULL, true, AUGROUP_ALL, NULL, NULL,
|
||||
&DICT_OBJ(data));
|
||||
}
|
||||
}
|
8
src/nvim/api/events.h
Normal file
8
src/nvim/api/events.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h> // IWYU pragma: keep
|
||||
|
||||
#include "nvim/api/keysets_defs.h" // IWYU pragma: keep
|
||||
#include "nvim/api/private/defs.h" // IWYU pragma: keep
|
||||
|
||||
#include "api/events.h.generated.h"
|
@@ -1004,7 +1004,7 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
|
||||
/// Note: this function should not be called often. Rather, the callbacks
|
||||
/// themselves can be used to throttle unneeded callbacks. the `on_start`
|
||||
/// callback can return `false` to disable the provider until the next redraw.
|
||||
/// Similarly, return `false` in `on_win` will skip the `on_line` calls
|
||||
/// Similarly, return `false` in `on_win` will skip the `on_line` and `on_range` calls
|
||||
/// for that window (but any extmarks set in `on_win` will still be used).
|
||||
/// A plugin managing multiple sources of decoration should ideally only set
|
||||
/// one provider, and merge the sources internally. You can use multiple `ns_id`
|
||||
@@ -1016,7 +1016,7 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
|
||||
/// Doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is quite dubious
|
||||
/// for the moment.
|
||||
///
|
||||
/// Note: It is not allowed to remove or update extmarks in `on_line` callbacks.
|
||||
/// Note: It is not allowed to remove or update extmarks in `on_line` or `on_range` callbacks.
|
||||
///
|
||||
/// @param ns_id Namespace id from |nvim_create_namespace()|
|
||||
/// @param opts Table of callbacks:
|
||||
@@ -1033,11 +1033,18 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
|
||||
/// ```
|
||||
/// ["win", winid, bufnr, toprow, botrow]
|
||||
/// ```
|
||||
/// - on_line: called for each buffer line being redrawn.
|
||||
/// (The interaction with fold lines is subject to change)
|
||||
/// - on_line: (deprecated, use on_range instead)
|
||||
/// ```
|
||||
/// ["line", winid, bufnr, row]
|
||||
/// ```
|
||||
/// - on_range: called for each buffer range being redrawn.
|
||||
/// Range is end-exclusive and may span multiple lines. Range
|
||||
/// bounds point to the first byte of a character. An end position
|
||||
/// of the form (lnum, 0), including (number of lines, 0), is valid
|
||||
/// and indicates that EOL of the preceding line is included.
|
||||
/// ```
|
||||
/// ["range", winid, bufnr, begin_row, begin_col, end_row, end_col]
|
||||
/// ```
|
||||
/// - on_end: called at the end of a redraw cycle
|
||||
/// ```
|
||||
/// ["end", tick]
|
||||
@@ -1061,6 +1068,7 @@ void nvim_set_decoration_provider(Integer ns_id, Dict(set_decoration_provider) *
|
||||
{ "on_buf", &opts->on_buf, &p->redraw_buf },
|
||||
{ "on_win", &opts->on_win, &p->redraw_win },
|
||||
{ "on_line", &opts->on_line, &p->redraw_line },
|
||||
{ "on_range", &opts->on_range, &p->redraw_range },
|
||||
{ "on_end", &opts->on_end, &p->redraw_end },
|
||||
{ "_on_hl_def", &opts->_on_hl_def, &p->hl_def },
|
||||
{ "_on_spell_nav", &opts->_on_spell_nav, &p->spell_nav },
|
||||
|
@@ -18,6 +18,8 @@ typedef struct {
|
||||
LuaRefOf(("win" _, Integer winid, Integer bufnr, Integer toprow, Integer botrow),
|
||||
*Boolean) on_win;
|
||||
LuaRefOf(("line" _, Integer winid, Integer bufnr, Integer row), *Boolean) on_line;
|
||||
LuaRefOf(("range" _, Integer winid, Integer bufnr, Integer start_row, Integer start_col,
|
||||
Integer end_row, Integer end_col), *Boolean) on_range;
|
||||
LuaRefOf(("end" _, Integer tick)) on_end;
|
||||
LuaRefOf(("hl_def" _)) _on_hl_def;
|
||||
LuaRefOf(("spell_nav" _)) _on_spell_nav;
|
||||
|
@@ -567,37 +567,6 @@ void nvim_ui_pum_set_bounds(uint64_t channel_id, Float width, Float height, Floa
|
||||
ui->pum_pos = true;
|
||||
}
|
||||
|
||||
/// Tells Nvim when a host-terminal event occurred.
|
||||
///
|
||||
/// Supports these events:
|
||||
///
|
||||
/// - "termresponse": The host-terminal sent a DA1, OSC, DCS, or APC response sequence to Nvim.
|
||||
/// The payload is the received response. Sets |v:termresponse| and fires
|
||||
/// |TermResponse|.
|
||||
///
|
||||
/// @param channel_id
|
||||
/// @param event Event name
|
||||
/// @param value Event payload
|
||||
/// @param[out] err Error details, if any.
|
||||
void nvim_ui_term_event(uint64_t channel_id, String event, Object value, Error *err)
|
||||
FUNC_API_SINCE(12) FUNC_API_REMOTE_ONLY
|
||||
{
|
||||
if (strequal("termresponse", event.data)) {
|
||||
if (value.type != kObjectTypeString) {
|
||||
api_set_error(err, kErrorTypeValidation, "termresponse must be a string");
|
||||
return;
|
||||
}
|
||||
|
||||
const String termresponse = value.data.string;
|
||||
set_vim_var_string(VV_TERMRESPONSE, termresponse.data, (ptrdiff_t)termresponse.size);
|
||||
|
||||
MAXSIZE_TEMP_DICT(data, 1);
|
||||
PUT_C(data, "sequence", value);
|
||||
apply_autocmds_group(EVENT_TERMRESPONSE, NULL, NULL, true, AUGROUP_ALL, NULL, NULL,
|
||||
&DICT_OBJ(data));
|
||||
}
|
||||
}
|
||||
|
||||
static void flush_event(RemoteUI *ui)
|
||||
{
|
||||
if (ui->cur_event) {
|
||||
|
@@ -165,7 +165,7 @@ void wildmenu_hide(void)
|
||||
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
|
||||
|
||||
void msg_show(String kind, Array content, Boolean replace_last, Boolean history, Boolean append,
|
||||
Object id, Dict progress)
|
||||
Object id)
|
||||
FUNC_API_SINCE(6) FUNC_API_FAST FUNC_API_REMOTE_ONLY;
|
||||
void msg_clear(void)
|
||||
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
|
||||
|
@@ -332,7 +332,7 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_ks)
|
||||
if (!dangerous) {
|
||||
ex_normal_busy++;
|
||||
}
|
||||
exec_normal(true);
|
||||
exec_normal(true, lowlevel);
|
||||
if (!dangerous) {
|
||||
ex_normal_busy--;
|
||||
}
|
||||
@@ -796,6 +796,7 @@ Union(Integer, String) nvim_echo(ArrayOf(Tuple(String, *HLGroupID)) chunks, Bool
|
||||
}
|
||||
|
||||
bool is_progress = strequal(kind, "progress");
|
||||
bool needs_clear = !history;
|
||||
|
||||
VALIDATE(is_progress
|
||||
|| (opts->status.size == 0 && opts->title.size == 0 && opts->percent == 0
|
||||
@@ -822,7 +823,7 @@ Union(Integer, String) nvim_echo(ArrayOf(Tuple(String, *HLGroupID)) chunks, Bool
|
||||
MessageData msg_data = { .title = opts->title, .status = opts->status,
|
||||
.percent = opts->percent, .data = opts->data };
|
||||
|
||||
id = msg_multihl(opts->id, hl_msg, kind, history, opts->err, &msg_data);
|
||||
id = msg_multihl(opts->id, hl_msg, kind, history, opts->err, &msg_data, &needs_clear);
|
||||
|
||||
if (opts->verbose) {
|
||||
verbose_leave();
|
||||
@@ -833,8 +834,8 @@ Union(Integer, String) nvim_echo(ArrayOf(Tuple(String, *HLGroupID)) chunks, Bool
|
||||
do_autocmd_progress(id, hl_msg, &msg_data);
|
||||
}
|
||||
|
||||
if (history) {
|
||||
// history takes ownership
|
||||
if (!needs_clear) {
|
||||
// history takes ownership of `hl_msg`
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -1557,7 +1558,7 @@ ArrayOf(Object, 2) nvim_get_api_info(uint64_t channel_id, Arena *arena)
|
||||
/// orchestration. (Note: Something is better than nothing! Fields are optional, but at least set
|
||||
/// `name`.)
|
||||
///
|
||||
/// Can be called more than once; the caller should merge old info if appropriate. Example: library
|
||||
/// Can be called more than once; caller should merge old info if appropriate. Example: a library
|
||||
/// first identifies the channel, then a plugin using that library later identifies itself.
|
||||
///
|
||||
/// @param channel_id
|
||||
@@ -2221,15 +2222,6 @@ DictAs(eval_statusline_ret) nvim_eval_statusline(String str, Dict(eval_statuslin
|
||||
return result;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
void nvim_error_event(uint64_t channel_id, Integer lvl, String data)
|
||||
FUNC_API_REMOTE_ONLY
|
||||
{
|
||||
// TODO(bfredl): consider printing message to user, as will be relevant
|
||||
// if we fork nvim processes as async workers
|
||||
ELOG("async error on channel %" PRId64 ": %s", channel_id, data.size ? data.data : "");
|
||||
}
|
||||
|
||||
/// EXPERIMENTAL: this API may change in the future.
|
||||
///
|
||||
/// Sets info for the completion item at the given index. If the info text was shown in a window,
|
||||
|
@@ -513,7 +513,7 @@ static void decor_state_pack(DecorState *state)
|
||||
state->future_begin = fut_beg;
|
||||
}
|
||||
|
||||
bool decor_redraw_line(win_T *wp, int row, DecorState *state)
|
||||
void decor_redraw_line(win_T *wp, int row, DecorState *state)
|
||||
{
|
||||
decor_state_pack(state);
|
||||
|
||||
@@ -527,7 +527,11 @@ bool decor_redraw_line(win_T *wp, int row, DecorState *state)
|
||||
state->row = row;
|
||||
state->col_until = -1;
|
||||
state->eol_col = -1;
|
||||
}
|
||||
|
||||
// Checks if there are (likely) more decorations on the current line.
|
||||
bool decor_has_more_decorations(DecorState *state, int row)
|
||||
{
|
||||
if (state->current_end != 0 || state->future_begin != (int)kv_size(state->ranges_i)) {
|
||||
return true;
|
||||
}
|
||||
|
@@ -149,6 +149,7 @@ typedef struct {
|
||||
LuaRef redraw_buf;
|
||||
LuaRef redraw_win;
|
||||
LuaRef redraw_line;
|
||||
LuaRef redraw_range;
|
||||
LuaRef redraw_end;
|
||||
LuaRef hl_def;
|
||||
LuaRef spell_nav;
|
||||
|
@@ -25,7 +25,7 @@ static kvec_t(DecorProvider) decor_providers = KV_INITIAL_VALUE;
|
||||
|
||||
#define DECORATION_PROVIDER_INIT(ns_id) (DecorProvider) \
|
||||
{ ns_id, kDecorProviderDisabled, LUA_NOREF, LUA_NOREF, \
|
||||
LUA_NOREF, LUA_NOREF, LUA_NOREF, \
|
||||
LUA_NOREF, LUA_NOREF, LUA_NOREF, LUA_NOREF, \
|
||||
LUA_NOREF, LUA_NOREF, -1, false, false, 0 }
|
||||
|
||||
static void decor_provider_error(DecorProvider *provider, const char *name, const char *msg)
|
||||
@@ -189,6 +189,30 @@ void decor_providers_invoke_line(win_T *wp, int row)
|
||||
decor_state.running_decor_provider = false;
|
||||
}
|
||||
|
||||
void decor_providers_invoke_range(win_T *wp, int start_row, int start_col, int end_row, int end_col)
|
||||
{
|
||||
decor_state.running_decor_provider = true;
|
||||
for (size_t i = 0; i < kv_size(decor_providers); i++) {
|
||||
DecorProvider *p = &kv_A(decor_providers, i);
|
||||
if (p->state == kDecorProviderActive && p->redraw_range != LUA_NOREF) {
|
||||
MAXSIZE_TEMP_ARRAY(args, 6);
|
||||
ADD_C(args, WINDOW_OBJ(wp->handle));
|
||||
ADD_C(args, BUFFER_OBJ(wp->w_buffer->handle));
|
||||
ADD_C(args, INTEGER_OBJ(start_row));
|
||||
ADD_C(args, INTEGER_OBJ(start_col));
|
||||
ADD_C(args, INTEGER_OBJ(end_row));
|
||||
ADD_C(args, INTEGER_OBJ(end_col));
|
||||
if (!decor_provider_invoke((int)i, "range", p->redraw_range, args, true)) {
|
||||
// return 'false' or error: skip rest of this window
|
||||
kv_A(decor_providers, i).state = kDecorProviderWinDisabled;
|
||||
}
|
||||
|
||||
hl_check_ns();
|
||||
}
|
||||
}
|
||||
decor_state.running_decor_provider = false;
|
||||
}
|
||||
|
||||
/// For each provider invoke the 'buf' callback for a given buffer.
|
||||
///
|
||||
/// @param buf Buffer
|
||||
@@ -272,6 +296,7 @@ void decor_provider_clear(DecorProvider *p)
|
||||
NLUA_CLEAR_REF(p->redraw_buf);
|
||||
NLUA_CLEAR_REF(p->redraw_win);
|
||||
NLUA_CLEAR_REF(p->redraw_line);
|
||||
NLUA_CLEAR_REF(p->redraw_range);
|
||||
NLUA_CLEAR_REF(p->redraw_end);
|
||||
NLUA_CLEAR_REF(p->spell_nav);
|
||||
NLUA_CLEAR_REF(p->conceal_line);
|
||||
|
@@ -1142,6 +1142,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
||||
// Not drawing text when line is concealed or drawing filler lines beyond last line.
|
||||
const bool draw_text = !concealed && (lnum != buf->b_ml.ml_line_count + 1);
|
||||
|
||||
int decor_provider_end_col;
|
||||
bool check_decor_providers = false;
|
||||
|
||||
if (col_rows == 0 && draw_text) {
|
||||
// To speed up the loop below, set extra_check when there is linebreak,
|
||||
// trailing white space and/or syntax processing to be done.
|
||||
@@ -1163,14 +1166,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
||||
}
|
||||
}
|
||||
|
||||
decor_providers_invoke_line(wp, lnum - 1); // may invalidate wp->w_virtcol
|
||||
validate_virtcol(wp);
|
||||
|
||||
has_decor = decor_redraw_line(wp, lnum - 1, &decor_state);
|
||||
|
||||
if (has_decor) {
|
||||
extra_check = true;
|
||||
}
|
||||
check_decor_providers = true;
|
||||
|
||||
// Check for columns to display for 'colorcolumn'.
|
||||
wlv.color_cols = wp->w_buffer->terminal ? NULL : wp->w_p_cc_cols;
|
||||
@@ -1466,22 +1462,22 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
||||
|
||||
// 'nowrap' or 'wrap' and a single line that doesn't fit: Advance to the
|
||||
// first character to be displayed.
|
||||
const int start_col = wp->w_p_wrap
|
||||
? (startrow == 0 ? wp->w_skipcol : 0)
|
||||
: wp->w_leftcol;
|
||||
const int start_vcol = wp->w_p_wrap
|
||||
? (startrow == 0 ? wp->w_skipcol : 0)
|
||||
: wp->w_leftcol;
|
||||
|
||||
if (has_foldtext) {
|
||||
wlv.vcol = start_col;
|
||||
} else if (start_col > 0 && col_rows == 0) {
|
||||
wlv.vcol = start_vcol;
|
||||
} else if (start_vcol > 0 && col_rows == 0) {
|
||||
char *prev_ptr = ptr;
|
||||
CharSize cs = { 0 };
|
||||
|
||||
CharsizeArg csarg;
|
||||
CSType cstype = init_charsize_arg(&csarg, wp, lnum, line);
|
||||
csarg.max_head_vcol = start_col;
|
||||
csarg.max_head_vcol = start_vcol;
|
||||
int vcol = wlv.vcol;
|
||||
StrCharInfo ci = utf_ptr2StrCharInfo(ptr);
|
||||
while (vcol < start_col && *ci.ptr != NUL) {
|
||||
while (vcol < start_vcol && *ci.ptr != NUL) {
|
||||
cs = win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &csarg);
|
||||
vcol += cs.width;
|
||||
prev_ptr = ci.ptr;
|
||||
@@ -1518,23 +1514,23 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
||||
// - the visual mode is active, or
|
||||
// - drawing a fold
|
||||
// the end of the line may be before the start of the displayed part.
|
||||
if (wlv.vcol < start_col && (wp->w_p_cuc
|
||||
|| wlv.color_cols
|
||||
|| virtual_active(wp)
|
||||
|| (VIsual_active && wp->w_buffer == curwin->w_buffer)
|
||||
|| has_fold)) {
|
||||
wlv.vcol = start_col;
|
||||
if (wlv.vcol < start_vcol && (wp->w_p_cuc
|
||||
|| wlv.color_cols
|
||||
|| virtual_active(wp)
|
||||
|| (VIsual_active && wp->w_buffer == curwin->w_buffer)
|
||||
|| has_fold)) {
|
||||
wlv.vcol = start_vcol;
|
||||
}
|
||||
|
||||
// Handle a character that's not completely on the screen: Put ptr at
|
||||
// that character but skip the first few screen characters.
|
||||
if (wlv.vcol > start_col) {
|
||||
if (wlv.vcol > start_vcol) {
|
||||
wlv.vcol -= charsize;
|
||||
ptr = prev_ptr;
|
||||
}
|
||||
|
||||
if (start_col > wlv.vcol) {
|
||||
wlv.skip_cells = start_col - wlv.vcol - head;
|
||||
if (start_vcol > wlv.vcol) {
|
||||
wlv.skip_cells = start_vcol - wlv.vcol - head;
|
||||
}
|
||||
|
||||
// Adjust for when the inverted text is before the screen,
|
||||
@@ -1588,6 +1584,23 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
||||
}
|
||||
}
|
||||
|
||||
if (check_decor_providers) {
|
||||
int const col = (int)(ptr - line);
|
||||
decor_provider_end_col = decor_providers_setup(endrow - startrow,
|
||||
start_vcol == 0,
|
||||
lnum,
|
||||
col,
|
||||
wp);
|
||||
line = ml_get_buf(wp->w_buffer, lnum);
|
||||
ptr = line + col;
|
||||
}
|
||||
|
||||
decor_redraw_line(wp, lnum - 1, &decor_state);
|
||||
if (!has_decor && decor_has_more_decorations(&decor_state, lnum - 1)) {
|
||||
has_decor = true;
|
||||
extra_check = true;
|
||||
}
|
||||
|
||||
// Correct highlighting for cursor that can't be disabled.
|
||||
// Avoids having to check this for each character.
|
||||
if (wlv.fromcol >= 0) {
|
||||
@@ -1642,6 +1655,18 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
||||
|
||||
bool did_decrement_ptr = false;
|
||||
|
||||
// Get next chunk of extmark highlights if previous approximation was smaller than needed.
|
||||
if (check_decor_providers && (int)(ptr - line) >= decor_provider_end_col) {
|
||||
int const col = (int)(ptr - line);
|
||||
decor_provider_end_col = invoke_range_next(wp, lnum, col, 100);
|
||||
line = ml_get_buf(wp->w_buffer, lnum);
|
||||
ptr = line + col;
|
||||
if (!has_decor && decor_has_more_decorations(&decor_state, lnum - 1)) {
|
||||
has_decor = true;
|
||||
extra_check = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip this quickly when working on the text.
|
||||
if (draw_cols) {
|
||||
if (cul_screenline) {
|
||||
@@ -2740,7 +2765,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
||||
// Highlight 'cursorcolumn' & 'colorcolumn' past end of the line.
|
||||
|
||||
// check if line ends before left margin
|
||||
wlv.vcol = MAX(wlv.vcol, start_col + wlv.col - win_col_off(wp));
|
||||
wlv.vcol = MAX(wlv.vcol, start_vcol + wlv.col - win_col_off(wp));
|
||||
// Get rid of the boguscols now, we want to draw until the right
|
||||
// edge for 'cursorcolumn'.
|
||||
wlv.col -= wlv.boguscols;
|
||||
@@ -2762,7 +2787,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, b
|
||||
|
||||
if (((wp->w_p_cuc
|
||||
&& wp->w_virtcol >= vcol_hlc(wlv) - eol_hl_off
|
||||
&& wp->w_virtcol < view_width * (ptrdiff_t)(wlv.row - startrow + 1) + start_col
|
||||
&& wp->w_virtcol < view_width * (ptrdiff_t)(wlv.row - startrow + 1) + start_vcol
|
||||
&& lnum != wp->w_cursor.lnum)
|
||||
|| wlv.color_cols || wlv.line_attr_lowprio || wlv.line_attr
|
||||
|| wlv.diff_hlf != 0 || wp->w_buffer->terminal)) {
|
||||
@@ -3180,3 +3205,50 @@ static void wlv_put_linebuf(win_T *wp, const winlinevars_T *wlv, int endcol, boo
|
||||
ScreenGrid *g = grid_adjust(grid, &row, &coloff);
|
||||
grid_put_linebuf(g, row, coloff, startcol, endcol, clear_width, bg_attr, 0, wlv->vcol - 1, flags);
|
||||
}
|
||||
|
||||
static int decor_providers_setup(int rows_to_draw, bool draw_from_line_start, linenr_T lnum,
|
||||
colnr_T col, win_T *wp)
|
||||
{
|
||||
// Approximate the number of bytes that will be drawn.
|
||||
// Assume we're dealing with 1-cell ascii and ignore
|
||||
// the effects of 'linebreak', 'breakindent', etc.
|
||||
int rem_vcols;
|
||||
if (wp->w_p_wrap) {
|
||||
int width = wp->w_view_width - win_col_off(wp);
|
||||
int width2 = width + win_col_off2(wp);
|
||||
|
||||
int first_row_width = draw_from_line_start ? width : width2;
|
||||
rem_vcols = first_row_width + (rows_to_draw - 1) * width2;
|
||||
} else {
|
||||
rem_vcols = wp->w_view_height - win_col_off(wp);
|
||||
}
|
||||
|
||||
// Call it here since we need to invalidate the line pointer anyway.
|
||||
decor_providers_invoke_line(wp, lnum - 1);
|
||||
validate_virtcol(wp);
|
||||
|
||||
return invoke_range_next(wp, lnum, col, rem_vcols + 1);
|
||||
}
|
||||
|
||||
/// @return New begin column, or INT_MAX.
|
||||
static int invoke_range_next(win_T *wp, int lnum, colnr_T begin_col, colnr_T col_off)
|
||||
{
|
||||
char const *const line = ml_get_buf(wp->w_buffer, lnum);
|
||||
int const line_len = ml_get_buf_len(wp->w_buffer, lnum);
|
||||
col_off = MAX(col_off, 1);
|
||||
|
||||
colnr_T new_col;
|
||||
if (col_off <= line_len - begin_col) {
|
||||
int end_col = begin_col + col_off;
|
||||
end_col += mb_off_next(line, line + end_col);
|
||||
decor_providers_invoke_range(wp, lnum - 1, begin_col, lnum - 1, end_col);
|
||||
validate_virtcol(wp);
|
||||
new_col = end_col;
|
||||
} else {
|
||||
decor_providers_invoke_range(wp, lnum - 1, begin_col, lnum, 0);
|
||||
validate_virtcol(wp);
|
||||
new_col = INT_MAX;
|
||||
}
|
||||
|
||||
return new_col;
|
||||
}
|
||||
|
@@ -2854,6 +2854,8 @@ static void ins_reg(void)
|
||||
vim_beep(kOptBoFlagRegister);
|
||||
need_redraw = true; // remove the '"'
|
||||
} else {
|
||||
yankreg_T *reg = get_yank_register(regname, YREG_PASTE);
|
||||
|
||||
if (literally == Ctrl_O || literally == Ctrl_P) {
|
||||
// Append the command to the redo buffer.
|
||||
AppendCharToRedobuff(Ctrl_R);
|
||||
@@ -2862,7 +2864,11 @@ static void ins_reg(void)
|
||||
|
||||
do_put(regname, NULL, BACKWARD, 1,
|
||||
(literally == Ctrl_P ? PUT_FIXINDENT : 0) | PUT_CURSEND);
|
||||
} else if (insert_reg(regname, NULL, literally) == FAIL) {
|
||||
} else if (reg->y_size > 1 && is_literal_register(regname)) {
|
||||
AppendCharToRedobuff(Ctrl_R);
|
||||
AppendCharToRedobuff(regname);
|
||||
do_put(regname, NULL, BACKWARD, 1, PUT_CURSEND);
|
||||
} else if (insert_reg(regname, NULL, !!literally) == FAIL) {
|
||||
vim_beep(kOptBoFlagRegister);
|
||||
need_redraw = true; // remove the '"'
|
||||
} else if (stop_insert_mode) {
|
||||
|
@@ -7072,21 +7072,26 @@ void exec_normal_cmd(char *cmd, int remap, bool silent)
|
||||
{
|
||||
// Stuff the argument into the typeahead buffer.
|
||||
ins_typebuf(cmd, remap, 0, true, silent);
|
||||
exec_normal(false);
|
||||
exec_normal(false, false);
|
||||
}
|
||||
|
||||
/// Execute normal_cmd() until there is no typeahead left.
|
||||
///
|
||||
/// @param was_typed whether or not something was typed
|
||||
void exec_normal(bool was_typed)
|
||||
/// @param use_vpeekc true to use vpeekc() to check for available chars
|
||||
void exec_normal(bool was_typed, bool use_vpeekc)
|
||||
{
|
||||
oparg_T oa;
|
||||
int c;
|
||||
|
||||
// When calling vpeekc() from feedkeys() it will return Ctrl_C when there
|
||||
// is nothing to get, so also check for Ctrl_C.
|
||||
clear_oparg(&oa);
|
||||
finish_op = false;
|
||||
while ((!stuff_empty()
|
||||
|| ((was_typed || !typebuf_typed())
|
||||
&& typebuf.tb_len > 0))
|
||||
&& typebuf.tb_len > 0)
|
||||
|| (use_vpeekc && (c = vpeekc()) != NUL && c != Ctrl_C))
|
||||
&& !got_int) {
|
||||
update_topline_cursor();
|
||||
normal_cmd(&oa, true); // execute a Normal mode cmd
|
||||
@@ -7355,7 +7360,7 @@ ssize_t find_cmdline_var(const char *src, size_t *usedlen)
|
||||
char *eval_vars(char *src, const char *srcstart, size_t *usedlen, linenr_T *lnump,
|
||||
const char **errormsg, int *escaped, bool empty_is_error)
|
||||
{
|
||||
char *result;
|
||||
char *result = "";
|
||||
char *resultbuf = NULL;
|
||||
size_t resultlen;
|
||||
int valid = VALID_HEAD | VALID_PATH; // Assume valid result.
|
||||
@@ -7571,7 +7576,6 @@ char *eval_vars(char *src, const char *srcstart, size_t *usedlen, linenr_T *lnum
|
||||
default:
|
||||
// should not happen
|
||||
*errormsg = "";
|
||||
result = ""; // avoid gcc warning
|
||||
break;
|
||||
}
|
||||
|
||||
|
@@ -317,7 +317,7 @@ static void hash_may_resize(hashtab_T *ht, size_t minitems)
|
||||
// Use specified size.
|
||||
minitems = MAX(minitems, ht->ht_used);
|
||||
// array is up to 2/3 full
|
||||
minsize = minitems * 3 / 2;
|
||||
minsize = (minitems * 3 + 1) / 2;
|
||||
}
|
||||
|
||||
size_t newsize = HT_INIT_SIZE;
|
||||
|
@@ -17,6 +17,8 @@ EXTERN const char *hlf_names[] INIT( = {
|
||||
[HLF_TERM] = "TermCursor",
|
||||
[HLF_AT] = "NonText",
|
||||
[HLF_D] = "Directory",
|
||||
[HLF_OK] = "OkMsg",
|
||||
[HLF_W] = "WarningMsg",
|
||||
[HLF_E] = "ErrorMsg",
|
||||
[HLF_SE] = "StderrMsg",
|
||||
[HLF_SO] = "StdoutMsg",
|
||||
@@ -39,7 +41,6 @@ EXTERN const char *hlf_names[] INIT( = {
|
||||
[HLF_V] = "Visual",
|
||||
[HLF_VNC] = "VisualNC",
|
||||
[HLF_VSP] = "VertSplit",
|
||||
[HLF_W] = "WarningMsg",
|
||||
[HLF_WM] = "WildMenu",
|
||||
[HLF_FL] = "Folded",
|
||||
[HLF_FC] = "FoldColumn",
|
||||
|
@@ -132,6 +132,7 @@ typedef enum {
|
||||
HLF_TSNC, ///< status line for non-current terminal window
|
||||
HLF_SE, ///< stderr messages (from shell)
|
||||
HLF_SO, ///< stdout messages (from shell)
|
||||
HLF_OK, ///< OK message
|
||||
HLF_COUNT, ///< MUST be the last one
|
||||
} hlf_T;
|
||||
|
||||
|
@@ -375,6 +375,7 @@ static const char *highlight_init_light[] = {
|
||||
"MoreMsg guifg=NvimDarkCyan ctermfg=6",
|
||||
"NonText guifg=NvimLightGrey4",
|
||||
"NormalFloat guibg=NvimLightGrey1",
|
||||
"OkMsg guifg=NvimDarkGreen ctermfg=2",
|
||||
"Pmenu guibg=NvimLightGrey3 cterm=reverse",
|
||||
"PmenuThumb guibg=NvimLightGrey4",
|
||||
"Question guifg=NvimDarkCyan ctermfg=6",
|
||||
@@ -459,6 +460,7 @@ static const char *highlight_init_dark[] = {
|
||||
"MoreMsg guifg=NvimLightCyan ctermfg=14",
|
||||
"NonText guifg=NvimDarkGrey4",
|
||||
"NormalFloat guibg=NvimDarkGrey1",
|
||||
"OkMsg guifg=NvimLightGreen ctermfg=10",
|
||||
"Pmenu guibg=NvimDarkGrey3 cterm=reverse",
|
||||
"PmenuThumb guibg=NvimDarkGrey4",
|
||||
"Question guifg=NvimLightCyan ctermfg=14",
|
||||
|
@@ -175,11 +175,18 @@ int nlua_pcall(lua_State *lstate, int nargs, int nresults)
|
||||
lua_getfield(lstate, -1, "traceback");
|
||||
lua_remove(lstate, -2);
|
||||
lua_insert(lstate, -2 - nargs);
|
||||
int pre_top = lua_gettop(lstate);
|
||||
int status = lua_pcall(lstate, nargs, nresults, -2 - nargs);
|
||||
if (status) {
|
||||
lua_remove(lstate, -2);
|
||||
} else {
|
||||
lua_remove(lstate, -1 - nresults);
|
||||
if (nresults == LUA_MULTRET) {
|
||||
int new_top = lua_gettop(lstate);
|
||||
int actual_nres = new_top - pre_top + nargs + 1;
|
||||
lua_remove(lstate, -1 - actual_nres);
|
||||
} else {
|
||||
lua_remove(lstate, -1 - nresults);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -415,16 +422,28 @@ static void dummy_timer_close_cb(TimeWatcher *tw, void *data)
|
||||
xfree(tw);
|
||||
}
|
||||
|
||||
static bool nlua_wait_condition(lua_State *lstate, int *status, bool *callback_result)
|
||||
static bool nlua_wait_condition(lua_State *lstate, int *status, bool *callback_result,
|
||||
int *nresults)
|
||||
{
|
||||
int top = lua_gettop(lstate);
|
||||
lua_pushvalue(lstate, 2);
|
||||
*status = nlua_pcall(lstate, 0, 1);
|
||||
*status = nlua_pcall(lstate, 0, LUA_MULTRET);
|
||||
if (*status) {
|
||||
return true; // break on error, but keep error on stack
|
||||
}
|
||||
*callback_result = lua_toboolean(lstate, -1);
|
||||
lua_pop(lstate, 1);
|
||||
return *callback_result; // break if true
|
||||
*nresults = lua_gettop(lstate) - top;
|
||||
if (*nresults == 0) {
|
||||
*callback_result = false;
|
||||
return false;
|
||||
}
|
||||
*callback_result = lua_toboolean(lstate, top + 1);
|
||||
if (!*callback_result) {
|
||||
lua_settop(lstate, top);
|
||||
return false;
|
||||
}
|
||||
lua_remove(lstate, top + 1);
|
||||
(*nresults)--;
|
||||
return true; // break if true
|
||||
}
|
||||
|
||||
/// "vim.wait(timeout, condition[, interval])" function
|
||||
@@ -454,8 +473,7 @@ static int nlua_wait(lua_State *lstate)
|
||||
}
|
||||
|
||||
if (!is_function) {
|
||||
lua_pushliteral(lstate,
|
||||
"vim.wait: if passed, condition must be a function");
|
||||
lua_pushliteral(lstate, "vim.wait: callback must be callable");
|
||||
return lua_error(lstate);
|
||||
}
|
||||
}
|
||||
@@ -488,6 +506,7 @@ static int nlua_wait(lua_State *lstate)
|
||||
|
||||
int pcall_status = 0;
|
||||
bool callback_result = false;
|
||||
int nresults = 0;
|
||||
|
||||
// Flush screen updates before blocking.
|
||||
ui_flush();
|
||||
@@ -497,7 +516,8 @@ static int nlua_wait(lua_State *lstate)
|
||||
(int)timeout,
|
||||
got_int || (is_function ? nlua_wait_condition(lstate,
|
||||
&pcall_status,
|
||||
&callback_result)
|
||||
&callback_result,
|
||||
&nresults)
|
||||
: false));
|
||||
|
||||
// Stop dummy timer
|
||||
@@ -508,18 +528,26 @@ static int nlua_wait(lua_State *lstate)
|
||||
return lua_error(lstate);
|
||||
} else if (callback_result) {
|
||||
lua_pushboolean(lstate, 1);
|
||||
lua_pushnil(lstate);
|
||||
if (nresults == 0) {
|
||||
lua_pushnil(lstate);
|
||||
nresults = 1;
|
||||
} else {
|
||||
lua_insert(lstate, -1 - nresults);
|
||||
}
|
||||
return nresults + 1;
|
||||
} else if (got_int) {
|
||||
got_int = false;
|
||||
vgetc();
|
||||
lua_pushboolean(lstate, 0);
|
||||
lua_pushinteger(lstate, -2);
|
||||
return 2;
|
||||
} else {
|
||||
lua_pushboolean(lstate, 0);
|
||||
lua_pushinteger(lstate, -1);
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 2;
|
||||
abort();
|
||||
}
|
||||
|
||||
static nlua_ref_state_t *nlua_new_ref_state(lua_State *lstate, bool is_thread)
|
||||
@@ -966,7 +994,8 @@ static void nlua_print_event(void **argv)
|
||||
HlMessage msg = KV_INITIAL_VALUE;
|
||||
HlMessageChunk chunk = { { .data = argv[0], .size = (size_t)(intptr_t)argv[1] - 1 }, 0 };
|
||||
kv_push(msg, chunk);
|
||||
msg_multihl(INTEGER_OBJ(0), msg, "lua_print", true, false, NULL);
|
||||
bool needs_clear = false;
|
||||
msg_multihl(INTEGER_OBJ(0), msg, "lua_print", true, false, NULL, &needs_clear);
|
||||
}
|
||||
|
||||
/// Print as a Vim message
|
||||
@@ -1496,7 +1525,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;
|
||||
}
|
||||
|
||||
|
@@ -1361,34 +1361,46 @@ static int tslua_push_querycursor(lua_State *L)
|
||||
|
||||
TSQuery *query = query_check(L, 2);
|
||||
TSQueryCursor *cursor = ts_query_cursor_new();
|
||||
|
||||
if (lua_gettop(L) >= 3 && !lua_isnil(L, 3)) {
|
||||
luaL_argcheck(L, lua_istable(L, 3), 3, "table expected");
|
||||
}
|
||||
|
||||
lua_getfield(L, 3, "start_row");
|
||||
uint32_t start_row = (uint32_t)luaL_checkinteger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, 3, "start_col");
|
||||
uint32_t start_col = (uint32_t)luaL_checkinteger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, 3, "end_row");
|
||||
uint32_t end_row = (uint32_t)luaL_checkinteger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, 3, "end_col");
|
||||
uint32_t end_col = (uint32_t)luaL_checkinteger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
ts_query_cursor_set_point_range(cursor, (TSPoint){ start_row, start_col },
|
||||
(TSPoint){ end_row, end_col });
|
||||
|
||||
lua_getfield(L, 3, "max_start_depth");
|
||||
if (!lua_isnil(L, -1)) {
|
||||
uint32_t max_start_depth = (uint32_t)luaL_checkinteger(L, -1);
|
||||
ts_query_cursor_set_max_start_depth(cursor, max_start_depth);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, 3, "match_limit");
|
||||
if (!lua_isnil(L, -1)) {
|
||||
uint32_t match_limit = (uint32_t)luaL_checkinteger(L, -1);
|
||||
ts_query_cursor_set_match_limit(cursor, match_limit);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
ts_query_cursor_exec(cursor, query, node);
|
||||
|
||||
if (lua_gettop(L) >= 3) {
|
||||
uint32_t start = (uint32_t)luaL_checkinteger(L, 3);
|
||||
uint32_t end = lua_gettop(L) >= 4 ? (uint32_t)luaL_checkinteger(L, 4) : MAXLNUM;
|
||||
ts_query_cursor_set_point_range(cursor, (TSPoint){ start, 0 }, (TSPoint){ end, 0 });
|
||||
}
|
||||
|
||||
if (lua_gettop(L) >= 5 && !lua_isnil(L, 5)) {
|
||||
luaL_argcheck(L, lua_istable(L, 5), 5, "table expected");
|
||||
lua_pushnil(L); // [dict, ..., nil]
|
||||
while (lua_next(L, 5)) {
|
||||
// [dict, ..., key, value]
|
||||
if (lua_type(L, -2) == LUA_TSTRING) {
|
||||
char *k = (char *)lua_tostring(L, -2);
|
||||
if (strequal("max_start_depth", k)) {
|
||||
uint32_t max_start_depth = (uint32_t)lua_tointeger(L, -1);
|
||||
ts_query_cursor_set_max_start_depth(cursor, max_start_depth);
|
||||
} else if (strequal("match_limit", k)) {
|
||||
uint32_t match_limit = (uint32_t)lua_tointeger(L, -1);
|
||||
ts_query_cursor_set_match_limit(cursor, match_limit);
|
||||
}
|
||||
}
|
||||
// pop the value; lua_next will pop the key.
|
||||
lua_pop(L, 1); // [dict, ..., key]
|
||||
}
|
||||
}
|
||||
|
||||
TSQueryCursor **ud = lua_newuserdata(L, sizeof(*ud)); // [node, query, ..., udata]
|
||||
*ud = cursor;
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, TS_META_QUERYCURSOR); // [node, query, ..., udata, meta]
|
||||
|
@@ -188,6 +188,7 @@ static bool event_teardown(void)
|
||||
/// Needed for unit tests.
|
||||
void early_init(mparm_T *paramp)
|
||||
{
|
||||
os_hint_priority();
|
||||
estack_init();
|
||||
cmdline_init();
|
||||
eval_init(); // init global variables
|
||||
|
@@ -863,7 +863,7 @@ void free_all_mem(void)
|
||||
|
||||
// Close all tabs and windows. Reset 'equalalways' to avoid redraws.
|
||||
p_ea = false;
|
||||
if (first_tabpage->tp_next != NULL) {
|
||||
if (first_tabpage != NULL && first_tabpage->tp_next != NULL) {
|
||||
do_cmdline_cmd("tabonly!");
|
||||
}
|
||||
|
||||
@@ -873,18 +873,20 @@ void free_all_mem(void)
|
||||
// Clear user commands (before deleting buffers).
|
||||
ex_comclear(NULL);
|
||||
|
||||
// Clear menus.
|
||||
do_cmdline_cmd("aunmenu *");
|
||||
do_cmdline_cmd("tlunmenu *");
|
||||
do_cmdline_cmd("menutranslate clear");
|
||||
if (curbuf != NULL) {
|
||||
// Clear menus.
|
||||
do_cmdline_cmd("aunmenu *");
|
||||
do_cmdline_cmd("tlunmenu *");
|
||||
do_cmdline_cmd("menutranslate clear");
|
||||
|
||||
// Clear mappings, abbreviations, breakpoints.
|
||||
// NB: curbuf not used with local=false arg
|
||||
map_clear_mode(curbuf, MAP_ALL_MODES, false, false);
|
||||
map_clear_mode(curbuf, MAP_ALL_MODES, false, true);
|
||||
do_cmdline_cmd("breakdel *");
|
||||
do_cmdline_cmd("profdel *");
|
||||
do_cmdline_cmd("set keymap=");
|
||||
// Clear mappings, abbreviations, breakpoints.
|
||||
// NB: curbuf not used with local=false arg
|
||||
map_clear_mode(curbuf, MAP_ALL_MODES, false, false);
|
||||
map_clear_mode(curbuf, MAP_ALL_MODES, false, true);
|
||||
do_cmdline_cmd("breakdel *");
|
||||
do_cmdline_cmd("profdel *");
|
||||
do_cmdline_cmd("set keymap=");
|
||||
}
|
||||
|
||||
free_titles();
|
||||
free_findfile();
|
||||
@@ -905,7 +907,9 @@ void free_all_mem(void)
|
||||
free_cd_dir();
|
||||
free_signs();
|
||||
set_expr_line(NULL);
|
||||
diff_clear(curtab);
|
||||
if (curtab != NULL) {
|
||||
diff_clear(curtab);
|
||||
}
|
||||
clear_sb_text(true); // free any scrollback text
|
||||
|
||||
// Free some global vars.
|
||||
@@ -922,8 +926,10 @@ void free_all_mem(void)
|
||||
// Close all script inputs.
|
||||
close_all_scripts();
|
||||
|
||||
// Destroy all windows. Must come before freeing buffers.
|
||||
win_free_all();
|
||||
if (curwin != NULL) {
|
||||
// Destroy all windows. Must come before freeing buffers.
|
||||
win_free_all();
|
||||
}
|
||||
|
||||
// Free all option values. Must come after closing windows.
|
||||
free_all_options();
|
||||
@@ -957,8 +963,10 @@ void free_all_mem(void)
|
||||
|
||||
reset_last_sourcing();
|
||||
|
||||
free_tabpage(first_tabpage);
|
||||
first_tabpage = NULL;
|
||||
if (first_tabpage != NULL) {
|
||||
free_tabpage(first_tabpage);
|
||||
first_tabpage = NULL;
|
||||
}
|
||||
|
||||
// message history
|
||||
msg_hist_clear(0);
|
||||
|
@@ -152,7 +152,6 @@ bool keep_msg_more = false; // keep_msg was set by msgmore()
|
||||
// Extended msg state, currently used for external UIs with ext_messages
|
||||
static const char *msg_ext_kind = NULL;
|
||||
static MsgID msg_ext_id = { .type = kObjectTypeInteger, .data.integer = 0 };
|
||||
static DictOf(Object) msg_ext_progress = ARRAY_DICT_INIT;
|
||||
static Array *msg_ext_chunks = NULL;
|
||||
static garray_T msg_ext_last_chunk = GA_INIT(sizeof(char), 40);
|
||||
static sattr_T msg_ext_last_attr = -1;
|
||||
@@ -293,19 +292,67 @@ void msg_multiline(String str, int hl_id, bool check_int, bool hist, bool *need_
|
||||
// Avoid starting a new message for each chunk and adding message to history in msg_keep().
|
||||
static bool is_multihl = false;
|
||||
|
||||
/// Format a progress message, adding title and percent if given.
|
||||
///
|
||||
/// @param hl_msg Message chunks
|
||||
/// @param msg_data Additional data for progress messages
|
||||
static HlMessage format_progress_message(HlMessage hl_msg, MessageData *msg_data)
|
||||
{
|
||||
HlMessage updated_msg = KV_INITIAL_VALUE;
|
||||
// progress messages are special. displayed as "title: percent% msg"
|
||||
if (msg_data->title.size != 0) {
|
||||
// this block draws the "title:" before the progress-message
|
||||
int hl_id = 0;
|
||||
if (msg_data->status.data == NULL) {
|
||||
hl_id = 0;
|
||||
} else if (strequal(msg_data->status.data, "success")) {
|
||||
hl_id = syn_check_group("OkMsg", STRLEN_LITERAL("OkMsg"));
|
||||
} else if (strequal(msg_data->status.data, "failed")) {
|
||||
hl_id = syn_check_group("ErrorMsg", STRLEN_LITERAL("ErrorMsg"));
|
||||
} else if (strequal(msg_data->status.data, "running")) {
|
||||
hl_id = syn_check_group("MoreMsg", STRLEN_LITERAL("MoreMsg"));
|
||||
} else if (strequal(msg_data->status.data, "cancel")) {
|
||||
hl_id = syn_check_group("WarningMsg", STRLEN_LITERAL("WarningMsg"));
|
||||
}
|
||||
kv_push(updated_msg,
|
||||
((HlMessageChunk){ .text = copy_string(msg_data->title, NULL), .hl_id = hl_id }));
|
||||
kv_push(updated_msg, ((HlMessageChunk){ .text = cstr_to_string(": "), .hl_id = 0 }));
|
||||
}
|
||||
if (msg_data->percent > 0) {
|
||||
char percent_buf[10];
|
||||
vim_snprintf(percent_buf, sizeof(percent_buf), "%3ld%% ", (long)msg_data->percent);
|
||||
String percent = cstr_to_string(percent_buf);
|
||||
int hl_id = syn_check_group("WarningMsg", STRLEN_LITERAL("WarningMsg"));
|
||||
kv_push(updated_msg, ((HlMessageChunk){ .text = percent, .hl_id = hl_id }));
|
||||
}
|
||||
|
||||
if (kv_size(updated_msg) != 0) {
|
||||
for (uint32_t i = 0; i < kv_size(hl_msg); i++) {
|
||||
kv_push(updated_msg,
|
||||
((HlMessageChunk){ .text = copy_string(kv_A(hl_msg, i).text, NULL),
|
||||
.hl_id = kv_A(hl_msg, i).hl_id }));
|
||||
}
|
||||
return updated_msg;
|
||||
} else {
|
||||
return hl_msg;
|
||||
}
|
||||
}
|
||||
|
||||
/// Print message chunks, each with their own highlight ID.
|
||||
///
|
||||
/// @param hl_msg Message chunks
|
||||
/// @param kind Message kind (can be NULL to avoid setting kind)
|
||||
/// @param history Whether to add message to history
|
||||
/// @param err Whether to print message as an error
|
||||
/// @param msg_data Progress-message data
|
||||
MsgID msg_multihl(MsgID id, HlMessage hl_msg, const char *kind, bool history, bool err,
|
||||
MessageData *msg_data)
|
||||
MessageData *msg_data, bool *needs_msg_clear)
|
||||
{
|
||||
no_wait_return++;
|
||||
msg_start();
|
||||
msg_clr_eos();
|
||||
bool need_clear = false;
|
||||
bool hl_msg_updated = false;
|
||||
msg_ext_history = history;
|
||||
if (kind != NULL) {
|
||||
msg_ext_set_kind(kind);
|
||||
@@ -322,6 +369,17 @@ MsgID msg_multihl(MsgID id, HlMessage hl_msg, const char *kind, bool history, bo
|
||||
msg_id_next = id.data.integer + 1;
|
||||
}
|
||||
}
|
||||
msg_ext_id = id;
|
||||
|
||||
// progress message are special displayed as "title: percent% msg"
|
||||
if (strequal(kind, "progress") && msg_data) {
|
||||
HlMessage formated_message = format_progress_message(hl_msg, msg_data);
|
||||
if (formated_message.items != hl_msg.items) {
|
||||
*needs_msg_clear = true;
|
||||
hl_msg_updated = true;
|
||||
hl_msg = formated_message;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < kv_size(hl_msg); i++) {
|
||||
HlMessageChunk chunk = kv_A(hl_msg, i);
|
||||
@@ -332,6 +390,7 @@ MsgID msg_multihl(MsgID id, HlMessage hl_msg, const char *kind, bool history, bo
|
||||
}
|
||||
assert(!ui_has(kUIMessages) || kind == NULL || msg_ext_kind == kind);
|
||||
}
|
||||
|
||||
if (history && kv_size(hl_msg)) {
|
||||
msg_hist_add_multihl(id, hl_msg, false, msg_data);
|
||||
}
|
||||
@@ -340,6 +399,10 @@ MsgID msg_multihl(MsgID id, HlMessage hl_msg, const char *kind, bool history, bo
|
||||
is_multihl = false;
|
||||
no_wait_return--;
|
||||
msg_end();
|
||||
|
||||
if (hl_msg_updated && !(history && kv_size(hl_msg))) {
|
||||
hl_msg_free(hl_msg);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -1105,19 +1168,6 @@ static void msg_hist_add_multihl(MsgID msg_id, HlMessage msg, bool temp, Message
|
||||
msg_hist_last = entry;
|
||||
msg_ext_history = true;
|
||||
|
||||
msg_ext_id = msg_id;
|
||||
if (strequal(msg_ext_kind, "progress") && msg_data != NULL && ui_has(kUIMessages)) {
|
||||
kv_resize(msg_ext_progress, 3);
|
||||
if (msg_data->title.size != 0) {
|
||||
PUT_C(msg_ext_progress, "title", STRING_OBJ(msg_data->title));
|
||||
}
|
||||
if (msg_data->status.size != 0) {
|
||||
PUT_C(msg_ext_progress, "status", STRING_OBJ(msg_data->status));
|
||||
}
|
||||
if (msg_data->percent >= 0) {
|
||||
PUT_C(msg_ext_progress, "percent", INTEGER_OBJ(msg_data->percent));
|
||||
}
|
||||
}
|
||||
msg_hist_clear(msg_hist_max);
|
||||
}
|
||||
|
||||
@@ -1262,7 +1312,8 @@ void ex_messages(exarg_T *eap)
|
||||
}
|
||||
if (redirecting() || !ui_has(kUIMessages)) {
|
||||
msg_silent += ui_has(kUIMessages);
|
||||
msg_multihl(INTEGER_OBJ(0), p->msg, p->kind, false, false, NULL);
|
||||
bool needs_clear = false;
|
||||
msg_multihl(INTEGER_OBJ(0), p->msg, p->kind, false, false, NULL, &needs_clear);
|
||||
msg_silent -= ui_has(kUIMessages);
|
||||
}
|
||||
}
|
||||
@@ -2210,8 +2261,7 @@ void msg_puts_len(const char *const str, const ptrdiff_t len, int hl_id, bool hi
|
||||
if (msg_silent != 0 || *str == NUL) {
|
||||
if (*str == NUL && ui_has(kUIMessages)) {
|
||||
ui_call_msg_show(cstr_as_string("empty"), (Array)ARRAY_DICT_INIT, false, false, false,
|
||||
INTEGER_OBJ(-1),
|
||||
(Dict)ARRAY_DICT_INIT);
|
||||
INTEGER_OBJ(-1));
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -3239,7 +3289,7 @@ void msg_ext_ui_flush(void)
|
||||
Array *tofree = msg_ext_init_chunks();
|
||||
|
||||
ui_call_msg_show(cstr_as_string(msg_ext_kind), *tofree, msg_ext_overwrite, msg_ext_history,
|
||||
msg_ext_append, msg_ext_id, msg_ext_progress);
|
||||
msg_ext_append, msg_ext_id);
|
||||
// clear info after emiting message.
|
||||
if (msg_ext_history) {
|
||||
api_free_array(*tofree);
|
||||
@@ -3260,7 +3310,6 @@ void msg_ext_ui_flush(void)
|
||||
msg_ext_append = false;
|
||||
msg_ext_kind = NULL;
|
||||
msg_ext_id = INTEGER_OBJ(0);
|
||||
kv_destroy(msg_ext_progress);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -99,7 +99,7 @@ static const char e_search_pattern_and_expression_register_may_not_contain_two_o
|
||||
/// The names of operators.
|
||||
/// IMPORTANT: Index must correspond with defines in ops.h!!!
|
||||
/// The third field indicates whether the operator always works on lines.
|
||||
static char opchars[][3] = {
|
||||
static const char opchars[][3] = {
|
||||
{ NUL, NUL, 0 }, // OP_NOP
|
||||
{ 'd', NUL, OPF_CHANGE }, // OP_DELETE
|
||||
{ 'y', NUL, 0 }, // OP_YANK
|
||||
|
@@ -152,7 +152,7 @@ static inline int op_reg_index(const int regname)
|
||||
static inline bool is_literal_register(const int regname)
|
||||
FUNC_ATTR_CONST
|
||||
{
|
||||
return regname == '*' || regname == '+';
|
||||
return regname == '*' || regname == '+' || ASCII_ISALNUM(regname);
|
||||
}
|
||||
|
||||
EXTERN LuaRef repeat_luaref INIT( = LUA_NOREF); ///< LuaRef for "."
|
||||
|
@@ -39,6 +39,10 @@
|
||||
# include "nvim/fileio.h"
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include <mach/task.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE__NSGETENVIRON
|
||||
# include <crt_externs.h>
|
||||
#endif
|
||||
@@ -367,6 +371,18 @@ int64_t os_get_pid(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Signals to the OS that Nvim is an application for "interactive work"
|
||||
/// which should be prioritized similar to a GUI app.
|
||||
void os_hint_priority(void)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
// By default, processes have the TASK_UNSPECIFIED "role", which means all of its threads are
|
||||
// clamped to Default QoS. Setting the role to TASK_DEFAULT_APPLICATION removes this clamp.
|
||||
integer_t policy = TASK_DEFAULT_APPLICATION;
|
||||
task_policy_set(mach_task_self(), TASK_CATEGORY_POLICY, &policy, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Gets the hostname of the current machine.
|
||||
///
|
||||
/// @param hostname Buffer to store the hostname.
|
||||
|
@@ -2453,7 +2453,9 @@ static bool found_tagfile_cb(int num_fnames, char **fnames, bool all, void *cook
|
||||
void free_tag_stuff(void)
|
||||
{
|
||||
ga_clear_strings(&tag_fnames);
|
||||
do_tag(NULL, DT_FREE, 0, 0, 0);
|
||||
if (curwin != NULL) {
|
||||
do_tag(NULL, DT_FREE, 0, 0, 0);
|
||||
}
|
||||
tag_freematch();
|
||||
|
||||
tagstack_clear_entry(&ptag_entry);
|
||||
|
@@ -335,7 +335,8 @@ ScreenGrid *ui_comp_get_grid_at_coord(int row, int col)
|
||||
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
|
||||
ScreenGrid *grid = &wp->w_grid_alloc;
|
||||
if (row >= grid->comp_row && row < grid->comp_row + grid->rows
|
||||
&& col >= grid->comp_col && col < grid->comp_col + grid->cols) {
|
||||
&& col >= grid->comp_col && col < grid->comp_col + grid->cols
|
||||
&& !wp->w_config.hide) {
|
||||
return grid;
|
||||
}
|
||||
}
|
||||
|
@@ -1053,7 +1053,9 @@ theend:
|
||||
void ex_comclear(exarg_T *eap)
|
||||
{
|
||||
uc_clear(&ucmds);
|
||||
uc_clear(&curbuf->b_ucmds);
|
||||
if (curbuf != NULL) {
|
||||
uc_clear(&curbuf->b_ucmds);
|
||||
}
|
||||
}
|
||||
|
||||
void free_ucmd(ucmd_T *cmd)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user