Regenerate nvim config
This commit is contained in:
89
config/neovim/store/lazy-plugins/flash.nvim/.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
89
config/neovim/store/lazy-plugins/flash.nvim/.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
name: Bug Report
|
||||
description: File a bug/issue
|
||||
title: "bug: "
|
||||
labels: [bug]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**Before** reporting an issue, make sure to read the [documentation](https://github.com/folke/flash.nvim) and search [existing issues](https://github.com/folke/flash.nvim/issues). Usage questions such as ***"How do I...?"*** belong in [Discussions](https://github.com/folke/flash.nvim/discussions) and will be closed.
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Did you check docs and existing issues?
|
||||
description: Make sure you checked all of the below before submitting an issue
|
||||
options:
|
||||
- label: I have read all the flash.nvim docs
|
||||
required: true
|
||||
- label: I have searched the existing issues of flash.nvim
|
||||
required: true
|
||||
- label: I have searched the existing issues of plugins related to this issue
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: "Neovim version (nvim -v)"
|
||||
placeholder: "0.8.0 commit db1b0ee3b30f"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: "Operating system/version"
|
||||
placeholder: "MacOS 11.5"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: A clear and concise description of what the bug is. Please include any related errors you see in Neovim.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps To Reproduce
|
||||
description: Steps to reproduce the behavior.
|
||||
placeholder: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: A concise description of what you expected to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Repro
|
||||
description: Minimal `init.lua` to reproduce this issue. Save as `repro.lua` and run with `nvim -u repro.lua`
|
||||
value: |
|
||||
-- DO NOT change the paths and don't remove the colorscheme
|
||||
local root = vim.fn.fnamemodify("./.repro", ":p")
|
||||
|
||||
-- set stdpaths to use .repro
|
||||
for _, name in ipairs({ "config", "data", "state", "cache" }) do
|
||||
vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name
|
||||
end
|
||||
|
||||
-- bootstrap lazy
|
||||
local lazypath = root .. "/plugins/lazy.nvim"
|
||||
if not vim.loop.fs_stat(lazypath) then
|
||||
vim.fn.system({ "git", "clone", "--filter=blob:none", "https://github.com/folke/lazy.nvim.git", lazypath, })
|
||||
end
|
||||
vim.opt.runtimepath:prepend(lazypath)
|
||||
|
||||
-- install plugins
|
||||
local plugins = {
|
||||
"folke/tokyonight.nvim",
|
||||
{ "folke/flash.nvim", opts = {} },
|
||||
-- add any other plugins here
|
||||
}
|
||||
require("lazy").setup(plugins, {
|
||||
root = root .. "/plugins",
|
||||
})
|
||||
|
||||
vim.cmd.colorscheme("tokyonight")
|
||||
-- add anything else here
|
||||
render: Lua
|
||||
validations:
|
||||
required: false
|
||||
36
config/neovim/store/lazy-plugins/flash.nvim/.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
36
config/neovim/store/lazy-plugins/flash.nvim/.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
name: Feature Request
|
||||
description: Suggest a new feature
|
||||
title: "feature: "
|
||||
labels: [enhancement]
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Did you check the docs?
|
||||
description: Make sure you read all the docs before submitting a feature request
|
||||
options:
|
||||
- label: I have read all the flash.nvim docs
|
||||
required: true
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Is your feature request related to a problem? Please describe.
|
||||
description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Describe the solution you'd like
|
||||
description: A clear and concise description of what you want to happen.
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Describe alternatives you've considered
|
||||
description: A clear and concise description of any alternative solutions or features you've considered.
|
||||
- type: textarea
|
||||
validations:
|
||||
required: false
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add any other context or screenshots about the feature request here.
|
||||
72
config/neovim/store/lazy-plugins/flash.nvim/.github/workflows/ci.yml
vendored
Normal file
72
config/neovim/store/lazy-plugins/flash.nvim/.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
strategy:
|
||||
matrix:
|
||||
# os: [ubuntu-latest, windows-latest]
|
||||
os: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install Neovim
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p /tmp/nvim
|
||||
wget -q https://github.com/neovim/neovim/releases/download/nightly/nvim.appimage -O /tmp/nvim/nvim.appimage
|
||||
cd /tmp/nvim
|
||||
chmod a+x ./nvim.appimage
|
||||
./nvim.appimage --appimage-extract
|
||||
echo "/tmp/nvim/squashfs-root/usr/bin/" >> $GITHUB_PATH
|
||||
- name: Run Tests
|
||||
run: |
|
||||
nvim --version
|
||||
[ ! -d tests ] && exit 0
|
||||
nvim --headless -u tests/init.lua -c "PlenaryBustedDirectory tests/ {minimal_init = 'tests/init.lua', sequential = true}"
|
||||
docs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: tests
|
||||
if: ${{ github.ref == 'refs/heads/main' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: panvimdoc
|
||||
uses: kdheepak/panvimdoc@main
|
||||
with:
|
||||
vimdoc: flash.nvim
|
||||
version: "Neovim >= 0.8.0"
|
||||
demojify: true
|
||||
treesitter: true
|
||||
- name: Push changes
|
||||
uses: stefanzweifel/git-auto-commit-action@v4
|
||||
with:
|
||||
commit_message: "chore(build): auto-generate vimdoc"
|
||||
commit_user_name: "github-actions[bot]"
|
||||
commit_user_email: "github-actions[bot]@users.noreply.github.com"
|
||||
commit_author: "github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
|
||||
release:
|
||||
name: release
|
||||
if: ${{ github.ref == 'refs/heads/main' }}
|
||||
needs:
|
||||
- docs
|
||||
- tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: google-github-actions/release-please-action@v3
|
||||
id: release
|
||||
with:
|
||||
release-type: simple
|
||||
package-name: flash.nvim
|
||||
- uses: actions/checkout@v3
|
||||
- name: tag stable versions
|
||||
if: ${{ steps.release.outputs.release_created }}
|
||||
run: |
|
||||
git config user.name github-actions[bot]
|
||||
git config user.email github-actions[bot]@users.noreply.github.com
|
||||
git remote add gh-token "https://${{ secrets.GITHUB_TOKEN }}@github.com/google-github-actions/release-please-action.git"
|
||||
git tag -d stable || true
|
||||
git push origin :stable || true
|
||||
git tag -a stable -m "Last Stable Release"
|
||||
git push origin stable
|
||||
8
config/neovim/store/lazy-plugins/flash.nvim/.gitignore
vendored
Normal file
8
config/neovim/store/lazy-plugins/flash.nvim/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
tt.*
|
||||
.tests
|
||||
doc/tags
|
||||
debug
|
||||
.repro
|
||||
foo.*
|
||||
*.log
|
||||
data
|
||||
10
config/neovim/store/lazy-plugins/flash.nvim/.neoconf.json
Normal file
10
config/neovim/store/lazy-plugins/flash.nvim/.neoconf.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"neodev": {
|
||||
"library": {
|
||||
"plugins": [
|
||||
"plenary.nvim",
|
||||
"lazy.nvim"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
474
config/neovim/store/lazy-plugins/flash.nvim/CHANGELOG.md
Normal file
474
config/neovim/store/lazy-plugins/flash.nvim/CHANGELOG.md
Normal file
@ -0,0 +1,474 @@
|
||||
# Changelog
|
||||
|
||||
## [1.18.3](https://github.com/folke/flash.nvim/compare/v1.18.2...v1.18.3) (2024-05-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **hacks:** use `vim.api.nvim__redraw` to fix the cursor instead of ffi. Fixes [#333](https://github.com/folke/flash.nvim/issues/333) ([1b128ff](https://github.com/folke/flash.nvim/commit/1b128ff527c3938460ef83fe6403ce6ce3f53b53))
|
||||
|
||||
## [1.18.2](https://github.com/folke/flash.nvim/compare/v1.18.1...v1.18.2) (2023-10-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **treesitter:** show warning when treesitter not available. Fixes [#261](https://github.com/folke/flash.nvim/issues/261) ([77c66d8](https://github.com/folke/flash.nvim/commit/77c66d84be3e2a2ef2e6689de668fe156af74498))
|
||||
|
||||
## [1.18.1](https://github.com/folke/flash.nvim/compare/v1.18.0...v1.18.1) (2023-10-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **char:** allow setting autohide=true for char mode. Fixes [#231](https://github.com/folke/flash.nvim/issues/231) ([71040c8](https://github.com/folke/flash.nvim/commit/71040c87bd64d2719727006f51f8679352eb6146))
|
||||
* **jump:** send `esc` when cancelling flash. Fixes [#212](https://github.com/folke/flash.nvim/issues/212). Fixes [#233](https://github.com/folke/flash.nvim/issues/233) ([677eb59](https://github.com/folke/flash.nvim/commit/677eb59f0a94ed3b735168d9e6738723fd44796d))
|
||||
* **treesitter:** include treesitter injections. Fixes [#242](https://github.com/folke/flash.nvim/issues/242) ([5fe47ba](https://github.com/folke/flash.nvim/commit/5fe47baf1be05ea34abb6912ed89a5a17cbf5661))
|
||||
* **treesitter:** keep treesitter sorting when doing ;,. Fixes [#219](https://github.com/folke/flash.nvim/issues/219) ([aae8352](https://github.com/folke/flash.nvim/commit/aae83521091fac904b8584bb2dffe13420b7adc7))
|
||||
|
||||
## [1.18.0](https://github.com/folke/flash.nvim/compare/v1.17.3...v1.18.0) (2023-10-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **char:** allow disabling clever-f motions. Fixes [#245](https://github.com/folke/flash.nvim/issues/245) ([bc1f49f](https://github.com/folke/flash.nvim/commit/bc1f49f428655b645948a3489bf0efcded6f46e6))
|
||||
* enable multi window in vscode ([#230](https://github.com/folke/flash.nvim/issues/230)) ([65bd3ee](https://github.com/folke/flash.nvim/commit/65bd3ee715229fecdb5a9727e8dcd099c187622b))
|
||||
* **highlight:** allow overriding flash cursor hl. Fixes [#228](https://github.com/folke/flash.nvim/issues/228) ([79d67c6](https://github.com/folke/flash.nvim/commit/79d67c6d29cd3d784eb5f1410ba057e1f1499fe9))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **char:** disable jump labels when reg recording/executing ([#226](https://github.com/folke/flash.nvim/issues/226)) ([503b0ab](https://github.com/folke/flash.nvim/commit/503b0ab0091776d2c40541507114ff4b2f24f5b9))
|
||||
* **jump:** only open folds containing match. Fixes [#224](https://github.com/folke/flash.nvim/issues/224). Fixes [#225](https://github.com/folke/flash.nvim/issues/225) ([a74d31f](https://github.com/folke/flash.nvim/commit/a74d31ffec4a6e9feb6adc33efdba247d5d912f0))
|
||||
* **search:** allow disabling multi window for search. Fixes [#198](https://github.com/folke/flash.nvim/issues/198). Fixes [#197](https://github.com/folke/flash.nvim/issues/197) ([0256d8e](https://github.com/folke/flash.nvim/commit/0256d8ecab33a9aa69fdaaf885db22e1103e2a3a))
|
||||
* **state:** use actions instead of opts.actions ([30442c8](https://github.com/folke/flash.nvim/commit/30442c88b817b5d00fcbe2f88977bbd5d0221a20))
|
||||
|
||||
## [1.17.3](https://github.com/folke/flash.nvim/compare/v1.17.2...v1.17.3) (2023-07-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **jump:** disable operator keymaps when replaying remote. Fixes [#165](https://github.com/folke/flash.nvim/issues/165) ([9f30d48](https://github.com/folke/flash.nvim/commit/9f30d48e2f509723e59c5b0915f343ce297cf386))
|
||||
|
||||
## [1.17.2](https://github.com/folke/flash.nvim/compare/v1.17.1...v1.17.2) (2023-07-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **char:** only use c for first search (of count) when current=true ([c92ecbf](https://github.com/folke/flash.nvim/commit/c92ecbff98fdc8770c283aa3934349e6889195dd))
|
||||
* **config:** run `setup` when using flash and it wasn't run yet. Fixes [#162](https://github.com/folke/flash.nvim/issues/162) ([c81e0d1](https://github.com/folke/flash.nvim/commit/c81e0d11b9e6e1279321e12a5d87dd3fac593854))
|
||||
* **state:** feed char when incremental and no match. Fixes [#57](https://github.com/folke/flash.nvim/issues/57) ([925f733](https://github.com/folke/flash.nvim/commit/925f733a731f8ed351e47d434e3a353995761012))
|
||||
|
||||
## [1.17.1](https://github.com/folke/flash.nvim/compare/v1.17.0...v1.17.1) (2023-07-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **char:** fix current for tT when count=0. Fixes [#159](https://github.com/folke/flash.nvim/issues/159) ([8604b56](https://github.com/folke/flash.nvim/commit/8604b562d919772dc161ac831dd7bfa948833fdd))
|
||||
* **char:** never add mappings for mapleader and maplocalleader ([6e3dab6](https://github.com/folke/flash.nvim/commit/6e3dab6b011bb7661b16e14dd4aa4215894c9291))
|
||||
* **char:** never overwrite existing mappings for ; and , ([abda6b8](https://github.com/folke/flash.nvim/commit/abda6b848bb11051e6a789f8a8572da3d3840bf1))
|
||||
* **char:** reset including current for tT searches. Fixes [#152](https://github.com/folke/flash.nvim/issues/152) ([9c53dad](https://github.com/folke/flash.nvim/commit/9c53dad391801acb9ce9aa49820f15f6692aec91))
|
||||
* **highlight:** set hl of target to current if it's a single character only. See [#158](https://github.com/folke/flash.nvim/issues/158) ([47d147b](https://github.com/folke/flash.nvim/commit/47d147b9527025b2ee73631b098edb5798afef4b))
|
||||
* **remote:** properly pass register for remote ops. Fixes [#156](https://github.com/folke/flash.nvim/issues/156) ([34cf6f6](https://github.com/folke/flash.nvim/commit/34cf6f685d2eabc8de438fdbaa41c8c17e9da459))
|
||||
|
||||
## [1.17.0](https://github.com/folke/flash.nvim/compare/v1.16.0...v1.17.0) (2023-07-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **labels:** allow disabling reusing labels. Closes [#147](https://github.com/folke/flash.nvim/issues/147) ([4b73e61](https://github.com/folke/flash.nvim/commit/4b73e6124f4e9b44713cb85ec5db3809923d2374))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **char:** properly exit op mode when doing esc with ftFT and jump labels ([4731cc4](https://github.com/folke/flash.nvim/commit/4731cc47459f66f9a73d19e11ea157e105384fd6))
|
||||
* **char:** set inclusive=false for FT. Fixes [#149](https://github.com/folke/flash.nvim/issues/149) ([b1af2b7](https://github.com/folke/flash.nvim/commit/b1af2b78b30e814c08840a5bb7f7ccef726ea771))
|
||||
* **jump:** better way to cancel operator pending mode ([4a980ea](https://github.com/folke/flash.nvim/commit/4a980ea7fedf20c902375fe7aa1141d671b0ffa7))
|
||||
|
||||
## [1.16.0](https://github.com/folke/flash.nvim/compare/v1.15.0...v1.16.0) (2023-07-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **fold:** show first label inside a fold on the folded text line. Fixes [#39](https://github.com/folke/flash.nvim/issues/39) ([2846324](https://github.com/folke/flash.nvim/commit/28463247f21a6e0b5486dc6d31c7ace0e43a4877))
|
||||
* **jump:** open folds when jumping to a folded position. See [#39](https://github.com/folke/flash.nvim/issues/39) ([dcb494c](https://github.com/folke/flash.nvim/commit/dcb494cfa79aae32e17a44026591564793b75434))
|
||||
* **search:** when nohlsearch=false, matches will now be shown after jump. Fixes [#142](https://github.com/folke/flash.nvim/issues/142) ([6e7d6c2](https://github.com/folke/flash.nvim/commit/6e7d6c26a4528a8d6a17e2d23c3f5738491d736d))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **repeat:** no dot repeat inside macros. Fixes [#143](https://github.com/folke/flash.nvim/issues/143) ([f7218c2](https://github.com/folke/flash.nvim/commit/f7218c2d44a8d67c5c4b40edd569c55f95754354))
|
||||
|
||||
## [1.15.0](https://github.com/folke/flash.nvim/compare/v1.14.0...v1.15.0) (2023-07-07)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **search:** flash toggle in search is now permanent until you toggle again. Closes [#134](https://github.com/folke/flash.nvim/issues/134) ([7ceee0d](https://github.com/folke/flash.nvim/commit/7ceee0de7e96c7453d5f82dcfc938f08d8029703))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **char:** special handling for t/T at current position. Fixes [#137](https://github.com/folke/flash.nvim/issues/137) ([268bffe](https://github.com/folke/flash.nvim/commit/268bffe7b9b1b9a3a4bb64a5bc8ac0627b4b7c14))
|
||||
|
||||
## [1.14.0](https://github.com/folke/flash.nvim/compare/v1.13.2...v1.14.0) (2023-07-05)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **char:** added optional multi_line=false for ftFT motions. See [#102](https://github.com/folke/flash.nvim/issues/102) ([2f92418](https://github.com/folke/flash.nvim/commit/2f924186255a56cab4cf22e13b0bc1fb906b11fa))
|
||||
* **char:** option for behavior of ;, and char repeats. Closes [#124](https://github.com/folke/flash.nvim/issues/124) ([97eba7d](https://github.com/folke/flash.nvim/commit/97eba7df4454097c1f6cc447de2a4e9230831ffb))
|
||||
* **search:** allow finding current ([6659a94](https://github.com/folke/flash.nvim/commit/6659a94a033c2f6fec1e142451aa264f03e5da90))
|
||||
* **state:** added optional `filter` for matches by non-search matcher. See [#118](https://github.com/folke/flash.nvim/issues/118) ([780ad57](https://github.com/folke/flash.nvim/commit/780ad57dedb464bfe8361356959b3ac5aaed533d))
|
||||
* **treesitter:** added `node:TSNode` to ts `Flash.Match.TS` ([1cbaff4](https://github.com/folke/flash.nvim/commit/1cbaff4a7f074c1121c89207210e4588321acd40))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **char:** fixed tT at current. Fixes [#128](https://github.com/folke/flash.nvim/issues/128) ([a1c8aa6](https://github.com/folke/flash.nvim/commit/a1c8aa62204d5eb2036e819f5b919b1fe4b88918))
|
||||
* **jump:** move offset calc outside op mode ([69141ea](https://github.com/folke/flash.nvim/commit/69141ea571602a9202ad51fae1cfe7c1894fe036))
|
||||
* **search:** count=0 ([6d1d066](https://github.com/folke/flash.nvim/commit/6d1d066e6b5fcc2ed3ca446d229c0a0d306acf17))
|
||||
* take into count of multi-width characters on offset of highlights and jump ([#125](https://github.com/folke/flash.nvim/issues/125)) ([41c09fa](https://github.com/folke/flash.nvim/commit/41c09faf8588887c7c15d8ca63c9ede805437da2))
|
||||
|
||||
## [1.13.2](https://github.com/folke/flash.nvim/compare/v1.13.1...v1.13.2) (2023-07-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **highlight:** dont use current when rainbow is used and match == target. Fixes [#109](https://github.com/folke/flash.nvim/issues/109) ([edb82f7](https://github.com/folke/flash.nvim/commit/edb82f763ac2b63006154e9da8b6629b570de551))
|
||||
|
||||
## [1.13.1](https://github.com/folke/flash.nvim/compare/v1.13.0...v1.13.1) (2023-07-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **config:** dont show jumpt labels by default! Fixup. See [#103](https://github.com/folke/flash.nvim/issues/103) ([7bb89b2](https://github.com/folke/flash.nvim/commit/7bb89b20fd42037c1cd7ed8d3193081d86f8c39b))
|
||||
* **highlight:** don't show the label when at cursor in same window and not a range. See [#74](https://github.com/folke/flash.nvim/issues/74) ([7a8e07e](https://github.com/folke/flash.nvim/commit/7a8e07e62ad1a378d6eca958aad90fc071d14e9c))
|
||||
* **labeler:** don't label folded lines. Fixes [#39](https://github.com/folke/flash.nvim/issues/39). See [#106](https://github.com/folke/flash.nvim/issues/106) ([8af3773](https://github.com/folke/flash.nvim/commit/8af3773b7b960b053038868ea18867b94abae9c8))
|
||||
|
||||
## [1.13.0](https://github.com/folke/flash.nvim/compare/v1.12.0...v1.13.0) (2023-07-01)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **config:** added `opts.config` for dynamically configuring flash. Closes [#103](https://github.com/folke/flash.nvim/issues/103) ([3829d81](https://github.com/folke/flash.nvim/commit/3829d81fd6f5f6ca784bb9628a1b99298b88a3af))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **state:** use strchars instead of strcharlen for compat 0.8.2. Fixes [#105](https://github.com/folke/flash.nvim/issues/105) ([33e0793](https://github.com/folke/flash.nvim/commit/33e0793a614735a3fffb93763c4c9bd81b55433b))
|
||||
|
||||
## [1.12.0](https://github.com/folke/flash.nvim/compare/v1.11.0...v1.12.0) (2023-06-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **state:** added support for custom keymaps and lmap. See [#66](https://github.com/folke/flash.nvim/issues/66) ([9aa7805](https://github.com/folke/flash.nvim/commit/9aa78057cf13dde3d39bf25cfe5caf092083cc0c))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **labeler:** fixed calculating skip labels for mbyte keymaps. See [#66](https://github.com/folke/flash.nvim/issues/66) ([2da635f](https://github.com/folke/flash.nvim/commit/2da635f54b81538a1e12b4859bc292d7d3e5f1b9))
|
||||
* **treesitter:** added support for Nvim 0.8.0. Fixes [#100](https://github.com/folke/flash.nvim/issues/100) ([67ed44d](https://github.com/folke/flash.nvim/commit/67ed44d5efd2d05b49af861859740eedf3a076b6))
|
||||
* **treesitter:** some nodes were missing ([7f4e25f](https://github.com/folke/flash.nvim/commit/7f4e25fae0fa1d3adfeb3e3e87fba9ff914032a0))
|
||||
|
||||
## [1.11.0](https://github.com/folke/flash.nvim/compare/v1.10.1...v1.11.0) (2023-06-29)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **char:** hide flash when doing an ftFT search while yanking. Closes [#6](https://github.com/folke/flash.nvim/issues/6) ([feda1d5](https://github.com/folke/flash.nvim/commit/feda1d5a98a1705e86966e62a052661a7369b3c0))
|
||||
* **char:** optional jump labels for ftFT searches ([d2ad5e0](https://github.com/folke/flash.nvim/commit/d2ad5e0d776a89ee424a7e0cd4364ec5dbf11dc4))
|
||||
* **char:** support alternative f/F/t/T/;/, keymaps (fix [#96](https://github.com/folke/flash.nvim/issues/96)) ([#99](https://github.com/folke/flash.nvim/issues/99)) ([c0c006a](https://github.com/folke/flash.nvim/commit/c0c006a7bb694b4cec9a5f40e632f871b478e0d0))
|
||||
* **label:** added `opts.label.format` for formatting rendered labels. Closes [#84](https://github.com/folke/flash.nvim/issues/84) ([2d3e7b9](https://github.com/folke/flash.nvim/commit/2d3e7b90c568083e9857b100dc2570d269da0a0c))
|
||||
* **labeler:** allow excluding certain labels with a specific case ([6b255d3](https://github.com/folke/flash.nvim/commit/6b255d37505445da3db6fae5d79dff63529cd222))
|
||||
* **pos:** Pos can now be initialized with window or current window cursor ([7a05cd5](https://github.com/folke/flash.nvim/commit/7a05cd5dadb78b8d475526157e464f24d14ff5b2))
|
||||
* **search:** you can now `toggle` flash while using regular search ([e761182](https://github.com/folke/flash.nvim/commit/e761182f6c79ff5f88c877729465ece05b01c65a))
|
||||
* **state:** custom char actions ([4f44bb4](https://github.com/folke/flash.nvim/commit/4f44bb454df0c6f598e75cd8501a1eb8e1bd2df5))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **hacks:** make sure to render the cursor before getchar ([2b328d1](https://github.com/folke/flash.nvim/commit/2b328d121c2b56cf25e1eb9ba92c7459beb241be))
|
||||
* **highlight:** never put an extmark on the current cursor position ([8434130](https://github.com/folke/flash.nvim/commit/843413028843d1c3ce29449fe9ff62af8f642540))
|
||||
* **highlight:** use current hl if pos == label pos ([56531ee](https://github.com/folke/flash.nvim/commit/56531ee85d919e787dbb247aabedb5d3dd0b7bd1))
|
||||
* **jump:** replace opfunc by noop to properly cancel custom operators. Fixes [#93](https://github.com/folke/flash.nvim/issues/93) ([40b2bcb](https://github.com/folke/flash.nvim/commit/40b2bcbb05f1452f2ee7d21b79ce8ba77ea6cc94))
|
||||
* **jump:** temporarily set selection=inclusive. Closes [#81](https://github.com/folke/flash.nvim/issues/81) ([5c9505a](https://github.com/folke/flash.nvim/commit/5c9505a19edcbb236d367282584ed5f02ccd4fb4))
|
||||
* **labeler:** fixed label distance calculation ([1d941de](https://github.com/folke/flash.nvim/commit/1d941de722564a8ac2f07c2df262a48c49c1cdb9))
|
||||
* **labeler:** put original pattern in a `\%()` group. Fixes some skip label issues ([6102a7c](https://github.com/folke/flash.nvim/commit/6102a7c0e93dbcf592a7ed2b7a2a5c2a84c5033e))
|
||||
* **labeler:** skip all labels on invalid regex. Fixes [#94](https://github.com/folke/flash.nvim/issues/94) ([1fff746](https://github.com/folke/flash.nvim/commit/1fff746049253b10a008d60e1752065a98fd8614))
|
||||
* **remote:** use nvim_input instead of nvim_feedkeys for clearing op mode ([c90eae5](https://github.com/folke/flash.nvim/commit/c90eae5172a00551d51883cf8b67306a812a713f))
|
||||
* **search:** correctly set match end pos for multi byte characters. Fixes [#90](https://github.com/folke/flash.nvim/issues/90) ([0193d52](https://github.com/folke/flash.nvim/commit/0193d52af38d228b79569c62e06ee36b77a1a85e))
|
||||
* **treesitter:** ignore windows without ts parser. Fixes [#91](https://github.com/folke/flash.nvim/issues/91) ([13022c0](https://github.com/folke/flash.nvim/commit/13022c09fa30fb03d14110a380238f6a75b42ab4))
|
||||
|
||||
## [1.10.1](https://github.com/folke/flash.nvim/compare/v1.10.0...v1.10.1) (2023-06-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **highlight:** apply after labels and then before ([4439fca](https://github.com/folke/flash.nvim/commit/4439fca240a54ef4d4537102668285e9cbb6f23c))
|
||||
* **highlight:** correctly order after labels at the same column ([b096797](https://github.com/folke/flash.nvim/commit/b096797b64f56357c40222f5a3cff6f25ac3b5dc))
|
||||
* **highlight:** make sure col is not negative with label.before = true ([cbce7f9](https://github.com/folke/flash.nvim/commit/cbce7f923c74fb75be030273c0d49f6a3447a95f))
|
||||
* **prompt:** never show the prompt when in regular search ([51149ba](https://github.com/folke/flash.nvim/commit/51149ba2e6bcba0a28e67b9654450835437a2914))
|
||||
* **rainbow:** stable rainbow label highlight groups ([937df4f](https://github.com/folke/flash.nvim/commit/937df4f097781e3e91594bf69425f3e74044b711))
|
||||
|
||||
## [1.10.0](https://github.com/folke/flash.nvim/compare/v1.9.0...v1.10.0) (2023-06-27)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **highlight:** added optional rainbow labels. Disabled by default. Useful for Treesitter ranges. ([#74](https://github.com/folke/flash.nvim/issues/74)) ([ffb865b](https://github.com/folke/flash.nvim/commit/ffb865b1a60732d9ce2c9bffe3fb6724e1004ebb))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **char:** force before=false with f, F motion ([#75](https://github.com/folke/flash.nvim/issues/75)) ([40313ec](https://github.com/folke/flash.nvim/commit/40313ecf3140264b6e9d9611a3832a32e5ab7a46))
|
||||
* **search:** fixup for search commmands ([0f2d53d](https://github.com/folke/flash.nvim/commit/0f2d53d63e9d90f7a310509fbf4e98fbe21be56e))
|
||||
|
||||
## [1.9.0](https://github.com/folke/flash.nvim/compare/v1.8.0...v1.9.0) (2023-06-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **treesitter:** added treesitter search to label ts nodes around search matches ([6f791d4](https://github.com/folke/flash.nvim/commit/6f791d4709a2c8ef2373302d3a067ae45fdc2f8d))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* added unicode support for labels/skips and fuzzy search. See [#66](https://github.com/folke/flash.nvim/issues/66) ([2528752](https://github.com/folke/flash.nvim/commit/2528752b7efbf3f67cce8b9d0d75ee769f72c01e))
|
||||
* **state:** restore window views on esc or ctrl-c ([7b21dfd](https://github.com/folke/flash.nvim/commit/7b21dfddcf7ccc4fb665ca0db80810210f8cde7c))
|
||||
* **treesitter:** add incremental = false to default settings of treesitter ([1cf706f](https://github.com/folke/flash.nvim/commit/1cf706f342bea4447c2f8ac13c2fab9df060ce1e))
|
||||
|
||||
## [1.8.0](https://github.com/folke/flash.nvim/compare/v1.7.0...v1.8.0) (2023-06-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* added prompt window that shows pattern during jump (can be disabled) ([3fff703](https://github.com/folke/flash.nvim/commit/3fff7033f53b8f0714efd0dd56b03aa3f22c6376))
|
||||
* **api:** allow a match to disable getting a label ([ea56cea](https://github.com/folke/flash.nvim/commit/ea56ceaea4760b2031719d8e5eb1b6231ef9f43c))
|
||||
* **api:** allow a match to enable/disable highlight ([38eca97](https://github.com/folke/flash.nvim/commit/38eca97c8bdbbbd7be64b562eeb9f964cf8bc145))
|
||||
* **ffi:** added `mappings_enabled` ([6f6af15](https://github.com/folke/flash.nvim/commit/6f6af15b491bee14460873fe63fc7b20e7c73dd8))
|
||||
* **hacks:** added support for detecting user input waiting ([81c610a](https://github.com/folke/flash.nvim/commit/81c610acd374b40fc7a7fa4b493b1b9783d3d52d))
|
||||
* **highlight:** added option to disable distance based labeling ([ad9212f](https://github.com/folke/flash.nvim/commit/ad9212f28ef37e893a5a4113f8757052b2035c36))
|
||||
* **highlight:** show fake cursor in all windows when flash is active ([471b165](https://github.com/folke/flash.nvim/commit/471b165722ae5db4ddad7cbaf1d351127fb55529))
|
||||
* **highlight:** when running in vscode, set default hl groups to something that works ([d4c30b1](https://github.com/folke/flash.nvim/commit/d4c30b169f01b8108c5bc38e230a975408133603))
|
||||
* **jump:** added jump offset ([0f2dfac](https://github.com/folke/flash.nvim/commit/0f2dfaca329ed9a7db9e5062d964492cf51765eb))
|
||||
* **jump:** added options for remote operator pending mode ([436d1f4](https://github.com/folke/flash.nvim/commit/436d1f402a696733b8a1512072bbd0ac8da72cea))
|
||||
* **jump:** remote operator pending operations will now always return to the original window ([c11d0d1](https://github.com/folke/flash.nvim/commit/c11d0d15660ce309c733982b2c34cd54c9c9d9f0))
|
||||
* **label:** minimum pattern length to show labels. Closes [#68](https://github.com/folke/flash.nvim/issues/68) ([2c2302a](https://github.com/folke/flash.nvim/commit/2c2302a3eae1dc72d2140c58974e2f73df41556d))
|
||||
* matcher function now has a from/to opts param ([1cb669d](https://github.com/folke/flash.nvim/commit/1cb669d2ce074ea39722da9fec6b0c2686b3b484))
|
||||
* **remote_op:** allow setting motion to `nil` to automatically start a new motion when needed ([259062d](https://github.com/folke/flash.nvim/commit/259062ddc47f9de11e0e498cd58040705d7b6f5c))
|
||||
* **remote:** implement remote using new `remote_op` options ([51f5c35](https://github.com/folke/flash.nvim/commit/51f5c352db8791f4218e19cc7fa40948cdda9647))
|
||||
* searches can now be continued. Closes [#54](https://github.com/folke/flash.nvim/issues/54) ([487aa52](https://github.com/folke/flash.nvim/commit/487aa52956fdf79ba545151227b0ad39c5276c69))
|
||||
* **state:** added support for restoring all window views and current window ([01736c0](https://github.com/folke/flash.nvim/commit/01736c01eb43dcf497a946689c7f434b1d13b4a8))
|
||||
* **util:** luv check that does something when something finishes ([a3643eb](https://github.com/folke/flash.nvim/commit/a3643eb5424c12b5abc7b08a74d0d53fa5a29af0))
|
||||
* **vscode:** make flash work properly in vscode by updating/changing the default config. Fixes [#58](https://github.com/folke/flash.nvim/issues/58) ([fa72836](https://github.com/folke/flash.nvim/commit/fa72836760417436cfe8e33ee74edaefd8ee9e00))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **config:** process modes in correct order. Fixes [#50](https://github.com/folke/flash.nvim/issues/50) again ([919cbe4](https://github.com/folke/flash.nvim/commit/919cbe49b66758cf57529847c396e718a9883de0))
|
||||
* disable prompt on vscode ([f93b33d](https://github.com/folke/flash.nvim/commit/f93b33d736fb2eb6f28526ab465cfe7f32e7d96f))
|
||||
* **jump:** fixup to always use a motion for remote ops ([11fa883](https://github.com/folke/flash.nvim/commit/11fa8833c62175a88fc35c50f1d23d5002d20fda))
|
||||
* **jump:** improved operator pending mode for jumps ([16f785f](https://github.com/folke/flash.nvim/commit/16f785f26e74b8f0b49901356c57cda2a06379f5))
|
||||
* **jump:** operator pending mode for remote jumps now behaves correctly ([cb24e66](https://github.com/folke/flash.nvim/commit/cb24e667ea58cfa7ea9df9fdf41bb6a26ea13da1))
|
||||
* **remote:** make sure opts always exists ([7083750](https://github.com/folke/flash.nvim/commit/7083750697dea16b3943ca8a92c958acd83c2126))
|
||||
* **search:** added support for search-commands. Fixes [#67](https://github.com/folke/flash.nvim/issues/67) ([7a59c42](https://github.com/folke/flash.nvim/commit/7a59c4239ed11ca3ec91cd7544535d836f09eb20))
|
||||
|
||||
## [1.7.0](https://github.com/folke/flash.nvim/compare/v1.6.0...v1.7.0) (2023-06-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **config:** allow mode inheritance. Closes [#50](https://github.com/folke/flash.nvim/issues/50) ([3deefe8](https://github.com/folke/flash.nvim/commit/3deefe88e02e68c163c320614be1727fa887cd65))
|
||||
* **jump:** added option to force inclusive/exclusive. Closes [#49](https://github.com/folke/flash.nvim/issues/49) ([e71efbf](https://github.com/folke/flash.nvim/commit/e71efbfbc73df21d3e79d30c4c27bd29892c216c))
|
||||
* **remote:** peoperly deal with c for remote. Will jump back when leaving insert mode ([1075013](https://github.com/folke/flash.nvim/commit/10750139d3d4f2fb6c7bb8cc33aef988a7b26b7c))
|
||||
* **state:** allow passing a callable object as matcher ([f49fa9c](https://github.com/folke/flash.nvim/commit/f49fa9cbddd6a30c59420892e09f57f391bd9516))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cache:** allow current window to be excluded ([770763c](https://github.com/folke/flash.nvim/commit/770763ce2d2b4c340249cb7000de81c2085438c8))
|
||||
* **cache:** fixup for window selection ([ed3bec6](https://github.com/folke/flash.nvim/commit/ed3bec6da9b92cee4954bfb71c4e71d06406191c))
|
||||
* **char:** add group to autocmd ([fc08d27](https://github.com/folke/flash.nvim/commit/fc08d279ddb92ba2323684a2077aa7797384fc3c))
|
||||
* **remote:** properly restore remote window as well. Also remove the `normal! o` ([587a243](https://github.com/folke/flash.nvim/commit/587a2436f84301b84937242657dcc03be4a80702))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **remote:** restore views on TextYankPost ([d4dadc8](https://github.com/folke/flash.nvim/commit/d4dadc8fae53ded2a51a2ca0a9d82889e148e0b7))
|
||||
|
||||
## [1.6.0](https://github.com/folke/flash.nvim/compare/v1.5.0...v1.6.0) (2023-06-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **config:** pattern can now have a `max_length`. When length is reached, labels are no longer skipped. When it exceeds, either a jump is followed or the search is ended ([bd9dbee](https://github.com/folke/flash.nvim/commit/bd9dbee041296a582faa6dfe25e1af87d65614c7))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **config:** exclude noice by default ([bc9a599](https://github.com/folke/flash.nvim/commit/bc9a5992b947ae84b5c1458f0b117abda1b61154))
|
||||
* **repeat:** make sure repeat is enabled for char searches. Fixes [#40](https://github.com/folke/flash.nvim/issues/40) ([219f0c0](https://github.com/folke/flash.nvim/commit/219f0c09b664257a7d9b46023bcb24563ae49832))
|
||||
* **state:** always reposition the cursor on incremental mode ([81e38d6](https://github.com/folke/flash.nvim/commit/81e38d604d285d835a9186f82e28a302bc048128))
|
||||
|
||||
## [1.5.0](https://github.com/folke/flash.nvim/compare/v1.4.1...v1.5.0) (2023-06-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* added remote plugin ([fb50450](https://github.com/folke/flash.nvim/commit/fb5045044f28caf08ca6d89e9fe40874138faeef))
|
||||
* flash remote. thank you [@max397574](https://github.com/max397574)! ([809ea4f](https://github.com/folke/flash.nvim/commit/809ea4f804d831ca5ff26c94b8d409ad9dfec8eb))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **char:** always stop highlights in insert mode ([64e5129](https://github.com/folke/flash.nvim/commit/64e51292e83e7ce409248fd07ff00b51a993a6c0))
|
||||
|
||||
## [1.4.1](https://github.com/folke/flash.nvim/compare/v1.4.0...v1.4.1) (2023-06-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **char:** don't repeat on motion char when executing a macro. See [#34](https://github.com/folke/flash.nvim/issues/34) ([674cfb4](https://github.com/folke/flash.nvim/commit/674cfb43e5424a5405661ba632810bacfc0a9c37))
|
||||
|
||||
## [1.4.0](https://github.com/folke/flash.nvim/compare/v1.3.0...v1.4.0) (2023-06-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **char:** tfTF now behave like clever-f when repeating the motion. Fixes [#26](https://github.com/folke/flash.nvim/issues/26) ([97c3a99](https://github.com/folke/flash.nvim/commit/97c3a993e60ebdd42c7671af07620f705ee6378f))
|
||||
* **config:** allow custom window filters. Added non-focusable windows by default ([e6ee00d](https://github.com/folke/flash.nvim/commit/e6ee00d4e76edac8cbcabe0f442a5ec34450d1f6))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **config:** dont show flash in cmp_menu ([29c35de](https://github.com/folke/flash.nvim/commit/29c35dec5f81504ee63a39fec90597222620af0a))
|
||||
* **treesitter:** always disable incremental mode for treesitter. Fixes [#27](https://github.com/folke/flash.nvim/issues/27) ([6e84716](https://github.com/folke/flash.nvim/commit/6e8471673a7158a8820986f6aad770a912a66eed))
|
||||
|
||||
## [1.3.0](https://github.com/folke/flash.nvim/compare/v1.2.0...v1.3.0) (2023-06-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **char:** optionally disable some ftFT keymaps ([3e27d9a](https://github.com/folke/flash.nvim/commit/3e27d9ab07b9363b0ecb94645eae38909f7baa5a))
|
||||
* **config:** show labels for current jump target by default ([0dcc00e](https://github.com/folke/flash.nvim/commit/0dcc00ea6a3b312b8e081f3f582adc26a4721ac7))
|
||||
* **search:** optional trigger character. Not recommended. Fixes [#21](https://github.com/folke/flash.nvim/issues/21) ([cb0977c](https://github.com/folke/flash.nvim/commit/cb0977cd0f7cec4573ee1210edc2032739866b2b))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **char:** fixup for keys ([81469aa](https://github.com/folke/flash.nvim/commit/81469aaf3ccf15d7c942bbd9144f2c06f68fe1ee))
|
||||
* **treesitter:** properly deal with nodes ending at col 0. Fixes [#17](https://github.com/folke/flash.nvim/issues/17) ([6cd4414](https://github.com/folke/flash.nvim/commit/6cd44145f75392fbfe67700b59517dbf8324bd21))
|
||||
* **treesitter:** removed debug print ([0fabd1b](https://github.com/folke/flash.nvim/commit/0fabd1b4ddea5754576ccc09a515867a3ac129ce))
|
||||
|
||||
## [1.2.0](https://github.com/folke/flash.nvim/compare/v1.1.0...v1.2.0) (2023-06-21)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* added example that matches beginning of words only ([1e2c61d](https://github.com/folke/flash.nvim/commit/1e2c61d8db882cc001fcebff9eba2549336ce87a))
|
||||
* **config:** setting to disable uppercase labels. Fixes [#11](https://github.com/folke/flash.nvim/issues/11) ([13d7b3e](https://github.com/folke/flash.nvim/commit/13d7b3e70cadc7e4d64f818a04fbca2b33ac1d4f))
|
||||
* **labeler:** reuse only lowercase labels by default. See [#11](https://github.com/folke/flash.nvim/issues/11) ([8f0b9ed](https://github.com/folke/flash.nvim/commit/8f0b9ed656d7b92eb0d60c34b6a5bd3803cc0e0b))
|
||||
|
||||
## [1.1.0](https://github.com/folke/flash.nvim/compare/v1.0.0...v1.1.0) (2023-06-21)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* added config.jump.autojump. Fixes [#5](https://github.com/folke/flash.nvim/issues/5) ([1808d3e](https://github.com/folke/flash.nvim/commit/1808d3ebb6ea5810957b8f8e32aab8f4e9e7f14c))
|
||||
* added custom actions on label select ([eb0769f](https://github.com/folke/flash.nvim/commit/eb0769ff38001ed3eead9e54289b7f63387e1525))
|
||||
* added example plugin that shows a diagnostic at a certain label without moving the cursor ([7a9bd11](https://github.com/folke/flash.nvim/commit/7a9bd118a3b4d2829d4718c26d8af21b36ebfb87))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **config:** get mode opts from options instead of defaults. Fixes [#4](https://github.com/folke/flash.nvim/issues/4) ([41fab4c](https://github.com/folke/flash.nvim/commit/41fab4cb225d9233fec7987bb1445c9768d84caf))
|
||||
* **diag:** always hide when done ([226c634](https://github.com/folke/flash.nvim/commit/226c634e3db6f02eb734d37c16d729bae41a77ef))
|
||||
* **jump:** register and history should use pattern.search instead of pattern. Fixes [#7](https://github.com/folke/flash.nvim/issues/7) ([a11cf6a](https://github.com/folke/flash.nvim/commit/a11cf6ad205dd2493d2af6643bc20bef925004f5))
|
||||
* **treesitter:** make treesitter plugin work with custom labels. Fixes [#9](https://github.com/folke/flash.nvim/issues/9) ([3fac625](https://github.com/folke/flash.nvim/commit/3fac6253fd59e7c32300e6209c8f1e60ea8a3c81))
|
||||
|
||||
## 1.0.0 (2023-06-21)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* abort_pattern can now be false ([e036667](https://github.com/folke/flash.nvim/commit/e0366678e337df4a93c0704e77a6909e617950c3))
|
||||
* add option to save loc to jumplist before jump ([0aae816](https://github.com/folke/flash.nvim/commit/0aae816ef419ad4554a784a07fe239aeee9a6934))
|
||||
* added char searches, f, F, t, T ([06839d8](https://github.com/folke/flash.nvim/commit/06839d8ac7f2ca42b639fc8f90e2c655234bba9a))
|
||||
* added config for forward/wrap ([b9649bd](https://github.com/folke/flash.nvim/commit/b9649bd226da89bcbef7fb6b27e5d3a08d0fe6b4))
|
||||
* added config.search.regex ([bda1be0](https://github.com/folke/flash.nvim/commit/bda1be00bca62d7ebd9de4c7848e7c70a65f2f91))
|
||||
* added ffi based searcher. Finally 100% correct end pos for matches ([46b41d1](https://github.com/folke/flash.nvim/commit/46b41d13d6943443c20b3bf87fdf8eb495fee4c2))
|
||||
* added option to label the first match ([63b75ed](https://github.com/folke/flash.nvim/commit/63b75ed8dcaec7efaf6e67e3913b59f2e614f043))
|
||||
* added optional backdrop ([2172a90](https://github.com/folke/flash.nvim/commit/2172a907aeba4a3961e399044a2f4ca1087e044d))
|
||||
* added support for label offsets and label styles ([3e9f630](https://github.com/folke/flash.nvim/commit/3e9f630ce04bdda14669592bc5d36af594077e95))
|
||||
* added treesitter command ([fd9bd80](https://github.com/folke/flash.nvim/commit/fd9bd8015a7df2b8aedc294bc517264837d218f9))
|
||||
* advance for results ([9d70126](https://github.com/folke/flash.nvim/commit/9d70126e09b20125752a43c1e26041eecc4f721c))
|
||||
* allow to always render search highlight to prevent flickering when updating ui ([ff0e25f](https://github.com/folke/flash.nvim/commit/ff0e25f63ae98f7ab2735293a40f02e8cfc85d2a))
|
||||
* **charsearch:** close on <esc> ([ee3228a](https://github.com/folke/flash.nvim/commit/ee3228af6b82204cb03c317526a0212229953272))
|
||||
* **charsearch:** make char search dot repeatable ([91485c1](https://github.com/folke/flash.nvim/commit/91485c12b2685bdde097b2351725e973cc2e1274))
|
||||
* dont stabalize labels for treesitter ([b20ad86](https://github.com/folke/flash.nvim/commit/b20ad8652f34a477f6bdab912258b176aeebdd0d))
|
||||
* expose commands on main module ([70130d2](https://github.com/folke/flash.nvim/commit/70130d29a3c4c8d90d96caae5871d0cc19e3f283))
|
||||
* fuzzy matching ([7407dd6](https://github.com/folke/flash.nvim/commit/7407dd679c90986dff09b22a690feb52aa5ea31a))
|
||||
* highlight groups config ([313e252](https://github.com/folke/flash.nvim/commit/313e252ecfd3252d2e39d7c012b0674388d65f8d))
|
||||
* **highlight:** added support for before/after labels ([d0133d2](https://github.com/folke/flash.nvim/commit/d0133d2966695f063f8909a0d80a97cd90d2848c))
|
||||
* **highlight:** allow diffrerent namespaces for highlight ([2649b18](https://github.com/folke/flash.nvim/commit/2649b1888fd84d1cee0ab3d5fdc5e82c8a5f391c))
|
||||
* initial version ([22913c6](https://github.com/folke/flash.nvim/commit/22913c65a1c960e3449c813824351abbdb327c7b))
|
||||
* jump position (start, end or range) ([335a5a9](https://github.com/folke/flash.nvim/commit/335a5a91222680f92c585c16d94d183a57b13c8d))
|
||||
* labels are now skipped based on regex searches to be able to fully support regex patterns ([e704d88](https://github.com/folke/flash.nvim/commit/e704d8846fd2d8189f127f2b080812ed2518fdc4))
|
||||
* lazy require ([171b9ff](https://github.com/folke/flash.nvim/commit/171b9ff3034b2afb5ad9a0420a906a8c597037ba))
|
||||
* make all the things repeatable without needing `expr=true` ([ec3a8ac](https://github.com/folke/flash.nvim/commit/ec3a8ac3ebfc9957c65620bcae7d91ed38a334b2))
|
||||
* much improved repeat api ([2f76471](https://github.com/folke/flash.nvim/commit/2f76471f3a178234a3b08a6ae5ca9f8082bacc46))
|
||||
* multiple modes ([ed1150f](https://github.com/folke/flash.nvim/commit/ed1150f2cabcca526894423de8fda74d756a0cff))
|
||||
* **pattern:** custom pattern functions ([b9e13f2](https://github.com/folke/flash.nvim/commit/b9e13f2c8cf603e70d7eff410ffbd88c8611d6d0))
|
||||
* **repeat:** show warning when keymap expr didn't execute. probably because expr=true was not used ([789d3b2](https://github.com/folke/flash.nvim/commit/789d3b22610fe8f45f7451afac5b1921db852dd6))
|
||||
* stable labels ([3e6b345](https://github.com/folke/flash.nvim/commit/3e6b345f590c70c83ccbe720afc268ba9ba3b442))
|
||||
* **state:** proper support for incremental search ([8a0fa11](https://github.com/folke/flash.nvim/commit/8a0fa1147cfad21b6576ee4d9320de6e78b1c24c))
|
||||
* **state:** state will now automatically updated on changedtick or when buf changes ([60193cb](https://github.com/folke/flash.nvim/commit/60193cb3aa384938bd7b9be8d5b594c0ebe0c867))
|
||||
* **state:** update matcher when view changed ([9f4dc50](https://github.com/folke/flash.nvim/commit/9f4dc506987a9381d67e3e602e9950a622c76276))
|
||||
* treesitter node jumping ([119643f](https://github.com/folke/flash.nvim/commit/119643fd672a959233da3b1c3b61de965dfe765b))
|
||||
* **treesitter:** ; & , to expand/descrease selection ([6551d97](https://github.com/folke/flash.nvim/commit/6551d970d270bda2b6bf9be09944196d8782a329))
|
||||
* **treesitter:** allow custom options ([d9d5e75](https://github.com/folke/flash.nvim/commit/d9d5e7558e11e1cdb9a48c87e442444664b3c0cf))
|
||||
* util module for dot-repeat ([e6f02b1](https://github.com/folke/flash.nvim/commit/e6f02b15608b625266f1564b8005c36d56f7fa71))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* allow space in string ([f1b8691](https://github.com/folke/flash.nvim/commit/f1b86913daa85aef94fae07e03cab8ccf7f9137f))
|
||||
* calculate target in update ([f3f915a](https://github.com/folke/flash.nvim/commit/f3f915ac0b5c4ff4598dd73b65cff9f9c0d3e57b))
|
||||
* **charsearch:** inclusive/exclusive operator pending fix ([fb1867c](https://github.com/folke/flash.nvim/commit/fb1867c908e488a7dbe1a83f7cad57a826bf977f))
|
||||
* **charsearch:** mode ([b8c18ba](https://github.com/folke/flash.nvim/commit/b8c18baad82145fe097db4d13440d44a9005f30d))
|
||||
* **config:** register and nohlsearch are disables by default ([f20d2f8](https://github.com/folke/flash.nvim/commit/f20d2f8d34142ec1674284f582e57f6f66a99cd8))
|
||||
* dont set search register by default ([f7352f7](https://github.com/folke/flash.nvim/commit/f7352f7c7e90e3e0b5818b398d543e2146f045ad))
|
||||
* fixup for first -> current ([43b96c6](https://github.com/folke/flash.nvim/commit/43b96c69d7f7fd97f5c9ec316cf8ee3c30badc48))
|
||||
* **highlight:** highlight each line of the backdrop separately to fix extmark priorities ([08bf4f6](https://github.com/folke/flash.nvim/commit/08bf4f6fad136743c6791f6db4659f314fe69104))
|
||||
* **highlight:** proper nvim 0.10.0 check for inline extmarks ([6da8904](https://github.com/folke/flash.nvim/commit/6da8904ed698069395baab49b168b37b0a35b839))
|
||||
* **highlight:** set cursorline hl group ([8715685](https://github.com/folke/flash.nvim/commit/8715685cd24e5d5727442063ce7e347bb0b567b7))
|
||||
* **init:** pass opts to config ([0627e2f](https://github.com/folke/flash.nvim/commit/0627e2f09e9a7b26d8755d8e4994e38cfdd58ba5))
|
||||
* **jump:** check pattern for jump target ([d29d5fc](https://github.com/folke/flash.nvim/commit/d29d5fc41dcbe6e7c751c30d28b362400f45f870))
|
||||
* **jump:** dont change ordering of matches when calculating labels ([8611eab](https://github.com/folke/flash.nvim/commit/8611eaba93c080175026dbd41fac9a7a9e535637))
|
||||
* **jump:** fix inclusive/excusive for operator pending mode ([99c99a7](https://github.com/folke/flash.nvim/commit/99c99a75754f107eef0cbc23f4745e7c0d784848))
|
||||
* **jump:** make it all work in operator pending mode ([1005faa](https://github.com/folke/flash.nvim/commit/1005faa1c21dcaa37232fd93c2ef7c71fc3b3099))
|
||||
* **labeler:** dont include end_pos to re-use stable labels ([dadca0e](https://github.com/folke/flash.nvim/commit/dadca0e75335dd9e3083ea11cd41f1d197ebe1a7))
|
||||
* **labels:** fixed some edge cases regarding labels ([124d1b6](https://github.com/folke/flash.nvim/commit/124d1b6900b30f5a2e1c60bc6a4ac0e1a0de889a))
|
||||
* **matcher:** match end_pos when finding relative to another match ([0794ba2](https://github.com/folke/flash.nvim/commit/0794ba238ada4ab820940a63dbd54f29679d10be))
|
||||
* **matcher:** ordering ([e46a629](https://github.com/folke/flash.nvim/commit/e46a629c679a022e822a4243ad15ebcb1474412d))
|
||||
* **search:** added support for match ([e3e3958](https://github.com/folke/flash.nvim/commit/e3e3958c871bf46d808605afbdcf07cafb1e98e4))
|
||||
* **search:** cleanup and add search to history ([175ffd9](https://github.com/folke/flash.nvim/commit/175ffd9960fdaf65b00d00782fdc0505678e9162))
|
||||
* **search:** dont add labels if too many results ([959af4e](https://github.com/folke/flash.nvim/commit/959af4e095df35a62200a35b1f3aef2e652c8dd5))
|
||||
* **searcher:** don't use ignore case for labels and skip both upper/lower when needed ([1b48511](https://github.com/folke/flash.nvim/commit/1b48511efa0834deb07461b3e076c8bafb66d876))
|
||||
* **searcher:** finally was able to properly fix finding ends of matches ([4251741](https://github.com/folke/flash.nvim/commit/4251741114187823b94957dfad40e7dcfa82ac2d))
|
||||
* **searcher:** skip all labels when pattern ends with escape character ([530038d](https://github.com/folke/flash.nvim/commit/530038d05925373feddb4742dcf742401532ed69))
|
||||
* **searcher:** use vim.regex to get match end and added support for multi-line ([ffcdf20](https://github.com/folke/flash.nvim/commit/ffcdf20d7ff15117a984244e1258794fef10efe8))
|
||||
* **search:** properly deal with invalid patterns ([46d6655](https://github.com/folke/flash.nvim/commit/46d6655891238b569ffa8c0334f2bdae39adc21e))
|
||||
* **search:** skip all labels when pattern is invalid regex ([9bb8079](https://github.com/folke/flash.nvim/commit/9bb8079c82dccccc54ec107e243f845e996a492b))
|
||||
* **state:** better operator pending mode detection for search ([f53dd07](https://github.com/folke/flash.nvim/commit/f53dd076af1e2f6f9374f6c26c8f474c83c5815d))
|
||||
* **state:** force update when making visible ([ada913d](https://github.com/folke/flash.nvim/commit/ada913d2a1cbdb765493419202a48addaf2c873a))
|
||||
* **state:** keep states as a key in a table to prevent double work ([4a6ea98](https://github.com/folke/flash.nvim/commit/4a6ea985c88eb8503515131f422d4cb856db4b3b))
|
||||
* **state:** results sorting ([9da4d28](https://github.com/folke/flash.nvim/commit/9da4d285d0d453fc9eb0f3bfcebde68be334f066))
|
||||
* **state:** stop searching when max matches reached ([4245e49](https://github.com/folke/flash.nvim/commit/4245e49fb878459bb5a074c9c8023900baf321cd))
|
||||
* **treesitter:** use state.pos as cursor to get nodes ([d1185ad](https://github.com/folke/flash.nvim/commit/d1185add4a6f624b150896ba4eb32855ef9e35b7))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* cache window matches ([678532a](https://github.com/folke/flash.nvim/commit/678532a956562a53887a5dda2e4513c3ba216de9))
|
||||
* lazy require/setup ([2bbf721](https://github.com/folke/flash.nvim/commit/2bbf72189c875509ac37130f56fc4cb6e0f65139))
|
||||
201
config/neovim/store/lazy-plugins/flash.nvim/LICENSE
Normal file
201
config/neovim/store/lazy-plugins/flash.nvim/LICENSE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
705
config/neovim/store/lazy-plugins/flash.nvim/README.md
Normal file
705
config/neovim/store/lazy-plugins/flash.nvim/README.md
Normal file
@ -0,0 +1,705 @@
|
||||
# ⚡flash.nvim
|
||||
|
||||
`flash.nvim` lets you navigate your code with search labels,
|
||||
enhanced character motions, and Treesitter integration.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Search Integration</th>
|
||||
<th>Standalone Jump</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<img src="https://github.com/folke/flash.nvim/assets/292349/e0ac4cbc-fa54-4505-8261-43ec0505518d" />
|
||||
</td>
|
||||
<td>
|
||||
<img src="https://github.com/folke/flash.nvim/assets/292349/90af85e3-3f22-4c51-af4b-2a2488c9560b" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><code>f</code>, <code>t</code>, <code>F</code>, <code>T</code></th>
|
||||
<th>Treesitter</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<img src="https://github.com/folke/flash.nvim/assets/292349/379cb2de-8ec3-4acf-8811-d3590a5854b6" />
|
||||
</td>
|
||||
<td>
|
||||
<img src="https://github.com/folke/flash.nvim/assets/292349/b963b05e-3d28-45ff-b43a-928a06e5f92a" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## ✨ Features
|
||||
|
||||
- 🔍 **Search Integration**: integrate **flash.nvim** with your regular
|
||||
search using `/` or `?`. Labels appear next to the matches,
|
||||
allowing you to quickly jump to any location. Labels are
|
||||
guaranteed not to exist as a continuation of the search pattern.
|
||||
- ⌨️ **type as many characters as you want** before using a jump label.
|
||||
- ⚡ **Enhanced `f`, `t`, `F`, `T` motions**
|
||||
- 🌳 **Treesitter Integration**: all parents of the Treesitter node
|
||||
under your cursor are highlighted with a label for quick selection
|
||||
of a specific Treesitter node.
|
||||
- 🎯 **Jump Mode**: a standalone jumping mode similar to search
|
||||
- 🔎 **Search Modes**: `exact`, `search` (regex), and `fuzzy` search modes
|
||||
- 🪟 **Multi Window** jumping
|
||||
- 🌐 **Remote Actions**: perform motions in remote locations
|
||||
- ⚫ **dot-repeatable** jumps
|
||||
- 📡 **highly extensible**: check the [examples](https://github.com/folke/flash.nvim#-examples)
|
||||
|
||||
## 📋 Requirements
|
||||
|
||||
- Neovim >= **0.8.0** (needs to be built with **LuaJIT**)
|
||||
|
||||
## 📦 Installation
|
||||
|
||||
Install the plugin with your preferred package manager:
|
||||
|
||||
[lazy.nvim](https://github.com/folke/lazy.nvim):
|
||||
|
||||
<!-- setup:start -->
|
||||
|
||||
```lua
|
||||
{
|
||||
"folke/flash.nvim",
|
||||
event = "VeryLazy",
|
||||
---@type Flash.Config
|
||||
opts = {},
|
||||
-- stylua: ignore
|
||||
keys = {
|
||||
{ "s", mode = { "n", "x", "o" }, function() require("flash").jump() end, desc = "Flash" },
|
||||
{ "S", mode = { "n", "x", "o" }, function() require("flash").treesitter() end, desc = "Flash Treesitter" },
|
||||
{ "r", mode = "o", function() require("flash").remote() end, desc = "Remote Flash" },
|
||||
{ "R", mode = { "o", "x" }, function() require("flash").treesitter_search() end, desc = "Treesitter Search" },
|
||||
{ "<c-s>", mode = { "c" }, function() require("flash").toggle() end, desc = "Toggle Flash Search" },
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
<!-- setup:end -->
|
||||
|
||||
> ⚠️ When creating the keymaps manually either use a lua function like
|
||||
> `function() require("flash").jump() end` as the **rhs**, or a string
|
||||
> like `<cmd>lua require("flash").jump()<cr>`.
|
||||
> **DO NOT** use `:lua`, since that will break **_dot-repeat_**
|
||||
|
||||
## ⚙️ Configuration
|
||||
|
||||
**flash.nvim** is highly configurable. Please refer to the default settings below.
|
||||
|
||||
<details><summary>Default Settings</summary>
|
||||
|
||||
<!-- config:start -->
|
||||
|
||||
```lua
|
||||
{
|
||||
-- labels = "abcdefghijklmnopqrstuvwxyz",
|
||||
labels = "asdfghjklqwertyuiopzxcvbnm",
|
||||
search = {
|
||||
-- search/jump in all windows
|
||||
multi_window = true,
|
||||
-- search direction
|
||||
forward = true,
|
||||
-- when `false`, find only matches in the given direction
|
||||
wrap = true,
|
||||
---@type Flash.Pattern.Mode
|
||||
-- Each mode will take ignorecase and smartcase into account.
|
||||
-- * exact: exact match
|
||||
-- * search: regular search
|
||||
-- * fuzzy: fuzzy search
|
||||
-- * fun(str): custom function that returns a pattern
|
||||
-- For example, to only match at the beginning of a word:
|
||||
-- mode = function(str)
|
||||
-- return "\\<" .. str
|
||||
-- end,
|
||||
mode = "exact",
|
||||
-- behave like `incsearch`
|
||||
incremental = false,
|
||||
-- Excluded filetypes and custom window filters
|
||||
---@type (string|fun(win:window))[]
|
||||
exclude = {
|
||||
"notify",
|
||||
"cmp_menu",
|
||||
"noice",
|
||||
"flash_prompt",
|
||||
function(win)
|
||||
-- exclude non-focusable windows
|
||||
return not vim.api.nvim_win_get_config(win).focusable
|
||||
end,
|
||||
},
|
||||
-- Optional trigger character that needs to be typed before
|
||||
-- a jump label can be used. It's NOT recommended to set this,
|
||||
-- unless you know what you're doing
|
||||
trigger = "",
|
||||
-- max pattern length. If the pattern length is equal to this
|
||||
-- labels will no longer be skipped. When it exceeds this length
|
||||
-- it will either end in a jump or terminate the search
|
||||
max_length = false, ---@type number|false
|
||||
},
|
||||
jump = {
|
||||
-- save location in the jumplist
|
||||
jumplist = true,
|
||||
-- jump position
|
||||
pos = "start", ---@type "start" | "end" | "range"
|
||||
-- add pattern to search history
|
||||
history = false,
|
||||
-- add pattern to search register
|
||||
register = false,
|
||||
-- clear highlight after jump
|
||||
nohlsearch = false,
|
||||
-- automatically jump when there is only one match
|
||||
autojump = false,
|
||||
-- You can force inclusive/exclusive jumps by setting the
|
||||
-- `inclusive` option. By default it will be automatically
|
||||
-- set based on the mode.
|
||||
inclusive = nil, ---@type boolean?
|
||||
-- jump position offset. Not used for range jumps.
|
||||
-- 0: default
|
||||
-- 1: when pos == "end" and pos < current position
|
||||
offset = nil, ---@type number
|
||||
},
|
||||
label = {
|
||||
-- allow uppercase labels
|
||||
uppercase = true,
|
||||
-- add any labels with the correct case here, that you want to exclude
|
||||
exclude = "",
|
||||
-- add a label for the first match in the current window.
|
||||
-- you can always jump to the first match with `<CR>`
|
||||
current = true,
|
||||
-- show the label after the match
|
||||
after = true, ---@type boolean|number[]
|
||||
-- show the label before the match
|
||||
before = false, ---@type boolean|number[]
|
||||
-- position of the label extmark
|
||||
style = "overlay", ---@type "eol" | "overlay" | "right_align" | "inline"
|
||||
-- flash tries to re-use labels that were already assigned to a position,
|
||||
-- when typing more characters. By default only lower-case labels are re-used.
|
||||
reuse = "lowercase", ---@type "lowercase" | "all" | "none"
|
||||
-- for the current window, label targets closer to the cursor first
|
||||
distance = true,
|
||||
-- minimum pattern length to show labels
|
||||
-- Ignored for custom labelers.
|
||||
min_pattern_length = 0,
|
||||
-- Enable this to use rainbow colors to highlight labels
|
||||
-- Can be useful for visualizing Treesitter ranges.
|
||||
rainbow = {
|
||||
enabled = false,
|
||||
-- number between 1 and 9
|
||||
shade = 5,
|
||||
},
|
||||
-- With `format`, you can change how the label is rendered.
|
||||
-- Should return a list of `[text, highlight]` tuples.
|
||||
---@class Flash.Format
|
||||
---@field state Flash.State
|
||||
---@field match Flash.Match
|
||||
---@field hl_group string
|
||||
---@field after boolean
|
||||
---@type fun(opts:Flash.Format): string[][]
|
||||
format = function(opts)
|
||||
return { { opts.match.label, opts.hl_group } }
|
||||
end,
|
||||
},
|
||||
highlight = {
|
||||
-- show a backdrop with hl FlashBackdrop
|
||||
backdrop = true,
|
||||
-- Highlight the search matches
|
||||
matches = true,
|
||||
-- extmark priority
|
||||
priority = 5000,
|
||||
groups = {
|
||||
match = "FlashMatch",
|
||||
current = "FlashCurrent",
|
||||
backdrop = "FlashBackdrop",
|
||||
label = "FlashLabel",
|
||||
},
|
||||
},
|
||||
-- action to perform when picking a label.
|
||||
-- defaults to the jumping logic depending on the mode.
|
||||
---@type fun(match:Flash.Match, state:Flash.State)|nil
|
||||
action = nil,
|
||||
-- initial pattern to use when opening flash
|
||||
pattern = "",
|
||||
-- When `true`, flash will try to continue the last search
|
||||
continue = false,
|
||||
-- Set config to a function to dynamically change the config
|
||||
config = nil, ---@type fun(opts:Flash.Config)|nil
|
||||
-- You can override the default options for a specific mode.
|
||||
-- Use it with `require("flash").jump({mode = "forward"})`
|
||||
---@type table<string, Flash.Config>
|
||||
modes = {
|
||||
-- options used when flash is activated through
|
||||
-- a regular search with `/` or `?`
|
||||
search = {
|
||||
-- when `true`, flash will be activated during regular search by default.
|
||||
-- You can always toggle when searching with `require("flash").toggle()`
|
||||
enabled = false,
|
||||
highlight = { backdrop = false },
|
||||
jump = { history = true, register = true, nohlsearch = true },
|
||||
search = {
|
||||
-- `forward` will be automatically set to the search direction
|
||||
-- `mode` is always set to `search`
|
||||
-- `incremental` is set to `true` when `incsearch` is enabled
|
||||
},
|
||||
},
|
||||
-- options used when flash is activated through
|
||||
-- `f`, `F`, `t`, `T`, `;` and `,` motions
|
||||
char = {
|
||||
enabled = true,
|
||||
-- dynamic configuration for ftFT motions
|
||||
config = function(opts)
|
||||
-- autohide flash when in operator-pending mode
|
||||
opts.autohide = opts.autohide or (vim.fn.mode(true):find("no") and vim.v.operator == "y")
|
||||
|
||||
-- disable jump labels when not enabled, when using a count,
|
||||
-- or when recording/executing registers
|
||||
opts.jump_labels = opts.jump_labels
|
||||
and vim.v.count == 0
|
||||
and vim.fn.reg_executing() == ""
|
||||
and vim.fn.reg_recording() == ""
|
||||
|
||||
-- Show jump labels only in operator-pending mode
|
||||
-- opts.jump_labels = vim.v.count == 0 and vim.fn.mode(true):find("o")
|
||||
end,
|
||||
-- hide after jump when not using jump labels
|
||||
autohide = false,
|
||||
-- show jump labels
|
||||
jump_labels = false,
|
||||
-- set to `false` to use the current line only
|
||||
multi_line = true,
|
||||
-- When using jump labels, don't use these keys
|
||||
-- This allows using those keys directly after the motion
|
||||
label = { exclude = "hjkliardc" },
|
||||
-- by default all keymaps are enabled, but you can disable some of them,
|
||||
-- by removing them from the list.
|
||||
-- If you rather use another key, you can map them
|
||||
-- to something else, e.g., { [";"] = "L", [","] = H }
|
||||
keys = { "f", "F", "t", "T", ";", "," },
|
||||
---@alias Flash.CharActions table<string, "next" | "prev" | "right" | "left">
|
||||
-- The direction for `prev` and `next` is determined by the motion.
|
||||
-- `left` and `right` are always left and right.
|
||||
char_actions = function(motion)
|
||||
return {
|
||||
[";"] = "next", -- set to `right` to always go right
|
||||
[","] = "prev", -- set to `left` to always go left
|
||||
-- clever-f style
|
||||
[motion:lower()] = "next",
|
||||
[motion:upper()] = "prev",
|
||||
-- jump2d style: same case goes next, opposite case goes prev
|
||||
-- [motion] = "next",
|
||||
-- [motion:match("%l") and motion:upper() or motion:lower()] = "prev",
|
||||
}
|
||||
end,
|
||||
search = { wrap = false },
|
||||
highlight = { backdrop = true },
|
||||
jump = { register = false },
|
||||
},
|
||||
-- options used for treesitter selections
|
||||
-- `require("flash").treesitter()`
|
||||
treesitter = {
|
||||
labels = "abcdefghijklmnopqrstuvwxyz",
|
||||
jump = { pos = "range" },
|
||||
search = { incremental = false },
|
||||
label = { before = true, after = true, style = "inline" },
|
||||
highlight = {
|
||||
backdrop = false,
|
||||
matches = false,
|
||||
},
|
||||
},
|
||||
treesitter_search = {
|
||||
jump = { pos = "range" },
|
||||
search = { multi_window = true, wrap = true, incremental = false },
|
||||
remote_op = { restore = true },
|
||||
label = { before = true, after = true, style = "inline" },
|
||||
},
|
||||
-- options used for remote flash
|
||||
remote = {
|
||||
remote_op = { restore = true, motion = true },
|
||||
},
|
||||
},
|
||||
-- options for the floating window that shows the prompt,
|
||||
-- for regular jumps
|
||||
prompt = {
|
||||
enabled = true,
|
||||
prefix = { { "⚡", "FlashPromptIcon" } },
|
||||
win_config = {
|
||||
relative = "editor",
|
||||
width = 1, -- when <=1 it's a percentage of the editor width
|
||||
height = 1,
|
||||
row = -1, -- when negative it's an offset from the bottom
|
||||
col = 0, -- when negative it's an offset from the right
|
||||
zindex = 1000,
|
||||
},
|
||||
},
|
||||
-- options for remote operator pending mode
|
||||
remote_op = {
|
||||
-- restore window views and cursor position
|
||||
-- after doing a remote operation
|
||||
restore = false,
|
||||
-- For `jump.pos = "range"`, this setting is ignored.
|
||||
-- `true`: always enter a new motion when doing a remote operation
|
||||
-- `false`: use the window's cursor position and jump target
|
||||
-- `nil`: act as `true` for remote windows, `false` for the current window
|
||||
motion = false,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
<!-- config:end -->
|
||||
|
||||
</details>
|
||||
|
||||
## 🚀 Usage
|
||||
|
||||
- **Treesitter**: `require("flash").treesitter(opts?)` opens **flash** in **Treesitter** mode
|
||||
- use a jump label, or use `;` and `,` to increase/decrease the selection
|
||||
- **regular search**: search as you normally do, but enhanced with jump labels.
|
||||
You need to set `opts.modes.search.enabled = true`, or toggle it with `require("flash").toggle()`
|
||||
- `f`, `t`, `F`, `T` motions:
|
||||
- After typing `f{char}` or `F{char},` you can repeat the motion with `f`
|
||||
or go to the previous match with `F` to undo a jump.
|
||||
- Similarly, after typing `t{char}` or `T{char},` you can repeat the motion
|
||||
with `t` or go to the previous match with `T`.
|
||||
- You can also go to the next match with `;` or previous match with `,`
|
||||
- Any highlights clear automatically when moving, changing buffers,
|
||||
or pressing `<esc>`.
|
||||
- **toggle Search**: `require("flash").toggle(boolean?)`
|
||||
- toggles **flash** on or off while using regular search
|
||||
- **Treesitter Search**: `require("flash").treesitter_search(opts?)` opens **flash** in **Treesitter Search** mode
|
||||
- combination of **Treesitter** and **Search** modes
|
||||
- do something like `yR`
|
||||
- you can now start typing a search pattern.
|
||||
- arround your matches, all the surrounding Treesitter nodes will be labeled.
|
||||
- select a label to perform the operator on the new selection
|
||||
- **remote**: `require("flash").remote(opts?)` opens **flash** in **remote** mode
|
||||
|
||||
- equivalent to:
|
||||
|
||||
```lua
|
||||
require("flash").jump({
|
||||
remote_op = {
|
||||
restore = true,
|
||||
motion = true,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
- this is only useful in operator pending mode.
|
||||
- For example, press `yr` to start yanking and open flash
|
||||
- select a label to set the cursor position
|
||||
- perform any motion, like `iw` or even start flash Treesitter with `S`
|
||||
- the yank will be performed on the new selection
|
||||
- you'll be back in the original window / position
|
||||
- You can also configure the `remote_op` options by default, so that `ys`,
|
||||
behaves like `yr` for remote operations
|
||||
|
||||
```lua
|
||||
require("flash").jump({
|
||||
remote_op = {
|
||||
restore = true,
|
||||
motion = nil,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
- **jump**: `require("flash").jump(opts?)` opens **flash** with the given options
|
||||
- type any number of characters before typing a jump label
|
||||
- **VS Code**: some functionality is changed/disabled when running **flash** in **VS Code**:
|
||||
- `prompt` is disabled
|
||||
- `highlights` are set to different defaults that will actually work in VS Code
|
||||
|
||||
## 📡 API
|
||||
|
||||
The options for `require("flash").jump(opts?)`, are the same as
|
||||
those in the config section, but can additionally have the following fields:
|
||||
|
||||
- `matcher`: a custom function that generates matches for a given window
|
||||
- `labeler`: a custom function to label matches
|
||||
|
||||
You can also add labels in the `matcher` function and then set `labeler`
|
||||
to an empty function `labeler = function() end`
|
||||
|
||||
<details><summary>Type Definitions</summary>
|
||||
|
||||
```typescript
|
||||
type FlashMatcher = (win: number, state: FlashState) => FlashMatch[];
|
||||
type FlashLabeler = (matches: FlashMatch[], state: FlashState) => void;
|
||||
|
||||
interface FlashMatch {
|
||||
win: number;
|
||||
pos: [number, number]; // (1,0)-indexed
|
||||
end_pos: [number, number]; // (1,0)-indexed
|
||||
label?: string | false; // set to false to never show a label for this match
|
||||
highlight?: boolean; // override opts.highlight.matches for this match
|
||||
}
|
||||
|
||||
// Check the code for the full definition
|
||||
// of Flash.State at `lua/flash/state.lua`
|
||||
type FlashState = {};
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## 💡 Examples
|
||||
|
||||
<details><summary>Forward search only</summary>
|
||||
|
||||
```lua
|
||||
require("flash").jump({
|
||||
search = { forward = true, wrap = false, multi_window = false },
|
||||
})
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>Backward search only</summary>
|
||||
|
||||
```lua
|
||||
require("flash").jump({
|
||||
search = { forward = false, wrap = false, multi_window = false },
|
||||
})
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>Show diagnostics at target, without changing cursor position</summary>
|
||||
|
||||
```lua
|
||||
require("flash").jump({
|
||||
action = function(match, state)
|
||||
vim.api.nvim_win_call(match.win, function()
|
||||
vim.api.nvim_win_set_cursor(match.win, match.pos)
|
||||
vim.diagnostic.open_float()
|
||||
end)
|
||||
state:restore()
|
||||
end,
|
||||
})
|
||||
|
||||
-- More advanced example that also highlights diagnostics:
|
||||
require("flash").jump({
|
||||
matcher = function(win)
|
||||
---@param diag Diagnostic
|
||||
return vim.tbl_map(function(diag)
|
||||
return {
|
||||
pos = { diag.lnum + 1, diag.col },
|
||||
end_pos = { diag.end_lnum + 1, diag.end_col - 1 },
|
||||
}
|
||||
end, vim.diagnostic.get(vim.api.nvim_win_get_buf(win)))
|
||||
end,
|
||||
action = function(match, state)
|
||||
vim.api.nvim_win_call(match.win, function()
|
||||
vim.api.nvim_win_set_cursor(match.win, match.pos)
|
||||
vim.diagnostic.open_float()
|
||||
end)
|
||||
state:restore()
|
||||
end,
|
||||
})
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>Match beginning of words only</summary>
|
||||
|
||||
```lua
|
||||
require("flash").jump({
|
||||
search = {
|
||||
mode = function(str)
|
||||
return "\\<" .. str
|
||||
end,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>Initialize flash with the word under the cursor</summary>
|
||||
|
||||
```lua
|
||||
require("flash").jump({
|
||||
pattern = vim.fn.expand("<cword>"),
|
||||
})
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>Jump to a line</summary>
|
||||
|
||||
```lua
|
||||
require("flash").jump({
|
||||
search = { mode = "search", max_length = 0 },
|
||||
label = { after = { 0, 0 } },
|
||||
pattern = "^"
|
||||
})
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>Select any word</summary>
|
||||
|
||||
```lua
|
||||
require("flash").jump({
|
||||
pattern = ".", -- initialize pattern with any char
|
||||
search = {
|
||||
mode = function(pattern)
|
||||
-- remove leading dot
|
||||
if pattern:sub(1, 1) == "." then
|
||||
pattern = pattern:sub(2)
|
||||
end
|
||||
-- return word pattern and proper skip pattern
|
||||
return ([[\<%s\w*\>]]):format(pattern), ([[\<%s]]):format(pattern)
|
||||
end,
|
||||
},
|
||||
-- select the range
|
||||
jump = { pos = "range" },
|
||||
})
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary><code>f</code>, <code>t</code>, <code>F</code>, <code>T</code> with labels</summary>
|
||||
|
||||
Use the options below:
|
||||
|
||||
```lua
|
||||
{
|
||||
modes = {
|
||||
char = {
|
||||
jump_labels = true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>Telescope integration</summary>
|
||||
|
||||
This will allow you to use `s` in normal mode
|
||||
and `<c-s>` in insert mode, to jump to a label in Telescope results.
|
||||
|
||||
```lua
|
||||
{
|
||||
"nvim-telescope/telescope.nvim",
|
||||
optional = true,
|
||||
opts = function(_, opts)
|
||||
local function flash(prompt_bufnr)
|
||||
require("flash").jump({
|
||||
pattern = "^",
|
||||
label = { after = { 0, 0 } },
|
||||
search = {
|
||||
mode = "search",
|
||||
exclude = {
|
||||
function(win)
|
||||
return vim.bo[vim.api.nvim_win_get_buf(win)].filetype ~= "TelescopeResults"
|
||||
end,
|
||||
},
|
||||
},
|
||||
action = function(match)
|
||||
local picker = require("telescope.actions.state").get_current_picker(prompt_bufnr)
|
||||
picker:set_selection(match.pos[1] - 1)
|
||||
end,
|
||||
})
|
||||
end
|
||||
opts.defaults = vim.tbl_deep_extend("force", opts.defaults or {}, {
|
||||
mappings = {
|
||||
n = { s = flash },
|
||||
i = { ["<c-s>"] = flash },
|
||||
},
|
||||
})
|
||||
end,
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>Continue last search</summary>
|
||||
|
||||
```lua
|
||||
require("flash").jump({continue = true})
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>2-char jump, similar to
|
||||
<a href="https://github.com/echasnovski/mini.nvim/blob/main/readmes/mini-jump2d.md">
|
||||
mini.jump2d
|
||||
</a>
|
||||
or
|
||||
<a href="https://github.com/phaazon/hop.nvim">
|
||||
HopWord (hop.nvim)
|
||||
</a>
|
||||
</summary>
|
||||
|
||||
```lua
|
||||
local Flash = require("flash")
|
||||
|
||||
---@param opts Flash.Format
|
||||
local function format(opts)
|
||||
-- always show first and second label
|
||||
return {
|
||||
{ opts.match.label1, "FlashMatch" },
|
||||
{ opts.match.label2, "FlashLabel" },
|
||||
}
|
||||
end
|
||||
|
||||
Flash.jump({
|
||||
search = { mode = "search" },
|
||||
label = { after = false, before = { 0, 0 }, uppercase = false, format = format },
|
||||
pattern = [[\<]],
|
||||
action = function(match, state)
|
||||
state:hide()
|
||||
Flash.jump({
|
||||
search = { max_length = 0 },
|
||||
highlight = { matches = false },
|
||||
label = { format = format },
|
||||
matcher = function(win)
|
||||
-- limit matches to the current label
|
||||
return vim.tbl_filter(function(m)
|
||||
return m.label == match.label and m.win == win
|
||||
end, state.results)
|
||||
end,
|
||||
labeler = function(matches)
|
||||
for _, m in ipairs(matches) do
|
||||
m.label = m.label2 -- use the second label
|
||||
end
|
||||
end,
|
||||
})
|
||||
end,
|
||||
labeler = function(matches, state)
|
||||
local labels = state:labels()
|
||||
for m, match in ipairs(matches) do
|
||||
match.label1 = labels[math.floor((m - 1) / #labels) + 1]
|
||||
match.label2 = labels[(m - 1) % #labels + 1]
|
||||
match.label = match.label1
|
||||
end
|
||||
end,
|
||||
})
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## 🌈 Highlights
|
||||
|
||||
| Group | Default | Description |
|
||||
| ----------------- | ------------ | -------------- |
|
||||
| `FlashBackdrop` | `Comment` | backdrop |
|
||||
| `FlashMatch` | `Search` | search matches |
|
||||
| `FlashCurrent` | `IncSearch` | current match |
|
||||
| `FlashLabel` | `Substitute` | jump label |
|
||||
| `FlashPrompt` | `MsgArea` | prompt |
|
||||
| `FlashPromptIcon` | `Special` | prompt icon |
|
||||
| `FlashCursor` | `Cursor` | cursor |
|
||||
|
||||
## 📦 Alternatives
|
||||
|
||||
- [leap.nvim](https://github.com/ggandor/leap.nvim)
|
||||
- [lightspeed.nvim](https://github.com/ggandor/lightspeed.nvim)
|
||||
- [vim-sneak](https://github.com/justinmk/vim-sneak)
|
||||
- [mini.jump](https://github.com/echasnovski/mini.nvim/blob/main/readmes/mini-jump.md)
|
||||
- [mini.jump2d](https://github.com/echasnovski/mini.nvim/blob/main/readmes/mini-jump2d.md)
|
||||
- [hop.nvim](https://github.com/phaazon/hop.nvim)
|
||||
- [pounce.nvim](https://github.com/rlane/pounce.nvim)
|
||||
- [sj.nvim](https://github.com/woosaaahh/sj.nvim)
|
||||
- [nvim-treehopper](https://github.com/mfussenegger/nvim-treehopper)
|
||||
- [flit.nvim](https://github.com/ggandor/flit.nvim)
|
||||
671
config/neovim/store/lazy-plugins/flash.nvim/doc/flash.nvim.txt
Normal file
671
config/neovim/store/lazy-plugins/flash.nvim/doc/flash.nvim.txt
Normal file
@ -0,0 +1,671 @@
|
||||
*flash.nvim.txt* For Neovim >= 0.8.0 Last change: 2024 May 14
|
||||
|
||||
==============================================================================
|
||||
Table of Contents *flash.nvim-table-of-contents*
|
||||
|
||||
1. flash.nvim |flash.nvim-flash.nvim|
|
||||
- Features |flash.nvim-flash.nvim-features|
|
||||
- Requirements |flash.nvim-flash.nvim-requirements|
|
||||
- Installation |flash.nvim-flash.nvim-installation|
|
||||
- Configuration |flash.nvim-flash.nvim-configuration|
|
||||
- Usage |flash.nvim-flash.nvim-usage|
|
||||
- API |flash.nvim-flash.nvim-api|
|
||||
- Examples |flash.nvim-flash.nvim-examples|
|
||||
- Highlights |flash.nvim-flash.nvim-highlights|
|
||||
- Alternatives |flash.nvim-flash.nvim-alternatives|
|
||||
|
||||
==============================================================================
|
||||
1. flash.nvim *flash.nvim-flash.nvim*
|
||||
|
||||
`flash.nvim` lets you navigate your code with search labels, enhanced character
|
||||
motions, and Treesitter integration.
|
||||
|
||||
Search IntegrationStandalone Jumpf, t, F, TTreesitter
|
||||
FEATURES *flash.nvim-flash.nvim-features*
|
||||
|
||||
- **Search Integration**integrate **flash.nvim** with your regular
|
||||
search using `/` or `?`. Labels appear next to the matches,
|
||||
allowing you to quickly jump to any location. Labels are
|
||||
guaranteed not to exist as a continuation of the search pattern.
|
||||
- **type as many characters as you want** before using a jump label.
|
||||
- **Enhanced f, t, F, T motions**
|
||||
- **Treesitter Integration**all parents of the Treesitter node
|
||||
under your cursor are highlighted with a label for quick selection
|
||||
of a specific Treesitter node.
|
||||
- **Jump Mode**a standalone jumping mode similar to search
|
||||
- **Search Modes**`exact`, `search` (regex), and `fuzzy` search modes
|
||||
- **Multi Window** jumping
|
||||
- **Remote Actions**perform motions in remote locations
|
||||
- **dot-repeatable** jumps
|
||||
- **highly extensible**check the examples <https://github.com/folke/flash.nvim#-examples>
|
||||
|
||||
|
||||
REQUIREMENTS *flash.nvim-flash.nvim-requirements*
|
||||
|
||||
- Neovim >= **0.8.0** (needs to be built with **LuaJIT**)
|
||||
|
||||
|
||||
INSTALLATION *flash.nvim-flash.nvim-installation*
|
||||
|
||||
Install the plugin with your preferred package manager:
|
||||
|
||||
lazy.nvim <https://github.com/folke/lazy.nvim>
|
||||
|
||||
>lua
|
||||
{
|
||||
"folke/flash.nvim",
|
||||
event = "VeryLazy",
|
||||
---@type Flash.Config
|
||||
opts = {},
|
||||
-- stylua: ignore
|
||||
keys = {
|
||||
{ "s", mode = { "n", "x", "o" }, function() require("flash").jump() end, desc = "Flash" },
|
||||
{ "S", mode = { "n", "x", "o" }, function() require("flash").treesitter() end, desc = "Flash Treesitter" },
|
||||
{ "r", mode = "o", function() require("flash").remote() end, desc = "Remote Flash" },
|
||||
{ "R", mode = { "o", "x" }, function() require("flash").treesitter_search() end, desc = "Treesitter Search" },
|
||||
{ "<c-s>", mode = { "c" }, function() require("flash").toggle() end, desc = "Toggle Flash Search" },
|
||||
},
|
||||
}
|
||||
<
|
||||
|
||||
|
||||
When creating the keymaps manually either use a lua function like `function()
|
||||
require("flash").jump() end` as the **rhs**, or a string like `<cmd>lua
|
||||
require("flash").jump()<cr>`. **DO NOT** use `:lua`, since that will break
|
||||
**dot-repeat**
|
||||
|
||||
CONFIGURATION *flash.nvim-flash.nvim-configuration*
|
||||
|
||||
**flash.nvim** is highly configurable. Please refer to the default settings
|
||||
below.
|
||||
|
||||
Default Settings ~
|
||||
|
||||
>lua
|
||||
{
|
||||
-- labels = "abcdefghijklmnopqrstuvwxyz",
|
||||
labels = "asdfghjklqwertyuiopzxcvbnm",
|
||||
search = {
|
||||
-- search/jump in all windows
|
||||
multi_window = true,
|
||||
-- search direction
|
||||
forward = true,
|
||||
-- when `false`, find only matches in the given direction
|
||||
wrap = true,
|
||||
---@type Flash.Pattern.Mode
|
||||
-- Each mode will take ignorecase and smartcase into account.
|
||||
-- * exact: exact match
|
||||
-- * search: regular search
|
||||
-- * fuzzy: fuzzy search
|
||||
-- * fun(str): custom function that returns a pattern
|
||||
-- For example, to only match at the beginning of a word:
|
||||
-- mode = function(str)
|
||||
-- return "\\<" .. str
|
||||
-- end,
|
||||
mode = "exact",
|
||||
-- behave like `incsearch`
|
||||
incremental = false,
|
||||
-- Excluded filetypes and custom window filters
|
||||
---@type (string|fun(win:window))[]
|
||||
exclude = {
|
||||
"notify",
|
||||
"cmp_menu",
|
||||
"noice",
|
||||
"flash_prompt",
|
||||
function(win)
|
||||
-- exclude non-focusable windows
|
||||
return not vim.api.nvim_win_get_config(win).focusable
|
||||
end,
|
||||
},
|
||||
-- Optional trigger character that needs to be typed before
|
||||
-- a jump label can be used. It's NOT recommended to set this,
|
||||
-- unless you know what you're doing
|
||||
trigger = "",
|
||||
-- max pattern length. If the pattern length is equal to this
|
||||
-- labels will no longer be skipped. When it exceeds this length
|
||||
-- it will either end in a jump or terminate the search
|
||||
max_length = false, ---@type number|false
|
||||
},
|
||||
jump = {
|
||||
-- save location in the jumplist
|
||||
jumplist = true,
|
||||
-- jump position
|
||||
pos = "start", ---@type "start" | "end" | "range"
|
||||
-- add pattern to search history
|
||||
history = false,
|
||||
-- add pattern to search register
|
||||
register = false,
|
||||
-- clear highlight after jump
|
||||
nohlsearch = false,
|
||||
-- automatically jump when there is only one match
|
||||
autojump = false,
|
||||
-- You can force inclusive/exclusive jumps by setting the
|
||||
-- `inclusive` option. By default it will be automatically
|
||||
-- set based on the mode.
|
||||
inclusive = nil, ---@type boolean?
|
||||
-- jump position offset. Not used for range jumps.
|
||||
-- 0: default
|
||||
-- 1: when pos == "end" and pos < current position
|
||||
offset = nil, ---@type number
|
||||
},
|
||||
label = {
|
||||
-- allow uppercase labels
|
||||
uppercase = true,
|
||||
-- add any labels with the correct case here, that you want to exclude
|
||||
exclude = "",
|
||||
-- add a label for the first match in the current window.
|
||||
-- you can always jump to the first match with `<CR>`
|
||||
current = true,
|
||||
-- show the label after the match
|
||||
after = true, ---@type boolean|number[]
|
||||
-- show the label before the match
|
||||
before = false, ---@type boolean|number[]
|
||||
-- position of the label extmark
|
||||
style = "overlay", ---@type "eol" | "overlay" | "right_align" | "inline"
|
||||
-- flash tries to re-use labels that were already assigned to a position,
|
||||
-- when typing more characters. By default only lower-case labels are re-used.
|
||||
reuse = "lowercase", ---@type "lowercase" | "all" | "none"
|
||||
-- for the current window, label targets closer to the cursor first
|
||||
distance = true,
|
||||
-- minimum pattern length to show labels
|
||||
-- Ignored for custom labelers.
|
||||
min_pattern_length = 0,
|
||||
-- Enable this to use rainbow colors to highlight labels
|
||||
-- Can be useful for visualizing Treesitter ranges.
|
||||
rainbow = {
|
||||
enabled = false,
|
||||
-- number between 1 and 9
|
||||
shade = 5,
|
||||
},
|
||||
-- With `format`, you can change how the label is rendered.
|
||||
-- Should return a list of `[text, highlight]` tuples.
|
||||
---@class Flash.Format
|
||||
---@field state Flash.State
|
||||
---@field match Flash.Match
|
||||
---@field hl_group string
|
||||
---@field after boolean
|
||||
---@type fun(opts:Flash.Format): string[][]
|
||||
format = function(opts)
|
||||
return { { opts.match.label, opts.hl_group } }
|
||||
end,
|
||||
},
|
||||
highlight = {
|
||||
-- show a backdrop with hl FlashBackdrop
|
||||
backdrop = true,
|
||||
-- Highlight the search matches
|
||||
matches = true,
|
||||
-- extmark priority
|
||||
priority = 5000,
|
||||
groups = {
|
||||
match = "FlashMatch",
|
||||
current = "FlashCurrent",
|
||||
backdrop = "FlashBackdrop",
|
||||
label = "FlashLabel",
|
||||
},
|
||||
},
|
||||
-- action to perform when picking a label.
|
||||
-- defaults to the jumping logic depending on the mode.
|
||||
---@type fun(match:Flash.Match, state:Flash.State)|nil
|
||||
action = nil,
|
||||
-- initial pattern to use when opening flash
|
||||
pattern = "",
|
||||
-- When `true`, flash will try to continue the last search
|
||||
continue = false,
|
||||
-- Set config to a function to dynamically change the config
|
||||
config = nil, ---@type fun(opts:Flash.Config)|nil
|
||||
-- You can override the default options for a specific mode.
|
||||
-- Use it with `require("flash").jump({mode = "forward"})`
|
||||
---@type table<string, Flash.Config>
|
||||
modes = {
|
||||
-- options used when flash is activated through
|
||||
-- a regular search with `/` or `?`
|
||||
search = {
|
||||
-- when `true`, flash will be activated during regular search by default.
|
||||
-- You can always toggle when searching with `require("flash").toggle()`
|
||||
enabled = false,
|
||||
highlight = { backdrop = false },
|
||||
jump = { history = true, register = true, nohlsearch = true },
|
||||
search = {
|
||||
-- `forward` will be automatically set to the search direction
|
||||
-- `mode` is always set to `search`
|
||||
-- `incremental` is set to `true` when `incsearch` is enabled
|
||||
},
|
||||
},
|
||||
-- options used when flash is activated through
|
||||
-- `f`, `F`, `t`, `T`, `;` and `,` motions
|
||||
char = {
|
||||
enabled = true,
|
||||
-- dynamic configuration for ftFT motions
|
||||
config = function(opts)
|
||||
-- autohide flash when in operator-pending mode
|
||||
opts.autohide = opts.autohide or (vim.fn.mode(true):find("no") and vim.v.operator == "y")
|
||||
|
||||
-- disable jump labels when not enabled, when using a count,
|
||||
-- or when recording/executing registers
|
||||
opts.jump_labels = opts.jump_labels
|
||||
and vim.v.count == 0
|
||||
and vim.fn.reg_executing() == ""
|
||||
and vim.fn.reg_recording() == ""
|
||||
|
||||
-- Show jump labels only in operator-pending mode
|
||||
-- opts.jump_labels = vim.v.count == 0 and vim.fn.mode(true):find("o")
|
||||
end,
|
||||
-- hide after jump when not using jump labels
|
||||
autohide = false,
|
||||
-- show jump labels
|
||||
jump_labels = false,
|
||||
-- set to `false` to use the current line only
|
||||
multi_line = true,
|
||||
-- When using jump labels, don't use these keys
|
||||
-- This allows using those keys directly after the motion
|
||||
label = { exclude = "hjkliardc" },
|
||||
-- by default all keymaps are enabled, but you can disable some of them,
|
||||
-- by removing them from the list.
|
||||
-- If you rather use another key, you can map them
|
||||
-- to something else, e.g., { [";"] = "L", [","] = H }
|
||||
keys = { "f", "F", "t", "T", ";", "," },
|
||||
---@alias Flash.CharActions table<string, "next" | "prev" | "right" | "left">
|
||||
-- The direction for `prev` and `next` is determined by the motion.
|
||||
-- `left` and `right` are always left and right.
|
||||
char_actions = function(motion)
|
||||
return {
|
||||
[";"] = "next", -- set to `right` to always go right
|
||||
[","] = "prev", -- set to `left` to always go left
|
||||
-- clever-f style
|
||||
[motion:lower()] = "next",
|
||||
[motion:upper()] = "prev",
|
||||
-- jump2d style: same case goes next, opposite case goes prev
|
||||
-- [motion] = "next",
|
||||
-- [motion:match("%l") and motion:upper() or motion:lower()] = "prev",
|
||||
}
|
||||
end,
|
||||
search = { wrap = false },
|
||||
highlight = { backdrop = true },
|
||||
jump = { register = false },
|
||||
},
|
||||
-- options used for treesitter selections
|
||||
-- `require("flash").treesitter()`
|
||||
treesitter = {
|
||||
labels = "abcdefghijklmnopqrstuvwxyz",
|
||||
jump = { pos = "range" },
|
||||
search = { incremental = false },
|
||||
label = { before = true, after = true, style = "inline" },
|
||||
highlight = {
|
||||
backdrop = false,
|
||||
matches = false,
|
||||
},
|
||||
},
|
||||
treesitter_search = {
|
||||
jump = { pos = "range" },
|
||||
search = { multi_window = true, wrap = true, incremental = false },
|
||||
remote_op = { restore = true },
|
||||
label = { before = true, after = true, style = "inline" },
|
||||
},
|
||||
-- options used for remote flash
|
||||
remote = {
|
||||
remote_op = { restore = true, motion = true },
|
||||
},
|
||||
},
|
||||
-- options for the floating window that shows the prompt,
|
||||
-- for regular jumps
|
||||
prompt = {
|
||||
enabled = true,
|
||||
prefix = { { "⚡", "FlashPromptIcon" } },
|
||||
win_config = {
|
||||
relative = "editor",
|
||||
width = 1, -- when <=1 it's a percentage of the editor width
|
||||
height = 1,
|
||||
row = -1, -- when negative it's an offset from the bottom
|
||||
col = 0, -- when negative it's an offset from the right
|
||||
zindex = 1000,
|
||||
},
|
||||
},
|
||||
-- options for remote operator pending mode
|
||||
remote_op = {
|
||||
-- restore window views and cursor position
|
||||
-- after doing a remote operation
|
||||
restore = false,
|
||||
-- For `jump.pos = "range"`, this setting is ignored.
|
||||
-- `true`: always enter a new motion when doing a remote operation
|
||||
-- `false`: use the window's cursor position and jump target
|
||||
-- `nil`: act as `true` for remote windows, `false` for the current window
|
||||
motion = false,
|
||||
},
|
||||
}
|
||||
<
|
||||
|
||||
|
||||
USAGE *flash.nvim-flash.nvim-usage*
|
||||
|
||||
- **Treesitter**`require("flash").treesitter(opts?)` opens **flash** in
|
||||
**Treesitter** mode
|
||||
- use a jump label, or use `;` and `,` to increase/decrease the selection
|
||||
- **regular search**search as you normally do, but enhanced with jump labels. You
|
||||
need to set `opts.modes.search.enabled = true`, or toggle it with
|
||||
`require("flash").toggle()`
|
||||
- `f`, `t`, `F`, `T` motions:
|
||||
- After typing `f{char}` or `F{char},` you can repeat the motion with `f`
|
||||
or go to the previous match with `F` to undo a jump.
|
||||
- Similarly, after typing `t{char}` or `T{char},` you can repeat the motion
|
||||
with `t` or go to the previous match with `T`.
|
||||
- You can also go to the next match with `;` or previous match with `,`
|
||||
- Any highlights clear automatically when moving, changing buffers,
|
||||
or pressing `<esc>`.
|
||||
- **toggle Search**`require("flash").toggle(boolean?)`
|
||||
- toggles **flash** on or off while using regular search
|
||||
- **Treesitter Search**`require("flash").treesitter_search(opts?)` opens
|
||||
**flash** in **Treesitter Search** mode
|
||||
- combination of **Treesitter** and **Search** modes
|
||||
- do something like `yR`
|
||||
- you can now start typing a search pattern.
|
||||
- arround your matches, all the surrounding Treesitter nodes will be labeled.
|
||||
- select a label to perform the operator on the new selection
|
||||
- **remote**`require("flash").remote(opts?)` opens **flash** in **remote** mode
|
||||
- equivalent to:
|
||||
>lua
|
||||
require("flash").jump({
|
||||
remote_op = {
|
||||
restore = true,
|
||||
motion = true,
|
||||
},
|
||||
})
|
||||
<
|
||||
- this is only useful in operator pending mode.
|
||||
- For example, press `yr` to start yanking and open flash
|
||||
- select a label to set the cursor position
|
||||
- perform any motion, like `iw` or even start flash Treesitter with `S`
|
||||
- the yank will be performed on the new selection
|
||||
- you’ll be back in the original window / position
|
||||
- You can also configure the `remote_op` options by default, so that `ys`,
|
||||
behaves like `yr` for remote operations
|
||||
>lua
|
||||
require("flash").jump({
|
||||
remote_op = {
|
||||
restore = true,
|
||||
motion = nil,
|
||||
},
|
||||
})
|
||||
<
|
||||
- **jump**`require("flash").jump(opts?)` opens **flash** with the given options
|
||||
- type any number of characters before typing a jump label
|
||||
- **VS Code**some functionality is changed/disabled when running **flash** in
|
||||
**VS Code**
|
||||
- `prompt`is disabled
|
||||
- `highlights` are set to different defaults that will actually work in VS Code
|
||||
|
||||
|
||||
API *flash.nvim-flash.nvim-api*
|
||||
|
||||
The options for `require("flash").jump(opts?)`, are the same as those in the
|
||||
config section, but can additionally have the following fields:
|
||||
|
||||
- `matcher`a custom function that generates matches for a given window
|
||||
- `labeler`a custom function to label matches
|
||||
|
||||
You can also add labels in the `matcher` function and then set `labeler` to an
|
||||
empty function `labeler = function() end`
|
||||
|
||||
Type Definitions ~
|
||||
|
||||
>typescript
|
||||
type FlashMatcher = (win: number, state: FlashState) => FlashMatch[];
|
||||
type FlashLabeler = (matches: FlashMatch[], state: FlashState) => void;
|
||||
|
||||
interface FlashMatch {
|
||||
win: number;
|
||||
pos: [number, number]; // (1,0)-indexed
|
||||
end_pos: [number, number]; // (1,0)-indexed
|
||||
label?: string | false; // set to false to never show a label for this match
|
||||
highlight?: boolean; // override opts.highlight.matches for this match
|
||||
}
|
||||
|
||||
// Check the code for the full definition
|
||||
// of Flash.State at `lua/flash/state.lua`
|
||||
type FlashState = {};
|
||||
<
|
||||
|
||||
|
||||
EXAMPLES *flash.nvim-flash.nvim-examples*
|
||||
|
||||
Forward search only ~
|
||||
|
||||
>lua
|
||||
require("flash").jump({
|
||||
search = { forward = true, wrap = false, multi_window = false },
|
||||
})
|
||||
<
|
||||
|
||||
Backward search only ~
|
||||
|
||||
>lua
|
||||
require("flash").jump({
|
||||
search = { forward = false, wrap = false, multi_window = false },
|
||||
})
|
||||
<
|
||||
|
||||
Show diagnostics at target, without changing cursor position ~
|
||||
|
||||
>lua
|
||||
require("flash").jump({
|
||||
action = function(match, state)
|
||||
vim.api.nvim_win_call(match.win, function()
|
||||
vim.api.nvim_win_set_cursor(match.win, match.pos)
|
||||
vim.diagnostic.open_float()
|
||||
end)
|
||||
state:restore()
|
||||
end,
|
||||
})
|
||||
|
||||
-- More advanced example that also highlights diagnostics:
|
||||
require("flash").jump({
|
||||
matcher = function(win)
|
||||
---@param diag Diagnostic
|
||||
return vim.tbl_map(function(diag)
|
||||
return {
|
||||
pos = { diag.lnum + 1, diag.col },
|
||||
end_pos = { diag.end_lnum + 1, diag.end_col - 1 },
|
||||
}
|
||||
end, vim.diagnostic.get(vim.api.nvim_win_get_buf(win)))
|
||||
end,
|
||||
action = function(match, state)
|
||||
vim.api.nvim_win_call(match.win, function()
|
||||
vim.api.nvim_win_set_cursor(match.win, match.pos)
|
||||
vim.diagnostic.open_float()
|
||||
end)
|
||||
state:restore()
|
||||
end,
|
||||
})
|
||||
<
|
||||
|
||||
Match beginning of words only ~
|
||||
|
||||
>lua
|
||||
require("flash").jump({
|
||||
search = {
|
||||
mode = function(str)
|
||||
return "\\<" .. str
|
||||
end,
|
||||
},
|
||||
})
|
||||
<
|
||||
|
||||
Initialize flash with the word under the cursor ~
|
||||
|
||||
>lua
|
||||
require("flash").jump({
|
||||
pattern = vim.fn.expand("<cword>"),
|
||||
})
|
||||
<
|
||||
|
||||
Jump to a line ~
|
||||
|
||||
>lua
|
||||
require("flash").jump({
|
||||
search = { mode = "search", max_length = 0 },
|
||||
label = { after = { 0, 0 } },
|
||||
pattern = "^"
|
||||
})
|
||||
<
|
||||
|
||||
Select any word ~
|
||||
|
||||
>lua
|
||||
require("flash").jump({
|
||||
pattern = ".", -- initialize pattern with any char
|
||||
search = {
|
||||
mode = function(pattern)
|
||||
-- remove leading dot
|
||||
if pattern:sub(1, 1) == "." then
|
||||
pattern = pattern:sub(2)
|
||||
end
|
||||
-- return word pattern and proper skip pattern
|
||||
return ([[\<%s\w*\>]]):format(pattern), ([[\<%s]]):format(pattern)
|
||||
end,
|
||||
},
|
||||
-- select the range
|
||||
jump = { pos = "range" },
|
||||
})
|
||||
<
|
||||
|
||||
f, t, F, T with labels ~
|
||||
|
||||
Use the options below:
|
||||
|
||||
>lua
|
||||
{
|
||||
modes = {
|
||||
char = {
|
||||
jump_labels = true
|
||||
}
|
||||
}
|
||||
}
|
||||
<
|
||||
|
||||
Telescope integration ~
|
||||
|
||||
This will allow you to use `s` in normal mode and `<c-s>` in insert mode, to
|
||||
jump to a label in Telescope results.
|
||||
|
||||
>lua
|
||||
{
|
||||
"nvim-telescope/telescope.nvim",
|
||||
optional = true,
|
||||
opts = function(_, opts)
|
||||
local function flash(prompt_bufnr)
|
||||
require("flash").jump({
|
||||
pattern = "^",
|
||||
label = { after = { 0, 0 } },
|
||||
search = {
|
||||
mode = "search",
|
||||
exclude = {
|
||||
function(win)
|
||||
return vim.bo[vim.api.nvim_win_get_buf(win)].filetype ~= "TelescopeResults"
|
||||
end,
|
||||
},
|
||||
},
|
||||
action = function(match)
|
||||
local picker = require("telescope.actions.state").get_current_picker(prompt_bufnr)
|
||||
picker:set_selection(match.pos[1] - 1)
|
||||
end,
|
||||
})
|
||||
end
|
||||
opts.defaults = vim.tbl_deep_extend("force", opts.defaults or {}, {
|
||||
mappings = {
|
||||
n = { s = flash },
|
||||
i = { ["<c-s>"] = flash },
|
||||
},
|
||||
})
|
||||
end,
|
||||
}
|
||||
<
|
||||
|
||||
Continue last search ~
|
||||
|
||||
>lua
|
||||
require("flash").jump({continue = true})
|
||||
<
|
||||
|
||||
2-char jump, similar to
|
||||
|
||||
mini.jump2d
|
||||
|
||||
or
|
||||
|
||||
HopWord (hop.nvim)
|
||||
~
|
||||
|
||||
>lua
|
||||
local Flash = require("flash")
|
||||
|
||||
---@param opts Flash.Format
|
||||
local function format(opts)
|
||||
-- always show first and second label
|
||||
return {
|
||||
{ opts.match.label1, "FlashMatch" },
|
||||
{ opts.match.label2, "FlashLabel" },
|
||||
}
|
||||
end
|
||||
|
||||
Flash.jump({
|
||||
search = { mode = "search" },
|
||||
label = { after = false, before = { 0, 0 }, uppercase = false, format = format },
|
||||
pattern = [[\<]],
|
||||
action = function(match, state)
|
||||
state:hide()
|
||||
Flash.jump({
|
||||
search = { max_length = 0 },
|
||||
highlight = { matches = false },
|
||||
label = { format = format },
|
||||
matcher = function(win)
|
||||
-- limit matches to the current label
|
||||
return vim.tbl_filter(function(m)
|
||||
return m.label == match.label and m.win == win
|
||||
end, state.results)
|
||||
end,
|
||||
labeler = function(matches)
|
||||
for _, m in ipairs(matches) do
|
||||
m.label = m.label2 -- use the second label
|
||||
end
|
||||
end,
|
||||
})
|
||||
end,
|
||||
labeler = function(matches, state)
|
||||
local labels = state:labels()
|
||||
for m, match in ipairs(matches) do
|
||||
match.label1 = labels[math.floor((m - 1) / #labels) + 1]
|
||||
match.label2 = labels[(m - 1) % #labels + 1]
|
||||
match.label = match.label1
|
||||
end
|
||||
end,
|
||||
})
|
||||
<
|
||||
|
||||
|
||||
HIGHLIGHTS *flash.nvim-flash.nvim-highlights*
|
||||
|
||||
Group Default Description
|
||||
----------------- ------------ ----------------
|
||||
FlashBackdrop Comment backdrop
|
||||
FlashMatch Search search matches
|
||||
FlashCurrent IncSearch current match
|
||||
FlashLabel Substitute jump label
|
||||
FlashPrompt MsgArea prompt
|
||||
FlashPromptIcon Special prompt icon
|
||||
FlashCursor Cursor cursor
|
||||
|
||||
ALTERNATIVES *flash.nvim-flash.nvim-alternatives*
|
||||
|
||||
- leap.nvim <https://github.com/ggandor/leap.nvim>
|
||||
- lightspeed.nvim <https://github.com/ggandor/lightspeed.nvim>
|
||||
- vim-sneak <https://github.com/justinmk/vim-sneak>
|
||||
- mini.jump <https://github.com/echasnovski/mini.nvim/blob/main/readmes/mini-jump.md>
|
||||
- mini.jump2d <https://github.com/echasnovski/mini.nvim/blob/main/readmes/mini-jump2d.md>
|
||||
- hop.nvim <https://github.com/phaazon/hop.nvim>
|
||||
- pounce.nvim <https://github.com/rlane/pounce.nvim>
|
||||
- sj.nvim <https://github.com/woosaaahh/sj.nvim>
|
||||
- nvim-treehopper <https://github.com/mfussenegger/nvim-treehopper>
|
||||
- flit.nvim <https://github.com/ggandor/flit.nvim>
|
||||
|
||||
Generated by panvimdoc <https://github.com/kdheepak/panvimdoc>
|
||||
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
||||
149
config/neovim/store/lazy-plugins/flash.nvim/lua/flash/cache.lua
Normal file
149
config/neovim/store/lazy-plugins/flash.nvim/lua/flash/cache.lua
Normal file
@ -0,0 +1,149 @@
|
||||
local Pattern = require("flash.search.pattern")
|
||||
local Pos = require("flash.search.pos")
|
||||
local Util = require("flash.util")
|
||||
|
||||
---@class Flash.State.Window
|
||||
---@field win number
|
||||
---@field buf number
|
||||
---@field topline number
|
||||
---@field botline number
|
||||
---@field changedtick number
|
||||
|
||||
---@class Flash.Cache
|
||||
---@field state Flash.State
|
||||
---@field pattern Flash.Pattern
|
||||
---@field wins Flash.State.Window[]
|
||||
local M = {}
|
||||
M.__index = M
|
||||
|
||||
---@type table<Flash.State.Window, {matches: Flash.Match[]}>
|
||||
M.cache = setmetatable({}, { __mode = "k" })
|
||||
|
||||
---@param state Flash.State
|
||||
function M.new(state)
|
||||
local self = setmetatable({}, M)
|
||||
self.state = state
|
||||
self.pattern = Pattern.new("", state.opts.search.mode, state.opts.search.trigger)
|
||||
self.wins = {}
|
||||
return self
|
||||
end
|
||||
|
||||
---@return boolean dirty Returns true when dirty
|
||||
function M:update()
|
||||
local dirty = false
|
||||
|
||||
if self.pattern ~= self.state.pattern then
|
||||
self.pattern = self.state.pattern:clone()
|
||||
dirty = true
|
||||
M.cache = {}
|
||||
end
|
||||
|
||||
local win = vim.api.nvim_get_current_win()
|
||||
if self.state.win ~= win then
|
||||
self.state.win = win
|
||||
self.state.pos = Pos(win)
|
||||
self.state.restore_windows = Util.save_layout()
|
||||
M.cache = {}
|
||||
dirty = true
|
||||
end
|
||||
|
||||
self:_update_wins()
|
||||
|
||||
for _, w in ipairs(self.state.wins) do
|
||||
if self:_dirty(w) then
|
||||
dirty = true
|
||||
end
|
||||
end
|
||||
return dirty
|
||||
end
|
||||
|
||||
---@param win window
|
||||
function M:get_state(win)
|
||||
local window = self:get(win)
|
||||
if not window then
|
||||
return
|
||||
end
|
||||
if M.cache[window] then
|
||||
return M.cache[window]
|
||||
end
|
||||
|
||||
local from = Pos({ window.topline, 0 })
|
||||
local to = Pos({ window.botline + 1, 0 })
|
||||
|
||||
if not self.state.opts.search.wrap and win == self.state.win then
|
||||
if self.state.opts.search.forward then
|
||||
from = self.state.pos
|
||||
else
|
||||
to = self.state.pos
|
||||
end
|
||||
end
|
||||
|
||||
local matcher = self.state:get_matcher(win)
|
||||
if matcher.update then
|
||||
matcher:update()
|
||||
end
|
||||
|
||||
M.cache[window] = {
|
||||
matches = matcher:get({ from = from, to = to }),
|
||||
}
|
||||
return M.cache[window]
|
||||
end
|
||||
|
||||
---@param win window
|
||||
---@return Flash.State.Window
|
||||
function M:get(win)
|
||||
return self.wins[win]
|
||||
end
|
||||
|
||||
function M:_update_wins()
|
||||
-- prioritize current window
|
||||
self.state.wins = { self.state.win }
|
||||
|
||||
if self.state.opts.search.multi_window then
|
||||
local keep_current = false
|
||||
|
||||
---@param w window
|
||||
self.state.wins = vim.tbl_filter(function(w)
|
||||
local buf = vim.api.nvim_win_get_buf(w)
|
||||
local ft = vim.bo[buf].filetype
|
||||
|
||||
for _, exclude in ipairs(self.state.opts.search.exclude) do
|
||||
if type(exclude) == "string" and exclude == ft then
|
||||
return false
|
||||
elseif type(exclude) == "function" and exclude(w) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
if w == self.state.win then
|
||||
keep_current = true
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end, vim.api.nvim_tabpage_list_wins(0))
|
||||
if keep_current then
|
||||
table.insert(self.state.wins, 1, self.state.win)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@param win window
|
||||
function M:_dirty(win)
|
||||
local info = vim.fn.getwininfo(win)[1]
|
||||
local buf = vim.api.nvim_win_get_buf(win)
|
||||
|
||||
---@type Flash.State.Window
|
||||
local state = {
|
||||
win = win,
|
||||
buf = buf,
|
||||
cursor = vim.api.nvim_win_get_cursor(win),
|
||||
topline = info.topline,
|
||||
botline = info.botline,
|
||||
changedtick = vim.b[buf].changedtick,
|
||||
}
|
||||
if not vim.deep_equal(state, self.wins[win]) then
|
||||
self.wins[win] = state
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
@ -0,0 +1,36 @@
|
||||
local Repeat = require("flash.repeat")
|
||||
|
||||
---@class Flash.Commands
|
||||
local M = {}
|
||||
|
||||
---@param opts? Flash.State.Config
|
||||
function M.jump(opts)
|
||||
local state = Repeat.get_state("jump", opts)
|
||||
state:loop()
|
||||
return state
|
||||
end
|
||||
|
||||
---@param opts? Flash.State.Config
|
||||
function M.treesitter(opts)
|
||||
return require("flash.plugins.treesitter").jump(opts)
|
||||
end
|
||||
|
||||
---@param opts? Flash.State.Config
|
||||
function M.treesitter_search(opts)
|
||||
return require("flash.plugins.treesitter").search(opts)
|
||||
end
|
||||
|
||||
---@param opts? Flash.State.Config
|
||||
function M.remote(opts)
|
||||
local Config = require("flash.config")
|
||||
opts = Config.get({ mode = "remote" }, opts)
|
||||
return M.jump(opts)
|
||||
end
|
||||
|
||||
---@param enabled? boolean
|
||||
function M.toggle(enabled)
|
||||
local Search = require("flash.plugins.search")
|
||||
return Search.toggle(enabled)
|
||||
end
|
||||
|
||||
return M
|
||||
345
config/neovim/store/lazy-plugins/flash.nvim/lua/flash/config.lua
Normal file
345
config/neovim/store/lazy-plugins/flash.nvim/lua/flash/config.lua
Normal file
@ -0,0 +1,345 @@
|
||||
---@type Flash.Config
|
||||
local M = {}
|
||||
|
||||
---@class Flash.Config
|
||||
---@field mode? string
|
||||
---@field enabled? boolean
|
||||
---@field ns? string
|
||||
---@field config? fun(opts:Flash.Config)
|
||||
local defaults = {
|
||||
-- labels = "abcdefghijklmnopqrstuvwxyz",
|
||||
labels = "asdfghjklqwertyuiopzxcvbnm",
|
||||
search = {
|
||||
-- search/jump in all windows
|
||||
multi_window = true,
|
||||
-- search direction
|
||||
forward = true,
|
||||
-- when `false`, find only matches in the given direction
|
||||
wrap = true,
|
||||
---@type Flash.Pattern.Mode
|
||||
-- Each mode will take ignorecase and smartcase into account.
|
||||
-- * exact: exact match
|
||||
-- * search: regular search
|
||||
-- * fuzzy: fuzzy search
|
||||
-- * fun(str): custom function that returns a pattern
|
||||
-- For example, to only match at the beginning of a word:
|
||||
-- mode = function(str)
|
||||
-- return "\\<" .. str
|
||||
-- end,
|
||||
mode = "exact",
|
||||
-- behave like `incsearch`
|
||||
incremental = false,
|
||||
-- Excluded filetypes and custom window filters
|
||||
---@type (string|fun(win:window))[]
|
||||
exclude = {
|
||||
"notify",
|
||||
"cmp_menu",
|
||||
"noice",
|
||||
"flash_prompt",
|
||||
function(win)
|
||||
-- exclude non-focusable windows
|
||||
return not vim.api.nvim_win_get_config(win).focusable
|
||||
end,
|
||||
},
|
||||
-- Optional trigger character that needs to be typed before
|
||||
-- a jump label can be used. It's NOT recommended to set this,
|
||||
-- unless you know what you're doing
|
||||
trigger = "",
|
||||
-- max pattern length. If the pattern length is equal to this
|
||||
-- labels will no longer be skipped. When it exceeds this length
|
||||
-- it will either end in a jump or terminate the search
|
||||
max_length = false, ---@type number|false
|
||||
},
|
||||
jump = {
|
||||
-- save location in the jumplist
|
||||
jumplist = true,
|
||||
-- jump position
|
||||
pos = "start", ---@type "start" | "end" | "range"
|
||||
-- add pattern to search history
|
||||
history = false,
|
||||
-- add pattern to search register
|
||||
register = false,
|
||||
-- clear highlight after jump
|
||||
nohlsearch = false,
|
||||
-- automatically jump when there is only one match
|
||||
autojump = false,
|
||||
-- You can force inclusive/exclusive jumps by setting the
|
||||
-- `inclusive` option. By default it will be automatically
|
||||
-- set based on the mode.
|
||||
inclusive = nil, ---@type boolean?
|
||||
-- jump position offset. Not used for range jumps.
|
||||
-- 0: default
|
||||
-- 1: when pos == "end" and pos < current position
|
||||
offset = nil, ---@type number
|
||||
},
|
||||
label = {
|
||||
-- allow uppercase labels
|
||||
uppercase = true,
|
||||
-- add any labels with the correct case here, that you want to exclude
|
||||
exclude = "",
|
||||
-- add a label for the first match in the current window.
|
||||
-- you can always jump to the first match with `<CR>`
|
||||
current = true,
|
||||
-- show the label after the match
|
||||
after = true, ---@type boolean|number[]
|
||||
-- show the label before the match
|
||||
before = false, ---@type boolean|number[]
|
||||
-- position of the label extmark
|
||||
style = "overlay", ---@type "eol" | "overlay" | "right_align" | "inline"
|
||||
-- flash tries to re-use labels that were already assigned to a position,
|
||||
-- when typing more characters. By default only lower-case labels are re-used.
|
||||
reuse = "lowercase", ---@type "lowercase" | "all" | "none"
|
||||
-- for the current window, label targets closer to the cursor first
|
||||
distance = true,
|
||||
-- minimum pattern length to show labels
|
||||
-- Ignored for custom labelers.
|
||||
min_pattern_length = 0,
|
||||
-- Enable this to use rainbow colors to highlight labels
|
||||
-- Can be useful for visualizing Treesitter ranges.
|
||||
rainbow = {
|
||||
enabled = false,
|
||||
-- number between 1 and 9
|
||||
shade = 5,
|
||||
},
|
||||
-- With `format`, you can change how the label is rendered.
|
||||
-- Should return a list of `[text, highlight]` tuples.
|
||||
---@class Flash.Format
|
||||
---@field state Flash.State
|
||||
---@field match Flash.Match
|
||||
---@field hl_group string
|
||||
---@field after boolean
|
||||
---@type fun(opts:Flash.Format): string[][]
|
||||
format = function(opts)
|
||||
return { { opts.match.label, opts.hl_group } }
|
||||
end,
|
||||
},
|
||||
highlight = {
|
||||
-- show a backdrop with hl FlashBackdrop
|
||||
backdrop = true,
|
||||
-- Highlight the search matches
|
||||
matches = true,
|
||||
-- extmark priority
|
||||
priority = 5000,
|
||||
groups = {
|
||||
match = "FlashMatch",
|
||||
current = "FlashCurrent",
|
||||
backdrop = "FlashBackdrop",
|
||||
label = "FlashLabel",
|
||||
},
|
||||
},
|
||||
-- action to perform when picking a label.
|
||||
-- defaults to the jumping logic depending on the mode.
|
||||
---@type fun(match:Flash.Match, state:Flash.State)|nil
|
||||
action = nil,
|
||||
-- initial pattern to use when opening flash
|
||||
pattern = "",
|
||||
-- When `true`, flash will try to continue the last search
|
||||
continue = false,
|
||||
-- Set config to a function to dynamically change the config
|
||||
config = nil, ---@type fun(opts:Flash.Config)|nil
|
||||
-- You can override the default options for a specific mode.
|
||||
-- Use it with `require("flash").jump({mode = "forward"})`
|
||||
---@type table<string, Flash.Config>
|
||||
modes = {
|
||||
-- options used when flash is activated through
|
||||
-- a regular search with `/` or `?`
|
||||
search = {
|
||||
-- when `true`, flash will be activated during regular search by default.
|
||||
-- You can always toggle when searching with `require("flash").toggle()`
|
||||
enabled = false,
|
||||
highlight = { backdrop = false },
|
||||
jump = { history = true, register = true, nohlsearch = true },
|
||||
search = {
|
||||
-- `forward` will be automatically set to the search direction
|
||||
-- `mode` is always set to `search`
|
||||
-- `incremental` is set to `true` when `incsearch` is enabled
|
||||
},
|
||||
},
|
||||
-- options used when flash is activated through
|
||||
-- `f`, `F`, `t`, `T`, `;` and `,` motions
|
||||
char = {
|
||||
enabled = true,
|
||||
-- dynamic configuration for ftFT motions
|
||||
config = function(opts)
|
||||
-- autohide flash when in operator-pending mode
|
||||
opts.autohide = opts.autohide or (vim.fn.mode(true):find("no") and vim.v.operator == "y")
|
||||
|
||||
-- disable jump labels when not enabled, when using a count,
|
||||
-- or when recording/executing registers
|
||||
opts.jump_labels = opts.jump_labels
|
||||
and vim.v.count == 0
|
||||
and vim.fn.reg_executing() == ""
|
||||
and vim.fn.reg_recording() == ""
|
||||
|
||||
-- Show jump labels only in operator-pending mode
|
||||
-- opts.jump_labels = vim.v.count == 0 and vim.fn.mode(true):find("o")
|
||||
end,
|
||||
-- hide after jump when not using jump labels
|
||||
autohide = false,
|
||||
-- show jump labels
|
||||
jump_labels = false,
|
||||
-- set to `false` to use the current line only
|
||||
multi_line = true,
|
||||
-- When using jump labels, don't use these keys
|
||||
-- This allows using those keys directly after the motion
|
||||
label = { exclude = "hjkliardc" },
|
||||
-- by default all keymaps are enabled, but you can disable some of them,
|
||||
-- by removing them from the list.
|
||||
-- If you rather use another key, you can map them
|
||||
-- to something else, e.g., { [";"] = "L", [","] = H }
|
||||
keys = { "f", "F", "t", "T", ";", "," },
|
||||
---@alias Flash.CharActions table<string, "next" | "prev" | "right" | "left">
|
||||
-- The direction for `prev` and `next` is determined by the motion.
|
||||
-- `left` and `right` are always left and right.
|
||||
char_actions = function(motion)
|
||||
return {
|
||||
[";"] = "next", -- set to `right` to always go right
|
||||
[","] = "prev", -- set to `left` to always go left
|
||||
-- clever-f style
|
||||
[motion:lower()] = "next",
|
||||
[motion:upper()] = "prev",
|
||||
-- jump2d style: same case goes next, opposite case goes prev
|
||||
-- [motion] = "next",
|
||||
-- [motion:match("%l") and motion:upper() or motion:lower()] = "prev",
|
||||
}
|
||||
end,
|
||||
search = { wrap = false },
|
||||
highlight = { backdrop = true },
|
||||
jump = { register = false },
|
||||
},
|
||||
-- options used for treesitter selections
|
||||
-- `require("flash").treesitter()`
|
||||
treesitter = {
|
||||
labels = "abcdefghijklmnopqrstuvwxyz",
|
||||
jump = { pos = "range" },
|
||||
search = { incremental = false },
|
||||
label = { before = true, after = true, style = "inline" },
|
||||
highlight = {
|
||||
backdrop = false,
|
||||
matches = false,
|
||||
},
|
||||
},
|
||||
treesitter_search = {
|
||||
jump = { pos = "range" },
|
||||
search = { multi_window = true, wrap = true, incremental = false },
|
||||
remote_op = { restore = true },
|
||||
label = { before = true, after = true, style = "inline" },
|
||||
},
|
||||
-- options used for remote flash
|
||||
remote = {
|
||||
remote_op = { restore = true, motion = true },
|
||||
},
|
||||
},
|
||||
-- options for the floating window that shows the prompt,
|
||||
-- for regular jumps
|
||||
prompt = {
|
||||
enabled = true,
|
||||
prefix = { { "⚡", "FlashPromptIcon" } },
|
||||
win_config = {
|
||||
relative = "editor",
|
||||
width = 1, -- when <=1 it's a percentage of the editor width
|
||||
height = 1,
|
||||
row = -1, -- when negative it's an offset from the bottom
|
||||
col = 0, -- when negative it's an offset from the right
|
||||
zindex = 1000,
|
||||
},
|
||||
},
|
||||
-- options for remote operator pending mode
|
||||
remote_op = {
|
||||
-- restore window views and cursor position
|
||||
-- after doing a remote operation
|
||||
restore = false,
|
||||
-- For `jump.pos = "range"`, this setting is ignored.
|
||||
-- `true`: always enter a new motion when doing a remote operation
|
||||
-- `false`: use the window's cursor position and jump target
|
||||
-- `nil`: act as `true` for remote windows, `false` for the current window
|
||||
motion = false,
|
||||
},
|
||||
}
|
||||
|
||||
---@type Flash.Config
|
||||
local options
|
||||
|
||||
---@param opts? Flash.Config
|
||||
function M.setup(opts)
|
||||
opts = opts or {}
|
||||
opts.mode = nil
|
||||
options = {}
|
||||
options = M.get(opts)
|
||||
|
||||
require("flash.plugins.search").setup()
|
||||
if options.modes.char.enabled then
|
||||
require("flash.plugins.char").setup()
|
||||
end
|
||||
end
|
||||
|
||||
---@param ... Flash.Config|Flash.State.Config|nil
|
||||
---@return Flash.State.Config
|
||||
function M.get(...)
|
||||
if options == nil then
|
||||
M.setup()
|
||||
end
|
||||
---@type Flash.Config[]
|
||||
local all = { {}, defaults, options or {} }
|
||||
|
||||
---@type table<string, boolean>
|
||||
local modes = {}
|
||||
|
||||
for i = 1, select("#", ...) do
|
||||
---@type Flash.Config?
|
||||
local opts = select(i, ...)
|
||||
if type(opts) == "string" then
|
||||
opts = options.modes[opts]
|
||||
end
|
||||
if opts then
|
||||
table.insert(all, opts)
|
||||
local idx = #all
|
||||
while opts.mode and not modes[opts.mode] do
|
||||
modes[opts.mode or ""] = true
|
||||
opts = options.modes[opts.mode] or {}
|
||||
table.insert(all, idx, opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- backward compatibility
|
||||
for _, o in ipairs(all) do
|
||||
if o.highlight and o.highlight.label then
|
||||
o.label = vim.tbl_deep_extend("force", o.label or {}, o.highlight.label)
|
||||
---@diagnostic disable-next-line: no-unknown
|
||||
o.highlight.label = nil
|
||||
vim.notify_once(
|
||||
"flash: `opts.highlight.label` is deprecated, use `opts.label` instead",
|
||||
vim.log.levels.WARN
|
||||
)
|
||||
end
|
||||
for _, field in ipairs({ "autohide", "jump_labels" }) do
|
||||
if type(o[field]) == "function" then
|
||||
local motion = require("flash.plugins.char").motion
|
||||
---@diagnostic disable-next-line: no-unknown
|
||||
o[field] = o[field](motion)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local ret = vim.tbl_deep_extend("force", unpack(all))
|
||||
---@cast ret Flash.State.Config
|
||||
|
||||
if type(ret.config) == "function" then
|
||||
ret.config(ret)
|
||||
end
|
||||
|
||||
if vim.g.vscode then
|
||||
ret.prompt.enabled = false
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
return setmetatable(M, {
|
||||
__index = function(_, key)
|
||||
if options == nil then
|
||||
M.setup()
|
||||
end
|
||||
return options[key]
|
||||
end,
|
||||
})
|
||||
@ -0,0 +1,32 @@
|
||||
local Docs = require("lazy.docs")
|
||||
|
||||
local M = {}
|
||||
|
||||
function M.update()
|
||||
local config = Docs.extract("lua/flash/config.lua", "\nlocal defaults = ({.-\n})")
|
||||
config = config:gsub("%s*debug = false.\n", "\n")
|
||||
Docs.save({
|
||||
config = config,
|
||||
setup = Docs.extract("lua/flash/docs.lua", "function M%.suggested%(%)\n%s*return (.-)\nend"),
|
||||
})
|
||||
end
|
||||
|
||||
function M.suggested()
|
||||
return {
|
||||
"folke/flash.nvim",
|
||||
event = "VeryLazy",
|
||||
---@type Flash.Config
|
||||
opts = {},
|
||||
-- stylua: ignore
|
||||
keys = {
|
||||
{ "s", mode = { "n", "x", "o" }, function() require("flash").jump() end, desc = "Flash" },
|
||||
{ "S", mode = { "n", "x", "o" }, function() require("flash").treesitter() end, desc = "Flash Treesitter" },
|
||||
{ "r", mode = "o", function() require("flash").remote() end, desc = "Remote Flash" },
|
||||
{ "R", mode = { "o", "x" }, function() require("flash").treesitter_search() end, desc = "Treesitter Search" },
|
||||
{ "<c-s>", mode = { "c" }, function() require("flash").toggle() end, desc = "Toggle Flash Search" },
|
||||
},
|
||||
}
|
||||
end
|
||||
M.update()
|
||||
|
||||
return M
|
||||
@ -0,0 +1,68 @@
|
||||
local Pos = require("flash.search.pos")
|
||||
|
||||
local M = {}
|
||||
|
||||
---@type ffi.namespace*
|
||||
local C
|
||||
local incsearch_state = {}
|
||||
|
||||
local function _ffi()
|
||||
if not C then
|
||||
local ffi = require("ffi")
|
||||
ffi.cdef([[
|
||||
int search_match_endcol;
|
||||
int no_mapping;
|
||||
unsigned int search_match_lines;
|
||||
void setcursor_mayforce(bool force);
|
||||
]])
|
||||
C = ffi.C
|
||||
end
|
||||
return C
|
||||
end
|
||||
|
||||
---@private
|
||||
---@param from Pos
|
||||
function M.get_end_pos(from)
|
||||
_ffi()
|
||||
local ret = Pos({
|
||||
from[1] + C.search_match_lines,
|
||||
math.max(0, C.search_match_endcol - 1),
|
||||
})
|
||||
local line = vim.api.nvim_buf_get_lines(0, ret[1] - 1, ret[1], false)[1]
|
||||
local char_idx = vim.fn.charidx(line, ret[2])
|
||||
ret[2] = vim.fn.byteidx(line, char_idx)
|
||||
return ret
|
||||
end
|
||||
|
||||
function M.save_incsearch_state()
|
||||
_ffi()
|
||||
incsearch_state = {
|
||||
match_endcol = C.search_match_endcol,
|
||||
match_lines = C.search_match_lines,
|
||||
}
|
||||
end
|
||||
|
||||
function M.mappings_enabled()
|
||||
_ffi()
|
||||
return C.no_mapping == 0
|
||||
end
|
||||
|
||||
function M.setcursor(force)
|
||||
if vim.api.nvim__redraw then
|
||||
vim.api.nvim__redraw({ cursor = true })
|
||||
else
|
||||
if force == nil then
|
||||
force = false
|
||||
end
|
||||
_ffi()
|
||||
return C.setcursor_mayforce(force)
|
||||
end
|
||||
end
|
||||
|
||||
function M.restore_incsearch_state()
|
||||
_ffi()
|
||||
C.search_match_endcol = incsearch_state.match_endcol
|
||||
C.search_match_lines = incsearch_state.match_lines
|
||||
end
|
||||
|
||||
return M
|
||||
@ -0,0 +1,216 @@
|
||||
local M = {}
|
||||
|
||||
function M.clear(ns)
|
||||
for _, buf in ipairs(vim.api.nvim_list_bufs()) do
|
||||
vim.api.nvim_buf_clear_namespace(buf, ns, 0, -1)
|
||||
end
|
||||
end
|
||||
|
||||
function M.setup()
|
||||
if vim.g.vscode then
|
||||
local hls = {
|
||||
FlashBackdrop = { fg = "#545c7e" },
|
||||
FlashCurrent = { bg = "#ff966c", fg = "#1b1d2b" },
|
||||
FlashLabel = { bg = "#ff007c", bold = true, fg = "#c8d3f5" },
|
||||
FlashMatch = { bg = "#3e68d7", fg = "#c8d3f5" },
|
||||
FlashCursor = { reverse = true },
|
||||
}
|
||||
for hl_group, hl in pairs(hls) do
|
||||
hl.default = true
|
||||
vim.api.nvim_set_hl(0, hl_group, hl)
|
||||
end
|
||||
else
|
||||
local links = {
|
||||
FlashBackdrop = "Comment",
|
||||
FlashMatch = "Search",
|
||||
FlashCurrent = "IncSearch",
|
||||
FlashLabel = "Substitute",
|
||||
FlashPrompt = "MsgArea",
|
||||
FlashPromptIcon = "Special",
|
||||
FlashCursor = "Cursor",
|
||||
}
|
||||
for hl_group, link in pairs(links) do
|
||||
vim.api.nvim_set_hl(0, hl_group, { link = link, default = true })
|
||||
end
|
||||
end
|
||||
end
|
||||
M.setup()
|
||||
|
||||
---@param state Flash.State
|
||||
function M.backdrop(state)
|
||||
for _, win in ipairs(state.wins) do
|
||||
local info = vim.fn.getwininfo(win)[1]
|
||||
local buf = vim.api.nvim_win_get_buf(win)
|
||||
local from = { info.topline, 0 }
|
||||
local to = { info.botline + 1, 0 }
|
||||
if state.win == win and not state.opts.search.wrap then
|
||||
if state.opts.search.forward then
|
||||
from = { state.pos[1], state.pos[2] + 1 }
|
||||
else
|
||||
to = state.pos
|
||||
end
|
||||
end
|
||||
-- we need to create a backdrop for each line because of the way
|
||||
-- extmarks priority rendering works
|
||||
for line = from[1], to[1] do
|
||||
vim.api.nvim_buf_set_extmark(buf, state.ns, line - 1, line == from[1] and from[2] or 0, {
|
||||
hl_group = state.opts.highlight.groups.backdrop,
|
||||
end_row = line == to[1] and line - 1 or line,
|
||||
hl_eol = line ~= to[1],
|
||||
end_col = line == to[1] and to[2] or from[2],
|
||||
priority = state.opts.highlight.priority,
|
||||
strict = false,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@param state Flash.State
|
||||
function M.cursor(state)
|
||||
for _, win in ipairs(state.wins) do
|
||||
local cursor = vim.api.nvim_win_get_cursor(win)
|
||||
local buf = vim.api.nvim_win_get_buf(win)
|
||||
vim.api.nvim_buf_set_extmark(buf, state.ns, cursor[1] - 1, cursor[2], {
|
||||
hl_group = "FlashCursor",
|
||||
end_col = cursor[2] + 1,
|
||||
priority = state.opts.highlight.priority + 3,
|
||||
strict = false,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
---@param state Flash.State
|
||||
function M.update(state)
|
||||
M.clear(state.ns)
|
||||
|
||||
if state.opts.highlight.backdrop then
|
||||
M.backdrop(state)
|
||||
end
|
||||
|
||||
local style = state.opts.label.style
|
||||
if style == "inline" and vim.fn.has("nvim-0.10.0") == 0 then
|
||||
style = "overlay"
|
||||
end
|
||||
|
||||
local after = state.opts.label.after
|
||||
after = after == true and { 0, 1 } or after
|
||||
---@cast after number[]
|
||||
local before = state.opts.label.before
|
||||
before = before == true and { 0, -1 } or before
|
||||
---@cast before number[]
|
||||
|
||||
if style == "inline" and before then
|
||||
before[2] = before[2] + 1
|
||||
end
|
||||
|
||||
local target = state.target
|
||||
|
||||
---@type table<string, {buf: number, row: number, col: number, text:string[][]}>
|
||||
local extmarks = {}
|
||||
|
||||
---@param match Flash.Match
|
||||
---@param pos number[]
|
||||
---@param offset number[]
|
||||
---@param is_after boolean
|
||||
local function label(match, pos, offset, is_after)
|
||||
local buf = vim.api.nvim_win_get_buf(match.win)
|
||||
local cursor = vim.api.nvim_win_get_cursor(match.win)
|
||||
local pos2 = require("flash.util").offset_pos(buf, pos, offset)
|
||||
local row, col = pos2[1] - 1, pos2[2]
|
||||
-- dont show the label if the cursor is on the same position
|
||||
-- in the same window
|
||||
-- and the label is not a range
|
||||
if
|
||||
cursor[1] == row + 1
|
||||
and cursor[2] == col
|
||||
and match.win == state.win
|
||||
and state.opts.jump.pos ~= "range"
|
||||
then
|
||||
return
|
||||
end
|
||||
if match.fold then
|
||||
-- set the row to the fold start
|
||||
row = match.fold - 1
|
||||
col = 0
|
||||
end
|
||||
|
||||
local hl_group = state.opts.highlight.groups.label
|
||||
if state.rainbow then
|
||||
hl_group = state.rainbow:get(match)
|
||||
elseif
|
||||
-- set hl_group to current if the match is the current target
|
||||
-- and the target is a single character
|
||||
target
|
||||
and target.pos[1] == row + 1
|
||||
and target.pos[2] == col
|
||||
and target.pos == target.end_pos
|
||||
then
|
||||
hl_group = state.opts.highlight.groups.current
|
||||
end
|
||||
if match.label == "" then
|
||||
-- when empty label, highlight the position
|
||||
vim.api.nvim_buf_set_extmark(buf, state.ns, row, col, {
|
||||
hl_group = hl_group,
|
||||
end_row = row,
|
||||
end_col = col + 1,
|
||||
strict = false,
|
||||
priority = state.opts.highlight.priority + 2,
|
||||
})
|
||||
else
|
||||
-- else highlight the label
|
||||
local key = buf .. ":" .. row .. ":" .. col
|
||||
extmarks[key] = extmarks[key] or { buf = buf, row = row, col = col, text = {} }
|
||||
local text = state.opts.label.format({
|
||||
state = state,
|
||||
match = match,
|
||||
hl_group = hl_group,
|
||||
after = is_after,
|
||||
})
|
||||
for i = #text, 1, -1 do
|
||||
table.insert(extmarks[key].text, 1, text[i])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, match in ipairs(state.results) do
|
||||
local buf = vim.api.nvim_win_get_buf(match.win)
|
||||
|
||||
local highlight = state.opts.highlight.matches
|
||||
if match.highlight ~= nil then
|
||||
highlight = match.highlight
|
||||
end
|
||||
|
||||
if highlight then
|
||||
vim.api.nvim_buf_set_extmark(buf, state.ns, match.pos[1] - 1, match.pos[2], {
|
||||
end_row = match.end_pos[1] - 1,
|
||||
end_col = match.end_pos[2] + 1,
|
||||
hl_group = target and match.pos == target.pos and state.opts.highlight.groups.current
|
||||
or state.opts.highlight.groups.match,
|
||||
strict = false,
|
||||
priority = state.opts.highlight.priority + 1,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
for _, match in ipairs(state.results) do
|
||||
if match.label and after then
|
||||
label(match, match.end_pos, after, true)
|
||||
end
|
||||
if match.label and before then
|
||||
label(match, match.pos, before, false)
|
||||
end
|
||||
end
|
||||
|
||||
for _, extmark in pairs(extmarks) do
|
||||
vim.api.nvim_buf_set_extmark(extmark.buf, state.ns, extmark.row, extmark.col, {
|
||||
virt_text = extmark.text,
|
||||
virt_text_pos = style,
|
||||
strict = false,
|
||||
priority = state.opts.highlight.priority + 2,
|
||||
})
|
||||
end
|
||||
|
||||
M.cursor(state)
|
||||
end
|
||||
|
||||
return M
|
||||
@ -0,0 +1,13 @@
|
||||
---@type Flash.Commands
|
||||
local M = {}
|
||||
|
||||
---@param opts? Flash.Config
|
||||
function M.setup(opts)
|
||||
require("flash.config").setup(opts)
|
||||
end
|
||||
|
||||
return setmetatable(M, {
|
||||
__index = function(_, k)
|
||||
return require("flash.commands")[k]
|
||||
end,
|
||||
})
|
||||
253
config/neovim/store/lazy-plugins/flash.nvim/lua/flash/jump.lua
Normal file
253
config/neovim/store/lazy-plugins/flash.nvim/lua/flash/jump.lua
Normal file
@ -0,0 +1,253 @@
|
||||
local Hacks = require("flash.hacks")
|
||||
local Pos = require("flash.search.pos")
|
||||
local Util = require("flash.util")
|
||||
local M = {}
|
||||
|
||||
---@param match Flash.Match
|
||||
---@param state Flash.State
|
||||
---@return Flash.Match?
|
||||
function M.jump(match, state)
|
||||
local register = vim.v.register
|
||||
-- add to jump list
|
||||
if state.opts.jump.jumplist then
|
||||
vim.cmd("normal! m'")
|
||||
end
|
||||
|
||||
local mode = vim.fn.mode(true)
|
||||
local is_op = mode:sub(1, 2) == "no"
|
||||
local is_visual = mode:sub(1, 1) == "v"
|
||||
|
||||
if is_op and (state.opts.remote_op.motion or match.win ~= vim.api.nvim_get_current_win()) then
|
||||
-- use our special logic for remote operator pending mode
|
||||
return M.remote_op(match, state, register)
|
||||
end
|
||||
|
||||
-- change window if needed
|
||||
if match.win ~= vim.api.nvim_get_current_win() then
|
||||
if is_visual then
|
||||
-- cancel visual mode in the current window,
|
||||
-- to avoid issues with the remote window
|
||||
vim.cmd("normal! v")
|
||||
end
|
||||
|
||||
vim.api.nvim_set_current_win(match.win)
|
||||
|
||||
if is_visual then
|
||||
-- enable visual mode in the remote window,
|
||||
-- from its current cursor position
|
||||
vim.cmd("normal! v")
|
||||
end
|
||||
end
|
||||
|
||||
M._jump(match, state, { op = is_op })
|
||||
end
|
||||
|
||||
function M.fix_selection()
|
||||
local selection = vim.go.selection
|
||||
vim.go.selection = "inclusive"
|
||||
vim.schedule(function()
|
||||
vim.go.selection = selection
|
||||
end)
|
||||
end
|
||||
|
||||
-- Remote operator pending mode.Cancel the operator and
|
||||
-- re-trigger the operator in the remote window.
|
||||
---@param match Flash.Match
|
||||
---@param state Flash.State
|
||||
---@param register string
|
||||
---@return Flash.Match?
|
||||
function M.remote_op(match, state, register)
|
||||
Util.exit()
|
||||
|
||||
-- schedule this so that the active operator is properly cancelled
|
||||
vim.schedule(function()
|
||||
local motion = state.opts.remote_op.motion
|
||||
if motion == nil then
|
||||
motion = match.win ~= vim.api.nvim_get_current_win()
|
||||
end
|
||||
|
||||
vim.api.nvim_set_current_win(match.win)
|
||||
|
||||
-- use a new motion to select the text-object to act on,
|
||||
-- unless we're jumping to a range
|
||||
if motion then
|
||||
if vim.fn.mode() == "v" then
|
||||
vim.cmd("normal! v")
|
||||
end
|
||||
|
||||
if state.opts.jump.pos == "range" then
|
||||
vim.api.nvim_win_set_cursor(match.win, match.pos)
|
||||
vim.cmd("normal! v")
|
||||
vim.api.nvim_win_set_cursor(match.win, match.end_pos)
|
||||
else
|
||||
vim.api.nvim_win_set_cursor(
|
||||
match.win,
|
||||
state.opts.jump.pos == "start" and match.pos or match.end_pos
|
||||
)
|
||||
end
|
||||
|
||||
-- otherwise, use the remote window's cursor position
|
||||
else
|
||||
local from = vim.api.nvim_win_get_cursor(match.win)
|
||||
M._jump(match, state, { op = true })
|
||||
local to = vim.api.nvim_win_get_cursor(match.win)
|
||||
|
||||
-- if a range was selected, use that instead
|
||||
if vim.fn.mode() == "v" then
|
||||
vim.cmd("normal! v") -- end the selection
|
||||
from = vim.api.nvim_buf_get_mark(0, "<")
|
||||
to = vim.api.nvim_buf_get_mark(0, ">")
|
||||
end
|
||||
|
||||
-- vim.api.nvim_buf_set_mark(0, "[", from[1], from[2], {})
|
||||
-- vim.api.nvim_buf_set_mark(0, "]", to[1], to[2], {})
|
||||
|
||||
-- select the range for the operator
|
||||
vim.api.nvim_win_set_cursor(0, from)
|
||||
vim.cmd("normal! v")
|
||||
vim.api.nvim_win_set_cursor(0, to)
|
||||
end
|
||||
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
local opmap = vim.fn.maparg(vim.v.operator, "", false, true) --[[@as any]]
|
||||
if not vim.tbl_isempty(opmap) then
|
||||
vim.keymap.del("", vim.v.operator)
|
||||
end
|
||||
|
||||
-- re-trigger the operator
|
||||
vim.api.nvim_input('"' .. register .. vim.v.operator)
|
||||
if state.opts.remote_op.restore then
|
||||
vim.schedule(function()
|
||||
if not vim.tbl_isempty(opmap) then
|
||||
vim.fn.mapset(opmap.mode, false, opmap)
|
||||
end
|
||||
M.restore_remote(state)
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Restore window views after the remote operation ends
|
||||
---@param state Flash.State
|
||||
function M.restore_remote(state)
|
||||
local restore = vim.schedule_wrap(function()
|
||||
state:restore()
|
||||
end)
|
||||
|
||||
-- wait till getting user input clears
|
||||
if not Hacks.mappings_enabled() then
|
||||
return Util.on_done(function()
|
||||
return Hacks.mappings_enabled()
|
||||
end, function()
|
||||
M.restore_remote(state)
|
||||
end)
|
||||
|
||||
-- wait till operator pending mode ends
|
||||
elseif vim.fn.mode(true):sub(1, 2) == "no" then
|
||||
return Util.on_done(function()
|
||||
return vim.fn.mode(true):sub(1, 2) ~= "no"
|
||||
end, function()
|
||||
M.restore_remote(state)
|
||||
end)
|
||||
|
||||
-- restore after making edits
|
||||
elseif vim.fn.mode() == "i" and vim.v.operator == "c" then
|
||||
vim.api.nvim_create_autocmd("InsertLeave", {
|
||||
once = true,
|
||||
callback = restore,
|
||||
})
|
||||
else
|
||||
restore()
|
||||
end
|
||||
end
|
||||
|
||||
-- Performs the actual jump in the current window,
|
||||
-- taking operator-pending mode into account.
|
||||
---@param match Flash.Match
|
||||
---@param state Flash.State
|
||||
---@param opts? {op:boolean}
|
||||
---@return Flash.Match?
|
||||
function M._jump(match, state, opts)
|
||||
opts = opts or {}
|
||||
M.fix_selection()
|
||||
M.open_folds(match)
|
||||
-- select range
|
||||
if state.opts.jump.pos == "range" then
|
||||
if vim.fn.mode() == "v" then
|
||||
vim.cmd("normal! v")
|
||||
end
|
||||
vim.api.nvim_win_set_cursor(match.win, match.pos)
|
||||
vim.cmd("normal! v")
|
||||
vim.api.nvim_win_set_cursor(match.win, match.end_pos)
|
||||
else
|
||||
local pos = state.opts.jump.pos == "start" and match.pos or match.end_pos
|
||||
|
||||
if opts.op then
|
||||
-- fix inclusive/exclusive
|
||||
-- default is exclusive
|
||||
if state.opts.jump.inclusive ~= false then
|
||||
vim.cmd("normal! v")
|
||||
end
|
||||
end
|
||||
local current = Pos(match.win)
|
||||
local offset = state.opts.jump.offset
|
||||
|
||||
if not offset and state.opts.jump.pos == "end" and pos < current then
|
||||
offset = 1
|
||||
end
|
||||
|
||||
pos = Pos(
|
||||
require("flash.util").offset_pos(vim.api.nvim_win_get_buf(match.win), pos, { 0, offset or 0 })
|
||||
)
|
||||
pos[2] = math.max(0, pos[2])
|
||||
|
||||
vim.api.nvim_win_set_cursor(match.win, pos)
|
||||
end
|
||||
end
|
||||
|
||||
---@param match Flash.Match
|
||||
function M.open_folds(match)
|
||||
local cursor = vim.api.nvim_win_get_cursor(match.win)
|
||||
local from = match.pos[1]
|
||||
local to = match.end_pos[1]
|
||||
local is_visual = vim.fn.mode(true):find("v")
|
||||
local opened = false
|
||||
for line = from, to do
|
||||
if vim.fn.foldclosed(line) ~= -1 then
|
||||
vim.api.nvim_win_set_cursor(match.win, { line, 0 })
|
||||
vim.cmd("normal! zO")
|
||||
opened = true
|
||||
end
|
||||
end
|
||||
if opened then
|
||||
vim.api.nvim_win_set_cursor(match.win, cursor)
|
||||
if is_visual then
|
||||
vim.cmd("normal! v")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@param state Flash.State
|
||||
function M.on_jump(state)
|
||||
-- fix or restore the search register
|
||||
local sf = vim.v.searchforward
|
||||
if state.opts.jump.register then
|
||||
vim.fn.setreg("/", state.pattern.search)
|
||||
end
|
||||
vim.v.searchforward = sf
|
||||
|
||||
-- add the real search pattern to the history
|
||||
if state.opts.jump.history then
|
||||
vim.fn.histadd("search", state.pattern.search)
|
||||
end
|
||||
|
||||
-- clear the highlight
|
||||
if state.opts.jump.nohlsearch then
|
||||
vim.cmd.nohlsearch()
|
||||
elseif state.opts.jump.register then
|
||||
-- this will show the search matches again
|
||||
vim.cmd("set hlsearch")
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
@ -0,0 +1,228 @@
|
||||
---@class Flash.Labeler
|
||||
---@field state Flash.State
|
||||
---@field used table<string, string>
|
||||
---@field labels string[]
|
||||
local M = {}
|
||||
M.__index = M
|
||||
|
||||
function M.new(state)
|
||||
local self
|
||||
self = setmetatable({}, M)
|
||||
self.state = state
|
||||
self.used = {}
|
||||
self:reset()
|
||||
return self
|
||||
end
|
||||
|
||||
function M:labeler()
|
||||
return function()
|
||||
return self:update()
|
||||
end
|
||||
end
|
||||
|
||||
function M:update()
|
||||
self:reset()
|
||||
|
||||
if #self.state.pattern() < self.state.opts.label.min_pattern_length then
|
||||
return
|
||||
end
|
||||
|
||||
local matches = self:filter()
|
||||
|
||||
for _, match in ipairs(matches) do
|
||||
self:label(match, true)
|
||||
end
|
||||
|
||||
for _, match in ipairs(matches) do
|
||||
if not self:label(match) then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M:reset()
|
||||
local skip = {} ---@type table<string, boolean>
|
||||
self.labels = {}
|
||||
|
||||
for _, l in ipairs(self.state:labels()) do
|
||||
if not skip[l] then
|
||||
self.labels[#self.labels + 1] = l
|
||||
skip[l] = true
|
||||
end
|
||||
end
|
||||
if
|
||||
not self.state.opts.search.max_length
|
||||
or #self.state.pattern() < self.state.opts.search.max_length
|
||||
then
|
||||
for _, win in pairs(self.state.wins) do
|
||||
self.labels = self:skip(win, self.labels)
|
||||
end
|
||||
end
|
||||
for _, m in ipairs(self.state.results) do
|
||||
if m.label ~= false then
|
||||
m.label = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M:valid(label)
|
||||
return vim.tbl_contains(self.labels, label)
|
||||
end
|
||||
|
||||
function M:use(label)
|
||||
self.labels = vim.tbl_filter(function(c)
|
||||
return c ~= label
|
||||
end, self.labels)
|
||||
end
|
||||
|
||||
---@param m Flash.Match
|
||||
---@param used boolean?
|
||||
function M:label(m, used)
|
||||
if m.label ~= nil then
|
||||
return true
|
||||
end
|
||||
local pos = m.pos:id(m.win)
|
||||
local label ---@type string?
|
||||
if used then
|
||||
label = self.used[pos]
|
||||
else
|
||||
label = self.labels[1]
|
||||
end
|
||||
if label and self:valid(label) then
|
||||
self:use(label)
|
||||
local reuse = self.state.opts.label.reuse == "all"
|
||||
or (self.state.opts.label.reuse == "lowercase" and label:lower() == label)
|
||||
|
||||
if reuse then
|
||||
self.used[pos] = label
|
||||
end
|
||||
m.label = label
|
||||
end
|
||||
return #self.labels > 0
|
||||
end
|
||||
|
||||
function M:filter()
|
||||
---@type Flash.Match[]
|
||||
local ret = {}
|
||||
|
||||
local target = self.state.target
|
||||
|
||||
local from = vim.api.nvim_win_get_cursor(self.state.win)
|
||||
---@type table<number, boolean>
|
||||
local folds = {}
|
||||
|
||||
-- only label visible matches
|
||||
for _, match in ipairs(self.state.results) do
|
||||
-- and don't label the first match in the current window
|
||||
local skip = (target and match.pos == target.pos)
|
||||
and not self.state.opts.label.current
|
||||
and match.win == self.state.win
|
||||
|
||||
-- Only label the first match in each fold
|
||||
if not skip and match.fold then
|
||||
if folds[match.fold] then
|
||||
skip = true
|
||||
else
|
||||
folds[match.fold] = true
|
||||
end
|
||||
end
|
||||
|
||||
if not skip then
|
||||
table.insert(ret, match)
|
||||
end
|
||||
end
|
||||
|
||||
-- sort by current win, other win, then by distance
|
||||
table.sort(ret, function(a, b)
|
||||
local use_distance = self.state.opts.label.distance and a.win == self.state.win
|
||||
|
||||
if a.win ~= b.win then
|
||||
local aw = a.win == self.state.win and 0 or a.win
|
||||
local bw = b.win == self.state.win and 0 or b.win
|
||||
return aw < bw
|
||||
end
|
||||
if use_distance then
|
||||
local dfrom = from[1] * vim.go.columns + from[2]
|
||||
local da = a.pos[1] * vim.go.columns + a.pos[2]
|
||||
local db = b.pos[1] * vim.go.columns + b.pos[2]
|
||||
return math.abs(dfrom - da) < math.abs(dfrom - db)
|
||||
end
|
||||
if a.pos[1] ~= b.pos[1] then
|
||||
return a.pos[1] < b.pos[1]
|
||||
end
|
||||
return a.pos[2] < b.pos[2]
|
||||
end)
|
||||
return ret
|
||||
end
|
||||
|
||||
-- Returns valid labels for the current search pattern
|
||||
-- in this window.
|
||||
---@param labels string[]
|
||||
---@return string[] returns labels to skip or `nil` when all labels should be skipped
|
||||
function M:skip(win, labels)
|
||||
local pattern = self.state.pattern.skip
|
||||
|
||||
-- skip all labels if the pattern is empty
|
||||
if pattern == "" then
|
||||
return {}
|
||||
end
|
||||
|
||||
-- skip all labels if the pattern is invalid
|
||||
local ok = pcall(vim.regex, pattern)
|
||||
if not ok then
|
||||
return {}
|
||||
end
|
||||
|
||||
-- skip all labels if the pattern ends with a backslash
|
||||
-- except if it's escaped
|
||||
if pattern:find("\\$") and not pattern:find("\\\\$") then
|
||||
return {}
|
||||
end
|
||||
|
||||
vim.api.nvim_win_call(win, function()
|
||||
while #labels > 0 do
|
||||
-- this is needed, since an uppercase label would trigger smartcase
|
||||
local label_group = table.concat(labels, "")
|
||||
if vim.go.ignorecase then
|
||||
label_group = label_group:lower()
|
||||
end
|
||||
|
||||
local p = "\\%(" .. pattern .. "\\)\\m\\zs[" .. label_group .. "]"
|
||||
local pos
|
||||
ok, pos = pcall(vim.fn.searchpos, p, "cnw")
|
||||
|
||||
if not ok then
|
||||
labels = {}
|
||||
break
|
||||
end
|
||||
|
||||
-- not found, we're done
|
||||
if pos[1] == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local line = vim.api.nvim_buf_get_lines(0, pos[1] - 1, pos[1], false)[1]
|
||||
local char = vim.fn.strpart(line, pos[2] - 1, 1, true)
|
||||
|
||||
local label_count = #labels
|
||||
labels = vim.tbl_filter(function(c)
|
||||
-- when ignorecase is set, we need to skip
|
||||
-- both the upper and lower case labels
|
||||
if vim.go.ignorecase then
|
||||
return c:lower() ~= char:lower()
|
||||
end
|
||||
return c ~= char
|
||||
end, labels)
|
||||
|
||||
-- HACK: this will fail if the pattern is an incomplete regex
|
||||
-- In that case, we skip all labels
|
||||
if label_count == #labels then
|
||||
labels = {}
|
||||
break
|
||||
end
|
||||
end
|
||||
end)
|
||||
return labels
|
||||
end
|
||||
|
||||
return M
|
||||
@ -0,0 +1,299 @@
|
||||
local require = require("flash.require")
|
||||
|
||||
local Config = require("flash.config")
|
||||
local Labeler = require("flash.labeler")
|
||||
local Repeat = require("flash.repeat")
|
||||
local Util = require("flash.util")
|
||||
|
||||
local M = {}
|
||||
|
||||
---@alias Flash.Char.Motion "'f'" | "'F'" | "'t'" | "'T'"
|
||||
M.motion = "f" ---@type Flash.Char.Motion
|
||||
M.char = nil ---@type string?
|
||||
M.jumping = false
|
||||
M.state = nil ---@type Flash.State?
|
||||
M.jump_labels = false
|
||||
|
||||
---@type table<Flash.Char.Motion, Flash.State.Config>
|
||||
M.motions = {
|
||||
f = { label = { after = { 0, 0 }, before = false } },
|
||||
t = {},
|
||||
F = {
|
||||
jump = { inclusive = false },
|
||||
search = { forward = false },
|
||||
label = { after = { 0, 0 }, before = false },
|
||||
},
|
||||
T = {
|
||||
jump = { inclusive = false },
|
||||
search = { forward = false },
|
||||
label = { before = true, after = false },
|
||||
},
|
||||
}
|
||||
|
||||
function M.new()
|
||||
local State = require("flash.state")
|
||||
local opts = Config.get({
|
||||
mode = "char",
|
||||
labeler = M.labeler,
|
||||
search = {
|
||||
multi_window = false,
|
||||
mode = M.mode(M.motion),
|
||||
max_length = 1,
|
||||
},
|
||||
prompt = {
|
||||
enabled = false,
|
||||
},
|
||||
}, M.motions[M.motion])
|
||||
|
||||
-- never show the current match label
|
||||
opts.highlight.groups.current = M.motion:lower() == "f" and opts.highlight.groups.label
|
||||
or opts.highlight.groups.match
|
||||
|
||||
-- exclude the motion labels so we can use them for next/prev
|
||||
opts.labels = opts.labels:gsub(M.motion:lower(), "")
|
||||
opts.labels = opts.labels:gsub(M.motion:upper(), "")
|
||||
return State.new(opts)
|
||||
end
|
||||
|
||||
function M.labeler(matches, state)
|
||||
if M.jump_labels then
|
||||
if not state._labeler then
|
||||
state._labeler = Labeler.new(state)
|
||||
end
|
||||
state._labeler:update()
|
||||
else
|
||||
-- set to empty label, so that the character will just be highlighted
|
||||
for _, m in ipairs(matches) do
|
||||
m.label = ""
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@param motion Flash.Char.Motion
|
||||
function M.mode(motion)
|
||||
---@param c string
|
||||
return function(c)
|
||||
c = c:gsub("\\", "\\\\")
|
||||
local pattern ---@type string
|
||||
if motion == "t" then
|
||||
pattern = "\\m.\\ze\\V" .. c
|
||||
elseif motion == "T" then
|
||||
pattern = "\\V" .. c .. "\\zs\\m."
|
||||
else
|
||||
pattern = "\\V" .. c
|
||||
end
|
||||
if not Config.get("char").multi_line then
|
||||
local pos = vim.api.nvim_win_get_cursor(0)
|
||||
pattern = ("\\%%%dl"):format(pos[1]) .. pattern
|
||||
end
|
||||
|
||||
return pattern
|
||||
end
|
||||
end
|
||||
|
||||
function M.visible()
|
||||
return M.state and M.state.visible
|
||||
end
|
||||
|
||||
function M.setup()
|
||||
Repeat.setup()
|
||||
|
||||
local keys = {}
|
||||
|
||||
for k, v in pairs(Config.modes.char.keys) do
|
||||
if vim.g.mapleader ~= v and vim.g.maplocalleader ~= v then
|
||||
keys[type(k) == "number" and v or k] = v
|
||||
end
|
||||
end
|
||||
|
||||
-- don't override ;, mappings if they exist
|
||||
for _, key in ipairs({ ";", "," }) do
|
||||
local mapping = vim.fn.maparg(key, "n", false, false)
|
||||
if keys[key] == key and mapping ~= "" then
|
||||
keys[key] = nil
|
||||
end
|
||||
end
|
||||
|
||||
for _, key in ipairs({ "f", "F", "t", "T", ";", "," }) do
|
||||
if keys[key] then
|
||||
vim.keymap.set({ "n", "x", "o" }, keys[key], function()
|
||||
M.jumping = true
|
||||
local autohide = Config.get("char").autohide
|
||||
if Repeat.is_repeat then
|
||||
M.jump_labels = false -- never show jump labels when repeating
|
||||
M.state:jump({ count = vim.v.count1 })
|
||||
M.state:show()
|
||||
else
|
||||
M.jump(key)
|
||||
end
|
||||
vim.schedule(function()
|
||||
M.jumping = false
|
||||
if M.state and autohide then
|
||||
M.state:hide()
|
||||
end
|
||||
end)
|
||||
end, {
|
||||
silent = true,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
vim.api.nvim_create_autocmd({ "BufLeave", "CursorMoved", "InsertEnter" }, {
|
||||
group = vim.api.nvim_create_augroup("flash_char", { clear = true }),
|
||||
callback = function(event)
|
||||
local hide = event.event == "InsertEnter" or not M.jumping
|
||||
if hide and M.state then
|
||||
M.state:hide()
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
vim.on_key(function(key)
|
||||
if M.state and key == Util.ESC and (vim.fn.mode() == "n" or vim.fn.mode() == "v") then
|
||||
M.state:hide()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function M.parse(key)
|
||||
---@class Flash.Char.Parse
|
||||
local ret = {
|
||||
jump = M.next,
|
||||
actions = {}, ---@type table<string, fun()>
|
||||
getchar = false,
|
||||
}
|
||||
-- repeat last search when hitting the same key
|
||||
-- don't repeat when executing a macro
|
||||
if M.visible() and vim.fn.reg_executing() == "" and M.motion:lower() == key:lower() then
|
||||
ret.actions = M.actions(M.motion)
|
||||
if ret.actions[key] then
|
||||
ret.jump = ret.actions[key]
|
||||
return ret
|
||||
else
|
||||
-- no action defined, so clear the state
|
||||
M.motion = ""
|
||||
end
|
||||
end
|
||||
|
||||
-- different motion, clear the state
|
||||
if M.motions[key] and M.motion ~= key then
|
||||
if M.state then
|
||||
M.state:hide()
|
||||
end
|
||||
M.motion = key
|
||||
end
|
||||
|
||||
ret.actions = M.actions(M.motion)
|
||||
|
||||
if M.motions[key] then
|
||||
ret.getchar = true
|
||||
else -- ;,
|
||||
ret.jump = ret.actions[key] or M.next
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
---@param motion Flash.Char.Motion
|
||||
---@return table<string, fun()>
|
||||
function M.actions(motion)
|
||||
local ret = Config.get("char").char_actions(motion)
|
||||
for key, value in pairs(ret) do
|
||||
ret[key] = M[value]
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
function M.jump(key)
|
||||
local parsed = M.parse(key)
|
||||
if not M.motion then
|
||||
return
|
||||
end
|
||||
|
||||
local is_op = vim.fn.mode(true):sub(1, 2) == "no"
|
||||
|
||||
-- always re-calculate when not visible
|
||||
M.state = M.visible() and M.state or M.new()
|
||||
|
||||
-- get a new target
|
||||
if parsed.getchar or not M.char then
|
||||
local char = M.state:get_char()
|
||||
if char then
|
||||
M.char = char
|
||||
else
|
||||
return M.state:hide()
|
||||
end
|
||||
end
|
||||
|
||||
-- HACK: When the motion is t or T, we need to set the current position as a valid target
|
||||
-- but only when we are not repeating
|
||||
M.current = M.motion:lower() == "t" and parsed.getchar
|
||||
|
||||
-- update the state when needed
|
||||
if M.state.pattern:empty() then
|
||||
M.state:update({ pattern = M.char })
|
||||
end
|
||||
|
||||
local jump = parsed.jump
|
||||
|
||||
M.jump_labels = Config.get("char").jump_labels
|
||||
jump()
|
||||
M.state:update({ force = true })
|
||||
|
||||
if M.jump_labels then
|
||||
parsed.actions[Util.CR] = function()
|
||||
return false
|
||||
end
|
||||
M.state:loop({
|
||||
restore = is_op,
|
||||
abort = function()
|
||||
Util.exit()
|
||||
end,
|
||||
jump_on_max_length = false,
|
||||
actions = parsed.actions,
|
||||
})
|
||||
end
|
||||
|
||||
return M.state
|
||||
end
|
||||
|
||||
M.current = false
|
||||
|
||||
function M.right()
|
||||
return M.state.opts.search.forward and M.next() or M.prev()
|
||||
end
|
||||
|
||||
function M.left()
|
||||
return M.state.opts.search.forward and M.prev() or M.next()
|
||||
end
|
||||
|
||||
function M.next()
|
||||
M.state:jump({
|
||||
count = vim.v.count1,
|
||||
forward = M.state.opts.search.forward,
|
||||
current = M.current,
|
||||
})
|
||||
M.current = false
|
||||
return true
|
||||
end
|
||||
|
||||
function M.prev()
|
||||
M.state:jump({
|
||||
count = vim.v.count1,
|
||||
forward = not M.state.opts.search.forward,
|
||||
current = M.current,
|
||||
})
|
||||
M.current = false
|
||||
-- check if we should enable wrapping.
|
||||
if not M.state.opts.search.wrap then
|
||||
local before = M.state:find({ count = 1, forward = false })
|
||||
if before and (before.pos < M.state.pos) == M.state.opts.search.forward then
|
||||
M.state.opts.search.wrap = true
|
||||
M.state._labeler = nil
|
||||
M.state:update({ force = true })
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
return M
|
||||
@ -0,0 +1,163 @@
|
||||
local require = require("flash.require")
|
||||
|
||||
local Config = require("flash.config")
|
||||
local Jump = require("flash.jump")
|
||||
local State = require("flash.state")
|
||||
local Util = require("flash.util")
|
||||
|
||||
local M = {}
|
||||
|
||||
---@type Flash.State?
|
||||
M.state = nil
|
||||
M.op = false
|
||||
M.enabled = true
|
||||
|
||||
---@param enabled? boolean
|
||||
function M.toggle(enabled)
|
||||
if enabled == nil then
|
||||
enabled = not M.enabled
|
||||
end
|
||||
|
||||
if M.enabled == enabled then
|
||||
return M.enabled
|
||||
end
|
||||
|
||||
M.enabled = enabled
|
||||
|
||||
if State.is_search() then
|
||||
if M.enabled then
|
||||
M.start()
|
||||
M.update(false)
|
||||
elseif M.state then
|
||||
M.state:hide()
|
||||
M.state = nil
|
||||
end
|
||||
-- redraw to show the change
|
||||
vim.cmd("redraw")
|
||||
-- trigger incsearch to update the matches
|
||||
vim.api.nvim_feedkeys(" " .. Util.BS, "n", true)
|
||||
end
|
||||
return M.enabled
|
||||
end
|
||||
|
||||
---@param check_jump? boolean
|
||||
function M.update(check_jump)
|
||||
if not M.state then
|
||||
return
|
||||
end
|
||||
|
||||
local pattern = vim.fn.getcmdline()
|
||||
|
||||
-- when doing // or ??, get the pattern from the search register
|
||||
-- See :h search-commands
|
||||
if pattern:sub(1, 1) == vim.fn.getcmdtype() then
|
||||
pattern = vim.fn.getreg("/") .. pattern:sub(2)
|
||||
end
|
||||
M.state:update({ pattern = pattern, check_jump = check_jump })
|
||||
end
|
||||
|
||||
function M.start()
|
||||
M.state = State.new({
|
||||
mode = "search",
|
||||
action = M.jump,
|
||||
search = {
|
||||
forward = vim.fn.getcmdtype() == "/",
|
||||
mode = "search",
|
||||
incremental = vim.go.incsearch,
|
||||
},
|
||||
})
|
||||
if M.op then
|
||||
M.state.opts.search.multi_window = false
|
||||
end
|
||||
end
|
||||
|
||||
function M.setup()
|
||||
local group = vim.api.nvim_create_augroup("flash", { clear = true })
|
||||
M.enabled = Config.modes.search.enabled or false
|
||||
|
||||
local function wrap(fn)
|
||||
return function(...)
|
||||
if M.state then
|
||||
return fn(...)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
vim.api.nvim_create_autocmd("CmdlineChanged", {
|
||||
group = group,
|
||||
callback = wrap(function()
|
||||
M.update()
|
||||
end),
|
||||
})
|
||||
|
||||
vim.api.nvim_create_autocmd("CmdlineLeave", {
|
||||
group = group,
|
||||
callback = wrap(function()
|
||||
M.state:hide()
|
||||
M.state = nil
|
||||
end),
|
||||
})
|
||||
vim.api.nvim_create_autocmd("CmdlineEnter", {
|
||||
group = group,
|
||||
callback = function()
|
||||
if State.is_search() and M.enabled then
|
||||
M.start()
|
||||
M.set_op(vim.fn.mode() == "v")
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
vim.api.nvim_create_autocmd("ModeChanged", {
|
||||
pattern = "*:c",
|
||||
group = group,
|
||||
callback = function()
|
||||
M.set_op(vim.v.event.old_mode:sub(1, 2) == "no" or vim.fn.mode() == "v")
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
function M.set_op(op)
|
||||
M.op = op
|
||||
if M.op and M.state then
|
||||
M.state.opts.search.multi_window = false
|
||||
end
|
||||
end
|
||||
|
||||
---@param self Flash.State
|
||||
---@param match Flash.Match
|
||||
function M.jump(match, self)
|
||||
local pos = match.pos
|
||||
local search_reg = vim.fn.getreg("/")
|
||||
|
||||
-- For operator pending mode, set the search pattern to the
|
||||
-- first character on the match position
|
||||
if M.op then
|
||||
local pos_pattern = ("\\%%%dl\\%%%dc."):format(pos[1], pos[2] + 1)
|
||||
vim.fn.setcmdline(pos_pattern)
|
||||
end
|
||||
|
||||
-- schedule a <cr> input to trigger the search
|
||||
vim.schedule(function()
|
||||
vim.api.nvim_input(M.op and "<cr>" or "<esc>")
|
||||
end)
|
||||
|
||||
-- restore the real search pattern after the search
|
||||
-- and perform the jump when not in operator pending mode
|
||||
vim.api.nvim_create_autocmd("CmdlineLeave", {
|
||||
once = true,
|
||||
callback = vim.schedule_wrap(function()
|
||||
-- delete the search pattern.
|
||||
-- The correct one will be added in `on_jump`
|
||||
vim.fn.histdel("search", -1)
|
||||
if M.op then
|
||||
-- restore original search pattern
|
||||
vim.fn.setreg("/", search_reg)
|
||||
else
|
||||
Jump.jump(match, self)
|
||||
end
|
||||
Jump.on_jump(self)
|
||||
end),
|
||||
})
|
||||
end
|
||||
|
||||
return M
|
||||
@ -0,0 +1,181 @@
|
||||
local Config = require("flash.config")
|
||||
local Pos = require("flash.search.pos")
|
||||
local Repeat = require("flash.repeat")
|
||||
local Util = require("flash.util")
|
||||
|
||||
local M = {}
|
||||
|
||||
---@class Flash.Match.TS: Flash.Match
|
||||
---@field node TSNode
|
||||
---@field depth? number
|
||||
|
||||
---@param win window
|
||||
---@param pos? Pos
|
||||
function M.get_nodes(win, pos)
|
||||
local buf = vim.api.nvim_win_get_buf(win)
|
||||
local line_count = vim.api.nvim_buf_line_count(buf)
|
||||
pos = pos or Pos()
|
||||
|
||||
local nodes = {} ---@type TSNode[]
|
||||
|
||||
local ok, tree = pcall(vim.treesitter.get_parser, buf)
|
||||
if not ok then
|
||||
vim.notify(
|
||||
"No treesitter parser for this buffer with filetype=" .. vim.bo[buf].filetype,
|
||||
vim.log.levels.WARN,
|
||||
{ title = "flash.nvim" }
|
||||
)
|
||||
vim.api.nvim_input("<esc>")
|
||||
end
|
||||
if not (ok and tree) then
|
||||
return {}
|
||||
end
|
||||
|
||||
do
|
||||
-- get all ranges of the current node and its parents
|
||||
local node = tree:named_node_for_range({ pos[1] - 1, pos[2], pos[1] - 1, pos[2] }, {
|
||||
ignore_injections = false,
|
||||
})
|
||||
|
||||
while node do
|
||||
nodes[#nodes + 1] = node
|
||||
node = node:parent() ---@type TSNode
|
||||
end
|
||||
end
|
||||
|
||||
-- convert ranges to matches
|
||||
---@type Flash.Match.TS[]
|
||||
local ret = {}
|
||||
local first = true
|
||||
---@type table<string,boolean>
|
||||
local done = {}
|
||||
for _, node in ipairs(nodes) do
|
||||
local range = { node:range() }
|
||||
---@type Flash.Match.TS
|
||||
local match = {
|
||||
node = node,
|
||||
pos = { range[1] + 1, range[2] },
|
||||
end_pos = { range[3] + 1, range[4] - 1 },
|
||||
first = first,
|
||||
}
|
||||
first = false
|
||||
-- If the match is at the end of the buffer,
|
||||
-- then move it to the last character of the last line.
|
||||
if match.end_pos[1] > line_count then
|
||||
match.end_pos[1] = line_count
|
||||
match.end_pos[2] =
|
||||
#vim.api.nvim_buf_get_lines(buf, match.end_pos[1] - 1, match.end_pos[1], false)[1]
|
||||
elseif match.end_pos[2] == -1 then
|
||||
-- If the end points to the start of the next line, move it to the
|
||||
-- end of the previous line.
|
||||
-- Otherwise operations include the first character of the next line
|
||||
local line =
|
||||
vim.api.nvim_buf_get_lines(buf, match.end_pos[1] - 2, match.end_pos[1] - 1, false)[1]
|
||||
match.end_pos[1] = match.end_pos[1] - 1
|
||||
match.end_pos[2] = #line
|
||||
end
|
||||
local id = table.concat(vim.tbl_flatten({ match.pos, match.end_pos }), ".")
|
||||
if not done[id] then
|
||||
done[id] = true
|
||||
ret[#ret + 1] = match
|
||||
end
|
||||
end
|
||||
|
||||
for m, match in ipairs(ret) do
|
||||
match.pos = Pos(match.pos)
|
||||
match.end_pos = Pos(match.end_pos)
|
||||
match.win = win
|
||||
match.depth = #ret - m
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
---@param win window
|
||||
---@param state Flash.State
|
||||
function M.matcher(win, state)
|
||||
local labels = state:labels()
|
||||
local ret = M.get_nodes(win, state.pos)
|
||||
|
||||
for i = 1, #ret do
|
||||
ret[i].label = table.remove(labels, 1)
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
---@param opts? Flash.Config
|
||||
function M.jump(opts)
|
||||
local state = Repeat.get_state(
|
||||
"treesitter",
|
||||
Config.get({ mode = "treesitter" }, opts, {
|
||||
matcher = M.matcher,
|
||||
labeler = function() end,
|
||||
search = { multi_window = false, wrap = true, incremental = false, max_length = 0 },
|
||||
})
|
||||
)
|
||||
|
||||
---@type Flash.Match?
|
||||
local current
|
||||
for _, m in ipairs(state.results) do
|
||||
---@cast m Flash.Match.TS
|
||||
if not current or m.depth > current.depth then
|
||||
current = m
|
||||
end
|
||||
end
|
||||
current = state:jump(current)
|
||||
|
||||
state:loop({
|
||||
abort = function()
|
||||
vim.cmd([[normal! v]])
|
||||
end,
|
||||
actions = {
|
||||
[";"] = function()
|
||||
current = state:jump({ match = current, forward = false })
|
||||
end,
|
||||
[","] = function()
|
||||
current = state:jump({ forward = true, match = current })
|
||||
end,
|
||||
[Util.CR] = function()
|
||||
state:jump(current and current.label or nil)
|
||||
return false
|
||||
end,
|
||||
},
|
||||
jump_on_max_length = false,
|
||||
})
|
||||
|
||||
return state
|
||||
end
|
||||
|
||||
---@param opts? Flash.Config
|
||||
function M.search(opts)
|
||||
opts = Config.get({ mode = "treesitter_search" }, opts, {
|
||||
matcher = function(win, _state, _opts)
|
||||
local Search = require("flash.search")
|
||||
local search = Search.new(win, _state)
|
||||
local matches = {} ---@type Flash.Match[]
|
||||
for _, m in ipairs(search:get(_opts)) do
|
||||
-- don't add labels to the search results
|
||||
m.label = false
|
||||
table.insert(matches, m)
|
||||
for _, n in ipairs(M.get_nodes(win, m.pos)) do
|
||||
-- don't highlight treesitter nodes. Use labels only
|
||||
n.highlight = false
|
||||
table.insert(matches, n)
|
||||
end
|
||||
end
|
||||
return matches
|
||||
end,
|
||||
jump = { pos = "range" },
|
||||
})
|
||||
|
||||
opts.search.exclude = vim.deepcopy(opts.search.exclude)
|
||||
table.insert(opts.search.exclude, function(win)
|
||||
local buf = vim.api.nvim_win_get_buf(win)
|
||||
return not pcall(vim.treesitter.get_parser, buf)
|
||||
end)
|
||||
|
||||
local state = Repeat.get_state("treesitter-search", opts)
|
||||
state:loop()
|
||||
return state
|
||||
end
|
||||
|
||||
return M
|
||||
@ -0,0 +1,85 @@
|
||||
local Config = require("flash.config")
|
||||
|
||||
---@class Flash.Prompt
|
||||
---@field win window
|
||||
---@field buf buffer
|
||||
local M = {}
|
||||
|
||||
local ns = vim.api.nvim_create_namespace("flash_prompt")
|
||||
|
||||
function M.visible()
|
||||
return M.win and vim.api.nvim_win_is_valid(M.win) and M.buf and vim.api.nvim_buf_is_valid(M.buf)
|
||||
end
|
||||
|
||||
function M.show()
|
||||
if M.visible() then
|
||||
return
|
||||
end
|
||||
require("flash.highlight")
|
||||
|
||||
M.buf = vim.api.nvim_create_buf(false, true)
|
||||
vim.bo[M.buf].buftype = "nofile"
|
||||
vim.bo[M.buf].bufhidden = "wipe"
|
||||
vim.bo[M.buf].filetype = "flash_prompt"
|
||||
|
||||
local config = vim.deepcopy(Config.prompt.win_config)
|
||||
|
||||
if config.width <= 1 then
|
||||
config.width = config.width * vim.go.columns
|
||||
end
|
||||
|
||||
if config.row < 0 then
|
||||
config.row = vim.go.lines + config.row
|
||||
end
|
||||
|
||||
if config.col < 0 then
|
||||
config.col = vim.go.columns + config.col
|
||||
end
|
||||
|
||||
config = vim.tbl_extend("force", config, {
|
||||
style = "minimal",
|
||||
focusable = false,
|
||||
noautocmd = true,
|
||||
})
|
||||
|
||||
M.win = vim.api.nvim_open_win(M.buf, false, config)
|
||||
vim.wo[M.win].winhighlight = "Normal:FlashPrompt"
|
||||
end
|
||||
|
||||
function M.hide()
|
||||
if M.win and vim.api.nvim_win_is_valid(M.win) then
|
||||
vim.api.nvim_win_close(M.win, true)
|
||||
M.win = nil
|
||||
end
|
||||
if M.buf and vim.api.nvim_buf_is_valid(M.buf) then
|
||||
vim.api.nvim_buf_delete(M.buf, { force = true })
|
||||
M.buf = nil
|
||||
end
|
||||
end
|
||||
|
||||
---@param pattern string
|
||||
function M.set(pattern)
|
||||
M.show()
|
||||
local text = vim.deepcopy(Config.prompt.prefix)
|
||||
text[#text + 1] = { pattern }
|
||||
|
||||
local str = ""
|
||||
for _, item in ipairs(text) do
|
||||
str = str .. item[1]
|
||||
end
|
||||
vim.api.nvim_buf_set_lines(M.buf, 0, -1, false, { str })
|
||||
vim.api.nvim_buf_clear_namespace(M.buf, ns, 0, -1)
|
||||
local col = 0
|
||||
for _, item in ipairs(text) do
|
||||
local width = vim.fn.strlen(item[1])
|
||||
if item[2] then
|
||||
vim.api.nvim_buf_set_extmark(M.buf, ns, 0, col, {
|
||||
hl_group = item[2],
|
||||
end_col = col + width,
|
||||
})
|
||||
end
|
||||
col = col + width
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
@ -0,0 +1,401 @@
|
||||
---@class Flash.Rainbow
|
||||
---@field cache table<string, string>
|
||||
---@field count number
|
||||
---@field shade number
|
||||
local M = {}
|
||||
|
||||
---@type table<string,true>
|
||||
M.hl = {}
|
||||
|
||||
function M.setup()
|
||||
if M.did_setup then
|
||||
return
|
||||
end
|
||||
M.did_setup = true
|
||||
vim.api.nvim_create_autocmd("ColorScheme", {
|
||||
callback = function()
|
||||
M.hl = {}
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
---@param state Flash.State
|
||||
function M.new(state)
|
||||
local self = setmetatable({}, { __index = M })
|
||||
self.cache = {}
|
||||
self.count = 0
|
||||
self.shade = state.opts.label.rainbow.shade
|
||||
return self
|
||||
end
|
||||
|
||||
---@param match Flash.Match
|
||||
function M:get(match)
|
||||
local buf = vim.api.nvim_win_get_buf(match.win)
|
||||
local id = match.pos:id(buf)
|
||||
if match.depth then
|
||||
id = id .. ":" .. tostring(match.depth)
|
||||
end
|
||||
|
||||
if not self.cache[id] then
|
||||
self.count = self.count + 1
|
||||
self.cache[id] = M.get_color(self.count, self.shade)
|
||||
end
|
||||
return self.cache[id]
|
||||
end
|
||||
|
||||
---@param idx number
|
||||
---@param shade number
|
||||
function M.get_color(idx, shade)
|
||||
M.setup()
|
||||
idx = (idx - 1) % #M.rainbow + 1
|
||||
local color = M.rainbow[idx]
|
||||
shade = (shade or 5) * 100
|
||||
local bg = vim.tbl_get(M.colors, color, shade)
|
||||
if bg then
|
||||
local hl = "FlashColor" .. color .. shade
|
||||
if not M.hl[hl] then
|
||||
M.hl[hl] = true
|
||||
local bg_shade = shade == 500 and 950 or shade < 500 and 900 or 50
|
||||
local fg = vim.tbl_get(M.colors, color, bg_shade)
|
||||
vim.api.nvim_set_hl(0, hl, { bg = "#" .. bg, fg = "#" .. fg, bold = true })
|
||||
end
|
||||
return hl
|
||||
end
|
||||
end
|
||||
|
||||
M.rainbow = {
|
||||
-- "slate",
|
||||
-- "gray",
|
||||
-- "zinc",
|
||||
-- "neutral",
|
||||
-- "stone",
|
||||
"red",
|
||||
-- "orange",
|
||||
"amber",
|
||||
-- "yellow",
|
||||
"lime",
|
||||
"green",
|
||||
-- "emerald",
|
||||
"teal",
|
||||
"cyan",
|
||||
-- "sky",
|
||||
"blue",
|
||||
-- "indigo",
|
||||
"violet",
|
||||
-- "purple",
|
||||
"fuchsia",
|
||||
-- "pink",
|
||||
"rose",
|
||||
}
|
||||
|
||||
M.colors = {
|
||||
slate = {
|
||||
[50] = "f8fafc",
|
||||
[100] = "f1f5f9",
|
||||
[200] = "e2e8f0",
|
||||
[300] = "cbd5e1",
|
||||
[400] = "94a3b8",
|
||||
[500] = "64748b",
|
||||
[600] = "475569",
|
||||
[700] = "334155",
|
||||
[800] = "1e293b",
|
||||
[900] = "0f172a",
|
||||
[950] = "020617",
|
||||
},
|
||||
|
||||
gray = {
|
||||
[50] = "f9fafb",
|
||||
[100] = "f3f4f6",
|
||||
[200] = "e5e7eb",
|
||||
[300] = "d1d5db",
|
||||
[400] = "9ca3af",
|
||||
[500] = "6b7280",
|
||||
[600] = "4b5563",
|
||||
[700] = "374151",
|
||||
[800] = "1f2937",
|
||||
[900] = "111827",
|
||||
[950] = "030712",
|
||||
},
|
||||
|
||||
zinc = {
|
||||
[50] = "fafafa",
|
||||
[100] = "f4f4f5",
|
||||
[200] = "e4e4e7",
|
||||
[300] = "d4d4d8",
|
||||
[400] = "a1a1aa",
|
||||
[500] = "71717a",
|
||||
[600] = "52525b",
|
||||
[700] = "3f3f46",
|
||||
[800] = "27272a",
|
||||
[900] = "18181b",
|
||||
[950] = "09090B",
|
||||
},
|
||||
|
||||
neutral = {
|
||||
[50] = "fafafa",
|
||||
[100] = "f5f5f5",
|
||||
[200] = "e5e5e5",
|
||||
[300] = "d4d4d4",
|
||||
[400] = "a3a3a3",
|
||||
[500] = "737373",
|
||||
[600] = "525252",
|
||||
[700] = "404040",
|
||||
[800] = "262626",
|
||||
[900] = "171717",
|
||||
[950] = "0a0a0a",
|
||||
},
|
||||
|
||||
stone = {
|
||||
[50] = "fafaf9",
|
||||
[100] = "f5f5f4",
|
||||
[200] = "e7e5e4",
|
||||
[300] = "d6d3d1",
|
||||
[400] = "a8a29e",
|
||||
[500] = "78716c",
|
||||
[600] = "57534e",
|
||||
[700] = "44403c",
|
||||
[800] = "292524",
|
||||
[900] = "1c1917",
|
||||
[950] = "0a0a0a",
|
||||
},
|
||||
|
||||
red = {
|
||||
[50] = "fef2f2",
|
||||
[100] = "fee2e2",
|
||||
[200] = "fecaca",
|
||||
[300] = "fca5a5",
|
||||
[400] = "f87171",
|
||||
[500] = "ef4444",
|
||||
[600] = "dc2626",
|
||||
[700] = "b91c1c",
|
||||
[800] = "991b1b",
|
||||
[900] = "7f1d1d",
|
||||
[950] = "450a0a",
|
||||
},
|
||||
|
||||
orange = {
|
||||
[50] = "fff7ed",
|
||||
[100] = "ffedd5",
|
||||
[200] = "fed7aa",
|
||||
[300] = "fdba74",
|
||||
[400] = "fb923c",
|
||||
[500] = "f97316",
|
||||
[600] = "ea580c",
|
||||
[700] = "c2410c",
|
||||
[800] = "9a3412",
|
||||
[900] = "7c2d12",
|
||||
[950] = "431407",
|
||||
},
|
||||
|
||||
amber = {
|
||||
[50] = "fffbeb",
|
||||
[100] = "fef3c7",
|
||||
[200] = "fde68a",
|
||||
[300] = "fcd34d",
|
||||
[400] = "fbbf24",
|
||||
[500] = "f59e0b",
|
||||
[600] = "d97706",
|
||||
[700] = "b45309",
|
||||
[800] = "92400e",
|
||||
[900] = "78350f",
|
||||
[950] = "451a03",
|
||||
},
|
||||
|
||||
yellow = {
|
||||
[50] = "fefce8",
|
||||
[100] = "fef9c3",
|
||||
[200] = "fef08a",
|
||||
[300] = "fde047",
|
||||
[400] = "facc15",
|
||||
[500] = "eab308",
|
||||
[600] = "ca8a04",
|
||||
[700] = "a16207",
|
||||
[800] = "854d0e",
|
||||
[900] = "713f12",
|
||||
[950] = "422006",
|
||||
},
|
||||
|
||||
lime = {
|
||||
[50] = "f7fee7",
|
||||
[100] = "ecfccb",
|
||||
[200] = "d9f99d",
|
||||
[300] = "bef264",
|
||||
[400] = "a3e635",
|
||||
[500] = "84cc16",
|
||||
[600] = "65a30d",
|
||||
[700] = "4d7c0f",
|
||||
[800] = "3f6212",
|
||||
[900] = "365314",
|
||||
[950] = "1a2e05",
|
||||
},
|
||||
|
||||
green = {
|
||||
[50] = "f0fdf4",
|
||||
[100] = "dcfce7",
|
||||
[200] = "bbf7d0",
|
||||
[300] = "86efac",
|
||||
[400] = "4ade80",
|
||||
[500] = "22c55e",
|
||||
[600] = "16a34a",
|
||||
[700] = "15803d",
|
||||
[800] = "166534",
|
||||
[900] = "14532d",
|
||||
[950] = "052e16",
|
||||
},
|
||||
|
||||
emerald = {
|
||||
[50] = "ecfdf5",
|
||||
[100] = "d1fae5",
|
||||
[200] = "a7f3d0",
|
||||
[300] = "6ee7b7",
|
||||
[400] = "34d399",
|
||||
[500] = "10b981",
|
||||
[600] = "059669",
|
||||
[700] = "047857",
|
||||
[800] = "065f46",
|
||||
[900] = "064e3b",
|
||||
[950] = "022c22",
|
||||
},
|
||||
|
||||
teal = {
|
||||
[50] = "f0fdfa",
|
||||
[100] = "ccfbf1",
|
||||
[200] = "99f6e4",
|
||||
[300] = "5eead4",
|
||||
[400] = "2dd4bf",
|
||||
[500] = "14b8a6",
|
||||
[600] = "0d9488",
|
||||
[700] = "0f766e",
|
||||
[800] = "115e59",
|
||||
[900] = "134e4a",
|
||||
[950] = "042f2e",
|
||||
},
|
||||
|
||||
cyan = {
|
||||
[50] = "ecfeff",
|
||||
[100] = "cffafe",
|
||||
[200] = "a5f3fc",
|
||||
[300] = "67e8f9",
|
||||
[400] = "22d3ee",
|
||||
[500] = "06b6d4",
|
||||
[600] = "0891b2",
|
||||
[700] = "0e7490",
|
||||
[800] = "155e75",
|
||||
[900] = "164e63",
|
||||
[950] = "083344",
|
||||
},
|
||||
|
||||
sky = {
|
||||
[50] = "f0f9ff",
|
||||
[100] = "e0f2fe",
|
||||
[200] = "bae6fd",
|
||||
[300] = "7dd3fc",
|
||||
[400] = "38bdf8",
|
||||
[500] = "0ea5e9",
|
||||
[600] = "0284c7",
|
||||
[700] = "0369a1",
|
||||
[800] = "075985",
|
||||
[900] = "0c4a6e",
|
||||
[950] = "082f49",
|
||||
},
|
||||
|
||||
blue = {
|
||||
[50] = "eff6ff",
|
||||
[100] = "dbeafe",
|
||||
[200] = "bfdbfe",
|
||||
[300] = "93c5fd",
|
||||
[400] = "60a5fa",
|
||||
[500] = "3b82f6",
|
||||
[600] = "2563eb",
|
||||
[700] = "1d4ed8",
|
||||
[800] = "1e40af",
|
||||
[900] = "1e3a8a",
|
||||
[950] = "172554",
|
||||
},
|
||||
|
||||
indigo = {
|
||||
[50] = "eef2ff",
|
||||
[100] = "e0e7ff",
|
||||
[200] = "c7d2fe",
|
||||
[300] = "a5b4fc",
|
||||
[400] = "818cf8",
|
||||
[500] = "6366f1",
|
||||
[600] = "4f46e5",
|
||||
[700] = "4338ca",
|
||||
[800] = "3730a3",
|
||||
[900] = "312e81",
|
||||
[950] = "1e1b4b",
|
||||
},
|
||||
|
||||
violet = {
|
||||
[50] = "f5f3ff",
|
||||
[100] = "ede9fe",
|
||||
[200] = "ddd6fe",
|
||||
[300] = "c4b5fd",
|
||||
[400] = "a78bfa",
|
||||
[500] = "8b5cf6",
|
||||
[600] = "7c3aed",
|
||||
[700] = "6d28d9",
|
||||
[800] = "5b21b6",
|
||||
[900] = "4c1d95",
|
||||
[950] = "2e1065",
|
||||
},
|
||||
|
||||
purple = {
|
||||
[50] = "faf5ff",
|
||||
[100] = "f3e8ff",
|
||||
[200] = "e9d5ff",
|
||||
[300] = "d8b4fe",
|
||||
[400] = "c084fc",
|
||||
[500] = "a855f7",
|
||||
[600] = "9333ea",
|
||||
[700] = "7e22ce",
|
||||
[800] = "6b21a8",
|
||||
[900] = "581c87",
|
||||
[950] = "3b0764",
|
||||
},
|
||||
|
||||
fuchsia = {
|
||||
[50] = "fdf4ff",
|
||||
[100] = "fae8ff",
|
||||
[200] = "f5d0fe",
|
||||
[300] = "f0abfc",
|
||||
[400] = "e879f9",
|
||||
[500] = "d946ef",
|
||||
[600] = "c026d3",
|
||||
[700] = "a21caf",
|
||||
[800] = "86198f",
|
||||
[900] = "701a75",
|
||||
[950] = "4a044e",
|
||||
},
|
||||
|
||||
pink = {
|
||||
[50] = "fdf2f8",
|
||||
[100] = "fce7f3",
|
||||
[200] = "fbcfe8",
|
||||
[300] = "f9a8d4",
|
||||
[400] = "f472b6",
|
||||
[500] = "ec4899",
|
||||
[600] = "db2777",
|
||||
[700] = "be185d",
|
||||
[800] = "9d174d",
|
||||
[900] = "831843",
|
||||
[950] = "500724",
|
||||
},
|
||||
|
||||
rose = {
|
||||
[50] = "fff1f2",
|
||||
[100] = "ffe4e6",
|
||||
[200] = "fecdd3",
|
||||
[300] = "fda4af",
|
||||
[400] = "fb7185",
|
||||
[500] = "f43f5e",
|
||||
[600] = "e11d48",
|
||||
[700] = "be123c",
|
||||
[800] = "9f1239",
|
||||
[900] = "881337",
|
||||
[950] = "4c0519",
|
||||
},
|
||||
}
|
||||
|
||||
return M
|
||||
@ -0,0 +1,55 @@
|
||||
local require = require("flash.require")
|
||||
|
||||
local State = require("flash.state")
|
||||
|
||||
local M = {}
|
||||
|
||||
---@type {is_repeat:boolean, fn:fun()}[]
|
||||
M._funcs = {}
|
||||
M._repeat = nil
|
||||
|
||||
-- Sets the current operatorfunc to the given function.
|
||||
function M.set(fn)
|
||||
vim.go.operatorfunc = [[{x -> x}]]
|
||||
local visual = vim.fn.mode() == "v"
|
||||
vim.cmd("normal! g@l")
|
||||
if visual then
|
||||
vim.cmd("normal! gv")
|
||||
end
|
||||
M._repeat = fn
|
||||
vim.go.operatorfunc = [[v:lua.require'flash.repeat'._repeat]]
|
||||
end
|
||||
|
||||
M.is_repeat = false
|
||||
function M.setup()
|
||||
if M._did_setup then
|
||||
return
|
||||
end
|
||||
M._did_setup = true
|
||||
vim.on_key(function(key)
|
||||
if key == "." and vim.fn.reg_executing() == "" and vim.fn.reg_recording() == "" then
|
||||
M.is_repeat = true
|
||||
vim.schedule(function()
|
||||
M.is_repeat = false
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
---@type table<string, Flash.State>
|
||||
M._states = {}
|
||||
|
||||
---@param mode string
|
||||
---@param opts? Flash.State.Config
|
||||
function M.get_state(mode, opts)
|
||||
M.setup()
|
||||
local last = M._states[mode]
|
||||
if (M.is_repeat or (opts and opts.continue)) and last then
|
||||
last:show()
|
||||
return last
|
||||
end
|
||||
M._states[mode] = State.new(opts)
|
||||
return M._states[mode]
|
||||
end
|
||||
|
||||
return M
|
||||
@ -0,0 +1,25 @@
|
||||
return function(module)
|
||||
local mod = nil
|
||||
|
||||
local function load()
|
||||
if not mod then
|
||||
mod = require(module)
|
||||
package.loaded[module] = mod
|
||||
end
|
||||
return mod
|
||||
end
|
||||
-- if already loaded, return the module
|
||||
-- otherwise return a lazy module
|
||||
return type(package.loaded[module]) == "table" and package.loaded[module]
|
||||
or setmetatable({}, {
|
||||
__index = function(_, key)
|
||||
return load()[key]
|
||||
end,
|
||||
__newindex = function(_, key, value)
|
||||
load()[key] = value
|
||||
end,
|
||||
__call = function(_, ...)
|
||||
return load()(...)
|
||||
end,
|
||||
})
|
||||
end
|
||||
@ -0,0 +1,117 @@
|
||||
local require = require("flash.require")
|
||||
|
||||
local Hacks = require("flash.hacks")
|
||||
local Matcher = require("flash.search.matcher")
|
||||
local Pos = require("flash.search.pos")
|
||||
|
||||
---@class Flash.Search: Flash.Matcher
|
||||
---@field state Flash.State
|
||||
---@field win window
|
||||
local M = {}
|
||||
M.__index = M
|
||||
|
||||
---@param win number
|
||||
---@param state Flash.State
|
||||
function M.new(win, state)
|
||||
local self = setmetatable({}, M)
|
||||
self.state = state
|
||||
self.win = win
|
||||
return self
|
||||
end
|
||||
|
||||
---@param flags? string
|
||||
---@return Flash.Match?
|
||||
function M:_next(flags)
|
||||
flags = flags or ""
|
||||
local ok, pos = pcall(vim.fn.searchpos, self.state.pattern.search, flags or "")
|
||||
-- incomplete or invalid pattern
|
||||
if not ok then
|
||||
return
|
||||
end
|
||||
if pos[1] == 0 then
|
||||
return
|
||||
end
|
||||
pos = Pos({ pos[1], pos[2] - 1 })
|
||||
return { win = self.win, pos = pos, end_pos = Hacks.get_end_pos(pos) }
|
||||
end
|
||||
|
||||
---@param pos Pos
|
||||
---@param fn function
|
||||
function M:_call(pos, fn)
|
||||
pos = Pos(pos)
|
||||
|
||||
local view = vim.api.nvim_win_call(self.win, vim.fn.winsaveview)
|
||||
local buf = vim.api.nvim_win_get_buf(self.win)
|
||||
local line_count = vim.api.nvim_buf_line_count(buf)
|
||||
if pos[1] > line_count then
|
||||
pos[1] = line_count
|
||||
local line = vim.api.nvim_buf_get_lines(buf, pos[1] - 1, pos[1], false)[1]
|
||||
pos[2] = #line - 1
|
||||
end
|
||||
vim.api.nvim_win_set_cursor(self.win, pos)
|
||||
---@type boolean, any?
|
||||
local ok, err
|
||||
vim.api.nvim_win_call(self.win, function()
|
||||
ok, err = pcall(fn)
|
||||
vim.fn.winrestview(view)
|
||||
end)
|
||||
return not ok and error(err) or err
|
||||
end
|
||||
|
||||
---@param opts? {from?:Pos, to?:Pos}
|
||||
function M:get(opts)
|
||||
if self.state.pattern:empty() then
|
||||
return {}
|
||||
end
|
||||
|
||||
opts = opts or {}
|
||||
opts.from = opts.from and Pos(opts.from) or nil
|
||||
opts.to = opts.to and Pos(opts.to) or nil
|
||||
|
||||
---@type Flash.Match[]
|
||||
local ret = {}
|
||||
|
||||
self:_call(opts.from or { 1, 0 }, function()
|
||||
local next = self:_next("cW")
|
||||
while next and (not opts.to or next.pos <= opts.to) do
|
||||
table.insert(ret, next)
|
||||
next = self:_next("W")
|
||||
end
|
||||
end)
|
||||
return ret
|
||||
end
|
||||
|
||||
-- Moves the results cursor by `amount` (default 1) and wraps around.
|
||||
-- When forward is `nil` it uses the current search direction.
|
||||
-- Otherwise it uses the given direction.
|
||||
---@param opts? Flash.Match.Find
|
||||
function M:find(opts)
|
||||
if self.state.pattern:empty() then
|
||||
return
|
||||
end
|
||||
|
||||
opts = Matcher.defaults(opts)
|
||||
local flags = (opts.forward and "" or "b")
|
||||
.. (opts.wrap and "w" or "W")
|
||||
.. ((opts.count == 0 or opts.current) and "c" or "")
|
||||
if opts.match then
|
||||
opts.pos = opts.match.pos
|
||||
end
|
||||
|
||||
---@type Flash.Match?
|
||||
local ret
|
||||
|
||||
self:_call(opts.pos, function()
|
||||
for _ = 1, math.max(opts.count, 1) do
|
||||
ret = self:_next(flags)
|
||||
flags = flags:gsub("c", "")
|
||||
end
|
||||
end)
|
||||
|
||||
if not ret or (opts.count == 0 and ret.pos ~= opts.pos) then
|
||||
return
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
return M
|
||||
@ -0,0 +1,179 @@
|
||||
local Pos = require("flash.search.pos")
|
||||
|
||||
---@class Flash.Match
|
||||
---@field win window
|
||||
---@field pos Pos -- (1,0) indexed
|
||||
---@field end_pos Pos -- (1,0) indexed
|
||||
---@field label? string|false -- set to false to disable label
|
||||
---@field highlight? boolean
|
||||
---@field fold? number
|
||||
|
||||
---@alias Flash.Match.Find {forward?:boolean, wrap?:boolean, count?:number, pos?: Pos, match?:Flash.Match, current?:boolean}
|
||||
|
||||
---@class Flash.Matcher
|
||||
---@field win window
|
||||
---@field get fun(self, opts?: {from?:Pos, to?:Pos}): Flash.Match[]
|
||||
---@field find fun(self, opts?: Flash.Match.Find): Flash.Match
|
||||
---@field labels fun(self, labels: string[]): string[]
|
||||
---@field update? fun(self)
|
||||
|
||||
---@class Flash.Matcher.Custom: Flash.Matcher
|
||||
---@field matches Flash.Match[]
|
||||
local M = {}
|
||||
M.__index = M
|
||||
|
||||
function M.new(win)
|
||||
local self = setmetatable({}, M)
|
||||
self.matches = {}
|
||||
self.win = win
|
||||
return self
|
||||
end
|
||||
|
||||
---@param fn fun(win: window, state:Flash.State, opts: {from:Pos, to:Pos}): Flash.Match[]
|
||||
function M.from(fn)
|
||||
---@param win window
|
||||
---@param state Flash.State
|
||||
return function(win, state)
|
||||
local ret = M.new(win)
|
||||
ret.get = function(self, opts)
|
||||
local matches = fn(win, state, opts)
|
||||
if state.opts.filter then
|
||||
matches = state.opts.filter(matches, state) or matches
|
||||
end
|
||||
self:set(matches)
|
||||
return M.get(self, opts)
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
end
|
||||
|
||||
---@param ...? Flash.Match.Find
|
||||
---@return Flash.Match.Find
|
||||
function M.defaults(...)
|
||||
local other = vim.tbl_filter(function(k)
|
||||
return k ~= nil
|
||||
end, { ... })
|
||||
|
||||
local opts = vim.tbl_extend("force", {
|
||||
pos = vim.api.nvim_win_get_cursor(0),
|
||||
forward = true,
|
||||
wrap = true,
|
||||
count = 1,
|
||||
}, {}, unpack(other))
|
||||
opts.pos = Pos(opts.pos)
|
||||
return opts
|
||||
end
|
||||
|
||||
---@param opts? Flash.Match.Find
|
||||
function M:find(opts)
|
||||
opts = M.defaults(opts)
|
||||
|
||||
if opts.count == 0 then
|
||||
for _, match in ipairs(self.matches) do
|
||||
if match.pos == opts.pos then
|
||||
return match
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
---@type number?
|
||||
local idx
|
||||
|
||||
if opts.match then
|
||||
for m, match in ipairs(self.matches) do
|
||||
if match.pos == opts.match.pos and match.end_pos == opts.match.end_pos then
|
||||
idx = m + (opts.forward and 1 or -1)
|
||||
break
|
||||
end
|
||||
end
|
||||
elseif opts.forward then
|
||||
for i = 1, #self.matches, 1 do
|
||||
if self.matches[i].pos > opts.pos then
|
||||
idx = i
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
for i = #self.matches, 1, -1 do
|
||||
if self.matches[i].pos < opts.pos then
|
||||
idx = i
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not idx then
|
||||
if not opts.wrap then
|
||||
return
|
||||
end
|
||||
idx = opts.forward and 1 or #self.matches
|
||||
end
|
||||
|
||||
if opts.forward then
|
||||
idx = idx + opts.count - 1
|
||||
else
|
||||
idx = idx - opts.count + 1
|
||||
end
|
||||
|
||||
if opts.wrap then
|
||||
idx = (idx - 1) % #self.matches + 1
|
||||
end
|
||||
return self.matches[idx]
|
||||
end
|
||||
|
||||
---@param labels string[]
|
||||
function M:labels(labels)
|
||||
return labels
|
||||
end
|
||||
|
||||
---@param opts? {from?:Pos, to?:Pos}
|
||||
function M:get(opts)
|
||||
return M.filter(self.matches, opts)
|
||||
end
|
||||
|
||||
---@param matches Flash.Match[]
|
||||
---@param opts? {from?:Pos, to?:Pos}
|
||||
function M.filter(matches, opts)
|
||||
opts = opts or {}
|
||||
opts.from = opts.from and Pos(opts.from)
|
||||
opts.to = opts.to and Pos(opts.to)
|
||||
---@param match Flash.Match
|
||||
return vim.tbl_filter(function(match)
|
||||
if opts.from and match.end_pos < opts.from then
|
||||
return false
|
||||
end
|
||||
if opts.to and match.pos > opts.to then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end, matches)
|
||||
end
|
||||
|
||||
---@param matches Flash.Match[]
|
||||
function M:set(matches)
|
||||
for _, match in ipairs(matches) do
|
||||
match.pos = Pos(match.pos)
|
||||
match.end_pos = Pos(match.end_pos)
|
||||
match.win = match.win or self.win
|
||||
end
|
||||
|
||||
table.sort(matches, function(a, b)
|
||||
if a.win ~= b.win then
|
||||
return a.win < b.win
|
||||
end
|
||||
if a.pos ~= b.pos then
|
||||
return a.pos < b.pos
|
||||
end
|
||||
local da = a.depth or 0
|
||||
local db = b.depth or 0
|
||||
if da ~= db then
|
||||
return da < db
|
||||
end
|
||||
return a.end_pos < b.end_pos
|
||||
end)
|
||||
self.matches = matches
|
||||
end
|
||||
|
||||
return M
|
||||
@ -0,0 +1,108 @@
|
||||
local Util = require("flash.util")
|
||||
|
||||
---@class Flash.Pattern
|
||||
---@field pattern string
|
||||
---@field search string
|
||||
---@field skip string
|
||||
---@field trigger string
|
||||
---@field mode Flash.Pattern.Mode
|
||||
---@operator call:string Returns the input pattern
|
||||
local M = {}
|
||||
M.__index = M
|
||||
|
||||
---@alias Flash.Pattern.Mode "exact" | "fuzzy" | "search" | (fun(input:string):string,string?)
|
||||
|
||||
---@param pattern string
|
||||
---@param mode Flash.Pattern.Mode
|
||||
---@param trigger string
|
||||
function M.new(pattern, mode, trigger)
|
||||
local self = setmetatable({}, M)
|
||||
self.mode = mode
|
||||
self.trigger = trigger or ""
|
||||
self:set(pattern or "")
|
||||
return self
|
||||
end
|
||||
|
||||
function M:__eq(other)
|
||||
return other and other.pattern == self.pattern and other.mode == self.mode
|
||||
end
|
||||
|
||||
function M:clone()
|
||||
return M.new(self.pattern, self.mode, self.trigger)
|
||||
end
|
||||
|
||||
function M:empty()
|
||||
return self.pattern == ""
|
||||
end
|
||||
|
||||
---@param pattern string
|
||||
---@return boolean updated
|
||||
function M:set(pattern)
|
||||
if pattern ~= self.pattern then
|
||||
self.pattern = pattern
|
||||
if pattern == "" then
|
||||
self.search = ""
|
||||
self.skip = ""
|
||||
else
|
||||
if self.trigger ~= "" and pattern:sub(-1) == self.trigger then
|
||||
pattern = pattern:sub(1, -2)
|
||||
end
|
||||
self.search, self.skip = M._get(pattern, self.mode)
|
||||
end
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
---@param char string
|
||||
function M:extend(char)
|
||||
if char == Util.BS then
|
||||
return self.pattern:sub(1, -2)
|
||||
end
|
||||
return self.pattern .. char
|
||||
end
|
||||
|
||||
---@return string the input pattern
|
||||
function M:__call()
|
||||
return self.pattern
|
||||
end
|
||||
|
||||
---@param pattern string
|
||||
---@param mode Flash.Pattern.Mode
|
||||
---@private
|
||||
function M._get(pattern, mode)
|
||||
local skip ---@type string?
|
||||
if type(mode) == "function" then
|
||||
pattern, skip = mode(pattern)
|
||||
elseif mode == "exact" then
|
||||
pattern, skip = M._exact(pattern)
|
||||
elseif mode == "fuzzy" then
|
||||
pattern, skip = M._fuzzy(pattern)
|
||||
end
|
||||
return pattern, skip or pattern
|
||||
end
|
||||
|
||||
---@param pattern string
|
||||
function M._exact(pattern)
|
||||
return "\\V" .. pattern:gsub("\\", "\\\\")
|
||||
end
|
||||
|
||||
---@param opts? {ignorecase: boolean, whitespace:boolean}
|
||||
function M._fuzzy(pattern, opts)
|
||||
opts = vim.tbl_deep_extend("force", {
|
||||
ignorecase = vim.go.ignorecase,
|
||||
whitespace = false,
|
||||
}, opts or {})
|
||||
|
||||
local sep = opts.whitespace and ".\\{-}" or "\\[^\\ ]\\{-}"
|
||||
|
||||
---@param c string
|
||||
local chars = vim.tbl_map(function(c)
|
||||
return c == "\\" and "\\\\" or c
|
||||
end, vim.fn.split(pattern, "\\zs"))
|
||||
|
||||
local ret = "\\V" .. table.concat(chars, sep) .. (opts.ignorecase and "\\c" or "\\C")
|
||||
return ret, ret .. sep
|
||||
end
|
||||
|
||||
return M
|
||||
@ -0,0 +1,85 @@
|
||||
---@class Pos
|
||||
---@field row number
|
||||
---@field col number
|
||||
---@field [1] number
|
||||
---@field [2] number
|
||||
---@overload fun(pos?: number[] | { row: number, col: number } | number): Pos
|
||||
local P = {}
|
||||
|
||||
---@param pos? number[] | { row: number, col: number } | number
|
||||
function P.new(pos)
|
||||
if pos == nil then
|
||||
pos = vim.api.nvim_win_get_cursor(0)
|
||||
elseif type(pos) == "number" then
|
||||
pos = vim.api.nvim_win_get_cursor(pos)
|
||||
end
|
||||
|
||||
if getmetatable(pos) == P then
|
||||
return pos
|
||||
end
|
||||
local self = setmetatable({}, P)
|
||||
self[1] = pos[1] or pos.row
|
||||
self[2] = pos[2] or pos.col
|
||||
return self
|
||||
end
|
||||
|
||||
function P:__index(key)
|
||||
if key == "row" then
|
||||
return rawget(self, 1)
|
||||
elseif key == "col" then
|
||||
return rawget(self, 2)
|
||||
end
|
||||
return P[key]
|
||||
end
|
||||
|
||||
function P:__newindex(key, value)
|
||||
if key == "row" then
|
||||
rawset(self, 1, value)
|
||||
elseif key == "col" then
|
||||
rawset(self, 2, value)
|
||||
else
|
||||
rawset(self, key, value)
|
||||
end
|
||||
end
|
||||
|
||||
function P:__eq(other)
|
||||
return self[1] == other[1] and self[2] == other[2]
|
||||
end
|
||||
|
||||
function P:__tostring()
|
||||
return ("[%d, %d]"):format(self[1], self[2])
|
||||
end
|
||||
|
||||
function P:id(buf)
|
||||
return table.concat({ buf, self[1], self[2] }, ":")
|
||||
end
|
||||
|
||||
function P:dist(other)
|
||||
return math.abs(self[1] - other[1]) + math.abs(self[2] - other[2])
|
||||
end
|
||||
|
||||
function P:__add(other)
|
||||
other = P(other)
|
||||
return P.new({ self[1] + other[1], self[2] + other[2] })
|
||||
end
|
||||
|
||||
function P:__sub(other)
|
||||
other = P(other)
|
||||
return P.new({ self[1] - other[1], self[2] - other[2] })
|
||||
end
|
||||
|
||||
function P:__lt(other)
|
||||
other = P(other)
|
||||
return self[1] < other[1] or (self[1] == other[1] and self[2] < other[2])
|
||||
end
|
||||
|
||||
function P:__le(other)
|
||||
other = P(other)
|
||||
return self < other or self == other
|
||||
end
|
||||
|
||||
return setmetatable(P, {
|
||||
__call = function(_, pos)
|
||||
return P.new(pos)
|
||||
end,
|
||||
})
|
||||
420
config/neovim/store/lazy-plugins/flash.nvim/lua/flash/state.lua
Normal file
420
config/neovim/store/lazy-plugins/flash.nvim/lua/flash/state.lua
Normal file
@ -0,0 +1,420 @@
|
||||
local require = require("flash.require")
|
||||
|
||||
local Cache = require("flash.cache")
|
||||
local Config = require("flash.config")
|
||||
local Hacks = require("flash.hacks")
|
||||
local Highlight = require("flash.highlight")
|
||||
local Jump = require("flash.jump")
|
||||
local Matcher = require("flash.search.matcher")
|
||||
local Pattern = require("flash.search.pattern")
|
||||
local Prompt = require("flash.prompt")
|
||||
local Rainbow = require("flash.rainbow")
|
||||
local Search = require("flash.search")
|
||||
local Util = require("flash.util")
|
||||
|
||||
---@class Flash.State.Config: Flash.Config
|
||||
---@field matcher? fun(win: window, state:Flash.State, pos: {from:Pos, to:Pos}): Flash.Match[]
|
||||
---@field filter? fun(matches:Flash.Match[], state:Flash.State): Flash.Match[]
|
||||
---@field pattern? string
|
||||
---@field labeler? fun(matches:Flash.Match[], state:Flash.State)
|
||||
---@field actions? table<string, fun(state:Flash.State, char:string):boolean?>
|
||||
|
||||
---@class Flash.State
|
||||
---@field win window
|
||||
---@field wins window[]
|
||||
---@field cache Flash.Cache
|
||||
---@field pos Pos
|
||||
---@field view any
|
||||
---@field results Flash.Match[]
|
||||
---@field target? Flash.Match
|
||||
---@field pattern Flash.Pattern
|
||||
---@field opts Flash.State.Config
|
||||
---@field labeler fun(matches:Flash.Match[], state:Flash.State)
|
||||
---@field visible boolean
|
||||
---@field matcher fun(win: window, state:Flash.State): Flash.Matcher
|
||||
---@field matchers Flash.Matcher[]
|
||||
---@field restore_windows? fun()
|
||||
---@field rainbow? Flash.Rainbow
|
||||
---@field ns number
|
||||
---@field langmap table<string, string>
|
||||
local M = {}
|
||||
M.__index = M
|
||||
|
||||
---@type table<Flash.State, boolean>
|
||||
M._states = setmetatable({}, { __mode = "k" })
|
||||
|
||||
function M.setup()
|
||||
if M._did_setup then
|
||||
return
|
||||
end
|
||||
M._did_setup = true
|
||||
local ns = vim.api.nvim_create_namespace("flash")
|
||||
vim.api.nvim_set_decoration_provider(ns, {
|
||||
on_start = function()
|
||||
for state in pairs(M._states) do
|
||||
if state.visible then
|
||||
local ok, err = pcall(state.update, state)
|
||||
if not ok then
|
||||
vim.schedule(function()
|
||||
vim.notify(
|
||||
"Flash error during redraw:\n" .. err,
|
||||
vim.log.levels.ERROR,
|
||||
{ title = "flash.nvim" }
|
||||
)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
---@param char string
|
||||
function M:lmap(char)
|
||||
return vim.bo.iminsert == 1 and self.langmap[char] or char
|
||||
end
|
||||
|
||||
function M:get_char()
|
||||
local ret = Util.get_char()
|
||||
return ret and self:lmap(ret) or nil
|
||||
end
|
||||
|
||||
function M:labels()
|
||||
local labels = self.opts.labels
|
||||
if self.opts.label.uppercase then
|
||||
labels = labels .. self.opts.labels:upper()
|
||||
end
|
||||
local list = vim.fn.split(labels, "\\zs")
|
||||
local ret = {} ---@type string[]
|
||||
local added = {} ---@type table<string, boolean>
|
||||
for _, l in ipairs(vim.fn.split(self.opts.label.exclude, "\\zs")) do
|
||||
added[l] = true
|
||||
end
|
||||
for _, l in ipairs(list) do
|
||||
if not added[l] then
|
||||
added[l] = true
|
||||
ret[#ret + 1] = self:lmap(l)
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
function M.is_search()
|
||||
local t = vim.fn.getcmdtype()
|
||||
return t == "/" or t == "?"
|
||||
end
|
||||
|
||||
---@param opts? Flash.State.Config
|
||||
function M.new(opts)
|
||||
M.setup()
|
||||
local self = setmetatable({}, M)
|
||||
self.opts = Config.get(opts)
|
||||
self.langmap = {}
|
||||
if vim.bo.iminsert == 1 then
|
||||
local lmap = vim.api.nvim_buf_get_keymap(0, "l")
|
||||
for _, m in ipairs(lmap) do
|
||||
if m.lhs ~= "" then
|
||||
self.langmap[m.lhs] = m.rhs
|
||||
end
|
||||
end
|
||||
end
|
||||
self.results = {}
|
||||
self.matchers = {}
|
||||
self.wins = {}
|
||||
self.matcher = self.opts.matcher
|
||||
if type(self.matcher) == "function" then
|
||||
self.matcher = Matcher.from(self.opts.matcher)
|
||||
elseif self.matcher == nil then
|
||||
self.matcher = Search.new
|
||||
end
|
||||
self.pattern = Pattern.new(self.opts.pattern, self.opts.search.mode, self.opts.search.trigger)
|
||||
self.visible = true
|
||||
self.cache = Cache.new(self)
|
||||
self.labeler = self.opts.labeler or require("flash.labeler").new(self):labeler()
|
||||
self.ns = vim.api.nvim_create_namespace(self.opts.ns or "flash")
|
||||
M._states[self] = true
|
||||
if self.opts.label.rainbow.enabled then
|
||||
self.rainbow = Rainbow.new(self)
|
||||
end
|
||||
|
||||
self:update()
|
||||
return self
|
||||
end
|
||||
|
||||
---@param target? string|Flash.Match.Find|Flash.Match
|
||||
---@return Flash.Match?
|
||||
function M:jump(target)
|
||||
local match ---@type Flash.Match?
|
||||
if type(target) == "string" then
|
||||
match = self:find({ label = target })
|
||||
elseif target and target.end_pos then
|
||||
match = target
|
||||
elseif target then
|
||||
match = self:find(target)
|
||||
else
|
||||
match = self.target
|
||||
end
|
||||
if match then
|
||||
if self.opts.action then
|
||||
self.opts.action(match, self)
|
||||
else
|
||||
Jump.jump(match, self)
|
||||
Jump.on_jump(self)
|
||||
end
|
||||
return match
|
||||
end
|
||||
end
|
||||
|
||||
-- Will restore all window views
|
||||
function M:restore()
|
||||
if self.restore_windows then
|
||||
self.restore_windows()
|
||||
end
|
||||
end
|
||||
|
||||
function M:get_matcher(win)
|
||||
self.matchers[win] = self.matchers[win] or self.matcher(win, self)
|
||||
return self.matchers[win]
|
||||
end
|
||||
|
||||
---@param opts? Flash.Match.Find | {label?:string, pos?: Pos}
|
||||
function M:find(opts)
|
||||
if opts and opts.label then
|
||||
for _, m in ipairs(self.results) do
|
||||
if m.label == opts.label then
|
||||
return m
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
opts = Matcher.defaults({
|
||||
forward = self.opts.search.forward,
|
||||
wrap = self.opts.search.wrap,
|
||||
}, opts)
|
||||
|
||||
local matcher = self:get_matcher(self.win)
|
||||
local ret = matcher:find(opts)
|
||||
|
||||
if ret then
|
||||
for _, m in ipairs(self.results) do
|
||||
if m.pos == ret.pos and m.end_pos == ret.end_pos then
|
||||
return m
|
||||
end
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
-- Checks if the given pattern is a jump label and jumps to it.
|
||||
---@param pattern string
|
||||
function M:check_jump(pattern)
|
||||
if not self.visible then
|
||||
return
|
||||
end
|
||||
|
||||
if self.opts.search.trigger ~= "" and self.pattern():sub(-1) ~= self.opts.search.trigger then
|
||||
return
|
||||
end
|
||||
local chars = vim.fn.strchars(pattern)
|
||||
if
|
||||
pattern:find(self.pattern(), 1, true) == 1 and chars == vim.fn.strchars(self.pattern()) + 1
|
||||
then
|
||||
local label = vim.fn.strcharpart(pattern, chars - 1, 1)
|
||||
if self:jump(label) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@param opts? {pattern:string, force:boolean, check_jump:boolean}
|
||||
---@return boolean? abort `true` if the search was aborted
|
||||
function M:update(opts)
|
||||
opts = opts or {}
|
||||
|
||||
if opts.pattern then
|
||||
-- abort if pattern is a jump label
|
||||
if opts.check_jump ~= false and self:check_jump(opts.pattern) then
|
||||
return true
|
||||
end
|
||||
self.pattern:set(opts.pattern)
|
||||
end
|
||||
|
||||
if not self.visible then
|
||||
return
|
||||
end
|
||||
|
||||
if self.cache:update() or opts.force then
|
||||
self:_update()
|
||||
end
|
||||
end
|
||||
|
||||
function M:hide()
|
||||
if self.visible then
|
||||
self.visible = false
|
||||
Highlight.clear(self.ns)
|
||||
end
|
||||
end
|
||||
|
||||
function M:show()
|
||||
if not self.visible then
|
||||
self.visible = true
|
||||
-- force cache to update win and position
|
||||
self.win = nil
|
||||
self:update({ force = true })
|
||||
end
|
||||
end
|
||||
|
||||
function M:_update()
|
||||
-- This is needed because we trigger searches during redraw.
|
||||
-- We need to save the state of the incsearch so that current match
|
||||
-- will still be displayed correctly.
|
||||
if M.is_search() then
|
||||
Hacks.save_incsearch_state()
|
||||
end
|
||||
|
||||
self.results = {}
|
||||
local done = {} ---@type table<string, boolean>
|
||||
---@type Flash.Matcher[]
|
||||
local matchers = {}
|
||||
for _, win in ipairs(self.wins) do
|
||||
local buf = vim.api.nvim_win_get_buf(win)
|
||||
matchers[win] = self:get_matcher(win)
|
||||
local state = self.cache:get_state(win)
|
||||
for _, m in ipairs(state and state.matches or {}) do
|
||||
local id = m.pos:id(buf) .. m.end_pos:id(buf)
|
||||
if not done[id] then
|
||||
done[id] = true
|
||||
table.insert(self.results, m)
|
||||
end
|
||||
end
|
||||
end
|
||||
self.matchers = matchers
|
||||
|
||||
for _, match in ipairs(self.results) do
|
||||
vim.api.nvim_win_call(match.win, function()
|
||||
local fold = vim.fn.foldclosed(match.pos[1])
|
||||
match.fold = fold ~= -1 and fold or nil
|
||||
end)
|
||||
end
|
||||
|
||||
self:update_target()
|
||||
self.labeler(self.results, self)
|
||||
|
||||
if M.is_search() then
|
||||
Hacks.restore_incsearch_state()
|
||||
end
|
||||
|
||||
Highlight.update(self)
|
||||
end
|
||||
|
||||
function M:update_target()
|
||||
-- set target to next match.
|
||||
-- When not using incremental search,
|
||||
-- we need to set the target to the previous match
|
||||
self.target = self:find({
|
||||
pos = self.pos,
|
||||
count = vim.v.count1,
|
||||
})
|
||||
|
||||
local info = vim.fn.getwininfo(self.win)[1]
|
||||
local function is_visible()
|
||||
return self.target and self.target.pos[1] >= info.topline and self.target.pos[1] <= info.botline
|
||||
end
|
||||
|
||||
if self.opts.search.incremental then
|
||||
-- only update cursor if the target is not visible
|
||||
-- and we are not activated
|
||||
if self.target and not self.is_search() then
|
||||
vim.api.nvim_win_set_cursor(self.win, self.target.pos)
|
||||
end
|
||||
elseif not is_visible() then
|
||||
self.target = self:find({
|
||||
pos = self.pos,
|
||||
count = vim.v.count1,
|
||||
forward = not self.opts.search.forward,
|
||||
})
|
||||
if not is_visible() then
|
||||
self.target = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@class Flash.Step.Options
|
||||
---@field actions? table<string, fun(state:Flash.State, char:string):boolean?>
|
||||
---@field restore? boolean
|
||||
---@field abort? fun()
|
||||
---@field jump_on_max_length? boolean
|
||||
|
||||
---@param opts? Flash.Step.Options
|
||||
function M:step(opts)
|
||||
opts = opts or {}
|
||||
if self.opts.prompt.enabled and not M.is_search() then
|
||||
Prompt.set(self.pattern())
|
||||
end
|
||||
local actions = opts.actions or self.opts.actions or {}
|
||||
local c = self:get_char()
|
||||
if c == nil then
|
||||
vim.api.nvim_input("<esc>")
|
||||
if opts.restore ~= false then
|
||||
self:restore()
|
||||
end
|
||||
if opts.abort then
|
||||
opts.abort()
|
||||
end
|
||||
return
|
||||
elseif actions[c] then
|
||||
local ret = actions[c](self, c)
|
||||
if ret == nil then
|
||||
return true
|
||||
end
|
||||
return ret
|
||||
-- jump to first
|
||||
elseif c == Util.CR then
|
||||
self:jump()
|
||||
return
|
||||
end
|
||||
|
||||
local orig = self.pattern()
|
||||
|
||||
-- break if we jumped
|
||||
if self:update({ pattern = self.pattern:extend(c) }) then
|
||||
return
|
||||
end
|
||||
|
||||
-- when we exceed max length, either jump to the label,
|
||||
-- or input the last key and break
|
||||
if self.opts.search.max_length and #self.pattern() > self.opts.search.max_length then
|
||||
self:update({ pattern = orig })
|
||||
if opts.jump_on_max_length ~= false then
|
||||
self:jump()
|
||||
end
|
||||
vim.api.nvim_input(c)
|
||||
return
|
||||
end
|
||||
|
||||
-- exit if no results and not in regular search mode
|
||||
if #self.results == 0 and not self.pattern:empty() and self.pattern.mode ~= 'search' then
|
||||
if self.opts.search.incremental then
|
||||
vim.api.nvim_input(c)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- autojump if only one result
|
||||
if #self.results == 1 and self.opts.jump.autojump then
|
||||
self:jump()
|
||||
return
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
---@param opts? Flash.Step.Options
|
||||
function M:loop(opts)
|
||||
while self:step(opts) do
|
||||
end
|
||||
self:hide()
|
||||
Prompt.hide()
|
||||
end
|
||||
|
||||
return M
|
||||
105
config/neovim/store/lazy-plugins/flash.nvim/lua/flash/util.lua
Normal file
105
config/neovim/store/lazy-plugins/flash.nvim/lua/flash/util.lua
Normal file
@ -0,0 +1,105 @@
|
||||
local Hacks = require("flash.hacks")
|
||||
local require = require("flash.require")
|
||||
|
||||
local M = {}
|
||||
|
||||
function M.t(str)
|
||||
return vim.api.nvim_replace_termcodes(str, true, true, true)
|
||||
end
|
||||
|
||||
M.CR = M.t("<cr>")
|
||||
M.ESC = M.t("<esc>")
|
||||
M.BS = M.t("<bs>")
|
||||
M.EXIT = M.t("<C-\\><C-n>")
|
||||
M.LUA_CALLBACK = "\x80\253g"
|
||||
M.CMD = "\x80\253h"
|
||||
|
||||
function M.exit()
|
||||
vim.api.nvim_feedkeys(M.EXIT, "nx", false)
|
||||
vim.api.nvim_feedkeys(M.ESC, "n", false)
|
||||
end
|
||||
|
||||
---@param buf number
|
||||
---@param pos number[] (1,0)-indexed position
|
||||
---@param offset number[]
|
||||
---@return number[] (1,0)-indexed position
|
||||
function M.offset_pos(buf, pos, offset)
|
||||
local row = pos[1] + offset[1]
|
||||
local ok, lines = pcall(vim.api.nvim_buf_get_lines, buf, row - 1, row, true)
|
||||
if not ok or lines == nil then
|
||||
-- fallback to old behavior if anything wrong happens
|
||||
return { row, math.max(pos[2] + offset[2], 0) }
|
||||
end
|
||||
|
||||
local line = lines[1]
|
||||
local charidx = vim.fn.charidx(line, pos[2])
|
||||
local col = vim.fn.byteidx(line, charidx + offset[2])
|
||||
|
||||
return { row, math.max(col, 0) }
|
||||
end
|
||||
|
||||
function M.get_char()
|
||||
Hacks.setcursor()
|
||||
vim.cmd("redraw")
|
||||
local ok, ret = pcall(vim.fn.getcharstr)
|
||||
return ok and ret ~= M.ESC and ret or nil
|
||||
end
|
||||
|
||||
function M.layout_wins()
|
||||
local queue = { vim.fn.winlayout() }
|
||||
---@type table<window, window>
|
||||
local wins = {}
|
||||
while #queue > 0 do
|
||||
local node = table.remove(queue)
|
||||
if node[1] == "leaf" then
|
||||
wins[node[2]] = node[2]
|
||||
else
|
||||
vim.list_extend(queue, node[2])
|
||||
end
|
||||
end
|
||||
return wins
|
||||
end
|
||||
|
||||
function M.save_layout()
|
||||
local current_win = vim.api.nvim_get_current_win()
|
||||
local wins = M.layout_wins()
|
||||
---@type table<window, table>
|
||||
local state = {}
|
||||
for _, win in pairs(wins) do
|
||||
state[win] = vim.api.nvim_win_call(win, vim.fn.winsaveview)
|
||||
end
|
||||
return function()
|
||||
for win, s in pairs(state) do
|
||||
if vim.api.nvim_win_is_valid(win) then
|
||||
local buf = vim.api.nvim_win_get_buf(win)
|
||||
-- never restore terminal buffers to prevent flickering
|
||||
if vim.bo[buf].buftype ~= "terminal" then
|
||||
pcall(vim.api.nvim_win_call, win, function()
|
||||
vim.fn.winrestview(s)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
vim.api.nvim_set_current_win(current_win)
|
||||
state = {}
|
||||
end
|
||||
end
|
||||
|
||||
---@param done fun():boolean
|
||||
---@param on_done fun()
|
||||
function M.on_done(done, on_done)
|
||||
local check = assert(vim.loop.new_check())
|
||||
local fn = function()
|
||||
if check:is_closing() then
|
||||
return
|
||||
end
|
||||
if done() then
|
||||
check:stop()
|
||||
check:close()
|
||||
on_done()
|
||||
end
|
||||
end
|
||||
check:start(vim.schedule_wrap(fn))
|
||||
end
|
||||
|
||||
return M
|
||||
1
config/neovim/store/lazy-plugins/flash.nvim/selene.toml
Normal file
1
config/neovim/store/lazy-plugins/flash.nvim/selene.toml
Normal file
@ -0,0 +1 @@
|
||||
std="vim"
|
||||
5
config/neovim/store/lazy-plugins/flash.nvim/stylua.toml
Normal file
5
config/neovim/store/lazy-plugins/flash.nvim/stylua.toml
Normal file
@ -0,0 +1,5 @@
|
||||
indent_type = "Spaces"
|
||||
indent_width = 2
|
||||
column_width = 100
|
||||
[sort_requires]
|
||||
enabled = true
|
||||
@ -0,0 +1,90 @@
|
||||
local Char = require("flash.plugins.char")
|
||||
local assert = require("luassert")
|
||||
require("flash").setup()
|
||||
|
||||
describe("char", function()
|
||||
local function set(text, pos)
|
||||
local lines = vim.split(vim.trim(text), "\n")
|
||||
lines = vim.tbl_map(function(line)
|
||||
return vim.trim(line)
|
||||
end, lines)
|
||||
vim.api.nvim_buf_set_lines(1, 0, -1, false, lines)
|
||||
vim.api.nvim_win_set_cursor(0, pos or { 1, 0 })
|
||||
end
|
||||
|
||||
before_each(function()
|
||||
set("abc_xyz", { 1, 3 })
|
||||
local state = require("flash.plugins.char").state
|
||||
if state then
|
||||
state:hide()
|
||||
end
|
||||
end)
|
||||
|
||||
local function get()
|
||||
return table.concat(vim.api.nvim_buf_get_lines(1, 0, -1, false), "\n")
|
||||
end
|
||||
|
||||
--- tests for deletes with ftFT motions
|
||||
--- test always runs on input "abc_xyz"
|
||||
--- with cursor at position { 1, 3 }
|
||||
local tests = {
|
||||
-- f
|
||||
{ motion = "dfx", result = "abcyz" },
|
||||
{ motion = "dfz", result = "abc" },
|
||||
{ motion = "df_", result = "abc_xyz" },
|
||||
{ motion = "dfa", result = "abc_xyz" },
|
||||
-- t
|
||||
{ motion = "dtx", result = "abcxyz" },
|
||||
{ motion = "dtz", result = "abcz" },
|
||||
{ motion = "dt_", result = "abc_xyz" },
|
||||
{ motion = "dta", result = "abc_xyz" },
|
||||
-- F
|
||||
{ motion = "dFa", result = "_xyz" },
|
||||
{ motion = "dFc", result = "ab_xyz" },
|
||||
{ motion = "dF_", result = "abc_xyz" },
|
||||
{ motion = "dFx", result = "abc_xyz" },
|
||||
-- T
|
||||
{ motion = "dTa", result = "a_xyz" },
|
||||
{ motion = "dTc", result = "abc_xyz" },
|
||||
{ motion = "dT_", result = "abc_xyz" },
|
||||
{ motion = "dTx", result = "abc_xyz" },
|
||||
}
|
||||
|
||||
for _, test in ipairs(tests) do
|
||||
it("works with " .. test.motion, function()
|
||||
vim.cmd("norm! " .. test.motion)
|
||||
assert.same(test.result, get())
|
||||
end)
|
||||
end
|
||||
for _, test in ipairs(tests) do
|
||||
it("works with " .. test.motion .. " (flash)", function()
|
||||
-- vim.api.nvim_feedkeys(test.motion, "mtx", false)
|
||||
vim.cmd("norm " .. test.motion)
|
||||
assert.same(test.result, get())
|
||||
end)
|
||||
end
|
||||
|
||||
local input = "abcd1abcd2abcd"
|
||||
for _, motion in ipairs({ "f", "t", "F", "T" }) do
|
||||
for col = 0, #input - 1 do
|
||||
for count = -1, 3 do
|
||||
count = count == -1 and "" or count
|
||||
for _, char in ipairs({ "a", "b", "c", "d" }) do
|
||||
local cmd = count .. "d" .. motion .. char
|
||||
local pos = { 1, col }
|
||||
it("works with " .. cmd .. " at " .. col, function()
|
||||
set(input, pos)
|
||||
vim.cmd("norm! " .. cmd)
|
||||
local ret = get()
|
||||
set(input, pos)
|
||||
if Char.state then
|
||||
Char.state:hide()
|
||||
end
|
||||
vim.cmd("norm " .. cmd)
|
||||
assert.same(ret, get())
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
@ -0,0 +1,38 @@
|
||||
local Config = require("flash.config")
|
||||
local assert = require("luassert")
|
||||
|
||||
describe("config", function()
|
||||
before_each(function()
|
||||
Config.setup()
|
||||
end)
|
||||
|
||||
it("processes modes", function()
|
||||
Config.setup({ modes = { foo = { bar = true } } })
|
||||
assert.is_true(Config.modes.foo.bar)
|
||||
assert.is_true(Config.get({ mode = "foo" }).bar)
|
||||
end)
|
||||
|
||||
it("processes modes recursively", function()
|
||||
Config.setup({
|
||||
modes = {
|
||||
foo = { mode = "bar" },
|
||||
bar = { field = true, mode = "foo" },
|
||||
},
|
||||
})
|
||||
assert.is_true(Config.get({ mode = "foo" }).field)
|
||||
assert.is_true(Config.get({ mode = "bar" }).field)
|
||||
end)
|
||||
|
||||
it("processes modes recursively in correct order", function()
|
||||
Config.setup({
|
||||
modes = {
|
||||
a = { mode = "b", v = "a" },
|
||||
b = { mode = "c", v = "b" },
|
||||
c = { v = "c" },
|
||||
},
|
||||
})
|
||||
assert.same("d", Config.get({ mode = "a", v = "d" }).v)
|
||||
assert.same("a", Config.get({ mode = "a" }).v)
|
||||
assert.same("b", Config.get({ mode = "b" }).v)
|
||||
end)
|
||||
end)
|
||||
36
config/neovim/store/lazy-plugins/flash.nvim/tests/init.lua
Normal file
36
config/neovim/store/lazy-plugins/flash.nvim/tests/init.lua
Normal file
@ -0,0 +1,36 @@
|
||||
local M = {}
|
||||
|
||||
function M.root(root)
|
||||
local f = debug.getinfo(1, "S").source:sub(2)
|
||||
return vim.fn.fnamemodify(f, ":p:h:h") .. "/" .. (root or "")
|
||||
end
|
||||
|
||||
---@param plugin string
|
||||
function M.load(plugin)
|
||||
local name = plugin:match(".*/(.*)")
|
||||
local package_root = M.root(".tests/site/pack/deps/start/")
|
||||
if not vim.loop.fs_stat(package_root .. name) then
|
||||
print("Installing " .. plugin)
|
||||
vim.fn.mkdir(package_root, "p")
|
||||
vim.fn.system({
|
||||
"git",
|
||||
"clone",
|
||||
"--depth=1",
|
||||
"https://github.com/" .. plugin .. ".git",
|
||||
package_root .. "/" .. name,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function M.setup()
|
||||
vim.cmd([[set runtimepath=$VIMRUNTIME]])
|
||||
vim.opt.runtimepath:append(M.root())
|
||||
vim.opt.packpath = { M.root(".tests/site") }
|
||||
M.load("nvim-lua/plenary.nvim")
|
||||
vim.env.XDG_CONFIG_HOME = M.root(".tests/config")
|
||||
vim.env.XDG_DATA_HOME = M.root(".tests/data")
|
||||
vim.env.XDG_STATE_HOME = M.root(".tests/state")
|
||||
vim.env.XDG_CACHE_HOME = M.root(".tests/cache")
|
||||
end
|
||||
|
||||
M.setup()
|
||||
3
config/neovim/store/lazy-plugins/flash.nvim/tests/run
Executable file
3
config/neovim/store/lazy-plugins/flash.nvim/tests/run
Executable file
@ -0,0 +1,3 @@
|
||||
#!/nix/store/306znyj77fv49kwnkpxmb0j2znqpa8bj-bash-5.2p26/bin/sh
|
||||
|
||||
nvim --headless -u tests/init.lua -c "PlenaryBustedDirectory tests {minimal_init = 'tests//init.lua', sequential = true}"
|
||||
@ -0,0 +1,51 @@
|
||||
local Search = require("flash.search")
|
||||
local State = require("flash.state")
|
||||
|
||||
describe("search", function()
|
||||
before_each(function()
|
||||
vim.opt.ignorecase = true
|
||||
vim.opt.smartcase = true
|
||||
vim.api.nvim_buf_set_lines(1, 0, -1, false, {})
|
||||
end)
|
||||
|
||||
---@param opts? Flash.State.Config | string
|
||||
local function get_search(opts)
|
||||
if type(opts) == "string" then
|
||||
opts = { pattern = opts }
|
||||
end
|
||||
local state = State.new(opts)
|
||||
local win = vim.api.nvim_get_current_win()
|
||||
return Search.new(win, state)
|
||||
end
|
||||
|
||||
local function set(text, pos)
|
||||
local lines = vim.split(vim.trim(text), "\n")
|
||||
lines = vim.tbl_map(function(line)
|
||||
return vim.trim(line)
|
||||
end, lines)
|
||||
vim.api.nvim_buf_set_lines(1, 0, -1, false, lines)
|
||||
vim.api.nvim_win_set_cursor(0, pos or { 1, 0 })
|
||||
end
|
||||
|
||||
it("finds matches", function()
|
||||
set([[
|
||||
foobar
|
||||
line1
|
||||
line2
|
||||
]])
|
||||
|
||||
local search = get_search("line")
|
||||
local matches = search:get()
|
||||
assert.same("\\Vline", search.state.pattern.search)
|
||||
assert.same({
|
||||
{ win = 1000, pos = { 2, 0 }, end_pos = { 2, 3 } },
|
||||
{ win = 1000, pos = { 3, 0 }, end_pos = { 3, 3 } },
|
||||
}, matches)
|
||||
assert.same({ win = 1000, pos = { 2, 0 }, end_pos = { 2, 3 } }, search:find())
|
||||
assert.same({ win = 1000, pos = { 3, 0 }, end_pos = { 3, 3 } }, search:find({ count = 2 }))
|
||||
assert.same(
|
||||
{ win = 1000, pos = { 3, 0 }, end_pos = { 3, 3 } },
|
||||
search:find({ forward = false })
|
||||
)
|
||||
end)
|
||||
end)
|
||||
@ -0,0 +1,85 @@
|
||||
local Jump = require("flash.jump")
|
||||
local Pos = require("flash.search.pos")
|
||||
local State = require("flash.state")
|
||||
local assert = require("luassert")
|
||||
|
||||
describe("jump", function()
|
||||
local function set(text, pos)
|
||||
local lines = vim.split(vim.trim(text), "\n")
|
||||
lines = vim.tbl_map(function(line)
|
||||
return vim.trim(line)
|
||||
end, lines)
|
||||
if vim.fn.mode() == "v" then
|
||||
vim.cmd("normal! v")
|
||||
end
|
||||
vim.api.nvim_buf_set_lines(1, 0, -1, false, lines)
|
||||
vim.api.nvim_win_set_cursor(0, pos or { 1, 0 })
|
||||
end
|
||||
|
||||
---@param match Flash.Match
|
||||
---@param opts? Flash.State.Config
|
||||
local function jump(match, opts)
|
||||
match.win = vim.api.nvim_get_current_win()
|
||||
local state = State.new(opts)
|
||||
Jump.jump(match, state)
|
||||
end
|
||||
|
||||
before_each(function()
|
||||
vim.opt.ignorecase = true
|
||||
vim.opt.smartcase = true
|
||||
vim.api.nvim_buf_set_lines(0, 0, -1, false, {})
|
||||
set([[
|
||||
line1 foo
|
||||
line2 foo
|
||||
line3 foo
|
||||
]])
|
||||
end)
|
||||
|
||||
it("jumps to start", function()
|
||||
local match = {
|
||||
pos = Pos({ 1, 6 }),
|
||||
end_pos = Pos({ 1, 8 }),
|
||||
}
|
||||
jump(match)
|
||||
assert.same({ 1, 6 }, vim.api.nvim_win_get_cursor(0))
|
||||
end)
|
||||
|
||||
it("selects to start", function()
|
||||
assert.same({ 1, 0 }, vim.api.nvim_win_get_cursor(0))
|
||||
local match = {
|
||||
pos = Pos({ 1, 6 }),
|
||||
end_pos = Pos({ 1, 8 }),
|
||||
}
|
||||
|
||||
assert.same("n", vim.fn.mode())
|
||||
vim.cmd("normal! v")
|
||||
jump(match, { jump = { pos = "start", inclusive = false } })
|
||||
assert.same("v", vim.fn.mode())
|
||||
vim.cmd("normal! v")
|
||||
|
||||
assert.same({ 1, 6 }, vim.api.nvim_win_get_cursor(0))
|
||||
assert.same({ 1, 0 }, vim.api.nvim_buf_get_mark(0, "<"))
|
||||
assert.same({ 1, 6 }, vim.api.nvim_buf_get_mark(0, ">"))
|
||||
end)
|
||||
|
||||
-- it("yanks to start", function()
|
||||
-- assert.same({ 1, 0 }, vim.api.nvim_win_get_cursor(0))
|
||||
-- local match = {
|
||||
-- pos = Pos({ 1, 6 }),
|
||||
-- end_pos = Pos({ 1, 8 }),
|
||||
-- }
|
||||
--
|
||||
-- assert.same("n", vim.fn.mode())
|
||||
-- -- vim.cmd("normal! y")
|
||||
-- vim.api.nvim_feedkeys("y", "n", false)
|
||||
-- assert.same("no", vim.fn.mode(true))
|
||||
-- jump(match, { jump = { pos = "start", inclusive = false } })
|
||||
-- assert.same("n", vim.fn.mode())
|
||||
--
|
||||
-- vim.print(vim.fn.getmarklist(vim.api.nvim_get_current_buf()))
|
||||
--
|
||||
-- assert.same({ 1, 6 }, vim.api.nvim_win_get_cursor(0))
|
||||
-- assert.same({ 1, 0 }, vim.api.nvim_buf_get_mark(0, "["))
|
||||
-- assert.same({ 1, 6 }, vim.api.nvim_buf_get_mark(0, "]"))
|
||||
-- end)
|
||||
end)
|
||||
@ -0,0 +1,103 @@
|
||||
local Labeler = require("flash.labeler")
|
||||
local Search = require("flash.search")
|
||||
local State = require("flash.state")
|
||||
local assert = require("luassert")
|
||||
|
||||
describe("labeler", function()
|
||||
local function set(text, pos)
|
||||
local lines = vim.split(vim.trim(text), "\n")
|
||||
lines = vim.tbl_map(function(line)
|
||||
return vim.trim(line)
|
||||
end, lines)
|
||||
vim.api.nvim_buf_set_lines(1, 0, -1, false, lines)
|
||||
vim.api.nvim_win_set_cursor(0, pos or { 1, 0 })
|
||||
end
|
||||
|
||||
local function search(pattern)
|
||||
local state = State.new({ pattern = pattern, search = {
|
||||
mode = "search",
|
||||
} })
|
||||
return Labeler.new(state)
|
||||
end
|
||||
|
||||
before_each(function()
|
||||
vim.opt.ignorecase = true
|
||||
vim.opt.smartcase = true
|
||||
end)
|
||||
|
||||
it("skips labels", function()
|
||||
set([[
|
||||
foo foo
|
||||
bar
|
||||
barfoo
|
||||
]])
|
||||
local labels = search("bar"):skip(1000, { "a", "b", "c", "f" })
|
||||
assert.same({ "a", "b", "c" }, labels)
|
||||
end)
|
||||
it("skips all labels for an empty pattern", function()
|
||||
set([[
|
||||
test pattern
|
||||
]])
|
||||
local labels = search(""):skip(1000, { "a", "b", "c", "t" })
|
||||
assert.same({}, labels)
|
||||
end)
|
||||
|
||||
it("skips all labels for an invalid pattern", function()
|
||||
set([[
|
||||
invalid pattern
|
||||
]])
|
||||
local labels = search("[i"):skip(1000, { "a", "b", "i", "v" })
|
||||
assert.same({}, labels)
|
||||
end)
|
||||
|
||||
it("skips all labels when pattern ends with unescaped backslash", function()
|
||||
set([[
|
||||
pattern with backslash\
|
||||
]])
|
||||
local labels = search("backslash\\"):skip(1000, { "a", "b", "s", "\\" })
|
||||
assert.same({}, labels)
|
||||
end)
|
||||
|
||||
it("skips label that matches pattern", function()
|
||||
set([[
|
||||
pattern withc
|
||||
]])
|
||||
local labels = search("with"):skip(1000, { "a", "b", "c", "p", "w" })
|
||||
assert.same({ "a", "b", "p", "w" }, labels)
|
||||
end)
|
||||
|
||||
it("considers ignorecase when skipping labels", function()
|
||||
set([[
|
||||
pattern withC
|
||||
]])
|
||||
vim.opt.ignorecase = true
|
||||
local labels = search("with"):skip(1000, { "a", "b", "C", "p", "w" })
|
||||
assert.same({ "a", "b", "p", "w" }, labels)
|
||||
end)
|
||||
|
||||
it("considers ignorecase3 when skipping labels", function()
|
||||
set([[
|
||||
pattern withC
|
||||
]])
|
||||
vim.opt.ignorecase = true
|
||||
local labels = search("with"):skip(1000, { "a", "b", "c", "p", "w" })
|
||||
assert.same({ "a", "b", "p", "w" }, labels)
|
||||
end)
|
||||
|
||||
it("considers ignorecase2 when skipping labels", function()
|
||||
set([[
|
||||
pattern withc
|
||||
]])
|
||||
vim.opt.ignorecase = true
|
||||
local labels = search("with"):skip(1000, { "a", "b", "C", "p", "w" })
|
||||
assert.same({ "a", "b", "p", "w" }, labels)
|
||||
end)
|
||||
|
||||
it("skips all labels when pattern is an incomplete regex", function()
|
||||
set([[
|
||||
pattern with incomplete regex (
|
||||
]])
|
||||
local labels = search("regex \\("):skip(1000, { "a", "b", "i", "r", "(" })
|
||||
assert.same({}, labels)
|
||||
end)
|
||||
end)
|
||||
@ -0,0 +1,184 @@
|
||||
local Matcher = require("flash.search.matcher")
|
||||
local assert = require("luassert")
|
||||
|
||||
describe("search", function()
|
||||
local matcher = Matcher.new(1000)
|
||||
local matches = {
|
||||
{ win = 1000, pos = { 1, 0 }, end_pos = { 1, 2 } },
|
||||
{ win = 1000, pos = { 1, 7 }, end_pos = { 1, 9 } },
|
||||
{ win = 1000, pos = { 3, 4 }, end_pos = { 3, 6 } },
|
||||
}
|
||||
matcher:set(matches)
|
||||
|
||||
it("sets matches", function()
|
||||
assert.same(matches, matcher:get())
|
||||
end)
|
||||
|
||||
it("finds backward from after end", function()
|
||||
assert.same(
|
||||
matches[3],
|
||||
matcher:find({
|
||||
forward = false,
|
||||
pos = { 4, 6 },
|
||||
wrap = false,
|
||||
})
|
||||
)
|
||||
end)
|
||||
|
||||
it("handles count = 0", function()
|
||||
assert.same(
|
||||
matches[2],
|
||||
matcher:find({
|
||||
pos = { 1, 7 },
|
||||
count = 0,
|
||||
})
|
||||
)
|
||||
assert.is_nil(matcher:find({
|
||||
pos = { 2, 7 },
|
||||
count = 0,
|
||||
}))
|
||||
end)
|
||||
|
||||
it("returns forward matches", function()
|
||||
assert.same(
|
||||
{ matches[3] },
|
||||
matcher:get({
|
||||
from = { 2, 6 },
|
||||
})
|
||||
)
|
||||
end)
|
||||
|
||||
it("returns forward matches", function()
|
||||
assert.same(
|
||||
{ matches[3] },
|
||||
matcher:get({
|
||||
from = { 3, 4 },
|
||||
})
|
||||
)
|
||||
end)
|
||||
|
||||
it("returns backward matches", function()
|
||||
assert.same(
|
||||
{ matches[1] },
|
||||
matcher:get({
|
||||
to = { 1, 6 },
|
||||
})
|
||||
)
|
||||
end)
|
||||
|
||||
it("returns backward matches", function()
|
||||
assert.same(
|
||||
{ matches[1] },
|
||||
matcher:get({
|
||||
to = { 1, 0 },
|
||||
})
|
||||
)
|
||||
end)
|
||||
|
||||
it("finds matcher", function()
|
||||
assert.same({ win = 1000, pos = { 1, 7 }, end_pos = { 1, 9 } }, matcher:find())
|
||||
assert.same({ win = 1000, pos = { 3, 4 }, end_pos = { 3, 6 } }, matcher:find({ count = 2 }))
|
||||
assert.same(
|
||||
{ win = 1000, pos = { 3, 4 }, end_pos = { 3, 6 } },
|
||||
matcher:find({ forward = false })
|
||||
)
|
||||
assert.same(
|
||||
{ win = 1000, pos = { 1, 7 }, end_pos = { 1, 9 } },
|
||||
matcher:find({
|
||||
forward = false,
|
||||
pos = { 2, 7 },
|
||||
})
|
||||
)
|
||||
assert.same(
|
||||
{ win = 1000, pos = { 1, 7 }, end_pos = { 1, 9 } },
|
||||
matcher:find({
|
||||
forward = false,
|
||||
pos = { 3, 4 },
|
||||
})
|
||||
)
|
||||
end)
|
||||
|
||||
it("sorts matches", function()
|
||||
local m = Matcher.new(1000)
|
||||
local mm = {
|
||||
{ pos = { 3, 4 }, end_pos = { 3, 6 } },
|
||||
{ pos = { 1, 0 }, end_pos = { 1, 2 } },
|
||||
{ pos = { 1, 7 }, end_pos = { 1, 9 } },
|
||||
}
|
||||
m:set(mm)
|
||||
assert.same({
|
||||
{ win = 1000, pos = { 1, 0 }, end_pos = { 1, 2 } },
|
||||
{ win = 1000, pos = { 1, 7 }, end_pos = { 1, 9 } },
|
||||
{ win = 1000, pos = { 3, 4 }, end_pos = { 3, 6 } },
|
||||
}, m:get())
|
||||
end)
|
||||
it("finds forward skipping match at current position", function()
|
||||
assert.same(
|
||||
matches[2],
|
||||
matcher:find({
|
||||
forward = true,
|
||||
pos = { 1, 0 },
|
||||
wrap = false,
|
||||
})
|
||||
)
|
||||
end)
|
||||
|
||||
it("finds backward skipping match at current position", function()
|
||||
assert.same(
|
||||
matches[2],
|
||||
matcher:find({
|
||||
forward = false,
|
||||
pos = { 3, 4 },
|
||||
wrap = true,
|
||||
})
|
||||
)
|
||||
end)
|
||||
|
||||
it("finds forward from a non-match position", function()
|
||||
assert.same(
|
||||
matches[2],
|
||||
matcher:find({
|
||||
forward = true,
|
||||
pos = { 1, 3 },
|
||||
wrap = false,
|
||||
})
|
||||
)
|
||||
end)
|
||||
|
||||
it("finds backward from a non-match position", function()
|
||||
assert.same(
|
||||
matches[2],
|
||||
matcher:find({
|
||||
forward = false,
|
||||
pos = { 3, 2 },
|
||||
wrap = true,
|
||||
})
|
||||
)
|
||||
end)
|
||||
|
||||
it("returns nil when wrapping is disabled and no match is found forward", function()
|
||||
assert.is_nil(matcher:find({
|
||||
forward = true,
|
||||
pos = { 4, 0 },
|
||||
wrap = false,
|
||||
}))
|
||||
end)
|
||||
|
||||
it("returns nil when wrapping is disabled and no match is found backward", function()
|
||||
assert.is_nil(matcher:find({
|
||||
forward = false,
|
||||
pos = { 0, 0 },
|
||||
wrap = false,
|
||||
}))
|
||||
end)
|
||||
|
||||
it("can handle multiple matches on the same pos", function()
|
||||
local mm = Matcher.new(1000)
|
||||
mm:set({
|
||||
{ win = 1000, pos = { 1, 0 }, end_pos = { 1, 1 } },
|
||||
{ win = 1000, pos = { 1, 0 }, end_pos = { 1, 2 } },
|
||||
{ win = 1000, pos = { 1, 0 }, end_pos = { 1, 3 } },
|
||||
})
|
||||
-- assert.same()
|
||||
end)
|
||||
end)
|
||||
@ -0,0 +1,43 @@
|
||||
--# selene: allow(incorrect_standard_library_use)
|
||||
local Pos = require("flash.search.pos")
|
||||
|
||||
describe("pos", function()
|
||||
it("handles new", function()
|
||||
assert.same(Pos({ row = 1, col = 2 }), { 1, 2 })
|
||||
assert.same(Pos({ 1, 2 }), { 1, 2 })
|
||||
end)
|
||||
|
||||
it("handles cmp", function()
|
||||
assert(Pos({ 1, 2 }) < Pos({ 2, 2 }))
|
||||
assert(Pos({ 1, 2 }) < Pos({ 1, 3 }))
|
||||
assert(Pos({ 1, 2 }) <= Pos({ 1, 2 }))
|
||||
assert(Pos({ 1, 2 }) <= Pos({ 2, 2 }))
|
||||
assert(Pos({ 1, 2 }) <= Pos({ 1, 3 }))
|
||||
assert(Pos({ 1, 2 }) == Pos({ 1, 2 }))
|
||||
assert(Pos({ 1, 2 }) == Pos({ 1, 2 }))
|
||||
assert(Pos({ 1, 2 }) ~= Pos({ 2, 2 }))
|
||||
assert(Pos({ 1, 2 }) <= Pos({ 2, 2 }))
|
||||
assert(Pos({ 1, 2 }) >= Pos({ 1, 2 }))
|
||||
assert(Pos({ 1, 2 }) >= Pos({ 1, 1 }))
|
||||
end)
|
||||
|
||||
it("handles add", function()
|
||||
assert.same(Pos({ 1, 2 }) + Pos({ 1, 2 }), { 2, 4 })
|
||||
assert.same(Pos({ 1, 2 }) + { 1, 2 }, { 2, 4 })
|
||||
assert.same(Pos({ 1, 2 }) + { row = 1, col = 2 }, { 2, 4 })
|
||||
end)
|
||||
|
||||
it("handles sub", function()
|
||||
assert.same(Pos({ 1, 2 }) - Pos({ 1, 2 }), { 0, 0 })
|
||||
assert.same(Pos({ 1, 2 }) - { 1, 2 }, { 0, 0 })
|
||||
assert.same(Pos({ 1, 2 }) - { row = 1, col = 2 }, { 0, 0 })
|
||||
end)
|
||||
|
||||
it("handles tostring", function()
|
||||
assert.same(tostring(Pos({ 1, 2 })), "[1, 2]")
|
||||
end)
|
||||
|
||||
it("handles id", function()
|
||||
assert.same(Pos({ 1, 2 }):id(123), "123:1:2")
|
||||
end)
|
||||
end)
|
||||
@ -0,0 +1,182 @@
|
||||
local Search = require("flash.search")
|
||||
local State = require("flash.state")
|
||||
local assert = require("luassert")
|
||||
|
||||
describe("search", function()
|
||||
local function set(text, pos)
|
||||
local lines = vim.split(vim.trim(text), "\n")
|
||||
lines = vim.tbl_map(function(line)
|
||||
return vim.trim(line)
|
||||
end, lines)
|
||||
vim.api.nvim_buf_set_lines(1, 0, -1, false, lines)
|
||||
vim.api.nvim_win_set_cursor(0, pos or { 1, 0 })
|
||||
end
|
||||
|
||||
before_each(function()
|
||||
vim.opt.ignorecase = true
|
||||
vim.opt.smartcase = true
|
||||
vim.api.nvim_buf_set_lines(1, 0, -1, false, {})
|
||||
set([[
|
||||
foo foo
|
||||
bar
|
||||
barfoo
|
||||
]])
|
||||
end)
|
||||
|
||||
local state = State.new({ pattern = "foo" })
|
||||
local search = Search.new(1000, state)
|
||||
|
||||
local matches = {
|
||||
{ win = 1000, pos = { 1, 0 }, end_pos = { 1, 2 } },
|
||||
{ win = 1000, pos = { 1, 4 }, end_pos = { 1, 6 } },
|
||||
{ win = 1000, pos = { 3, 3 }, end_pos = { 3, 5 } },
|
||||
}
|
||||
|
||||
it("sets matches", function()
|
||||
assert.same(matches, search:get())
|
||||
end)
|
||||
|
||||
it("finds backward from after end", function()
|
||||
assert.same(
|
||||
matches[3],
|
||||
search:find({
|
||||
forward = false,
|
||||
pos = { 4, 6 },
|
||||
wrap = false,
|
||||
})
|
||||
)
|
||||
end)
|
||||
|
||||
it("handles count = 0", function()
|
||||
assert.same(
|
||||
matches[2],
|
||||
search:find({
|
||||
pos = { 1, 4 },
|
||||
count = 0,
|
||||
})
|
||||
)
|
||||
assert.is_nil(search:find({
|
||||
pos = { 2, 7 },
|
||||
count = 0,
|
||||
}))
|
||||
end)
|
||||
|
||||
it("returns forward matches", function()
|
||||
assert.same(
|
||||
{ matches[3] },
|
||||
search:get({
|
||||
from = { 2, 6 },
|
||||
})
|
||||
)
|
||||
end)
|
||||
|
||||
it("returns forward matches", function()
|
||||
assert.same(
|
||||
{ matches[3] },
|
||||
search:get({
|
||||
from = { 3, 3 },
|
||||
})
|
||||
)
|
||||
end)
|
||||
|
||||
it("returns backward matches", function()
|
||||
assert.same(
|
||||
{ matches[1] },
|
||||
search:get({
|
||||
to = { 1, 3 },
|
||||
})
|
||||
)
|
||||
end)
|
||||
|
||||
it("returns backward matches at pos", function()
|
||||
assert.same(
|
||||
{ matches[1] },
|
||||
search:get({
|
||||
to = { 1, 0 },
|
||||
})
|
||||
)
|
||||
end)
|
||||
|
||||
it("finds matcher", function()
|
||||
assert.same({ win = 1000, pos = { 1, 4 }, end_pos = { 1, 6 } }, search:find())
|
||||
assert.same({ win = 1000, pos = { 3, 3 }, end_pos = { 3, 5 } }, search:find({ count = 2 }))
|
||||
assert.same(
|
||||
{ win = 1000, pos = { 3, 3 }, end_pos = { 3, 5 } },
|
||||
search:find({ forward = false })
|
||||
)
|
||||
assert.same(
|
||||
{ win = 1000, pos = { 1, 4 }, end_pos = { 1, 6 } },
|
||||
search:find({
|
||||
forward = false,
|
||||
pos = { 2, 7 },
|
||||
})
|
||||
)
|
||||
assert.same(
|
||||
{ win = 1000, pos = { 1, 4 }, end_pos = { 1, 6 } },
|
||||
search:find({
|
||||
forward = false,
|
||||
pos = { 3, 2 },
|
||||
})
|
||||
)
|
||||
end)
|
||||
|
||||
it("finds forward skipping match at current position", function()
|
||||
assert.same(
|
||||
matches[2],
|
||||
search:find({
|
||||
forward = true,
|
||||
pos = { 1, 0 },
|
||||
wrap = false,
|
||||
})
|
||||
)
|
||||
end)
|
||||
|
||||
it("finds backward skipping match at current position", function()
|
||||
assert.same(
|
||||
matches[2],
|
||||
search:find({
|
||||
forward = false,
|
||||
pos = { 3, 3 },
|
||||
wrap = true,
|
||||
})
|
||||
)
|
||||
end)
|
||||
|
||||
it("finds forward from a non-match position", function()
|
||||
assert.same(
|
||||
matches[2],
|
||||
search:find({
|
||||
forward = true,
|
||||
pos = { 1, 3 },
|
||||
wrap = false,
|
||||
})
|
||||
)
|
||||
end)
|
||||
|
||||
it("finds backward from a non-match position", function()
|
||||
assert.same(
|
||||
matches[2],
|
||||
search:find({
|
||||
forward = false,
|
||||
pos = { 3, 2 },
|
||||
wrap = true,
|
||||
})
|
||||
)
|
||||
end)
|
||||
|
||||
it("returns nil when wrapping is disabled and no match is found forward", function()
|
||||
assert.is_nil(search:find({
|
||||
forward = true,
|
||||
pos = { 4, 0 },
|
||||
wrap = false,
|
||||
}))
|
||||
end)
|
||||
|
||||
it("returns nil when wrapping is disabled and no match is found backward", function()
|
||||
assert.is_nil(search:find({
|
||||
forward = false,
|
||||
pos = { 1, 0 },
|
||||
wrap = false,
|
||||
}))
|
||||
end)
|
||||
end)
|
||||
@ -0,0 +1,190 @@
|
||||
local Search = require("flash.search")
|
||||
local State = require("flash.state")
|
||||
|
||||
describe("search.view", function()
|
||||
before_each(function()
|
||||
vim.opt.ignorecase = true
|
||||
vim.opt.smartcase = true
|
||||
vim.api.nvim_buf_set_lines(1, 0, -1, false, {})
|
||||
end)
|
||||
|
||||
local function get_matches(pattern)
|
||||
local state = State.new({ pattern = pattern, search = {
|
||||
mode = "search",
|
||||
} })
|
||||
local win = vim.api.nvim_get_current_win()
|
||||
local search = Search.new(win, state)
|
||||
return search:get()
|
||||
end
|
||||
|
||||
local function set(text, pos)
|
||||
local lines = vim.split(vim.trim(text), "\n")
|
||||
lines = vim.tbl_map(function(line)
|
||||
return vim.trim(line)
|
||||
end, lines)
|
||||
vim.api.nvim_buf_set_lines(1, 0, -1, false, lines)
|
||||
vim.api.nvim_win_set_cursor(0, pos or { 1, 0 })
|
||||
end
|
||||
|
||||
it("finds matches", function()
|
||||
set([[
|
||||
foobar
|
||||
line1
|
||||
line2
|
||||
]])
|
||||
|
||||
local matches = get_matches("line")
|
||||
assert.same({
|
||||
{ win = 1000, pos = { 2, 0 }, end_pos = { 2, 3 } },
|
||||
{ win = 1000, pos = { 3, 0 }, end_pos = { 3, 3 } },
|
||||
}, matches)
|
||||
end)
|
||||
|
||||
it("finds multi matches on same line", function()
|
||||
set([[
|
||||
foobar foobar
|
||||
line1
|
||||
lineFoo
|
||||
]])
|
||||
|
||||
local matches = get_matches("foo")
|
||||
assert.same({
|
||||
{ win = 1000, pos = { 1, 0 }, end_pos = { 1, 2 } },
|
||||
{ win = 1000, pos = { 1, 7 }, end_pos = { 1, 9 } },
|
||||
{ win = 1000, pos = { 3, 4 }, end_pos = { 3, 6 } },
|
||||
}, matches)
|
||||
end)
|
||||
|
||||
it("deals with case", function()
|
||||
set([[
|
||||
foobar
|
||||
Line1
|
||||
line2
|
||||
]])
|
||||
|
||||
local matches = get_matches("line")
|
||||
assert.same({
|
||||
{ win = 1000, pos = { 2, 0 }, end_pos = { 2, 3 } },
|
||||
{ win = 1000, pos = { 3, 0 }, end_pos = { 3, 3 } },
|
||||
}, matches)
|
||||
end)
|
||||
|
||||
it("deals with smartcase", function()
|
||||
set([[
|
||||
foobar
|
||||
Line1
|
||||
line2
|
||||
]])
|
||||
|
||||
local matches = get_matches("Line")
|
||||
assert.same({
|
||||
{ win = 1000, pos = { 2, 0 }, end_pos = { 2, 3 } },
|
||||
}, matches)
|
||||
end)
|
||||
|
||||
it("finds matches on each line", function()
|
||||
set([[
|
||||
line1
|
||||
line2
|
||||
line3
|
||||
]])
|
||||
|
||||
local matches = get_matches("line")
|
||||
assert.same({
|
||||
{ win = 1000, pos = { 1, 0 }, end_pos = { 1, 3 } },
|
||||
{ win = 1000, pos = { 2, 0 }, end_pos = { 2, 3 } },
|
||||
{ win = 1000, pos = { 3, 0 }, end_pos = { 3, 3 } },
|
||||
}, matches)
|
||||
end)
|
||||
|
||||
it("handles '\\Vi\\zs\\.'", function()
|
||||
set([[
|
||||
line1
|
||||
line2
|
||||
line3
|
||||
]])
|
||||
|
||||
local matches = get_matches([[\Vi\zs\m.]])
|
||||
assert.same({
|
||||
{ win = 1000, pos = { 1, 2 }, end_pos = { 1, 2 } },
|
||||
{ win = 1000, pos = { 2, 2 }, end_pos = { 2, 2 } },
|
||||
{ win = 1000, pos = { 3, 2 }, end_pos = { 3, 2 } },
|
||||
}, matches)
|
||||
end)
|
||||
|
||||
it("handles ^", function()
|
||||
set([[
|
||||
foobar
|
||||
line1
|
||||
line2
|
||||
]])
|
||||
|
||||
local matches = get_matches("^")
|
||||
assert.same({
|
||||
{ win = 1000, pos = { 1, 0 }, end_pos = { 1, 0 } },
|
||||
{ win = 1000, pos = { 2, 0 }, end_pos = { 2, 0 } },
|
||||
{ win = 1000, pos = { 3, 0 }, end_pos = { 3, 0 } },
|
||||
}, matches)
|
||||
end)
|
||||
|
||||
it("handles ^", function()
|
||||
set([[
|
||||
foobar
|
||||
line1
|
||||
line2
|
||||
]])
|
||||
|
||||
local matches = get_matches("^")
|
||||
assert.same({
|
||||
{ win = 1000, pos = { 1, 0 }, end_pos = { 1, 0 } },
|
||||
{ win = 1000, pos = { 2, 0 }, end_pos = { 2, 0 } },
|
||||
{ win = 1000, pos = { 3, 0 }, end_pos = { 3, 0 } },
|
||||
}, matches)
|
||||
end)
|
||||
|
||||
it("handles ^.\\?", function()
|
||||
set([[
|
||||
foobar
|
||||
line1
|
||||
line2
|
||||
]])
|
||||
|
||||
local matches = get_matches("^.\\?")
|
||||
assert.same({
|
||||
{ win = 1000, pos = { 1, 0 }, end_pos = { 1, 0 } },
|
||||
{ win = 1000, pos = { 2, 0 }, end_pos = { 2, 0 } },
|
||||
{ win = 1000, pos = { 3, 0 }, end_pos = { 3, 0 } },
|
||||
}, matches)
|
||||
end)
|
||||
|
||||
it("handles ^l", function()
|
||||
set([[
|
||||
foobar
|
||||
line1
|
||||
line2
|
||||
]])
|
||||
|
||||
local matches = get_matches("^l")
|
||||
assert.same({
|
||||
{ win = 1000, pos = { 2, 0 }, end_pos = { 2, 0 } },
|
||||
{ win = 1000, pos = { 3, 0 }, end_pos = { 3, 0 } },
|
||||
}, matches)
|
||||
end)
|
||||
|
||||
it("handles wrapping", function()
|
||||
set(
|
||||
[[
|
||||
foo
|
||||
line1
|
||||
foo
|
||||
]],
|
||||
{ 3, 0 }
|
||||
)
|
||||
|
||||
local matches = get_matches("foo")
|
||||
assert.same({
|
||||
{ win = 1000, pos = { 1, 0 }, end_pos = { 1, 2 } },
|
||||
{ win = 1000, pos = { 3, 0 }, end_pos = { 3, 2 } },
|
||||
}, matches)
|
||||
end)
|
||||
end)
|
||||
@ -0,0 +1,11 @@
|
||||
local State = require("flash.state")
|
||||
local assert = require("luassert")
|
||||
|
||||
describe("unicode", function()
|
||||
local labels = "😅😀🏇🐎🐴🐵🐒"
|
||||
|
||||
it("splits labels", function()
|
||||
local state = State.new({ labels = labels })
|
||||
assert.same({ "😅", "😀", "🏇", "🐎", "🐴", "🐵", "🐒" }, state:labels())
|
||||
end)
|
||||
end)
|
||||
49
config/neovim/store/lazy-plugins/flash.nvim/vim.toml
Normal file
49
config/neovim/store/lazy-plugins/flash.nvim/vim.toml
Normal file
@ -0,0 +1,49 @@
|
||||
[selene]
|
||||
base = "lua51"
|
||||
name = "vim"
|
||||
|
||||
[vim]
|
||||
any = true
|
||||
|
||||
[jit]
|
||||
any = true
|
||||
|
||||
[[describe.args]]
|
||||
type = "string"
|
||||
[[describe.args]]
|
||||
type = "function"
|
||||
|
||||
[[it.args]]
|
||||
type = "string"
|
||||
[[it.args]]
|
||||
type = "function"
|
||||
|
||||
[[before_each.args]]
|
||||
type = "function"
|
||||
[[after_each.args]]
|
||||
type = "function"
|
||||
|
||||
[assert.is_not]
|
||||
any = true
|
||||
|
||||
[[assert.equals.args]]
|
||||
type = "any"
|
||||
[[assert.equals.args]]
|
||||
type = "any"
|
||||
[[assert.equals.args]]
|
||||
type = "any"
|
||||
required = false
|
||||
|
||||
[[assert.same.args]]
|
||||
type = "any"
|
||||
[[assert.same.args]]
|
||||
type = "any"
|
||||
|
||||
[[assert.truthy.args]]
|
||||
type = "any"
|
||||
|
||||
[[assert.spy.args]]
|
||||
type = "any"
|
||||
|
||||
[[assert.stub.args]]
|
||||
type = "any"
|
||||
Reference in New Issue
Block a user