commit 97c20626c101040d81ada72ac226910335f5e54b Author: Max Amundsen Date: Thu Aug 14 11:58:41 2025 -0400 init diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..4b583e3 Binary files /dev/null and b/.DS_Store differ diff --git a/.config/.DS_Store b/.config/.DS_Store new file mode 100644 index 0000000..7ad9def Binary files /dev/null and b/.config/.DS_Store differ diff --git a/.config/nvim/init.lua b/.config/nvim/init.lua new file mode 100644 index 0000000..3ecb0e6 --- /dev/null +++ b/.config/nvim/init.lua @@ -0,0 +1,44 @@ +-- Max Amundsen's Neovim Config +vim.opt.number = true +vim.opt.relativenumber = true -- Show relative line numbers +vim.opt.tabstop = 4 -- Number of spaces a tab counts for +vim.opt.shiftwidth = 4 -- Number of spaces for each indent +vim.opt.expandtab = true -- Convert tabs to spaces +vim.opt.autoindent = true -- Copy indent from current line when starting new line +vim.opt.ignorecase = true -- Case insensitive searching +vim.opt.smartcase = true -- Case sensitive when search contains uppercase +vim.opt.hlsearch = false -- Highlight search results +vim.opt.incsearch = true -- Show matches while typing +vim.opt.cursorline = false -- Highlight current line +vim.opt.showmatch = true -- Highlight matching brackets +-- vim.opt.termguicolors = true -- Enable 24-bit RGB colors +vim.opt.scrolloff = 8 -- Keep 8 lines visible above/below cursor +vim.opt.mouse = 'a' -- Enable mouse in all modes +vim.opt.clipboard = 'unnamedplus' -- Use system clipboard +vim.opt.undofile = true -- Persistent undo +vim.opt.swapfile = false -- Disable swap files +vim.opt.formatoptions:remove('r') +vim.g.mapleader = ' ' -- Set leader key to space +vim.opt.wrap = false + +-- KEYMAPS +vim.keymap.set('n', 'w', ':w') +vim.keymap.set('n', 'q', ':q') +vim.keymap.set('n', 'e', ':Explore') +vim.keymap.set("i", "", "") +vim.keymap.set("n", "s", [[:%s/\<\>//gI]]) +vim.keymap.set("n", "n", ":e ~/.config/nvim/init.lua") +vim.keymap.set("n", "a", "ggVG") +vim.keymap.set("n", "v", ":vsl") +vim.keymap.set("n", "h", ":spj") + +vim.keymap.set("n", "", "h") +vim.keymap.set("n", "", "l") +vim.keymap.set("n", "", "j") +vim.keymap.set("n", "", "k") + +local builtin = require('telescope.builtin') +vim.keymap.set('n', 'f', builtin.git_files, { desc = 'Telescope: Git find files' }) + +-- appearance + diff --git a/.config/nvim/pack/.DS_Store b/.config/nvim/pack/.DS_Store new file mode 100644 index 0000000..cab82cf Binary files /dev/null and b/.config/nvim/pack/.DS_Store differ diff --git a/.config/nvim/pack/vendor/.DS_Store b/.config/nvim/pack/vendor/.DS_Store new file mode 100644 index 0000000..64d255f Binary files /dev/null and b/.config/nvim/pack/vendor/.DS_Store differ diff --git a/.config/nvim/pack/vendor/start/.DS_Store b/.config/nvim/pack/vendor/start/.DS_Store new file mode 100644 index 0000000..99a7be4 Binary files /dev/null and b/.config/nvim/pack/vendor/start/.DS_Store differ diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/.github/FUNDING.yml b/.config/nvim/pack/vendor/start/plenary.nvim/.github/FUNDING.yml new file mode 100644 index 0000000..dc9d7fb --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/.github/FUNDING.yml @@ -0,0 +1 @@ +github: tjdevries diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/.github/workflows/default.yml b/.config/nvim/pack/vendor/start/plenary.nvim/.github/workflows/default.yml new file mode 100644 index 0000000..3ea01aa --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/.github/workflows/default.yml @@ -0,0 +1,68 @@ +name: default + +on: [push, pull_request] + +jobs: + run_tests: + name: unit tests + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-22.04 + rev: nightly/nvim-linux-x86_64.tar.gz + - os: ubuntu-22.04 + rev: v0.8.3/nvim-linux64.tar.gz + - os: ubuntu-22.04 + rev: v0.9.5/nvim-linux64.tar.gz + - os: ubuntu-22.04 + rev: v0.10.4/nvim-linux-x86_64.tar.gz + steps: + - uses: actions/checkout@v4 + - run: date +%F > todays-date + - name: Restore cache for today's nightly. + uses: actions/cache@v4 + with: + path: _neovim + key: ${{ runner.os }}-${{ matrix.rev }}-${{ hashFiles('todays-date') }} + - name: Prepare + run: | + test -d _neovim || { + mkdir -p _neovim + curl -sL "https://github.com/neovim/neovim/releases/download/${{ matrix.rev }}" | tar xzf - --strip-components=1 -C "${PWD}/_neovim" + } + + - name: Run tests + run: | + export PATH="${PWD}/_neovim/bin:${PATH}" + export VIM="${PWD}/_neovim/share/nvim/runtime" + nvim --version + make test + + stylua: + name: stylua + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: JohnnyMorganz/stylua-action@v2 + with: + token: ${{ secrets.GITHUB_TOKEN }} + version: latest + # CLI arguments + args: --color always --check . + + luacheck: + name: Luacheck + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + + - name: Prepare + run: | + sudo apt-get update + sudo apt-get install -y luarocks + sudo luarocks install luacheck + + - name: Lint + run: sudo make lint diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/.github/workflows/release.yml b/.config/nvim/pack/vendor/start/plenary.nvim/.github/workflows/release.yml new file mode 100644 index 0000000..cf8a766 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/.github/workflows/release.yml @@ -0,0 +1,21 @@ +name: "release" +on: + push: + tags: + - 'v*' +jobs: + luarocks-upload: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - name: LuaRocks Upload + uses: nvim-neorocks/luarocks-tag-release@v1.0.2 + env: + LUAROCKS_API_KEY: ${{ secrets.LUAROCKS_API_KEY }} + with: + summary: "lua functions you don't want to write" + detailed_description: | + plenary: full; complete; entire; absolute; unqualified. + All the lua functions I don't want to write twice. + template: "./rockspec.template" + diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/.gitignore b/.config/nvim/pack/vendor/start/plenary.nvim/.gitignore new file mode 100644 index 0000000..26def9e --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/.gitignore @@ -0,0 +1,43 @@ +# Compiled Lua sources +luac.out + +# luarocks build files +*.src.rock +*.zip +*.tar.gz + +# Object files +*.o +*.os +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo +*.def +*.exp + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +build/ +doc/tags diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/.luacheckrc b/.config/nvim/pack/vendor/start/plenary.nvim/.luacheckrc new file mode 100644 index 0000000..0bd6ca4 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/.luacheckrc @@ -0,0 +1,60 @@ +-- Rerun tests only if their modification time changed. +cache = true + +std = luajit +codes = true + +self = false + +-- Glorious list of warnings: https://luacheck.readthedocs.io/en/stable/warnings.html +ignore = { + "212", -- Unused argument, In the case of callback function, _arg_name is easier to understand than _, so this option is set to off. + "122", -- Indirectly setting a readonly global +} + +globals = { + "_", + "_PlenaryLeafTable", + "_PlenaryBustedOldAssert", + "_AssociatedBufs", +} + +-- Global objects defined by the C code +read_globals = { + "vim", +} + +exclude_files = { + "lua/plenary/profile/lua_profiler.lua", + "lua/plenary/profile/memory_profiler.lua", + "lua/plenary/async_lib/*.lua", +} + +files = { + ["lua/plenary/busted.lua"] = { + globals = { + "describe", + "it", + "pending", + "before_each", + "after_each", + "clear", + "assert", + "print", + }, + }, + ["lua/plenary/async/init.lua"] = { + globals = { + "a", + }, + }, + ["lua/plenary/async/tests.lua"] = { + globals = { + "describe", + "it", + "pending", + "before_each", + "after_each", + }, + }, +} diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/.stylua.toml b/.config/nvim/pack/vendor/start/plenary.nvim/.stylua.toml new file mode 100644 index 0000000..ecb6dca --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/.stylua.toml @@ -0,0 +1,6 @@ +column_width = 120 +line_endings = "Unix" +indent_type = "Spaces" +indent_width = 2 +quote_style = "AutoPreferDouble" +call_parentheses = "None" diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/.styluaignore b/.config/nvim/pack/vendor/start/plenary.nvim/.styluaignore new file mode 100644 index 0000000..a817c12 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/.styluaignore @@ -0,0 +1,10 @@ +build/ +data/ +lua/luassert +lua/plenary/profile.lua +lua/plenary/profile/ +lua/plenary/bit.lua +lua/plenary/_meta/_luassert.lua +lua/say.lua +scratch/ +scripts/ diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/LICENSE b/.config/nvim/pack/vendor/start/plenary.nvim/LICENSE new file mode 100644 index 0000000..f8e3950 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 TJ DeVries + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/Makefile b/.config/nvim/pack/vendor/start/plenary.nvim/Makefile new file mode 100644 index 0000000..304e1f8 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/Makefile @@ -0,0 +1,12 @@ +.PHONY: test generate_filetypes lint luarocks_upload test_luarocks_install +test: + nvim --headless --noplugin -u scripts/minimal.vim -c "PlenaryBustedDirectory tests/plenary/ {minimal_init = 'tests/minimal_init.vim', sequential = true}" + +generate_filetypes: + nvim --headless -c 'luafile scripts/update_filetypes_from_github.lua' -c 'qa!' + +generate_luassert_types: + nvim --headless -c 'luafile scripts/generate_luassert_types.lua' -c 'qa!' + +lint: + luacheck lua/plenary diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/POPUP.md b/.config/nvim/pack/vendor/start/plenary.nvim/POPUP.md new file mode 100644 index 0000000..c56df43 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/POPUP.md @@ -0,0 +1,89 @@ +# Popup tracking + +[WIP] An implementation of the Popup API from vim in Neovim. Hope to upstream +when complete + +## Goals + +Provide an API that is compatible with the vim `popup_*` APIs. After +stablization and any required features are merged into Neovim, we can upstream +this and expose the API in vimL to create better compatibility. + +## Notices +- **2021-09-19:** we now follow Vim's convention of the first line/column of the screen being indexed 1, so that 0 can be used for centering. +- **2021-08-19:** we now follow Vim's default to `noautocmd` on popup creation. This can be overriden with `vim_options.noautocmd=false` + +## List of Neovim Features Required: + +- [ ] Key handlers (used for `popup_filter`) +- [ ] scrollbar for floating windows + - [ ] scrollbar + - [ ] scrollbarhighlight + - [ ] thumbhighlight + +Optional: + +- [ ] Add forced transparency to a floating window. + - Apparently overrides text? + - This is for the `mask` feature flag + + +Unlikely (due to technical difficulties): + +- [ ] Add `textprop` wrappers? + - textprop + - textpropwin + - textpropid +- [ ] "close" + - But this is mostly because I don't know how to use mouse APIs in nvim. If someone knows. please make an issue in the repo, and maybe we can get it sorted out. + +Unlikely (due to not sure if people are using): +- [ ] tabpage + +## Progress + +Suported Features: + +- [x] what + - string + - list of strings +- [x] popup_create-arguments + - [x] border + - [x] borderchars + - [x] col + - [x] cursorline + - [x] highlight + - [x] line + - [x] {max,min}{height,width} + - [?] moved + - [x] "any" + - [ ] "word" + - [ ] "WORD" + - [ ] "expr" + - [ ] (list options) + - [x] padding + - [?] pos + - Somewhat implemented. Doesn't work with borders though. + - [x] posinvert + - [x] time + - [x] title + - [x] wrap + - [x] zindex + +## All known unimplemented vim features at the moment + +- firstline +- hidden +- ~ pos +- fixed +- filter +- filtermode +- mapping +- callback +- mouse: + - mousemoved + - close + - drag + - resize + +- (not implemented in vim yet) flip diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/README.md b/.config/nvim/pack/vendor/start/plenary.nvim/README.md new file mode 100644 index 0000000..85cbb8d --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/README.md @@ -0,0 +1,398 @@ +# plenary.nvim + +All the lua functions I don't want to write twice. + +> plenary: +> +> full; complete; entire; absolute; unqualified. + +Note that this library is useless outside of Neovim since it requires Neovim functions. It should be usable with any recent version of Neovim though. + +At the moment, it is very much in pre-alpha :smile: Expect changes to the way some functions are structured. I'm hoping to finish some document generators to provide better documentation for people to use and consume and then at some point we'll stabilize on a few more stable APIs. + +## Installation + +[![LuaRocks](https://img.shields.io/luarocks/v/Conni2461/plenary.nvim?logo=lua&color=purple)](https://luarocks.org/modules/Conni2461/plenary.nvim) + +Using [plug](https://github.com/junegunn/vim-plug): + +```vim +Plug 'nvim-lua/plenary.nvim' +``` + +Using [packer](https://github.com/wbthomason/packer.nvim): + +``` +use "nvim-lua/plenary.nvim" +``` + +## Modules + +- [plenary.async](#plenaryasync) +- [plenary.async_lib](#plenaryasync_lib) +- [plenary.job](#plenaryjob) +- [plenary.path](#plenarypath) +- [plenary.scandir](#plenaryscandir) +- [plenary.context_manager](#plenarycontext_manager) +- [plenary.test_harness](#plenarytest_harness) +- [plenary.filetype](#plenaryfiletype) +- [plenary.strings](#plenarystrings) + +### plenary.async + +A Lua module for asynchronous programming using coroutines. This library is built on native lua coroutines and `libuv`. Coroutines make it easy to avoid callback hell and allow for easy cooperative concurrency and cancellation. Apart from allowing users to perform asynchronous io easily, this library also functions as an abstraction for coroutines. + +#### Getting started + +You can do +```lua +local async = require "plenary.async" +``` +All other modules are automatically required and can be accessed by indexing `async`. +You needn't worry about performance as this will require all the submodules lazily. + +#### A quick example + +Libuv luv provides this example of reading a file. + +```lua +local uv = vim.loop + +local read_file = function(path, callback) + uv.fs_open(path, "r", 438, function(err, fd) + assert(not err, err) + uv.fs_fstat(fd, function(err, stat) + assert(not err, err) + uv.fs_read(fd, stat.size, 0, function(err, data) + assert(not err, err) + uv.fs_close(fd, function(err) + assert(not err, err) + callback(data) + end) + end) + end) + end) +end +``` + +We can write it using the library like this: +```lua +local a = require "plenary.async" + +local read_file = function(path) + local err, fd = a.uv.fs_open(path, "r", 438) + assert(not err, err) + + local err, stat = a.uv.fs_fstat(fd) + assert(not err, err) + + local err, data = a.uv.fs_read(fd, stat.size, 0) + assert(not err, err) + + local err = a.uv.fs_close(fd) + assert(not err, err) + + return data +end +``` + +#### Plugins using this + +- [telescope.nvim](https://github.com/nvim-telescope/telescope.nvim) +- [vgit.nvim](https://github.com/tanvirtin/vgit.nvim) +- [neogit](https://github.com/TimUntersberger/neogit) +- [neo-tree.nvim](https://github.com/nvim-neo-tree/neo-tree.nvim) + +### plenary.async_lib + +Please use `plenary.async` instead. This was version 1 and is just here for compatibility reasons. + +### plenary.async.control.channel.oneshot + +Creates a oneshot channel. It can only send data one time. + +The sender is not async while the receiver is. + +Example: + +```lua +local a = require'plenary.async' +local tx, rx = a.control.channel.oneshot() + +a.run(function() + local ret = long_running_fn() + tx(ret) +end) + +local ret = rx() +``` + +### plenary.async.control.channel.mpsc + +Creates a multiple producer single consumer channel. + +Example: + +```lua +local a = require'plenary.async' +local sender, receiver = a.control.channel.mpsc() + +a.run(function() + sender.send(10) + sender.send(20) +end) + +a.run(function() + sender.send(30) + sender.send(40) +end) + +for _ = 1, 4 do + local value = receiver.recv() + print('received:', value) +end +``` + +### plenary.job + +A Lua module to interact with system processes. Pass in your `command`, the desired `args`, `env` and `cwd`. +Define optional callbacks for `on_stdout`, `on_stderr` and `on_exit` and `start` your Job. + +Note: Each job has an empty environment. + +```lua +local Job = require'plenary.job' + +Job:new({ + command = 'rg', + args = { '--files' }, + cwd = '/usr/bin', + env = { ['a'] = 'b' }, + on_exit = function(j, return_val) + print(return_val) + print(j:result()) + end, +}):sync() -- or start() +``` + +### plenary.path + +A Lua module that implements a bunch of the things from `pathlib` from Python, so that paths are easy to work with. + +### plenary.scandir + +`plenary.scandir` is fast recursive file operations. It is similar to unix `find` or `fd` in that it can do recursive scans over a given directory, or a set of directories. + +It offers a wide range of opts for limiting the depth, show hidden and more. `plenary.scan_dir` can be ran synchronously and asynchronously and offers `on_insert(file, typ)` and `on_exit(files)` callbacks. `on_insert(file, typ)` is available for both while `on_exit(files)` is only available for async. + +```lua +local scan = require'plenary.scandir' +scan.scan_dir('.', { hidden = true, depth = 2 }) +``` + +This module also offers `ls -la` sync and async functions that will return a formated string for all files in the directory. +Why? Just for fun + +### plenary.context_manager + +Implements `with` and `open` just like in Python. For example: + +```lua +local with = context_manager.with +local open = context_manager.open + +local result = with(open("README.md"), function(reader) + return reader:read() +end) + +assert(result == "# plenary.nvim") +``` + + +### plenary.test_harness + +`:help plenary-test` + +Supports (simple) busted-style testing. It implements a mock-ed busted interface, that will allow you to run simple +busted style tests in separate neovim instances. + +To run the current spec file in a floating window, you can use the keymap `PlenaryTestFile`. For example: + +``` +nmap t PlenaryTestFile +``` +In this case, the test is run with a minimal configuration, that includes in +its runtimepath only plenary.nvim and the current working directory. + +To run a whole directory from the command line, you could do something like: + +``` +nvim --headless -c "PlenaryBustedDirectory tests/plenary/ {options}" +``` + +Where the first argument is the directory you'd like to test. It will search for files with +the pattern `*_spec.lua` and execute them in separate neovim instances. + +Without second argument, `PlenaryBustedDirectory` is also run with a minimal +configuration. Otherwise it is a Lua option table with the following fields: +- `nvim_cmd`: specify the command to launch this neovim instance (defaults to `vim.v.progpath`) +- `init`: specify an init.vim to use for this instance +- `minimal_init`: as for `init`, but also run the neovim instance with `--noplugin` +- `sequential`: whether to run tests sequentially (default is to run in parallel) +- `keep_going`: if `sequential`, whether to continue on test failure (default true) +- `timeout`: controls the maximum time allotted to each job in parallel or + sequential operation (defaults to 50,000 milliseconds) + +The exit code is 0 when success and 1 when fail, so you can use it easily in a `Makefile`! + + +NOTE: + +So far, the only supported busted items are: + +- `describe` +- `it` +- `pending` +- `before_each` +- `after_each` +- `clear` +- `assert.*` etc. (from luassert, which is bundled) + +OTHER NOTE: + +We used to support `luaunit` and original `busted` but it turns out it was way too hard and not worthwhile +for the difficulty of getting them setup, particularly on other platforms or in CI. Now, we have a dep free +(or at least, no other installation steps necessary) `busted` implementation that can be used more easily. + +Please take a look at the new APIs and make any issues for things that aren't clear. I am happy to fix them +and make it work well :) + +OTHER OTHER NOTE: +Take a look at some test examples [here](TESTS_README.md). + +#### Colors + +You no longer need nvim-terminal to get this to work. We use `nvim_open_term` now. + +### plenary.filetype + +Will detect the filetype based on `extension`/`special filename`/`shebang` or `modeline` + +- `require'plenary.filetype'.detect(filepath, opts)` is a function that does all of above and exits as soon as a filetype is found +- `require'plenary.filetype'.detect_from_extension(filepath)` +- `require'plenary.filetype'.detect_from_name(filepath)` +- `require'plenary.filetype'.detect_from_modeline(filepath)` +- `require'plenary.filetype'.detect_from_shebang(filepath)` + +Add filetypes by creating a new file named `~/.config/nvim/data/plenary/filetypes/foo.lua` and register that file with +`:lua require'plenary.filetype'.add_file('foo')`. Content of the file should look like that: +```lua +return { + extension = { + -- extension = filetype + -- example: + ['jl'] = 'julia', + }, + file_name = { + -- special filenames, likes .bashrc + -- we provide a decent amount + -- name = filetype + -- example: + ['.bashrc'] = 'bash', + }, + shebang = { + -- Shebangs are supported as well. Currently we provide + -- sh, bash, zsh, python, perl with different prefixes like + -- /usr/bin, /bin/, /usr/bin/env, /bin/env + -- shebang = filetype + -- example: + ['/usr/bin/node'] = 'javascript', + } +} +``` + +### plenary.strings + +Re-implement VimL funcs to use them in Lua loop. + +* `strings.strdisplaywidth` +* `strings.strcharpart` + +And some other funcs are here to deal with common problems. + +* `strings.truncate` +* `strings.align_str` +* `strings.dedent` + +### plenary.profile + +Thin wrapper around LuaJIT's [`jit.p` profiler](https://blast.hk/moonloader/luajit/ext_profiler.html). + +```lua +require'plenary.profile'.start("profile.log") + +-- code to be profiled + +require'plenary.profile'.stop() +``` + +You can use `start("profile.log", {flame = true})` to output the log in a +flamegraph-compatible format. A flamegraph can be created from this using +https://github.com/jonhoo/inferno via +``` +inferno-flamegraph profile.log > flame.svg +``` +The resulting interactive SVG file can be viewed in any browser. + +Status: WIP + +### plenary.popup + +See [popup documentation](./POPUP.md) for both progress tracking and implemented APIs. + +### plenary.window + +Window helper functions to wrap some of the more difficult cases. Particularly for floating windows. + +Status: WIP + +### plenary.collections + +Contains pure lua implementations for various standard collections. + +```lua +local List = require 'plenary.collections.py_list' + +local myList = List { 9, 14, 32, 5 } + +for i, v in myList:iter() do + print(i, v) +end + +``` + +Status: WIP + +### Troubleshooting + +If you're having trouble / things are hanging / other problems: + +``` +$ export DEBUG_PLENARY=true +``` + +This will enable debugging for the plugin. + +### plenary.neorocks + +DELETED: Please use packer.nvim or other lua-rocks wrapper instead. This no longer exists. + +### FAQ + +1. Error: Too many open files + +- \*nix systems have a setting to configure the maximum amount of open file + handles. It can occur that the default value is pretty low and that you end up + getting this error after opening a couple of files. On Linux you can see the + current limit with `ulimit -n` and set it with `ulimit -n 4096`. If you're on + macOS the command is `sudo launchctl limit maxfiles 4096 4096`. diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/TESTS_README.md b/.config/nvim/pack/vendor/start/plenary.nvim/TESTS_README.md new file mode 100644 index 0000000..955cdf9 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/TESTS_README.md @@ -0,0 +1,153 @@ +# Testing Guide + +Some testing examples using Plenary.nvim + +# A simple test + +This tests demonstrates a **describe** block that contains two tests defined with **it** blocks, the describe block also contains a **before_each** call that gets called before each test. + +```lua +describe("some basics", function() + + local bello = function(boo) + return "bello " .. boo + end + + local bounter + + before_each(function() + bounter = 0 + end) + + it("some test", function() + bounter = 100 + assert.equals("bello Brian", bello("Brian")) + end) + + it("some other test", function() + assert.equals(0, bounter) + end) +end) +``` + +The test **some test** checks that a functions output is as expected based on the input. The second test **some other test** checks that the variable **bounter** is reset for each test (as defined in the before_each block). + +# Running tests + +Run the test using `:PlenaryBustedFile `. + +```vimscript +" Run the test in the current buffer +:PlenaryBustedFile % +" Run all tests in the directory "tests/plenary/" +:PlenaryBustedDirectory tests/plenary/ +``` + +Or you can run tests in headless mode to see output in terminal: + +```bash +# run all tests in terminal +cd plenary.nvim +nvim --headless -c 'PlenaryBustedDirectory tests' +``` + +# mocking with luassert + +Plenary.nvim comes bundled with [luassert](https://github.com/Olivine-Labs/luassert) a library that's built to extend the built-int assertions... but it also comes with stubs, mocks and spies! + +Sometimes it's useful to test functions that have nvim api function calls within them, take for example the following example of a simple module that creates a new buffer and opens in it in a split. + + +**module.lua** +```lua +local M = {} + +function M.realistic_func() + local buf = vim.api.nvim_create_buf(false, true) + vim.api.nvim_command("sbuffer " .. buf) +end + +return M +``` + +The following is an example of completely mocking a module, and another of just stubbing a single function within a module. In this case the module is `vim.api`, with an aim of giving an example of a unit test (fully mocked) and an integration test... details in the comments. + +**module.lua** +```lua +-- import the luassert.mock module +local mock = require('luassert.mock') +local stub = require('luassert.stub') + +describe("example", function() + -- instance of module to be tested + local testModule = require('example.module') + -- mocked instance of api to interact with + + describe("realistic_func", function() + it("Should make expected calls to api, fully mocked", function() + -- mock the vim.api + local api = mock(vim.api, true) + + -- set expectation when mocked api call made + api.nvim_create_buf.returns(5) + + testModule.realistic_func() + + -- assert api was called with expcted values + assert.stub(api.nvim_create_buf).was_called_with(false, true) + -- assert api was called with set expectation + assert.stub(api.nvim_command).was_called_with("sbuffer 5") + + -- revert api back to it's former glory + mock.revert(api) + end) + + it("Should mock single api call", function() + -- capture some number of windows and buffers before + -- running our function + local buf_count = #vim.api.nvim_list_bufs() + local win_count = #vim.api.nvim_list_wins() + -- stub a single function in the api + stub(vim.api, "nvim_command") + + testModule.realistic_func() + + -- capture some details after running out function + local after_buf_count = #vim.api.nvim_list_bufs() + local after_win_count = #vim.api.nvim_list_wins() + + -- why 3 not two? NO IDEA! The point is we mocked + -- nvim_commad and there is only a single window + assert.equals(3, buf_count) + assert.equals(4, after_buf_count) + + -- WOOPIE! + assert.equals(1, win_count) + assert.equals(1, after_win_count) + end) + end) +end) +``` + +To test this in your `~/.config/nvim` configuration, try the suggested file structure: + +``` +lua/example/module.lua +lua/spec/example/module_spec.lua +``` + +# Asynchronous testing + +Tests run in a coroutine, which can be yielded and resumed. This can be used to +test code that uses asynchronous Neovim functionalities. For example, this can +be done inside a test: + +```lua +local co = coroutine.running() +vim.defer_fn(function() + coroutine.resume(co) +end, 1000) +--The test will reach here immediately. +coroutine.yield() +--The test will only reach here after one second, when the deferred function runs. +``` diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/data/plenary/filetypes/base.lua b/.config/nvim/pack/vendor/start/plenary.nvim/data/plenary/filetypes/base.lua new file mode 100644 index 0000000..1adffb3 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/data/plenary/filetypes/base.lua @@ -0,0 +1,886 @@ +return { + extension = { + ['ncl'] = [[text]], + ['ph'] = [[perl]], + ['al'] = [[perl]], + ['cl'] = [[lisp]], + ['vhi'] = [[vhdl]], + ['sublime-snippet'] = [[xml]], + ['tcl'] = [[tcl]], + ['pp'] = [[pascal]], + ['builds'] = [[xml]], + ['lua'] = [[lua]], + ['pkb'] = [[plsql]], + ['wl'] = [[mma]], + ['6'] = [[groff]], + ['scm'] = [[scheme]], + ['ml'] = [[ocaml]], + ['filters'] = [[xml]], + ['st'] = [[html]], + ['ksy'] = [[yaml]], + ['mt'] = [[mma]], + ['ada'] = [[ada]], + ['vho'] = [[vhdl]], + ['nawk'] = [[awk]], + ['3pm'] = [[groff]], + ['maxhelp'] = [[json]], + ['ct'] = [[xml]], + ['ipp'] = [[cpp]], + ['a51'] = [[asm]], + ['meta'] = [[yaml]], + ['fsproj'] = [[xml]], + ['ccxml'] = [[xml]], + ['ado'] = [[stata]], + ['mli'] = [[ocaml]], + ['r2'] = [[rebol]], + ['csl'] = [[xml]], + ['bbx'] = [[tex]], + ['xsjslib'] = [[javascript]], + ['e'] = [[eiffel]], + ['tac'] = [[python]], + ['mustache'] = [[smarty]], + ['zcml'] = [[xml]], + ['glade'] = [[xml]], + ['cuh'] = [[cuda]], + ['cxx'] = [[cpp]], + ['urdf'] = [[xml]], + ['ditaval'] = [[xml]], + ['cscfg'] = [[xml]], + ['j2'] = [[django]], + ['pkgproj'] = [[xml]], + ['ma'] = [[mma]], + ['cljs.hl'] = [[clojure]], + ['brd'] = [[xml]], + ['asn'] = [[asn]], + ['xspec'] = [[xml]], + ['db2'] = [[sql]], + ['sjs'] = [[javascript]], + ['m'] = [[matlab]], + ['gdbinit'] = [[gdb]], + ['re'] = [[cpp]], + ['adoc'] = [[asciidoc]], + ['pyde'] = [[python]], + ['mjml'] = [[xml]], + ['workbook'] = [[markdown]], + ['ssjs'] = [[javascript]], + ['dircolors'] = [[dircolors]], + ['ui'] = [[xml]], + ['ant'] = [[xml]], + ['wast'] = [[wast]], + ['_js'] = [[javascript]], + ['7'] = [[groff]], + ['dylan'] = [[dylan]], + ['jsproj'] = [[xml]], + ['jsb'] = [[javascript]], + ['xml'] = [[xml]], + ['tcsh'] = [[tcsh]], + ['xpy'] = [[python]], + ['wisp'] = [[clojure]], + ['tm'] = [[tcl]], + ['plot'] = [[gnuplot]], + ['mjs'] = [[javascript]], + ['cjs'] = [[javascript]], + ['env'] = [[sh]], + ['jsfl'] = [[javascript]], + ['eye'] = [[ruby]], + ['jinja2'] = [[django]], + ['lookml'] = [[yaml]], + ['mcmeta'] = [[json]], + ['workflow'] = [[xml]], + ['eq'] = [[cs]], + ['storyboard'] = [[xml]], + ['nbp'] = [[mma]], + ['maxpat'] = [[json]], + ['yy'] = [[yacc]], + ['h++'] = [[cpp]], + ['asc'] = [[asciidoc]], + ['phps'] = [[php]], + ['cabal'] = [[cabal]], + ['xacro'] = [[xml]], + ['dhall'] = [[haskell]], + ['vba'] = [[vim]], + ['feature'] = [[cucumber]], + ['psc1'] = [[xml]], + ['plb'] = [[plsql]], + ['logtalk'] = [[logtalk]], + ['pascal'] = [[pascal]], + ['smk'] = [[python]], + ['rst.txt'] = [[rst]], + ['svh'] = [[systemverilog]], + ['sthlp'] = [[stata]], + ['nb'] = [[text]], + ['hrl'] = [[erlang]], + ['f'] = [[forth]], + ['rb'] = [[ruby]], + ['xsd'] = [[xml]], + ['cfg'] = [[dosini]], + ['1in'] = [[groff]], + ['mkiv'] = [[tex]], + ['mask'] = [[yaml]], + ['cproject'] = [[xml]], + ['gnuplot'] = [[gnuplot]], + ['sagews'] = [[python]], + ['nsh'] = [[nsis]], + ['njk'] = [[django]], + ['pic'] = [[pic]], + ['man'] = [[groff]], + ['owl'] = [[xml]], + ['ino'] = [[cpp]], + ['dtx'] = [[tex]], + ['volt'] = [[d]], + ['ltx'] = [[tex]], + ['rtf'] = [[rtf]], + ['jscad'] = [[javascript]], + ['8'] = [[groff]], + ['3qt'] = [[groff]], + ['n'] = [[groff]], + ['lisp'] = [[lisp]], + ['forth'] = [[forth]], + ['dll.config'] = [[xml]], + ['prefab'] = [[yaml]], + ['ads'] = [[ada]], + ['jake'] = [[javascript]], + ['zsh'] = [[sh]], + ['v'] = [[verilog]], + ['ini'] = [[dosini]], + ['command'] = [[sh]], + ['fr'] = [[forth]], + ['yacc'] = [[yacc]], + ['ccproj'] = [[xml]], + ['xqy'] = [[xquery]], + ['rabl'] = [[ruby]], + ['nr'] = [[groff]], + ['wsgi'] = [[python]], + ['model.lkml'] = [[yaml]], + ['lsl'] = [[lsl]], + ['yyp'] = [[json]], + ['yaml-tmlanguage'] = [[yaml]], + ['rbi'] = [[ruby]], + ['srt'] = [[lisp]], + ['unity'] = [[yaml]], + ['vhs'] = [[vhdl]], + ['yap'] = [[prolog]], + ['php5'] = [[php]], + ['pas'] = [[pascal]], + ['gyp'] = [[python]], + ['jsonl'] = [[json]], + ['cc'] = [[cpp]], + ['grt'] = [[groovy]], + ['txi'] = [[texinfo]], + ['asm'] = [[asm]], + ['plx'] = [[perl]], + ['prefs'] = [[dosini]], + ['mly'] = [[ocaml]], + ['xsl'] = [[xslt]], + ['osm'] = [[xml]], + ['sc'] = [[scala]], + ['au3'] = [[autoit]], + ['obj'] = [[obj]], + ['uc'] = [[java]], + ['lhs'] = [[lhaskell]], + ['1'] = [[groff]], + ['resx'] = [[xml]], + ['geojson'] = [[json]], + ['tmux'] = [[sh]], + ['rg'] = [[clojure]], + ['tcc'] = [[cpp]], + ['cbl'] = [[cobol]], + ['wat'] = [[wast]], + ['com'] = [[dcl]], + ['podspec'] = [[ruby]], + ['no'] = [[text]], + ['spec'] = [[python]], + ['vhw'] = [[vhdl]], + ['maxproj'] = [[json]], + ['xbm'] = [[c]], + ['wxl'] = [[xml]], + ['mk'] = [[make]], + ['epj'] = [[json]], + ['do'] = [[stata]], + ['rexx'] = [[rexx]], + ['ms'] = [[groff]], + ['w'] = [[cweb]], + ['ss'] = [[scheme]], + ['desktop.in'] = [[desktop]], + ['po'] = [[po]], + ['mdpolicy'] = [[xml]], + ['mathematica'] = [[mma]], + ['vw'] = [[plsql]], + ['rss'] = [[xml]], + ['cs'] = [[cs]], + ['mkdown'] = [[markdown]], + ['gs'] = [[javascript]], + ['doh'] = [[stata]], + ['vbproj'] = [[xml]], + ['sfproj'] = [[xml]], + ['mat'] = [[yaml]], + ['cmake.in'] = [[cmake]], + ['rest.txt'] = [[rst]], + ['sls'] = [[scheme]], + ['fxml'] = [[xml]], + ['jss'] = [[javascript]], + ['avsc'] = [[json]], + ['mbox'] = [[basic]], + ['cake'] = [[cs]], + ['gst'] = [[xml]], + ['p6l'] = [[perl6]], + ['cdf'] = [[mma]], + ['pyi'] = [[python]], + ['ahk'] = [[autohotkey]], + ['tmac'] = [[groff]], + ['xaml'] = [[xml]], + ['texi'] = [[texinfo]], + ['udf'] = [[sql]], + ['rebol'] = [[rebol]], + ['gvy'] = [[groovy]], + ['h'] = [[c]], + ['pm6'] = [[perl6]], + ['ivy'] = [[xml]], + ['properties'] = [[dosini]], + ['eliomi'] = [[ocaml]], + ['nanorc'] = [[nanorc]], + ['sps'] = [[scheme]], + ['nproj'] = [[xml]], + ['ch'] = [[clipper]], + ['odd'] = [[xml]], + ['fun'] = [[sml]], + ['mir'] = [[yaml]], + ['nuspec'] = [[xml]], + ['pck'] = [[plsql]], + ['2'] = [[groff]], + ['nl'] = [[lisp]], + ['fth'] = [[forth]], + ['cps'] = [[pascal]], + ['sh'] = [[sh]], + ['rbw'] = [[ruby]], + ['dlm'] = [[idl]], + ['pod'] = [[pod]], + ['clixml'] = [[xml]], + ['duby'] = [[ruby]], + ['gdb'] = [[gdb]], + ['boot'] = [[clojure]], + ['adb'] = [[ada]], + ['tex'] = [[tex]], + ['csx'] = [[cs]], + ['sbt'] = [[scala]], + ['csdef'] = [[xml]], + ['dpr'] = [[pascal]], + ['rex'] = [[rexx]], + ['srdf'] = [[xml]], + ['pl'] = [[perl]], + ['markdown'] = [[markdown]], + ['cgi'] = [[perl]], + ['cp'] = [[cpp]], + ['jinja'] = [[django]], + ['gp'] = [[gnuplot]], + ['x'] = [[rpcgen]], + ['pt'] = [[xml]], + ['tpp'] = [[cpp]], + ['intr'] = [[dylan]], + ['JSON-tmLanguage'] = [[json]], + ['ux'] = [[xml]], + ['fpp'] = [[fortran]], + ['phpt'] = [[php]], + ['4th'] = [[forth]], + ['dita'] = [[xml]], + ['viw'] = [[sql]], + ['vsixmanifest'] = [[xml]], + ['clj'] = [[clojure]], + ['yml.mysql'] = [[yaml]], + ['hpp'] = [[cpp]], + ['xsp.metadata'] = [[xml]], + ['sexp'] = [[lisp]], + ['csproj'] = [[xml]], + ['tab'] = [[sql]], + ['cql'] = [[sql]], + ['abap'] = [[abap]], + ['wlua'] = [[lua]], + ['lex'] = [[lex]], + ['java'] = [[java]], + ['edn'] = [[clojure]], + ['ditamap'] = [[xml]], + ['3p'] = [[groff]], + ['xul'] = [[xml]], + ['cmake'] = [[cmake]], + ['kit'] = [[basic]], + ['rs.in'] = [[rust]], + ['php4'] = [[php]], + ['hxx'] = [[cpp]], + ['cljscm'] = [[clojure]], + ['vcxproj'] = [[xml]], + ['php3'] = [[php]], + ['xml.dist'] = [[xml]], + ['ged'] = [[gedcom]], + ['i'] = [[asm]], + ['xproc'] = [[xml]], + ['ndproj'] = [[xml]], + ['less'] = [[less]], + ['lgt'] = [[logtalk]], + ['sql'] = [[plsql]], + ['cls'] = [[tex]], + ['targets'] = [[xml]], + ['me'] = [[groff]], + ['3x'] = [[groff]], + ['gtpl'] = [[groovy]], + ['prg'] = [[clipper]], + ['natvis'] = [[xml]], + ['3'] = [[groff]], + ['di'] = [[d]], + ['1x'] = [[groff]], + ['mdoc'] = [[groff]], + ['xsjs'] = [[javascript]], + ['iml'] = [[xml]], + ['ctp'] = [[php]], + ['kml'] = [[xml]], + ['eml'] = [[basic]], + ['raml'] = [[raml]], + ['gml'] = [[xml]], + ['yml'] = [[yaml]], + ['xq'] = [[xquery]], + ['sml'] = [[sml]], + ['sublime-syntax'] = [[yaml]], + ['mm'] = [[xml]], + ['y'] = [[yacc]], + ['mu'] = [[mupad]], + ['sld'] = [[scheme]], + ['asd'] = [[lisp]], + ['pgsql'] = [[sql]], + ['nqp'] = [[perl6]], + ['anim'] = [[yaml]], + ['vhf'] = [[vhdl]], + ['cu'] = [[cuda]], + ['make'] = [[make]], + ['pyx'] = [[pyrex]], + ['c++'] = [[cpp]], + ['lslp'] = [[lsl]], + ['rake'] = [[ruby]], + ['webmanifest'] = [[json]], + ['ijs'] = [[j]], + ['hsc'] = [[haskell]], + ['pl6'] = [[perl6]], + ['p6m'] = [[perl6]], + ['ny'] = [[lisp]], + ['mak'] = [[make]], + ['p6'] = [[perl6]], + ['6pm'] = [[perl6]], + ['lfe'] = [[lisp]], + ['6pl'] = [[perl6]], + ['toc'] = [[tex]], + ['yrl'] = [[erlang]], + ['vssettings'] = [[xml]], + ['sty'] = [[tex]], + ['ipynb'] = [[json]], + ['mkvi'] = [[tex]], + ['p'] = [[gnuplot]], + ['lmi'] = [[python]], + ['rockspec'] = [[lua]], + ['vhd'] = [[vhdl]], + ['sieve'] = [[sieve]], + ['lbx'] = [[tex]], + ['1m'] = [[groff]], + ['jelly'] = [[xml]], + ['3m'] = [[groff]], + ['ins'] = [[tex]], + ['xib'] = [[xml]], + ['cbx'] = [[tex]], + ['pd_lua'] = [[lua]], + ['dae'] = [[xml]], + ['emacs.desktop'] = [[lisp]], + ['bison'] = [[yacc]], + ['matlab'] = [[matlab]], + ['emacs'] = [[lisp]], + ['pot'] = [[po]], + ['el'] = [[lisp]], + ['podsl'] = [[lisp]], + ['pac'] = [[javascript]], + ['gitignore'] = [[gitignore]], + ['vue'] = [[vue]], + ['html.hl'] = [[html]], + ['trg'] = [[plsql]], + ['tps'] = [[plsql]], + ['hs-boot'] = [[haskell]], + ['csh'] = [[tcsh]], + ['mawk'] = [[awk]], + ['fan'] = [[fan]], + ['pike'] = [[pike]], + ['story'] = [[cucumber]], + ['plsql'] = [[plsql]], + ['app.src'] = [[erlang]], + ['mkd'] = [[markdown]], + ['ksh'] = [[sh]], + ['inl'] = [[cpp]], + ['ml4'] = [[ocaml]], + ['f77'] = [[fortran]], + ['pls'] = [[plsql]], + ['opencl'] = [[c]], + ['proj'] = [[xml]], + ['4'] = [[groff]], + ['wsf'] = [[xml]], + ['ninja'] = [[ninja]], + ['rest'] = [[rst]], + ['tpb'] = [[plsql]], + ['xquery'] = [[xquery]], + ['gitconfig'] = [[gitconfig]], + ['r3'] = [[rebol]], + ['reb'] = [[rebol]], + ['gawk'] = [[awk]], + ['groovy'] = [[groovy]], + ['auk'] = [[awk]], + ['r'] = [[rebol]], + ['cmd'] = [[dosbatch]], + ['awk'] = [[awk]], + ['xslt'] = [[xslt]], + ['ps1xml'] = [[xml]], + ['spc'] = [[plsql]], + ['wixproj'] = [[xml]], + ['upc'] = [[c]], + ['hic'] = [[clojure]], + ['cljx'] = [[clojure]], + ['cljs'] = [[clojure]], + ['json'] = [[json]], + ['cljc'] = [[clojure]], + ['pfa'] = [[postscr]], + ['vhdl'] = [[vhdl]], + ['cl2'] = [[clojure]], + ['nsi'] = [[nsis]], + ['g4'] = [[antlr]], + ['sage'] = [[python]], + ['haml.deface'] = [[haml]], + ['haml'] = [[haml]], + ['rno'] = [[groff]], + ['xrl'] = [[erlang]], + ['escript'] = [[erlang]], + ['pks'] = [[plsql]], + ['grxml'] = [[xml]], + ['erl'] = [[erlang]], + ['chem'] = [[pic]], + ['eb'] = [[python]], + ['patch'] = [[diff]], + ['diff'] = [[diff]], + ['mspec'] = [[ruby]], + ['xsp-config'] = [[xml]], + ['gv'] = [[dot]], + ['adp'] = [[tcl]], + ['webapp'] = [[json]], + ['epsi'] = [[postscr]], + ['dot'] = [[dot]], + ['phtml'] = [[php]], + ['lid'] = [[dylan]], + ['cpy'] = [[cobol]], + ['mkdn'] = [[markdown]], + ['ccp'] = [[cobol]], + ['cob'] = [[cobol]], + ['glf'] = [[tcl]], + ['sass'] = [[sass]], + ['gnu'] = [[gnuplot]], + ['pyp'] = [[python]], + ['gsp'] = [[gsp]], + ['asn1'] = [[asn]], + ['sig'] = [[sml]], + ['t'] = [[perl]], + ['xlf'] = [[xml]], + ['perl'] = [[perl]], + ['rpy'] = [[python]], + ['jbuilder'] = [[ruby]], + ['mdwn'] = [[markdown]], + ['dfm'] = [[pascal]], + ['c'] = [[c]], + ['pyw'] = [[python]], + ['mkfile'] = [[make]], + ['py3'] = [[python]], + ['gypi'] = [[python]], + ['py'] = [[python]], + ['watchr'] = [[ruby]], + ['thor'] = [[ruby]], + ['ruby'] = [[ruby]], + ['ru'] = [[ruby]], + ['gltf'] = [[json]], + ['rbx'] = [[ruby]], + ['rbuild'] = [[ruby]], + ['m4'] = [[m4]], + ['god'] = [[ruby]], + ['har'] = [[json]], + ['sas'] = [[sas]], + ['mkii'] = [[tex]], + ['frt'] = [[forth]], + ['icl'] = [[clean]], + ['mirah'] = [[ruby]], + ['wxi'] = [[xml]], + ['lektorproject'] = [[dosini]], + ['nasm'] = [[asm]], + ['njs'] = [[javascript]], + ['vstemplate'] = [[xml]], + ['jsm'] = [[javascript]], + ['html'] = [[html]], + ['xpm'] = [[xpm]], + ['tool'] = [[sh]], + ['rdf'] = [[xml]], + ['rd'] = [[r]], + ['dyl'] = [[dylan]], + ['p8'] = [[lua]], + ['prw'] = [[clipper]], + ['es6'] = [[javascript]], + ['gmx'] = [[xml]], + ['pluginspec'] = [[ruby]], + ['gemspec'] = [[ruby]], + ['aux'] = [[tex]], + ['lvlib'] = [[xml]], + ['cats'] = [[c]], + ['fcgi'] = [[perl]], + ['for'] = [[forth]], + ['scxml'] = [[xml]], + ['mdown'] = [[markdown]], + ['scala'] = [[scala]], + ['pxd'] = [[pyrex]], + ['mxml'] = [[xml]], + ['chs'] = [[haskell]], + ['syntax'] = [[yaml]], + ['5'] = [[groff]], + ['xliff'] = [[xml]], + ['pyt'] = [[python]], + ['view.lkml'] = [[yaml]], + ['pat'] = [[json]], + ['bash'] = [[sh]], + ['tfstate'] = [[json]], + ['vmb'] = [[vim]], + ['prolog'] = [[prolog]], + ['asciidoc'] = [[asciidoc]], + ['asset'] = [[yaml]], + ['mxt'] = [[json]], + ['rviz'] = [[yaml]], + ['yaml.sed'] = [[yaml]], + ['inc'] = [[php]], + ['props'] = [[xml]], + ['psgi'] = [[perl]], + ['axml'] = [[xml]], + ['pxi'] = [[pyrex]], + ['admx'] = [[xml]], + ['nse'] = [[lua]], + ['wxs'] = [[xml]], + ['ice'] = [[json]], + ['builder'] = [[ruby]], + ['jsp'] = [[jsp]], + ['eliom'] = [[ocaml]], + ['go'] = [[go]], + ['ihlp'] = [[stata]], + ['scss'] = [[scss]], + ['sce'] = [[scilab]], + ['lsp'] = [[lisp]], + ['x3d'] = [[xml]], + ['rs'] = [[rust]], + ['php'] = [[php]], + ['htm'] = [[html]], + ['pprx'] = [[rexx]], + ['es'] = [[erlang]], + ['js'] = [[javascript]], + ['ts'] = [[typescript]], + ['uno'] = [[cs]], + ['sch'] = [[scheme]], + ['lvproj'] = [[xml]], + ['xs'] = [[xs]], + ['pro'] = [[idl]], + ['dotsettings'] = [[xml]], + ['res'] = [[xml]], + ['xmi'] = [[xml]], + ['ahkl'] = [[autohotkey]], + ['plt'] = [[gnuplot]], + ['wlt'] = [[mma]], + ['lpr'] = [[pascal]], + ['dof'] = [[dosini]], + ['fs'] = [[forth]], + ['sh.in'] = [[sh]], + ['bat'] = [[dosbatch]], + ['roff'] = [[groff]], + ['depproj'] = [[xml]], + ['mdx'] = [[markdown]], + ['hs'] = [[haskell]], + ['frag'] = [[javascript]], + ['ps'] = [[postscr]], + ['9'] = [[groff]], + ['eps'] = [[postscr]], + ['ck'] = [[java]], + ['rst'] = [[rst]], + ['css'] = [[css]], + ['aw'] = [[php]], + ['veo'] = [[verilog]], + ['adml'] = [[xml]], + ['mll'] = [[ocaml]], + ['tst'] = [[scilab]], + ['svg'] = [[svg]], + ['bdy'] = [[plsql]], + ['xql'] = [[xquery]], + ['xqm'] = [[xquery]], + ['pm'] = [[perl]], + ['texinfo'] = [[texinfo]], + ['prc'] = [[plsql]], + ['3in'] = [[groff]], + ['rbxs'] = [[lua]], + ['xproj'] = [[xml]], + ['bdf'] = [[bdf]], + ['ampl'] = [[ampl]], + ['d'] = [[d]], + ['fnc'] = [[plsql]], + ['cobol'] = [[cobol]], + ['txt'] = [[text]], + ['vim'] = [[vim]], + ['mata'] = [[stata]], + ['linq'] = [[cs]], + ['launch'] = [[xml]], + ['sv'] = [[systemverilog]], + ['nlogo'] = [[lisp]], + ['sci'] = [[scilab]], + ['dockerfile'] = [[dockerfile]], + ['vht'] = [[vhdl]], + ['sed'] = [[sed]], + ['xht'] = [[html]], + ['liquid'] = [[liquid]], + ['latte'] = [[latte]], + ['vhost'] = [[apache]], + ['matah'] = [[stata]], + ['ronn'] = [[markdown]], + ['tpl'] = [[smarty]], + ['xhtml'] = [[html]], + ['bones'] = [[javascript]], + ['bats'] = [[sh]], + ['xpl'] = [[xml]], + ['shproj'] = [[xml]], + ['tfstate.backup'] = [[json]], + ['bzl'] = [[bzl]], + ['cpp'] = [[cpp]], + ['cppm'] = [[cpp]], + ['mod'] = [[ampl]], + ['idc'] = [[c]], + ['hh'] = [[cpp]], + ['druby'] = [[ruby]], + ['apacheconf'] = [[apache]], + ['l'] = [[lex]], + ['md'] = [[markdown]], + ['vxml'] = [[xml]], + ['pmod'] = [[pike]], + ['wsdl'] = [[xml]], + ['mtml'] = [[basic]], + ['vh'] = [[systemverilog]], + ['ddl'] = [[plsql]], + ['topojson'] = [[json]], + ['mysql'] = [[sql]], + ['kojo'] = [[scala]], + ['rsx'] = [[r]], + ['tml'] = [[xml]], + ['dcl'] = [[clean]], + ['reek'] = [[yaml]], + ['yaml'] = [[yaml]], + ['desktop'] = [[desktop]], + ['tsx'] = [[xml]], + }, + file_name = { + ['.classpath'] = [[xml]], + ['bsdmakefile'] = [[make]], + ['delete.me'] = [[text]], + ['packages.config'] = [[xml]], + ['jenkinsfile'] = [[groovy]], + ['ant.xml'] = [[ant]], + ['makefile.frag'] = [[make]], + ['puppetfile'] = [[ruby]], + ['.inputrc'] = [[readline]], + ['.zshrc'] = [[sh]], + ['inputrc'] = [[readline]], + ['gnumakefile'] = [[make]], + ['makefile.sco'] = [[make]], + ['pkgbuild'] = [[sh]], + ['.babelignore'] = [[gitignore]], + ['.bash_logout'] = [[sh]], + ['.nanorc'] = [[nanorc]], + ['berksfile'] = [[ruby]], + ['lexer.x'] = [[lex]], + ['sconscript'] = [[python]], + ['makefile.boot'] = [[make]], + ['starfield'] = [[tcl]], + ['.login'] = [[sh]], + ['composer.lock'] = [[json]], + ['dir_colors'] = [[dircolors]], + ['.nvimrc'] = [[vim]], + ['.project'] = [[xml]], + ['web.config'] = [[xml]], + ['makefile.in'] = [[make]], + ['readme.me'] = [[text]], + ['.cproject'] = [[xml]], + ['nuget.config'] = [[xml]], + ['zlogin'] = [[sh]], + ['phakefile'] = [[php]], + ['brewfile'] = [[ruby]], + ['podfile'] = [[ruby]], + ['use.stable.mask'] = [[text]], + ['.gemrc'] = [[yaml]], + ['emakefile'] = [[erlang]], + ['notebook'] = [[json]], + ['read.me'] = [[text]], + ['.dircolors'] = [[dircolors]], + ['.gitmodules'] = [[gitconfig]], + ['owh'] = [[tcl]], + ['zlogout'] = [[sh]], + ['package.use.stable.mask'] = [[text]], + ['httpd.conf'] = [[apache]], + ['buildozer.spec'] = [[dosini]], + ['.zshenv'] = [[sh]], + ['workspace'] = [[bzl]], + ['readme.1st'] = [[text]], + ['.simplecov'] = [[ruby]], + ['.vimrc'] = [[vim]], + ['.htmlhintrc'] = [[json]], + ['mmn'] = [[groff]], + ['rexfile'] = [[perl]], + ['m3overrides'] = [[quake]], + ['.arcconfig'] = [[json]], + ['.htaccess'] = [[apache]], + ['.php_cs'] = [[php]], + ['tiltfile'] = [[bzl]], + ['wscript'] = [[python]], + ['.stylelintignore'] = [[gitignore]], + ['gvimrc'] = [[vim]], + ['.tern-config'] = [[json]], + ['_dir_colors'] = [[dircolors]], + ['app.config'] = [[xml]], + ['abbrev_defs'] = [[lisp]], + ['_emacs'] = [[lisp]], + ['buck'] = [[bzl]], + ['dockerfile'] = [[dockerfile]], + ['project.ede'] = [[lisp]], + ['thorfile'] = [[ruby]], + ['rakefile'] = [[ruby]], + ['.viper'] = [[lisp]], + ['.spacemacs'] = [[lisp]], + ['.gnus'] = [[lisp]], + ['snapfile'] = [[ruby]], + ['eqnrc'] = [[groff]], + ['_dircolors'] = [[dircolors]], + ['configure.ac'] = [[m4]], + ['cabal.project'] = [[cabal]], + ['.env'] = [[sh]], + ['.luacheckrc'] = [[lua]], + ['9fs'] = [[sh]], + ['cabal.config'] = [[cabal]], + ['.bash_aliases'] = [[sh]], + ['install.mysql'] = [[text]], + ['yarn.lock'] = [[yaml]], + ['gitignore_global'] = [[gitignore]], + ['.cshrc'] = [[sh]], + ['.bash_history'] = [[sh]], + ['.env.example'] = [[sh]], + ['.npmignore'] = [[gitignore]], + ['gitignore-global'] = [[gitignore]], + ['build.bazel'] = [[bzl]], + ['.flaskenv'] = [[sh]], + ['license'] = [[text]], + ['mavenfile'] = [[ruby]], + ['.vscodeignore'] = [[gitignore]], + ['.abbrev_defs'] = [[lisp]], + ['gemfile.lock'] = [[ruby]], + ['go.mod'] = [[text]], + ['.gvimrc'] = [[vim]], + ['.nodemonignore'] = [[gitignore]], + ['bashrc'] = [[sh]], + ['man'] = [[sh]], + ['.dockerignore'] = [[gitignore]], + ['makefile.am'] = [[make]], + ['.zlogin'] = [[sh]], + ['.cvsignore'] = [[gitignore]], + ['.coffeelintignore'] = [[gitignore]], + ['.php'] = [[php]], + ['expr-dist'] = [[r]], + ['zshrc'] = [[sh]], + ['.clang-format'] = [[yaml]], + ['bash_aliases'] = [[sh]], + ['.exrc'] = [[vim]], + ['web.release.config'] = [[xml]], + ['.atomignore'] = [[gitignore]], + ['makefile.wat'] = [[make]], + ['troffrc-end'] = [[groff]], + ['vimrc'] = [[vim]], + ['cmakelists.txt'] = [[cmake]], + ['.gitconfig'] = [[gitconfig]], + ['riemann.config'] = [[clojure]], + ['zprofile'] = [[sh]], + ['rebar.lock'] = [[erlang]], + ['news'] = [[text]], + ['rebar.config'] = [[erlang]], + ['mcmod.info'] = [[json]], + ['cpanfile'] = [[perl]], + ['readme.mysql'] = [[text]], + ['makefile.pl'] = [[perl]], + ['dangerfile'] = [[ruby]], + ['snakefile'] = [[python]], + ['contents.lr'] = [[markdown]], + ['sconstruct'] = [[python]], + ['glide.lock'] = [[yaml]], + ['deps'] = [[python]], + ['.zprofile'] = [[sh]], + ['.gclient'] = [[python]], + ['keep.me'] = [[text]], + ['troffrc'] = [[groff]], + ['m3makefile'] = [[quake]], + ['cshrc'] = [[sh]], + ['.emacs.desktop'] = [[lisp]], + ['.zlogout'] = [[sh]], + ['.rprofile'] = [[r]], + ['cask'] = [[lisp]], + ['jarfile'] = [[ruby]], + ['deliverfile'] = [[ruby]], + ['copyright.regex'] = [[text]], + ['.prettierignore'] = [[gitignore]], + ['gemfile'] = [[ruby]], + ['profile'] = [[sh]], + ['fastfile'] = [[ruby]], + ['guardfile'] = [[ruby]], + ['capfile'] = [[ruby]], + ['buildfile'] = [[ruby]], + ['jakefile'] = [[javascript]], + ['appraisals'] = [[ruby]], + ['fontlog'] = [[text]], + ['package.use.mask'] = [[text]], + ['.pryrc'] = [[ruby]], + ['.emacs'] = [[lisp]], + ['.watchmanconfig'] = [[json]], + ['.irbrc'] = [[ruby]], + ['.tern-project'] = [[json]], + ['web.debug.config'] = [[xml]], + ['.eslintignore'] = [[gitignore]], + ['.gitignore'] = [[gitignore]], + ['copying.regex'] = [[text]], + ['build'] = [[bzl]], + ['nvimrc'] = [[vim]], + ['.dir_colors'] = [[dircolors]], + ['.bashrc'] = [[sh]], + ['kbuild'] = [[make]], + ['.profile'] = [[sh]], + ['.php_cs.dist'] = [[php]], + ['gradlew'] = [[sh]], + ['settings.stylecop'] = [[xml]], + ['makefile.inc'] = [[make]], + ['mkfile'] = [[make]], + ['.bzrignore'] = [[gitignore]], + ['ack'] = [[perl]], + ['license.mysql'] = [[text]], + ['makefile'] = [[make]], + ['vagrantfile'] = [[ruby]], + ['nanorc'] = [[nanorc]], + ['.bash_profile'] = [[sh]], + ['bash_logout'] = [[sh]], + ['bash_profile'] = [[sh]], + ['click.me'] = [[text]], + ['login'] = [[sh]], + ['_vimrc'] = [[vim]], + ['go.sum'] = [[text]], + ['apache2.conf'] = [[apache]], + ['use.mask'] = [[text]], + ['build.xml'] = [[ant]], + ['mmt'] = [[groff]], + ['zshenv'] = [[sh]], + ['copying'] = [[text]], + ['install'] = [[text]], + ['test.me'] = [[text]], + ['package.mask'] = [[text]], + ['.clang-tidy'] = [[yaml]], + ['readme.nss'] = [[text]], + ['rebar.config.lock'] = [[erlang]], + }, +} diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/data/plenary/filetypes/builtin.lua b/.config/nvim/pack/vendor/start/plenary.nvim/data/plenary/filetypes/builtin.lua new file mode 100644 index 0000000..6f341d6 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/data/plenary/filetypes/builtin.lua @@ -0,0 +1,73 @@ +local shebang_prefixes = { '/usr/bin/', '/bin/', '/usr/bin/env ', '/bin/env ' } +local shebang_fts = { + ['fish'] = 'fish', + ['perl'] = 'perl', + ['python'] = 'python', + ['python2'] = 'python', + ['python3'] = 'python', + ['bash'] = 'sh', + ['sh'] = 'sh', + ['zsh'] = 'zsh', +} + +local shebang = {} +for _, prefix in ipairs(shebang_prefixes) do + for k, v in pairs(shebang_fts) do + shebang[prefix .. k] = v + end +end + +return { + extension = { + ['_coffee'] = 'coffee', + ['astro'] = 'astro', + ['cairo'] = 'cairo', + ['cts'] = 'typescript', + ['cljd'] = 'clojure', + ['coffee'] = 'coffee', + ['dart'] = 'dart', + ['erb'] = 'eruby', + ['ex'] = 'elixir', + ['exs'] = 'elixir', + ['fish'] = 'fish', + ['fnl'] = 'fennel', + ['gd'] = 'gdscript', + ['gql'] = 'graphql', + ['gradle'] = 'groovy', + ['graphql'] = 'graphql', + ['hbs'] = 'handlebars', + ['hdbs'] = 'handlebars', + ['hlsl'] = 'hlsl', + ['jai'] = 'jai', + ['janet'] = 'janet', + ['jl'] = 'julia', + ['jsx'] = 'javascriptreact', + ['kt'] = 'kotlin', + ['mts'] = 'typescript', + ['nix'] = 'nix', + ['plist'] = 'xml', + ['purs'] = 'purescript', + ['r'] = 'r', + ['res'] = 'rescript', + ['resi'] = 'rescript', + ['rkt'] = 'racket', + ['svelte'] = 'svelte', + ['swift'] = 'swift', + ['tres'] = 'gdresource', + ['tscn'] = 'gdresource', + ['tsx'] = 'typescriptreact', + ['smithy'] = [[smithy]], + ['sol'] = 'solidity', + ['dtsi'] = 'dts', + }, + file_name = { + ['cakefile'] = 'coffee', + ['.babelrc'] = 'json', + ['.clangd'] = 'yaml', + ['.eslintrc'] = 'json', + ['.firebaserc'] = 'json', + ['.prettierrc'] = 'json', + ['.stylelintrc'] = 'json', + }, + shebang = shebang +} diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/doc/plenary-test.txt b/.config/nvim/pack/vendor/start/plenary.nvim/doc/plenary-test.txt new file mode 100644 index 0000000..d1c7373 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/doc/plenary-test.txt @@ -0,0 +1,110 @@ +*plenary-test* + + +Supports (simple) busted-style testing. It implements a mock-ed busted +interface, that will allow you to run simple busted style tests in separate +neovim instances. + + + +USAGE *plenary-test-usage* +============================================================================== + +To run the current spec file in a floating window, you can use the keymap +`PlenaryTestFile`. For example: +> + nmap t PlenaryTestFile +< +In this case, the test is run with a minimal configuration, that includes in +its runtimepath only plenary.nvim and the current working directory. + +To run a whole directory from the command line, you could do something like: +> + nvim --headless -c "PlenaryBustedDirectory tests/plenary/ { }" + +Where the first argument is the directory you'd like to test. It will search +for files with the pattern `*_spec.lua` and execute them in separate neovim +instances. + +The second argument is a Lua option table with the following fields: + + `nvim_cmd` specify the command to launch this neovim instance (defaults + to `vim.v.progpath`). + `init` specify an init.vim to use for this instance, if not given + a minimal configuration is used. + `minimal_init` as for `init`, but also run the neovim instance with + `--noplugin`. + `sequential` whether to run tests sequentially (default is to run in + parallel). + `keep_going` if `sequential`, whether to continue on test failure (default + true). + `timeout` controls the maximum time allotted to each job in parallel or + sequential operation (defaults to 50,000 milliseconds). + +Unless `init` is given, the neovim instance is run with the `--noplugin` +argument. + +The exit code is 0 for success and 1 for fail, so you can use it easily in +a `Makefile`. + + + +SUPPORTED BUSTED ITEMS *plenary-test-busted* +============================================================================== + +So far, the only supported busted items are: + +- `describe` +- `it` +- `pending` +- `before_each` +- `after_each` +- `clear` +- `assert.*` etc. (from luassert, which is bundled) + +We used to support `luaunit` and original `busted` but it turns out it was way +too hard and not worthwhile for the difficulty of getting them setup, +particularly on other platforms or in CI. Now, we have a dep free (or at +least, no other installation steps necessary) `busted` implementation that can +be used more easily. + +Please take a look at the new APIs and make any issues for things that aren't +clear. + + + +COMMANDS *plenary-test-commands* +============================================================================== + +*:PlenaryBustedFile* {path} + + Run a test on a single `_spec.lua` file. + + +*:PlenaryBustedDirectory* {path} {options} + + Run tests for all `*_spec.lua` files in the given path. + + {options} is a table, see |plenary-test-usage|. + + + + +PLUGS *plenary-test-plugs* +============================================================================== + +PlenaryTestFile + + Can be used to run a test on a single file, with a minimal configuration. + + + + +LICENSE *plenary-test-license* +============================================================================== + +MIT license + + +============================================================================== +vim:tw=78:ft=help:norl:et:ts=2:sw=2:fen:fdl=0: diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/array.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/array.lua new file mode 100644 index 0000000..f9cb655 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/array.lua @@ -0,0 +1,70 @@ +local assert = require('luassert.assert') +local say = require('say') + +-- Example usage: +-- local arr = { "one", "two", "three" } +-- +-- assert.array(arr).has.no.holes() -- checks the array to not contain holes --> passes +-- assert.array(arr).has.no.holes(4) -- sets explicit length to 4 --> fails +-- +-- local first_hole = assert.array(arr).has.holes(4) -- check array of size 4 to contain holes --> passes +-- assert.equal(4, first_hole) -- passes, as the index of the first hole is returned + + +-- Unique key to store the object we operate on in the state object +-- key must be unique, to make sure we do not have name collissions in the shared state object +local ARRAY_STATE_KEY = "__array_state" + +-- The modifier, to store the object in our state +local function array(state, args, level) + assert(args.n > 0, "No array provided to the array-modifier") + assert(rawget(state, ARRAY_STATE_KEY) == nil, "Array already set") + rawset(state, ARRAY_STATE_KEY, args[1]) + return state +end + +-- The actual assertion that operates on our object, stored via the modifier +local function holes(state, args, level) + local length = args[1] + local arr = rawget(state, ARRAY_STATE_KEY) -- retrieve previously set object + -- only check against nil, metatable types are allowed + assert(arr ~= nil, "No array set, please use the array modifier to set the array to validate") + if length == nil then + length = 0 + for i in pairs(arr) do + if type(i) == "number" and + i > length and + math.floor(i) == i then + length = i + end + end + end + assert(type(length) == "number", "expected array length to be of type 'number', got: "..tostring(length)) + -- let's do the actual assertion + local missing + for i = 1, length do + if arr[i] == nil then + missing = i + break + end + end + -- format arguments for output strings; + args[1] = missing + args.n = missing and 1 or 0 + return missing ~= nil, { missing } -- assert result + first missing index as return value +end + +-- Register the proper assertion messages +say:set("assertion.array_holes.positive", [[ +Expected array to have holes, but none was found. +]]) +say:set("assertion.array_holes.negative", [[ +Expected array to not have holes, hole found at position: %s +]]) + +-- Register the assertion, and the modifier +assert:register("assertion", "holes", holes, + "assertion.array_holes.positive", + "assertion.array_holes.negative") + +assert:register("modifier", "array", array) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/assert.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/assert.lua new file mode 100644 index 0000000..7fe7569 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/assert.lua @@ -0,0 +1,180 @@ +local s = require 'say' +local astate = require 'luassert.state' +local util = require 'luassert.util' +local unpack = util.unpack +local obj -- the returned module table +local level_mt = {} + +-- list of namespaces +local namespace = require 'luassert.namespaces' + +local function geterror(assertion_message, failure_message, args) + if util.hastostring(failure_message) then + failure_message = tostring(failure_message) + elseif failure_message ~= nil then + failure_message = astate.format_argument(failure_message) + end + local message = s(assertion_message, obj:format(args)) + if message and failure_message then + message = failure_message .. "\n" .. message + end + return message or failure_message +end + +local __state_meta = { + + __call = function(self, ...) + local keys = util.extract_keys("assertion", self.tokens) + + local assertion + + for _, key in ipairs(keys) do + assertion = namespace.assertion[key] or assertion + end + + if assertion then + for _, key in ipairs(keys) do + if namespace.modifier[key] then + namespace.modifier[key].callback(self) + end + end + + local arguments = util.make_arglist(...) + local val, retargs = assertion.callback(self, arguments, util.errorlevel()) + + if (not val) == self.mod then + local message = assertion.positive_message + if not self.mod then + message = assertion.negative_message + end + local err = geterror(message, rawget(self,"failure_message"), arguments) + error(err or "assertion failed!", util.errorlevel()) + end + + if retargs then + return unpack(retargs) + end + return ... + else + local arguments = util.make_arglist(...) + self.tokens = {} + + for _, key in ipairs(keys) do + if namespace.modifier[key] then + namespace.modifier[key].callback(self, arguments, util.errorlevel()) + end + end + end + + return self + end, + + __index = function(self, key) + for token in key:lower():gmatch('[^_]+') do + table.insert(self.tokens, token) + end + + return self + end +} + +obj = { + state = function() return setmetatable({mod=true, tokens={}}, __state_meta) end, + + -- registers a function in namespace + register = function(self, nspace, name, callback, positive_message, negative_message) + local lowername = name:lower() + if not namespace[nspace] then + namespace[nspace] = {} + end + namespace[nspace][lowername] = { + callback = callback, + name = lowername, + positive_message=positive_message, + negative_message=negative_message + } + end, + + -- unregisters a function in a namespace + unregister = function(self, nspace, name) + local lowername = name:lower() + if not namespace[nspace] then + namespace[nspace] = {} + end + namespace[nspace][lowername] = nil + end, + + -- registers a formatter + -- a formatter takes a single argument, and converts it to a string, or returns nil if it cannot format the argument + add_formatter = function(self, callback) + astate.add_formatter(callback) + end, + + -- unregisters a formatter + remove_formatter = function(self, fmtr) + astate.remove_formatter(fmtr) + end, + + format = function(self, args) + -- args.n specifies the number of arguments in case of 'trailing nil' arguments which get lost + local nofmt = args.nofmt or {} -- arguments in this list should not be formatted + local fmtargs = args.fmtargs or {} -- additional arguments to be passed to formatter + for i = 1, (args.n or #args) do -- cannot use pairs because table might have nils + if not nofmt[i] then + local val = args[i] + local valfmt = astate.format_argument(val, nil, fmtargs[i]) + if valfmt == nil then valfmt = tostring(val) end -- no formatter found + args[i] = valfmt + end + end + return args + end, + + set_parameter = function(self, name, value) + astate.set_parameter(name, value) + end, + + get_parameter = function(self, name) + return astate.get_parameter(name) + end, + + add_spy = function(self, spy) + astate.add_spy(spy) + end, + + snapshot = function(self) + return astate.snapshot() + end, + + level = function(self, level) + return setmetatable({ + level = level + }, level_mt) + end, + + -- returns the level if a level-value, otherwise nil + get_level = function(self, level) + if getmetatable(level) ~= level_mt then + return nil -- not a valid error-level + end + return level.level + end, +} + +local __meta = { + + __call = function(self, bool, message, level, ...) + if not bool then + local err_level = (self:get_level(level) or 1) + 1 + error(message or "assertion failed!", err_level) + end + return bool , message , level , ... + end, + + __index = function(self, key) + return rawget(self, key) or self.state()[key] + end, + +} + +return setmetatable(obj, __meta) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/assertions.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/assertions.lua new file mode 100644 index 0000000..e5083c3 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/assertions.lua @@ -0,0 +1,334 @@ +-- module will not return anything, only register assertions with the main assert engine + +-- assertions take 2 parameters; +-- 1) state +-- 2) arguments list. The list has a member 'n' with the argument count to check for trailing nils +-- 3) level The level of the error position relative to the called function +-- returns; boolean; whether assertion passed + +local assert = require('luassert.assert') +local astate = require ('luassert.state') +local util = require ('luassert.util') +local s = require('say') + +local function format(val) + return astate.format_argument(val) or tostring(val) +end + +local function set_failure_message(state, message) + if message ~= nil then + state.failure_message = message + end +end + +local function unique(state, arguments, level) + local list = arguments[1] + local deep + local argcnt = arguments.n + if type(arguments[2]) == "boolean" or (arguments[2] == nil and argcnt > 2) then + deep = arguments[2] + set_failure_message(state, arguments[3]) + else + if type(arguments[3]) == "boolean" then + deep = arguments[3] + end + set_failure_message(state, arguments[2]) + end + for k,v in pairs(list) do + for k2, v2 in pairs(list) do + if k ~= k2 then + if deep and util.deepcompare(v, v2, true) then + return false + else + if v == v2 then + return false + end + end + end + end + end + return true +end + +local function near(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + assert(argcnt > 2, s("assertion.internal.argtolittle", { "near", 3, tostring(argcnt) }), level) + local expected = tonumber(arguments[1]) + local actual = tonumber(arguments[2]) + local tolerance = tonumber(arguments[3]) + local numbertype = "number or object convertible to a number" + assert(expected, s("assertion.internal.badargtype", { 1, "near", numbertype, format(arguments[1]) }), level) + assert(actual, s("assertion.internal.badargtype", { 2, "near", numbertype, format(arguments[2]) }), level) + assert(tolerance, s("assertion.internal.badargtype", { 3, "near", numbertype, format(arguments[3]) }), level) + -- switch arguments for proper output message + util.tinsert(arguments, 1, util.tremove(arguments, 2)) + arguments[3] = tolerance + arguments.nofmt = arguments.nofmt or {} + arguments.nofmt[3] = true + set_failure_message(state, arguments[4]) + return (actual >= expected - tolerance and actual <= expected + tolerance) +end + +local function matches(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + assert(argcnt > 1, s("assertion.internal.argtolittle", { "matches", 2, tostring(argcnt) }), level) + local pattern = arguments[1] + local actual = nil + if util.hastostring(arguments[2]) or type(arguments[2]) == "number" then + actual = tostring(arguments[2]) + end + local err_message + local init_arg_num = 3 + for i=3,argcnt,1 do + if arguments[i] and type(arguments[i]) ~= "boolean" and not tonumber(arguments[i]) then + if i == 3 then init_arg_num = init_arg_num + 1 end + err_message = util.tremove(arguments, i) + break + end + end + local init = arguments[3] + local plain = arguments[4] + local stringtype = "string or object convertible to a string" + assert(type(pattern) == "string", s("assertion.internal.badargtype", { 1, "matches", "string", type(arguments[1]) }), level) + assert(actual, s("assertion.internal.badargtype", { 2, "matches", stringtype, format(arguments[2]) }), level) + assert(init == nil or tonumber(init), s("assertion.internal.badargtype", { init_arg_num, "matches", "number", type(arguments[3]) }), level) + -- switch arguments for proper output message + util.tinsert(arguments, 1, util.tremove(arguments, 2)) + set_failure_message(state, err_message) + local retargs + local ok + if plain then + ok = (actual:find(pattern, init, plain) ~= nil) + retargs = ok and { pattern } or {} + else + retargs = { actual:match(pattern, init) } + ok = (retargs[1] ~= nil) + end + return ok, retargs +end + +local function equals(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + assert(argcnt > 1, s("assertion.internal.argtolittle", { "equals", 2, tostring(argcnt) }), level) + local result = arguments[1] == arguments[2] + -- switch arguments for proper output message + util.tinsert(arguments, 1, util.tremove(arguments, 2)) + set_failure_message(state, arguments[3]) + return result +end + +local function same(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + assert(argcnt > 1, s("assertion.internal.argtolittle", { "same", 2, tostring(argcnt) }), level) + if type(arguments[1]) == 'table' and type(arguments[2]) == 'table' then + local result, crumbs = util.deepcompare(arguments[1], arguments[2], true) + -- switch arguments for proper output message + util.tinsert(arguments, 1, util.tremove(arguments, 2)) + arguments.fmtargs = arguments.fmtargs or {} + arguments.fmtargs[1] = { crumbs = crumbs } + arguments.fmtargs[2] = { crumbs = crumbs } + set_failure_message(state, arguments[3]) + return result + end + local result = arguments[1] == arguments[2] + -- switch arguments for proper output message + util.tinsert(arguments, 1, util.tremove(arguments, 2)) + set_failure_message(state, arguments[3]) + return result +end + +local function truthy(state, arguments, level) + local argcnt = arguments.n + assert(argcnt > 0, s("assertion.internal.argtolittle", { "truthy", 1, tostring(argcnt) }), level) + set_failure_message(state, arguments[2]) + return arguments[1] ~= false and arguments[1] ~= nil +end + +local function falsy(state, arguments, level) + local argcnt = arguments.n + assert(argcnt > 0, s("assertion.internal.argtolittle", { "falsy", 1, tostring(argcnt) }), level) + return not truthy(state, arguments, level) +end + +local function has_error(state, arguments, level) + local level = (level or 1) + 1 + local retargs = util.shallowcopy(arguments) + local func = arguments[1] + local err_expected = arguments[2] + local failure_message = arguments[3] + assert(util.callable(func), s("assertion.internal.badargtype", { 1, "error", "function or callable object", type(func) }), level) + local ok, err_actual = pcall(func) + if type(err_actual) == 'string' then + -- remove 'path/to/file:line: ' from string + err_actual = err_actual:gsub('^.-:%d+: ', '', 1) + end + retargs[1] = err_actual + arguments.nofmt = {} + arguments.n = 2 + arguments[1] = (ok and '(no error)' or err_actual) + arguments[2] = (err_expected == nil and '(error)' or err_expected) + arguments.nofmt[1] = ok + arguments.nofmt[2] = (err_expected == nil) + set_failure_message(state, failure_message) + + if ok or err_expected == nil then + return not ok, retargs + end + if type(err_expected) == 'string' then + -- err_actual must be (convertible to) a string + if util.hastostring(err_actual) then + err_actual = tostring(err_actual) + retargs[1] = err_actual + end + if type(err_actual) == 'string' then + return err_expected == err_actual, retargs + end + elseif type(err_expected) == 'number' then + if type(err_actual) == 'string' then + return tostring(err_expected) == tostring(tonumber(err_actual)), retargs + end + end + return same(state, {err_expected, err_actual, ["n"] = 2}), retargs +end + +local function error_matches(state, arguments, level) + local level = (level or 1) + 1 + local retargs = util.shallowcopy(arguments) + local argcnt = arguments.n + local func = arguments[1] + local pattern = arguments[2] + assert(argcnt > 1, s("assertion.internal.argtolittle", { "error_matches", 2, tostring(argcnt) }), level) + assert(util.callable(func), s("assertion.internal.badargtype", { 1, "error_matches", "function or callable object", type(func) }), level) + assert(pattern == nil or type(pattern) == "string", s("assertion.internal.badargtype", { 2, "error", "string", type(pattern) }), level) + + local failure_message + local init_arg_num = 3 + for i=3,argcnt,1 do + if arguments[i] and type(arguments[i]) ~= "boolean" and not tonumber(arguments[i]) then + if i == 3 then init_arg_num = init_arg_num + 1 end + failure_message = util.tremove(arguments, i) + break + end + end + local init = arguments[3] + local plain = arguments[4] + assert(init == nil or tonumber(init), s("assertion.internal.badargtype", { init_arg_num, "matches", "number", type(arguments[3]) }), level) + + local ok, err_actual = pcall(func) + if type(err_actual) == 'string' then + -- remove 'path/to/file:line: ' from string + err_actual = err_actual:gsub('^.-:%d+: ', '', 1) + end + retargs[1] = err_actual + arguments.nofmt = {} + arguments.n = 2 + arguments[1] = (ok and '(no error)' or err_actual) + arguments[2] = pattern + arguments.nofmt[1] = ok + arguments.nofmt[2] = false + set_failure_message(state, failure_message) + + if ok then return not ok, retargs end + if err_actual == nil and pattern == nil then + return true, {} + end + + -- err_actual must be (convertible to) a string + if util.hastostring(err_actual) or + type(err_actual) == "number" or + type(err_actual) == "boolean" then + err_actual = tostring(err_actual) + retargs[1] = err_actual + end + if type(err_actual) == 'string' then + local ok + local retargs_ok + if plain then + retargs_ok = { pattern } + ok = (err_actual:find(pattern, init, plain) ~= nil) + else + retargs_ok = { err_actual:match(pattern, init) } + ok = (retargs_ok[1] ~= nil) + end + if ok then retargs = retargs_ok end + return ok, retargs + end + + return false, retargs +end + +local function is_true(state, arguments, level) + util.tinsert(arguments, 2, true) + set_failure_message(state, arguments[3]) + return arguments[1] == arguments[2] +end + +local function is_false(state, arguments, level) + util.tinsert(arguments, 2, false) + set_failure_message(state, arguments[3]) + return arguments[1] == arguments[2] +end + +local function is_type(state, arguments, level, etype) + util.tinsert(arguments, 2, "type " .. etype) + arguments.nofmt = arguments.nofmt or {} + arguments.nofmt[2] = true + set_failure_message(state, arguments[3]) + return arguments.n > 1 and type(arguments[1]) == etype +end + +local function returned_arguments(state, arguments, level) + arguments[1] = tostring(arguments[1]) + arguments[2] = tostring(arguments.n - 1) + arguments.nofmt = arguments.nofmt or {} + arguments.nofmt[1] = true + arguments.nofmt[2] = true + if arguments.n < 2 then arguments.n = 2 end + return arguments[1] == arguments[2] +end + +local function set_message(state, arguments, level) + state.failure_message = arguments[1] +end + +local function is_boolean(state, arguments, level) return is_type(state, arguments, level, "boolean") end +local function is_number(state, arguments, level) return is_type(state, arguments, level, "number") end +local function is_string(state, arguments, level) return is_type(state, arguments, level, "string") end +local function is_table(state, arguments, level) return is_type(state, arguments, level, "table") end +local function is_nil(state, arguments, level) return is_type(state, arguments, level, "nil") end +local function is_userdata(state, arguments, level) return is_type(state, arguments, level, "userdata") end +local function is_function(state, arguments, level) return is_type(state, arguments, level, "function") end +local function is_thread(state, arguments, level) return is_type(state, arguments, level, "thread") end + +assert:register("modifier", "message", set_message) +assert:register("assertion", "true", is_true, "assertion.same.positive", "assertion.same.negative") +assert:register("assertion", "false", is_false, "assertion.same.positive", "assertion.same.negative") +assert:register("assertion", "boolean", is_boolean, "assertion.same.positive", "assertion.same.negative") +assert:register("assertion", "number", is_number, "assertion.same.positive", "assertion.same.negative") +assert:register("assertion", "string", is_string, "assertion.same.positive", "assertion.same.negative") +assert:register("assertion", "table", is_table, "assertion.same.positive", "assertion.same.negative") +assert:register("assertion", "nil", is_nil, "assertion.same.positive", "assertion.same.negative") +assert:register("assertion", "userdata", is_userdata, "assertion.same.positive", "assertion.same.negative") +assert:register("assertion", "function", is_function, "assertion.same.positive", "assertion.same.negative") +assert:register("assertion", "thread", is_thread, "assertion.same.positive", "assertion.same.negative") +assert:register("assertion", "returned_arguments", returned_arguments, "assertion.returned_arguments.positive", "assertion.returned_arguments.negative") + +assert:register("assertion", "same", same, "assertion.same.positive", "assertion.same.negative") +assert:register("assertion", "matches", matches, "assertion.matches.positive", "assertion.matches.negative") +assert:register("assertion", "match", matches, "assertion.matches.positive", "assertion.matches.negative") +assert:register("assertion", "near", near, "assertion.near.positive", "assertion.near.negative") +assert:register("assertion", "equals", equals, "assertion.equals.positive", "assertion.equals.negative") +assert:register("assertion", "equal", equals, "assertion.equals.positive", "assertion.equals.negative") +assert:register("assertion", "unique", unique, "assertion.unique.positive", "assertion.unique.negative") +assert:register("assertion", "error", has_error, "assertion.error.positive", "assertion.error.negative") +assert:register("assertion", "errors", has_error, "assertion.error.positive", "assertion.error.negative") +assert:register("assertion", "error_matches", error_matches, "assertion.error.positive", "assertion.error.negative") +assert:register("assertion", "error_match", error_matches, "assertion.error.positive", "assertion.error.negative") +assert:register("assertion", "matches_error", error_matches, "assertion.error.positive", "assertion.error.negative") +assert:register("assertion", "match_error", error_matches, "assertion.error.positive", "assertion.error.negative") +assert:register("assertion", "truthy", truthy, "assertion.truthy.positive", "assertion.truthy.negative") +assert:register("assertion", "falsy", falsy, "assertion.falsy.positive", "assertion.falsy.negative") diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/compatibility.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/compatibility.lua new file mode 100644 index 0000000..88290ad --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/compatibility.lua @@ -0,0 +1,9 @@ +-- no longer needed, only for backward compatibility +local unpack = require ("luassert.util").unpack + +return { + unpack = function(...) + print(debug.traceback("WARN: calling deprecated function 'luassert.compatibility.unpack' use 'luassert.util.unpack' instead")) + return unpack(...) + end +} diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/formatters/binarystring.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/formatters/binarystring.lua new file mode 100644 index 0000000..02c05ea --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/formatters/binarystring.lua @@ -0,0 +1,28 @@ +local format = function (str) + if type(str) ~= "string" then return nil end + local result = "Binary string length; " .. tostring(#str) .. " bytes\n" + local i = 1 + local hex = "" + local chr = "" + while i <= #str do + local byte = str:byte(i) + hex = string.format("%s%2x ", hex, byte) + if byte < 32 then byte = string.byte(".") end + chr = chr .. string.char(byte) + if math.floor(i/16) == i/16 or i == #str then + -- reached end of line + hex = hex .. string.rep(" ", 16 * 3 - #hex) + chr = chr .. string.rep(" ", 16 - #chr) + + result = result .. hex:sub(1, 8 * 3) .. " " .. hex:sub(8*3+1, -1) .. " " .. chr:sub(1,8) .. " " .. chr:sub(9,-1) .. "\n" + + hex = "" + chr = "" + end + i = i + 1 + end + return result +end + +return format + diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/formatters/init.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/formatters/init.lua new file mode 100644 index 0000000..0ff67c9 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/formatters/init.lua @@ -0,0 +1,255 @@ +-- module will not return anything, only register formatters with the main assert engine +local assert = require('luassert.assert') +local match = require('luassert.match') +local util = require('luassert.util') + +local isatty, colors do + local ok, term = pcall(require, 'term') + isatty = io.type(io.stdout) == 'file' and ok and term.isatty(io.stdout) + if not isatty then + local isWindows = package.config:sub(1,1) == '\\' + if isWindows and os.getenv("ANSICON") then + isatty = true + end + end + + colors = setmetatable({ + none = function(c) return c end + },{ __index = function(self, key) + return function(c) + for token in key:gmatch("[^%.]+") do + c = term.colors[token](c) + end + return c + end + end + }) +end + +local function fmt_string(arg) + if type(arg) == "string" then + return string.format("(string) '%s'", arg) + end +end + +-- A version of tostring which formats numbers more precisely. +local function tostr(arg) + if type(arg) ~= "number" then + return tostring(arg) + end + + if arg ~= arg then + return "NaN" + elseif arg == 1/0 then + return "Inf" + elseif arg == -1/0 then + return "-Inf" + end + + local str = string.format("%.20g", arg) + + if math.type and math.type(arg) == "float" and not str:find("[%.,]") then + -- Number is a float but looks like an integer. + -- Insert ".0" after first run of digits. + str = str:gsub("%d+", "%0.0", 1) + end + + return str +end + +local function fmt_number(arg) + if type(arg) == "number" then + return string.format("(number) %s", tostr(arg)) + end +end + +local function fmt_boolean(arg) + if type(arg) == "boolean" then + return string.format("(boolean) %s", tostring(arg)) + end +end + +local function fmt_nil(arg) + if type(arg) == "nil" then + return "(nil)" + end +end + +local type_priorities = { + number = 1, + boolean = 2, + string = 3, + table = 4, + ["function"] = 5, + userdata = 6, + thread = 7 +} + +local function is_in_array_part(key, length) + return type(key) == "number" and 1 <= key and key <= length and math.floor(key) == key +end + +local function get_sorted_keys(t) + local keys = {} + local nkeys = 0 + + for key in pairs(t) do + nkeys = nkeys + 1 + keys[nkeys] = key + end + + local length = #t + + local function key_comparator(key1, key2) + local type1, type2 = type(key1), type(key2) + local priority1 = is_in_array_part(key1, length) and 0 or type_priorities[type1] or 8 + local priority2 = is_in_array_part(key2, length) and 0 or type_priorities[type2] or 8 + + if priority1 == priority2 then + if type1 == "string" or type1 == "number" then + return key1 < key2 + elseif type1 == "boolean" then + return key1 -- put true before false + end + else + return priority1 < priority2 + end + end + + table.sort(keys, key_comparator) + return keys, nkeys +end + +local function fmt_table(arg, fmtargs) + if type(arg) ~= "table" then + return + end + + local tmax = assert:get_parameter("TableFormatLevel") + local showrec = assert:get_parameter("TableFormatShowRecursion") + local errchar = assert:get_parameter("TableErrorHighlightCharacter") or "" + local errcolor = assert:get_parameter("TableErrorHighlightColor") + local crumbs = fmtargs and fmtargs.crumbs or {} + local cache = {} + local type_desc + + if getmetatable(arg) == nil then + type_desc = "(" .. tostring(arg) .. ") " + elseif not pcall(setmetatable, arg, getmetatable(arg)) then + -- cannot set same metatable, so it is protected, skip id + type_desc = "(table) " + else + -- unprotected metatable, temporary remove the mt + local mt = getmetatable(arg) + setmetatable(arg, nil) + type_desc = "(" .. tostring(arg) .. ") " + setmetatable(arg, mt) + end + + local function ft(t, l, with_crumbs) + if showrec and cache[t] and cache[t] > 0 then + return "{ ... recursive }" + end + + if next(t) == nil then + return "{ }" + end + + if l > tmax and tmax >= 0 then + return "{ ... more }" + end + + local result = "{" + local keys, nkeys = get_sorted_keys(t) + + cache[t] = (cache[t] or 0) + 1 + local crumb = crumbs[#crumbs - l + 1] + + for i = 1, nkeys do + local k = keys[i] + local v = t[k] + local use_crumbs = with_crumbs and k == crumb + + if type(v) == "table" then + v = ft(v, l + 1, use_crumbs) + elseif type(v) == "string" then + v = "'"..v.."'" + end + + local ch = use_crumbs and errchar or "" + local indent = string.rep(" ",l * 2 - ch:len()) + local mark = (ch:len() == 0 and "" or colors[errcolor](ch)) + result = result .. string.format("\n%s%s[%s] = %s", indent, mark, tostr(k), tostr(v)) + end + + cache[t] = cache[t] - 1 + + return result .. " }" + end + + return type_desc .. ft(arg, 1, true) +end + +local function fmt_function(arg) + if type(arg) == "function" then + local debug_info = debug.getinfo(arg) + return string.format("%s @ line %s in %s", tostring(arg), tostring(debug_info.linedefined), tostring(debug_info.source)) + end +end + +local function fmt_userdata(arg) + if type(arg) == "userdata" then + return string.format("(userdata) '%s'", tostring(arg)) + end +end + +local function fmt_thread(arg) + if type(arg) == "thread" then + return string.format("(thread) '%s'", tostring(arg)) + end +end + +local function fmt_matcher(arg) + if not match.is_matcher(arg) then + return + end + local not_inverted = { + [true] = "is.", + [false] = "no.", + } + local args = {} + for idx = 1, arg.arguments.n do + table.insert(args, assert:format({ arg.arguments[idx], n = 1, })[1]) + end + return string.format("(matcher) %s%s(%s)", + not_inverted[arg.mod], + tostring(arg.name), + table.concat(args, ", ")) +end + +local function fmt_arglist(arglist) + if not util.is_arglist(arglist) then + return + end + local formatted_vals = {} + for idx = 1, arglist.n do + table.insert(formatted_vals, assert:format({ arglist[idx], n = 1, })[1]) + end + return "(values list) (" .. table.concat(formatted_vals, ", ") .. ")" +end + +assert:add_formatter(fmt_string) +assert:add_formatter(fmt_number) +assert:add_formatter(fmt_boolean) +assert:add_formatter(fmt_nil) +assert:add_formatter(fmt_table) +assert:add_formatter(fmt_function) +assert:add_formatter(fmt_userdata) +assert:add_formatter(fmt_thread) +assert:add_formatter(fmt_matcher) +assert:add_formatter(fmt_arglist) +-- Set default table display depth for table formatter +assert:set_parameter("TableFormatLevel", 3) +assert:set_parameter("TableFormatShowRecursion", false) +assert:set_parameter("TableErrorHighlightCharacter", "*") +assert:set_parameter("TableErrorHighlightColor", isatty and "red" or "none") diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/init.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/init.lua new file mode 100644 index 0000000..4ecf8b4 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/init.lua @@ -0,0 +1,17 @@ +local assert = require('luassert.assert') + +assert._COPYRIGHT = "Copyright (c) 2018 Olivine Labs, LLC." +assert._DESCRIPTION = "Extends Lua's built-in assertions to provide additional tests and the ability to create your own." +assert._VERSION = "Luassert 1.8.0" + +-- load basic asserts +require('luassert.assertions') +require('luassert.modifiers') +require('luassert.array') +require('luassert.matchers') +require('luassert.formatters') + +-- load default language +require('luassert.languages.en') + +return assert diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/languages/en.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/languages/en.lua new file mode 100644 index 0000000..0d84b6d --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/languages/en.lua @@ -0,0 +1,48 @@ +local s = require('say') + +s:set_namespace('en') + +s:set("assertion.same.positive", "Expected objects to be the same.\nPassed in:\n%s\nExpected:\n%s") +s:set("assertion.same.negative", "Expected objects to not be the same.\nPassed in:\n%s\nDid not expect:\n%s") + +s:set("assertion.equals.positive", "Expected objects to be equal.\nPassed in:\n%s\nExpected:\n%s") +s:set("assertion.equals.negative", "Expected objects to not be equal.\nPassed in:\n%s\nDid not expect:\n%s") + +s:set("assertion.near.positive", "Expected values to be near.\nPassed in:\n%s\nExpected:\n%s +/- %s") +s:set("assertion.near.negative", "Expected values to not be near.\nPassed in:\n%s\nDid not expect:\n%s +/- %s") + +s:set("assertion.matches.positive", "Expected strings to match.\nPassed in:\n%s\nExpected:\n%s") +s:set("assertion.matches.negative", "Expected strings not to match.\nPassed in:\n%s\nDid not expect:\n%s") + +s:set("assertion.unique.positive", "Expected object to be unique:\n%s") +s:set("assertion.unique.negative", "Expected object to not be unique:\n%s") + +s:set("assertion.error.positive", "Expected a different error.\nCaught:\n%s\nExpected:\n%s") +s:set("assertion.error.negative", "Expected no error, but caught:\n%s") + +s:set("assertion.truthy.positive", "Expected to be truthy, but value was:\n%s") +s:set("assertion.truthy.negative", "Expected to not be truthy, but value was:\n%s") + +s:set("assertion.falsy.positive", "Expected to be falsy, but value was:\n%s") +s:set("assertion.falsy.negative", "Expected to not be falsy, but value was:\n%s") + +s:set("assertion.called.positive", "Expected to be called %s time(s), but was called %s time(s)") +s:set("assertion.called.negative", "Expected not to be called exactly %s time(s), but it was.") + +s:set("assertion.called_at_least.positive", "Expected to be called at least %s time(s), but was called %s time(s)") +s:set("assertion.called_at_most.positive", "Expected to be called at most %s time(s), but was called %s time(s)") +s:set("assertion.called_more_than.positive", "Expected to be called more than %s time(s), but was called %s time(s)") +s:set("assertion.called_less_than.positive", "Expected to be called less than %s time(s), but was called %s time(s)") + +s:set("assertion.called_with.positive", "Function was never called with matching arguments.\nCalled with (last call if any):\n%s\nExpected:\n%s") +s:set("assertion.called_with.negative", "Function was called with matching arguments at least once.\nCalled with (last matching call):\n%s\nDid not expect:\n%s") + +s:set("assertion.returned_with.positive", "Function never returned matching arguments.\nReturned (last call if any):\n%s\nExpected:\n%s") +s:set("assertion.returned_with.negative", "Function returned matching arguments at least once.\nReturned (last matching call):\n%s\nDid not expect:\n%s") + +s:set("assertion.returned_arguments.positive", "Expected to be called with %s argument(s), but was called with %s") +s:set("assertion.returned_arguments.negative", "Expected not to be called with %s argument(s), but was called with %s") + +-- errors +s:set("assertion.internal.argtolittle", "the '%s' function requires a minimum of %s arguments, got: %s") +s:set("assertion.internal.badargtype", "bad argument #%s to '%s' (%s expected, got %s)") diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/match.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/match.lua new file mode 100644 index 0000000..671c82f --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/match.lua @@ -0,0 +1,79 @@ +local namespace = require 'luassert.namespaces' +local util = require 'luassert.util' + +local matcher_mt = { + __call = function(self, value) + return self.callback(value) == self.mod + end, +} + +local state_mt = { + __call = function(self, ...) + local keys = util.extract_keys("matcher", self.tokens) + self.tokens = {} + + local matcher + + for _, key in ipairs(keys) do + matcher = namespace.matcher[key] or matcher + end + + if matcher then + for _, key in ipairs(keys) do + if namespace.modifier[key] then + namespace.modifier[key].callback(self) + end + end + + local arguments = util.make_arglist(...) + local matches = matcher.callback(self, arguments, util.errorlevel()) + return setmetatable({ + name = matcher.name, + mod = self.mod, + callback = matches, + arguments = arguments, + }, matcher_mt) + else + local arguments = util.make_arglist(...) + + for _, key in ipairs(keys) do + if namespace.modifier[key] then + namespace.modifier[key].callback(self, arguments, util.errorlevel()) + end + end + end + + return self + end, + + __index = function(self, key) + for token in key:lower():gmatch('[^_]+') do + table.insert(self.tokens, token) + end + + return self + end +} + +local match = { + _ = setmetatable({mod=true, callback=function() return true end}, matcher_mt), + + state = function() return setmetatable({mod=true, tokens={}}, state_mt) end, + + is_matcher = function(object) + return type(object) == "table" and getmetatable(object) == matcher_mt + end, + + is_ref_matcher = function(object) + local ismatcher = (type(object) == "table" and getmetatable(object) == matcher_mt) + return ismatcher and object.name == "ref" + end, +} + +local mt = { + __index = function(self, key) + return rawget(self, key) or self.state()[key] + end, +} + +return setmetatable(match, mt) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/matchers/composite.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/matchers/composite.lua new file mode 100644 index 0000000..e4775e0 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/matchers/composite.lua @@ -0,0 +1,61 @@ +local assert = require('luassert.assert') +local match = require ('luassert.match') +local s = require('say') + +local function none(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + assert(argcnt > 0, s("assertion.internal.argtolittle", { "none", 1, tostring(argcnt) }), level) + for i = 1, argcnt do + assert(match.is_matcher(arguments[i]), s("assertion.internal.badargtype", { 1, "none", "matcher", type(arguments[i]) }), level) + end + + return function(value) + for _, matcher in ipairs(arguments) do + if matcher(value) then + return false + end + end + return true + end +end + +local function any(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + assert(argcnt > 0, s("assertion.internal.argtolittle", { "any", 1, tostring(argcnt) }), level) + for i = 1, argcnt do + assert(match.is_matcher(arguments[i]), s("assertion.internal.badargtype", { 1, "any", "matcher", type(arguments[i]) }), level) + end + + return function(value) + for _, matcher in ipairs(arguments) do + if matcher(value) then + return true + end + end + return false + end +end + +local function all(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + assert(argcnt > 0, s("assertion.internal.argtolittle", { "all", 1, tostring(argcnt) }), level) + for i = 1, argcnt do + assert(match.is_matcher(arguments[i]), s("assertion.internal.badargtype", { 1, "all", "matcher", type(arguments[i]) }), level) + end + + return function(value) + for _, matcher in ipairs(arguments) do + if not matcher(value) then + return false + end + end + return true + end +end + +assert:register("matcher", "none_of", none) +assert:register("matcher", "any_of", any) +assert:register("matcher", "all_of", all) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/matchers/core.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/matchers/core.lua new file mode 100644 index 0000000..4335baf --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/matchers/core.lua @@ -0,0 +1,173 @@ +-- module will return the list of matchers, and registers matchers with the main assert engine + +-- matchers take 1 parameters; +-- 1) state +-- 2) arguments list. The list has a member 'n' with the argument count to check for trailing nils +-- 3) level The level of the error position relative to the called function +-- returns; function (or callable object); a function that, given an argument, returns a boolean + +local assert = require('luassert.assert') +local astate = require('luassert.state') +local util = require('luassert.util') +local s = require('say') + +local function format(val) + return astate.format_argument(val) or tostring(val) +end + +local function unique(state, arguments, level) + local deep = arguments[1] + return function(value) + local list = value + for k,v in pairs(list) do + for k2, v2 in pairs(list) do + if k ~= k2 then + if deep and util.deepcompare(v, v2, true) then + return false + else + if v == v2 then + return false + end + end + end + end + end + return true + end +end + +local function near(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + assert(argcnt > 1, s("assertion.internal.argtolittle", { "near", 2, tostring(argcnt) }), level) + local expected = tonumber(arguments[1]) + local tolerance = tonumber(arguments[2]) + local numbertype = "number or object convertible to a number" + assert(expected, s("assertion.internal.badargtype", { 1, "near", numbertype, format(arguments[1]) }), level) + assert(tolerance, s("assertion.internal.badargtype", { 2, "near", numbertype, format(arguments[2]) }), level) + + return function(value) + local actual = tonumber(value) + if not actual then return false end + return (actual >= expected - tolerance and actual <= expected + tolerance) + end +end + +local function matches(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + assert(argcnt > 0, s("assertion.internal.argtolittle", { "matches", 1, tostring(argcnt) }), level) + local pattern = arguments[1] + local init = arguments[2] + local plain = arguments[3] + assert(type(pattern) == "string", s("assertion.internal.badargtype", { 1, "matches", "string", type(arguments[1]) }), level) + assert(init == nil or tonumber(init), s("assertion.internal.badargtype", { 2, "matches", "number", type(arguments[2]) }), level) + + return function(value) + local actualtype = type(value) + local actual = nil + if actualtype == "string" or actualtype == "number" or + actualtype == "table" and (getmetatable(value) or {}).__tostring then + actual = tostring(value) + end + if not actual then return false end + return (actual:find(pattern, init, plain) ~= nil) + end +end + +local function equals(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + assert(argcnt > 0, s("assertion.internal.argtolittle", { "equals", 1, tostring(argcnt) }), level) + return function(value) + return value == arguments[1] + end +end + +local function same(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + assert(argcnt > 0, s("assertion.internal.argtolittle", { "same", 1, tostring(argcnt) }), level) + return function(value) + if type(value) == 'table' and type(arguments[1]) == 'table' then + local result = util.deepcompare(value, arguments[1], true) + return result + end + return value == arguments[1] + end +end + +local function ref(state, arguments, level) + local level = (level or 1) + 1 + local argcnt = arguments.n + local argtype = type(arguments[1]) + local isobject = (argtype == "table" or argtype == "function" or argtype == "thread" or argtype == "userdata") + assert(argcnt > 0, s("assertion.internal.argtolittle", { "ref", 1, tostring(argcnt) }), level) + assert(isobject, s("assertion.internal.badargtype", { 1, "ref", "object", argtype }), level) + return function(value) + return value == arguments[1] + end +end + +local function is_true(state, arguments, level) + return function(value) + return value == true + end +end + +local function is_false(state, arguments, level) + return function(value) + return value == false + end +end + +local function truthy(state, arguments, level) + return function(value) + return value ~= false and value ~= nil + end +end + +local function falsy(state, arguments, level) + local is_truthy = truthy(state, arguments, level) + return function(value) + return not is_truthy(value) + end +end + +local function is_type(state, arguments, level, etype) + return function(value) + return type(value) == etype + end +end + +local function is_nil(state, arguments, level) return is_type(state, arguments, level, "nil") end +local function is_boolean(state, arguments, level) return is_type(state, arguments, level, "boolean") end +local function is_number(state, arguments, level) return is_type(state, arguments, level, "number") end +local function is_string(state, arguments, level) return is_type(state, arguments, level, "string") end +local function is_table(state, arguments, level) return is_type(state, arguments, level, "table") end +local function is_function(state, arguments, level) return is_type(state, arguments, level, "function") end +local function is_userdata(state, arguments, level) return is_type(state, arguments, level, "userdata") end +local function is_thread(state, arguments, level) return is_type(state, arguments, level, "thread") end + +assert:register("matcher", "true", is_true) +assert:register("matcher", "false", is_false) + +assert:register("matcher", "nil", is_nil) +assert:register("matcher", "boolean", is_boolean) +assert:register("matcher", "number", is_number) +assert:register("matcher", "string", is_string) +assert:register("matcher", "table", is_table) +assert:register("matcher", "function", is_function) +assert:register("matcher", "userdata", is_userdata) +assert:register("matcher", "thread", is_thread) + +assert:register("matcher", "ref", ref) +assert:register("matcher", "same", same) +assert:register("matcher", "matches", matches) +assert:register("matcher", "match", matches) +assert:register("matcher", "near", near) +assert:register("matcher", "equals", equals) +assert:register("matcher", "equal", equals) +assert:register("matcher", "unique", unique) +assert:register("matcher", "truthy", truthy) +assert:register("matcher", "falsy", falsy) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/matchers/init.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/matchers/init.lua new file mode 100644 index 0000000..c0ad62b --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/matchers/init.lua @@ -0,0 +1,3 @@ +-- load basic machers +require('luassert.matchers.core') +require('luassert.matchers.composite') diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/mock.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/mock.lua new file mode 100644 index 0000000..0a3bf3d --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/mock.lua @@ -0,0 +1,61 @@ +-- module will return a mock module table, and will not register any assertions +local spy = require 'luassert.spy' +local stub = require 'luassert.stub' + +local function mock_apply(object, action) + if type(object) ~= "table" then return end + if spy.is_spy(object) then + return object[action](object) + end + for k,v in pairs(object) do + mock_apply(v, action) + end + return object +end + +local mock +mock = { + new = function(object, dostub, func, self, key) + local visited = {} + local function do_mock(object, self, key) + local mock_handlers = { + ["table"] = function() + if spy.is_spy(object) or visited[object] then return end + visited[object] = true + for k,v in pairs(object) do + object[k] = do_mock(v, object, k) + end + return object + end, + ["function"] = function() + if dostub then + return stub(self, key, func) + elseif self==nil then + return spy.new(object) + else + return spy.on(self, key) + end + end + } + local handler = mock_handlers[type(object)] + return handler and handler() or object + end + return do_mock(object, self, key) + end, + + clear = function(object) + return mock_apply(object, "clear") + end, + + revert = function(object) + return mock_apply(object, "revert") + end +} + +return setmetatable(mock, { + __call = function(self, ...) + -- mock originally was a function only. Now that it is a module table + -- the __call method is required for backward compatibility + return mock.new(...) + end +}) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/modifiers.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/modifiers.lua new file mode 100644 index 0000000..9493228 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/modifiers.lua @@ -0,0 +1,19 @@ +-- module will not return anything, only register assertions/modifiers with the main assert engine +local assert = require('luassert.assert') + +local function is(state) + return state +end + +local function is_not(state) + state.mod = not state.mod + return state +end + +assert:register("modifier", "is", is) +assert:register("modifier", "are", is) +assert:register("modifier", "was", is) +assert:register("modifier", "has", is) +assert:register("modifier", "does", is) +assert:register("modifier", "not", is_not) +assert:register("modifier", "no", is_not) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/namespaces.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/namespaces.lua new file mode 100644 index 0000000..0790fce --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/namespaces.lua @@ -0,0 +1,2 @@ +-- stores the list of namespaces +return {} diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/spy.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/spy.lua new file mode 100644 index 0000000..eb7fc06 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/spy.lua @@ -0,0 +1,195 @@ +-- module will return spy table, and register its assertions with the main assert engine +local assert = require('luassert.assert') +local util = require('luassert.util') + +-- Spy metatable +local spy_mt = { + __call = function(self, ...) + local arguments = util.make_arglist(...) + table.insert(self.calls, util.copyargs(arguments)) + local function get_returns(...) + local returnvals = util.make_arglist(...) + table.insert(self.returnvals, util.copyargs(returnvals)) + return ... + end + return get_returns(self.callback(...)) + end +} + +local spy -- must make local before defining table, because table contents refers to the table (recursion) +spy = { + new = function(callback) + callback = callback or function() end + if not util.callable(callback) then + error("Cannot spy on type '" .. type(callback) .. "', only on functions or callable elements", util.errorlevel()) + end + local s = setmetatable({ + calls = {}, + returnvals = {}, + callback = callback, + + target_table = nil, -- these will be set when using 'spy.on' + target_key = nil, + + revert = function(self) + if not self.reverted then + if self.target_table and self.target_key then + self.target_table[self.target_key] = self.callback + end + self.reverted = true + end + return self.callback + end, + + clear = function(self) + self.calls = {} + self.returnvals = {} + return self + end, + + called = function(self, times, compare) + if times or compare then + local compare = compare or function(count, expected) return count == expected end + return compare(#self.calls, times), #self.calls + end + + return (#self.calls > 0), #self.calls + end, + + called_with = function(self, args) + local last_arglist = nil + if #self.calls > 0 then + last_arglist = self.calls[#self.calls].vals + end + local matching_arglists = util.matchargs(self.calls, args) + if matching_arglists ~= nil then + return true, matching_arglists.vals + end + return false, last_arglist + end, + + returned_with = function(self, args) + local last_returnvallist = nil + if #self.returnvals > 0 then + last_returnvallist = self.returnvals[#self.returnvals].vals + end + local matching_returnvallists = util.matchargs(self.returnvals, args) + if matching_returnvallists ~= nil then + return true, matching_returnvallists.vals + end + return false, last_returnvallist + end + }, spy_mt) + assert:add_spy(s) -- register with the current state + return s + end, + + is_spy = function(object) + return type(object) == "table" and getmetatable(object) == spy_mt + end, + + on = function(target_table, target_key) + local s = spy.new(target_table[target_key]) + target_table[target_key] = s + -- store original data + s.target_table = target_table + s.target_key = target_key + + return s + end +} + +local function set_spy(state, arguments, level) + state.payload = arguments[1] + if arguments[2] ~= nil then + state.failure_message = arguments[2] + end +end + +local function returned_with(state, arguments, level) + local level = (level or 1) + 1 + local payload = rawget(state, "payload") + if payload and payload.returned_with then + local assertion_holds, matching_or_last_returnvallist = state.payload:returned_with(arguments) + local expected_returnvallist = util.shallowcopy(arguments) + util.cleararglist(arguments) + util.tinsert(arguments, 1, matching_or_last_returnvallist) + util.tinsert(arguments, 2, expected_returnvallist) + return assertion_holds + else + error("'returned_with' must be chained after 'spy(aspy)'", level) + end +end + +local function called_with(state, arguments, level) + local level = (level or 1) + 1 + local payload = rawget(state, "payload") + if payload and payload.called_with then + local assertion_holds, matching_or_last_arglist = state.payload:called_with(arguments) + local expected_arglist = util.shallowcopy(arguments) + util.cleararglist(arguments) + util.tinsert(arguments, 1, matching_or_last_arglist) + util.tinsert(arguments, 2, expected_arglist) + return assertion_holds + else + error("'called_with' must be chained after 'spy(aspy)'", level) + end +end + +local function called(state, arguments, level, compare) + local level = (level or 1) + 1 + local num_times = arguments[1] + if not num_times and not state.mod then + state.mod = true + num_times = 0 + end + local payload = rawget(state, "payload") + if payload and type(payload) == "table" and payload.called then + local result, count = state.payload:called(num_times, compare) + arguments[1] = tostring(num_times or ">0") + util.tinsert(arguments, 2, tostring(count)) + arguments.nofmt = arguments.nofmt or {} + arguments.nofmt[1] = true + arguments.nofmt[2] = true + return result + elseif payload and type(payload) == "function" then + error("When calling 'spy(aspy)', 'aspy' must not be the original function, but the spy function replacing the original", level) + else + error("'called' must be chained after 'spy(aspy)'", level) + end +end + +local function called_at_least(state, arguments, level) + local level = (level or 1) + 1 + return called(state, arguments, level, function(count, expected) return count >= expected end) +end + +local function called_at_most(state, arguments, level) + local level = (level or 1) + 1 + return called(state, arguments, level, function(count, expected) return count <= expected end) +end + +local function called_more_than(state, arguments, level) + local level = (level or 1) + 1 + return called(state, arguments, level, function(count, expected) return count > expected end) +end + +local function called_less_than(state, arguments, level) + local level = (level or 1) + 1 + return called(state, arguments, level, function(count, expected) return count < expected end) +end + +assert:register("modifier", "spy", set_spy) +assert:register("assertion", "returned_with", returned_with, "assertion.returned_with.positive", "assertion.returned_with.negative") +assert:register("assertion", "called_with", called_with, "assertion.called_with.positive", "assertion.called_with.negative") +assert:register("assertion", "called", called, "assertion.called.positive", "assertion.called.negative") +assert:register("assertion", "called_at_least", called_at_least, "assertion.called_at_least.positive", "assertion.called_less_than.positive") +assert:register("assertion", "called_at_most", called_at_most, "assertion.called_at_most.positive", "assertion.called_more_than.positive") +assert:register("assertion", "called_more_than", called_more_than, "assertion.called_more_than.positive", "assertion.called_at_most.positive") +assert:register("assertion", "called_less_than", called_less_than, "assertion.called_less_than.positive", "assertion.called_at_least.positive") + +return setmetatable(spy, { + __call = function(self, ...) + return spy.new(...) + end +}) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/state.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/state.lua new file mode 100644 index 0000000..6de0efe --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/state.lua @@ -0,0 +1,127 @@ +-- maintains a state of the assert engine in a linked-list fashion +-- records; formatters, parameters, spies and stubs + +local state_mt = { + __call = function(self) + self:revert() + end +} + +local spies_mt = { __mode = "kv" } + +local nilvalue = {} -- unique ID to refer to nil values for parameters + +-- will hold the current state +local current + +-- exported module table +local state = {} + +------------------------------------------------------ +-- Reverts to a (specific) snapshot. +-- @param self (optional) the snapshot to revert to. If not provided, it will revert to the last snapshot. +state.revert = function(self) + if not self then + -- no snapshot given, so move 1 up + self = current + if not self.previous then + -- top of list, no previous one, nothing to do + return + end + end + if getmetatable(self) ~= state_mt then error("Value provided is not a valid snapshot", 2) end + + if self.next then + self.next:revert() + end + -- revert formatters in 'last' + self.formatters = {} + -- revert parameters in 'last' + self.parameters = {} + -- revert spies/stubs in 'last' + for s,_ in pairs(self.spies) do + self.spies[s] = nil + s:revert() + end + setmetatable(self, nil) -- invalidate as a snapshot + current = self.previous + current.next = nil +end + +------------------------------------------------------ +-- Creates a new snapshot. +-- @return snapshot table +state.snapshot = function() + local new = setmetatable ({ + formatters = {}, + parameters = {}, + spies = setmetatable({}, spies_mt), + previous = current, + revert = state.revert, + }, state_mt) + if current then current.next = new end + current = new + return current +end + + +-- FORMATTERS +state.add_formatter = function(callback) + table.insert(current.formatters, 1, callback) +end + +state.remove_formatter = function(callback, s) + s = s or current + for i, v in ipairs(s.formatters) do + if v == callback then + table.remove(s.formatters, i) + break + end + end + -- wasn't found, so traverse up 1 state + if s.previous then + state.remove_formatter(callback, s.previous) + end +end + +state.format_argument = function(val, s, fmtargs) + s = s or current + for _, fmt in ipairs(s.formatters) do + local valfmt = fmt(val, fmtargs) + if valfmt ~= nil then return valfmt end + end + -- nothing found, check snapshot 1 up in list + if s.previous then + return state.format_argument(val, s.previous, fmtargs) + end + return nil -- end of list, couldn't format +end + + +-- PARAMETERS +state.set_parameter = function(name, value) + if value == nil then value = nilvalue end + current.parameters[name] = value +end + +state.get_parameter = function(name, s) + s = s or current + local val = s.parameters[name] + if val == nil and s.previous then + -- not found, so check 1 up in list + return state.get_parameter(name, s.previous) + end + if val ~= nilvalue then + return val + end + return nil +end + +-- SPIES / STUBS +state.add_spy = function(spy) + current.spies[spy] = true +end + +state.snapshot() -- create initial state + +return state diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/stub.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/stub.lua new file mode 100644 index 0000000..91ae6e0 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/stub.lua @@ -0,0 +1,107 @@ +-- module will return a stub module table +local assert = require 'luassert.assert' +local spy = require 'luassert.spy' +local util = require 'luassert.util' +local unpack = util.unpack +local pack = util.pack + +local stub = {} + +function stub.new(object, key, ...) + if object == nil and key == nil then + -- called without arguments, create a 'blank' stub + object = {} + key = "" + end + local return_values = pack(...) + assert(type(object) == "table" and key ~= nil, "stub.new(): Can only create stub on a table key, call with 2 params; table, key", util.errorlevel()) + assert(object[key] == nil or util.callable(object[key]), "stub.new(): The element for which to create a stub must either be callable, or be nil", util.errorlevel()) + local old_elem = object[key] -- keep existing element (might be nil!) + + local fn = (return_values.n == 1 and util.callable(return_values[1]) and return_values[1]) + local defaultfunc = fn or function() + return unpack(return_values) + end + local oncalls = {} + local callbacks = {} + local stubfunc = function(...) + local args = util.make_arglist(...) + local match = util.matchoncalls(oncalls, args) + if match then + return callbacks[match](...) + end + return defaultfunc(...) + end + + object[key] = stubfunc -- set the stubfunction + local s = spy.on(object, key) -- create a spy on top of the stub function + local spy_revert = s.revert -- keep created revert function + + s.revert = function(self) -- wrap revert function to restore original element + if not self.reverted then + spy_revert(self) + object[key] = old_elem + self.reverted = true + end + return old_elem + end + + s.returns = function(...) + local return_args = pack(...) + defaultfunc = function() + return unpack(return_args) + end + return s + end + + s.invokes = function(func) + defaultfunc = function(...) + return func(...) + end + return s + end + + s.by_default = { + returns = s.returns, + invokes = s.invokes, + } + + s.on_call_with = function(...) + local match_args = util.make_arglist(...) + match_args = util.copyargs(match_args) + return { + returns = function(...) + local return_args = pack(...) + table.insert(oncalls, match_args) + callbacks[match_args] = function() + return unpack(return_args) + end + return s + end, + invokes = function(func) + table.insert(oncalls, match_args) + callbacks[match_args] = function(...) + return func(...) + end + return s + end + } + end + + return s +end + +local function set_stub(state, arguments) + state.payload = arguments[1] + state.failure_message = arguments[2] +end + +assert:register("modifier", "stub", set_stub) + +return setmetatable(stub, { + __call = function(self, ...) + -- stub originally was a function only. Now that it is a module table + -- the __call method is required for backward compatibility + return stub.new(...) + end +}) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/util.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/util.lua new file mode 100644 index 0000000..da2f247 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/luassert/util.lua @@ -0,0 +1,362 @@ +local util = {} +local arglist_mt = {} + +-- have pack/unpack both respect the 'n' field +local _unpack = table.unpack or unpack +local unpack = function(t, i, j) return _unpack(t, i or 1, j or t.n or #t) end +local pack = function(...) return { n = select("#", ...), ... } end +util.pack = pack +util.unpack = unpack + + +function util.deepcompare(t1,t2,ignore_mt,cycles,thresh1,thresh2) + local ty1 = type(t1) + local ty2 = type(t2) + -- non-table types can be directly compared + if ty1 ~= 'table' or ty2 ~= 'table' then return t1 == t2 end + local mt1 = debug.getmetatable(t1) + local mt2 = debug.getmetatable(t2) + -- would equality be determined by metatable __eq? + if mt1 and mt1 == mt2 and mt1.__eq then + -- then use that unless asked not to + if not ignore_mt then return t1 == t2 end + else -- we can skip the deep comparison below if t1 and t2 share identity + if rawequal(t1, t2) then return true end + end + + -- handle recursive tables + cycles = cycles or {{},{}} + thresh1, thresh2 = (thresh1 or 1), (thresh2 or 1) + cycles[1][t1] = (cycles[1][t1] or 0) + cycles[2][t2] = (cycles[2][t2] or 0) + if cycles[1][t1] == 1 or cycles[2][t2] == 1 then + thresh1 = cycles[1][t1] + 1 + thresh2 = cycles[2][t2] + 1 + end + if cycles[1][t1] > thresh1 and cycles[2][t2] > thresh2 then + return true + end + + cycles[1][t1] = cycles[1][t1] + 1 + cycles[2][t2] = cycles[2][t2] + 1 + + for k1,v1 in next, t1 do + local v2 = t2[k1] + if v2 == nil then + return false, {k1} + end + + local same, crumbs = util.deepcompare(v1,v2,nil,cycles,thresh1,thresh2) + if not same then + crumbs = crumbs or {} + table.insert(crumbs, k1) + return false, crumbs + end + end + for k2,_ in next, t2 do + -- only check whether each element has a t1 counterpart, actual comparison + -- has been done in first loop above + if t1[k2] == nil then return false, {k2} end + end + + cycles[1][t1] = cycles[1][t1] - 1 + cycles[2][t2] = cycles[2][t2] - 1 + + return true +end + +function util.shallowcopy(t) + if type(t) ~= "table" then return t end + local copy = {} + setmetatable(copy, getmetatable(t)) + for k,v in next, t do + copy[k] = v + end + return copy +end + +function util.deepcopy(t, deepmt, cache) + local spy = require 'luassert.spy' + if type(t) ~= "table" then return t end + local copy = {} + + -- handle recursive tables + local cache = cache or {} + if cache[t] then return cache[t] end + cache[t] = copy + + for k,v in next, t do + copy[k] = (spy.is_spy(v) and v or util.deepcopy(v, deepmt, cache)) + end + if deepmt then + debug.setmetatable(copy, util.deepcopy(debug.getmetatable(t), false, cache)) + else + debug.setmetatable(copy, debug.getmetatable(t)) + end + return copy +end + +----------------------------------------------- +-- Copies arguments as a list of arguments +-- @param args the arguments of which to copy +-- @return the copy of the arguments +function util.copyargs(args) + local copy = {} + setmetatable(copy, getmetatable(args)) + local match = require 'luassert.match' + local spy = require 'luassert.spy' + for k,v in pairs(args) do + copy[k] = ((match.is_matcher(v) or spy.is_spy(v)) and v or util.deepcopy(v)) + end + return { vals = copy, refs = util.shallowcopy(args) } +end + +----------------------------------------------- +-- Clear an arguments or return values list from a table +-- @param arglist the table to clear of arguments or return values and their count +-- @return No return values +function util.cleararglist(arglist) + for idx = arglist.n, 1, -1 do + util.tremove(arglist, idx) + end + arglist.n = nil +end + +----------------------------------------------- +-- Test specs against an arglist in deepcopy and refs flavours. +-- @param args deepcopy arglist +-- @param argsrefs refs arglist +-- @param specs arguments/return values to match against args/argsrefs +-- @return true if specs match args/argsrefs, false otherwise +local function matcharg(args, argrefs, specs) + local match = require 'luassert.match' + for idx, argval in pairs(args) do + local spec = specs[idx] + if match.is_matcher(spec) then + if match.is_ref_matcher(spec) then + argval = argrefs[idx] + end + if not spec(argval) then + return false + end + elseif (spec == nil or not util.deepcompare(argval, spec)) then + return false + end + end + + for idx, spec in pairs(specs) do + -- only check whether each element has an args counterpart, + -- actual comparison has been done in first loop above + local argval = args[idx] + if argval == nil then + -- no args counterpart, so try to compare using matcher + if match.is_matcher(spec) then + if not spec(argval) then + return false + end + else + return false + end + end + end + return true +end + +----------------------------------------------- +-- Find matching arguments/return values in a saved list of +-- arguments/returned values. +-- @param invocations_list list of arguments/returned values to search (list of lists) +-- @param specs arguments/return values to match against argslist +-- @return the last matching arguments/returned values if a match is found, otherwise nil +function util.matchargs(invocations_list, specs) + -- Search the arguments/returned values last to first to give the + -- most helpful answer possible. In the cases where you can place + -- your assertions between calls to check this gives you the best + -- information if no calls match. In the cases where you can't do + -- that there is no good way to predict what would work best. + assert(not util.is_arglist(invocations_list), "expected a list of arglist-object, got an arglist") + for ii = #invocations_list, 1, -1 do + local val = invocations_list[ii] + if matcharg(val.vals, val.refs, specs) then + return val + end + end + return nil +end + +----------------------------------------------- +-- Find matching oncall for an actual call. +-- @param oncalls list of oncalls to search +-- @param args actual call argslist to match against +-- @return the first matching oncall if a match is found, otherwise nil +function util.matchoncalls(oncalls, args) + for _, callspecs in ipairs(oncalls) do + -- This lookup is done immediately on *args* passing into the stub + -- so pass *args* as both *args* and *argsref* without copying + -- either. + if matcharg(args, args, callspecs.vals) then + return callspecs + end + end + return nil +end + +----------------------------------------------- +-- table.insert() replacement that respects nil values. +-- The function will use table field 'n' as indicator of the +-- table length, if not set, it will be added. +-- @param t table into which to insert +-- @param pos (optional) position in table where to insert. NOTE: not optional if you want to insert a nil-value! +-- @param val value to insert +-- @return No return values +function util.tinsert(...) + -- check optional POS value + local args = {...} + local c = select('#',...) + local t = args[1] + local pos = args[2] + local val = args[3] + if c < 3 then + val = pos + pos = nil + end + -- set length indicator n if not present (+1) + t.n = (t.n or #t) + 1 + if not pos then + pos = t.n + elseif pos > t.n then + -- out of our range + t[pos] = val + t.n = pos + end + -- shift everything up 1 pos + for i = t.n, pos + 1, -1 do + t[i]=t[i-1] + end + -- add element to be inserted + t[pos] = val +end +----------------------------------------------- +-- table.remove() replacement that respects nil values. +-- The function will use table field 'n' as indicator of the +-- table length, if not set, it will be added. +-- @param t table from which to remove +-- @param pos (optional) position in table to remove +-- @return No return values +function util.tremove(t, pos) + -- set length indicator n if not present (+1) + t.n = t.n or #t + if not pos then + pos = t.n + elseif pos > t.n then + local removed = t[pos] + -- out of our range + t[pos] = nil + return removed + end + local removed = t[pos] + -- shift everything up 1 pos + for i = pos, t.n do + t[i]=t[i+1] + end + -- set size, clean last + t[t.n] = nil + t.n = t.n - 1 + return removed +end + +----------------------------------------------- +-- Checks an element to be callable. +-- The type must either be a function or have a metatable +-- containing an '__call' function. +-- @param object element to inspect on being callable or not +-- @return boolean, true if the object is callable +function util.callable(object) + return type(object) == "function" or type((debug.getmetatable(object) or {}).__call) == "function" +end + +----------------------------------------------- +-- Checks an element has tostring. +-- The type must either be a string or have a metatable +-- containing an '__tostring' function. +-- @param object element to inspect on having tostring or not +-- @return boolean, true if the object has tostring +function util.hastostring(object) + return type(object) == "string" or type((debug.getmetatable(object) or {}).__tostring) == "function" +end + +----------------------------------------------- +-- Find the first level, not defined in the same file as the caller's +-- code file to properly report an error. +-- @param level the level to use as the caller's source file +-- @return number, the level of which to report an error +function util.errorlevel(level) + local level = (level or 1) + 1 -- add one to get level of the caller + local info = debug.getinfo(level) + local source = (info or {}).source + local file = source + while file and (file == source or source == "=(tail call)") do + level = level + 1 + info = debug.getinfo(level) + source = (info or {}).source + end + if level > 1 then level = level - 1 end -- deduct call to errorlevel() itself + return level +end + +----------------------------------------------- +-- Extract modifier and namespace keys from list of tokens. +-- @param nspace the namespace from which to match tokens +-- @param tokens list of tokens to search for keys +-- @return table, list of keys that were extracted +function util.extract_keys(nspace, tokens) + local namespace = require 'luassert.namespaces' + + -- find valid keys by coalescing tokens as needed, starting from the end + local keys = {} + local key = nil + local i = #tokens + while i > 0 do + local token = tokens[i] + key = key and (token .. '_' .. key) or token + + -- find longest matching key in the given namespace + local longkey = i > 1 and (tokens[i-1] .. '_' .. key) or nil + while i > 1 and longkey and namespace[nspace][longkey] do + key = longkey + i = i - 1 + token = tokens[i] + longkey = (token .. '_' .. key) + end + + if namespace.modifier[key] or namespace[nspace][key] then + table.insert(keys, 1, key) + key = nil + end + i = i - 1 + end + + -- if there's anything left we didn't recognize it + if key then + error("luassert: unknown modifier/" .. nspace .. ": '" .. key .."'", util.errorlevel(2)) + end + + return keys +end + +----------------------------------------------- +-- store argument list for return values of a function in a table. +-- The table will get a metatable to identify it as an arglist +function util.make_arglist(...) + local arglist = { ... } + arglist.n = select('#', ...) -- add values count for trailing nils + return setmetatable(arglist, arglist_mt) +end + +----------------------------------------------- +-- check a table to be an arglist type. +function util.is_arglist(object) + return getmetatable(object) == arglist_mt +end + +return util diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/_meta/_luassert.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/_meta/_luassert.lua new file mode 100644 index 0000000..c589970 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/_meta/_luassert.lua @@ -0,0 +1,289 @@ +---@meta +---This file is autogenerated, DO NOT EDIT +error "Cannot require a meta file" + +---@generic T:any +---@alias LuassertFunction fun(value:T, message?:string):T +---@alias LuassertFunctionTwoArgs fun(expected:T, actual:T, message?:string):T +---@alias LuassertFunctionMultiArgs fun(...:T):T + +---@class Luassert +---@field are_boolean LuassertFunction +---@field are_equal LuassertFunctionTwoArgs +---@field are_equals LuassertFunctionTwoArgs +---@field are_error LuassertFunction +---@field are_error_match LuassertFunctionTwoArgs +---@field are_error_matches LuassertFunctionTwoArgs +---@field are_errors LuassertFunction +---@field are_false LuassertFunction +---@field are_falsy LuassertFunction +---@field are_function LuassertFunction +---@field are_holes LuassertFunction +---@field are_match LuassertFunctionTwoArgs +---@field are_match_error LuassertFunctionTwoArgs +---@field are_matches LuassertFunctionTwoArgs +---@field are_matches_error LuassertFunctionTwoArgs +---@field are_near LuassertFunctionMultiArgs +---@field are_nil LuassertFunction +---@field are_number LuassertFunction +---@field are_returned_arguments LuassertFunction +---@field are_same LuassertFunctionTwoArgs +---@field are_string LuassertFunction +---@field are_table LuassertFunction +---@field are_thread LuassertFunction +---@field are_true LuassertFunction +---@field are_truthy LuassertFunction +---@field are_unique LuassertFunction +---@field are_userdata LuassertFunction +---@field array_boolean LuassertFunction +---@field array_equal LuassertFunctionTwoArgs +---@field array_equals LuassertFunctionTwoArgs +---@field array_error LuassertFunction +---@field array_error_match LuassertFunctionTwoArgs +---@field array_error_matches LuassertFunctionTwoArgs +---@field array_errors LuassertFunction +---@field array_false LuassertFunction +---@field array_falsy LuassertFunction +---@field array_function LuassertFunction +---@field array_holes LuassertFunction +---@field array_match LuassertFunctionTwoArgs +---@field array_match_error LuassertFunctionTwoArgs +---@field array_matches LuassertFunctionTwoArgs +---@field array_matches_error LuassertFunctionTwoArgs +---@field array_near LuassertFunctionMultiArgs +---@field array_nil LuassertFunction +---@field array_number LuassertFunction +---@field array_returned_arguments LuassertFunction +---@field array_same LuassertFunctionTwoArgs +---@field array_string LuassertFunction +---@field array_table LuassertFunction +---@field array_thread LuassertFunction +---@field array_true LuassertFunction +---@field array_truthy LuassertFunction +---@field array_unique LuassertFunction +---@field array_userdata LuassertFunction +---@field does_boolean LuassertFunction +---@field does_equal LuassertFunctionTwoArgs +---@field does_equals LuassertFunctionTwoArgs +---@field does_error LuassertFunction +---@field does_error_match LuassertFunctionTwoArgs +---@field does_error_matches LuassertFunctionTwoArgs +---@field does_errors LuassertFunction +---@field does_false LuassertFunction +---@field does_falsy LuassertFunction +---@field does_function LuassertFunction +---@field does_holes LuassertFunction +---@field does_match LuassertFunctionTwoArgs +---@field does_match_error LuassertFunctionTwoArgs +---@field does_matches LuassertFunctionTwoArgs +---@field does_matches_error LuassertFunctionTwoArgs +---@field does_near LuassertFunctionMultiArgs +---@field does_nil LuassertFunction +---@field does_number LuassertFunction +---@field does_returned_arguments LuassertFunction +---@field does_same LuassertFunctionTwoArgs +---@field does_string LuassertFunction +---@field does_table LuassertFunction +---@field does_thread LuassertFunction +---@field does_true LuassertFunction +---@field does_truthy LuassertFunction +---@field does_unique LuassertFunction +---@field does_userdata LuassertFunction +---@field has_boolean LuassertFunction +---@field has_equal LuassertFunctionTwoArgs +---@field has_equals LuassertFunctionTwoArgs +---@field has_error LuassertFunction +---@field has_error_match LuassertFunctionTwoArgs +---@field has_error_matches LuassertFunctionTwoArgs +---@field has_errors LuassertFunction +---@field has_false LuassertFunction +---@field has_falsy LuassertFunction +---@field has_function LuassertFunction +---@field has_holes LuassertFunction +---@field has_match LuassertFunctionTwoArgs +---@field has_match_error LuassertFunctionTwoArgs +---@field has_matches LuassertFunctionTwoArgs +---@field has_matches_error LuassertFunctionTwoArgs +---@field has_near LuassertFunctionMultiArgs +---@field has_nil LuassertFunction +---@field has_number LuassertFunction +---@field has_returned_arguments LuassertFunction +---@field has_same LuassertFunctionTwoArgs +---@field has_string LuassertFunction +---@field has_table LuassertFunction +---@field has_thread LuassertFunction +---@field has_true LuassertFunction +---@field has_truthy LuassertFunction +---@field has_unique LuassertFunction +---@field has_userdata LuassertFunction +---@field is_boolean LuassertFunction +---@field is_equal LuassertFunctionTwoArgs +---@field is_equals LuassertFunctionTwoArgs +---@field is_error LuassertFunction +---@field is_error_match LuassertFunctionTwoArgs +---@field is_error_matches LuassertFunctionTwoArgs +---@field is_errors LuassertFunction +---@field is_false LuassertFunction +---@field is_falsy LuassertFunction +---@field is_function LuassertFunction +---@field is_holes LuassertFunction +---@field is_match LuassertFunctionTwoArgs +---@field is_match_error LuassertFunctionTwoArgs +---@field is_matches LuassertFunctionTwoArgs +---@field is_matches_error LuassertFunctionTwoArgs +---@field is_near LuassertFunctionMultiArgs +---@field is_nil LuassertFunction +---@field is_number LuassertFunction +---@field is_returned_arguments LuassertFunction +---@field is_same LuassertFunctionTwoArgs +---@field is_string LuassertFunction +---@field is_table LuassertFunction +---@field is_thread LuassertFunction +---@field is_true LuassertFunction +---@field is_truthy LuassertFunction +---@field is_unique LuassertFunction +---@field is_userdata LuassertFunction +---@field message_boolean LuassertFunction +---@field message_equal LuassertFunctionTwoArgs +---@field message_equals LuassertFunctionTwoArgs +---@field message_error LuassertFunction +---@field message_error_match LuassertFunctionTwoArgs +---@field message_error_matches LuassertFunctionTwoArgs +---@field message_errors LuassertFunction +---@field message_false LuassertFunction +---@field message_falsy LuassertFunction +---@field message_function LuassertFunction +---@field message_holes LuassertFunction +---@field message_match LuassertFunctionTwoArgs +---@field message_match_error LuassertFunctionTwoArgs +---@field message_matches LuassertFunctionTwoArgs +---@field message_matches_error LuassertFunctionTwoArgs +---@field message_near LuassertFunctionMultiArgs +---@field message_nil LuassertFunction +---@field message_number LuassertFunction +---@field message_returned_arguments LuassertFunction +---@field message_same LuassertFunctionTwoArgs +---@field message_string LuassertFunction +---@field message_table LuassertFunction +---@field message_thread LuassertFunction +---@field message_true LuassertFunction +---@field message_truthy LuassertFunction +---@field message_unique LuassertFunction +---@field message_userdata LuassertFunction +---@field no_boolean LuassertFunction +---@field no_equal LuassertFunctionTwoArgs +---@field no_equals LuassertFunctionTwoArgs +---@field no_error LuassertFunction +---@field no_error_match LuassertFunctionTwoArgs +---@field no_error_matches LuassertFunctionTwoArgs +---@field no_errors LuassertFunction +---@field no_false LuassertFunction +---@field no_falsy LuassertFunction +---@field no_function LuassertFunction +---@field no_holes LuassertFunction +---@field no_match LuassertFunctionTwoArgs +---@field no_match_error LuassertFunctionTwoArgs +---@field no_matches LuassertFunctionTwoArgs +---@field no_matches_error LuassertFunctionTwoArgs +---@field no_near LuassertFunctionMultiArgs +---@field no_nil LuassertFunction +---@field no_number LuassertFunction +---@field no_returned_arguments LuassertFunction +---@field no_same LuassertFunctionTwoArgs +---@field no_string LuassertFunction +---@field no_table LuassertFunction +---@field no_thread LuassertFunction +---@field no_true LuassertFunction +---@field no_truthy LuassertFunction +---@field no_unique LuassertFunction +---@field no_userdata LuassertFunction +---@field not_boolean LuassertFunction +---@field not_equal LuassertFunctionTwoArgs +---@field not_equals LuassertFunctionTwoArgs +---@field not_error LuassertFunction +---@field not_error_match LuassertFunctionTwoArgs +---@field not_error_matches LuassertFunctionTwoArgs +---@field not_errors LuassertFunction +---@field not_false LuassertFunction +---@field not_falsy LuassertFunction +---@field not_function LuassertFunction +---@field not_holes LuassertFunction +---@field not_match LuassertFunctionTwoArgs +---@field not_match_error LuassertFunctionTwoArgs +---@field not_matches LuassertFunctionTwoArgs +---@field not_matches_error LuassertFunctionTwoArgs +---@field not_near LuassertFunctionMultiArgs +---@field not_nil LuassertFunction +---@field not_number LuassertFunction +---@field not_returned_arguments LuassertFunction +---@field not_same LuassertFunctionTwoArgs +---@field not_string LuassertFunction +---@field not_table LuassertFunction +---@field not_thread LuassertFunction +---@field not_true LuassertFunction +---@field not_truthy LuassertFunction +---@field not_unique LuassertFunction +---@field not_userdata LuassertFunction +---@field was_boolean LuassertFunction +---@field was_equal LuassertFunctionTwoArgs +---@field was_equals LuassertFunctionTwoArgs +---@field was_error LuassertFunction +---@field was_error_match LuassertFunctionTwoArgs +---@field was_error_matches LuassertFunctionTwoArgs +---@field was_errors LuassertFunction +---@field was_false LuassertFunction +---@field was_falsy LuassertFunction +---@field was_function LuassertFunction +---@field was_holes LuassertFunction +---@field was_match LuassertFunctionTwoArgs +---@field was_match_error LuassertFunctionTwoArgs +---@field was_matches LuassertFunctionTwoArgs +---@field was_matches_error LuassertFunctionTwoArgs +---@field was_near LuassertFunctionMultiArgs +---@field was_nil LuassertFunction +---@field was_number LuassertFunction +---@field was_returned_arguments LuassertFunction +---@field was_same LuassertFunctionTwoArgs +---@field was_string LuassertFunction +---@field was_table LuassertFunction +---@field was_thread LuassertFunction +---@field was_true LuassertFunction +---@field was_truthy LuassertFunction +---@field was_unique LuassertFunction +---@field was_userdata LuassertFunction +---@field boolean LuassertFunction +---@field equal LuassertFunctionTwoArgs +---@field equals LuassertFunctionTwoArgs +---@field error LuassertFunction +---@field error_match LuassertFunctionTwoArgs +---@field error_matches LuassertFunctionTwoArgs +---@field errors LuassertFunction +---@field False LuassertFunction +---@field falsy LuassertFunction +---@field Function LuassertFunction +---@field holes LuassertFunction +---@field match LuassertFunctionTwoArgs +---@field match_error LuassertFunctionTwoArgs +---@field matches LuassertFunctionTwoArgs +---@field matches_error LuassertFunctionTwoArgs +---@field near LuassertFunctionMultiArgs +---@field Nil LuassertFunction +---@field number LuassertFunction +---@field returned_arguments LuassertFunction +---@field same LuassertFunctionTwoArgs +---@field string LuassertFunction +---@field table LuassertFunction +---@field thread LuassertFunction +---@field True LuassertFunction +---@field truthy LuassertFunction +---@field unique LuassertFunction +---@field userdata LuassertFunction +---@field are Luassert +---@field array Luassert +---@field does Luassert +---@field has Luassert +---@field is Luassert +---@field message Luassert +---@field no Luassert +---@field Not Luassert +---@field was Luassert \ No newline at end of file diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/api.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/api.lua new file mode 100644 index 0000000..2a4e9c0 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/api.lua @@ -0,0 +1,14 @@ +local util = require "plenary.async.util" + +return setmetatable({}, { + __index = function(t, k) + return function(...) + -- if we are in a fast event await the scheduler + if vim.in_fast_event() then + util.scheduler() + end + + return vim.api[k](...) + end + end, +}) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/async.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/async.lua new file mode 100644 index 0000000..ff49288 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/async.lua @@ -0,0 +1,122 @@ +local co = coroutine +local vararg = require "plenary.vararg" +local errors = require "plenary.errors" +local traceback_error = errors.traceback_error +local f = require "plenary.functional" + +local M = {} + +local function is_callable(fn) + return type(fn) == "function" or (type(fn) == "table" and type(getmetatable(fn)["__call"]) == "function") +end + +---because we can't store varargs +local function callback_or_next(step, thread, callback, ...) + local stat = f.first(...) + + if not stat then + error(string.format("The coroutine failed with this message: %s", f.second(...))) + end + + if co.status(thread) == "dead" then + if callback == nil then + return + end + callback(select(2, ...)) + else + local returned_function = f.second(...) + local nargs = f.third(...) + + assert(is_callable(returned_function), "type error :: expected func") + returned_function(vararg.rotate(nargs, step, select(4, ...))) + end +end + +---Executes a future with a callback when it is done +---@param async_function Future: the future to execute +---@param callback function: the callback to call when done +local execute = function(async_function, callback, ...) + assert(is_callable(async_function), "type error :: expected func") + + local thread = co.create(async_function) + + local step + step = function(...) + callback_or_next(step, thread, callback, co.resume(thread, ...)) + end + + step(...) +end + +local add_leaf_function +do + ---A table to store all leaf async functions + _PlenaryLeafTable = setmetatable({}, { + __mode = "k", + }) + + add_leaf_function = function(async_func, argc) + assert(_PlenaryLeafTable[async_func] == nil, "Async function should not already be in the table") + _PlenaryLeafTable[async_func] = argc + end + + function M.is_leaf_function(async_func) + return _PlenaryLeafTable[async_func] ~= nil + end + + function M.get_leaf_function_argc(async_func) + return _PlenaryLeafTable[async_func] + end +end + +---Creates an async function with a callback style function. +---@param func function: A callback style function to be converted. The last argument must be the callback. +---@param argc number: The number of arguments of func. Must be included. +---@return function: Returns an async function +M.wrap = function(func, argc) + if not is_callable(func) then + traceback_error("type error :: expected func, got " .. type(func)) + end + + if type(argc) ~= "number" then + traceback_error("type error :: expected number, got " .. type(argc)) + end + + local function leaf(...) + local nargs = select("#", ...) + + if nargs == argc then + return func(...) + else + return co.yield(func, argc, ...) + end + end + + add_leaf_function(leaf, argc) + + return leaf +end + +---Use this to either run a future concurrently and then do something else +---or use it to run a future with a callback in a non async context +---@param async_function function +---@param callback function +M.run = function(async_function, callback) + if M.is_leaf_function(async_function) then + async_function(callback) + else + execute(async_function, callback) + end +end + +---Use this to create a function which executes in an async context but +---called from a non-async context. Inherently this cannot return anything +---since it is non-blocking +---@param func function +M.void = function(func) + return function(...) + execute(func, nil, ...) + end +end + +return M diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/control.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/control.lua new file mode 100644 index 0000000..3a8a8ed --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/control.lua @@ -0,0 +1,229 @@ +local a = require "plenary.async.async" +local Deque = require("plenary.async.structs").Deque +local tbl = require "plenary.tbl" + +local M = {} + +local Condvar = {} +Condvar.__index = Condvar + +---@class Condvar +---@return Condvar +function Condvar.new() + return setmetatable({ handles = {} }, Condvar) +end + +---`blocks` the thread until a notification is received +Condvar.wait = a.wrap(function(self, callback) + -- not calling the callback will block the coroutine + table.insert(self.handles, callback) +end, 2) + +---notify everyone that is waiting on this Condvar +function Condvar:notify_all() + local len = #self.handles + for i, callback in ipairs(self.handles) do + if i > len then + -- this means that more handles were added while we were notifying + -- if we don't break we can get starvation notifying as soon as new handles are added + break + end + + callback() + end + + for _ = 1, len do + -- table.remove will ensure that indexes are correct and make "ipairs" safe, + -- which is not the case for "self.handles[i] = nil" + table.remove(self.handles) + end +end + +---notify randomly one person that is waiting on this Condvar +function Condvar:notify_one() + if #self.handles == 0 then + return + end + + local idx = math.random(#self.handles) + self.handles[idx]() + table.remove(self.handles, idx) +end + +M.Condvar = Condvar + +local Semaphore = {} +Semaphore.__index = Semaphore + +---@class Semaphore +---@param initial_permits number: the number of permits that it can give out +---@return Semaphore +function Semaphore.new(initial_permits) + vim.validate { + initial_permits = { + initial_permits, + function(n) + return n > 0 + end, + "number greater than 0", + }, + } + + return setmetatable({ permits = initial_permits, handles = {} }, Semaphore) +end + +---async function, blocks until a permit can be acquired +---example: +---local semaphore = Semaphore.new(1024) +---local permit = semaphore:acquire() +---permit:forget() +---when a permit can be acquired returns it +---call permit:forget() to forget the permit +Semaphore.acquire = a.wrap(function(self, callback) + if self.permits > 0 then + self.permits = self.permits - 1 + else + table.insert(self.handles, callback) + return + end + + local permit = {} + + permit.forget = function(self_permit) + self.permits = self.permits + 1 + + if self.permits > 0 and #self.handles > 0 then + self.permits = self.permits - 1 + table.remove(self.handles)(self_permit) + end + end + + callback(permit) +end, 2) + +M.Semaphore = Semaphore + +M.channel = {} + +---Creates a oneshot channel +---returns a sender and receiver function +---the sender is not async while the receiver is +---@return function, function +M.channel.oneshot = function() + local val = nil + local saved_callback = nil + local sent = false + local received = false + local is_single = false + + --- sender is not async + --- sends a value which can be nil + local sender = function(...) + assert(not sent, "Oneshot channel can only send once") + sent = true + + if saved_callback ~= nil then + saved_callback(...) + return + end + + -- optimise for when there is only one or zero argument, no need to pack + local nargs = select("#", ...) + if nargs == 1 or nargs == 0 then + val = ... + is_single = true + else + val = tbl.pack(...) + end + end + + --- receiver is async + --- blocks until a value is received + local receiver = a.wrap(function(callback) + assert(not received, "Oneshot channel can only receive one value!") + + if sent then + received = true + if is_single then + return callback(val) + else + return callback(tbl.unpack(val)) + end + else + saved_callback = callback + end + end, 1) + + return sender, receiver +end + +---A counter channel. +---Basically a channel that you want to use only to notify and not to send any actual values. +---@return function: sender +---@return function: receiver +M.channel.counter = function() + local counter = 0 + local condvar = Condvar.new() + + local Sender = {} + + function Sender:send() + counter = counter + 1 + condvar:notify_all() + end + + local Receiver = {} + + Receiver.recv = function() + if counter == 0 then + condvar:wait() + end + counter = counter - 1 + end + + Receiver.last = function() + if counter == 0 then + condvar:wait() + end + counter = 0 + end + + return Sender, Receiver +end + +---A multiple producer single consumer channel +---@return table +---@return table +M.channel.mpsc = function() + local deque = Deque.new() + local condvar = Condvar.new() + + local Sender = {} + + function Sender.send(...) + deque:pushleft { ... } + condvar:notify_all() + end + + local Receiver = {} + + Receiver.recv = function() + if deque:is_empty() then + condvar:wait() + end + return unpack(deque:popright()) + end + + Receiver.last = function() + if deque:is_empty() then + condvar:wait() + end + local val = deque:popleft() + deque:clear() + return unpack(val or {}) + end + + return Sender, Receiver +end + +return M diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/init.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/init.lua new file mode 100644 index 0000000..027614a --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/init.lua @@ -0,0 +1,57 @@ +---@brief [[ +--- NOTE: This API is still under construction. +--- It may change in the future :) +---@brief ]] + +local lookups = { + uv = "plenary.async.uv_async", + util = "plenary.async.util", + lsp = "plenary.async.lsp", + api = "plenary.async.api", + tests = "plenary.async.tests", + control = "plenary.async.control", +} + +local exports = setmetatable(require "plenary.async.async", { + __index = function(t, k) + local require_path = lookups[k] + if not require_path then + return + end + + local mod = require(require_path) + t[k] = mod + + return mod + end, +}) + +exports.tests.add_globals = function() + a = exports + + -- must prefix with a or stack overflow, plenary.test harness already added it + a.describe = exports.tests.describe + -- must prefix with a or stack overflow + a.it = exports.tests.it + a.pending = exports.tests.pending + a.before_each = exports.tests.before_each + a.after_each = exports.tests.after_each +end + +exports.tests.add_to_env = function() + local env = getfenv(2) + + env.a = exports + + -- must prefix with a or stack overflow, plenary.test harness already added it + env.a.describe = exports.tests.describe + -- must prefix with a or stack overflow + env.a.it = exports.tests.it + env.a.pending = exports.tests.pending + env.a.before_each = exports.tests.before_each + env.a.after_each = exports.tests.after_each + + setfenv(2, env) +end + +return exports diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/lsp.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/lsp.lua new file mode 100644 index 0000000..e76e4c4 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/lsp.lua @@ -0,0 +1,12 @@ +local a = require "plenary.async.async" + +local M = {} + +---This will be deprecated because the callback can be called multiple times. +---This will give a coroutine error because the coroutine will be resumed multiple times. +---Please use buf_request_all instead. +M.buf_request = a.wrap(vim.lsp.buf_request, 4) + +M.buf_request_all = a.wrap(vim.lsp.buf_request_all, 4) + +return M diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/structs.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/structs.lua new file mode 100644 index 0000000..c133a21 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/structs.lua @@ -0,0 +1,116 @@ +local M = {} + +local Deque = {} +Deque.__index = Deque + +---@class Deque +---A double ended queue +--- +---@return Deque +function Deque.new() + -- the indexes are created with an offset so that the indices are consequtive + -- otherwise, when both pushleft and pushright are used, the indices will have a 1 length hole in the middle + return setmetatable({ first = 0, last = -1 }, Deque) +end + +---push to the left of the deque +---@param value any +function Deque:pushleft(value) + local first = self.first - 1 + self.first = first + self[first] = value +end + +---push to the right of the deque +---@param value any +function Deque:pushright(value) + local last = self.last + 1 + self.last = last + self[last] = value +end + +---pop from the left of the deque +---@return any +function Deque:popleft() + local first = self.first + if first > self.last then + return nil + end + local value = self[first] + self[first] = nil -- to allow garbage collection + self.first = first + 1 + return value +end + +---pops from the right of the deque +---@return any +function Deque:popright() + local last = self.last + if self.first > last then + return nil + end + local value = self[last] + self[last] = nil -- to allow garbage collection + self.last = last - 1 + return value +end + +---checks if the deque is empty +---@return boolean +function Deque:is_empty() + return self:len() == 0 +end + +---returns the number of elements of the deque +---@return number +function Deque:len() + return self.last - self.first + 1 +end + +---returns and iterator of the indices and values starting from the left +---@return function +function Deque:ipairs_left() + local i = self.first + + return function() + local res = self[i] + local idx = i + + if res then + i = i + 1 + + return idx, res + end + end +end + +---returns and iterator of the indices and values starting from the right +---@return function +function Deque:ipairs_right() + local i = self.last + + return function() + local res = self[i] + local idx = i + + if res then + i = i - 1 -- advance the iterator before we return + + return idx, res + end + end +end + +---removes all values from the deque +---@return nil +function Deque:clear() + for i, _ in self:ipairs_left() do + self[i] = nil + end + self.first = 0 + self.last = -1 +end + +M.Deque = Deque + +return M diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/tests.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/tests.lua new file mode 100644 index 0000000..d10db48 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/tests.lua @@ -0,0 +1,25 @@ +local util = require "plenary.async.util" + +local M = {} + +M.describe = function(s, async_func) + describe(s, async_func) +end + +M.it = function(s, async_func) + it(s, util.will_block(async_func, tonumber(vim.env.PLENARY_TEST_TIMEOUT))) +end + +M.pending = function(async_func) + pending(async_func) +end + +M.before_each = function(async_func) + before_each(util.will_block(async_func)) +end + +M.after_each = function(async_func) + after_each(util.will_block(async_func)) +end + +return M diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/util.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/util.lua new file mode 100644 index 0000000..b80f3fd --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/util.lua @@ -0,0 +1,145 @@ +local a = require "plenary.async.async" +local vararg = require "plenary.vararg" +-- local control = a.control +local control = require "plenary.async.control" +local channel = control.channel + +local M = {} + +local defer_swapped = function(timeout, callback) + vim.defer_fn(callback, timeout) +end + +---Sleep for milliseconds +---@param ms number +M.sleep = a.wrap(defer_swapped, 2) + +---This will COMPLETELY block neovim +---please just use a.run unless you have a very special usecase +---for example, in plenary test_harness you must use this +---@param async_function Future +---@param timeout number: Stop blocking if the timeout was surpassed. Default 2000. +M.block_on = function(async_function, timeout) + async_function = M.protected(async_function) + + local stat + local ret = {} + + a.run(async_function, function(stat_, ...) + stat = stat_ + ret = { ... } + end) + + vim.wait(timeout or 2000, function() + return stat ~= nil + end, 20, false) + + if stat == false then + error(string.format("Blocking on future timed out or was interrupted.\n%s", unpack(ret))) + end + + return unpack(ret) +end + +---@see M.block_on +---@param async_function Future +---@param timeout number +M.will_block = function(async_function, timeout) + return function() + M.block_on(async_function, timeout) + end +end + +M.join = function(async_fns) + local len = #async_fns + local results = {} + if len == 0 then + return results + end + + local done = 0 + + local tx, rx = channel.oneshot() + + for i, async_fn in ipairs(async_fns) do + assert(type(async_fn) == "function", "type error :: future must be function") + + local cb = function(...) + results[i] = { ... } + done = done + 1 + if done == len then + tx() + end + end + + a.run(async_fn, cb) + end + + rx() + + return results +end + +---Returns a result from the future that finishes at the first +---@param async_functions table: The futures that you want to select +---@return ... +M.run_first = a.wrap(function(async_functions, step) + local ran = false + + for _, async_function in ipairs(async_functions) do + assert(type(async_function) == "function", "type error :: future must be function") + + local callback = function(...) + if not ran then + ran = true + step(...) + end + end + + async_function(callback) + end +end, 2) + +---Returns a result from the functions that finishes at the first +---@param funcs table: The async functions that you want to select +---@return ... +M.race = function(funcs) + local async_functions = vim.tbl_map(function(func) + return function(callback) + a.run(func, callback) + end + end, funcs) + return M.run_first(async_functions) +end + +M.run_all = function(async_fns, callback) + a.run(function() + M.join(async_fns) + end, callback) +end + +function M.apcall(async_fn, ...) + local nargs = a.get_leaf_function_argc(async_fn) + if nargs then + local tx, rx = channel.oneshot() + local stat, ret = pcall(async_fn, vararg.rotate(nargs, tx, ...)) + if not stat then + return stat, ret + else + return stat, rx() + end + else + return pcall(async_fn, ...) + end +end + +function M.protected(async_fn) + return function() + return M.apcall(async_fn) + end +end + +---An async function that when called will yield to the neovim scheduler to be able to call the api. +M.scheduler = a.wrap(vim.schedule, 1) + +return M diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/uv_async.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/uv_async.lua new file mode 100644 index 0000000..427e1a5 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async/uv_async.lua @@ -0,0 +1,84 @@ +local a = require "plenary.async.async" +local uv = vim.loop + +local M = {} + +local function add(name, argc, custom) + local success, ret = pcall(a.wrap, custom or uv[name], argc) + + if not success then + error("Failed to add function with name " .. name) + end + + M[name] = ret +end + +add("close", 4) -- close a handle + +-- filesystem operations +add("fs_open", 4) +add("fs_read", 4) +add("fs_close", 2) +add("fs_unlink", 2) +add("fs_write", 4) +add("fs_mkdir", 3) +add("fs_mkdtemp", 2) +-- 'fs_mkstemp', +add("fs_rmdir", 2) +add("fs_scandir", 2) +add("fs_stat", 2) +add("fs_fstat", 2) +add("fs_lstat", 2) +add("fs_rename", 3) +add("fs_fsync", 2) +add("fs_fdatasync", 2) +add("fs_ftruncate", 3) +add("fs_sendfile", 5) +add("fs_access", 3) +add("fs_chmod", 3) +add("fs_fchmod", 3) +add("fs_utime", 4) +add("fs_futime", 4) +-- 'fs_lutime', +add("fs_link", 3) +add("fs_symlink", 4) +add("fs_readlink", 2) +add("fs_realpath", 2) +add("fs_chown", 4) +add("fs_fchown", 4) +-- 'fs_lchown', +add("fs_copyfile", 4) +add("fs_opendir", 3, function(path, entries, callback) + return uv.fs_opendir(path, callback, entries) +end) +add("fs_readdir", 2) +add("fs_closedir", 2) +-- 'fs_statfs', + +-- stream +add("shutdown", 2) +add("listen", 3) +-- add('read_start', 2) -- do not do this one, the callback is made multiple times +add("write", 3) +add("write2", 4) +add("shutdown", 2) + +-- tcp +add("tcp_connect", 4) +-- 'tcp_close_reset', + +-- pipe +add("pipe_connect", 3) + +-- udp +add("udp_send", 5) +add("udp_recv_start", 2) + +-- fs event (wip make into async await event) +-- fs poll event (wip make into async await event) + +-- dns +add("getaddrinfo", 4) +add("getnameinfo", 2) + +return M diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/api.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/api.lua new file mode 100644 index 0000000..baaf80c --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/api.lua @@ -0,0 +1,15 @@ +local a = require "plenary.async_lib.async" +local async, await = a.async, a.await + +return setmetatable({}, { + __index = function(t, k) + return async(function(...) + -- if we are in a fast event await the scheduler + if vim.in_fast_event() then + await(a.scheduler()) + end + + vim.api[k](...) + end) + end, +}) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/async.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/async.lua new file mode 100644 index 0000000..84ca70d --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/async.lua @@ -0,0 +1,213 @@ +local co = coroutine +local errors = require "plenary.errors" +local traceback_error = errors.traceback_error +local f = require "plenary.functional" +local tbl = require "plenary.tbl" + +local M = {} + +---because we can't store varargs +local function callback_or_next(step, thread, callback, ...) + local stat = f.first(...) + + if not stat then + error(string.format("The coroutine failed with this message: %s", f.second(...))) + end + + if co.status(thread) == "dead" then + (callback or function() end)(select(2, ...)) + else + assert(select("#", select(2, ...)) == 1, "expected a single return value") + local returned_future = f.second(...) + assert(type(returned_future) == "function", "type error :: expected func") + returned_future(step) + end +end + +---@class Future +---Something that will give a value when run + +---Executes a future with a callback when it is done +---@param future Future: the future to execute +---@param callback function: the callback to call when done +local execute = function(future, callback) + assert(type(future) == "function", "type error :: expected func") + local thread = co.create(future) + + local step + step = function(...) + callback_or_next(step, thread, callback, co.resume(thread, ...)) + end + + step() +end + +---Creates an async function with a callback style function. +---@param func function: A callback style function to be converted. The last argument must be the callback. +---@param argc number: The number of arguments of func. Must be included. +---@return function: Returns an async function +M.wrap = function(func, argc) + if type(func) ~= "function" then + traceback_error("type error :: expected func, got " .. type(func)) + end + + if type(argc) ~= "number" and argc ~= "vararg" then + traceback_error "expected argc to be a number or string literal 'vararg'" + end + + return function(...) + local params = tbl.pack(...) + + local function future(step) + if step then + if type(argc) == "number" then + params[argc] = step + params.n = argc + else + table.insert(params, step) -- change once not optional + params.n = params.n + 1 + end + + return func(tbl.unpack(params)) + else + return co.yield(future) + end + end + return future + end +end + +---Return a new future that when run will run all futures concurrently. +---@param futures table: the futures that you want to join +---@return Future: returns a future +M.join = M.wrap(function(futures, step) + local len = #futures + local results = {} + local done = 0 + + if len == 0 then + return step(results) + end + + for i, future in ipairs(futures) do + assert(type(future) == "function", "type error :: future must be function") + + local callback = function(...) + results[i] = { ... } + done = done + 1 + if done == len then + step(results) + end + end + + future(callback) + end +end, 2) + +---Returns a future that when run will select the first future that finishes +---@param futures table: The future that you want to select +---@return Future +M.select = M.wrap(function(futures, step) + local selected = false + + for _, future in ipairs(futures) do + assert(type(future) == "function", "type error :: future must be function") + + local callback = function(...) + if not selected then + selected = true + step(...) + end + end + + future(callback) + end +end, 2) + +---Use this to either run a future concurrently and then do something else +---or use it to run a future with a callback in a non async context +---@param future Future +---@param callback function +M.run = function(future, callback) + future(callback or function() end) +end + +---Same as run but runs multiple futures +---@param futures table +---@param callback function +M.run_all = function(futures, callback) + M.run(M.join(futures), callback) +end + +---Await a future, yielding the current function +---@param future Future +---@return any: returns the result of the future when it is done +M.await = function(future) + assert(type(future) == "function", "type error :: expected function to await") + return future(nil) +end + +---Same as await but can await multiple futures. +---If the futures have libuv leaf futures they will be run concurrently +---@param futures table +---@return table: returns a table of results that each future returned. Note that if the future returns multiple values they will be packed into a table. +M.await_all = function(futures) + assert(type(futures) == "table", "type error :: expected table") + return M.await(M.join(futures)) +end + +---suspend a coroutine +M.suspend = co.yield + +---create a async scope +M.scope = function(func) + M.run(M.future(func)) +end + +--- Future a :: a -> (a -> ()) +--- turns this signature +--- ... -> Future a +--- into this signature +--- ... -> () +M.void = function(async_func) + return function(...) + async_func(...)(function() end) + end +end + +M.async_void = function(func) + return M.void(M.async(func)) +end + +---creates an async function +---@param func function +---@return function: returns an async function +M.async = function(func) + if type(func) ~= "function" then + traceback_error("type error :: expected func, got " .. type(func)) + end + + return function(...) + local args = tbl.pack(...) + local function future(step) + if step == nil then + return func(tbl.unpack(args)) + else + execute(future, step) + end + end + return future + end +end + +---creates a future +---@param func function +---@return Future +M.future = function(func) + return M.async(func)() +end + +---An async function that when awaited will await the scheduler to be able to call the api. +M.scheduler = M.wrap(vim.schedule, 1) + +return M diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/init.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/init.lua new file mode 100644 index 0000000..4f1ce55 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/init.lua @@ -0,0 +1,41 @@ +---@brief [[ +--- NOTE: This API is still under construction. +--- It may change in the future :) +---@brief ]] + +local exports = require "plenary.async_lib.async" +exports.uv = require "plenary.async_lib.uv_async" +exports.util = require "plenary.async_lib.util" +exports.lsp = require "plenary.async_lib.lsp" +exports.api = require "plenary.async_lib.api" +exports.tests = require "plenary.async_lib.tests" + +exports.tests.add_globals = function() + a = exports + async = exports.async + await = exports.await + await_all = exports.await_all + + -- must prefix with a or stack overflow, plenary.test harness already added it + a.describe = exports.tests.describe + -- must prefix with a or stack overflow + a.it = exports.tests.it +end + +exports.tests.add_to_env = function() + local env = getfenv(2) + + env.a = exports + env.async = exports.async + env.await = exports.await + env.await_all = exports.await_all + + -- must prefix with a or stack overflow, plenary.test harness already added it + env.a.describe = exports.tests.describe + -- must prefix with a or stack overflow + env.a.it = exports.tests.it + + setfenv(2, env) +end + +return exports diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/lsp.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/lsp.lua new file mode 100644 index 0000000..924a763 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/lsp.lua @@ -0,0 +1,15 @@ +local a = require "plenary.async_lib.async" + +local M = {} + +---This will be deprecated because the callback can be called multiple times. +---This will give a coroutine error because the coroutine will be resumed multiple times. +---Please use buf_request_all instead. +M.buf_request = a.wrap(vim.lsp.buf_request, 4) + +--This was recently merged into master so we just check if it is there +if vim.lsp.buf_request_all ~= nil then + M.buf_request_all = a.wrap(vim.lsp.buf_request_all, 4) +end + +return M diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/structs.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/structs.lua new file mode 100644 index 0000000..73252e4 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/structs.lua @@ -0,0 +1,116 @@ +local M = {} + +Deque = {} +Deque.__index = Deque + +---@class Deque +---A double ended queue +--- +---@return Deque +function Deque.new() + -- the indexes are created with an offset so that the indices are consequtive + -- otherwise, when both pushleft and pushright are used, the indices will have a 1 length hole in the middle + return setmetatable({ first = 0, last = -1 }, Deque) +end + +---push to the left of the deque +---@param value any +function Deque:pushleft(value) + local first = self.first - 1 + self.first = first + self[first] = value +end + +---push to the right of the deque +---@param value any +function Deque:pushright(value) + local last = self.last + 1 + self.last = last + self[last] = value +end + +---pop from the left of the deque +---@return any +function Deque:popleft() + local first = self.first + if first > self.last then + return nil + end + local value = self[first] + self[first] = nil -- to allow garbage collection + self.first = first + 1 + return value +end + +---pops from the right of the deque +---@return any +function Deque:popright() + local last = self.last + if self.first > last then + return nil + end + local value = self[last] + self[last] = nil -- to allow garbage collection + self.last = last - 1 + return value +end + +---checks if the deque is empty +---@return boolean +function Deque:is_empty() + return self:len() == 0 +end + +---returns the number of elements of the deque +---@return number +function Deque:len() + return self.last - self.first + 1 +end + +---returns and iterator of the indices and values starting from the left +---@return function +function Deque:ipairs_left() + local i = self.first + + return function() + local res = self[i] + local idx = i + + if res then + i = i + 1 + + return idx, res + end + end +end + +---returns and iterator of the indices and values starting from the right +---@return function +function Deque:ipairs_right() + local i = self.last + + return function() + local res = self[i] + local idx = i + + if res then + i = i - 1 -- advance the iterator before we return + + return idx, res + end + end +end + +---removes all values from the deque +---@return nil +function Deque:clear() + for i, _ in self:ipairs_left() do + self[i] = nil + end + self.first = 0 + self.last = -1 +end + +M.Deque = Deque + +return M diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/tests.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/tests.lua new file mode 100644 index 0000000..8dff05b --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/tests.lua @@ -0,0 +1,14 @@ +local a = require "plenary.async_lib.async" +local util = require "plenary.async_lib.util" + +local M = {} + +M.describe = function(s, func) + describe(s, util.will_block(a.future(func))) +end + +M.it = function(s, func) + it(s, util.will_block(a.future(func))) +end + +return M diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/util.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/util.lua new file mode 100644 index 0000000..1de65fe --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/util.lua @@ -0,0 +1,339 @@ +local a = require "plenary.async_lib.async" +local await = a.await +local async = a.async +local co = coroutine +local Deque = require("plenary.async_lib.structs").Deque +local uv = vim.loop + +local M = {} + +---Sleep for milliseconds +---@param ms number +M.sleep = a.wrap(function(ms, callback) + local timer = uv.new_timer() + uv.timer_start(timer, ms, 0, function() + uv.timer_stop(timer) + uv.close(timer) + callback() + end) +end, 2) + +---Takes a future and a millisecond as the timeout. +---If the time is reached and the future hasn't completed yet, it will short circuit the future +---NOTE: the future will still be running in libuv, we are just not waiting for it to complete +---thats why you should call this on a leaf future only to avoid unexpected results +---@param future Future +---@param ms number +M.timeout = a.wrap(function(future, ms, callback) + -- make sure that the callback isn't called twice, or else the coroutine can be dead + local done = false + + local timeout_callback = function(...) + if not done then + done = true + callback(false, ...) -- false because it has run normally + end + end + + vim.defer_fn(function() + if not done then + done = true + callback(true) -- true because it has timed out + end + end, ms) + + a.run(future, timeout_callback) +end, 3) + +---create an async function timer +---@param ms number +M.timer = function(ms) + return async(function() + await(M.sleep(ms)) + end) +end + +---id function that can be awaited +---@param nil ... +---@return ... +M.id = async(function(...) + return ... +end) + +---Running this function will yield now and do nothing else +M.yield_now = async(function() + await(M.id()) +end) + +local Condvar = {} +Condvar.__index = Condvar + +---@class Condvar +---@return Condvar +function Condvar.new() + return setmetatable({ handles = {} }, Condvar) +end + +---`blocks` the thread until a notification is received +Condvar.wait = a.wrap(function(self, callback) + -- not calling the callback will block the coroutine + table.insert(self.handles, callback) +end, 2) + +---notify everyone that is waiting on this Condvar +function Condvar:notify_all() + if #self.handles == 0 then + return + end + + for i, callback in ipairs(self.handles) do + callback() + self.handles[i] = nil + end +end + +---notify randomly one person that is waiting on this Condvar +function Condvar:notify_one() + if #self.handles == 0 then + return + end + + local idx = math.random(#self.handles) + self.handles[idx]() + table.remove(self.handles, idx) +end + +M.Condvar = Condvar + +local Semaphore = {} +Semaphore.__index = Semaphore + +---@class Semaphore +---@param initial_permits number: the number of permits that it can give out +---@return Semaphore +function Semaphore.new(initial_permits) + vim.validate { + initial_permits = { + initial_permits, + function(n) + return n > 0 + end, + "number greater than 0", + }, + } + + return setmetatable({ permits = initial_permits, handles = {} }, Semaphore) +end + +---async function, blocks until a permit can be acquired +---example: +---local semaphore = Semaphore.new(1024) +---local permit = await(semaphore:acquire()) +---permit:forget() +---when a permit can be acquired returns it +---call permit:forget() to forget the permit +Semaphore.acquire = a.wrap(function(self, callback) + if self.permits > 0 then + self.permits = self.permits - 1 + else + table.insert(self.handles, callback) + return + end + + local permit = {} + + permit.forget = function(self_permit) + self.permits = self.permits + 1 + + if self.permits > 0 and #self.handles > 0 then + self.permits = self.permits - 1 + local callback = table.remove(self.handles) + callback(self_permit) + end + end + + callback(permit) +end, 2) + +M.Semaphore = Semaphore + +M.channel = {} + +---Creates a oneshot channel +---returns a sender and receiver function +---the sender is not async while the receiver is +---@return function, function +M.channel.oneshot = function() + local val = nil + local saved_callback = nil + local sent = false + local received = false + + --- sender is not async + --- sends a value + local sender = function(...) + if sent then + error "Oneshot channel can only send once" + end + + sent = true + + local args = { ... } + + if saved_callback then + saved_callback(unpack(val or args)) + else + val = args + end + end + + --- receiver is async + --- blocks until a value is received + local receiver = a.wrap(function(callback) + if received then + error "Oneshot channel can only send one value!" + end + + if val then + received = true + callback(unpack(val)) + else + saved_callback = callback + end + end, 1) + + return sender, receiver +end + +---A counter channel. +---Basically a channel that you want to use only to notify and not to send any actual values. +---@return function: sender +---@return function: receiver +M.channel.counter = function() + local counter = 0 + local condvar = Condvar.new() + + local Sender = {} + + function Sender:send() + counter = counter + 1 + condvar:notify_all() + end + + local Receiver = {} + + Receiver.recv = async(function() + if counter == 0 then + await(condvar:wait()) + end + counter = counter - 1 + end) + + Receiver.last = async(function() + if counter == 0 then + await(condvar:wait()) + end + counter = 0 + end) + + return Sender, Receiver +end + +---A multiple producer single consumer channel +---@return table +---@return table +M.channel.mpsc = function() + local deque = Deque.new() + local condvar = Condvar.new() + + local Sender = {} + + function Sender.send(...) + deque:pushleft { ... } + condvar:notify_all() + end + + local Receiver = {} + + Receiver.recv = async(function() + if deque:is_empty() then + await(condvar:wait()) + end + return unpack(deque:popright()) + end) + + Receiver.last = async(function() + if deque:is_empty() then + await(condvar:wait()) + end + local val = deque:popright() + deque:clear() + return unpack(val) + end) + + return Sender, Receiver +end + +local pcall_wrap = function(func) + return function(...) + return pcall(func, ...) + end +end + +---Makes a future protected. It is like pcall but for futures. +---Only works for non-leaf futures +M.protected_non_leaf = async(function(future) + return await(pcall_wrap(future)) +end) + +---Makes a future protected. It is like pcall but for futures. +---@param future Future +---@return Future +M.protected = async(function(future) + local tx, rx = M.channel.oneshot() + + stat, ret = pcall(future, tx) + + if stat == true then + return stat, await(rx()) + else + return stat, ret + end +end) + +---This will COMPLETELY block neovim +---please just use a.run unless you have a very special usecase +---for example, in plenary test_harness you must use this +---@param future Future +---@param timeout number: Stop blocking if the timeout was surpassed. Default 2000. +M.block_on = function(future, timeout) + future = M.protected(future) + + local stat, ret + a.run(future, function(_stat, ...) + stat = _stat + ret = { ... } + end) + + local function check() + if stat == false then + error("Blocking on future failed " .. unpack(ret)) + end + return stat == true + end + + if not vim.wait(timeout or 2000, check, 20, false) then + error "Blocking on future timed out or was interrupted" + end + + return unpack(ret) +end + +---Returns a new future that WILL BLOCK +---@param future Future +---@return Future +M.will_block = async(function(future) + return M.block_on(future) +end) + +return M diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/uv_async.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/uv_async.lua new file mode 100644 index 0000000..4ff4ef6 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/async_lib/uv_async.lua @@ -0,0 +1,82 @@ +local a = require "plenary.async_lib.async" +local uv = vim.loop + +local M = {} + +local function add(name, argc) + local success, ret = pcall(a.wrap, uv[name], argc) + + if not success then + error("Failed to add function with name " .. name) + end + + M[name] = ret +end + +add("close", 4) -- close a handle + +-- filesystem operations +add("fs_open", 4) +add("fs_read", 4) +add("fs_close", 2) +add("fs_unlink", 2) +add("fs_write", 4) +add("fs_mkdir", 3) +add("fs_mkdtemp", 2) +-- 'fs_mkstemp', +add("fs_rmdir", 2) +add("fs_scandir", 2) +add("fs_stat", 2) +add("fs_fstat", 2) +add("fs_lstat", 2) +add("fs_rename", 3) +add("fs_fsync", 2) +add("fs_fdatasync", 2) +add("fs_ftruncate", 3) +add("fs_sendfile", 5) +add("fs_access", 3) +add("fs_chmod", 3) +add("fs_fchmod", 3) +add("fs_utime", 4) +add("fs_futime", 4) +-- 'fs_lutime', +add("fs_link", 3) +add("fs_symlink", 4) +add("fs_readlink", 2) +add("fs_realpath", 2) +add("fs_chown", 4) +add("fs_fchown", 4) +-- 'fs_lchown', +add("fs_copyfile", 4) +-- add('fs_opendir', 3) -- TODO: fix this one +add("fs_readdir", 2) +add("fs_closedir", 2) +-- 'fs_statfs', + +-- stream +add("shutdown", 2) +add("listen", 3) +-- add('read_start', 2) -- do not do this one, the callback is made multiple times +add("write", 3) +add("write2", 4) +add("shutdown", 2) + +-- tcp +add("tcp_connect", 4) +-- 'tcp_close_reset', + +-- pipe +add("pipe_connect", 3) + +-- udp +add("udp_send", 5) +add("udp_recv_start", 2) + +-- fs event (wip make into async await event) +-- fs poll event (wip make into async await event) + +-- dns +add("getaddrinfo", 4) +add("getnameinfo", 2) + +return M diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/benchmark/init.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/benchmark/init.lua new file mode 100644 index 0000000..ec32940 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/benchmark/init.lua @@ -0,0 +1,125 @@ +local stat = require "plenary.benchmark.stat" + +local get_stats = function(results) + local ret = {} + + ret.max, ret.min = stat.maxmin(results) + ret.mean = stat.mean(results) + ret.median = stat.median(results) + ret.std = stat.std_dev(results) + + return ret +end + +local get_output = function(index, res, runs) + -- divine with a sutable one / 1e3, 1e6, 1e9 + local time_types = { "ns", "μs", "ms" } + + local get_leading = function(time) + time = math.floor(time) + local count = 0 + repeat + time = math.floor(time / 10) + count = count + 1 + until time <= 0 + return count + end + + local get_best_fmt = function(time) + for _, v in ipairs(time_types) do + if math.abs(time) < 1000.0 then + return string.format("%s%3.1f %s", string.rep(" ", 3 - get_leading(time)), time, v) + end + time = time / 1000.0 + end + return string.format("%.1f %s", time, "s") + end + + return string.format( + "Benchmark #%d: '%s'\n Time(mean ± σ): %s ± %s\n Range(min … max): %s … %s %d runs\n", + index, + res.name, + get_best_fmt(res.stats.mean), + get_best_fmt(res.stats.std), + get_best_fmt(res.stats.min), + get_best_fmt(res.stats.max), + runs + ) +end + +local get_summary = function(res) + if #res == 1 then + return "" + end + + local fastest_mean = math.huge + local fastest_index = 1 + for i, benchmark in ipairs(res) do + if benchmark.stats.mean < fastest_mean then + fastest_mean = benchmark.stats.mean + fastest_index = i + end + end + + if fastest_mean == math.huge then + return "" + end + + local output = {} + local fastest = res[fastest_index].stats + for i, benchmark in ipairs(res) do + if i ~= fastest_index then + local result = benchmark.stats + local ratio = result.mean / fastest.mean + + -- // https://en.wikipedia.org/wiki/Propagation_of_uncertainty#Example_formulas + -- // Covariance asssumed to be 0, i.e. variables are assumed to be independent + local ratio_std = ratio + * math.sqrt(math.pow(result.std / result.mean, 2) + math.pow(fastest.std / fastest.mean, 2)) + + table.insert(output, string.format(" %.1f ± %.1f times faster than '%s'\n", ratio, ratio_std, benchmark.name)) + end + end + + return string.format("Summary\n '%s' ran\n%s", res[fastest_index].name, table.concat(output, "")) +end + +---@class benchmark_run_opts +---@field warmup number @number of initial runs before starting to track time. +---@field runs number @number of runs to make +---@field fun table> @functions to execute + +---Benchmark a function +---@param name string @benchmark name +---@param opts benchmark_run_opts +local bench = function(name, opts) + vim.validate { + opts = { opts, "table" }, + fun = { opts.fun, "table" }, + } + opts.warmup = vim.F.if_nil(opts.warmup, 3) + opts.runs = vim.F.if_nil(opts.runs, 5) + + opts.fun = type(opts.fun) == "function" and { opts.fun } or opts.fun + local output = { string.format("Benchmark Group: '%s' -----------------------\n", name) } + local res = {} + for i, fun in ipairs(opts.fun) do + res[i] = { name = fun[1], results = {} } + for _ = 1, opts.warmup do + fun[2]() + end + for j = 1, opts.runs do + local start = vim.loop.hrtime() + fun[2]() + res[i].results[j] = vim.loop.hrtime() - start + end + res[i].stats = get_stats(res[i].results) + table.insert(output, get_output(i, res[i], opts.runs)) + end + + print(string.format("%s\n%s", table.concat(output, ""), get_summary(res))) + + return res +end + +return bench diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/benchmark/stat.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/benchmark/stat.lua new file mode 100644 index 0000000..63c1f6b --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/benchmark/stat.lua @@ -0,0 +1,86 @@ +local stat = {} + +---Calculate mean +---@param t number[] @double +---@return number @double +stat.mean = function(t) + local sum = 0 + local count = 0 + + for _, v in pairs(t) do + if type(v) == "number" then + sum = sum + v + count = count + 1 + end + end + + return (sum / count) +end + +-- Get the median of a table. +---@param t number[] +---@return number +stat.median = function(t) + local temp = {} + + -- deep copy table so that when we sort it, the original is unchanged + -- also weed out any non numbers + for _, v in pairs(t) do + if type(v) == "number" then + table.insert(temp, v) + end + end + + table.sort(temp) + + -- If we have an even number of table elements or odd. + if math.fmod(#temp, 2) == 0 then + -- return mean value of middle two elements + return (temp[#temp / 2] + temp[(#temp / 2) + 1]) / 2 + else + -- return middle element + return temp[math.ceil(#temp / 2)] + end +end + +--- Get the standard deviation of a table +---@param t number[] +stat.std_dev = function(t) + local m, vm, result + local sum = 0 + local count = 0 + + m = stat.mean(t) + + for _, v in pairs(t) do + if type(v) == "number" then + vm = v - m + sum = sum + (vm * vm) + count = count + 1 + end + end + + result = math.sqrt(sum / (count - 1)) + + return result +end + +---Get the max and min for a table +---@param t number[] +---@return number +---@return number +stat.maxmin = function(t) + local max = -math.huge + local min = math.huge + + for _, v in pairs(t) do + if type(v) == "number" then + max = math.max(max, v) + min = math.min(min, v) + end + end + + return max, min +end + +return stat diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/bit.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/bit.lua new file mode 100644 index 0000000..68a6091 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/bit.lua @@ -0,0 +1,339 @@ +-- Shortcircuit to returning bit if it already exists +if bit then return bit end + +--[[ + +Credit: https://github.com/davidm/lua-bit-numberlua/blob/master/lmod/bit/numberlua.lua + +LUA MODULE + + bit.numberlua - Bitwise operations implemented in pure Lua as numbers, + with Lua 5.2 'bit32' and (LuaJIT) LuaBitOp 'bit' compatibility interfaces. + +SYNOPSIS + + local bit = require 'bit.numberlua' + print(bit.band(0xff00ff00, 0x00ff00ff)) --> 0xffffffff + + -- Interface providing strong (LuaJIT) LuaBitOp 'bit' compatibility + local bit = require 'plenary.bit' + assert(bit.tobit(0xffffffff) == -1) + + REMOVED! + -- Interface providing strong Lua 5.2 'bit32' compatibility + local bit32 = require 'bit.numberlua'.bit32 + assert(bit32.band(-1) == 0xffffffff) + + +DESCRIPTION + + This library implements bitwise operations entirely in Lua. + This module is typically intended if for some reasons you don't want + to or cannot install a popular C based bit library like BitOp 'bit' [1] + (which comes pre-installed with LuaJIT) or 'bit32' (which comes + pre-installed with Lua 5.2) but want a similar interface. + + This modules represents bit arrays as non-negative Lua numbers. [1] + It can represent 32-bit bit arrays when Lua is compiled + with lua_Number as double-precision IEEE 754 floating point. + + The module is nearly the most efficient it can be but may be a few times + slower than the C based bit libraries and is orders or magnitude + slower than LuaJIT bit operations, which compile to native code. Therefore, + this library is inferior in performane to the other modules. + + The `xor` function in this module is based partly on Roberto Ierusalimschy's + post in http://lua-users.org/lists/lua-l/2002-09/msg00134.html . + + The included BIT.bit32 and BIT.bit sublibraries aims to provide 100% + compatibility with the Lua 5.2 "bit32" and (LuaJIT) LuaBitOp "bit" library. + This compatbility is at the cost of some efficiency since inputted + numbers are normalized and more general forms (e.g. multi-argument + bitwise operators) are supported. + +STATUS + + WARNING: Not all corner cases have been tested and documented. + Some attempt was made to make these similar to the Lua 5.2 [2] + and LuaJit BitOp [3] libraries, but this is not fully tested and there + are currently some differences. Addressing these differences may + be improved in the future but it is not yet fully determined how to + resolve these differences. + + The BIT.bit32 library passes the Lua 5.2 test suite (bitwise.lua) + http://www.lua.org/tests/5.2/ . The BIT.bit library passes the LuaBitOp + test suite (bittest.lua). However, these have not been tested on + platforms with Lua compiled with 32-bit integer numbers. + +API + + Module's return + + This table contains functions that aim to provide 100% compatibility + with the LuaBitOp "bit" library (from LuaJIT). + + bit.tobit(x) --> y + bit.tohex(x [,n]) --> y + bit.bnot(x) --> y + bit.bor(x1 [,x2...]) --> y + bit.band(x1 [,x2...]) --> y + bit.bxor(x1 [,x2...]) --> y + bit.lshift(x, n) --> y + bit.rshift(x, n) --> y + bit.arshift(x, n) --> y + bit.rol(x, n) --> y + bit.ror(x, n) --> y + bit.bswap(x) --> y + +DEPENDENCIES + + None (other than Lua 5.1 or 5.2). + +REFERENCES + + [1] http://lua-users.org/wiki/FloatingPoint + [2] http://www.lua.org/manual/5.2/ + [3] http://bitop.luajit.org/ + +LICENSE + + (c) 2008-2011 David Manura. Licensed under the same terms as Lua (MIT). + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + (end license) + + Some modifications by plenary team. + +--]] + +local M = {_TYPE='module', _NAME='bit.numberlua', _VERSION='0.3.1.20120131'} + +local floor = math.floor + +local MOD = 2^32 +local MODM = MOD-1 + +local function memoize(f) + local mt = {} + local t = setmetatable({}, mt) + function mt:__index(k) + local v = f(k); t[k] = v + return v + end + return t +end + +local function make_bitop_uncached(t, m) + local function bitop(a, b) + local res,p = 0,1 + while a ~= 0 and b ~= 0 do + local am, bm = a%m, b%m + res = res + t[am][bm]*p + a = (a - am) / m + b = (b - bm) / m + p = p*m + end + res = res + (a+b)*p + return res + end + return bitop +end + +local function make_bitop(t) + local op1 = make_bitop_uncached(t,2^1) + local op2 = memoize(function(a) + return memoize(function(b) + return op1(a, b) + end) + end) + return make_bitop_uncached(op2, 2^(t.n or 1)) +end + +-- ok? probably not if running on a 32-bit int Lua number type platform +function M.tobit(x) + return x % 2^32 +end + +M.bxor = make_bitop {[0]={[0]=0,[1]=1},[1]={[0]=1,[1]=0}, n=4} +local bxor = M.bxor + +function M.bnot(a) return MODM - a end +local bnot = M.bnot + +function M.band(a,b) return ((a+b) - bxor(a,b))/2 end +local band = M.band + +function M.bor(a,b) return MODM - band(MODM - a, MODM - b) end +local bor = M.bor + +local lshift, rshift -- forward declare + +function M.rshift(a,disp) -- Lua5.2 insipred + if disp < 0 then return lshift(a,-disp) end + return floor(a % 2^32 / 2^disp) +end +rshift = M.rshift + +function M.lshift(a,disp) -- Lua5.2 inspired + if disp < 0 then return rshift(a,-disp) end + return (a * 2^disp) % 2^32 +end +lshift = M.lshift + +function M.tohex(x, n) -- BitOp style + n = n or 8 + local up + if n <= 0 then + if n == 0 then return '' end + up = true + n = - n + end + x = band(x, 16^n-1) + return ('%0'..n..(up and 'X' or 'x')):format(x) +end +local tohex = M.tohex + +function M.extract(n, field, width) -- Lua5.2 inspired + width = width or 1 + return band(rshift(n, field), 2^width-1) +end + +function M.replace(n, v, field, width) -- Lua5.2 inspired + width = width or 1 + local mask1 = 2^width-1 + v = band(v, mask1) -- required by spec? + local mask = bnot(lshift(mask1, field)) + return band(n, mask) + lshift(v, field) +end + +function M.bswap(x) -- BitOp style + local a = band(x, 0xff); x = rshift(x, 8) + local b = band(x, 0xff); x = rshift(x, 8) + local c = band(x, 0xff); x = rshift(x, 8) + local d = band(x, 0xff) + return lshift(lshift(lshift(a, 8) + b, 8) + c, 8) + d +end +local bswap = M.bswap + +function M.rrotate(x, disp) -- Lua5.2 inspired + disp = disp % 32 + local low = band(x, 2^disp-1) + return rshift(x, disp) + lshift(low, 32-disp) +end +local rrotate = M.rrotate + +function M.lrotate(x, disp) -- Lua5.2 inspired + return rrotate(x, -disp) +end +local lrotate = M.lrotate + +M.rol = M.lrotate -- LuaOp inspired +M.ror = M.rrotate -- LuaOp insipred + + +function M.arshift(x, disp) -- Lua5.2 inspired + local z = rshift(x, disp) + if x >= 0x80000000 then z = z + lshift(2^disp-1, 32-disp) end + return z +end +local arshift = M.arshift + +function M.btest(x, y) -- Lua5.2 inspired + return band(x, y) ~= 0 +end + +-- +-- Start LuaBitOp "bit" compat section. +-- + +M.bit = {} -- LuaBitOp "bit" compatibility + +function M.bit.tobit(x) + x = x % MOD + if x >= 0x80000000 then x = x - MOD end + return x +end +local bit_tobit = M.bit.tobit + +function M.bit.tohex(x, ...) + return tohex(x % MOD, ...) +end + +function M.bit.bnot(x) + return bit_tobit(bnot(x % MOD)) +end + +local function bit_bor(a, b, c, ...) + if c then + return bit_bor(bit_bor(a, b), c, ...) + elseif b then + return bit_tobit(bor(a % MOD, b % MOD)) + else + return bit_tobit(a) + end +end +M.bit.bor = bit_bor + +local function bit_band(a, b, c, ...) + if c then + return bit_band(bit_band(a, b), c, ...) + elseif b then + return bit_tobit(band(a % MOD, b % MOD)) + else + return bit_tobit(a) + end +end +M.bit.band = bit_band + +local function bit_bxor(a, b, c, ...) + if c then + return bit_bxor(bit_bxor(a, b), c, ...) + elseif b then + return bit_tobit(bxor(a % MOD, b % MOD)) + else + return bit_tobit(a) + end +end +M.bit.bxor = bit_bxor + +function M.bit.lshift(x, n) + return bit_tobit(lshift(x % MOD, n % 32)) +end + +function M.bit.rshift(x, n) + return bit_tobit(rshift(x % MOD, n % 32)) +end + +function M.bit.arshift(x, n) + return bit_tobit(arshift(x % MOD, n % 32)) +end + +function M.bit.rol(x, n) + return bit_tobit(lrotate(x % MOD, n % 32)) +end + +function M.bit.ror(x, n) + return bit_tobit(rrotate(x % MOD, n % 32)) +end + +function M.bit.bswap(x) + return bit_tobit(bswap(x % MOD)) +end + +return M.bit diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/busted.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/busted.lua new file mode 100644 index 0000000..ed1b68d --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/busted.lua @@ -0,0 +1,271 @@ +local dirname = function(p) + return vim.fn.fnamemodify(p, ":h") +end + +local function get_trace(element, level, msg) + local function trimTrace(info) + local index = info.traceback:find "\n%s*%[C]" + info.traceback = info.traceback:sub(1, index) + return info + end + level = level or 3 + + local thisdir = dirname(debug.getinfo(1, "Sl").source, ":h") + local info = debug.getinfo(level, "Sl") + while + info.what == "C" + or info.short_src:match "luassert[/\\].*%.lua$" + or (info.source:sub(1, 1) == "@" and thisdir == dirname(info.source)) + do + level = level + 1 + info = debug.getinfo(level, "Sl") + end + + info.traceback = debug.traceback("", level) + info.message = msg + + -- local file = busted.getFile(element) + local file = false + return file and file.getTrace(file.name, info) or trimTrace(info) +end + +local is_headless = require("plenary.nvim_meta").is_headless + +-- We are shadowing print so people can reliably print messages +print = function(...) + for _, v in ipairs { ... } do + io.stdout:write(tostring(v)) + io.stdout:write "\t" + end + + io.stdout:write "\r\n" +end + +local mod = {} + +local results = {} +local current_description = {} +local current_before_each = {} +local current_after_each = {} + +local add_description = function(desc) + table.insert(current_description, desc) + + return vim.deepcopy(current_description) +end + +local pop_description = function() + current_description[#current_description] = nil +end + +local add_new_each = function() + current_before_each[#current_description] = {} + current_after_each[#current_description] = {} +end + +local clear_last_each = function() + current_before_each[#current_description] = nil + current_after_each[#current_description] = nil +end + +local call_inner = function(desc, func) + local desc_stack = add_description(desc) + add_new_each() + local ok, msg = xpcall(func, function(msg) + -- debug.traceback + -- return vim.inspect(get_trace(nil, 3, msg)) + local trace = get_trace(nil, 3, msg) + return trace.message .. "\n" .. trace.traceback + end) + clear_last_each() + pop_description() + + return ok, msg, desc_stack +end + +local color_table = { + yellow = 33, + green = 32, + red = 31, +} + +local color_string = function(color, str) + if not is_headless then + return str + end + + return string.format("%s[%sm%s%s[%sm", string.char(27), color_table[color] or 0, str, string.char(27), 0) +end + +local SUCCESS = color_string("green", "Success") +local FAIL = color_string("red", "Fail") +local PENDING = color_string("yellow", "Pending") + +local HEADER = string.rep("=", 40) + +mod.format_results = function(res) + print "" + print(color_string("green", "Success: "), #res.pass) + print(color_string("red", "Failed : "), #res.fail) + print(color_string("red", "Errors : "), #res.errs) + print(HEADER) +end + +mod.describe = function(desc, func) + results.pass = results.pass or {} + results.fail = results.fail or {} + results.errs = results.errs or {} + + describe = mod.inner_describe + local ok, msg, desc_stack = call_inner(desc, func) + describe = mod.describe + + if not ok then + table.insert(results.errs, { + descriptions = desc_stack, + msg = msg, + }) + end +end + +mod.inner_describe = function(desc, func) + local ok, msg, desc_stack = call_inner(desc, func) + + if not ok then + table.insert(results.errs, { + descriptions = desc_stack, + msg = msg, + }) + end +end + +mod.before_each = function(fn) + table.insert(current_before_each[#current_description], fn) +end + +mod.after_each = function(fn) + table.insert(current_after_each[#current_description], fn) +end + +mod.clear = function() + vim.api.nvim_buf_set_lines(0, 0, -1, false, {}) +end + +local indent = function(msg, spaces) + if spaces == nil then + spaces = 4 + end + + local prefix = string.rep(" ", spaces) + return prefix .. msg:gsub("\n", "\n" .. prefix) +end + +local run_each = function(tbl) + for _, v in ipairs(tbl) do + for _, w in ipairs(v) do + if type(w) == "function" then + w() + end + end + end +end + +mod.it = function(desc, func) + run_each(current_before_each) + local ok, msg, desc_stack = call_inner(desc, func) + run_each(current_after_each) + + local test_result = { + descriptions = desc_stack, + msg = nil, + } + + -- TODO: We should figure out how to determine whether + -- and assert failed or whether it was an error... + + local to_insert + if not ok then + to_insert = results.fail + test_result.msg = msg + + print(FAIL, "||", table.concat(test_result.descriptions, " ")) + print(indent(msg, 12)) + else + to_insert = results.pass + print(SUCCESS, "||", table.concat(test_result.descriptions, " ")) + end + + table.insert(to_insert, test_result) +end + +mod.pending = function(desc, func) + local curr_stack = vim.deepcopy(current_description) + table.insert(curr_stack, desc) + print(PENDING, "||", table.concat(curr_stack, " ")) +end + +_PlenaryBustedOldAssert = _PlenaryBustedOldAssert or assert + +describe = mod.describe +it = mod.it +pending = mod.pending +before_each = mod.before_each +after_each = mod.after_each +clear = mod.clear +---@type Luassert +assert = require "luassert" + +mod.run = function(file) + file = file:gsub("\\", "/") + + print("\n" .. HEADER) + print("Testing: ", file) + + local loaded, msg = loadfile(file) + + if not loaded then + print(HEADER) + print "FAILED TO LOAD FILE" + print(color_string("red", msg)) + print(HEADER) + if is_headless then + return vim.cmd "2cq" + else + return + end + end + + coroutine.wrap(function() + loaded() + + -- If nothing runs (empty file without top level describe) + if not results.pass then + if is_headless then + return vim.cmd "0cq" + else + return + end + end + + mod.format_results(results) + + if #results.errs ~= 0 then + print("We had an unexpected error: ", vim.inspect(results.errs), vim.inspect(results)) + if is_headless then + return vim.cmd "2cq" + end + elseif #results.fail > 0 then + print "Tests Failed. Exit: 1" + + if is_headless then + return vim.cmd "1cq" + end + else + if is_headless then + return vim.cmd "0cq" + end + end + end)() +end + +return mod diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/class.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/class.lua new file mode 100644 index 0000000..e82f073 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/class.lua @@ -0,0 +1,80 @@ +---@brief [[ +---classic +--- +---Copyright (c) 2014, rxi +---@brief ]] + +---@class Object +local Object = {} +Object.__index = Object + +---Does nothing. +---You have to implement this yourself for extra functionality when initializing +---@param self Object +function Object:new() end + +---Create a new class/object by extending the base Object class. +---The extended object will have a field called `super` that will access the super class. +---@param self Object +---@return Object +function Object:extend() + local cls = {} + for k, v in pairs(self) do + if k:find "__" == 1 then + cls[k] = v + end + end + cls.__index = cls + cls.super = self + setmetatable(cls, self) + return cls +end + +---Implement a mixin onto this Object. +---@param self Object +---@param nil ... +function Object:implement(...) + for _, cls in pairs { ... } do + for k, v in pairs(cls) do + if self[k] == nil and type(v) == "function" then + self[k] = v + end + end + end +end + +---Checks if the object is an instance +---This will start with the lowest class and loop over all the superclasses. +---@param self Object +---@param T Object +---@return boolean +function Object:is(T) + local mt = getmetatable(self) + while mt do + if mt == T then + return true + end + mt = getmetatable(mt) + end + return false +end + +---The default tostring implementation for an object. +---You can override this to provide a different tostring. +---@param self Object +---@return string +function Object:__tostring() + return "Object" +end + +---You can call the class the initialize it without using `Object:new`. +---@param self Object +---@param nil ... +---@return Object +function Object:__call(...) + local obj = setmetatable({}, self) + obj:new(...) + return obj +end + +return Object diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/collections/py_list.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/collections/py_list.lua new file mode 100644 index 0000000..25df4b0 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/collections/py_list.lua @@ -0,0 +1,393 @@ +---@brief [[ +--- This module implements python-like lists. It can be used like so: +---
+---     local List = require 'plenary.collections.py_list'
+---     local l = List{3, 20, 44}
+---     print(l)  -- [3, 20, 44]
+--- 
+---@brief ]] +local List = {} + +---@class List @The base class for all list objects + +---List constructor. Can be used in higher order functions +---@param tbl table: A list-like table containing the initial elements of the list +---@return List: A new list object +function List.new(tbl) + if type(tbl) == "table" then + local len = #tbl + local obj = setmetatable(tbl, List) + obj._len = len + return obj + end + error "List constructor must be called with table argument" +end + +--- Checks whether the argument is a List object +--- @param tbl table: The object to test +--- @return boolean: Whether tbl is an instance of List +function List.is_list(tbl) + local meta = getmetatable(tbl) or {} + return meta == List +end + +function List:__index(key) + if self ~= List then + local field = List[key] + if field then + return field + end + end +end + +-- TODO: Similar to python, use [...] if the table references itself -- +function List:__tostring() + local elements = self:join ", " + return "[" .. elements .. "]" +end + +function List:__eq(other) + if #self ~= #other then + return false + end + for i = 1, #self do + if self[i] ~= other[i] then + return false + end + end + return true +end + +function List:__mul(other) + local result = List.new {} + for i = 1, other do + result[i] = self + end + return result +end + +function List:__len() + return self._len +end + +function List:__concat(other) + return self:concat(other) +end + +--- Pushes the element to the end of the list +--- @param other any: The object to append +--- @see List.pop +function List:push(other) + self[#self + 1] = other + self._len = self._len + 1 +end + +--- Pops the last element off the list and returns it +--- @return any: The (previously) last element from the list +--- @see List.push +function List:pop() + local result = table.remove(self) + self._len = self._len - 1 + return result +end + +--- Inserts other into the specified idx +--- @param idx number: The index that other will be inserted to +--- @param other any: The element to insert +--- @see List.remove +function List:insert(idx, other) + table.insert(self, idx, other) + self._len = self._len + 1 +end + +--- Removes the element at index idx and returns it +--- @param idx number: The index of the element to remove +--- @return any: The element previously at index idx +--- @see List.insert +function List:remove(idx) + self._len = self._len - 1 + return table.remove(self, idx) +end + +--- Can be used to compare elements with any list-like table. It only checks for +--- shallow equality +--- @param other any: The element to test for +--- @return boolean: True if other is a list object and all it's elements are equal +--- @see List.deep_equal +function List:equal(other) + return self:__eq(other) +end + +--- Checks for deep equality between lists. This uses vim.deep_equal for testing +--- @param other any: The element to test for +--- @return boolean: True if all elements and their children are equal +--- @see List.equal +--- @see vim.deep_equal +function List:deep_equal(other) + return vim.deep_equal(self, other) +end + +--- Returns a copy of the list with elements between a and b, inclusive +---
+---     local list = List{1, 2, 3, 4}
+---     local slice = list:slice(2, 3)
+---     print(slice) -- [2, 3]
+--- 
+--- @param a number: The low end of the slice +--- @param b number: The high end of the slice +--- @return List: A list with elements between a and b +function List:slice(a, b) + return List.new(vim.list_slice(self, a, b)) +end + +--- Similar to slice, but with every element. It only makes a shallow copy +--- @return List: A slice from 1 to #self, i.e., a complete copy of the list +--- @see List.deep_copy +function List:copy() + return self:slice(1, #self) +end + +--- Similar to copy, but makes a deep copy instead +--- @return List: A deep copy of the object +--- @see List.copy +--- @see vim.deep_copy +function List:deep_copy() + return vim.deep_copy(self) +end + +--- Reverses the list in place. If you don't want this, you could do something +--- like this +---
+---     local list = List{1, 2, 3, 4}
+---     local reversed = list:copy():reverse()
+--- 
+--- @return List: The list itself, so you can chain method calls +--- @see List.copy +--- @see List.deep_copy +function List:reverse() + local n = #self + local i = 1 + while i < n do + self[i], self[n] = self[n], self[i] + i = i + 1 + n = n - 1 + end + return self +end + +--- Concatenates the elements whithin the list separated by the given string +---
+---     local list = List{1, 2, 3, 4}
+---     print(list:join('-'))  -- 1-2-3-4
+--- 
+--- @param sep string: The separator to place between the elements. Default '' +--- @return string: The elements in the list separated by sep +function List:join(sep) + sep = sep or "" + local result = "" + for i, v in self:iter() do + result = result .. tostring(v) + if i ~= #self then + result = result .. sep + end + end + return result +end + +--- Returns a list with the elements of self concatenated with those in the +--- given arguments +--- @vararg table|List: The sequences to concatenate to this one +--- @return List +function List:concat(...) + local result = self:copy() + local others = { ... } + for _, other in ipairs(others) do + for _, v in ipairs(other) do + result:push(v) + end + end + return result +end + +--- Moves the elements between from and from+len in self, to positions between +--- to and to+len in other, like so +---
+---     other[to], other[to+1]... other[to+len] = self[from], self[from+1]... self[from+len]
+--- 
+--- @param from number: The first index of the origin slice +--- @param len number: The length of the slices +--- @param to number: The first index of the destination slice +--- @param other table|List: The destination list. Defaults to self +--- @see table.move +function List:move(from, len, to, other) + return table.move(self, from, len, to, other) +end + +--- Packs the given elements into a list. Similar to lua 5.3's table.pack +--- @vararg any: The elements to pack +--- @return List: a list containing all the given elements +--- @see table.pack +function List.pack(...) + return List.new { ... } +end + +--- Unpacks the elements from this list and returns them +--- @return ...any: All the elements from self[1] to self[#self] +function List:unpack() + return unpack(self, 1, #self) +end + +-- Iterator stuff + +local Iter = require "plenary.iterators" + +local itermetatable = getmetatable(Iter:wrap()) + +local function forward_list_gen(param, state) + state = state + 1 + local v = param[state] + if v ~= nil then + return state, v + end +end + +local function backward_list_gen(param, state) + state = state - 1 + local v = param[state] + if v ~= nil then + return state, v + end +end + +--- Run the given predicate through all the elements pointed by this iterator, +--- and classify them into two lists. The first one holds the elements for which +--- predicate returned a truthy value, and the second holds the rest. For +--- example: +--- +---
+---     local list = List{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
+---     local evens, odds = list:iter():partition(function(e)
+---         return e % 2 == 0
+---     end)
+---     print(evens, odds)
+--- 
+--- +--- Would print +--- +---
+---     [0, 2, 4, 6, 8] [1, 3, 5, 7, 9]
+--- 
+---@param predicate function: The predicate to classify the elements +---@return List,List +local function partition(self, predicate) + local list1, list2 = List.new {}, List.new {} + for _, v in self do + if predicate(v) then + list1:push(v) + else + list2:push(v) + end + end + return list1, list2 +end + +local function wrap_iter(f, l, n) + local iter = Iter.wrap(f, l, n) + iter.partition = partition + return iter +end + +--- Counts the occurrences of e inside the list +--- @param e any: The element to test for +--- @return number: The number of occurrences of e +function List:count(e) + local count = 0 + for _, v in self:iter() do + if e == v then + count = count + 1 + end + end + return count +end + +--- Appends the elements in the given iterator to the list +--- @param other table: An iterator object +function List:extend(other) + if type(other) == "table" and getmetatable(other) == itermetatable then + for _, v in other do + self:push(v) + end + else + error "Argument must be an iterator" + end +end + +--- Checks whether there is an occurence of the given element in the list +--- @param e any: The object to test for +--- @return boolean: True if e is present +function List:contains(e) + for _, v in self:iter() do + if v == e then + return true + end + end + return false +end + +--- Creates an iterator for the list. For example: +---
+---     local list = List{8, 4, 7, 9}
+---     for i, v in list:iter() do
+---         print(i, v)
+---     end
+--- 
+--- Would print: +---
+---     1    8
+---     2    4
+---     3    7
+---     4    9
+--- 
+--- @return table: An iterator object +function List:iter() + return wrap_iter(forward_list_gen, self, 0) +end + +--- Creates a reverse iterator for the list. For example: +---
+---     local list = List{8, 4, 7, 9}
+---     for i, v in list:riter() do
+---         print(i, v)
+---     end
+--- 
+--- Would print: +---
+---     4    9
+---     3    7
+---     2    4
+---     1    8
+--- 
+--- @return table: An iterator object +function List:riter() + return wrap_iter(backward_list_gen, self, #self + 1) +end + +-- Miscellaneous + +--- Create a list from the elements pointed at by the given iterator. +--- @param iter table: An iterator object +--- @return List +function List.from_iter(iter) + local result = List.new {} + for _, v in iter do + result:push(v) + end + return result +end + +return setmetatable({}, { + __call = function(_, tbl) + return List.new(tbl) + end, + __index = List, +}) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/compat.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/compat.lua new file mode 100644 index 0000000..5e7e24d --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/compat.lua @@ -0,0 +1,17 @@ +local m = {} + +m.flatten = (function() + if vim.fn.has "nvim-0.11" == 1 then + return function(t) + return vim.iter(t):flatten():totable() + end + else + return function(t) + return vim.tbl_flatten(t) + end + end +end)() + +m.islist = vim.islist or vim.tbl_islist + +return m diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/context_manager.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/context_manager.lua new file mode 100644 index 0000000..7d617da --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/context_manager.lua @@ -0,0 +1,56 @@ +--- I like context managers for Python +--- I want them in Lua. + +local context_manager = {} + +function context_manager.with(obj, callable) + -- Wrap functions for people since we're nice + if type(obj) == "function" then + obj = coroutine.create(obj) + end + + if type(obj) == "thread" then + local ok, context = coroutine.resume(obj) + assert(ok, "Should have yielded in coroutine.") + + local succeeded, result = pcall(callable, context) + + local done, _ = coroutine.resume(obj) + assert(done, "Should be done") + + local no_other = not coroutine.resume(obj) + assert(no_other, "Should not yield anymore, otherwise that would make things complicated") + + assert(succeeded, result) + return result + else + assert(obj.enter) + assert(obj.exit) + + -- TODO: Callable can be string for vimL function or a lua callable + local context = obj:enter() + local succeeded, result = pcall(callable, context) + obj:exit() + + assert(succeeded, result) + return result + end +end + +--- @param filename string|table -- If string, used as io.open(filename) +--- Else, should be a table with `filename` as an attribute +function context_manager.open(filename, mode) + if type(filename) == "table" and filename.filename then + filename = filename.filename + end + + local file_io = assert(io.open(filename, mode)) + + return coroutine.create(function() + coroutine.yield(file_io) + + file_io:close() + end) +end + +return context_manager diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/curl.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/curl.lua new file mode 100644 index 0000000..321bed8 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/curl.lua @@ -0,0 +1,359 @@ +--[[ +Curl Wrapper + +all curl methods accepts + + url = "The url to make the request to.", (string) + query = "url query, append after the url", (table) + body = "The request body" (string/filepath/table) + auth = "Basic request auth, 'user:pass', or {"user", "pass"}" (string/array) + form = "request form" (table) + raw = "any additonal curl args, it must be an array/list." (array) + dry_run = "whether to return the args to be ran through curl." (boolean) + output = "where to download something." (filepath) + timeout = "request timeout in mseconds" (number) + http_version = "HTTP version to use: 'HTTP/0.9', 'HTTP/1.0', 'HTTP/1.1', 'HTTP/2', or 'HTTP/3'" (string) + proxy = "[protocol://]host[:port] Use this proxy" (string) + insecure = "Allow insecure server connections" (boolean) + +and returns table: + + exit = "The shell process exit code." (number) + status = "The https response status." (number) + headers = "The https response headers." (array) + body = "The http response body." (string) + +see test/plenary/curl_spec.lua for examples. + +author = github.com/tami5 +]] +-- + +local util, parse = {}, {} + +-- Helpers -------------------------------------------------- +------------------------------------------------------------- +local F = require "plenary.functional" +local J = require "plenary.job" +local P = require "plenary.path" +local compat = require "plenary.compat" + +-- Utils ---------------------------------------------------- +------------------------------------------------------------- + +util.url_encode = function(str) + if type(str) ~= "number" then + str = str:gsub("\r?\n", "\r\n") + str = str:gsub("([^%w%-%.%_%~ ])", function(c) + return string.format("%%%02X", c:byte()) + end) + str = str:gsub(" ", "+") + return str + else + return str + end +end + +util.kv_to_list = function(kv, prefix, sep) + return compat.flatten(F.kv_map(function(kvp) + return { prefix, kvp[1] .. sep .. kvp[2] } + end, kv)) +end + +util.kv_to_str = function(kv, sep, kvsep) + return F.join( + F.kv_map(function(kvp) + return kvp[1] .. kvsep .. util.url_encode(kvp[2]) + end, kv), + sep + ) +end + +util.gen_dump_path = function() + local path + local id = string.gsub("xxxx4xxx", "[xy]", function(l) + local v = (l == "x") and math.random(0, 0xf) or math.random(0, 0xb) + return string.format("%x", v) + end) + if P.path.sep == "\\" then + path = string.format("%s\\AppData\\Local\\Temp\\plenary_curl_%s.headers", os.getenv "USERPROFILE", id) + else + local temp_dir = os.getenv "XDG_RUNTIME_DIR" or "/tmp" + path = temp_dir .. "/plenary_curl_" .. id .. ".headers" + end + return { "-D", path } +end + +-- Parsers ---------------------------------------------------- +--------------------------------------------------------------- + +parse.headers = function(t) + if not t then + return + end + local upper = function(str) + return string.gsub(" " .. str, "%W%l", string.upper):sub(2) + end + return util.kv_to_list( + (function() + local normilzed = {} + for k, v in pairs(t) do + normilzed[upper(k:gsub("_", "%-"))] = v + end + return normilzed + end)(), + "-H", + ": " + ) +end + +parse.data_body = function(t) + if not t then + return + end + return util.kv_to_list(t, "-d", "=") +end + +parse.raw_body = function(xs) + if not xs then + return + end + if type(xs) == "table" then + return parse.data_body(xs) + else + return { "--data-raw", xs } + end +end + +parse.form = function(t) + if not t then + return + end + return util.kv_to_list(t, "-F", "=") +end + +parse.curl_query = function(t) + if not t then + return + end + return util.kv_to_str(t, "&", "=") +end + +parse.method = function(s) + if not s then + return + end + if s ~= "head" then + return { "-X", string.upper(s) } + else + return { "-I" } + end +end + +parse.file = function(p) + if not p then + return + end + return { "-d", "@" .. P.expand(P.new(p)) } +end + +parse.auth = function(xs) + if not xs then + return + end + return { "-u", type(xs) == "table" and util.kv_to_str(xs, nil, ":") or xs } +end + +parse.url = function(xs, q) + if not xs then + return + end + q = parse.curl_query(q) + if type(xs) == "string" then + return q and xs .. "?" .. q or xs + elseif type(xs) == "table" then + error "Low level URL definition is not supported." + end +end + +parse.accept_header = function(s) + if not s then + return + end + return { "-H", "Accept: " .. s } +end + +parse.http_version = function(s) + if not s then + return + end + if s == "HTTP/0.9" or s == "HTTP/1.0" or s == "HTTP/1.1" or s == "HTTP/2" or s == "HTTP/3" then + s = s:lower() + s = s:gsub("/", "") + return { "--" .. s } + else + error "Unknown HTTP version." + end +end + +-- Parse Request ------------------------------------------- +------------------------------------------------------------ +parse.request = function(opts) + if opts.body then + local b = opts.body + local silent_is_file = function() + local status, result = pcall(P.is_file, P.new(b)) + return status and result + end + opts.body = nil + if type(b) == "table" then + opts.data = b + elseif silent_is_file() then + opts.in_file = b + elseif type(b) == "string" then + opts.raw_body = b + end + end + local result = { "-sSL", opts.dump } + local append = function(v) + if v then + table.insert(result, v) + end + end + + if opts.insecure then + table.insert(result, "--insecure") + end + if opts.proxy then + table.insert(result, { "--proxy", opts.proxy }) + end + if opts.compressed then + table.insert(result, "--compressed") + end + append(parse.method(opts.method)) + append(parse.headers(opts.headers)) + append(parse.accept_header(opts.accept)) + append(parse.raw_body(opts.raw_body)) + append(parse.data_body(opts.data)) + append(parse.form(opts.form)) + append(parse.file(opts.in_file)) + append(parse.auth(opts.auth)) + append(parse.http_version(opts.http_version)) + append(opts.raw) + if opts.output then + table.insert(result, { "-o", opts.output }) + end + table.insert(result, parse.url(opts.url, opts.query)) + return compat.flatten(result), opts +end + +-- Parse response ------------------------------------------ +------------------------------------------------------------ +parse.response = function(lines, dump_path, code) + local headers = P.readlines(dump_path) + local status = nil + local processed_headers = {} + + -- Process headers in a single pass + for _, line in ipairs(headers) do + local status_match = line:match "^HTTP/%S*%s+(%d+)" + if status_match then + status = tonumber(status_match) + elseif line ~= "" then + table.insert(processed_headers, line) + end + end + + local body = F.join(lines, "\n") + vim.loop.fs_unlink(dump_path) + + return { + status = status or 0, + headers = processed_headers, + body = body, + exit = code, + } +end + +local request = function(specs) + local response = {} + local args, opts = parse.request(vim.tbl_extend("force", { + compressed = package.config:sub(1, 1) ~= "\\", + dry_run = false, + dump = util.gen_dump_path(), + }, specs)) + + if opts.dry_run then + return args + end + + local job_opts = { + command = vim.g.plenary_curl_bin_path or "curl", + args = args, + } + + if opts.stream then + job_opts.on_stdout = opts.stream + end + + job_opts.on_exit = function(j, code) + if code ~= 0 then + local stderr = vim.inspect(j:stderr_result()) + local message = string.format("%s %s - curl error exit_code=%s stderr=%s", opts.method, opts.url, code, stderr) + if opts.on_error then + return opts.on_error { + message = message, + stderr = stderr, + exit = code, + } + else + error(message) + end + end + local output = parse.response(j:result(), opts.dump[2], code) + if opts.callback then + return opts.callback(output) + else + response = output + end + end + local job = J:new(job_opts) + + if opts.callback or opts.stream then + job:start() + return job + else + local timeout = opts.timeout or 10000 + job:sync(timeout) + return response + end +end + +-- Main ---------------------------------------------------- +------------------------------------------------------------ +return (function() + local partial = function(method) + return function(url, opts) + local spec = {} + opts = opts or {} + if type(url) == "table" then + opts = url + spec.method = method + else + spec.url = url + spec.method = method + end + opts = method == "request" and opts or (vim.tbl_extend("keep", opts, spec)) + return request(opts) + end + end + return { + get = partial "get", + post = partial "post", + put = partial "put", + head = partial "head", + patch = partial "patch", + delete = partial "delete", + request = partial "request", + } +end)() diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/debug_utils.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/debug_utils.lua new file mode 100644 index 0000000..c4caf65 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/debug_utils.lua @@ -0,0 +1,13 @@ +local debug_utils = {} + +function debug_utils.sourced_filepath() + local str = debug.getinfo(2, "S").source:sub(2) + return str +end + +function debug_utils.sourced_filename() + local str = debug_utils.sourced_filepath() + return str:match "^.*/(.*).lua$" or str +end + +return debug_utils diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/enum.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/enum.lua new file mode 100644 index 0000000..22d4410 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/enum.lua @@ -0,0 +1,162 @@ +---@brief [[ +--- This module defines an idiomatic way to create enum classes, similar to +--- those in java or kotlin. There are two ways to create an enum, one is with +--- the exported `make_enum` function, or calling the module directly with the +--- enum spec. +--- +--- The enum spec consists of a list-like table whose members can be either a +--- string or a tuple of the form {string, number}. In the former case, the enum +--- member will take the next available value, while in the latter, the member +--- will take the string as it's name and the number as it's value. In both +--- cases, the name must start with a capital letter. +--- +--- Here is an example: +--- +---
+--- local Enum = require 'plenary.enum'
+--- local myEnum = Enum {
+---     'Foo',          -- Takes value 1
+---     'Bar',          -- Takes value 2
+---     {'Qux', 10},    -- Takes value 10
+---     'Baz',          -- Takes value 11
+--- }
+--- 
+--- +--- In case of name or value clashing, the call will fail. For this reason, it's +--- best if you define the members in ascending order. +---@brief ]] +local Enum = {} + +---@class Enum + +---@class Variant + +local function validate_member_name(name) + if #name > 0 and name:sub(1, 1):match "%u" then + return name + end + error('"' .. name .. '" should start with a capital letter') +end + +--- Creates an enum from the given list-like table, like so: +---
+--- local enum = Enum.make_enum{
+---     'Foo',
+---     'Bar',
+---     {'Qux', 10}
+--- }
+--- 
+--- @return Enum: A new enum +local function make_enum(tbl) + local enum = {} + + local Variant = {} + Variant.__index = Variant + + local function newVariant(i) + return setmetatable({ value = i }, Variant) + end + + -- we don't need __eq because the __eq metamethod will only ever be + -- invoked when they both have the same metatable + + function Variant:__lt(o) + return self.value < o.value + end + + function Variant:__gt(o) + return self.value > o.value + end + + function Variant:__tostring() + return tostring(self.value) + end + + local function find_next_idx(e, i) + local newI = i + 1 + if not e[newI] then + return newI + end + error("Overlapping index: " .. tostring(newI)) + end + + local i = 0 + + for _, v in ipairs(tbl) do + if type(v) == "string" then + local name = validate_member_name(v) + local idx = find_next_idx(enum, i) + enum[idx] = name + if enum[name] then + error("Duplicate enum member name: " .. name) + end + enum[name] = newVariant(idx) + i = idx + elseif type(v) == "table" and type(v[1]) == "string" and type(v[2]) == "number" then + local name = validate_member_name(v[1]) + local idx = v[2] + if enum[idx] then + error("Overlapping index: " .. tostring(idx)) + end + enum[idx] = name + if enum[name] then + error("Duplicate name: " .. name) + end + enum[name] = newVariant(idx) + i = idx + else + error "Invalid way to specify an enum variant" + end + end + + return require("plenary.tbl").freeze(setmetatable(enum, Enum)) +end + +Enum.__index = function(_, key) + if Enum[key] then + return Enum[key] + end + error("Invalid enum key: " .. tostring(key)) +end + +--- Checks whether the enum has a member with the given name +--- @param key string: The element to check for +--- @return boolean: True if key is present +function Enum:has_key(key) + if rawget(getmetatable(self).__index, key) then + return true + end + return false +end + +--- If there is a member named 'key', return it, otherwise return nil +--- @param key string: The element to check for +--- @return Variant: The element named by key, or nil if not present +function Enum:from_str(key) + if self:has_key(key) then + return self[key] + end +end + +--- If there is a member of value 'num', return it, otherwise return nil +--- @param num number: The value of the element to check for +--- @return Variant: The element whose value is num +function Enum:from_num(num) + local key = self[num] + if key then + return self[key] + end +end + +--- Checks whether the given object corresponds to an instance of Enum +--- @param tbl table: The object to be checked +--- @return boolean: True if tbl is an Enum +local function is_enum(tbl) + return getmetatable(getmetatable(tbl).__index) == Enum +end + +return setmetatable({ is_enum = is_enum, make_enum = make_enum }, { + __call = function(_, tbl) + return make_enum(tbl) + end, +}) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/errors.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/errors.lua new file mode 100644 index 0000000..64849f6 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/errors.lua @@ -0,0 +1,15 @@ +local M = {} + +M.traceback_error = function(s, level) + local traceback = debug.traceback() + traceback = traceback .. "\n" .. s + error(traceback, (level or 1) + 1) +end + +M.info_error = function(s, func_info, level) + local info = debug.getinfo(func_info) + info = info .. "\n" .. s + error(info, (level or 1) + 1) +end + +return M diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/filetype.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/filetype.lua new file mode 100644 index 0000000..60cd307 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/filetype.lua @@ -0,0 +1,196 @@ +local Path = require "plenary.path" + +local os_sep = Path.path.sep + +local filetype = {} + +local filetype_table = { + extension = {}, + file_name = {}, + shebang = {}, +} + +filetype.add_table = function(new_filetypes) + local valid_keys = { "extension", "file_name", "shebang" } + local new_keys = {} + + -- Validate keys + for k, _ in pairs(new_filetypes) do + new_keys[k] = true + end + for _, k in ipairs(valid_keys) do + new_keys[k] = nil + end + + for k, v in pairs(new_keys) do + error(debug.traceback("Invalid key / value:" .. tostring(k) .. " / " .. tostring(v))) + end + + if new_filetypes.extension then + filetype_table.extension = vim.tbl_extend("force", filetype_table.extension, new_filetypes.extension) + end + + if new_filetypes.file_name then + filetype_table.file_name = vim.tbl_extend("force", filetype_table.file_name, new_filetypes.file_name) + end + + if new_filetypes.shebang then + filetype_table.shebang = vim.tbl_extend("force", filetype_table.shebang, new_filetypes.shebang) + end +end + +filetype.add_file = function(filename) + local filetype_files = vim.api.nvim_get_runtime_file(string.format("data/plenary/filetypes/%s.lua", filename), true) + + for _, file in ipairs(filetype_files) do + local ok, msg = pcall(filetype.add_table, dofile(file)) + if not ok then + error("Unable to add file " .. file .. ":\n" .. msg) + end + end +end + +local filename_regex = "[^" .. os_sep .. "].*" +filetype._get_extension_parts = function(filename) + local current_match = filename:match(filename_regex) + local possibilities = {} + while current_match do + current_match = current_match:match "[^.]%.(.*)" + if current_match then + table.insert(possibilities, current_match:lower()) + else + return possibilities + end + end + return possibilities +end + +filetype._parse_modeline = function(tail) + if tail:find "vim:" then + return tail:match ".*:ft=([^: ]*):.*$" or "" + end + return "" +end + +filetype._parse_shebang = function(head) + if head:sub(1, 2) == "#!" then + local match = filetype_table.shebang[head:sub(3, #head)] + if match then + return match + end + end + return "" +end + +local done_adding = false +local extend_tbl_with_ext_eq_ft_entries = function() + if not done_adding then + if vim.in_fast_event() then + return + end + local all_valid_filetypes = vim.fn.getcompletion("", "filetype") + for _, v in ipairs(all_valid_filetypes) do + if not filetype_table.extension[v] then + filetype_table.extension[v] = v + end + end + done_adding = true + return true + end +end + +filetype.detect_from_extension = function(filepath) + local exts = filetype._get_extension_parts(filepath) + for _, ext in ipairs(exts) do + local match = ext and filetype_table.extension[ext] + if match then + return match + end + end + if extend_tbl_with_ext_eq_ft_entries() then + for _, ext in ipairs(exts) do + local match = ext and filetype_table.extension[ext] + if match then + return match + end + end + end + return "" +end + +filetype.detect_from_name = function(filepath) + if filepath then + filepath = filepath:lower() + local split_path = vim.split(filepath, os_sep, true) + local fname = split_path[#split_path] + local match = filetype_table.file_name[fname] + if match then + return match + end + end + return "" +end + +filetype.detect_from_modeline = function(filepath) + local tail = Path:new(filepath):readbyterange(-256, 256) + if not tail then + return "" + end + local lines = vim.split(tail, "\n") + local idx = lines[#lines] ~= "" and #lines or #lines - 1 + if idx >= 1 then + return filetype._parse_modeline(lines[idx]) + end +end + +filetype.detect_from_shebang = function(filepath) + local head = Path:new(filepath):readbyterange(0, 256) + if not head then + return "" + end + local lines = vim.split(head, "\n") + return filetype._parse_shebang(lines[1]) +end + +--- Detect a filetype from a path. +--- +---@param opts table: Table with optional keys +--- - fs_access (bool, default=true): Should check a file if it exists +filetype.detect = function(filepath, opts) + opts = opts or {} + opts.fs_access = opts.fs_access or true + + if type(filepath) ~= string then + filepath = tostring(filepath) + end + + local match = filetype.detect_from_name(filepath) + if match ~= "" then + return match + end + + match = filetype.detect_from_extension(filepath) + + if opts.fs_access and Path:new(filepath):exists() then + if match == "" then + match = filetype.detect_from_shebang(filepath) + if match ~= "" then + return match + end + end + + if match == "text" or match == "" then + match = filetype.detect_from_modeline(filepath) + if match ~= "" then + return match + end + end + end + + return match +end + +filetype.add_file "base" +filetype.add_file "builtin" + +return filetype diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/fun.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/fun.lua new file mode 100644 index 0000000..e70367f --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/fun.lua @@ -0,0 +1,23 @@ +local M = {} + +M.bind = require("plenary.functional").partial + +function M.arify(fn, argc) + return function(...) + if select("#", ...) ~= argc then + error(("Expected %s number of arguments"):format(argc)) + end + + fn(...) + end +end + +function M.create_wrapper(map) + return function(to_wrap) + return function(...) + return map(to_wrap(...)) + end + end +end + +return M diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/functional.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/functional.lua new file mode 100644 index 0000000..6a5b367 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/functional.lua @@ -0,0 +1,77 @@ +local f = {} + +function f.kv_pairs(t) + local results = {} + for k, v in pairs(t) do + table.insert(results, { k, v }) + end + return results +end + +function f.kv_map(fun, t) + return vim.tbl_map(fun, f.kv_pairs(t)) +end + +function f.join(array, sep) + return table.concat(vim.tbl_map(tostring, array), sep) +end + +local function bind_n(fn, n, a, ...) + if n == 0 then + return fn + end + return bind_n(function(...) + return fn(a, ...) + end, n - 1, ...) +end + +function f.partial(fun, ...) + return bind_n(fun, select("#", ...), ...) +end + +function f.any(fun, iterable) + for k, v in pairs(iterable) do + if fun(k, v) then + return true + end + end + + return false +end + +function f.all(fun, iterable) + for k, v in pairs(iterable) do + if not fun(k, v) then + return false + end + end + + return true +end + +function f.if_nil(val, was_nil, was_not_nil) + if val == nil then + return was_nil + else + return was_not_nil + end +end + +function f.select_only(n) + return function(...) + local x = select(n, ...) + return x + end +end + +f.first = f.select_only(1) +f.second = f.select_only(2) +f.third = f.select_only(3) + +function f.last(...) + local length = select("#", ...) + local x = select(length, ...) + return x +end + +return f diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/init.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/init.lua new file mode 100644 index 0000000..4805a4f --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/init.lua @@ -0,0 +1,14 @@ +-- Lazy load everything into plenary. +local plenary = setmetatable({}, { + __index = function(t, k) + local ok, val = pcall(require, string.format("plenary.%s", k)) + + if ok then + rawset(t, k, val) + end + + return val + end, +}) + +return plenary diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/iterators.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/iterators.lua new file mode 100644 index 0000000..2aac0d2 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/iterators.lua @@ -0,0 +1,695 @@ +---@brief [[ +---An adaptation of luafun for neovim. +---This library will use neovim specific functions. +---Some documentation is the same as from luafun. +---Some extra functions are present that are not in luafun +---@brief ]] + +local co = coroutine +local f = require "plenary.functional" +local compat = require "plenary.compat" + +-------------------------------------------------------------------------------- +-- Tools +-------------------------------------------------------------------------------- + +local exports = {} + +---@class Iterator +---@field gen function +---@field param any +---@field state any +local Iterator = {} +Iterator.__index = Iterator + +---Makes a for loop work +---If not called without param or state, will just generate with the starting state +---This is useful because the original luafun will also return param and state in addition to the iterator as a multival +---This can cause problems because when using iterators as expressions the multivals can bleed +---For example i.iter { 1, 2, i.iter { 3, 4 } } will not work because the inner iterator returns a multival thus +---polluting the list with internal values. +---So instead we do not return param and state as multivals when doing wrap +---This causes the first loop iteration to call param and state with nil because we didn't return them as multivals +---We have to use or to check for nil and default to interal starting state and param +function Iterator:__call(param, state) + return self.gen(param or self.param, state or self.state) +end + +function Iterator:__tostring() + return "" +end + +-- A special hack for zip/chain to skip last two state, if a wrapped iterator +-- has been passed +local numargs = function(...) + local n = select("#", ...) + if n >= 3 then + -- Fix last argument + local it = select(n - 2, ...) + if + type(it) == "table" + and getmetatable(it) == Iterator + and it.param == select(n - 1, ...) + and it.state == select(n, ...) + then + return n - 2 + end + end + return n +end + +local return_if_not_empty = function(state_x, ...) + if state_x == nil then + return nil + end + return ... +end + +local call_if_not_empty = function(fun, state_x, ...) + if state_x == nil then + return nil + end + return state_x, fun(...) +end + +-------------------------------------------------------------------------------- +-- Basic Functions +-------------------------------------------------------------------------------- +local nil_gen = function(_param, _state) + return nil +end + +local pairs_gen = pairs {} + +local map_gen = function(map, key) + local value + key, value = pairs_gen(map, key) + return key, key, value +end + +local string_gen = function(param, state) + state = state + 1 + if state > #param then + return nil + end + local r = string.sub(param, state, state) + return state, r +end + +local rawiter = function(obj, param, state) + assert(obj ~= nil, "invalid iterator") + + if type(obj) == "table" then + local mt = getmetatable(obj) + + if mt ~= nil then + if mt == Iterator then + return obj.gen, obj.param, obj.state + end + end + + if compat.islist(obj) then + return ipairs(obj) + else + -- hash + return map_gen, obj, nil + end + elseif type(obj) == "function" then + return obj, param, state + elseif type(obj) == "string" then + if #obj == 0 then + return nil_gen, nil, nil + end + + return string_gen, obj, 0 + end + + error(string.format('object %s of type "%s" is not iterable', obj, type(obj))) +end + +---Wraps the iterator triplet into a table to allow metamethods and calling with method form +---Important! We do not return param and state as multivals like the original luafun +---See the __call metamethod for more information +---@param gen any +---@param param any +---@param state any +---@return Iterator +local function wrap(gen, param, state) + return setmetatable({ + gen = gen, + param = param, + state = state, + }, Iterator) +end + +---Unwrap an iterator metatable into the iterator triplet +---@param self Iterator +---@return any +---@return any +---@return any +local unwrap = function(self) + return self.gen, self.param, self.state +end + +---Create an iterator from an object +---@param obj any +---@param param any (optional) +---@param state any (optional) +---@return Iterator +local iter = function(obj, param, state) + return wrap(rawiter(obj, param, state)) +end + +exports.iter = iter +exports.wrap = wrap +exports.unwrap = unwrap + +function Iterator:for_each(fn) + local param, state = self.param, self.state + repeat + state = call_if_not_empty(fn, self.gen(param, state)) + until state == nil +end + +function Iterator:stateful() + return wrap( + co.wrap(function() + self:for_each(function(...) + co.yield(f.first(...), ...) + end) + + -- too make sure that we always return nil if there are no more + while true do + co.yield() + end + end), + nil, + nil + ) +end + +-- function Iterator:stateful() +-- local gen, param, state = self.gen, self.param, self.state + +-- local function return_and_set_state(state_x, ...) +-- state = state_x +-- if state == nil then return end +-- return state_x, ... +-- end + +-- local stateful_gen = function() +-- return return_and_set_state(gen(param, state)) +-- end + +-- return wrap(stateful_gen, false, false) +-- end + +-------------------------------------------------------------------------------- +-- Generators +-------------------------------------------------------------------------------- +local range_gen = function(param, state) + local stop, step = param[1], param[2] + state = state + step + if state > stop then + return nil + end + return state, state +end + +local range_rev_gen = function(param, state) + local stop, step = param[1], param[2] + state = state + step + if state < stop then + return nil + end + return state, state +end + +---Creates a range iterator +---@param start number +---@param stop number +---@param step number +---@return Iterator +local range = function(start, stop, step) + if step == nil then + if stop == nil then + if start == 0 then + return nil_gen, nil, nil + end + stop = start + start = stop > 0 and 1 or -1 + end + step = start <= stop and 1 or -1 + end + + assert(type(start) == "number", "start must be a number") + assert(type(stop) == "number", "stop must be a number") + assert(type(step) == "number", "step must be a number") + assert(step ~= 0, "step must not be zero") + + if step > 0 then + return wrap(range_gen, { stop, step }, start - step) + elseif step < 0 then + return wrap(range_rev_gen, { stop, step }, start - step) + end +end +exports.range = range + +local duplicate_table_gen = function(param_x, state_x) + return state_x + 1, unpack(param_x) +end + +local duplicate_fun_gen = function(param_x, state_x) + return state_x + 1, param_x(state_x) +end + +local duplicate_gen = function(param_x, state_x) + return state_x + 1, param_x +end + +---Creates an infinite iterator that will yield the arguments +---If multiple arguments are passed, the args will be packed and unpacked +---@param ...: the arguments to duplicate +---@return Iterator +local duplicate = function(...) + if select("#", ...) <= 1 then + return wrap(duplicate_gen, select(1, ...), 0) + else + return wrap(duplicate_table_gen, { ... }, 0) + end +end +exports.duplicate = duplicate + +---Creates an iterator from a function +---NOTE: if the function is a closure and modifies state, the resulting iterator will not be stateless +---@param fun function +---@return Iterator +local from_fun = function(fun) + assert(type(fun) == "function") + return wrap(duplicate_fun_gen, fun, 0) +end +exports.from_fun = from_fun + +---Creates an infinite iterator that will yield zeros. +---This is an alias to calling duplicate(0) +---@return Iterator +local zeros = function() + return wrap(duplicate_gen, 0, 0) +end +exports.zeros = zeros + +---Creates an infinite iterator that will yield ones. +---This is an alias to calling duplicate(1) +---@return Iterator +local ones = function() + return wrap(duplicate_gen, 1, 0) +end +exports.ones = ones + +local rands_gen = function(param_x, _state_x) + return 0, math.random(param_x[1], param_x[2]) +end + +local rands_nil_gen = function(_param_x, _state_x) + return 0, math.random() +end + +---Creates an infinite iterator that will yield random values. +---@param n number +---@param m number +---@return Iterator +local rands = function(n, m) + if n == nil and m == nil then + return wrap(rands_nil_gen, 0, 0) + end + assert(type(n) == "number", "invalid first arg to rands") + if m == nil then + m = n + n = 0 + else + assert(type(m) == "number", "invalid second arg to rands") + end + assert(n < m, "empty interval") + return wrap(rands_gen, { n, m - 1 }, 0) +end +exports.rands = rands + +local split_gen = function(param, state) + local input, sep = param[1], param[2] + local input_len = #input + + if state > input_len + 1 then + return + end + + local start, finish = string.find(input, sep, state, true) + if not start then + start = input_len + 1 + finish = input_len + 1 + end + + local sub_str = input:sub(state, start - 1) + + return finish + 1, sub_str +end + +---Return an iterator of substrings separated by a string +---@param input string: the string to split +---@param sep string: the separator to find and split based on +---@return Iterator +local split = function(input, sep) + return wrap(split_gen, { input, sep }, 1) +end +exports.split = split + +---Splits a string based on a single space +---An alias for split(input, " ") +---@param input any +---@return any +local words = function(input) + return split(input, " ") +end +exports.words = words + +local lines = function(input) + -- TODO: platform specific linebreaks + return split(input, "\n") +end +exports.lines = lines + +-------------------------------------------------------------------------------- +-- Transformations +-------------------------------------------------------------------------------- +local map_gen2 = function(param, state) + local gen_x, param_x, fun = param[1], param[2], param[3] + return call_if_not_empty(fun, gen_x(param_x, state)) +end + +---Iterator adapter that maps the previous iterator with a function +---@param fun function: The function to map with. Will be called on each element +---@return Iterator +function Iterator:map(fun) + return wrap(map_gen2, { self.gen, self.param, fun }, self.state) +end + +local flatten_gen1 +do + local it = function(new_iter, state_x, ...) + if state_x == nil then + return nil + end + return { new_iter.gen, new_iter.param, state_x }, ... + end + + flatten_gen1 = function(state, state_x, ...) + if state_x == nil then + return nil + end + + local first_arg = f.first(...) + + -- experimental part + if getmetatable(first_arg) == Iterator then + -- attach the iterator to the rest + local new_iter = (first_arg .. wrap(state[1], state[2], state_x)):flatten() + -- advance the iterator by one + return it(new_iter, new_iter.gen(new_iter.param, new_iter.state)) + end + + return { state[1], state[2], state_x }, ... + end +end + +local flatten_gen = function(_, state) + if state == nil then + return + end + local gen_x, param_x, state_x = state[1], state[2], state[3] + return flatten_gen1(state, gen_x(param_x, state_x)) +end + +---Iterator adapter that will recursivley flatten nested iterator structure +---@return Iterator +function Iterator:flatten() + return wrap(flatten_gen, false, { self.gen, self.param, self.state }) +end + +-------------------------------------------------------------------------------- +-- Filtering +-------------------------------------------------------------------------------- +local filter1_gen = function(fun, gen_x, param_x, state_x, a) + while true do + if state_x == nil or fun(a) then + break + end + state_x, a = gen_x(param_x, state_x) + end + return state_x, a +end + +-- call each other +-- because we can't assign a vararg mutably in a while loop like filter1_gen +-- so we have to use recursion in calling both of these functions +local filterm_gen +local filterm_gen_shrink = function(fun, gen_x, param_x, state_x) + return filterm_gen(fun, gen_x, param_x, gen_x(param_x, state_x)) +end + +filterm_gen = function(fun, gen_x, param_x, state_x, ...) + if state_x == nil then + return nil + end + + if fun(...) then + return state_x, ... + end + + return filterm_gen_shrink(fun, gen_x, param_x, state_x) +end + +local filter_detect = function(fun, gen_x, param_x, state_x, ...) + if select("#", ...) < 2 then + return filter1_gen(fun, gen_x, param_x, state_x, ...) + else + return filterm_gen(fun, gen_x, param_x, state_x, ...) + end +end + +local filter_gen = function(param, state_x) + local gen_x, param_x, fun = param[1], param[2], param[3] + return filter_detect(fun, gen_x, param_x, gen_x(param_x, state_x)) +end + +---Iterator adapter that will filter values +---@param fun function: The function to filter values with. If the function returns true, the value will be kept. +---@return Iterator +function Iterator:filter(fun) + return wrap(filter_gen, { self.gen, self.param, fun }, self.state) +end + +---Iterator adapter that will provide numbers from 1 to n as the first multival +---@return Iterator +function Iterator:enumerate() + local i = 0 + return self:map(function(...) + i = i + 1 + return i, ... + end) +end + +-------------------------------------------------------------------------------- +-- Reducing +-------------------------------------------------------------------------------- + +---Returns true if any of the values in the iterator satisfy a predicate +---@param fun function +---@return boolean +function Iterator:any(fun) + local r + local state, param, gen = self.state, self.param, self.gen + repeat + state, r = call_if_not_empty(fun, gen(param, state)) + until state == nil or r + return r +end + +---Returns true if all of the values in the iterator satisfy a predicate +---@param fun function +---@return boolean +function Iterator:all(fun) + local r + local state, param, gen = self.state, self.param, self.gen + repeat + state, r = call_if_not_empty(fun, gen(param, state)) + until state == nil or not r + return state == nil +end + +---Finds a value that is equal to the provided value of satisfies a predicate. +---@param val_or_fn any +---@return any +function Iterator:find(val_or_fn) + local gen, param, state = self.gen, self.param, self.state + if type(val_or_fn) == "function" then + return return_if_not_empty(filter_detect(val_or_fn, gen, param, gen(param, state))) + else + for _, r in gen, param, state do + if r == val_or_fn then + return r + end + end + return nil + end +end + +---Folds an iterator into a single value using a function. +---@param init any +---@param fun fun(acc: any, val: any): any +---@return any +function Iterator:fold(init, fun) + local acc = init + local gen, param, state = self.gen, self.param, self.state + for _, r in gen, param, state do + acc = fun(acc, r) + end + return acc +end + +---Turns an iterator into a list. +---If the iterator yields multivals only the first multival will be used. +---@return table +function Iterator:tolist() + local list = {} + self:for_each(function(a) + table.insert(list, a) + end) + return list +end + +---Turns an iterator into a list. +---If the iterator yields multivals all multivals will be used and packed into a table. +---@return table +function Iterator:tolistn() + local list = {} + self:for_each(function(...) + table.insert(list, { ... }) + end) + return list +end + +---Turns an iterator into a map. +---The first multival that the iterator yields will be the key. +---The second multival that the iterator yields will be the value. +---@return table +function Iterator:tomap() + local map = {} + self:for_each(function(key, value) + map[key] = value + end) + return map +end + +-------------------------------------------------------------------------------- +-- Compositions +-------------------------------------------------------------------------------- +-- call each other +local chain_gen_r1 +local chain_gen_r2 = function(param, state, state_x, ...) + if state_x == nil then + local i = state[1] + 1 + if param[3 * i - 1] == nil then + return nil + end + state_x = param[3 * i] + return chain_gen_r1(param, { i, state_x }) + end + return { state[1], state_x }, ... +end + +chain_gen_r1 = function(param, state) + local i, state_x = state[1], state[2] + local gen_x, param_x = param[3 * i - 2], param[3 * i - 1] + return chain_gen_r2(param, state, gen_x(param_x, state_x)) +end + +---Make an iterator that returns elements from the first iterator until it is exhausted, +---then proceeds to the next iterator, +---until all of the iterators are exhausted. +---Used for treating consecutive iterators as a single iterator. +---Infinity iterators are supported, but are not recommended. +---@param ...: the iterators to chain +---@return Iterator +local chain = function(...) + local n = numargs(...) + + if n == 0 then + return wrap(nil_gen, nil, nil) + end + + local param = { [3 * n] = 0 } + + local gen_x, param_x, state_x + for i = 1, n, 1 do + local elem = select(i, ...) + gen_x, param_x, state_x = unwrap(elem) + param[3 * i - 2] = gen_x + param[3 * i - 1] = param_x + param[3 * i] = state_x + end + + return wrap(chain_gen_r1, param, { 1, param[3] }) +end + +Iterator.chain = chain +Iterator.__concat = chain +exports.chain = chain + +local function zip_gen_r(param, state, state_new, ...) + if #state_new == #param / 2 then + return state_new, ... + end + + local i = #state_new + 1 + local gen_x, param_x = param[2 * i - 1], param[2 * i] + local state_x, r = gen_x(param_x, state[i]) + if state_x == nil then + return nil + end + table.insert(state_new, state_x) + return zip_gen_r(param, state, state_new, r, ...) +end + +local zip_gen = function(param, state) + return zip_gen_r(param, state, {}) +end + +---Return a new iterator where i-th return value contains the i-th element from each of the iterators. +---The returned iterator is truncated in length to the length of the shortest iterator. +---For multi-return iterators only the first variable is used. +---@param ...: the iterators to zip +---@return Iterator +local zip = function(...) + local n = numargs(...) + if n == 0 then + return wrap(nil_gen, nil, nil) + end + local param = { [2 * n] = 0 } + local state = { [n] = 0 } + + local gen_x, param_x, state_x + for i = 1, n, 1 do + local it = select(n - i + 1, ...) + gen_x, param_x, state_x = rawiter(it) + param[2 * i - 1] = gen_x + param[2 * i] = param_x + state[i] = state_x + end + + return wrap(zip_gen, param, state) +end + +Iterator.zip = zip +Iterator.__div = zip +exports.zip = zip + +return exports diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/job.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/job.lua new file mode 100644 index 0000000..7f54971 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/job.lua @@ -0,0 +1,678 @@ +local vim = vim +local uv = vim.loop +local compat = require "plenary.compat" + +local F = require "plenary.functional" + +---@class Job +---@field command string Command to run +---@field args? string[] List of arguments to pass +---@field cwd? string Working directory for job +---@field env? table|string[] Environment looking like: { ['VAR'] = 'VALUE' } or { 'VAR=VALUE' } +---@field interactive? boolean +---@field detached? boolean Spawn the child in a detached state making it a process group leader +---@field skip_validation? boolean Skip validating the arguments +---@field enable_handlers? boolean If set to false, disables all callbacks associated with output (default: true) +---@field enable_recording? boolean +---@field on_start? fun() +---@field on_stdout? fun(error: string, data: string, self?: Job) +---@field on_stderr? fun(error: string, data: string, self?: Job) +---@field on_exit? fun(self: Job, code: number, signal: number) +---@field maximum_results? number Stop processing results after this number +---@field writer? Job|table|string Job that writes to stdin of this job. +local Job = {} +Job.__index = Job + +local function close_safely(j, key) + local handle = j[key] + + if not handle then + return + end + + if not handle:is_closing() then + handle:close() + end +end + +local start_shutdown_check = function(child, options, code, signal) + uv.check_start(child._shutdown_check, function() + if not child:_pipes_are_closed(options) then + return + end + + -- Wait until all the pipes are closing. + uv.check_stop(child._shutdown_check) + child._shutdown_check = nil + + child:_shutdown(code, signal) + + -- Remove left over references + child = nil + end) +end + +local shutdown_factory = function(child, options) + return function(code, signal) + if uv.is_closing(child._shutdown_check) then + return child:shutdown(code, signal) + else + start_shutdown_check(child, options, code, signal) + end + end +end + +local function expand(path) + if vim.in_fast_event() then + return assert(uv.fs_realpath(path), string.format("Path must be valid: %s", path)) + else + -- TODO: Probably want to check that this is valid here... otherwise that's weird. + return vim.fn.expand(vim.fn.escape(path, "[]$"), true) + end +end + +---@class Array +--- Numeric table + +---@class Map +--- Map-like table + +---Create a new job +---@param o Job +---@return Job +function Job:new(o) + if not o then + error(debug.traceback "Options are required for Job:new") + end + + local command = o.command + if not command then + if o[1] then + command = o[1] + else + error(debug.traceback "'command' is required for Job:new") + end + elseif o[1] then + error(debug.traceback "Cannot pass both 'command' and array args") + end + + local args = o.args + if not args then + if #o > 1 then + args = { select(2, unpack(o)) } + end + end + + local ok, is_exe = pcall(vim.fn.executable, command) + if not o.skip_validation and ok and 1 ~= is_exe then + error(debug.traceback(command .. ": Executable not found")) + end + + local obj = {} + + obj.command = command + obj.args = args + obj._raw_cwd = o.cwd + if o.env then + if type(o.env) ~= "table" then + error "[plenary.job] env has to be a table" + end + + local transform = {} + for k, v in pairs(o.env) do + if type(k) == "number" then + table.insert(transform, v) + elseif type(k) == "string" then + table.insert(transform, k .. "=" .. tostring(v)) + end + end + obj.env = transform + end + if o.interactive == nil then + obj.interactive = true + else + obj.interactive = o.interactive + end + + if o.detached then + obj.detached = true + end + + -- enable_handlers: Do you want to do ANYTHING with the stdout/stderr of the proc + obj.enable_handlers = F.if_nil(o.enable_handlers, true, o.enable_handlers) + + -- enable_recording: Do you want to record stdout/stderr into a table. + -- Since it cannot be enabled when enable_handlers is false, + -- we try and make sure they are associated correctly. + obj.enable_recording = + F.if_nil(F.if_nil(o.enable_recording, o.enable_handlers, o.enable_recording), true, o.enable_recording) + + if not obj.enable_handlers and obj.enable_recording then + error "[plenary.job] Cannot record items but disable handlers" + end + + obj._user_on_start = o.on_start + obj._user_on_stdout = o.on_stdout + obj._user_on_stderr = o.on_stderr + obj._user_on_exit = o.on_exit + + obj._additional_on_exit_callbacks = {} + + obj._maximum_results = o.maximum_results + + obj.user_data = {} + + obj.writer = o.writer + + self._reset(obj) + + return setmetatable(obj, self) +end + +function Job:_reset() + self.is_shutdown = nil + + if self._shutdown_check and uv.is_active(self._shutdown_check) and not uv.is_closing(self._shutdown_check) then + vim.api.nvim_err_writeln(debug.traceback "We may be memory leaking here. Please report to TJ.") + end + self._shutdown_check = uv.new_check() + + self.stdin = nil + self.stdout = nil + self.stderr = nil + + self._stdout_reader = nil + self._stderr_reader = nil + + if self.enable_recording then + self._stdout_results = {} + self._stderr_results = {} + else + self._stdout_results = nil + self._stderr_results = nil + end +end + +--- Stop a job and close all handles +function Job:_stop() + close_safely(self, "stdin") + close_safely(self, "stderr") + close_safely(self, "stdout") + close_safely(self, "handle") +end + +function Job:_pipes_are_closed(options) + for _, pipe in ipairs { options.stdin, options.stdout, options.stderr } do + if pipe and not uv.is_closing(pipe) then + return false + end + end + + return true +end + +--- Shutdown a job. +function Job:shutdown(code, signal) + if self._shutdown_check and uv.is_active(self._shutdown_check) then + -- shutdown has already started + return + end + + self:_shutdown(code, signal) +end + +function Job:_shutdown(code, signal) + if self.is_shutdown then + return + end + + self.code = code + self.signal = signal + + if self._stdout_reader then + pcall(self._stdout_reader, nil, nil, true) + end + + if self._stderr_reader then + pcall(self._stderr_reader, nil, nil, true) + end + + if self._user_on_exit then + self:_user_on_exit(code, signal) + end + + for _, v in ipairs(self._additional_on_exit_callbacks) do + v(self, code, signal) + end + + if self.stdout then + self.stdout:read_stop() + end + + if self.stderr then + self.stderr:read_stop() + end + + self:_stop() + + self.is_shutdown = true + + self._stdout_reader = nil + self._stderr_reader = nil +end + +function Job:_create_uv_options() + local options = {} + + options.command = self.command + options.args = self.args + options.stdio = { self.stdin, self.stdout, self.stderr } + + if self._raw_cwd then + options.cwd = expand(self._raw_cwd) + end + if self.env then + options.env = self.env + end + + if self.detached then + options.detached = true + end + + return options +end + +local on_output = function(self, result_key, cb) + return coroutine.wrap(function(err, data, is_complete) + local result_index = 1 + + local line, start, result_line, found_newline + + -- We repeat forever as a coroutine so that we can keep calling this. + while true do + if data then + data = data:gsub("\r", "") + + local processed_index = 1 + local data_length = #data + 1 + + repeat + start = string.find(data, "\n", processed_index, true) or data_length + line = string.sub(data, processed_index, start - 1) + found_newline = start ~= data_length + + -- Concat to last line if there was something there already. + -- This happens when "data" is broken into chunks and sometimes + -- the content is sent without any newlines. + if result_line then + -- results[result_index] = results[result_index] .. line + result_line = result_line .. line + + -- Only put in a new line when we actually have new data to split. + -- This is generally only false when we do end with a new line. + -- It prevents putting in a "" to the end of the results. + elseif start ~= processed_index or found_newline then + -- results[result_index] = line + result_line = line + + -- Otherwise, we don't need to do anything. + end + + if found_newline then + if not result_line then + return vim.api.nvim_err_writeln( + "Broken data thing due to: " .. tostring(result_line) .. " " .. tostring(data) + ) + end + + if self.enable_recording then + self[result_key][result_index] = result_line + end + + if cb then + cb(err, result_line, self) + end + + -- Stop processing if we've surpassed the maximum. + if self._maximum_results and result_index > self._maximum_results then + -- Shutdown once we get the chance. + -- Can't call it here, because we'll just keep calling ourselves. + vim.schedule(function() + self:shutdown() + end) + + return + end + + result_index = result_index + 1 + result_line = nil + end + + processed_index = start + 1 + until not found_newline + end + + if self.enable_recording then + self[result_key][result_index] = result_line + end + + -- If we didn't get a newline on the last execute, send the final results. + if cb and is_complete and not found_newline then + cb(err, result_line, self) + end + + if is_complete then + return + end + + err, data, is_complete = coroutine.yield() + end + end) +end + +--- Stop previous execution and add new pipes. +--- Also regenerates pipes of writer. +function Job:_prepare_pipes() + self:_stop() + + if self.writer then + if Job.is_job(self.writer) then + self.writer:_prepare_pipes() + self.stdin = self.writer.stdout + elseif self.writer.write then + self.stdin = self.writer + end + end + + if not self.stdin then + self.stdin = self.interactive and uv.new_pipe(false) or nil + end + + self.stdout = uv.new_pipe(false) + self.stderr = uv.new_pipe(false) +end + +--- Execute job. Should be called only after preprocessing is done. +function Job:_execute() + local options = self:_create_uv_options() + + if self._user_on_start then + self:_user_on_start() + end + + self.handle, self.pid = uv.spawn(options.command, options, shutdown_factory(self, options)) + + if not self.handle then + error(debug.traceback("Failed to spawn process: " .. vim.inspect(self))) + end + + if self.enable_handlers then + self._stdout_reader = on_output(self, "_stdout_results", self._user_on_stdout) + self.stdout:read_start(self._stdout_reader) + + self._stderr_reader = on_output(self, "_stderr_results", self._user_on_stderr) + self.stderr:read_start(self._stderr_reader) + end + + if self.writer then + if Job.is_job(self.writer) then + self.writer:_execute() + elseif type(self.writer) == "table" and compat.islist(self.writer) then + local writer_len = #self.writer + for i, v in ipairs(self.writer) do + self.stdin:write(v) + if i ~= writer_len then + self.stdin:write "\n" + else + self.stdin:write("\n", function() + pcall(self.stdin.close, self.stdin) + end) + end + end + elseif type(self.writer) == "string" then + self.stdin:write(self.writer, function() + self.stdin:close() + end) + elseif self.writer.write then + self.stdin = self.writer + else + error("Unknown self.writer: " .. vim.inspect(self.writer)) + end + end + + return self +end + +function Job:start() + self:_reset() + self:_prepare_pipes() + self:_execute() +end + +function Job:sync(timeout, wait_interval) + self:start() + self:wait(timeout, wait_interval) + + return self.enable_recording and self:result() or nil, self.code +end + +function Job:result() + assert(self.enable_recording, "'enable_recording' is not enabled for this job.") + return self._stdout_results +end + +function Job:stderr_result() + assert(self.enable_recording, "'enable_recording' is not enabled for this job.") + return self._stderr_results +end + +function Job:pid() + return self.pid +end + +function Job:wait(timeout, wait_interval, should_redraw) + timeout = timeout or 5000 + wait_interval = wait_interval or 10 + + if self.handle == nil then + local msg = vim.inspect(self) + vim.schedule(function() + vim.api.nvim_err_writeln(msg) + end) + + return + end + + -- Wait five seconds, or until timeout. + local wait_result = vim.wait(timeout, function() + if should_redraw then + vim.cmd [[redraw!]] + end + + if self.is_shutdown then + assert(not self.handle or self.handle:is_closing(), "Job must be shutdown if it's closing") + end + + return self.is_shutdown + end, wait_interval, not should_redraw) + + if not wait_result then + error( + string.format( + "'%s %s' was unable to complete in %s ms", + self.command, + table.concat(self.args or {}, " "), + timeout + ) + ) + end + + return self +end + +function Job:co_wait(wait_time) + wait_time = wait_time or 5 + + if self.handle == nil then + vim.api.nvim_err_writeln(vim.inspect(self)) + return + end + + while not vim.wait(wait_time, function() + return self.is_shutdown + end) do + coroutine.yield() + end + + return self +end + +--- Wait for all jobs to complete +function Job.join(...) + local jobs_to_wait = { ... } + local num_jobs = table.getn(jobs_to_wait) + + -- last entry can be timeout + local timeout + if type(jobs_to_wait[num_jobs]) == "number" then + timeout = table.remove(jobs_to_wait, num_jobs) + num_jobs = num_jobs - 1 + end + + local completed = 0 + + return vim.wait(timeout or 10000, function() + for index, current_job in pairs(jobs_to_wait) do + if current_job.is_shutdown then + jobs_to_wait[index] = nil + completed = completed + 1 + end + end + + return num_jobs == completed + end) +end + +local _request_id = 0 +local _request_status = {} + +function Job:and_then(next_job) + self:add_on_exit_callback(function() + next_job:start() + end) +end + +function Job:and_then_wrap(next_job) + self:add_on_exit_callback(vim.schedule_wrap(function() + next_job:start() + end)) +end + +function Job:after(fn) + self:add_on_exit_callback(fn) + return self +end + +function Job:and_then_on_success(next_job) + self:add_on_exit_callback(function(_, code) + if code == 0 then + next_job:start() + end + end) +end + +function Job:and_then_on_success_wrap(next_job) + self:add_on_exit_callback(vim.schedule_wrap(function(_, code) + if code == 0 then + next_job:start() + end + end)) +end + +function Job:after_success(fn) + self:add_on_exit_callback(function(j, code, signal) + if code == 0 then + fn(j, code, signal) + end + end) +end + +function Job:and_then_on_failure(next_job) + self:add_on_exit_callback(function(_, code) + if code ~= 0 then + next_job:start() + end + end) +end + +function Job:and_then_on_failure_wrap(next_job) + self:add_on_exit_callback(vim.schedule_wrap(function(_, code) + if code ~= 0 then + next_job:start() + end + end)) +end + +function Job:after_failure(fn) + self:add_on_exit_callback(function(j, code, signal) + if code ~= 0 then + fn(j, code, signal) + end + end) +end + +function Job.chain(...) + _request_id = _request_id + 1 + _request_status[_request_id] = false + + local jobs = { ... } + + for index = 2, #jobs do + local prev_job = jobs[index - 1] + local job = jobs[index] + + prev_job:add_on_exit_callback(vim.schedule_wrap(function() + job:start() + end)) + end + + local last_on_exit = jobs[#jobs]._user_on_exit + jobs[#jobs]._user_on_exit = function(self, err, data) + if last_on_exit then + last_on_exit(self, err, data) + end + + _request_status[_request_id] = true + end + + jobs[1]:start() + + return _request_id +end + +function Job.chain_status(id) + return _request_status[id] +end + +function Job.is_job(item) + if type(item) ~= "table" then + return false + end + + return getmetatable(item) == Job +end + +function Job:add_on_exit_callback(cb) + table.insert(self._additional_on_exit_callbacks, cb) +end + +--- Send data to a job. +function Job:send(data) + if not self.stdin then + error "job has no 'stdin'. Have you run `job:start()` yet?" + end + + self.stdin:write(data) +end + +return Job diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/json.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/json.lua new file mode 100644 index 0000000..05581f7 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/json.lua @@ -0,0 +1,116 @@ +-- based on https://github.com/sindresorhus/strip-json-comments + +local singleComment = "singleComment" +local multiComment = "multiComment" +local stripWithoutWhitespace = function() + return "" +end + +local function slice(str, from, to) + from = from or 1 + to = to or #str + return str:sub(from, to) +end + +local stripWithWhitespace = function(str, from, to) + return slice(str, from, to):gsub("%S", " ") +end + +local isEscaped = function(jsonString, quotePosition) + local index = quotePosition - 1 + local backslashCount = 0 + + while jsonString:sub(index, index) == "\\" do + index = index - 1 + backslashCount = backslashCount + 1 + end + return backslashCount % 2 == 1 and true or false +end + +local M = {} + +-- Strips any json comments from a json string. +-- The resulting string can then be used by `vim.fn.json_decode` +-- +---@param jsonString string +---@param options? table +--- * whitespace: +--- - defaults to true +--- - when true, comments will be replaced by whitespace +--- - when false, comments will be stripped +--- * trailing_commas: +--- - defaults to false +--- - when true, trailing commas will be included +--- - when false, trailing commas will be removed +function M.json_strip_comments(jsonString, options) + options = options or {} + local strip = options.whitespace == false and stripWithoutWhitespace or stripWithWhitespace + local omitTrailingCommas = not options.trailing_commas + + local insideString = false + local insideComment = false + local offset = 1 + local result = "" + local skip = false + local lastComma = 0 + + for i = 1, #jsonString, 1 do + if skip then + skip = false + else + local currentCharacter = jsonString:sub(i, i) + local nextCharacter = jsonString:sub(i + 1, i + 1) + + if not insideComment and currentCharacter == '"' then + local escaped = isEscaped(jsonString, i) + if not escaped then + insideString = not insideString + end + end + + if not insideString then + if not insideComment and currentCharacter .. nextCharacter == "//" then + result = result .. slice(jsonString, offset, i - 1) + offset = i + insideComment = singleComment + skip = true + elseif insideComment == singleComment and currentCharacter .. nextCharacter == "\r\n" then + i = i + 1 + skip = true + insideComment = false + result = result .. strip(jsonString, offset, i - 1) + offset = i + elseif insideComment == singleComment and currentCharacter == "\n" then + insideComment = false + result = result .. strip(jsonString, offset, i - 1) + offset = i + elseif not insideComment and currentCharacter .. nextCharacter == "/*" then + result = result .. slice(jsonString, offset, i - 1) + offset = i + insideComment = multiComment + skip = true + elseif insideComment == multiComment and currentCharacter .. nextCharacter == "*/" then + i = i + 1 + skip = true + insideComment = false + result = result .. strip(jsonString, offset, i) + offset = i + 1 + elseif omitTrailingCommas and not insideComment then + if currentCharacter == "," then + lastComma = i + elseif (currentCharacter == "]" or currentCharacter == "}") and lastComma > 0 then + result = result .. slice(jsonString, offset, lastComma - 1) .. slice(jsonString, lastComma + 1, i) + offset = i + 1 + lastComma = 0 + elseif currentCharacter:match "%S" then + lastComma = 0 + end + end + end + end + end + + return result .. (insideComment and strip(slice(jsonString, offset)) or slice(jsonString, offset)) +end + +return M diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/log.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/log.lua new file mode 100644 index 0000000..d7d3334 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/log.lua @@ -0,0 +1,235 @@ +-- log.lua +-- Does only support logging source files. +-- +-- Inspired by rxi/log.lua +-- Modified by tjdevries and can be found at github.com/tjdevries/vlog.nvim +-- +-- This library is free software; you can redistribute it and/or modify it +-- under the terms of the MIT license. See LICENSE for details. + +local Path = require "plenary.path" + +local p_debug = vim.fn.getenv "DEBUG_PLENARY" +if p_debug == vim.NIL then + p_debug = false +end + +-- User configuration section +local default_config = { + -- Name of the plugin. Prepended to log messages. + plugin = "plenary", + + -- Should print the output to neovim while running. + -- values: 'sync','async',false + use_console = "async", + + -- Should highlighting be used in console (using echohl). + highlights = true, + + -- Should write to a file. + -- Default output for logging file is `stdpath("log")/plugin.log`. + use_file = true, + + -- Output file has precedence over plugin, if not nil. + -- Used for the logging file, if not nil and use_file == true. + outfile = nil, + + -- Should write to the quickfix list. + use_quickfix = false, + + -- Any messages above this level will be logged. + level = p_debug and "debug" or "info", + + -- Level configuration. + modes = { + { name = "trace", hl = "Comment" }, + { name = "debug", hl = "Comment" }, + { name = "info", hl = "None" }, + { name = "warn", hl = "WarningMsg" }, + { name = "error", hl = "ErrorMsg" }, + { name = "fatal", hl = "ErrorMsg" }, + }, + + -- Can limit the number of decimals displayed for floats. + float_precision = 0.01, + + -- Adjust content as needed, but must keep function parameters to be filled + -- by library code. + ---@param is_console boolean If output is for console or log file. + ---@param mode_name string Level configuration 'modes' field 'name' + ---@param src_path string Path to source file given by debug.info.source + ---@param src_line integer Line into source file given by debug.info.currentline + ---@param msg string Message, which is later on escaped, if needed. + fmt_msg = function(is_console, mode_name, src_path, src_line, msg) + local nameupper = mode_name:upper() + local lineinfo = src_path .. ":" .. src_line + if is_console then + return string.format("[%-6s%s] %s: %s", nameupper, os.date "%H:%M:%S", lineinfo, msg) + else + return string.format("[%-6s%s] %s: %s\n", nameupper, os.date(), lineinfo, msg) + end + end, +} + +-- {{{ NO NEED TO CHANGE +local log = {} + +local unpack = unpack or table.unpack + +log.new = function(config, standalone) + config = vim.tbl_deep_extend("force", default_config, config) + + local outfile = vim.F.if_nil( + config.outfile, + Path:new(vim.api.nvim_call_function("stdpath", { "log" }), config.plugin .. ".log").filename + ) + + local obj + if standalone then + obj = log + else + obj = config + end + + local levels = {} + for i, v in ipairs(config.modes) do + levels[v.name] = i + end + + local round = function(x, increment) + if x == 0 then + return x + end + increment = increment or 1 + x = x / increment + return (x > 0 and math.floor(x + 0.5) or math.ceil(x - 0.5)) * increment + end + + local make_string = function(...) + local t = {} + for i = 1, select("#", ...) do + local x = select(i, ...) + + if type(x) == "number" and config.float_precision then + x = tostring(round(x, config.float_precision)) + elseif type(x) == "table" then + x = vim.inspect(x) + else + x = tostring(x) + end + + t[#t + 1] = x + end + return table.concat(t, " ") + end + + local log_at_level = function(level, level_config, message_maker, ...) + -- Return early if we're below the config.level + if level < levels[config.level] then + return + end + local msg = message_maker(...) + local info = debug.getinfo(config.info_level or 2, "Sl") + local src_path = info.source:sub(2) + local src_line = info.currentline + -- Output to console + if config.use_console then + local log_to_console = function() + local console_string = config.fmt_msg(true, level_config.name, src_path, src_line, msg) + + if config.highlights and level_config.hl then + vim.cmd(string.format("echohl %s", level_config.hl)) + end + + local split_console = vim.split(console_string, "\n") + for _, v in ipairs(split_console) do + local formatted_msg = string.format("[%s] %s", config.plugin, vim.fn.escape(v, [["\]])) + + local ok = pcall(vim.cmd, string.format([[echom "%s"]], formatted_msg)) + if not ok then + vim.api.nvim_out_write(msg .. "\n") + end + end + + if config.highlights and level_config.hl then + vim.cmd "echohl NONE" + end + end + if config.use_console == "sync" and not vim.in_fast_event() then + log_to_console() + else + vim.schedule(log_to_console) + end + end + + -- Output to log file + if config.use_file then + local outfile_parent_path = Path:new(outfile):parent() + if not outfile_parent_path:exists() then + outfile_parent_path:mkdir { parents = true } + end + local fp = assert(io.open(outfile, "a")) + local str = config.fmt_msg(false, level_config.name, src_path, src_line, msg) + fp:write(str) + fp:close() + end + + -- Output to quickfix + if config.use_quickfix then + local nameupper = level_config.name:upper() + local formatted_msg = string.format("[%s] %s", nameupper, msg) + local qf_entry = { + -- remove the @ getinfo adds to the file path + filename = info.source:sub(2), + lnum = info.currentline, + col = 1, + text = formatted_msg, + } + vim.fn.setqflist({ qf_entry }, "a") + end + end + + for i, x in ipairs(config.modes) do + -- log.info("these", "are", "separated") + obj[x.name] = function(...) + return log_at_level(i, x, make_string, ...) + end + + -- log.fmt_info("These are %s strings", "formatted") + obj[("fmt_%s"):format(x.name)] = function(...) + return log_at_level(i, x, function(...) + local passed = { ... } + local fmt = table.remove(passed, 1) + local inspected = {} + for _, v in ipairs(passed) do + table.insert(inspected, vim.inspect(v)) + end + return string.format(fmt, unpack(inspected)) + end, ...) + end + + -- log.lazy_info(expensive_to_calculate) + obj[("lazy_%s"):format(x.name)] = function() + return log_at_level(i, x, function(f) + return f() + end) + end + + -- log.file_info("do not print") + obj[("file_%s"):format(x.name)] = function(vals, override) + local original_console = config.use_console + config.use_console = false + config.info_level = override.info_level + log_at_level(i, x, make_string, unpack(vals)) + config.use_console = original_console + config.info_level = nil + end + end + + return obj +end + +log.new(default_config, true) +-- }}} + +return log diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/lsp/override.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/lsp/override.lua new file mode 100644 index 0000000..76bf02a --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/lsp/override.lua @@ -0,0 +1,30 @@ +local vim = vim + +local M = {} + +M._original_functions = {} + +--- Override an lsp method default callback +--- @param method string +--- @param new_function function +function M.override(method, new_function) + if M._original_functions[method] == nil then + M._original_functions[method] = vim.lsp.callbacks[method] + end + + vim.lsp.callbacks[method] = new_function +end + +--- Get the original method callback +--- useful if you only want to override in some circumstances +--- +--- @param method string +function M.get_original_function(method) + if M._original_functions[method] == nil then + M._original_functions[method] = vim.lsp.callbacks[method] + end + + return M._original_functions[method] +end + +return M diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/neorocks/init.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/neorocks/init.lua new file mode 100644 index 0000000..df95ac3 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/neorocks/init.lua @@ -0,0 +1 @@ +error "neorocks is no longer supported. Please use packer.nvim or other project for neorocks usage." diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/nvim_meta.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/nvim_meta.lua new file mode 100644 index 0000000..12d9e81 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/nvim_meta.lua @@ -0,0 +1,18 @@ +local get_lua_version = function() + if jit then + return { + lua = string.gsub(_VERSION, "Lua ", ""), + jit = not not string.find(jit.version, "LuaJIT"), + version = string.gsub(jit.version, "LuaJIT ", ""), + } + end + + error("NEOROCKS: Unsupported Lua Versions", _VERSION) +end + +return { + -- Is run in `--headless` mode. + is_headless = (#vim.api.nvim_list_uis() == 0), + + lua_jit = get_lua_version(), +} diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/operators.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/operators.lua new file mode 100644 index 0000000..ccc00c9 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/operators.lua @@ -0,0 +1,100 @@ +---@brief [[ +---Operators that are functions. +---This is useful when you want to pass operators to higher order functions. +---Lua has no currying so we have to make a function for each operator. +---@brief ]] + +return { + ---------------------------------------------------------------------------- + -- Comparison operators + ---------------------------------------------------------------------------- + lt = function(a, b) + return a < b + end, + le = function(a, b) + return a <= b + end, + eq = function(a, b) + return a == b + end, + ne = function(a, b) + return a ~= b + end, + ge = function(a, b) + return a >= b + end, + gt = function(a, b) + return a > b + end, + + ---------------------------------------------------------------------------- + -- Arithmetic operators + ---------------------------------------------------------------------------- + add = function(a, b) + return a + b + end, + div = function(a, b) + return a / b + end, + floordiv = function(a, b) + return math.floor(a / b) + end, + intdiv = function(a, b) + local q = a / b + if a >= 0 then + return math.floor(q) + else + return math.ceil(q) + end + end, + mod = function(a, b) + return a % b + end, + mul = function(a, b) + return a * b + end, + neq = function(a) + return -a + end, + unm = function(a) + return -a + end, -- an alias + pow = function(a, b) + return a ^ b + end, + sub = function(a, b) + return a - b + end, + truediv = function(a, b) + return a / b + end, + + ---------------------------------------------------------------------------- + -- String operators + ---------------------------------------------------------------------------- + concat = function(a, b) + return a .. b + end, + len = function(a) + return #a + end, + length = function(a) + return #a + end, -- an alias + + ---------------------------------------------------------------------------- + -- Logical operators + ---------------------------------------------------------------------------- + land = function(a, b) + return a and b + end, + lor = function(a, b) + return a or b + end, + lnot = function(a) + return not a + end, + truth = function(a) + return not not a + end, +} diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/path.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/path.lua new file mode 100644 index 0000000..0865f2e --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/path.lua @@ -0,0 +1,947 @@ +--- Path.lua +--- +--- Goal: Create objects that are extremely similar to Python's `Path` Objects. +--- Reference: https://docs.python.org/3/library/pathlib.html + +local bit = require "plenary.bit" +local uv = vim.loop + +local F = require "plenary.functional" + +local S_IF = { + -- S_IFDIR = 0o040000 # directory + DIR = 0x4000, + -- S_IFREG = 0o100000 # regular file + REG = 0x8000, +} + +local path = {} +path.home = vim.loop.os_homedir() + +path.sep = (function() + if jit then + local os = string.lower(jit.os) + if os ~= "windows" then + return "/" + else + return "\\" + end + else + return package.config:sub(1, 1) + end +end)() + +path.root = (function() + if path.sep == "/" then + return function() + return "/" + end + else + return function(base) + base = base or vim.loop.cwd() + return base:sub(1, 1) .. ":\\" + end + end +end)() + +path.S_IF = S_IF + +local band = function(reg, value) + return bit.band(reg, value) == reg +end + +local concat_paths = function(...) + return table.concat({ ... }, path.sep) +end + +local function is_root(pathname) + if path.sep == "\\" then + return string.match(pathname, "^[A-Z]:\\?$") + end + return pathname == "/" +end + +local _split_by_separator = (function() + local formatted = string.format("([^%s]+)", path.sep) + return function(filepath) + local t = {} + for str in string.gmatch(filepath, formatted) do + table.insert(t, str) + end + return t + end +end)() + +local is_uri = function(filename) + return string.match(filename, "^%a[%w+-.]*://") ~= nil +end + +local is_absolute = function(filename, sep) + if sep == "\\" then + return string.match(filename, "^[%a]:[\\/].*$") ~= nil + end + return string.sub(filename, 1, 1) == sep +end + +local function _normalize_path(filename, cwd) + if is_uri(filename) then + return filename + end + + -- handles redundant `./` in the middle + local redundant = path.sep .. "%." .. path.sep + if filename:match(redundant) then + filename = filename:gsub(redundant, path.sep) + end + + local out_file = filename + + local has = string.find(filename, path.sep .. "..", 1, true) or string.find(filename, ".." .. path.sep, 1, true) + + if has then + local is_abs = is_absolute(filename, path.sep) + local split_without_disk_name = function(filename_local) + local parts = _split_by_separator(filename_local) + -- Remove disk name part on Windows + if path.sep == "\\" and is_abs then + table.remove(parts, 1) + end + return parts + end + + local parts = split_without_disk_name(filename) + local idx = 1 + local initial_up_count = 0 + + repeat + if parts[idx] == ".." then + if idx == 1 then + initial_up_count = initial_up_count + 1 + end + table.remove(parts, idx) + table.remove(parts, idx - 1) + if idx > 1 then + idx = idx - 2 + else + idx = idx - 1 + end + end + idx = idx + 1 + until idx > #parts + + local prefix = "" + if is_abs or #split_without_disk_name(cwd) == initial_up_count then + prefix = path.root(filename) + end + + out_file = prefix .. table.concat(parts, path.sep) + end + + return out_file +end + +local clean = function(pathname) + if is_uri(pathname) then + return pathname + end + + -- Remove double path seps, it's annoying + pathname = pathname:gsub(path.sep .. path.sep, path.sep) + + -- Remove trailing path sep if not root + if not is_root(pathname) and pathname:sub(-1) == path.sep then + return pathname:sub(1, -2) + end + return pathname +end + +-- S_IFCHR = 0o020000 # character device +-- S_IFBLK = 0o060000 # block device +-- S_IFIFO = 0o010000 # fifo (named pipe) +-- S_IFLNK = 0o120000 # symbolic link +-- S_IFSOCK = 0o140000 # socket file + +---@class Path +local Path = { + path = path, +} + +local check_self = function(self) + if type(self) == "string" then + return Path:new(self) + end + + return self +end + +Path.__index = function(t, k) + local raw = rawget(Path, k) + if raw then + return raw + end + + if k == "_cwd" then + local cwd = uv.fs_realpath "." + t._cwd = cwd + return cwd + end + + if k == "_absolute" then + local absolute = uv.fs_realpath(t.filename) + t._absolute = absolute + return absolute + end +end + +-- TODO: Could use this to not have to call new... not sure +-- Path.__call = Path:new + +Path.__div = function(self, other) + assert(Path.is_path(self)) + assert(Path.is_path(other) or type(other) == "string") + + return self:joinpath(other) +end + +Path.__tostring = function(self) + return clean(self.filename) +end + +-- TODO: See where we concat the table, and maybe we could make this work. +Path.__concat = function(self, other) + return self.filename .. other +end + +Path.is_path = function(a) + return getmetatable(a) == Path +end + +function Path:new(...) + local args = { ... } + + if type(self) == "string" then + table.insert(args, 1, self) + self = Path -- luacheck: ignore + end + + local path_input + if #args == 1 then + path_input = args[1] + else + path_input = args + end + + -- If we already have a Path, it's fine. + -- Just return it + if Path.is_path(path_input) then + return path_input + end + + -- TODO: Should probably remove and dumb stuff like double seps, periods in the middle, etc. + local sep = path.sep + if type(path_input) == "table" then + sep = path_input.sep or path.sep + path_input.sep = nil + end + + local path_string + if type(path_input) == "table" then + -- TODO: It's possible this could be done more elegantly with __concat + -- But I'm unsure of what we'd do to make that happen + local path_objs = {} + for _, v in ipairs(path_input) do + if Path.is_path(v) then + table.insert(path_objs, v.filename) + else + assert(type(v) == "string") + table.insert(path_objs, v) + end + end + + path_string = table.concat(path_objs, sep) + else + assert(type(path_input) == "string", vim.inspect(path_input)) + path_string = path_input + end + + local obj = { + filename = path_string, + + _sep = sep, + } + + setmetatable(obj, Path) + + return obj +end + +function Path:_fs_filename() + return self:absolute() or self.filename +end + +function Path:_stat() + return uv.fs_stat(self:_fs_filename()) or {} + -- local stat = uv.fs_stat(self:absolute()) + -- if not self._absolute then return {} end + + -- if not self._stat_result then + -- self._stat_result = + -- end + + -- return self._stat_result +end + +function Path:_st_mode() + return self:_stat().mode or 0 +end + +function Path:joinpath(...) + return Path:new(self.filename, ...) +end + +function Path:absolute() + if self:is_absolute() then + return _normalize_path(self.filename, self._cwd) + else + return _normalize_path(self._absolute or table.concat({ self._cwd, self.filename }, self._sep), self._cwd) + end +end + +function Path:exists() + return not vim.tbl_isempty(self:_stat()) +end + +function Path:expand() + if is_uri(self.filename) then + return self.filename + end + + -- TODO support windows + local expanded + if string.find(self.filename, "~") then + expanded = string.gsub(self.filename, "^~", vim.loop.os_homedir()) + elseif string.find(self.filename, "^%.") then + expanded = vim.loop.fs_realpath(self.filename) + if expanded == nil then + expanded = vim.fn.fnamemodify(self.filename, ":p") + end + elseif string.find(self.filename, "%$") then + local rep = string.match(self.filename, "([^%$][^/]*)") + local val = os.getenv(rep) + if val then + expanded = string.gsub(string.gsub(self.filename, rep, val), "%$", "") + else + expanded = nil + end + else + expanded = self.filename + end + return expanded and expanded or error "Path not valid" +end + +function Path:make_relative(cwd) + if is_uri(self.filename) then + return self.filename + end + + self.filename = clean(self.filename) + cwd = clean(F.if_nil(cwd, self._cwd, cwd)) + if self.filename == cwd then + self.filename = "." + else + if cwd:sub(#cwd, #cwd) ~= path.sep then + cwd = cwd .. path.sep + end + + if self.filename:sub(1, #cwd) == cwd then + self.filename = self.filename:sub(#cwd + 1, -1) + end + end + + return self.filename +end + +function Path:normalize(cwd) + if is_uri(self.filename) then + return self.filename + end + + self:make_relative(cwd) + + -- Substitute home directory w/ "~" + -- string.gsub is not useful here because usernames with dashes at the end + -- will be seen as a regexp pattern rather than a raw string + local home = path.home + if string.sub(path.home, -1) ~= path.sep then + home = home .. path.sep + end + local start, finish = string.find(self.filename, home, 1, true) + if start == 1 then + self.filename = "~" .. path.sep .. string.sub(self.filename, (finish + 1), -1) + end + + return _normalize_path(clean(self.filename), self._cwd) +end + +local function shorten_len(filename, len, exclude) + len = len or 1 + exclude = exclude or { -1 } + local exc = {} + + -- get parts in a table + local parts = {} + local empty_pos = {} + for m in (filename .. path.sep):gmatch("(.-)" .. path.sep) do + if m ~= "" then + parts[#parts + 1] = m + else + table.insert(empty_pos, #parts + 1) + end + end + + for _, v in pairs(exclude) do + if v < 0 then + exc[v + #parts + 1] = true + else + exc[v] = true + end + end + + local final_path_components = {} + local count = 1 + for _, match in ipairs(parts) do + if not exc[count] and #match > len then + table.insert(final_path_components, string.sub(match, 1, len)) + else + table.insert(final_path_components, match) + end + table.insert(final_path_components, path.sep) + count = count + 1 + end + + local l = #final_path_components -- so that we don't need to keep calculating length + table.remove(final_path_components, l) -- remove final slash + + -- add back empty positions + for i = #empty_pos, 1, -1 do + table.insert(final_path_components, empty_pos[i], path.sep) + end + + return table.concat(final_path_components) +end + +local shorten = (function() + local fallback = function(filename) + return shorten_len(filename, 1) + end + + if jit and path.sep ~= "\\" then + local ffi = require "ffi" + ffi.cdef [[ + typedef unsigned char char_u; + void shorten_dir(char_u *str); + ]] + local ffi_func = function(filename) + if not filename or is_uri(filename) then + return filename + end + + local c_str = ffi.new("char[?]", #filename + 1) + ffi.copy(c_str, filename) + ffi.C.shorten_dir(c_str) + return ffi.string(c_str) + end + local ok = pcall(ffi_func, "/tmp/path/file.lua") + if ok then + return ffi_func + else + return fallback + end + end + return fallback +end)() + +function Path:shorten(len, exclude) + assert(len ~= 0, "len must be at least 1") + if (len and len > 1) or exclude ~= nil then + return shorten_len(self.filename, len, exclude) + end + return shorten(self.filename) +end + +function Path:mkdir(opts) + opts = opts or {} + + local mode = opts.mode or 448 -- 0700 -> decimal + local parents = F.if_nil(opts.parents, false, opts.parents) + local exists_ok = F.if_nil(opts.exists_ok, true, opts.exists_ok) + + local exists = self:exists() + if not exists_ok and exists then + error("FileExistsError:" .. self:absolute()) + end + + -- fs_mkdir returns nil if folder exists + if not uv.fs_mkdir(self:_fs_filename(), mode) and not exists then + if parents then + local dirs = self:_split() + local processed = "" + for _, dir in ipairs(dirs) do + if dir ~= "" then + local joined = concat_paths(processed, dir) + if processed == "" and self._sep == "\\" then + joined = dir + end + local stat = uv.fs_stat(joined) or {} + local file_mode = stat.mode or 0 + if band(S_IF.REG, file_mode) then + error(string.format("%s is a regular file so we can't mkdir it", joined)) + elseif band(S_IF.DIR, file_mode) then + processed = joined + else + if uv.fs_mkdir(joined, mode) then + processed = joined + else + error("We couldn't mkdir: " .. joined) + end + end + end + end + else + error "FileNotFoundError" + end + end + + return true +end + +function Path:rmdir() + if not self:exists() then + return + end + + uv.fs_rmdir(self:absolute()) +end + +function Path:rename(opts) + opts = opts or {} + if not opts.new_name or opts.new_name == "" then + error "Please provide the new name!" + end + + -- handles `.`, `..`, `./`, and `../` + if opts.new_name:match "^%.%.?/?\\?.+" then + opts.new_name = { + uv.fs_realpath(opts.new_name:sub(1, 3)), + opts.new_name:sub(4, #opts.new_name), + } + end + + local new_path = Path:new(opts.new_name) + + if new_path:exists() then + error "File or directory already exists!" + end + + local status = uv.fs_rename(self:absolute(), new_path:absolute()) + self.filename = new_path.filename + + return status +end + +--- Copy files or folders with defaults akin to GNU's `cp`. +---@param opts table: options to pass to toggling registered actions +---@field destination string|Path: target file path to copy to +---@field recursive bool: whether to copy folders recursively (default: false) +---@field override bool: whether to override files (default: true) +---@field interactive bool: confirm if copy would override; precedes `override` (default: false) +---@field respect_gitignore bool: skip folders ignored by all detected `gitignore`s (default: false) +---@field hidden bool: whether to add hidden files in recursively copying folders (default: true) +---@field parents bool: whether to create possibly non-existing parent dirs of `opts.destination` (default: false) +---@field exists_ok bool: whether ok if `opts.destination` exists, if so folders are merged (default: true) +---@return table {[Path of destination]: bool} indicating success of copy; nested tables constitute sub dirs +function Path:copy(opts) + opts = opts or {} + opts.recursive = F.if_nil(opts.recursive, false, opts.recursive) + opts.override = F.if_nil(opts.override, true, opts.override) + + local dest = opts.destination + -- handles `.`, `..`, `./`, and `../` + if not Path.is_path(dest) then + if type(dest) == "string" and dest:match "^%.%.?/?\\?.+" then + dest = { + uv.fs_realpath(dest:sub(1, 3)), + dest:sub(4, #dest), + } + end + dest = Path:new(dest) + end + -- success is true in case file is copied, false otherwise + local success = {} + if not self:is_dir() then + if opts.interactive and dest:exists() then + vim.ui.select( + { "Yes", "No" }, + { prompt = string.format("Overwrite existing %s?", dest:absolute()) }, + function(_, idx) + success[dest] = uv.fs_copyfile(self:absolute(), dest:absolute(), { excl = idx ~= 1 }) or false + end + ) + else + -- nil: not overriden if `override = false` + success[dest] = uv.fs_copyfile(self:absolute(), dest:absolute(), { excl = not opts.override }) or false + end + return success + end + -- dir + if opts.recursive then + dest:mkdir { + parents = F.if_nil(opts.parents, false, opts.parents), + exists_ok = F.if_nil(opts.exists_ok, true, opts.exists_ok), + } + local scan = require "plenary.scandir" + local data = scan.scan_dir(self.filename, { + respect_gitignore = F.if_nil(opts.respect_gitignore, false, opts.respect_gitignore), + hidden = F.if_nil(opts.hidden, true, opts.hidden), + depth = 1, + add_dirs = true, + }) + for _, entry in ipairs(data) do + local entry_path = Path:new(entry) + local suffix = table.remove(entry_path:_split()) + local new_dest = dest:joinpath(suffix) + -- clear destination as it might be Path table otherwise failing w/ extend + opts.destination = nil + local new_opts = vim.tbl_deep_extend("force", opts, { destination = new_dest }) + -- nil: not overriden if `override = false` + success[new_dest] = entry_path:copy(new_opts) or false + end + return success + else + error(string.format("Warning: %s was not copied as `recursive=false`", self:absolute())) + end +end + +function Path:touch(opts) + opts = opts or {} + + local mode = opts.mode or 420 + local parents = F.if_nil(opts.parents, false, opts.parents) + + if self:exists() then + local new_time = os.time() + uv.fs_utime(self:_fs_filename(), new_time, new_time) + return + end + + if parents then + Path:new(self:parent()):mkdir { parents = true } + end + + local fd = uv.fs_open(self:_fs_filename(), "w", mode) + if not fd then + error("Could not create file: " .. self:_fs_filename()) + end + uv.fs_close(fd) + + return true +end + +function Path:rm(opts) + opts = opts or {} + + local recursive = F.if_nil(opts.recursive, false, opts.recursive) + if recursive then + local scan = require "plenary.scandir" + local abs = self:absolute() + + -- first unlink all files + scan.scan_dir(abs, { + hidden = true, + on_insert = function(file) + uv.fs_unlink(file) + end, + }) + + local dirs = scan.scan_dir(abs, { add_dirs = true, hidden = true }) + -- iterate backwards to clean up remaining dirs + for i = #dirs, 1, -1 do + uv.fs_rmdir(dirs[i]) + end + + -- now only abs is missing + uv.fs_rmdir(abs) + else + uv.fs_unlink(self:absolute()) + end +end + +-- Path:is_* {{{ +function Path:is_dir() + -- TODO: I wonder when this would be better, if ever. + -- return self:_stat().type == 'directory' + + return band(S_IF.DIR, self:_st_mode()) +end + +function Path:is_absolute() + return is_absolute(self.filename, self._sep) +end +-- }}} + +function Path:_split() + return vim.split(self:absolute(), self._sep) +end + +local _get_parent = (function() + local formatted = string.format("^(.+)%s[^%s]+", path.sep, path.sep) + return function(abs_path) + local parent = abs_path:match(formatted) + if parent ~= nil and not parent:find(path.sep) then + return parent .. path.sep + end + return parent + end +end)() + +function Path:parent() + return Path:new(_get_parent(self:absolute()) or path.root(self:absolute())) +end + +function Path:parents() + local results = {} + local cur = self:absolute() + repeat + cur = _get_parent(cur) + table.insert(results, cur) + until not cur + table.insert(results, path.root(self:absolute())) + return results +end + +function Path:is_file() + return self:_stat().type == "file" and true or nil +end + +-- TODO: +-- Maybe I can use libuv for this? +function Path:open() end + +function Path:close() end + +function Path:write(txt, flag, mode) + assert(flag, [[Path:write_text requires a flag! For example: 'w' or 'a']]) + + mode = mode or 438 + + local fd = assert(uv.fs_open(self:_fs_filename(), flag, mode)) + assert(uv.fs_write(fd, txt, -1)) + assert(uv.fs_close(fd)) +end + +-- TODO: Asyncify this and use vim.wait in the meantime. +-- This will allow other events to happen while we're waiting! +function Path:_read() + self = check_self(self) + + local fd = assert(uv.fs_open(self:_fs_filename(), "r", 438)) -- for some reason test won't pass with absolute + local stat = assert(uv.fs_fstat(fd)) + local data = assert(uv.fs_read(fd, stat.size, 0)) + assert(uv.fs_close(fd)) + + return data +end + +function Path:_read_async(callback) + vim.loop.fs_open(self.filename, "r", 438, function(err_open, fd) + if err_open then + print("We tried to open this file but couldn't. We failed with following error message: " .. err_open) + return + end + vim.loop.fs_fstat(fd, function(err_fstat, stat) + assert(not err_fstat, err_fstat) + if stat.type ~= "file" then + return callback "" + end + vim.loop.fs_read(fd, stat.size, 0, function(err_read, data) + assert(not err_read, err_read) + vim.loop.fs_close(fd, function(err_close) + assert(not err_close, err_close) + return callback(data) + end) + end) + end) + end) +end + +function Path:read(callback) + if callback then + return self:_read_async(callback) + end + return self:_read() +end + +function Path:head(lines) + lines = lines or 10 + self = check_self(self) + local chunk_size = 256 + + local fd = uv.fs_open(self:_fs_filename(), "r", 438) + if not fd then + return + end + local stat = assert(uv.fs_fstat(fd)) + if stat.type ~= "file" then + uv.fs_close(fd) + return nil + end + + local data = "" + local index, count = 0, 0 + while count < lines and index < stat.size do + local read_chunk = assert(uv.fs_read(fd, chunk_size, index)) + + local i = 0 + for char in read_chunk:gmatch "." do + if char == "\n" then + count = count + 1 + if count >= lines then + break + end + end + index = index + 1 + i = i + 1 + end + data = data .. read_chunk:sub(1, i) + end + assert(uv.fs_close(fd)) + + -- Remove potential newline at end of file + if data:sub(-1) == "\n" then + data = data:sub(1, -2) + end + + return data +end + +function Path:tail(lines) + lines = lines or 10 + self = check_self(self) + local chunk_size = 256 + + local fd = uv.fs_open(self:_fs_filename(), "r", 438) + if not fd then + return + end + local stat = assert(uv.fs_fstat(fd)) + if stat.type ~= "file" then + uv.fs_close(fd) + return nil + end + + local data = "" + local index, count = stat.size - 1, 0 + while count < lines and index > 0 do + local real_index = index - chunk_size + if real_index < 0 then + chunk_size = chunk_size + real_index + real_index = 0 + end + + local read_chunk = assert(uv.fs_read(fd, chunk_size, real_index)) + + local i = #read_chunk + while i > 0 do + local char = read_chunk:sub(i, i) + if char == "\n" then + count = count + 1 + if count >= lines then + break + end + end + index = index - 1 + i = i - 1 + end + data = read_chunk:sub(i + 1, #read_chunk) .. data + end + assert(uv.fs_close(fd)) + + return data +end + +function Path:readlines() + self = check_self(self) + + local data = self:read() + + data = data:gsub("\r", "") + return vim.split(data, "\n") +end + +function Path:iter() + local data = self:readlines() + local i = 0 + local n = #data + return function() + i = i + 1 + if i <= n then + return data[i] + end + end +end + +function Path:readbyterange(offset, length) + self = check_self(self) + + local fd = uv.fs_open(self:_fs_filename(), "r", 438) + if not fd then + return + end + local stat = assert(uv.fs_fstat(fd)) + if stat.type ~= "file" then + uv.fs_close(fd) + return nil + end + + if offset < 0 then + offset = stat.size + offset + -- Windows fails if offset is < 0 even though offset is defined as signed + -- http://docs.libuv.org/en/v1.x/fs.html#c.uv_fs_read + if offset < 0 then + offset = 0 + end + end + + local data = "" + while #data < length do + local read_chunk = assert(uv.fs_read(fd, length - #data, offset)) + if #read_chunk == 0 then + break + end + data = data .. read_chunk + offset = offset + #read_chunk + end + + assert(uv.fs_close(fd)) + + return data +end + +function Path:find_upwards(filename) + local folder = Path:new(self) + local root = path.root(folder:absolute()) + + while true do + local p = folder:joinpath(filename) + if p:exists() then + return p + end + if folder:absolute() == root then + break + end + folder = folder:parent() + end + return nil +end + +return Path diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/popup/init.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/popup/init.lua new file mode 100644 index 0000000..1c50c06 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/popup/init.lua @@ -0,0 +1,489 @@ +--- popup.lua +--- +--- Wrapper to make the popup api from vim in neovim. +--- Hope to get this part merged in at some point in the future. +--- +--- Please make sure to update "POPUP.md" with any changes and/or notes. + +local Border = require "plenary.window.border" +local Window = require "plenary.window" +local utils = require "plenary.popup.utils" + +local if_nil = vim.F.if_nil + +local popup = {} + +popup._pos_map = { + topleft = "NW", + topright = "NE", + botleft = "SW", + botright = "SE", +} + +-- Keep track of hidden popups, so we can load them with popup.show() +popup._hidden = {} + +-- Keep track of popup borders, so we don't have to pass them between functions +popup._borders = {} + +local function dict_default(options, key, default) + if options[key] == nil then + return default[key] + else + return options[key] + end +end + +-- Callbacks to be called later by popup.execute_callback +popup._callbacks = {} + +-- Convert the positional {vim_options} to compatible neovim options and add them to {win_opts} +-- If an option is not given in {vim_options}, fall back to {default_opts} +local function add_position_config(win_opts, vim_options, default_opts) + default_opts = default_opts or {} + + local cursor_relative_pos = function(pos_str, dim) + assert(string.find(pos_str, "^cursor"), "Invalid value for " .. dim) + win_opts.relative = "cursor" + local line = 0 + if (pos_str):match "cursor%+(%d+)" then + line = line + tonumber((pos_str):match "cursor%+(%d+)") + elseif (pos_str):match "cursor%-(%d+)" then + line = line - tonumber((pos_str):match "cursor%-(%d+)") + end + return line + end + + -- Feels like maxheight, minheight, maxwidth, minwidth will all be related + -- + -- maxheight Maximum height of the contents, excluding border and padding. + -- minheight Minimum height of the contents, excluding border and padding. + -- maxwidth Maximum width of the contents, excluding border, padding and scrollbar. + -- minwidth Minimum width of the contents, excluding border, padding and scrollbar. + local width = if_nil(vim_options.width, default_opts.width) + local height = if_nil(vim_options.height, default_opts.height) + win_opts.width = utils.bounded(width, vim_options.minwidth, vim_options.maxwidth) + win_opts.height = utils.bounded(height, vim_options.minheight, vim_options.maxheight) + + if vim_options.line and vim_options.line ~= 0 then + if type(vim_options.line) == "string" then + win_opts.row = cursor_relative_pos(vim_options.line, "row") + else + win_opts.row = vim_options.line - 1 + end + else + win_opts.row = math.floor((vim.o.lines - win_opts.height) / 2) + end + + if vim_options.col and vim_options.col ~= 0 then + if type(vim_options.col) == "string" then + win_opts.col = cursor_relative_pos(vim_options.col, "col") + else + win_opts.col = vim_options.col - 1 + end + else + win_opts.col = math.floor((vim.o.columns - win_opts.width) / 2) + end + + -- pos + -- + -- Using "topleft", "topright", "botleft", "botright" defines what corner of the popup "line" + -- and "col" are used for. When not set "topleft" behaviour is used. + -- Alternatively "center" can be used to position the popup in the center of the Neovim window, + -- in which case "line" and "col" are ignored. + if vim_options.pos then + if vim_options.pos == "center" then + vim_options.line = 0 + vim_options.col = 0 + win_opts.anchor = "NW" + else + win_opts.anchor = popup._pos_map[vim_options.pos] + end + else + win_opts.anchor = "NW" -- This is the default, but makes `posinvert` easier to implement + end + + -- , fixed When FALSE (the default), and: + -- , - "pos" is "botleft" or "topleft", and + -- , - "wrap" is off, and + -- , - the popup would be truncated at the right edge of + -- , the screen, then + -- , the popup is moved to the left so as to fit the + -- , contents on the screen. Set to TRUE to disable this. +end + +function popup.create(what, vim_options) + vim_options = vim.deepcopy(vim_options) + + local bufnr + if type(what) == "number" then + bufnr = what + else + bufnr = vim.api.nvim_create_buf(false, true) + assert(bufnr, "Failed to create buffer") + + vim.api.nvim_buf_set_option(bufnr, "bufhidden", "wipe") + vim.api.nvim_buf_set_option(bufnr, "modifiable", true) + + -- TODO: Handle list of lines + if type(what) == "string" then + what = { what } + else + assert(type(what) == "table", '"what" must be a table') + end + + -- padding List with numbers, defining the padding + -- above/right/below/left of the popup (similar to CSS). + -- An empty list uses a padding of 1 all around. The + -- padding goes around the text, inside any border. + -- Padding uses the 'wincolor' highlight. + -- Example: [1, 2, 1, 3] has 1 line of padding above, 2 + -- columns on the right, 1 line below and 3 columns on + -- the left. + if vim_options.padding then + local pad_top, pad_right, pad_below, pad_left + if vim.tbl_isempty(vim_options.padding) then + pad_top = 1 + pad_right = 1 + pad_below = 1 + pad_left = 1 + else + local padding = vim_options.padding + pad_top = padding[1] or 0 + pad_right = padding[2] or 0 + pad_below = padding[3] or 0 + pad_left = padding[4] or 0 + end + + local left_padding = string.rep(" ", pad_left) + local right_padding = string.rep(" ", pad_right) + for index = 1, #what do + what[index] = string.format("%s%s%s", left_padding, what[index], right_padding) + end + + for _ = 1, pad_top do + table.insert(what, 1, "") + end + + for _ = 1, pad_below do + table.insert(what, "") + end + end + + vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, what) + end + + local option_defaults = { + posinvert = true, + zindex = 50, + } + + vim_options.width = if_nil(vim_options.width, 1) + if type(what) == "number" then + vim_options.height = vim.api.nvim_buf_line_count(what) + else + for _, v in ipairs(what) do + vim_options.width = math.max(vim_options.width, #v) + end + vim_options.height = #what + end + + local win_opts = {} + win_opts.relative = "editor" + win_opts.style = "minimal" + + -- Add positional and sizing config to win_opts + add_position_config(win_opts, vim_options, { width = 1, height = 1 }) + + -- posinvert, When FALSE the value of "pos" is always used. When + -- , TRUE (the default) and the popup does not fit + -- , vertically and there is more space on the other side + -- , then the popup is placed on the other side of the + -- , position indicated by "line". + if dict_default(vim_options, "posinvert", option_defaults) then + if win_opts.anchor == "NW" or win_opts.anchor == "NE" then + if win_opts.row + win_opts.height > vim.o.lines and win_opts.row * 2 > vim.o.lines then + -- Don't know why, but this is how vim adjusts it + win_opts.row = win_opts.row - win_opts.height - 2 + end + elseif win_opts.anchor == "SW" or win_opts.anchor == "SE" then + if win_opts.row - win_opts.height < 0 and win_opts.row * 2 < vim.o.lines then + -- Don't know why, but this is how vim adjusts it + win_opts.row = win_opts.row + win_opts.height + 2 + end + end + end + + -- textprop, When present the popup is positioned next to a text + -- , property with this name and will move when the text + -- , property moves. Use an empty string to remove. See + -- , |popup-textprop-pos|. + -- related: + -- textpropwin + -- textpropid + + -- zindex, Priority for the popup, default 50. Minimum value is + -- , 1, maximum value is 32000. + local zindex = dict_default(vim_options, "zindex", option_defaults) + win_opts.zindex = utils.bounded(zindex, 1, 32000) + + -- noautocmd, undocumented vim default per https://github.com/vim/vim/issues/5737 + win_opts.noautocmd = if_nil(vim_options.noautocmd, true) + + -- focusable, + -- vim popups are not focusable windows + win_opts.focusable = if_nil(vim_options.focusable, false) + + local win_id + if vim_options.hidden then + assert(false, "I have not implemented this yet and don't know how") + else + win_id = vim.api.nvim_open_win(bufnr, false, win_opts) + end + + -- Moved, handled after since we need the window ID + if vim_options.moved then + if vim_options.moved == "any" then + vim.lsp.util.close_preview_autocmd({ "CursorMoved", "CursorMovedI" }, win_id) + -- elseif vim_options.moved == "word" then + -- TODO: Handle word, WORD, expr, and the range functions... which seem hard? + end + else + local silent = false + vim.cmd( + string.format( + "autocmd BufDelete %s ++once ++nested :lua require('plenary.window').try_close(%s, true)", + (silent and "") or "", + bufnr, + win_id + ) + ) + end + + if vim_options.time then + local timer = vim.loop.new_timer() + timer:start( + vim_options.time, + 0, + vim.schedule_wrap(function() + Window.try_close(win_id, false) + end) + ) + end + + -- Buffer Options + if vim_options.cursorline then + vim.api.nvim_win_set_option(win_id, "cursorline", true) + end + + if vim_options.wrap ~= nil then + -- set_option wrap should/will trigger autocmd, see https://github.com/neovim/neovim/pull/13247 + if vim_options.noautocmd then + vim.cmd(string.format("noautocmd lua vim.api.nvim_set_option(%s, wrap, %s)", win_id, vim_options.wrap)) + else + vim.api.nvim_win_set_option(win_id, "wrap", vim_options.wrap) + end + end + + -- ===== Not Implemented Options ===== + -- flip: not implemented at the time of writing + -- Mouse: + -- mousemoved: no idea how to do the things with the mouse, so it's an exercise for the reader. + -- drag: mouses are hard + -- resize: mouses are hard + -- close: mouses are hard + -- + -- scrollbar + -- scrollbarhighlight + -- thumbhighlight + + -- tabpage: seems useless + + -- Create border + + local should_show_border = nil + local border_options = {} + + -- border List with numbers, defining the border thickness + -- above/right/below/left of the popup (similar to CSS). + -- Only values of zero and non-zero are recognized. + -- An empty list uses a border all around. + if vim_options.border then + should_show_border = true + + if type(vim_options.border) == "boolean" or vim.tbl_isempty(vim_options.border) then + border_options.border_thickness = Border._default_thickness + elseif #vim_options.border == 4 then + border_options.border_thickness = { + top = utils.bounded(vim_options.border[1], 0, 1), + right = utils.bounded(vim_options.border[2], 0, 1), + bot = utils.bounded(vim_options.border[3], 0, 1), + left = utils.bounded(vim_options.border[4], 0, 1), + } + else + error(string.format("Invalid configuration for border: %s", vim.inspect(vim_options.border))) + end + elseif vim_options.border == false then + should_show_border = false + end + + if (should_show_border == nil or should_show_border) and vim_options.borderchars then + should_show_border = true + + -- borderchars List with characters, defining the character to use + -- for the top/right/bottom/left border. Optionally + -- followed by the character to use for the + -- topleft/topright/botright/botleft corner. + -- Example: ['-', '|', '-', '|', '┌', '┐', '┘', '└'] + -- When the list has one character it is used for all. + -- When the list has two characters the first is used for + -- the border lines, the second for the corners. + -- By default a double line is used all around when + -- 'encoding' is "utf-8" and 'ambiwidth' is "single", + -- otherwise ASCII characters are used. + + local b_top, b_right, b_bot, b_left, b_topleft, b_topright, b_botright, b_botleft + if vim_options.borderchars == nil then + b_top, b_right, b_bot, b_left, b_topleft, b_topright, b_botright, b_botleft = + "═", "║", "═", "║", "╔", "╗", "╝", "╚" + elseif #vim_options.borderchars == 1 then + local b_char = vim_options.borderchars[1] + b_top, b_right, b_bot, b_left, b_topleft, b_topright, b_botright, b_botleft = + b_char, b_char, b_char, b_char, b_char, b_char, b_char, b_char + elseif #vim_options.borderchars == 2 then + local b_char = vim_options.borderchars[1] + local c_char = vim_options.borderchars[2] + b_top, b_right, b_bot, b_left, b_topleft, b_topright, b_botright, b_botleft = + b_char, b_char, b_char, b_char, c_char, c_char, c_char, c_char + elseif #vim_options.borderchars == 8 then + b_top, b_right, b_bot, b_left, b_topleft, b_topright, b_botright, b_botleft = unpack(vim_options.borderchars) + else + error(string.format 'Not enough arguments for "borderchars"') + end + + border_options.top = b_top + border_options.bot = b_bot + border_options.right = b_right + border_options.left = b_left + border_options.topleft = b_topleft + border_options.topright = b_topright + border_options.botright = b_botright + border_options.botleft = b_botleft + end + + -- title + if vim_options.title then + -- TODO: Check out how title works with weird border combos. + border_options.title = vim_options.title + end + + local border = nil + if should_show_border then + border_options.focusable = vim_options.border_focusable + border_options.highlight = vim_options.borderhighlight and string.format("Normal:%s", vim_options.borderhighlight) + border_options.titlehighlight = vim_options.titlehighlight + border = Border:new(bufnr, win_id, win_opts, border_options) + popup._borders[win_id] = border + end + + if vim_options.highlight then + vim.api.nvim_win_set_option( + win_id, + "winhl", + string.format("Normal:%s,EndOfBuffer:%s", vim_options.highlight, vim_options.highlight) + ) + end + + -- enter + local should_enter = vim_options.enter + if should_enter == nil then + should_enter = true + end + + if should_enter then + -- set focus after border creation so that it's properly placed (especially + -- in relative cursor layout) + if vim_options.noautocmd then + vim.cmd("noautocmd lua vim.api.nvim_set_current_win(" .. win_id .. ")") + else + vim.api.nvim_set_current_win(win_id) + end + end + + -- callback + if vim_options.callback then + popup._callbacks[bufnr] = function() + -- (jbyuki): Giving win_id is pointless here because it's closed right afterwards + -- but it might make more sense once hidden is implemented + local row, _ = unpack(vim.api.nvim_win_get_cursor(win_id)) + vim_options.callback(win_id, what[row]) + vim.api.nvim_win_close(win_id, true) + end + vim.api.nvim_buf_set_keymap( + bufnr, + "n", + "", + 'lua require"plenary.popup".execute_callback(' .. bufnr .. ")", + { noremap = true } + ) + end + + if vim_options.finalize_callback then + vim_options.finalize_callback(win_id, bufnr) + end + + -- TODO: Perhaps there's a way to return an object that looks like a window id, + -- but actually has some extra metadata about it. + -- + -- This would make `hidden` a lot easier to manage + return win_id, { + win_id = win_id, + border = border, + } +end + +-- Move popup with window id {win_id} to the position specified with {vim_options}. +-- {vim_options} may contain the following items that determine the popup position/size: +-- - line +-- - col +-- - height +-- - width +-- - maxheight/minheight +-- - maxwidth/minwidth +-- - pos +-- Unimplemented vim options here include: fixed +function popup.move(win_id, vim_options) + -- Create win_options + local win_opts = {} + win_opts.relative = "editor" + + local current_pos = vim.api.nvim_win_get_position(win_id) + local default_opts = { + width = vim.api.nvim_win_get_width(win_id), + height = vim.api.nvim_win_get_height(win_id), + row = current_pos[1], + col = current_pos[2], + } + + -- Add positional and sizing config to win_opts + add_position_config(win_opts, vim_options, default_opts) + + -- Update content window + vim.api.nvim_win_set_config(win_id, win_opts) + + -- Update border window (if present) + local border = popup._borders[win_id] + if border ~= nil then + border:move(win_opts, border._border_win_options) + end +end + +function popup.execute_callback(bufnr) + if popup._callbacks[bufnr] then + local wrapper = popup._callbacks[bufnr] + wrapper() + popup._callbacks[bufnr] = nil + end +end + +return popup diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/popup/utils.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/popup/utils.lua new file mode 100644 index 0000000..e665e15 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/popup/utils.lua @@ -0,0 +1,33 @@ +local utils = {} + +utils.bounded = function(value, min, max) + min = min or 0 + max = max or math.huge + + if min then + value = math.max(value, min) + end + if max then + value = math.min(value, max) + end + + return value +end + +utils.apply_defaults = function(original, defaults) + if original == nil then + original = {} + end + + original = vim.deepcopy(original) + + for k, v in pairs(defaults) do + if original[k] == nil then + original[k] = v + end + end + + return original +end + +return utils diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/profile.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/profile.lua new file mode 100644 index 0000000..0afc4a7 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/profile.lua @@ -0,0 +1,31 @@ +local profile = {} + +-- bundled version of upstream jit.p until LuaJIT is updated to include +-- https://github.com/LuaJIT/LuaJIT/commit/95140c50010c0557af66dac944403a1a65dd312c +local p = require'plenary.profile.p' + +---start profiling using LuaJIT profiler +---@param out name and path of log file +---@param opts table of options +--- flame (bool, default false) write log in flamegraph format +-- (see https://github.com/jonhoo/inferno) +function profile.start(out, opts) + out = out or "profile.log" + opts = opts or {} + local popts = "10,i1,s,m0" + if opts.flame then popts = popts .. ",G" end + p.start(popts, out) +end + +---stop profiling +profile.stop = p.stop + +function profile.benchmark(iterations, f, ...) + local start_time = vim.loop.hrtime() + for _ = 1, iterations do + f(...) + end + return (vim.loop.hrtime() - start_time) / 1E9 +end + +return profile diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/profile/lua_profiler.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/profile/lua_profiler.lua new file mode 100644 index 0000000..e29e06f --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/profile/lua_profiler.lua @@ -0,0 +1,252 @@ +--[[ Copyright (c) 2018-2020, Charles Mallah ]] +-- Released with MIT License +-- +-- Originally link: +-- https://github.com/charlesmallah/lua-profiler +-- +-- Hopefully will add some better neovim stuff in the future. +-- Shoutout to @clason for finding this. + + +---------------------------------------| +--- Configuration +-- +---------------------------------------| + +local PROFILER_FILENAME = "lua/telescope/profile/lua_profiler.lua" -- Location and name of profiler (to remove itself from reports); +-- e.g. if this is in a 'tool' folder, rename this as: "tool/profiler.lua" + +local EMPTY_TIME = "0.0000" -- Detect empty time, replace with tag below +local emptyToThis = "~" + +local fileWidth = 75 +local funcWidth = 22 +local lineWidth = 6 +local timeWidth = 7 +local relaWidth = 6 +local callWidth = 4 + +local reportSaved = " > Report saved to" +local formatOutputHeader = "| %-"..fileWidth.."s: %-"..funcWidth.."s: %-"..lineWidth.."s: %-"..timeWidth.."s: %-"..relaWidth.."s: %-"..callWidth.."s|\n" +local formatOutputTitle = "%-"..fileWidth.."."..fileWidth.."s: %-"..funcWidth.."."..funcWidth.."s: %-"..lineWidth.."s" -- File / Function / Line count +local formatOutput = "| %s: %-"..timeWidth.."s: %-"..relaWidth.."s: %-"..callWidth.."s|\n" -- Time / Relative / Called +local formatTotalTime = "TOTAL TIME = %f s\n" +local formatFunLine = "%"..(lineWidth - 2).."i" +local formatFunTime = "%04.4f" +local formatFunRelative = "%03.1f" +local formatFunCount = "%"..(callWidth - 1).."i" +local formatHeader = string.format(formatOutputHeader, "FILE", "FUNCTION", "LINE", "TIME", "%", "#") + + +---------------------------------------| +--- Locals +-- +---------------------------------------| + +local module = {} + +local getTime = os.clock +local string = string +local debug = debug +local table = table + +local TABL_REPORT_CACHE = {} +local TABL_REPORTS = {} +local reportCount = 0 +local startTime = 0 +local stopTime = 0 + +local printFun = nil +local verbosePrint = false + +local function functionReport(information) + local src = information.short_src + if src == nil then + src = "" + elseif string.sub(src, #src - 3, #src) == ".lua" then + src = string.sub(src, 1, #src - 4) + end + + local name = information.name + if name == nil then + name = "Anon" + elseif string.sub(name, #name - 1, #name) == "_l" then + name = string.sub(name, 1, #name - 2) + end + + local title = string.format(formatOutputTitle, + src, name, + string.format(formatFunLine, information.linedefined or 0)) + + local funcReport = TABL_REPORT_CACHE[title] + if not funcReport then + funcReport = { + title = string.format(formatOutputTitle, + src, name, + string.format(formatFunLine, information.linedefined or 0)), + count = 0, + timer = 0, + } + TABL_REPORT_CACHE[title] = funcReport + reportCount = reportCount + 1 + TABL_REPORTS[reportCount] = funcReport + end + + return funcReport +end + +local onDebugHook = function(hookType) + local information = debug.getinfo(2, "nS") + if hookType == "call" then + local funcReport = functionReport(information) + funcReport.callTime = getTime() + funcReport.count = funcReport.count + 1 + elseif hookType == "return" then + local funcReport = functionReport(information) + if funcReport.callTime and funcReport.count > 0 then + funcReport.timer = funcReport.timer + (getTime() - funcReport.callTime) + end + end +end + +local function charRepetition(n, character) + local s = "" + character = character or " " + for _ = 1, n do + s = s..character + end + return s +end + +local function singleSearchReturn(str, search) + for _ in string.gmatch(str, search) do + do return true end + end + return false +end + +local divider = charRepetition(#formatHeader - 1, "-").."\n" + + +---------------------------------------| +--- Functions +-- +---------------------------------------| + +--- Attach a print function to the profiler, to receive a single string parameter +-- +function module.attachPrintFunction(fn, verbose) + printFun = fn + if verbose ~= nil then + verbosePrint = verbose + end +end + +--- +-- +function module.start() + TABL_REPORT_CACHE = {} + TABL_REPORTS = {} + reportCount = 0 + startTime = getTime() + stopTime = nil + debug.sethook(onDebugHook, "cr", 0) +end + +--- +-- +function module.stop() + stopTime = getTime() + debug.sethook() +end + +--- Writes the profile report to file +-- +function module.report(filename) + if stopTime == nil then + module.stop() + end + + if reportCount > 0 then + filename = filename or "profiler.log" + table.sort(TABL_REPORTS, function(a, b) return a.timer > b.timer end) + local file = io.open(filename, "w+") + + if reportCount > 0 then + local divide = false + local totalTime = stopTime - startTime + local totalTimeOutput = " > "..string.format(formatTotalTime, totalTime) + + file:write(totalTimeOutput) + if printFun ~= nil then + printFun(totalTimeOutput) + end + + file:write("\n"..divider) + file:write(formatHeader) + file:write(divider) + + for i = 1, reportCount do + local funcReport = TABL_REPORTS[i] + + if funcReport.count > 0 and funcReport.timer <= totalTime then + local printThis = true + + if PROFILER_FILENAME ~= "" then + if singleSearchReturn(funcReport.title, PROFILER_FILENAME) then + printThis = false + end + end + + -- Remove line if not needed + if printThis == true then + if singleSearchReturn(funcReport.title, "[[C]]") then + printThis = false + end + end + + if printThis == true then + local count = string.format(formatFunCount, funcReport.count) + local timer = string.format(formatFunTime, funcReport.timer) + local relTime = string.format(formatFunRelative, (funcReport.timer / totalTime) * 100) + if divide == false and timer == EMPTY_TIME then + file:write(divider) + divide = true + end + + -- Replace + if timer == EMPTY_TIME then + timer = emptyToThis + relTime = emptyToThis + end + + -- Build final line + local outputLine = string.format(formatOutput, funcReport.title, timer, relTime, count) + file:write(outputLine) + + -- This is a verbose print to the printFun, however maybe make this smaller for on screen debug? + if printFun ~= nil and verbosePrint == true then + printFun(outputLine) + end + + end + end + end + + file:write(divider) + + end + + file:close() + + if printFun ~= nil then + printFun(reportSaved.."'"..filename.."'") + end + + end + +end + +--- End +-- +return module diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/profile/memory_profiler.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/profile/memory_profiler.lua new file mode 100644 index 0000000..64476ed --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/profile/memory_profiler.lua @@ -0,0 +1,1072 @@ +-- https://github.com/yaukeywang/LuaMemorySnapshotDump +-- Collect memory reference info. +-- +-- @filename MemoryReferenceInfo.lua +-- @author WangYaoqi +-- @date 2016-02-03 + +-- The global config of the mri. +local cConfig = +{ + m_bAllMemoryRefFileAddTime = true, + m_bSingleMemoryRefFileAddTime = true, + m_bComparedMemoryRefFileAddTime = true +} + +-- Get the format string of date time. +local function FormatDateTimeNow() + local cDateTime = os.date("*t") + local strDateTime = string.format("%04d%02d%02d-%02d%02d%02d", tostring(cDateTime.year), tostring(cDateTime.month), tostring(cDateTime.day), + tostring(cDateTime.hour), tostring(cDateTime.min), tostring(cDateTime.sec)) + return strDateTime +end + +-- Get the string result without overrided __tostring. +local function GetOriginalToStringResult(cObject) + if not cObject then + return "" + end + + local cMt = getmetatable(cObject) + if not cMt then + return tostring(cObject) + end + + -- Check tostring override. + local strName = "" + local cToString = rawget(cMt, "__tostring") + if cToString then + rawset(cMt, "__tostring", nil) + strName = tostring(cObject) + rawset(cMt, "__tostring", cToString) + else + strName = tostring(cObject) + end + + return strName +end + +-- Create a container to collect the mem ref info results. +local function CreateObjectReferenceInfoContainer() + -- Create new container. + local cContainer = {} + + -- Contain [table/function] - [reference count] info. + local cObjectReferenceCount = {} + setmetatable(cObjectReferenceCount, {__mode = "k"}) + + -- Contain [table/function] - [name] info. + local cObjectAddressToName = {} + setmetatable(cObjectAddressToName, {__mode = "k"}) + + -- Set members. + cContainer.m_cObjectReferenceCount = cObjectReferenceCount + cContainer.m_cObjectAddressToName = cObjectAddressToName + + -- For stack info. + cContainer.m_nStackLevel = -1 + cContainer.m_strShortSrc = "None" + cContainer.m_nCurrentLine = -1 + + return cContainer +end + +-- Create a container to collect the mem ref info results from a dumped file. +-- strFilePath - The file path. +local function CreateObjectReferenceInfoContainerFromFile(strFilePath) + -- Create a empty container. + local cContainer = CreateObjectReferenceInfoContainer() + cContainer.m_strShortSrc = strFilePath + + -- Cache ref info. + local cRefInfo = cContainer.m_cObjectReferenceCount + local cNameInfo = cContainer.m_cObjectAddressToName + + -- Read each line from file. + local cFile = assert(io.open(strFilePath, "rb")) + for strLine in cFile:lines() do + local strHeader = string.sub(strLine, 1, 2) + if "--" ~= strHeader then + local _, _, strAddr, strName, strRefCount= string.find(strLine, "(.+)\t(.*)\t(%d+)") + if strAddr then + cRefInfo[strAddr] = strRefCount + cNameInfo[strAddr] = strName + end + end + end + + -- Close and clear file handler. + io.close(cFile) + cFile = nil + + return cContainer +end + +-- Create a container to collect the mem ref info results from a dumped file. +-- strObjectName - The object name you need to collect info. +-- cObject - The object you need to collect info. +local function CreateSingleObjectReferenceInfoContainer(strObjectName, cObject) + -- Create new container. + local cContainer = {} + + -- Contain [address] - [true] info. + local cObjectExistTag = {} + setmetatable(cObjectExistTag, {__mode = "k"}) + + -- Contain [name] - [true] info. + local cObjectAliasName = {} + + -- Contain [access] - [true] info. + local cObjectAccessTag = {} + setmetatable(cObjectAccessTag, {__mode = "k"}) + + -- Set members. + cContainer.m_cObjectExistTag = cObjectExistTag + cContainer.m_cObjectAliasName = cObjectAliasName + cContainer.m_cObjectAccessTag = cObjectAccessTag + + -- For stack info. + cContainer.m_nStackLevel = -1 + cContainer.m_strShortSrc = "None" + cContainer.m_nCurrentLine = -1 + + -- Init with object values. + cContainer.m_strObjectName = strObjectName + cContainer.m_strAddressName = (("string" == type(cObject)) and ("\"" .. tostring(cObject) .. "\"")) or GetOriginalToStringResult(cObject) + cContainer.m_cObjectExistTag[cObject] = true + + return cContainer +end + +-- Collect memory reference info from a root table or function. +-- strName - The root object name that start to search, default is "_G" if leave this to nil. +-- cObject - The root object that start to search, default is _G if leave this to nil. +-- cDumpInfoContainer - The container of the dump result info. +local function CollectObjectReferenceInMemory(strName, cObject, cDumpInfoContainer) + if not cObject then + return + end + + if not strName then + strName = "" + end + + -- Check container. + if (not cDumpInfoContainer) then + cDumpInfoContainer = CreateObjectReferenceInfoContainer() + end + + -- Check stack. + if cDumpInfoContainer.m_nStackLevel > 0 then + local cStackInfo = debug.getinfo(cDumpInfoContainer.m_nStackLevel, "Sl") + if cStackInfo then + cDumpInfoContainer.m_strShortSrc = cStackInfo.short_src + cDumpInfoContainer.m_nCurrentLine = cStackInfo.currentline + end + + cDumpInfoContainer.m_nStackLevel = -1 + end + + -- Get ref and name info. + local cRefInfoContainer = cDumpInfoContainer.m_cObjectReferenceCount + local cNameInfoContainer = cDumpInfoContainer.m_cObjectAddressToName + + local strType = type(cObject) + if "table" == strType then + -- Check table with class name. + if rawget(cObject, "__cname") then + if "string" == type(cObject.__cname) then + strName = strName .. "[class:" .. cObject.__cname .. "]" + end + elseif rawget(cObject, "class") then + if "string" == type(cObject.class) then + strName = strName .. "[class:" .. cObject.class .. "]" + end + elseif rawget(cObject, "_className") then + if "string" == type(cObject._className) then + strName = strName .. "[class:" .. cObject._className .. "]" + end + end + + -- Check if table is _G. + if cObject == _G then + strName = strName .. "[_G]" + end + + -- Get metatable. + local bWeakK = false + local bWeakV = false + local cMt = getmetatable(cObject) + if cMt then + -- Check mode. + local strMode = rawget(cMt, "__mode") + if strMode then + if "k" == strMode then + bWeakK = true + elseif "v" == strMode then + bWeakV = true + elseif "kv" == strMode then + bWeakK = true + bWeakV = true + end + end + end + + -- Add reference and name. + cRefInfoContainer[cObject] = (cRefInfoContainer[cObject] and (cRefInfoContainer[cObject] + 1)) or 1 + if cNameInfoContainer[cObject] then + return + end + + -- Set name. + cNameInfoContainer[cObject] = strName + + -- Dump table key and value. + for k, v in pairs(cObject) do + -- Check key type. + local strKeyType = type(k) + if "table" == strKeyType then + if not bWeakK then + CollectObjectReferenceInMemory(strName .. ".[table:key.table]", k, cDumpInfoContainer) + end + + if not bWeakV then + CollectObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer) + end + elseif "function" == strKeyType then + if not bWeakK then + CollectObjectReferenceInMemory(strName .. ".[table:key.function]", k, cDumpInfoContainer) + end + + if not bWeakV then + CollectObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer) + end + elseif "thread" == strKeyType then + if not bWeakK then + CollectObjectReferenceInMemory(strName .. ".[table:key.thread]", k, cDumpInfoContainer) + end + + if not bWeakV then + CollectObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer) + end + elseif "userdata" == strKeyType then + if not bWeakK then + CollectObjectReferenceInMemory(strName .. ".[table:key.userdata]", k, cDumpInfoContainer) + end + + if not bWeakV then + CollectObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer) + end + else + CollectObjectReferenceInMemory(strName .. "." .. k, v, cDumpInfoContainer) + end + end + + -- Dump metatable. + if cMt then + CollectObjectReferenceInMemory(strName ..".[metatable]", cMt, cDumpInfoContainer) + end + elseif "function" == strType then + -- Get function info. + local cDInfo = debug.getinfo(cObject, "Su") + + -- Write this info. + cRefInfoContainer[cObject] = (cRefInfoContainer[cObject] and (cRefInfoContainer[cObject] + 1)) or 1 + if cNameInfoContainer[cObject] then + return + end + + -- Set name. + cNameInfoContainer[cObject] = strName .. "[line:" .. tostring(cDInfo.linedefined) .. "@file:" .. cDInfo.short_src .. "]" + + -- Get upvalues. + local nUpsNum = cDInfo.nups + for i = 1, nUpsNum do + local strUpName, cUpValue = debug.getupvalue(cObject, i) + local strUpValueType = type(cUpValue) + --print(strUpName, cUpValue) + if "table" == strUpValueType then + CollectObjectReferenceInMemory(strName .. ".[ups:table:" .. strUpName .. "]", cUpValue, cDumpInfoContainer) + elseif "function" == strUpValueType then + CollectObjectReferenceInMemory(strName .. ".[ups:function:" .. strUpName .. "]", cUpValue, cDumpInfoContainer) + elseif "thread" == strUpValueType then + CollectObjectReferenceInMemory(strName .. ".[ups:thread:" .. strUpName .. "]", cUpValue, cDumpInfoContainer) + elseif "userdata" == strUpValueType then + CollectObjectReferenceInMemory(strName .. ".[ups:userdata:" .. strUpName .. "]", cUpValue, cDumpInfoContainer) + end + end + + -- Dump environment table. + local getfenv = debug.getfenv + if getfenv then + local cEnv = getfenv(cObject) + if cEnv then + CollectObjectReferenceInMemory(strName ..".[function:environment]", cEnv, cDumpInfoContainer) + end + end + elseif "thread" == strType then + -- Add reference and name. + cRefInfoContainer[cObject] = (cRefInfoContainer[cObject] and (cRefInfoContainer[cObject] + 1)) or 1 + if cNameInfoContainer[cObject] then + return + end + + -- Set name. + cNameInfoContainer[cObject] = strName + + -- Dump environment table. + local getfenv = debug.getfenv + if getfenv then + local cEnv = getfenv(cObject) + if cEnv then + CollectObjectReferenceInMemory(strName ..".[thread:environment]", cEnv, cDumpInfoContainer) + end + end + + -- Dump metatable. + local cMt = getmetatable(cObject) + if cMt then + CollectObjectReferenceInMemory(strName ..".[thread:metatable]", cMt, cDumpInfoContainer) + end + elseif "userdata" == strType then + -- Add reference and name. + cRefInfoContainer[cObject] = (cRefInfoContainer[cObject] and (cRefInfoContainer[cObject] + 1)) or 1 + if cNameInfoContainer[cObject] then + return + end + + -- Set name. + cNameInfoContainer[cObject] = strName + + -- Dump environment table. + local getfenv = debug.getfenv + if getfenv then + local cEnv = getfenv(cObject) + if cEnv then + CollectObjectReferenceInMemory(strName ..".[userdata:environment]", cEnv, cDumpInfoContainer) + end + end + + -- Dump metatable. + local cMt = getmetatable(cObject) + if cMt then + CollectObjectReferenceInMemory(strName ..".[userdata:metatable]", cMt, cDumpInfoContainer) + end + elseif "string" == strType then + -- Add reference and name. + cRefInfoContainer[cObject] = (cRefInfoContainer[cObject] and (cRefInfoContainer[cObject] + 1)) or 1 + if cNameInfoContainer[cObject] then + return + end + + -- Set name. + cNameInfoContainer[cObject] = strName .. "[" .. strType .. "]" + else + -- For "number" and "boolean". (If you want to dump them, uncomment the followed lines.) + + -- -- Add reference and name. + -- cRefInfoContainer[cObject] = (cRefInfoContainer[cObject] and (cRefInfoContainer[cObject] + 1)) or 1 + -- if cNameInfoContainer[cObject] then + -- return + -- end + + -- -- Set name. + -- cNameInfoContainer[cObject] = strName .. "[" .. strType .. ":" .. tostring(cObject) .. "]" + end +end + +-- Collect memory reference info of a single object from a root table or function. +-- strName - The root object name that start to search, can not be nil. +-- cObject - The root object that start to search, can not be nil. +-- cDumpInfoContainer - The container of the dump result info. +local function CollectSingleObjectReferenceInMemory(strName, cObject, cDumpInfoContainer) + if not cObject then + return + end + + if not strName then + strName = "" + end + + -- Check container. + if (not cDumpInfoContainer) then + cDumpInfoContainer = CreateObjectReferenceInfoContainer() + end + + -- Check stack. + if cDumpInfoContainer.m_nStackLevel > 0 then + local cStackInfo = debug.getinfo(cDumpInfoContainer.m_nStackLevel, "Sl") + if cStackInfo then + cDumpInfoContainer.m_strShortSrc = cStackInfo.short_src + cDumpInfoContainer.m_nCurrentLine = cStackInfo.currentline + end + + cDumpInfoContainer.m_nStackLevel = -1 + end + + local cExistTag = cDumpInfoContainer.m_cObjectExistTag + local cNameAllAlias = cDumpInfoContainer.m_cObjectAliasName + local cAccessTag = cDumpInfoContainer.m_cObjectAccessTag + + local strType = type(cObject) + if "table" == strType then + -- Check table with class name. + if rawget(cObject, "__cname") then + if "string" == type(cObject.__cname) then + strName = strName .. "[class:" .. cObject.__cname .. "]" + end + elseif rawget(cObject, "class") then + if "string" == type(cObject.class) then + strName = strName .. "[class:" .. cObject.class .. "]" + end + elseif rawget(cObject, "_className") then + if "string" == type(cObject._className) then + strName = strName .. "[class:" .. cObject._className .. "]" + end + end + + -- Check if table is _G. + if cObject == _G then + strName = strName .. "[_G]" + end + + -- Get metatable. + local bWeakK = false + local bWeakV = false + local cMt = getmetatable(cObject) + if cMt then + -- Check mode. + local strMode = rawget(cMt, "__mode") + if strMode then + if "k" == strMode then + bWeakK = true + elseif "v" == strMode then + bWeakV = true + elseif "kv" == strMode then + bWeakK = true + bWeakV = true + end + end + end + + -- Check if the specified object. + if cExistTag[cObject] and (not cNameAllAlias[strName]) then + cNameAllAlias[strName] = true + end + + -- Add reference and name. + if cAccessTag[cObject] then + return + end + + -- Get this name. + cAccessTag[cObject] = true + + -- Dump table key and value. + for k, v in pairs(cObject) do + -- Check key type. + local strKeyType = type(k) + if "table" == strKeyType then + if not bWeakK then + CollectSingleObjectReferenceInMemory(strName .. ".[table:key.table]", k, cDumpInfoContainer) + end + + if not bWeakV then + CollectSingleObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer) + end + elseif "function" == strKeyType then + if not bWeakK then + CollectSingleObjectReferenceInMemory(strName .. ".[table:key.function]", k, cDumpInfoContainer) + end + + if not bWeakV then + CollectSingleObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer) + end + elseif "thread" == strKeyType then + if not bWeakK then + CollectSingleObjectReferenceInMemory(strName .. ".[table:key.thread]", k, cDumpInfoContainer) + end + + if not bWeakV then + CollectSingleObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer) + end + elseif "userdata" == strKeyType then + if not bWeakK then + CollectSingleObjectReferenceInMemory(strName .. ".[table:key.userdata]", k, cDumpInfoContainer) + end + + if not bWeakV then + CollectSingleObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer) + end + else + CollectSingleObjectReferenceInMemory(strName .. "." .. k, v, cDumpInfoContainer) + end + end + + -- Dump metatable. + if cMt then + CollectSingleObjectReferenceInMemory(strName ..".[metatable]", cMt, cDumpInfoContainer) + end + elseif "function" == strType then + -- Get function info. + local cDInfo = debug.getinfo(cObject, "Su") + local cCombinedName = strName .. "[line:" .. tostring(cDInfo.linedefined) .. "@file:" .. cDInfo.short_src .. "]" + + -- Check if the specified object. + if cExistTag[cObject] and (not cNameAllAlias[cCombinedName]) then + cNameAllAlias[cCombinedName] = true + end + + -- Write this info. + if cAccessTag[cObject] then + return + end + + -- Set name. + cAccessTag[cObject] = true + + -- Get upvalues. + local nUpsNum = cDInfo.nups + for i = 1, nUpsNum do + local strUpName, cUpValue = debug.getupvalue(cObject, i) + local strUpValueType = type(cUpValue) + --print(strUpName, cUpValue) + if "table" == strUpValueType then + CollectSingleObjectReferenceInMemory(strName .. ".[ups:table:" .. strUpName .. "]", cUpValue, cDumpInfoContainer) + elseif "function" == strUpValueType then + CollectSingleObjectReferenceInMemory(strName .. ".[ups:function:" .. strUpName .. "]", cUpValue, cDumpInfoContainer) + elseif "thread" == strUpValueType then + CollectSingleObjectReferenceInMemory(strName .. ".[ups:thread:" .. strUpName .. "]", cUpValue, cDumpInfoContainer) + elseif "userdata" == strUpValueType then + CollectSingleObjectReferenceInMemory(strName .. ".[ups:userdata:" .. strUpName .. "]", cUpValue, cDumpInfoContainer) + end + end + + -- Dump environment table. + local getfenv = debug.getfenv + if getfenv then + local cEnv = getfenv(cObject) + if cEnv then + CollectSingleObjectReferenceInMemory(strName ..".[function:environment]", cEnv, cDumpInfoContainer) + end + end + elseif "thread" == strType then + -- Check if the specified object. + if cExistTag[cObject] and (not cNameAllAlias[strName]) then + cNameAllAlias[strName] = true + end + + -- Add reference and name. + if cAccessTag[cObject] then + return + end + + -- Get this name. + cAccessTag[cObject] = true + + -- Dump environment table. + local getfenv = debug.getfenv + if getfenv then + local cEnv = getfenv(cObject) + if cEnv then + CollectSingleObjectReferenceInMemory(strName ..".[thread:environment]", cEnv, cDumpInfoContainer) + end + end + + -- Dump metatable. + local cMt = getmetatable(cObject) + if cMt then + CollectSingleObjectReferenceInMemory(strName ..".[thread:metatable]", cMt, cDumpInfoContainer) + end + elseif "userdata" == strType then + -- Check if the specified object. + if cExistTag[cObject] and (not cNameAllAlias[strName]) then + cNameAllAlias[strName] = true + end + + -- Add reference and name. + if cAccessTag[cObject] then + return + end + + -- Get this name. + cAccessTag[cObject] = true + + -- Dump environment table. + local getfenv = debug.getfenv + if getfenv then + local cEnv = getfenv(cObject) + if cEnv then + CollectSingleObjectReferenceInMemory(strName ..".[userdata:environment]", cEnv, cDumpInfoContainer) + end + end + + -- Dump metatable. + local cMt = getmetatable(cObject) + if cMt then + CollectSingleObjectReferenceInMemory(strName ..".[userdata:metatable]", cMt, cDumpInfoContainer) + end + elseif "string" == strType then + -- Check if the specified object. + if cExistTag[cObject] and (not cNameAllAlias[strName]) then + cNameAllAlias[strName] = true + end + + -- Add reference and name. + if cAccessTag[cObject] then + return + end + + -- Get this name. + cAccessTag[cObject] = true + else + -- For "number" and "boolean" type, they are not object type, skip. + end +end + +-- The base method to dump a mem ref info result into a file. +-- strSavePath - The save path of the file to store the result, must be a directory path, If nil or "" then the result will output to console as print does. +-- strExtraFileName - If you want to add extra info append to the end of the result file, give a string, nothing will do if set to nil or "". +-- nMaxRescords - How many rescords of the results in limit to save in the file or output to the console, -1 will give all the result. +-- strRootObjectName - The header info to show the root object name, can be nil. +-- cRootObject - The header info to show the root object address, can be nil. +-- cDumpInfoResultsBase - The base dumped mem info result, nil means no compare and only output cDumpInfoResults, otherwise to compare with cDumpInfoResults. +-- cDumpInfoResults - The compared dumped mem info result, dump itself only if cDumpInfoResultsBase is nil, otherwise dump compared results with cDumpInfoResultsBase. +local function OutputMemorySnapshot(strSavePath, strExtraFileName, nMaxRescords, strRootObjectName, cRootObject, cDumpInfoResultsBase, cDumpInfoResults) + -- Check results. + if not cDumpInfoResults then + return + end + + -- Get time format string. + local strDateTime = FormatDateTimeNow() + + -- Collect memory info. + local cRefInfoBase = (cDumpInfoResultsBase and cDumpInfoResultsBase.m_cObjectReferenceCount) or nil + local cNameInfoBase = (cDumpInfoResultsBase and cDumpInfoResultsBase.m_cObjectAddressToName) or nil + local cRefInfo = cDumpInfoResults.m_cObjectReferenceCount + local cNameInfo = cDumpInfoResults.m_cObjectAddressToName + + -- Create a cache result to sort by ref count. + local cRes = {} + local nIdx = 0 + for k in pairs(cRefInfo) do + nIdx = nIdx + 1 + cRes[nIdx] = k + end + + -- Sort result. + table.sort(cRes, function (l, r) + return cRefInfo[l] > cRefInfo[r] + end) + + -- Save result to file. + local bOutputFile = strSavePath and (string.len(strSavePath) > 0) + local cOutputHandle = nil + local cOutputEntry = print + + if bOutputFile then + -- Check save path affix. + local strAffix = string.sub(strSavePath, -1) + if ("/" ~= strAffix) and ("\\" ~= strAffix) then + strSavePath = strSavePath .. "/" + end + + -- Combine file name. + local strFileName = strSavePath .. "LuaMemRefInfo-All" + if (not strExtraFileName) or (0 == string.len(strExtraFileName)) then + if cDumpInfoResultsBase then + if cConfig.m_bComparedMemoryRefFileAddTime then + strFileName = strFileName .. "-[" .. strDateTime .. "].txt" + else + strFileName = strFileName .. ".txt" + end + else + if cConfig.m_bAllMemoryRefFileAddTime then + strFileName = strFileName .. "-[" .. strDateTime .. "].txt" + else + strFileName = strFileName .. ".txt" + end + end + else + if cDumpInfoResultsBase then + if cConfig.m_bComparedMemoryRefFileAddTime then + strFileName = strFileName .. "-[" .. strDateTime .. "]-[" .. strExtraFileName .. "].txt" + else + strFileName = strFileName .. "-[" .. strExtraFileName .. "].txt" + end + else + if cConfig.m_bAllMemoryRefFileAddTime then + strFileName = strFileName .. "-[" .. strDateTime .. "]-[" .. strExtraFileName .. "].txt" + else + strFileName = strFileName .. "-[" .. strExtraFileName .. "].txt" + end + end + end + + local cFile = assert(io.open(strFileName, "w")) + cOutputHandle = cFile + cOutputEntry = cFile.write + end + + local cOutputer = function (strContent) + if cOutputHandle then + cOutputEntry(cOutputHandle, strContent) + else + cOutputEntry(strContent) + end + end + + -- Write table header. + if cDumpInfoResultsBase then + cOutputer("--------------------------------------------------------\n") + cOutputer("-- This is compared memory information.\n") + + cOutputer("--------------------------------------------------------\n") + cOutputer("-- Collect base memory reference at line:" .. tostring(cDumpInfoResultsBase.m_nCurrentLine) .. "@file:" .. cDumpInfoResultsBase.m_strShortSrc .. "\n") + cOutputer("-- Collect compared memory reference at line:" .. tostring(cDumpInfoResults.m_nCurrentLine) .. "@file:" .. cDumpInfoResults.m_strShortSrc .. "\n") + else + cOutputer("--------------------------------------------------------\n") + cOutputer("-- Collect memory reference at line:" .. tostring(cDumpInfoResults.m_nCurrentLine) .. "@file:" .. cDumpInfoResults.m_strShortSrc .. "\n") + end + + cOutputer("--------------------------------------------------------\n") + cOutputer("-- [Table/Function/String Address/Name]\t[Reference Path]\t[Reference Count]\n") + cOutputer("--------------------------------------------------------\n") + + if strRootObjectName and cRootObject then + if "string" == type(cRootObject) then + cOutputer("-- From Root Object: \"" .. tostring(cRootObject) .. "\" (" .. strRootObjectName .. ")\n") + else + cOutputer("-- From Root Object: " .. GetOriginalToStringResult(cRootObject) .. " (" .. strRootObjectName .. ")\n") + end + end + + -- Save each info. + for i, v in ipairs(cRes) do + if (not cDumpInfoResultsBase) or (not cRefInfoBase[v]) then + if (nMaxRescords > 0) then + if (i <= nMaxRescords) then + if "string" == type(v) then + local strOrgString = tostring(v) + local nPattenBegin, nPattenEnd = string.find(strOrgString, "string: \".*\"") + if ((not cDumpInfoResultsBase) and ((nil == nPattenBegin) or (nil == nPattenEnd))) then + local strRepString = string.gsub(strOrgString, "([\n\r])", "\\n") + cOutputer("string: \"" .. strRepString .. "\"\t" .. cNameInfo[v] .. "\t" .. tostring(cRefInfo[v]) .. "\n") + else + cOutputer(tostring(v) .. "\t" .. cNameInfo[v] .. "\t" .. tostring(cRefInfo[v]) .. "\n") + end + else + cOutputer(GetOriginalToStringResult(v) .. "\t" .. cNameInfo[v] .. "\t" .. tostring(cRefInfo[v]) .. "\n") + end + end + else + if "string" == type(v) then + local strOrgString = tostring(v) + local nPattenBegin, nPattenEnd = string.find(strOrgString, "string: \".*\"") + if ((not cDumpInfoResultsBase) and ((nil == nPattenBegin) or (nil == nPattenEnd))) then + local strRepString = string.gsub(strOrgString, "([\n\r])", "\\n") + cOutputer("string: \"" .. strRepString .. "\"\t" .. cNameInfo[v] .. "\t" .. tostring(cRefInfo[v]) .. "\n") + else + cOutputer(tostring(v) .. "\t" .. cNameInfo[v] .. "\t" .. tostring(cRefInfo[v]) .. "\n") + end + else + cOutputer(GetOriginalToStringResult(v) .. "\t" .. cNameInfo[v] .. "\t" .. tostring(cRefInfo[v]) .. "\n") + end + end + end + end + + if bOutputFile then + io.close(cOutputHandle) + cOutputHandle = nil + end +end + +-- The base method to dump a mem ref info result of a single object into a file. +-- strSavePath - The save path of the file to store the result, must be a directory path, If nil or "" then the result will output to console as print does. +-- strExtraFileName - If you want to add extra info append to the end of the result file, give a string, nothing will do if set to nil or "". +-- nMaxRescords - How many rescords of the results in limit to save in the file or output to the console, -1 will give all the result. +-- cDumpInfoResults - The dumped results. +local function OutputMemorySnapshotSingleObject(strSavePath, strExtraFileName, nMaxRescords, cDumpInfoResults) + -- Check results. + if not cDumpInfoResults then + return + end + + -- Get time format string. + local strDateTime = FormatDateTimeNow() + + -- Collect memory info. + local cObjectAliasName = cDumpInfoResults.m_cObjectAliasName + + -- Save result to file. + local bOutputFile = strSavePath and (string.len(strSavePath) > 0) + local cOutputHandle = nil + local cOutputEntry = print + + if bOutputFile then + -- Check save path affix. + local strAffix = string.sub(strSavePath, -1) + if ("/" ~= strAffix) and ("\\" ~= strAffix) then + strSavePath = strSavePath .. "/" + end + + -- Combine file name. + local strFileName = strSavePath .. "LuaMemRefInfo-Single" + if (not strExtraFileName) or (0 == string.len(strExtraFileName)) then + if cConfig.m_bSingleMemoryRefFileAddTime then + strFileName = strFileName .. "-[" .. strDateTime .. "].txt" + else + strFileName = strFileName .. ".txt" + end + else + if cConfig.m_bSingleMemoryRefFileAddTime then + strFileName = strFileName .. "-[" .. strDateTime .. "]-[" .. strExtraFileName .. "].txt" + else + strFileName = strFileName .. "-[" .. strExtraFileName .. "].txt" + end + end + + local cFile = assert(io.open(strFileName, "w")) + cOutputHandle = cFile + cOutputEntry = cFile.write + end + + local cOutputer = function (strContent) + if cOutputHandle then + cOutputEntry(cOutputHandle, strContent) + else + cOutputEntry(strContent) + end + end + + -- Write table header. + cOutputer("--------------------------------------------------------\n") + cOutputer("-- Collect single object memory reference at line:" .. tostring(cDumpInfoResults.m_nCurrentLine) .. "@file:" .. cDumpInfoResults.m_strShortSrc .. "\n") + cOutputer("--------------------------------------------------------\n") + + -- Calculate reference count. + local nCount = 0 + for k in pairs(cObjectAliasName) do + nCount = nCount + 1 + end + + -- Output reference count. + cOutputer("-- For Object: " .. cDumpInfoResults.m_strAddressName .. " (" .. cDumpInfoResults.m_strObjectName .. "), have " .. tostring(nCount) .. " reference in total.\n") + cOutputer("--------------------------------------------------------\n") + + -- Save each info. + for k in pairs(cObjectAliasName) do + if (nMaxRescords > 0) then + if (i <= nMaxRescords) then + cOutputer(k .. "\n") + end + else + cOutputer(k .. "\n") + end + end + + if bOutputFile then + io.close(cOutputHandle) + cOutputHandle = nil + end +end + +-- Fileter an existing result file and output it. +-- strFilePath - The existing result file. +-- strFilter - The filter string. +-- bIncludeFilter - Include(true) or exclude(false) the filter. +-- bOutputFile - Output to file(true) or console(false). +local function OutputFilteredResult(strFilePath, strFilter, bIncludeFilter, bOutputFile) + if (not strFilePath) or (0 == string.len(strFilePath)) then + print("You need to specify a file path.") + return + end + + if (not strFilter) or (0 == string.len(strFilter)) then + print("You need to specify a filter string.") + return + end + + -- Read file. + local cFilteredResult = {} + local cReadFile = assert(io.open(strFilePath, "rb")) + for strLine in cReadFile:lines() do + local nBegin, nEnd = string.find(strLine, strFilter) + if nBegin and nEnd then + if bIncludeFilter then + nBegin, nEnd = string.find(strLine, "[\r\n]") + if nBegin and nEnd and (string.len(strLine) == nEnd) then + table.insert(cFilteredResult, string.sub(strLine, 1, nBegin - 1)) + else + table.insert(cFilteredResult, strLine) + end + end + else + if not bIncludeFilter then + nBegin, nEnd = string.find(strLine, "[\r\n]") + if nBegin and nEnd and (string.len(strLine) == nEnd) then + table.insert(cFilteredResult, string.sub(strLine, 1, nBegin - 1)) + else + table.insert(cFilteredResult, strLine) + end + end + end + end + + -- Close and clear read file handle. + io.close(cReadFile) + cReadFile = nil + + -- Write filtered result. + local cOutputHandle = nil + local cOutputEntry = print + + if bOutputFile then + -- Combine file name. + local _, _, strResFileName = string.find(strFilePath, "(.*)%.txt") + strResFileName = strResFileName .. "-Filter-" .. ((bIncludeFilter and "I") or "E") .. "-[" .. strFilter .. "].txt" + + local cFile = assert(io.open(strResFileName, "w")) + cOutputHandle = cFile + cOutputEntry = cFile.write + end + + local cOutputer = function (strContent) + if cOutputHandle then + cOutputEntry(cOutputHandle, strContent) + else + cOutputEntry(strContent) + end + end + + -- Output result. + for i, v in ipairs(cFilteredResult) do + cOutputer(v .. "\n") + end + + if bOutputFile then + io.close(cOutputHandle) + cOutputHandle = nil + end +end + +-- Dump memory reference at current time. +-- strSavePath - The save path of the file to store the result, must be a directory path, If nil or "" then the result will output to console as print does. +-- strExtraFileName - If you want to add extra info append to the end of the result file, give a string, nothing will do if set to nil or "". +-- nMaxRescords - How many rescords of the results in limit to save in the file or output to the console, -1 will give all the result. +-- strRootObjectName - The root object name that start to search, default is "_G" if leave this to nil. +-- cRootObject - The root object that start to search, default is _G if leave this to nil. +local function DumpMemorySnapshot(strSavePath, strExtraFileName, nMaxRescords, strRootObjectName, cRootObject) + -- Get time format string. + local strDateTime = FormatDateTimeNow() + + -- Check root object. + if cRootObject then + if (not strRootObjectName) or (0 == string.len(strRootObjectName)) then + strRootObjectName = tostring(cRootObject) + end + else + cRootObject = debug.getregistry() + strRootObjectName = "registry" + end + + -- Create container. + local cDumpInfoContainer = CreateObjectReferenceInfoContainer() + local cStackInfo = debug.getinfo(2, "Sl") + if cStackInfo then + cDumpInfoContainer.m_strShortSrc = cStackInfo.short_src + cDumpInfoContainer.m_nCurrentLine = cStackInfo.currentline + end + + -- Collect memory info. + CollectObjectReferenceInMemory(strRootObjectName, cRootObject, cDumpInfoContainer) + + -- Dump the result. + OutputMemorySnapshot(strSavePath, strExtraFileName, nMaxRescords, strRootObjectName, cRootObject, nil, cDumpInfoContainer) +end + +-- Dump compared memory reference results generated by DumpMemorySnapshot. +-- strSavePath - The save path of the file to store the result, must be a directory path, If nil or "" then the result will output to console as print does. +-- strExtraFileName - If you want to add extra info append to the end of the result file, give a string, nothing will do if set to nil or "". +-- nMaxRescords - How many rescords of the results in limit to save in the file or output to the console, -1 will give all the result. +-- cResultBefore - The base dumped results. +-- cResultAfter - The compared dumped results. +local function DumpMemorySnapshotCompared(strSavePath, strExtraFileName, nMaxRescords, cResultBefore, cResultAfter) + -- Dump the result. + OutputMemorySnapshot(strSavePath, strExtraFileName, nMaxRescords, nil, nil, cResultBefore, cResultAfter) +end + +-- Dump compared memory reference file results generated by DumpMemorySnapshot. +-- strSavePath - The save path of the file to store the result, must be a directory path, If nil or "" then the result will output to console as print does. +-- strExtraFileName - If you want to add extra info append to the end of the result file, give a string, nothing will do if set to nil or "". +-- nMaxRescords - How many rescords of the results in limit to save in the file or output to the console, -1 will give all the result. +-- strResultFilePathBefore - The base dumped results file. +-- strResultFilePathAfter - The compared dumped results file. +local function DumpMemorySnapshotComparedFile(strSavePath, strExtraFileName, nMaxRescords, strResultFilePathBefore, strResultFilePathAfter) + -- Read results from file. + local cResultBefore = CreateObjectReferenceInfoContainerFromFile(strResultFilePathBefore) + local cResultAfter = CreateObjectReferenceInfoContainerFromFile(strResultFilePathAfter) + + -- Dump the result. + OutputMemorySnapshot(strSavePath, strExtraFileName, nMaxRescords, nil, nil, cResultBefore, cResultAfter) +end + +-- Dump memory reference of a single object at current time. +-- strSavePath - The save path of the file to store the result, must be a directory path, If nil or "" then the result will output to console as print does. +-- strExtraFileName - If you want to add extra info append to the end of the result file, give a string, nothing will do if set to nil or "". +-- nMaxRescords - How many rescords of the results in limit to save in the file or output to the console, -1 will give all the result. +-- strObjectName - The object name reference you want to dump. +-- cObject - The object reference you want to dump. +local function DumpMemorySnapshotSingleObject(strSavePath, strExtraFileName, nMaxRescords, strObjectName, cObject) + -- Check object. + if not cObject then + return + end + + if (not strObjectName) or (0 == string.len(strObjectName)) then + strObjectName = GetOriginalToStringResult(cObject) + end + + -- Get time format string. + local strDateTime = FormatDateTimeNow() + + -- Create container. + local cDumpInfoContainer = CreateSingleObjectReferenceInfoContainer(strObjectName, cObject) + local cStackInfo = debug.getinfo(2, "Sl") + if cStackInfo then + cDumpInfoContainer.m_strShortSrc = cStackInfo.short_src + cDumpInfoContainer.m_nCurrentLine = cStackInfo.currentline + end + + -- Collect memory info. + CollectSingleObjectReferenceInMemory("registry", debug.getregistry(), cDumpInfoContainer) + + -- Dump the result. + OutputMemorySnapshotSingleObject(strSavePath, strExtraFileName, nMaxRescords, cDumpInfoContainer) +end + +-- Return methods. +local cPublications = {m_cConfig = nil, m_cMethods = {}, m_cHelpers = {}, m_cBases = {}} + +cPublications.m_cConfig = cConfig + +cPublications.m_cMethods.DumpMemorySnapshot = DumpMemorySnapshot +cPublications.m_cMethods.DumpMemorySnapshotCompared = DumpMemorySnapshotCompared +cPublications.m_cMethods.DumpMemorySnapshotComparedFile = DumpMemorySnapshotComparedFile +cPublications.m_cMethods.DumpMemorySnapshotSingleObject = DumpMemorySnapshotSingleObject + +cPublications.m_cHelpers.FormatDateTimeNow = FormatDateTimeNow +cPublications.m_cHelpers.GetOriginalToStringResult = GetOriginalToStringResult + +cPublications.m_cBases.CreateObjectReferenceInfoContainer = CreateObjectReferenceInfoContainer +cPublications.m_cBases.CreateObjectReferenceInfoContainerFromFile = CreateObjectReferenceInfoContainerFromFile +cPublications.m_cBases.CreateSingleObjectReferenceInfoContainer = CreateSingleObjectReferenceInfoContainer +cPublications.m_cBases.CollectObjectReferenceInMemory = CollectObjectReferenceInMemory +cPublications.m_cBases.CollectSingleObjectReferenceInMemory = CollectSingleObjectReferenceInMemory +cPublications.m_cBases.OutputMemorySnapshot = OutputMemorySnapshot +cPublications.m_cBases.OutputMemorySnapshotSingleObject = OutputMemorySnapshotSingleObject +cPublications.m_cBases.OutputFilteredResult = OutputFilteredResult + +return cPublications diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/profile/p.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/profile/p.lua new file mode 100644 index 0000000..9eb6f16 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/profile/p.lua @@ -0,0 +1,312 @@ +---------------------------------------------------------------------------- +-- LuaJIT profiler. +-- +-- Copyright (C) 2005-2021 Mike Pall. All rights reserved. +-- Released under the MIT license. See Copyright Notice in luajit.h +---------------------------------------------------------------------------- +-- +-- This module is a simple command line interface to the built-in +-- low-overhead profiler of LuaJIT. +-- +-- The lower-level API of the profiler is accessible via the "jit.profile" +-- module or the luaJIT_profile_* C API. +-- +-- Example usage: +-- +-- luajit -jp myapp.lua +-- luajit -jp=s myapp.lua +-- luajit -jp=-s myapp.lua +-- luajit -jp=vl myapp.lua +-- luajit -jp=G,profile.txt myapp.lua +-- +-- The following dump features are available: +-- +-- f Stack dump: function name, otherwise module:line. Default mode. +-- F Stack dump: ditto, but always prepend module. +-- l Stack dump: module:line. +-- stack dump depth (callee < caller). Default: 1. +-- - Inverse stack dump depth (caller > callee). +-- s Split stack dump after first stack level. Implies abs(depth) >= 2. +-- p Show full path for module names. +-- v Show VM states. Can be combined with stack dumps, e.g. vf or fv. +-- z Show zones. Can be combined with stack dumps, e.g. zf or fz. +-- r Show raw sample counts. Default: show percentages. +-- a Annotate excerpts from source code files. +-- A Annotate complete source code files. +-- G Produce raw output suitable for graphical tools (e.g. flame graphs). +-- m Minimum sample percentage to be shown. Default: 3. +-- i Sampling interval in milliseconds. Default: 10. +-- +---------------------------------------------------------------------------- + +-- Cache some library functions and objects. +local jit = require("jit") +assert(20100 <= jit.version_num and jit.version_num <= 20199, "LuaJIT core/library version mismatch: " .. jit.version) +local profile = require("jit.profile") +local vmdef = require("jit.vmdef") +local math = math +local pairs, ipairs, tonumber, floor = pairs, ipairs, tonumber, math.floor +local sort, format = table.sort, string.format +local stdout = io.stdout +local zone -- Load jit.zone module on demand. + +-- Output file handle. +local out + +------------------------------------------------------------------------------ + +local prof_ud +local prof_states, prof_split, prof_min, prof_raw, prof_fmt, prof_depth +local prof_ann, prof_count1, prof_count2, prof_samples + +local map_vmmode = { + N = "Compiled", + I = "Interpreted", + C = "C code", + G = "Garbage Collector", + J = "JIT Compiler", +} + +-- Profiler callback. +local function prof_cb(th, samples, vmmode) + prof_samples = prof_samples + samples + local key_stack, key_stack2, key_state + -- Collect keys for sample. + if prof_states then + if prof_states == "v" then + key_state = map_vmmode[vmmode] or vmmode + else + key_state = zone:get() or "(none)" + end + end + if prof_fmt then + key_stack = profile.dumpstack(th, prof_fmt, prof_depth) + key_stack = key_stack:gsub("%[builtin#(%d+)%]", function(x) + return vmdef.ffnames[tonumber(x)] + end) + if prof_split == 2 then + local k1, k2 = key_stack:match("(.-) [<>] (.*)") + if k2 then key_stack, key_stack2 = k1, k2 end + elseif prof_split == 3 then + key_stack2 = profile.dumpstack(th, "l", 1) + end + end + -- Order keys. + local k1, k2 + if prof_split == 1 then + if key_state then + k1 = key_state + if key_stack then k2 = key_stack end + end + elseif key_stack then + k1 = key_stack + if key_stack2 then k2 = key_stack2 elseif key_state then k2 = key_state end + end + -- Coalesce samples in one or two levels. + if k1 then + local t1 = prof_count1 + t1[k1] = (t1[k1] or 0) + samples + if k2 then + local t2 = prof_count2 + local t3 = t2[k1] + if not t3 then t3 = {}; t2[k1] = t3 end + t3[k2] = (t3[k2] or 0) + samples + end + end +end + +------------------------------------------------------------------------------ + +-- Show top N list. +local function prof_top(count1, count2, samples, indent) + local t, n = {}, 0 + for k in pairs(count1) do + n = n + 1 + t[n] = k + end + sort(t, function(a, b) return count1[a] > count1[b] end) + for i=1,n do + local k = t[i] + local v = count1[k] + local pct = floor(v*100/samples + 0.5) + if pct < prof_min then break end + if not prof_raw then + out:write(format("%s%2d%% %s\n", indent, pct, k)) + elseif prof_raw == "r" then + out:write(format("%s%5d %s\n", indent, v, k)) + else + out:write(format("%s %d\n", k, v)) + end + if count2 then + local r = count2[k] + if r then + prof_top(r, nil, v, (prof_split == 3 or prof_split == 1) and " -- " or + (prof_depth < 0 and " -> " or " <- ")) + end + end + end +end + +-- Annotate source code +local function prof_annotate(count1, samples) + local files = {} + local ms = 0 + for k, v in pairs(count1) do + local pct = floor(v*100/samples + 0.5) + ms = math.max(ms, v) + if pct >= prof_min then + local file, line = k:match("^(.*):(%d+)$") + if not file then file = k; line = 0 end + local fl = files[file] + if not fl then fl = {}; files[file] = fl; files[#files+1] = file end + line = tonumber(line) + fl[line] = prof_raw and v or pct + end + end + sort(files) + local fmtv, fmtn = " %3d%% | %s\n", " | %s\n" + if prof_raw then + local n = math.max(5, math.ceil(math.log10(ms))) + fmtv = "%"..n.."d | %s\n" + fmtn = (" "):rep(n).." | %s\n" + end + local ann = prof_ann + for _, file in ipairs(files) do + local f0 = file:byte() + if f0 == 40 or f0 == 91 then + out:write(format("\n====== %s ======\n[Cannot annotate non-file]\n", file)) + break + end + local fp, err = io.open(file) + if not fp then + out:write(format("====== ERROR: %s: %s\n", file, err)) + break + end + out:write(format("\n====== %s ======\n", file)) + local fl = files[file] + local n, show = 1, false + if ann ~= 0 then + for i=1,ann do + if fl[i] then show = true; out:write("@@ 1 @@\n"); break end + end + end + for line in fp:lines() do + if line:byte() == 27 then + out:write("[Cannot annotate bytecode file]\n") + break + end + local v = fl[n] + if ann ~= 0 then + local v2 = fl[n+ann] + if show then + if v2 then show = n+ann elseif v then show = n + elseif show+ann < n then show = false end + elseif v2 then + show = n+ann + out:write(format("@@ %d @@\n", n)) + end + if not show then goto next end + end + if v then + out:write(format(fmtv, v, line)) + else + out:write(format(fmtn, line)) + end + ::next:: + n = n + 1 + end + fp:close() + end +end + +------------------------------------------------------------------------------ + +-- Finish profiling and dump result. +local function prof_finish() + if prof_ud then + profile.stop() + local samples = prof_samples + if samples == 0 then + if prof_raw ~= true then out:write("[No samples collected]\n") end + return + end + if prof_ann then + prof_annotate(prof_count1, samples) + else + prof_top(prof_count1, prof_count2, samples, "") + end + prof_count1 = nil + prof_count2 = nil + prof_ud = nil + if out ~= stdout then out:close() end + end +end + +-- Start profiling. +local function prof_start(mode) + local interval = "" + mode = mode:gsub("i%d*", function(s) interval = s; return "" end) + prof_min = 3 + mode = mode:gsub("m(%d+)", function(s) prof_min = tonumber(s); return "" end) + prof_depth = 1 + mode = mode:gsub("%-?%d+", function(s) prof_depth = tonumber(s); return "" end) + local m = {} + for c in mode:gmatch(".") do m[c] = c end + prof_states = m.z or m.v + if prof_states == "z" then zone = require("jit.zone") end + local scope = m.l or m.f or m.F or (prof_states and "" or "f") + local flags = (m.p or "") + prof_raw = m.r + if m.s then + prof_split = 2 + if prof_depth == -1 or m["-"] then prof_depth = -2 + elseif prof_depth == 1 then prof_depth = 2 end + elseif mode:find("[fF].*l") then + scope = "l" + prof_split = 3 + else + prof_split = (scope == "" or mode:find("[zv].*[lfF]")) and 1 or 0 + end + prof_ann = m.A and 0 or (m.a and 3) + if prof_ann then + scope = "l" + prof_fmt = "pl" + prof_split = 0 + prof_depth = 1 + elseif m.G and scope ~= "" then + prof_fmt = flags..scope.."Z;" + prof_depth = -100 + prof_raw = true + prof_min = 0 + elseif scope == "" then + prof_fmt = false + else + local sc = prof_split == 3 and m.f or m.F or scope + prof_fmt = flags..sc..(prof_depth >= 0 and "Z < " or "Z > ") + end + prof_count1 = {} + prof_count2 = {} + prof_samples = 0 + profile.start(scope:lower()..interval, prof_cb) + prof_ud = newproxy(true) + getmetatable(prof_ud).__gc = prof_finish +end + +------------------------------------------------------------------------------ + +local function start(mode, outfile) + if not outfile then outfile = os.getenv("LUAJIT_PROFILEFILE") end + if outfile then + out = outfile == "-" and stdout or assert(io.open(outfile, "w")) + else + out = stdout + end + prof_start(mode or "f") +end + +-- Public module functions. +return { + start = start, -- For -j command line option. + stop = prof_finish +} + diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/reload.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/reload.lua new file mode 100644 index 0000000..6189522 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/reload.lua @@ -0,0 +1,36 @@ +local reload = {} + +reload.reload_module = function(module_name, starts_with_only) + -- Default to starts with only + if starts_with_only == nil then + starts_with_only = true + end + + -- TODO: Might need to handle cpath / compiled lua packages? Not sure. + local matcher + if not starts_with_only then + matcher = function(pack) + return string.find(pack, module_name, 1, true) + end + else + local module_name_pattern = vim.pesc(module_name) + matcher = function(pack) + return string.find(pack, "^" .. module_name_pattern) + end + end + + -- Handle impatient.nvim automatically. + local luacache = (_G.__luacache or {}).cache + + for pack, _ in pairs(package.loaded) do + if matcher(pack) then + package.loaded[pack] = nil + + if luacache then + luacache[pack] = nil + end + end + end +end + +return reload diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/run.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/run.lua new file mode 100644 index 0000000..0661fc6 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/run.lua @@ -0,0 +1,28 @@ +local floatwin = require "plenary.window.float" + +local run = {} + +run.with_displayed_output = function(title_text, cmd, opts) + local views = floatwin.centered_with_top_win(title_text) + + local job_id = vim.fn.termopen(cmd) + + local count = 0 + while not vim.wait(1000, function() + return vim.fn.jobwait({ job_id }, 0)[1] == -1 + end) do + vim.cmd [[normal! G]] + count = count + 1 + + if count == 10 then + break + end + end + + vim.fn.win_gotoid(views.win_id) + vim.cmd [[startinsert]] + + return views.bufnr, views.win_id +end + +return run diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/scandir.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/scandir.lua new file mode 100644 index 0000000..d5b60ee --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/scandir.lua @@ -0,0 +1,620 @@ +local Path = require "plenary.path" +local os_sep = Path.path.sep +local F = require "plenary.functional" +local compat = require "plenary.compat" + +local uv = vim.loop + +local m = {} + +local make_gitignore = function(basepath) + local patterns = {} + local valid = false + for _, v in ipairs(basepath) do + local p = Path:new(v .. os_sep .. ".gitignore") + if p:exists() then + valid = true + patterns[v] = { ignored = {}, negated = {} } + for l in p:iter() do + local prefix = l:sub(1, 1) + local negated = prefix == "!" + if negated then + l = l:sub(2) + prefix = l:sub(1, 1) + end + if prefix == "/" then + l = v .. l + end + if not (prefix == "" or prefix == "#") then + local el = vim.trim(l) + el = el:gsub("%-", "%%-") + el = el:gsub("%.", "%%.") + el = el:gsub("/%*%*/", "/%%w+/") + el = el:gsub("%*%*", "") + el = el:gsub("%*", "%%w+") + el = el:gsub("%?", "%%w") + if el ~= "" then + table.insert(negated and patterns[v].negated or patterns[v].ignored, el) + end + end + end + end + end + if not valid then + return nil + end + return function(bp, entry) + for _, v in ipairs(bp) do + if entry:find(v, 1, true) then + local negated = false + for _, w in ipairs(patterns[v].ignored) do + if not negated and entry:match(w) then + for _, inverse in ipairs(patterns[v].negated) do + if not negated and entry:match(inverse) then + negated = true + end + end + if not negated then + return false + end + end + end + end + end + return true + end +end +-- exposed for testing +m.__make_gitignore = make_gitignore + +local handle_depth = function(base_paths, entry, depth) + for _, v in ipairs(base_paths) do + if entry:find(v, 1, true) then + local cut = entry:sub(#v + 1, -1) + cut = cut:sub(1, 1) == os_sep and cut:sub(2, -1) or cut + local _, count = cut:gsub(os_sep, "") + if depth <= (count + 1) then + return nil + end + end + end + return entry +end + +local gen_search_pat = function(pattern) + if type(pattern) == "string" then + return function(entry) + return entry:match(pattern) + end + elseif type(pattern) == "table" then + return function(entry) + for _, v in ipairs(pattern) do + if entry:match(v) then + return true + end + end + return false + end + elseif type(pattern) == "function" then + return pattern + end +end + +local process_item = function(opts, name, typ, current_dir, next_dir, bp, data, giti, msp) + if opts.hidden or name:sub(1, 1) ~= "." then + if typ == "directory" then + local entry = current_dir .. os_sep .. name + if opts.depth then + table.insert(next_dir, handle_depth(bp, entry, opts.depth)) + else + table.insert(next_dir, entry) + end + if opts.add_dirs or opts.only_dirs then + if not giti or giti(bp, entry .. "/") then + if not msp or msp(entry) then + table.insert(data, entry) + if opts.on_insert then + opts.on_insert(entry, typ) + end + end + end + end + elseif not opts.only_dirs then + local entry = current_dir .. os_sep .. name + if not giti or giti(bp, entry) then + if not msp or msp(entry) then + table.insert(data, entry) + if opts.on_insert then + opts.on_insert(entry, typ) + end + end + end + end + end +end + +--- m.scan_dir +-- Search directory recursive and syncronous +-- @param path: string or table +-- string has to be a valid path +-- table has to be a array of valid paths +-- @param opts: table to change behavior +-- opts.hidden (bool): if true hidden files will be added +-- opts.add_dirs (bool): if true dirs will also be added to the results +-- opts.only_dirs (bool): if true only dirs will be added to the results +-- opts.respect_gitignore (bool): if true will only add files that are not ignored by the git +-- opts.depth (int): depth on how deep the search should go +-- opts.search_pattern (regex): regex for which files will be added, string, table of strings, or fn(e) -> bool +-- opts.on_insert(entry): Will be called for each element +-- opts.silent (bool): if true will not echo messages that are not accessible +-- @return array with files +m.scan_dir = function(path, opts) + opts = opts or {} + + local data = {} + local base_paths = compat.flatten { path } + local next_dir = compat.flatten { path } + + local gitignore = opts.respect_gitignore and make_gitignore(base_paths) or nil + local match_search_pat = opts.search_pattern and gen_search_pat(opts.search_pattern) or nil + + for i = #base_paths, 1, -1 do + if uv.fs_access(base_paths[i], "X") == false then + if not F.if_nil(opts.silent, false, opts.silent) then + print(string.format("%s is not accessible by the current user!", base_paths[i])) + end + table.remove(base_paths, i) + end + end + if #base_paths == 0 then + return {} + end + + repeat + local current_dir = table.remove(next_dir, 1) + local fd = uv.fs_scandir(current_dir) + if fd then + while true do + local name, typ = uv.fs_scandir_next(fd) + if name == nil then + break + end + process_item(opts, name, typ, current_dir, next_dir, base_paths, data, gitignore, match_search_pat) + end + end + until #next_dir == 0 + return data +end + +--- m.scan_dir_async +-- Search directory recursive and asyncronous +-- @param path: string or table +-- string has to be a valid path +-- table has to be a array of valid paths +-- @param opts: table to change behavior +-- opts.hidden (bool): if true hidden files will be added +-- opts.add_dirs (bool): if true dirs will also be added to the results +-- opts.only_dirs (bool): if true only dirs will be added to the results +-- opts.respect_gitignore (bool): if true will only add files that are not ignored by git +-- opts.depth (int): depth on how deep the search should go +-- opts.search_pattern (regex): regex for which files will be added, string, table of strings, or fn(e) -> bool +-- opts.on_insert function(entry): will be called for each element +-- opts.on_exit function(results): will be called at the end +-- opts.silent (bool): if true will not echo messages that are not accessible +m.scan_dir_async = function(path, opts) + opts = opts or {} + + local data = {} + local base_paths = compat.flatten { path } + local next_dir = compat.flatten { path } + local current_dir = table.remove(next_dir, 1) + + -- TODO(conni2461): get gitignore is not async + local gitignore = opts.respect_gitignore and make_gitignore(base_paths) or nil + local match_search_pat = opts.search_pattern and gen_search_pat(opts.search_pattern) or nil + + -- TODO(conni2461): is not async. Shouldn't be that big of a problem but still + -- Maybe obers async pr can take me out of callback hell + for i = #base_paths, 1, -1 do + if uv.fs_access(base_paths[i], "X") == false then + if not F.if_nil(opts.silent, false, opts.silent) then + print(string.format("%s is not accessible by the current user!", base_paths[i])) + end + table.remove(base_paths, i) + end + end + if #base_paths == 0 then + return {} + end + + local read_dir + read_dir = function(err, fd) + if not err then + while true do + local name, typ = uv.fs_scandir_next(fd) + if name == nil then + break + end + process_item(opts, name, typ, current_dir, next_dir, base_paths, data, gitignore, match_search_pat) + end + if #next_dir == 0 then + if opts.on_exit then + opts.on_exit(data) + end + else + current_dir = table.remove(next_dir, 1) + uv.fs_scandir(current_dir, read_dir) + end + end + end + uv.fs_scandir(current_dir, read_dir) +end + +local gen_permissions = (function() + local conv_to_octal = function(nr) + local octal, i = 0, 1 + + while nr ~= 0 do + octal = octal + (nr % 8) * i + nr = math.floor(nr / 8) + i = i * 10 + end + + return octal + end + + local type_tbl = { [1] = "p", [2] = "c", [4] = "d", [6] = "b", [10] = ".", [12] = "l", [14] = "s" } + local permissions_tbl = { [0] = "---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx" } + local bit_tbl = { 4, 2, 1 } + + return function(cache, mode) + if cache[mode] then + return cache[mode] + end + + local octal = string.format("%6d", conv_to_octal(mode)) + local l4 = octal:sub(#octal - 3, -1) + local bit = tonumber(l4:sub(1, 1)) + + local result = type_tbl[tonumber(octal:sub(1, 2))] or "-" + for i = 2, #l4 do + result = result .. permissions_tbl[tonumber(l4:sub(i, i))] + if bit - bit_tbl[i - 1] >= 0 then + result = result:sub(1, -2) .. (bit_tbl[i - 1] == 1 and "T" or "S") + bit = bit - bit_tbl[i - 1] + end + end + + cache[mode] = result + return result + end +end)() + +local gen_size = (function() + local size_types = { "", "K", "M", "G", "T", "P", "E", "Z" } + + return function(size) + -- TODO(conni2461): If type directory we could just return 4.0K + for _, v in ipairs(size_types) do + if math.abs(size) < 1024.0 then + if math.abs(size) > 9 then + return string.format("%3d%s", size, v) + else + return string.format("%3.1f%s", size, v) + end + end + size = size / 1024.0 + end + return string.format("%.1f%s", size, "Y") + end +end)() + +local gen_date = (function() + local current_year = os.date "%Y" + return function(mtime) + if current_year ~= os.date("%Y", mtime) then + return os.date("%b %d %Y", mtime) + end + return os.date("%b %d %H:%M", mtime) + end +end)() + +local get_username = (function() + local fallback = function(tbl, id) + if not tbl then + return id + end + if tbl[id] then + return tbl[id] + end + tbl[id] = tostring(id) + return id + end + + if jit and os_sep ~= "\\" then + local ffi = require "ffi" + ffi.cdef [[ + typedef unsigned int __uid_t; + typedef __uid_t uid_t; + typedef unsigned int __gid_t; + typedef __gid_t gid_t; + + typedef struct { + char *pw_name; + char *pw_passwd; + __uid_t pw_uid; + __gid_t pw_gid; + char *pw_gecos; + char *pw_dir; + char *pw_shell; + } passwd; + + passwd *getpwuid(uid_t uid); + ]] + + local ffi_func = function(tbl, id) + if tbl[id] then + return tbl[id] + end + local struct = ffi.C.getpwuid(id) + local name + if struct == nil then + name = tostring(id) + else + name = ffi.string(struct.pw_name) + end + tbl[id] = name + return name + end + + local ok = pcall(ffi_func, {}, 1000) + if ok then + return ffi_func + else + return fallback + end + else + return fallback + end +end)() + +local get_groupname = (function() + local fallback = function(tbl, id) + if not tbl then + return id + end + if tbl[id] then + return tbl[id] + end + tbl[id] = tostring(id) + return id + end + + if jit and os_sep ~= "\\" then + local ffi = require "ffi" + ffi.cdef [[ + typedef unsigned int __gid_t; + typedef __gid_t gid_t; + + typedef struct { + char *gr_name; + char *gr_passwd; + __gid_t gr_gid; + char **gr_mem; + } group; + group *getgrgid(gid_t gid); + ]] + + local ffi_func = function(tbl, id) + if tbl[id] then + return tbl[id] + end + local struct = ffi.C.getgrgid(id) + local name + if struct == nil then + name = tostring(id) + else + name = ffi.string(struct.gr_name) + end + tbl[id] = name + return name + end + local ok = pcall(ffi_func, {}, 1000) + if ok then + return ffi_func + else + return fallback + end + else + return fallback + end +end)() + +local get_max_len = function(tbl) + if not tbl then + return 0 + end + local max_len = 0 + for _, v in pairs(tbl) do + if #v > max_len then + max_len = #v + end + end + return max_len +end + +local gen_ls = function(data, path, opts) + if not data or #data == 0 then + return {}, {} + end + + local check_link = function(per, file) + if per:sub(1, 1) == "l" then + local resolved = uv.fs_realpath(path .. os_sep .. file) + if not resolved then + return file + end + if resolved:sub(1, #path) == path then + resolved = resolved:sub(#path + 2, -1) + end + return string.format("%s -> %s", file, resolved) + end + return file + end + + local results, sections = {}, {} + + local users_tbl = os_sep ~= "\\" and {} or nil + local groups_tbl = os_sep ~= "\\" and {} or nil + + local stats, permissions_cache = {}, {} + for _, v in ipairs(data) do + local stat = uv.fs_lstat(v) + if stat then + stats[v] = stat + get_username(users_tbl, stat.uid) + get_groupname(groups_tbl, stat.gid) + end + end + + local insert_in_results = (function() + if not users_tbl and not groups_tbl then + local section_spacing_tbl = { [5] = 2, [6] = 0 } + + return function(...) + local args = { ... } + local section = { + { start_index = 01, end_index = 11 }, -- permissions, hardcoded indexes + { start_index = 12, end_index = 17 }, -- size, hardcoded indexes + } + local cur_index = 19 + for k = 5, 6 do + local v = section_spacing_tbl[k] + local end_index = cur_index + #args[k] + table.insert(section, { start_index = cur_index, end_index = end_index }) + cur_index = end_index + v + end + table.insert(sections, section) + table.insert( + results, + string.format("%10s %5s %s %s", args[1], args[2], args[5], check_link(args[1], args[6])) + ) + end + else + local max_user_len = get_max_len(users_tbl) + local max_group_len = get_max_len(groups_tbl) + + local section_spacing_tbl = { + [3] = { max = max_user_len, add = 1 }, + [4] = { max = max_group_len, add = 2 }, + [5] = { add = 2 }, + [6] = { add = 0 }, + } + local fmt_str = "%10s %5s %-" .. max_user_len .. "s %-" .. max_group_len .. "s %s %s" + + return function(...) + local args = { ... } + local section = { + { start_index = 01, end_index = 11 }, -- permissions, hardcoded indexes + { start_index = 12, end_index = 17 }, -- size, hardcoded indexes + } + local cur_index = 18 + for k = 3, 6 do + local v = section_spacing_tbl[k] + local end_index = cur_index + #args[k] + table.insert(section, { start_index = cur_index, end_index = end_index }) + if v.max then + cur_index = cur_index + v.max + v.add + else + cur_index = end_index + v.add + end + end + table.insert(sections, section) + table.insert( + results, + string.format(fmt_str, args[1], args[2], args[3], args[4], args[5], check_link(args[1], args[6])) + ) + end + end + end)() + + for name, stat in pairs(stats) do + insert_in_results( + gen_permissions(permissions_cache, stat.mode), + gen_size(stat.size), + get_username(users_tbl, stat.uid), + get_groupname(groups_tbl, stat.gid), + gen_date(stat.mtime.sec), + name:sub(#path + 2, -1) + ) + end + + if opts and opts.group_directories_first then + local sorted_results = {} + local sorted_sections = {} + for k, v in ipairs(results) do + if v:sub(1, 1) == "d" then + table.insert(sorted_results, v) + table.insert(sorted_sections, sections[k]) + end + end + for k, v in ipairs(results) do + if v:sub(1, 1) ~= "d" then + table.insert(sorted_results, v) + table.insert(sorted_sections, sections[k]) + end + end + return sorted_results, sorted_sections + else + return results, sections + end +end + +--- m.ls +-- List directory contents. Will always apply --long option. Use scan_dir for without --long +-- @param path: string +-- string has to be a valid path +-- @param opts: table to change behavior +-- opts.hidden (bool): if true hidden files will be added +-- opts.add_dirs (bool): if true dirs will also be added to the results, default: true +-- opts.respect_gitignore (bool): if true will only add files that are not ignored by git +-- opts.depth (int): depth on how deep the search should go, default: 1 +-- opts.group_directories_first (bool): same as real ls +-- @return array with formatted output +m.ls = function(path, opts) + opts = opts or {} + opts.depth = opts.depth or 1 + opts.add_dirs = opts.add_dirs or true + local data = m.scan_dir(path, opts) + + return gen_ls(data, path, opts) +end + +--- m.ls_async +-- List directory contents. Will always apply --long option. Use scan_dir for without --long +-- @param path: string +-- string has to be a valid path +-- @param opts: table to change behavior +-- opts.hidden (bool): if true hidden files will be added +-- opts.add_dirs (bool): if true dirs will also be added to the results, default: true +-- opts.respect_gitignore (bool): if true will only add files that are not ignored by git +-- opts.depth (int): depth on how deep the search should go, default: 1 +-- opts.group_directories_first (bool): same as real ls +-- opts.on_exit function(results): will be called at the end (required) +m.ls_async = function(path, opts) + opts = opts or {} + opts.depth = opts.depth or 1 + opts.add_dirs = opts.add_dirs or true + + local opts_copy = vim.deepcopy(opts) + + opts_copy.on_exit = function(data) + if opts.on_exit then + opts.on_exit(gen_ls(data, path, opts_copy)) + end + end + + m.scan_dir_async(path, opts_copy) +end + +return m diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/strings.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/strings.lua new file mode 100644 index 0000000..3e8e5bc --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/strings.lua @@ -0,0 +1,204 @@ +local path = require("plenary.path").path + +local M = {} + +M.strdisplaywidth = (function() + local fallback = function(str, col) + str = tostring(str) + if vim.in_fast_event() then + return #str - (col or 0) + end + return vim.fn.strdisplaywidth(str, col) + end + + if jit and path.sep ~= [[\]] then + local ffi = require "ffi" + ffi.cdef [[ + typedef unsigned char char_u; + int linetabsize_col(int startcol, char_u *s); + ]] + + local ffi_func = function(str, col) + str = tostring(str) + local startcol = col or 0 + local s = ffi.new("char[?]", #str + 1) + ffi.copy(s, str) + return ffi.C.linetabsize_col(startcol, s) - startcol + end + + local ok = pcall(ffi_func, "hello") + if ok then + return ffi_func + else + return fallback + end + else + return fallback + end +end)() + +M.strcharpart = (function() + local fallback = function(str, nchar, charlen) + if vim.in_fast_event() then + return str:sub(nchar + 1, charlen) + end + return vim.fn.strcharpart(str, nchar, charlen) + end + + if jit and path.sep ~= [[\]] then + local ffi = require "ffi" + ffi.cdef [[ + typedef unsigned char char_u; + int utf_ptr2len(const char_u *const p); + ]] + + local function utf_ptr2len(str) + local c_str = ffi.new("char[?]", #str + 1) + ffi.copy(c_str, str) + return ffi.C.utf_ptr2len(c_str) + end + + local ok = pcall(utf_ptr2len, "🔭") + if not ok then + return fallback + end + + return function(str, nchar, charlen) + local nbyte = 0 + if nchar > 0 then + while nchar > 0 and nbyte < #str do + nbyte = nbyte + utf_ptr2len(str:sub(nbyte + 1)) + nchar = nchar - 1 + end + else + nbyte = nchar + end + + local len = 0 + if charlen then + while charlen > 0 and nbyte + len < #str do + local off = nbyte + len + if off < 0 then + len = len + 1 + else + len = len + utf_ptr2len(str:sub(off + 1)) + end + charlen = charlen - 1 + end + else + len = #str - nbyte + end + + if nbyte < 0 then + len = len + nbyte + nbyte = 0 + elseif nbyte > #str then + nbyte = #str + end + if len < 0 then + len = 0 + elseif nbyte + len > #str then + len = #str - nbyte + end + + return str:sub(nbyte + 1, nbyte + len) + end + else + return fallback + end +end)() + +local truncate = function(str, len, dots, direction) + if M.strdisplaywidth(str) <= len then + return str + end + local start = direction > 0 and 0 or str:len() + local current = 0 + local result = "" + local len_of_dots = M.strdisplaywidth(dots) + local concat = function(a, b, dir) + if dir > 0 then + return a .. b + else + return b .. a + end + end + while true do + local part = M.strcharpart(str, start, 1) + current = current + M.strdisplaywidth(part) + if (current + len_of_dots) > len then + result = concat(result, dots, direction) + break + end + result = concat(result, part, direction) + start = start + direction + end + return result +end + +M.truncate = function(str, len, dots, direction) + str = tostring(str) -- We need to make sure its an actually a string and not a number + dots = dots or "…" + direction = direction or 1 + if direction ~= 0 then + return truncate(str, len, dots, direction) + else + if M.strdisplaywidth(str) <= len then + return str + end + local len1 = math.floor((len + M.strdisplaywidth(dots)) / 2) + local s1 = truncate(str, len1, dots, 1) + local len2 = len - M.strdisplaywidth(s1) + M.strdisplaywidth(dots) + local s2 = truncate(str, len2, dots, -1) + return s1 .. s2:sub(dots:len() + 1) + end +end + +M.align_str = function(string, width, right_justify) + local str_len = M.strdisplaywidth(string) + return right_justify and string.rep(" ", width - str_len) .. string or string .. string.rep(" ", width - str_len) +end + +M.dedent = function(str, leave_indent) + -- Check each line and detect the minimum indent. + local indent + local info = {} + for line in str:gmatch "[^\n]*\n?" do + -- It matches '' for the last line. + if line ~= "" then + local chars, width + local line_indent = line:match "^[ \t]+" + if line_indent then + chars = #line_indent + width = M.strdisplaywidth(line_indent) + if not indent or width < indent then + indent = width + end + -- Ignore empty lines + elseif line ~= "\n" then + indent = 0 + end + table.insert(info, { line = line, chars = chars, width = width }) + end + end + + -- Build up the result + leave_indent = leave_indent or 0 + local result = {} + for _, i in ipairs(info) do + local line + if i.chars then + local content = i.line:sub(i.chars + 1) + local indent_width = i.width - indent + leave_indent + line = (" "):rep(indent_width) .. content + elseif i.line == "\n" then + line = "\n" + else + line = (" "):rep(leave_indent) .. i.line + end + table.insert(result, line) + end + return table.concat(result) +end + +return M diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/tbl.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/tbl.lua new file mode 100644 index 0000000..276e9ab --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/tbl.lua @@ -0,0 +1,40 @@ +local tbl = {} + +function tbl.apply_defaults(original, defaults) + if original == nil then + original = {} + end + + original = vim.deepcopy(original) + + for k, v in pairs(defaults) do + if original[k] == nil then + original[k] = v + end + end + + return original +end + +function tbl.pack(...) + return { n = select("#", ...), ... } +end + +function tbl.unpack(t, i, j) + return unpack(t, i or 1, j or t.n or #t) +end + +---Freeze a table. A frozen table is not able to be modified. +---http://lua-users.org/wiki/ReadOnlyTables +---@param t table +---@return table +function tbl.freeze(t) + return setmetatable({}, { + __index = t, + __newindex = function() + error "Attempt to modify frozen table" + end, + }) +end + +return tbl diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/test_harness.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/test_harness.lua new file mode 100644 index 0000000..a548db7 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/test_harness.lua @@ -0,0 +1,246 @@ +local Path = require "plenary.path" +local Job = require "plenary.job" + +local f = require "plenary.functional" +local log = require "plenary.log" +local win_float = require "plenary.window.float" + +local headless = require("plenary.nvim_meta").is_headless + +local plenary_dir = vim.fn.fnamemodify(debug.getinfo(1).source:match "@?(.*[/\\])", ":p:h:h:h") + +local harness = {} + +local print_output = vim.schedule_wrap(function(_, ...) + for _, v in ipairs { ... } do + io.stdout:write(tostring(v)) + io.stdout:write "\n" + end + + vim.cmd [[mode]] +end) + +local get_nvim_output = function(job_id) + return vim.schedule_wrap(function(bufnr, ...) + if not vim.api.nvim_buf_is_valid(bufnr) then + return + end + for _, v in ipairs { ... } do + vim.api.nvim_chan_send(job_id, v .. "\r\n") + end + end) +end + +function harness.test_directory_command(command) + local split_string = vim.split(command, " ") + local directory = vim.fn.expand(table.remove(split_string, 1)) + + local opts = assert(loadstring("return " .. table.concat(split_string, " ")))() + + return harness.test_directory(directory, opts) +end + +local function test_paths(paths, opts) + local minimal = not opts or not opts.init or opts.minimal or opts.minimal_init + + opts = vim.tbl_deep_extend("force", { + nvim_cmd = vim.v.progpath, + winopts = { winblend = 3 }, + sequential = false, + keep_going = true, + timeout = 50000, + }, opts or {}) + + vim.env.PLENARY_TEST_TIMEOUT = opts.timeout + + local res = {} + if not headless then + res = win_float.percentage_range_window(0.95, 0.70, opts.winopts) + + res.job_id = vim.api.nvim_open_term(res.bufnr, {}) + vim.api.nvim_buf_set_keymap(res.bufnr, "n", "q", ":q", {}) + + vim.api.nvim_win_set_option(res.win_id, "winhl", "Normal:Normal") + vim.api.nvim_win_set_option(res.win_id, "conceallevel", 3) + vim.api.nvim_win_set_option(res.win_id, "concealcursor", "n") + + if res.border_win_id then + vim.api.nvim_win_set_option(res.border_win_id, "winhl", "Normal:Normal") + end + + if res.bufnr then + vim.api.nvim_buf_set_option(res.bufnr, "filetype", "PlenaryTestPopup") + end + vim.cmd "mode" + end + + local outputter = headless and print_output or get_nvim_output(res.job_id) + + local path_len = #paths + local failure = false + + local jobs = vim.tbl_map(function(p) + local args = { + "--headless", + "-c", + "set rtp+=.," .. vim.fn.escape(plenary_dir, " ") .. " | runtime plugin/plenary.vim", + } + + if minimal then + table.insert(args, "--noplugin") + if opts.minimal_init then + table.insert(args, "-u") + table.insert(args, opts.minimal_init) + end + elseif opts.init ~= nil then + table.insert(args, "-u") + table.insert(args, opts.init) + end + + table.insert(args, "-c") + table.insert(args, string.format('lua require("plenary.busted").run("%s")', p:absolute():gsub("\\", "\\\\"))) + + local job = Job:new { + command = opts.nvim_cmd, + args = args, + + -- Can be turned on to debug + on_stdout = function(_, data) + if path_len == 1 then + outputter(res.bufnr, data) + end + end, + + on_stderr = function(_, data) + if path_len == 1 then + outputter(res.bufnr, data) + end + end, + + on_exit = vim.schedule_wrap(function(j_self, _, _) + if path_len ~= 1 then + outputter(res.bufnr, unpack(j_self:stderr_result())) + outputter(res.bufnr, unpack(j_self:result())) + end + + vim.cmd "mode" + end), + } + job.nvim_busted_path = p.filename + return job + end, paths) + + log.debug "Running..." + for i, j in ipairs(jobs) do + outputter(res.bufnr, "Scheduling: " .. j.nvim_busted_path) + j:start() + if opts.sequential then + log.debug("... Sequential wait for job number", i) + if not Job.join(j, opts.timeout) then + log.debug("... Timed out job number", i) + failure = true + pcall(function() + j.handle:kill(15) -- SIGTERM + end) + else + log.debug("... Completed job number", i, j.code, j.signal) + failure = failure or j.code ~= 0 or j.signal ~= 0 + end + if failure and not opts.keep_going then + break + end + end + end + + -- TODO: Probably want to let people know when we've completed everything. + if not headless then + return + end + + if not opts.sequential then + table.insert(jobs, opts.timeout) + log.debug "... Parallel wait" + Job.join(unpack(jobs)) + log.debug "... Completed jobs" + table.remove(jobs, table.getn(jobs)) + failure = f.any(function(_, v) + return v.code ~= 0 + end, jobs) + end + vim.wait(100) + + if headless then + if failure then + return vim.cmd "1cq" + end + + return vim.cmd "0cq" + end +end + +function harness.test_directory(directory, opts) + print "Starting..." + directory = directory:gsub("\\", "/") + local paths = harness._find_files_to_run(directory) + + -- Paths work strangely on Windows, so lets have abs paths + if vim.fn.has "win32" == 1 or vim.fn.has "win64" == 1 then + paths = vim.tbl_map(function(p) + return Path:new(directory, p.filename) + end, paths) + end + + test_paths(paths, opts) +end + +function harness.test_file(filepath) + test_paths { Path:new(filepath) } +end + +function harness._find_files_to_run(directory) + local finder + if vim.fn.has "win32" == 1 or vim.fn.has "win64" == 1 then + -- On windows use powershell Get-ChildItem instead + local cmd = vim.fn.executable "pwsh.exe" == 1 and "pwsh" or "powershell" + finder = Job:new { + command = cmd, + args = { "-NoProfile", "-Command", [[Get-ChildItem -Recurse -n -Filter "*_spec.lua"]] }, + cwd = directory, + } + else + -- everywhere else use find + finder = Job:new { + command = "find", + args = { directory, "-type", "f", "-name", "*_spec.lua" }, + } + end + + return vim.tbl_map(Path.new, finder:sync(vim.env.PLENARY_TEST_TIMEOUT)) +end + +function harness._run_path(test_type, directory) + local paths = harness._find_files_to_run(directory) + + local bufnr = 0 + local win_id = 0 + + for _, p in pairs(paths) do + print " " + print("Loading Tests For: ", p:absolute(), "\n") + + local ok, _ = pcall(function() + dofile(p:absolute()) + end) + + if not ok then + print "Failed to load file" + end + end + + harness:run(test_type, bufnr, win_id) + vim.cmd "qa!" + + return paths +end + +return harness diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/vararg/init.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/vararg/init.lua new file mode 100644 index 0000000..1398d60 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/vararg/init.lua @@ -0,0 +1,3 @@ +return { + rotate = require "plenary.vararg.rotate", +} diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/vararg/rotate.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/vararg/rotate.lua new file mode 100644 index 0000000..6b71c63 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/vararg/rotate.lua @@ -0,0 +1,83 @@ +---@brief [[ +---Do not edit this file, it was generated! +---Provides a function to rotate a lua vararg +---@brief ]] +local tbl = require "plenary.tbl" + +local rotate_lookup = {} + +rotate_lookup[1] = function(A0) + return A0 +end + +rotate_lookup[2] = function(A0, A1) + return A1, A0 +end + +rotate_lookup[3] = function(A0, A1, A2) + return A1, A2, A0 +end + +rotate_lookup[4] = function(A0, A1, A2, A3) + return A1, A2, A3, A0 +end + +rotate_lookup[5] = function(A0, A1, A2, A3, A4) + return A1, A2, A3, A4, A0 +end + +rotate_lookup[6] = function(A0, A1, A2, A3, A4, A5) + return A1, A2, A3, A4, A5, A0 +end + +rotate_lookup[7] = function(A0, A1, A2, A3, A4, A5, A6) + return A1, A2, A3, A4, A5, A6, A0 +end + +rotate_lookup[8] = function(A0, A1, A2, A3, A4, A5, A6, A7) + return A1, A2, A3, A4, A5, A6, A7, A0 +end + +rotate_lookup[9] = function(A0, A1, A2, A3, A4, A5, A6, A7, A8) + return A1, A2, A3, A4, A5, A6, A7, A8, A0 +end + +rotate_lookup[10] = function(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9) + return A1, A2, A3, A4, A5, A6, A7, A8, A9, A0 +end + +rotate_lookup[11] = function(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10) + return A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A0 +end + +rotate_lookup[12] = function(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11) + return A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A0 +end + +rotate_lookup[13] = function(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12) + return A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A0 +end + +rotate_lookup[14] = function(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13) + return A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A0 +end + +rotate_lookup[15] = function(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14) + return A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A0 +end + +local function rotate_n(first, ...) + local n = select("#", ...) + 1 + local args = tbl.pack(...) + args[n] = first + return tbl.unpack(args, 1, n) +end + +local function rotate(nargs, ...) + if nargs == nil or nargs < 1 then + return + end + return (rotate_lookup[nargs] or rotate_n)(...) +end + +return rotate diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/window/border.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/window/border.lua new file mode 100644 index 0000000..a454b7f --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/window/border.lua @@ -0,0 +1,295 @@ +local strings = require "plenary.strings" + +local Border = {} + +Border.__index = Border + +Border._default_thickness = { + top = 1, + right = 1, + bot = 1, + left = 1, +} + +local calc_left_start = function(title_pos, title_len, total_width) + if string.find(title_pos, "W") then + return 0 + elseif string.find(title_pos, "E") then + return total_width - title_len + else + return math.floor((total_width - title_len) / 2) + end +end + +local create_horizontal_line = function(title, pos, width, left_char, mid_char, right_char) + local title_len + if title == "" then + title_len = 0 + else + local len = strings.strdisplaywidth(title) + local max_title_width = width - 2 + if len > max_title_width then + title = strings.truncate(title, max_title_width) + len = strings.strdisplaywidth(title) + end + title = string.format(" %s ", title) + title_len = len + 2 + end + + local left_start = calc_left_start(pos, title_len, width) + + local horizontal_line = string.format( + "%s%s%s%s%s", + left_char, + string.rep(mid_char, left_start), + title, + string.rep(mid_char, width - title_len - left_start), + right_char + ) + local ranges = {} + if title_len ~= 0 then + -- Need to calculate again due to multi-byte characters + local r_start = string.len(left_char) + math.max(left_start, 0) * string.len(mid_char) + ranges = { { r_start, r_start + string.len(title) } } + end + return horizontal_line, ranges +end + +function Border._create_lines(content_win_id, content_win_options, border_win_options) + local content_pos = vim.api.nvim_win_get_position(content_win_id) + local content_height = vim.api.nvim_win_get_height(content_win_id) + local content_width = vim.api.nvim_win_get_width(content_win_id) + + -- TODO: Handle border width, which I haven't right here. + local thickness = border_win_options.border_thickness + + local top_enabled = thickness.top == 1 + local right_enabled = thickness.right == 1 and content_pos[2] + content_width < vim.o.columns + local bot_enabled = thickness.bot == 1 + local left_enabled = thickness.left == 1 and content_pos[2] > 0 + + border_win_options.border_thickness.left = left_enabled and 1 or 0 + border_win_options.border_thickness.right = right_enabled and 1 or 0 + + local border_lines = {} + local ranges = {} + + -- border_win_options.title should have be a list with entries of the + -- form: { pos = foo, text = bar }. + -- pos can take values in { "NW", "N", "NE", "SW", "S", "SE" } + local titles = type(border_win_options.title) == "string" and { { pos = "N", text = border_win_options.title } } + or border_win_options.title + or {} + + local topline = nil + local topleft = (left_enabled and border_win_options.topleft) or "" + local topright = (right_enabled and border_win_options.topright) or "" + -- Only calculate the topline if there is space above the first content row (relative to the editor) + if content_pos[1] > 0 then + for _, title in ipairs(titles) do + if string.find(title.pos, "N") then + local top_ranges + topline, top_ranges = create_horizontal_line( + title.text, + title.pos, + content_win_options.width, + topleft, + border_win_options.top or "", + topright + ) + for _, r in pairs(top_ranges) do + table.insert(ranges, { 0, r[1], r[2] }) + end + break + end + end + if topline == nil then + if top_enabled then + topline = topleft .. string.rep(border_win_options.top, content_win_options.width) .. topright + end + end + else + border_win_options.border_thickness.top = 0 + end + + if topline then + table.insert(border_lines, topline) + end + + local middle_line = string.format( + "%s%s%s", + (left_enabled and border_win_options.left) or "", + string.rep(" ", content_win_options.width), + (right_enabled and border_win_options.right) or "" + ) + + for _ = 1, content_win_options.height do + table.insert(border_lines, middle_line) + end + + local botline = nil + local botleft = (left_enabled and border_win_options.botleft) or "" + local botright = (right_enabled and border_win_options.botright) or "" + if content_pos[1] + content_height < vim.o.lines then + for _, title in ipairs(titles) do + if string.find(title.pos, "S") then + local bot_ranges + botline, bot_ranges = create_horizontal_line( + title.text, + title.pos, + content_win_options.width, + botleft, + border_win_options.bot or "", + botright + ) + for _, r in pairs(bot_ranges) do + table.insert(ranges, { content_win_options.height + thickness.top, r[1], r[2] }) + end + break + end + end + if botline == nil then + if bot_enabled then + botline = botleft .. string.rep(border_win_options.bot, content_win_options.width) .. botright + end + end + else + border_win_options.border_thickness.bot = 0 + end + + if botline then + table.insert(border_lines, botline) + end + + return border_lines, ranges +end + +local set_title_highlights = function(bufnr, ranges, hl) + -- Check if both `hl` and `ranges` are provided, and `ranges` is not the empty table. + if hl and ranges and next(ranges) then + for _, r in pairs(ranges) do + vim.api.nvim_buf_add_highlight(bufnr, -1, hl, r[1], r[2], r[3]) + end + end +end + +function Border:change_title(new_title, pos) + if self._border_win_options.title == new_title then + return + end + + pos = pos + or (self._border_win_options.title and self._border_win_options.title[1] and self._border_win_options.title[1].pos) + if pos == nil then + self._border_win_options.title = new_title + else + self._border_win_options.title = { { text = new_title, pos = pos } } + end + + self.contents, self.title_ranges = + Border._create_lines(self.content_win_id, self.content_win_options, self._border_win_options) + vim.api.nvim_buf_set_lines(self.bufnr, 0, -1, false, self.contents) + + set_title_highlights(self.bufnr, self.title_ranges, self._border_win_options.titlehighlight) +end + +-- Updates characters for border lines, and returns nvim_win_config +-- (generally used in conjunction with `move` or `new`) +function Border:__align_calc_config(content_win_options, border_win_options) + border_win_options = vim.tbl_deep_extend("keep", border_win_options, { + border_thickness = Border._default_thickness, + + -- Border options, could be passed as a list? + topleft = "╔", + topright = "╗", + top = "═", + left = "║", + right = "║", + botleft = "╚", + botright = "╝", + bot = "═", + }) + + -- Ensure the relevant contents and border win_options are set + self._border_win_options = border_win_options + self.content_win_options = content_win_options + -- Update border characters and title_ranges + self.contents, self.title_ranges = Border._create_lines(self.content_win_id, content_win_options, border_win_options) + + vim.api.nvim_buf_set_option(self.bufnr, "modifiable", true) + vim.api.nvim_buf_set_lines(self.bufnr, 0, -1, false, self.contents) + + local thickness = border_win_options.border_thickness + local nvim_win_config = { + anchor = content_win_options.anchor, + relative = content_win_options.relative, + style = "minimal", + row = content_win_options.row - thickness.top, + col = content_win_options.col - thickness.left, + width = content_win_options.width + thickness.left + thickness.right, + height = content_win_options.height + thickness.top + thickness.bot, + zindex = content_win_options.zindex or 50, + noautocmd = content_win_options.noautocmd, + focusable = vim.F.if_nil(border_win_options.focusable, false), + } + + return nvim_win_config +end + +-- Sets the size and position of the given Border. +-- Can be used to create a new window (with `create_window = true`) +-- or change an existing one +function Border:move(content_win_options, border_win_options) + -- Update lines in border buffer, and get config for border window + local nvim_win_config = self:__align_calc_config(content_win_options, border_win_options) + + -- Set config for border window + vim.api.nvim_win_set_config(self.win_id, nvim_win_config) + + set_title_highlights(self.bufnr, self.title_ranges, self._border_win_options.titlehighlight) +end + +function Border:new(content_bufnr, content_win_id, content_win_options, border_win_options) + assert(type(content_win_id) == "number", "Must supply a valid win_id. It's possible you forgot to call with ':'") + + local obj = {} + + obj.content_win_id = content_win_id + + obj.bufnr = vim.api.nvim_create_buf(false, true) + assert(obj.bufnr, "Failed to create border buffer") + vim.api.nvim_buf_set_option(obj.bufnr, "bufhidden", "wipe") + + -- Create a border window and buffer, with border characters around the edge + local nvim_win_config = Border.__align_calc_config(obj, content_win_options, border_win_options) + obj.win_id = vim.api.nvim_open_win(obj.bufnr, false, nvim_win_config) + + if border_win_options.highlight then + vim.api.nvim_win_set_option(obj.win_id, "winhl", border_win_options.highlight) + end + + set_title_highlights(obj.bufnr, obj.title_ranges, obj._border_win_options.titlehighlight) + + vim.cmd( + string.format( + "autocmd BufDelete ++nested ++once :lua require('plenary.window').close_related_win(%s, %s)", + content_bufnr, + content_win_id, + obj.win_id + ) + ) + + vim.cmd( + string.format( + "autocmd WinClosed ++nested ++once :lua require('plenary.window').try_close(%s, true)", + content_bufnr, + obj.win_id + ) + ) + + setmetatable(obj, Border) + + return obj +end + +return Border diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/window/float.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/window/float.lua new file mode 100644 index 0000000..86e5c04 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/window/float.lua @@ -0,0 +1,212 @@ +local Border = require "plenary.window.border" +local tbl = require "plenary.tbl" + +_AssociatedBufs = {} + +local clear_buf_on_leave = function(bufnr) + vim.cmd( + string.format( + "autocmd WinLeave,BufLeave,BufDelete ++once ++nested lua require('plenary.window.float').clear(%s)", + bufnr, + bufnr + ) + ) +end + +local win_float = {} + +win_float.default_options = { + winblend = 15, + percentage = 0.9, +} + +function win_float.default_opts(options) + options = tbl.apply_defaults(options, win_float.default_options) + + local width = math.floor(vim.o.columns * options.percentage) + local height = math.floor(vim.o.lines * options.percentage) + + local top = math.floor(((vim.o.lines - height) / 2) - 1) + local left = math.floor((vim.o.columns - width) / 2) + + local opts = { + relative = "editor", + row = top, + col = left, + width = width, + height = height, + style = "minimal", + } + + return opts +end + +function win_float.centered(options) + options = tbl.apply_defaults(options, win_float.default_options) + + local win_opts = win_float.default_opts(options) + + local bufnr = options.bufnr or vim.api.nvim_create_buf(false, true) + local win_id = vim.api.nvim_open_win(bufnr, true, win_opts) + + vim.cmd "setlocal nocursorcolumn" + vim.api.nvim_win_set_option(win_id, "winblend", options.winblend) + + vim.cmd(string.format("autocmd WinLeave silent! execute 'bdelete! %s'", bufnr)) + + return { + bufnr = bufnr, + win_id = win_id, + } +end + +function win_float.centered_with_top_win(top_text, options) + options = tbl.apply_defaults(options, win_float.default_options) + + table.insert(top_text, 1, string.rep("=", 80)) + table.insert(top_text, string.rep("=", 80)) + + local primary_win_opts = win_float.default_opts(nil, nil, options) + local minor_win_opts = vim.deepcopy(primary_win_opts) + + primary_win_opts.height = primary_win_opts.height - #top_text - 1 + primary_win_opts.row = primary_win_opts.row + #top_text + 1 + + minor_win_opts.height = #top_text + + local minor_bufnr = vim.api.nvim_create_buf(false, true) + local minor_win_id = vim.api.nvim_open_win(minor_bufnr, true, minor_win_opts) + + vim.cmd "setlocal nocursorcolumn" + vim.api.nvim_win_set_option(minor_win_id, "winblend", options.winblend) + + vim.api.nvim_buf_set_lines(minor_bufnr, 0, -1, false, top_text) + + local primary_bufnr = vim.api.nvim_create_buf(false, true) + local primary_win_id = vim.api.nvim_open_win(primary_bufnr, true, primary_win_opts) + + vim.cmd "setlocal nocursorcolumn" + vim.api.nvim_win_set_option(primary_win_id, "winblend", options.winblend) + + -- vim.cmd( + -- string.format( + -- "autocmd WinLeave,BufDelete,BufLeave ++once ++nested silent! execute 'bdelete! %s'", + -- primary_buf, + -- minor_buf + -- ) + -- ) + + -- vim.cmd( + -- string.format( + -- "autocmd WinLeave,BufDelete,BufLeave ++once ++nested silent! execute 'bdelete! %s'", + -- primary_buf + -- ) + -- ) + + local primary_border = Border:new(primary_bufnr, primary_win_id, primary_win_opts, {}) + local minor_border = Border:new(minor_bufnr, minor_win_id, minor_win_opts, {}) + + _AssociatedBufs[primary_bufnr] = { + primary_win_id, + minor_win_id, + primary_border.win_id, + minor_border.win_id, + } + + clear_buf_on_leave(primary_bufnr) + + return { + bufnr = primary_bufnr, + win_id = primary_win_id, + + minor_bufnr = minor_bufnr, + minor_win_id = minor_win_id, + } +end + +--- Create window that takes up certain percentags of the current screen. +--- +--- Works regardless of current buffers, tabs, splits, etc. +--@param col_range number | Table: +-- If number, then center the window taking up this percentage of the screen. +-- If table, first index should be start, second_index should be end +--@param row_range number | Table: +-- If number, then center the window taking up this percentage of the screen. +-- If table, first index should be start, second_index should be end +--@param win_opts Table +--@param border_opts Table +function win_float.percentage_range_window(col_range, row_range, win_opts, border_opts) + win_opts = tbl.apply_defaults(win_opts, win_float.default_options) + + local default_win_opts = win_float.default_opts(win_opts) + default_win_opts.relative = "editor" + + local height_percentage, row_start_percentage + if type(row_range) == "number" then + assert(row_range <= 1) + assert(row_range > 0) + height_percentage = row_range + row_start_percentage = (1 - height_percentage) / 2 + elseif type(row_range) == "table" then + height_percentage = row_range[2] - row_range[1] + row_start_percentage = row_range[1] + else + error(string.format("Invalid type for 'row_range': %p", row_range)) + end + + default_win_opts.height = math.ceil(vim.o.lines * height_percentage) + default_win_opts.row = math.ceil(vim.o.lines * row_start_percentage) + + local width_percentage, col_start_percentage + if type(col_range) == "number" then + assert(col_range <= 1) + assert(col_range > 0) + width_percentage = col_range + col_start_percentage = (1 - width_percentage) / 2 + elseif type(col_range) == "table" then + width_percentage = col_range[2] - col_range[1] + col_start_percentage = col_range[1] + else + error(string.format("Invalid type for 'col_range': %p", col_range)) + end + + default_win_opts.col = math.floor(vim.o.columns * col_start_percentage) + default_win_opts.width = math.floor(vim.o.columns * width_percentage) + + local bufnr = win_opts.bufnr or vim.api.nvim_create_buf(false, true) + local win_id = vim.api.nvim_open_win(bufnr, true, default_win_opts) + vim.api.nvim_win_set_buf(win_id, bufnr) + + vim.cmd "setlocal nocursorcolumn" + vim.api.nvim_win_set_option(win_id, "winblend", win_opts.winblend) + + local border = Border:new(bufnr, win_id, default_win_opts, border_opts or {}) + + _AssociatedBufs[bufnr] = { win_id, border.win_id } + + clear_buf_on_leave(bufnr) + + return { + bufnr = bufnr, + win_id = win_id, + + border_bufnr = border.bufnr, + border_win_id = border.win_id, + } +end + +function win_float.clear(bufnr) + if _AssociatedBufs[bufnr] == nil then + return + end + + for _, win_id in ipairs(_AssociatedBufs[bufnr]) do + if vim.api.nvim_win_is_valid(win_id) then + vim.api.nvim_win_close(win_id, true) + end + end + + _AssociatedBufs[bufnr] = nil +end + +return win_float diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/window/init.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/window/init.lua new file mode 100644 index 0000000..e5b85e1 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/plenary/window/init.lua @@ -0,0 +1,16 @@ +local window = {} + +window.try_close = function(win_id, force) + if force == nil then + force = true + end + + pcall(vim.api.nvim_win_close, win_id, force) +end + +window.close_related_win = function(parent_win_id, child_win_id) + window.try_close(parent_win_id, true) + window.try_close(child_win_id, true) +end + +return window diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/lua/say.lua b/.config/nvim/pack/vendor/start/plenary.nvim/lua/say.lua new file mode 100644 index 0000000..f1d7e54 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/lua/say.lua @@ -0,0 +1,61 @@ +local unpack = table.unpack or unpack + +local registry = { } +local current_namespace +local fallback_namespace + +local s = { + + _COPYRIGHT = "Copyright (c) 2012 Olivine Labs, LLC.", + _DESCRIPTION = "A simple string key/value store for i18n or any other case where you want namespaced strings.", + _VERSION = "Say 1.2", + + set_namespace = function(self, namespace) + current_namespace = namespace + if not registry[current_namespace] then + registry[current_namespace] = {} + end + end, + + set_fallback = function(self, namespace) + fallback_namespace = namespace + if not registry[fallback_namespace] then + registry[fallback_namespace] = {} + end + end, + + set = function(self, key, value) + registry[current_namespace][key] = value + end +} + +local __meta = { + __call = function(self, key, vars) + vars = vars or {} + + local str = registry[current_namespace][key] or registry[fallback_namespace][key] + + if str == nil then + return nil + end + str = tostring(str) + local strings = {} + + for i,v in ipairs(vars) do + table.insert(strings, tostring(v)) + end + + return #strings > 0 and str:format(unpack(strings)) or str + end, + + __index = function(self, key) + return registry[key] + end +} + +s:set_fallback('en') +s:set_namespace('en') + +s._registry = registry + +return setmetatable(s, __meta) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/plenary.nvim-scm-1.rockspec b/.config/nvim/pack/vendor/start/plenary.nvim/plenary.nvim-scm-1.rockspec new file mode 100644 index 0000000..73a0f19 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/plenary.nvim-scm-1.rockspec @@ -0,0 +1,35 @@ +local _MODREV, _SPECREV = 'scm', '-1' +rockspec_format = "3.0" +package = 'plenary.nvim' +version = _MODREV .. _SPECREV + +description = { + summary = 'lua functions you don\'t want to write ', + labels = { "neovim" }, + detailed = [[ + plenary: full; complete; entire; absolute; unqualified. All the lua functions I don't want to write twice. + ]], + homepage = 'http://github.com/nvim-lua/plenary.nvim', + license = 'MIT/X11', +} + +dependencies = { + 'lua >= 5.1, < 5.4', + 'luassert' +} + +source = { + url = 'git://github.com/nvim-lua/plenary.nvim', +} + +build = { + type = 'builtin', + copy_directories = { + 'data', + 'plugin' + } +} +test = { + type = "command", + command = "make test" +} diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/plugin/plenary.vim b/.config/nvim/pack/vendor/start/plenary.nvim/plugin/plenary.vim new file mode 100644 index 0000000..3fb7bc8 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/plugin/plenary.vim @@ -0,0 +1,9 @@ + +" Create command for running busted +command! -nargs=1 -complete=file PlenaryBustedFile + \ lua require('plenary.test_harness').test_file([[]]) + +command! -nargs=+ -complete=file PlenaryBustedDirectory + \ lua require('plenary.test_harness').test_directory_command([[]]) + +nnoremap PlenaryTestFile :lua require('plenary.test_harness').test_file(vim.fn.expand("%:p")) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/rockspec.template b/.config/nvim/pack/vendor/start/plenary.nvim/rockspec.template new file mode 100644 index 0000000..c5070ef --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/rockspec.template @@ -0,0 +1,40 @@ +local git_tag = '$git_tag' +local modrev = '$modrev' +local specrev = '-1' + +local repo_url = '$repo_url' + +rockspec_format = '3.0' +package = '$package' +version = modrev .. specrev + +description = { + summary = '$summary', + detailed = $detailed_description, + labels = $labels, + homepage = '$homepage', + $license +} + +dependencies = { + 'lua >= 5.1, < 5.4', + 'luassert' +} + +source = { + url = repo_url .. '/archive/' .. git_tag .. '.zip', + dir = '$repo_name-' .. modrev, +} + +build = { + type = 'builtin', + copy_directories = { + 'data', + 'plugin' + } +} + +test = { + type = 'command', + command = 'make test' +} diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/scripts/generate_luassert_types.lua b/.config/nvim/pack/vendor/start/plenary.nvim/scripts/generate_luassert_types.lua new file mode 100644 index 0000000..0785fe1 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/scripts/generate_luassert_types.lua @@ -0,0 +1,94 @@ +require "luassert" +local namespaces = require "luassert.namespaces" + +local reserved_words = { + "and", + "break", + "do", + "else", + "elseif", + "end", + "false", + "for", + "function", + "if", + "in", + "local", + "nil", + "not", + "or", + "repeat", + "return", + "then", + "true", + "until", + "while", +} + +local content = { + "---@meta", + "---This file is autogenerated, DO NOT EDIT", + 'error "Cannot require a meta file"', + "", + "---@generic T:any", + "---@alias LuassertFunction fun(value:T, message?:string):T", + "---@alias LuassertFunctionTwoArgs fun(expected:T, actual:T, message?:string):T", + "---@alias LuassertFunctionMultiArgs fun(...:T):T", + "", + "---@class Luassert", +} + +local args_count_per_matcher = { + matches = 2, + match = 2, + equal = 2, + equals = 2, + same = 2, + error_match = 2, + error_matches = 2, + matches_error = 2, + match_error = 2, + near = 3, +} + +local get_type_by_args = function(assertion) + local arg_count = args_count_per_matcher[assertion] or 1 + if arg_count == 1 then + return "LuassertFunction" + end + if arg_count == 2 then + return "LuassertFunctionTwoArgs" + end + return "LuassertFunctionMultiArgs" +end + +local fix_reserved_words = function(word) + if vim.tbl_contains(reserved_words, word) then + return table.concat { word:sub(1, 1):upper(), word:sub(2) } + end + return word +end + +local assertions = vim.tbl_keys(namespaces.assertion) +local modifiers = vim.tbl_keys(namespaces.modifier) + +table.sort(assertions) +table.sort(modifiers) + +for _, modifier in ipairs(modifiers) do + for _, assertion in ipairs(assertions) do + table.insert(content, ("---@field %s_%s %s"):format(modifier, assertion, get_type_by_args(assertion))) + end +end + +for _, assertion in ipairs(assertions) do + table.insert(content, ("---@field %s %s"):format(fix_reserved_words(assertion), get_type_by_args(assertion))) +end + +for _, modifier in ipairs(modifiers) do + table.insert(content, ("---@field %s Luassert"):format(fix_reserved_words(modifier))) +end + +local fd = assert(vim.loop.fs_open("./lua/plenary/_meta/_luassert.lua", "w", 438)) +assert(vim.loop.fs_write(fd, table.concat(content, "\n"), 0)) +assert(vim.loop.fs_close(fd)) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/scripts/minimal.vim b/.config/nvim/pack/vendor/start/plenary.nvim/scripts/minimal.vim new file mode 100644 index 0000000..36acc45 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/scripts/minimal.vim @@ -0,0 +1,3 @@ + +set rtp+=. +runtime plugin/plenary.vim diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/scripts/update_filetypes_from_github.lua b/.config/nvim/pack/vendor/start/plenary.nvim/scripts/update_filetypes_from_github.lua new file mode 100644 index 0000000..1a2225f --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/scripts/update_filetypes_from_github.lua @@ -0,0 +1,208 @@ +local FORCE_DOWNLOAD = false + +local log = require('plenary.log') + + +-- Defines all Languages known to GitHub. +-- +-- fs_name - Optional field. Only necessary as a replacement for the sample directory name if the +-- language name is not a valid filename under the Windows filesystem (e.g., if it +-- contains an asterisk). +-- type - Either data, programming, markup, prose, or nil +-- aliases - An Array of additional aliases (implicitly includes name.downcase) +-- ace_mode - A String name of the Ace Mode used for highlighting whenever +-- a file is edited. This must match one of the filenames in http://git.io/3XO_Cg. +-- Use "text" if a mode does not exist. +-- codemirror_mode - A String name of the CodeMirror Mode used for highlighting whenever a file is edited. +-- This must match a mode from https://git.io/vi9Fx +-- codemirror_mime_type - A String name of the file mime type used for highlighting whenever a file is edited. +-- This should match the `mime` associated with the mode from https://git.io/f4SoQ +-- wrap - Boolean wrap to enable line wrapping (default: false) +-- extensions - An Array of associated extensions (the first one is +-- considered the primary extension, the others should be +-- listed alphabetically) +-- filenames - An Array of filenames commonly associated with the language +-- interpreters - An Array of associated interpreters +-- searchable - Boolean flag to enable searching (defaults to true) +-- language_id - Integer used as a language-name-independent indexed field so that we can rename +-- languages in Linguist without reindexing all the code on GitHub. Must not be +-- changed for existing languages without the explicit permission of GitHub staff. +-- color - CSS hex color to represent the language. Only used if type is "programming" or "markup". +-- tm_scope - The TextMate scope that represents this programming +-- language. This should match one of the scopes listed in +-- the grammars.yml file. Use "none" if there is no grammar +-- for this language. +-- group - Name of the parent language. Languages in a group are counted +-- in the statistics as the parent language. + +local lyaml = require('lyaml') + +local Path = require('plenary.path') +local curl = require('plenary.curl') + +local write_file = function(path, string) + local fd = assert(vim.loop.fs_open(path, "w", 438)) + assert(vim.loop.fs_write(fd, string, 0)) + assert(vim.loop.fs_close(fd)) +end + +if FORCE_DOWNLOAD or not Path:new("build/languages.yml"):exists() then + local languages_yml = curl.get( + 'https://raw.githubusercontent.com/github/linguist/master/lib/linguist/languages.yml' + ).body + + write_file("build/languages.yml", languages_yml) +else + print("Using already downloaded file!") +end + +local prio = { + no_match = -1, + scope = 1, + alias = 2, + exact_match = 3, +} + +local find_filetype = function(name, linguist_info, filetype_set) + name = string.lower(name) + + local filetype, priority = nil, -1 + if filetype_set[name] then + filetype, priority = name, prio.exact_match + end + + if not filetype then + if linguist_info.aliases then + for _, ft in ipairs(linguist_info.aliases) do + ft = string.lower(ft) + if filetype_set[ft] then + filetype, priority = ft, prio.alias + + break + end + end + end + end + + if not filetype then + if linguist_info.tm_scope then + local tm_scope_split = vim.split(linguist_info.tm_scope, ".", true) + local tm_scope = tm_scope_split[#tm_scope_split] + + if filetype_set[tm_scope] then + filetype, priority = tm_scope, prio.scope + end + end + end + + return filetype, priority +end + +local overeager_filetypes = { + xml = true, +} + + +local parse_file = function() + local yml_string = Path:new("build/languages.yml"):read() + local yml_table = lyaml.load(yml_string) + + local output = { + extension = {}, + file_name = {} + } + local intervention = {} + + local filetype_completions = vim.fn.getcompletion('', 'filetype') + + local filetype_set = {} + for _, completed_ft in ipairs(filetype_completions) do + filetype_set[completed_ft] = true + end + + local add_extension = function(ext, filetype, priority) + -- If we have a better match, don't do it. + if output.extension[ext] then + if overeager_filetypes[output.extension[ext].filetype] then + log.debug("Overager:", output.extension[ext].filetype) + elseif output.extension[ext].priority > priority then + log.debug( + "Skipping:", ext, filetype, priority, + "due to existing:", output.extension[ext].priority, output.extension[ext].filetype + ) + + return + else + log.debug( + "Override:", ext, filetype, priority, + "due to existing:", output.extension[ext].priority, output.extension[ext].filetype + ) + end + end + + output.extension[ext] = { + filetype = filetype, + priority = priority, + } + end + + local add_filename = function(filename, filetype) + output.file_name[filename:lower()] = { + filetype = filetype:lower() + } + end + + for k, v in pairs(yml_table) do + local filetype, priority = find_filetype(k, v, filetype_set) + + if filetype then + if v.extensions then + for _, ext in ipairs(v.extensions) do + if ext:sub(1, 1) == '.' then + ext = ext:sub(2, #ext) + end + + add_extension(ext, filetype, priority) + end + end + + -- For stuff like 'Makefile' + -- This should go in a separate table + if v.filenames then + for _, fname in ipairs(v.filenames) do + add_filename(fname, filetype) + end + end + else + table.insert(intervention, v) + end + end + + -- P(intervention) + + local result = 'return {\n' + + result = result .. " extension = {\n" + for k, v in pairs(output.extension) do + result = result .. string.format(" ['%s'] = [[%s]],\n", k, v.filetype) + end + result = result .. ' },\n' + + result = result .. " file_name = {\n" + for k, v in pairs(output.file_name) do + result = result .. string.format(" ['%s'] = [[%s]],\n", k, v.filetype) + end + result = result .. ' },\n' + + result = result .. '}\n' + + return result +end + +print("Parsing File...") +local res = parse_file() +print("...Done") + +print("Writing File...") +write_file('./data/plenary/filetypes/base.lua', res) +print("...Done!") diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/scripts/update_vararg.py b/.config/nvim/pack/vendor/start/plenary.nvim/scripts/update_vararg.py new file mode 100755 index 0000000..68f6733 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/scripts/update_vararg.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 +import os +import subprocess + +def generate_file(name, outpath, **kwargs): + from jinja2 import Environment, FileSystemLoader + env = Environment(loader=FileSystemLoader('./vararg')) + template = env.get_template(name) + path = os.path.join(outpath, name) + with open(path, 'w') as fp: + fp.write(template.render(kwargs)) + subprocess.run(["lua-format", "-i", path]) + +if __name__ == '__main__': + generate_file('rotate.lua', '../lua/plenary/vararg', amount=16) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/scripts/vararg/rotate.lua b/.config/nvim/pack/vendor/start/plenary.nvim/scripts/vararg/rotate.lua new file mode 100644 index 0000000..6790a9c --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/scripts/vararg/rotate.lua @@ -0,0 +1,30 @@ +---@brief [[ +---Do not edit this file, it was generated! +---Provides a function to rotate a lua vararg +---@brief ]] + +local tbl = require('plenary.tbl') + +local rotate_lookup = {} + +{% for n in range(1, amount) %} + rotate_lookup[{{n}}] = function ({% for n in range(n) %} A{{n}} {{ ", " if not loop.last else "" }} {% endfor %}) + return {% for n in range(1, n) %} A{{n}}, {% endfor %} A0 + end +{% endfor %} + +local function rotate_n(first, ...) + local n = select("#", ...) + 1 + local args = tbl.pack(...) + args[n] = first + return tbl.unpack(args, 1, n) +end + +local function rotate(nargs, ...) + if nargs == nil or nargs < 1 then + return + end + return (rotate_lookup[nargs] or rotate_n)(...) +end + +return rotate diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/manual/large_job_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/manual/large_job_spec.lua new file mode 100644 index 0000000..2ab72a2 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/manual/large_job_spec.lua @@ -0,0 +1,44 @@ +require("plenary.reload").reload_module "plenary" + +local Job = require "plenary.job" +local profiler = require "plenary.profile.lua_profiler" + +profiler.start() + +local start = vim.fn.reltime() +local finish = nil + +local results = {} + +local j = Job:new { + command = "fdfind", + + cwd = "~/plugins/", + + enable_handlers = false, + + on_stdout = function(_, data) + table.insert(results, data) + end, + + -- on_exit = vim.schedule_wrap(function() + -- finish = vim.fn.reltime(start) + -- end), +} + +pcall(function() + j:sync(2000, 5) +end) +finish = vim.fn.reltime(start) + +profiler.stop() +profiler.report "/home/tj/tmp/temp.txt" + +if finish == nil then + print "Did not finish :'(" +else + print("finished in:", vim.fn.reltimestr(finish)) +end + +collectgarbage() +print(collectgarbage "count") diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/minimal_init.vim b/.config/nvim/pack/vendor/start/plenary.nvim/tests/minimal_init.vim new file mode 100644 index 0000000..f3e071b --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/minimal_init.vim @@ -0,0 +1,5 @@ + +set rtp+=. +runtime plugin/plenary.vim + +nnoremap ,,x :luafile % diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async/async_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async/async_spec.lua new file mode 100644 index 0000000..2fb2db3 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async/async_spec.lua @@ -0,0 +1,53 @@ +require("plenary.async").tests.add_to_env() + +describe("async", function() + a.it("void functions can call wrapped functions", function() + local stat = 0 + local saved_arg + + local wrapped = a.wrap(function(inc, callback) + stat = stat + inc + callback() + end, 2) + + local voided = a.void(function(arg) + wrapped(1) + wrapped(2) + wrapped(3) + stat = stat + 1 + saved_arg = arg + end) + + voided "hello" + + assert(stat == 7) + assert(saved_arg == "hello") + end) + + a.it("void functions can call wrapped functions with ignored arguments", function() + local stat = 0 + local saved_arg + + local wrapped = a.wrap(function(inc, nil1, nil2, callback) + assert(type(inc) == "number") + assert(nil1 == nil) + assert(nil2 == nil) + assert(type(callback) == "function") + stat = stat + inc + callback() + end, 4) + + local voided = a.void(function(arg) + wrapped(1) + wrapped(2, nil) + wrapped(3, nil, nil) + stat = stat + 1 + saved_arg = arg + end) + + voided "hello" + + assert(stat == 7) + assert(saved_arg == "hello") + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async/channel_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async/channel_spec.lua new file mode 100644 index 0000000..df5f8ec --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async/channel_spec.lua @@ -0,0 +1,219 @@ +require("plenary.async").tests.add_to_env() +local channel = a.control.channel +local eq = assert.are.same +local apcall = a.util.apcall + +describe("channel", function() + describe("oneshot", function() + a.it("should work when rx is used first", function() + local tx, rx = channel.oneshot() + + a.run(function() + local got = rx() + + eq("sent value", got) + end) + + tx "sent value" + end) + + a.it("should work when tx is used first", function() + local tx, rx = channel.oneshot() + + tx "sent value" + + local got = rx() + + eq("sent value", got) + end) + + a.it("should work with multiple returns", function() + local tx, rx = channel.oneshot() + + a.run(function() + local got, got2 = rx() + eq("sent value", got) + eq("another sent value", got2) + end) + + tx("sent value", "another sent value") + end) + + a.it("should work when sending a falsey value", function() + local tx, rx = channel.oneshot() + + tx(false) + + local res = rx() + eq(res, false) + + local stat, ret = apcall(rx) + eq(stat, false) + local stat, ret = apcall(rx) + eq(stat, false) + end) + + a.it("should work when sending a nil value", function() + local tx, rx = channel.oneshot() + + tx(nil) + + local res = rx() + eq(res, nil) + + local stat, ret = apcall(rx) + eq(stat, false) + local stat, ret = apcall(rx) + eq(stat, false) + end) + + a.it("should error when sending mulitple times", function() + local tx, rx = channel.oneshot() + + tx() + local stat = pcall(tx) + eq(stat, false) + end) + + a.it("should block receiving multiple times", function() + local tx, rx = channel.oneshot() + tx(true) + rx() + local stat = apcall(rx) + eq(stat, false) + end) + end) + + describe("mpsc", function() + a.it("should wait multiple recv before any send", function() + local sender, receiver = channel.mpsc() + + local expected_count = 10 + + a.run(function() + for i = 1, expected_count do + a.util.sleep(250) + sender.send(i) + end + end) + + local receive_count = 0 + while receive_count < expected_count do + receive_count = receive_count + 1 + local i = receiver.recv() + eq(receive_count, i) + end + end) + + a.it("should queues multiple sends before any read", function() + local sender, receiver = channel.mpsc() + + local counter = 0 + + a.run(function() + counter = counter + 1 + sender.send(10) + + counter = counter + 1 + sender.send(20) + end) + + a.util.sleep(1000) + + eq(10, receiver.recv()) + eq(20, receiver.recv()) + eq(2, counter) + end) + + a.it("should queues multiple sends from multiple producers before any read", function() + local sender, receiver = channel.mpsc() + + local counter = 0 + + a.run(function() + counter = counter + 1 + sender.send(10) + + counter = counter + 1 + sender.send(20) + end) + + a.run(function() + counter = counter + 1 + sender.send(30) + + counter = counter + 1 + sender.send(40) + end) + + a.util.sleep(1000) + + local read_counter = 0 + a.util.block_on(function() + for _ = 1, 4 do + receiver.recv() + read_counter = read_counter + 1 + end + end, 1000) + eq(4, counter) + eq(4, read_counter) + end) + + a.it("should read only the last value", function() + local sender, receiver = channel.mpsc() + + local counter = 0 + + a.run(function() + counter = counter + 1 + sender.send(10) + + counter = counter + 1 + sender.send(20) + end) + + a.util.sleep(1000) + + eq(20, receiver.last()) + eq(2, counter) + end) + end) + + describe("counter", function() + a.it("should work", function() + local tx, rx = channel.counter() + + tx.send() + tx.send() + tx.send() + + local counter = 0 + + a.run(function() + for i = 1, 3 do + rx.recv() + counter = counter + 1 + end + end) + + eq(counter, 3) + end) + + a.it("should work when getting last", function() + local tx, rx = channel.counter() + + tx.send() + tx.send() + tx.send() + + local counter = 0 + + a.run(function() + rx.last() + counter = counter + 1 + end) + + eq(counter, 1) + end) + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async/condvar_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async/condvar_spec.lua new file mode 100644 index 0000000..b677762 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async/condvar_spec.lua @@ -0,0 +1,158 @@ +require("plenary.async").tests.add_to_env() +local Condvar = a.control.Condvar +local eq = assert.are.same +local join, run_all = a.util.join, a.util.run_all + +describe("condvar", function() + a.it("should allow blocking", function() + local var = false + + local condvar = Condvar.new() + + a.run(function() + condvar:wait() + var = true + end) + + eq(var, false) + + condvar:notify_one() + + eq(var, true) + end) + + a.it("should be able to notify one when running", function() + local counter = 0 + + local condvar = Condvar.new() + + local first = function() + condvar:wait() + counter = counter + 1 + end + + local second = function() + condvar:wait() + counter = counter + 1 + end + + local third = function() + condvar:wait() + counter = counter + 1 + end + + a.run(function() + join { first, second, third } + end) + + eq(0, counter) + + condvar:notify_one() + + eq(1, counter) + + condvar:notify_one() + + eq(counter, 2) + + condvar:notify_one() + + eq(counter, 3) + end) + + a.it("should allow notify_one to work when using await_all", function() + local counter = 0 + + local condvar = Condvar.new() + + local first = function() + condvar:wait() + counter = counter + 1 + end + + local second = function() + condvar:wait() + counter = counter + 1 + end + + local third = function() + condvar:wait() + counter = counter + 1 + end + + run_all { first, second, third } + + eq(0, counter) + + condvar:notify_one() + + eq(1, counter) + + condvar:notify_one() + + eq(counter, 2) + + condvar:notify_one() + + eq(counter, 3) + end) + + a.it("should notify_all", function() + local counter = 0 + + local condvar = Condvar.new() + + local first = function() + condvar:wait() + counter = counter + 1 + end + + local second = function() + condvar:wait() + counter = counter + 1 + end + + local third = function() + condvar:wait() + counter = counter + 1 + end + + run_all { first, second, third } + + eq(0, counter) + + condvar:notify_all() + + eq(3, counter) + end) + + a.it("notify all works multiple times", function() + local condvar = Condvar.new() + local counter = 0 + + a.run(function() + condvar:wait() + counter = counter + 1 + end) + + a.run(function() + condvar:wait() + counter = counter + 1 + end) + + eq(0, counter) + + condvar:notify_all() + + eq(2, counter) + + a.run(function() + condvar:wait() + counter = 0 + end) + + condvar:notify_all() + + eq(0, counter) + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async/deque_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async/deque_spec.lua new file mode 100644 index 0000000..3cf729b --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async/deque_spec.lua @@ -0,0 +1,91 @@ +local Deque = require("plenary.async.structs").Deque +local eq = assert.are.same + +-- just a helper to create the test deque +local function new_deque() + local deque = Deque.new() + eq(deque:len(), 0) + + deque:pushleft(1) + eq(deque:len(), 1) + + deque:pushleft(2) + eq(deque:len(), 2) + + deque:pushright(3) + eq(deque:len(), 3) + + deque:pushright(4) + eq(deque:len(), 4) + + deque:pushright(5) + eq(deque:len(), 5) + + return deque +end + +describe("deque", function() + it("should allow pushing and popping and finding len", function() + new_deque() + end) + + it("should be able to iterate from left", function() + local deque = new_deque() + + local iter = deque:ipairs_left() + + local i, v = iter() + eq(i, -2) + eq(v, 2) + + i, v = iter() + eq(i, -1) + eq(v, 1) + + i, v = iter() + eq(i, 0) + eq(v, 3) + + i, v = iter() + eq(i, 1) + eq(v, 4) + + i, v = iter() + eq(i, 2) + eq(v, 5) + end) + + it("should be able to iterate from right", function() + local deque = new_deque() + + local iter = deque:ipairs_right() + + local i, v = iter() + eq(i, 2) + eq(v, 5) + + i, v = iter() + eq(i, 1) + eq(v, 4) + + i, v = iter() + eq(i, 0) + eq(v, 3) + + i, v = iter() + eq(i, -1) + eq(v, 1) + + i, v = iter() + eq(i, -2) + eq(v, 2) + end) + + it("should allow clearing", function() + local deque = new_deque() + + deque:clear() + + assert(deque:is_empty()) + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async/semaphore_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async/semaphore_spec.lua new file mode 100644 index 0000000..1342350 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async/semaphore_spec.lua @@ -0,0 +1,58 @@ +require("plenary.async").tests.add_to_env() +local Semaphore = a.control.Semaphore + +local eq = assert.are.same + +describe("semaphore", function() + a.it("should validate arguments", function() + local status = pcall(Semaphore.new, -1) + eq(status, false) + + local status = pcall(Semaphore.new) + eq(status, false) + end) + + a.it("should acquire a permit if available", function() + local sem = Semaphore.new(1) + local permit = sem:acquire() + assert(permit ~= nil) + end) + + a.it("should block if no permit is available", function() + local sem = Semaphore.new(1) + sem:acquire() + + local completed = false + local blocking = function() + sem:acquire() + completed = true + end + a.run(blocking) + + eq(completed, false) + end) + + a.it("should give another permit when an acquired permit is released", function() + local sem = Semaphore.new(1) + local permit = sem:acquire() + permit:forget() + local next_permit = sem:acquire() + assert(next_permit ~= nil) + end) + + a.it("should permit the next waiting client when a permit is released", function() + local sem = Semaphore.new(1) + local permit = sem:acquire() + + local completed = false + local blocking = function() + sem:acquire() + completed = true + end + + a.run(blocking) + permit:forget() + + eq(completed, true) + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async/test_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async/test_spec.lua new file mode 100644 index 0000000..334910e --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async/test_spec.lua @@ -0,0 +1,58 @@ +require("plenary.async").tests.add_to_env() + +a.describe("a.before_each", function() + local counter = 0 + + local set_counter_to_one = a.wrap(function(callback) + a.util.sleep(5) + counter = 1 + end, 1) + + a.before_each(a.void(function() + set_counter_to_one() + end)) + + a.it("should run in async context", function() + counter = counter + 1 + assert.are.same(counter, 2) + end) + + a.it("should run for all tests", function() + counter = counter + 2 + assert.are.same(counter, 3) + end) +end) + +a.describe("a.after_each", function() + local counter = 0 + + local set_counter_to_one = a.wrap(function(callback) + a.util.sleep(5) + counter = 1 + end, 1) + + a.after_each(a.void(function() + set_counter_to_one() + end)) + + a.it("should not run before first test", function() + counter = counter + 1 + assert.are.same(counter, 1) + end) + + a.it("should run before the second test", function() + counter = counter + 2 + assert.are.same(counter, 3) + end) + + a.it("should run before the third test", function() + counter = counter + 3 + assert.are.same(counter, 4) + end) +end) + +a.describe("a.pending", function() + a.pending("This test is disabled", function() + assert(false, "Should not run") + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async/util_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async/util_spec.lua new file mode 100644 index 0000000..94ae324 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async/util_spec.lua @@ -0,0 +1,77 @@ +require("plenary.async").tests.add_to_env() +local block_on = a.util.block_on +local eq = assert.are.same +local id = a.util.id + +describe("async await util", function() + describe("block_on", function() + a.it("should block_on", function() + local fn = function() + a.util.sleep(100) + return "hello" + end + + local res = fn() + eq(res, "hello") + end) + + a.it("should work even when failing", function() + local nonleaf = function() + eq(true, false) + end + + local stat = pcall(block_on, nonleaf) + eq(stat, false) + end) + end) + + describe("protect", function() + a.it("should be able to protect a non-leaf future", function() + local nonleaf = function() + error "This should error" + return "return" + end + + local stat, ret = pcall(nonleaf) + eq(false, stat) + assert(ret:match "This should error") + end) + + a.it("should be able to protect a non-leaf future that doesnt fail", function() + local nonleaf = function() + return "didnt fail" + end + + local stat, ret = pcall(nonleaf) + eq(stat, true) + eq(ret, "didnt fail") + end) + end) + + local function sleep(msec) + return function() + a.util.sleep(msec) + return msec + end + end + + describe("race", function() + a.it("should return the first result", function() + local funcs = vim.tbl_map(sleep, { 300, 400, 100, 200 }) + local result = a.util.race(funcs) + eq(result, 100) + end) + end) + + describe("run_first", function() + a.it("should return the first result", function() + local async_functions = vim.tbl_map(function(num) + return function(callback) + return a.run(sleep(num), callback) + end + end, { 300, 400, 100, 200 }) + local result = a.util.run_first(async_functions) + eq(result, 100) + end) + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async_testing_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async_testing_spec.lua new file mode 100644 index 0000000..c1c991b --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/async_testing_spec.lua @@ -0,0 +1,75 @@ +local Job = require "plenary.job" + +local Timing = {} + +function Timing:log(name) + self[name] = vim.loop.uptime() +end + +function Timing:check(from, to, min_elapsed) + assert(self[from], "did not log " .. from) + assert(self[to], "did not log " .. to) + local elapsed = self[to] - self[from] + assert( + min_elapsed <= elapsed, + string.format("only took %s to get from %s to %s - expected at least %s", elapsed, from, to, min_elapsed) + ) +end + +describe("Async test", function() + it("can resume testing with vim.defer_fn", function() + local co = coroutine.running() + assert(co, "not running inside a coroutine") + + local timing = setmetatable({}, { __index = Timing }) + + vim.defer_fn(function() + coroutine.resume(co) + end, 200) + timing:log "before" + coroutine.yield() + timing:log "after" + timing:check("before", "after", 0.1) + end) + + it("can resume testing from job callback", function() + local co = coroutine.running() + assert(co, "not running inside a coroutine") + + local timing = setmetatable({}, { __index = Timing }) + + Job:new({ + command = "bash", + args = { + "-ce", + [[ + sleep 0.2 + echo hello + sleep 0.2 + echo world + sleep 0.2 + exit 42 + ]], + }, + on_stdout = function(_, data) + timing:log(data) + end, + on_exit = function(_, exit_status) + timing:log "exit" + --This is required so that the rest of the test will run in a proper context + vim.schedule(function() + coroutine.resume(co, exit_status) + end) + end, + }):start() + timing:log "job started" + local exit_status = coroutine.yield() + timing:log "job finished" + assert.are.equal(exit_status, 42) + + timing:check("job started", "job finished", 0.3) + timing:check("job started", "hello", 0.1) + timing:check("hello", "world", 0.1) + timing:check("world", "job finished", 0.1) + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/context_manager_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/context_manager_spec.lua new file mode 100644 index 0000000..54de82b --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/context_manager_spec.lua @@ -0,0 +1,203 @@ +local context_manager = require "plenary.context_manager" +local debug_utils = require "plenary.debug_utils" +local Path = require "plenary.path" + +local with = context_manager.with +local open = context_manager.open + +local README_STR_PATH = vim.fn.fnamemodify(debug_utils.sourced_filepath(), ":h:h:h") .. "/README.md" +local README_FIRST_LINE = "# plenary.nvim" + +describe("context_manager", function() + it("works with objects", function() + local obj_manager = { + enter = function(self) + self.result = 10 + return self.result + end, + + exit = function() end, + } + + local result = with(obj_manager, function(obj) + return obj + end) + + assert.are.same(10, result) + assert.are.same(obj_manager.result, result) + end) + + it("works with coroutine", function() + local co = function() + coroutine.yield(10) + end + + local result = with(co, function(obj) + return obj + end) + + assert.are.same(10, result) + end) + + it("does not work with coroutine with extra yields", function() + local co = function() + coroutine.yield(10) + + -- Can't yield twice. That'd be bad and wouldn't make any sense. + coroutine.yield(10) + end + + assert.has.error_match(function() + with(co, function(obj) + return obj + end) + end, "Should not yield anymore, otherwise that would make things complicated") + end) + + it("reads from files with open", function() + local result = with(open(README_STR_PATH), function(reader) + return reader:read() + end) + + assert.are.same(result, README_FIRST_LINE) + end) + + it("reads from Paths with open", function() + local p = Path:new(README_STR_PATH) + + local result = with(open(p), function(reader) + return reader:read() + end) + + assert.are.same(result, README_FIRST_LINE) + end) + + it("calls exit on error with objects", function() + local entered = false + local exited = false + local obj_manager = { + enter = function(self) + entered = true + end, + + exit = function(self) + exited = true + end, + } + + assert.has.error_match(function() + with(obj_manager, function(obj) + assert(false, "failed in callback") + end) + end, "failed in callback") + + assert.is["true"](entered) + assert.is["true"](exited) + end) + + it("calls exit on error with coroutines", function() + local entered = false + local exited = false + local co = function() + entered = true + coroutine.yield(nil) + + exited = true + end + + assert.has.error_match(function() + with(co, function(obj) + assert(false, "failed in callback") + end) + end, "failed in callback") + + assert.is["true"](entered) + assert.is["true"](exited) + end) + + it("fails from enter error with objects", function() + local exited = false + local obj_manager = { + enter = function(self) + assert(false, "failed in enter") + end, + + exit = function(self) + exited = true + end, + } + + local ran_callback = false + assert.has.error_match(function() + with(obj_manager, function(obj) + ran_callback = true + end) + end, "failed in enter") + + assert.is["false"](ran_callback) + assert.is["false"](exited) + end) + + it("fails from enter error with coroutines", function() + local exited = false + local co = function() + assert(false, "failed in enter") + coroutine.yield(nil) + + exited = true + end + + local ran_callback = false + assert.has.error_match(function() + with(co, function(obj) + ran_callback = true + end) + end, "Should have yielded in coroutine.") + + assert.is["false"](ran_callback) + assert.is["false"](exited) + end) + + it("fails from exit error with objects", function() + local entered = false + local obj_manager = { + enter = function(self) + entered = true + end, + + exit = function(self) + assert(false, "failed in exit") + end, + } + + local ran_callback = false + assert.has.error_match(function() + with(obj_manager, function(obj) + ran_callback = true + end) + end, "failed in exit") + + assert.is["true"](entered) + assert.is["true"](ran_callback) + end) + + it("fails from exit error with coroutines", function() + local entered = false + local co = function() + entered = true + coroutine.yield(nil) + + assert(false, "failed in exit") + end + + local ran_callback = false + assert.has.error_match(function() + with(co, function(obj) + ran_callback = true + end) + end, "Should be done") + + assert.is["true"](entered) + assert.is["true"](ran_callback) + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/curl_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/curl_spec.lua new file mode 100644 index 0000000..c4ba89a --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/curl_spec.lua @@ -0,0 +1,221 @@ +local curl = require "plenary.curl" +local eq = assert.are.same +local incl = function(p, s) + return (nil ~= string.find(s, p)) +end + +describe("CURL Wrapper:", function() + describe("request", function() ----------------------------------------------- + it("sends and returns table.", function() + eq( + "table", + type(curl.request { + url = "https://postman-echo.com/get", + method = "get", + accept = "application/json", + }) + ) + end) + + it("should accept the url as first argument.", function() + local res = curl.get("https://postman-echo.com/get", { + accept = "application/json", + }) + eq(200, res.status) + end) + end) + + describe("GET", function() -------------------------------------------------- + it("sends and returns table.", function() + eq( + "table", + type(curl.get { + url = "https://postman-echo.com/get", + accept = "application/json", + }) + ) + end) + + it("should accept the url as first argument.", function() + local res = curl.get("https://postman-echo.com/get", { + accept = "application/json", + }) + eq(200, res.status) -- table has response status + end) + + it("sends encoded URL query params.", function() + local query = { name = "john Doe", key = "123456" } + local response = curl.get("https://postman-echo.com/get", { + query = query, + }) + + eq(200, response.status) + eq(query, vim.fn.json_decode(response.body).args) + end) + + it("downloads files to opts.output synchronously", function() + local file = "https://media2.giphy.com/media/bEMcuOG3hXVnihvB7x/giphy.gif" + local loc = "/tmp/giphy2.gif" + local res = curl.get(file, { output = loc }) + + eq(1, vim.fn.filereadable(loc), "should exists") + eq(200, res.status, "should return 200") + eq(0, res.exit, "should have exit code of 0") + vim.fn.delete(loc) + end) + + it("downloads files to to opts.output asynchronous", function() + local res = nil + local succ = nil + local done = false + local file = "https://media2.giphy.com/media/notvalid.gif" + local loc = "/tmp/notvalid.gif" + + curl.get(file, { + output = loc, + callback = function(out) + done = true + succ = out.status == 200 + res = out + end, + }) + + vim.wait(60000, function() + return done + end) + + eq(403, res.status, "It should return 403") + assert(not succ, "It should fail") + + vim.fn.delete(loc) + end) + + it("sends with basic-auth as string", function() + local url = "https://postman-echo.com/basic-auth" + local auth, res + + auth = "postman:password" + res = curl.get(url, { auth = auth }) + assert(incl("authenticated.*true", res.body)) + eq(200, res.status) + + auth = "tami5:123456" + res = curl.get(url, { auth = auth }) + assert(not incl("authenticated.*true", res.body), "it should fail") + eq(401, res.status) + end) + + it("sends with basic-auth as table", function() + local url = "https://postman-echo.com/basic-auth" + local res = curl.get(url, { auth = { postman = "password" } }) + assert(incl("authenticated.*true", res.body)) + eq(200, res.status) + end) + end) + + describe("POST", function() -------------------------------------------------- + it("sends raw string", function() + local res = curl.post("https://postman-echo.com/post", { + body = "John Doe", + }) + assert(incl("John", res.body)) + eq(200, res.status) + end) + + it("sends lua table", function() + local res = curl.post("https://jsonplaceholder.typicode.com/posts", { + body = { + title = "Hello World", + body = "...", + }, + }) + eq(201, res.status) + end) + + it("sends file", function() + local res = curl.post("https://postman-echo.com/post", { + body = "./README.md", + }).body + + assert(incl("plenary.test_harness", res)) + end) + + it("sends and recives json body.", function() + local json = { title = "New", name = "YORK" } + local res = curl.post("https://postman-echo.com/post", { + body = vim.fn.json_encode(json), + headers = { + content_type = "application/json", + }, + }).body + eq(json, vim.fn.json_decode(res).json) + end) + + it("should not include the body twice", function() + local json = { title = "New", name = "YORK" } + local body = vim.fn.json_encode(json) + local res = curl.post("https://postman-echo.com/post", { + body = body, + headers = { + content_type = "application/json", + }, + dry_run = true, + }) + local joined_response = table.concat(res, " ") + local first_index = joined_response:find(body) + + eq(nil, joined_response:find(body, first_index + 1)) + end) + end) + describe("PUT", function() -------------------------------------------------- + it("sends changes and get be back the new version.", function() + local cha = { title = "New Title" } + local res = curl.put("https://jsonplaceholder.typicode.com/posts/8", { + body = cha, + }) + eq(cha.title, vim.fn.json_decode(res.body).title) + eq(200, res.status) + end) + end) + + describe("PATCH", function() ------------------------------------------------ + it("sends changes and get be back the new version.", function() + local cha = { title = "New Title" } + local res = curl.patch("https://jsonplaceholder.typicode.com/posts/8", { + body = cha, + }) + eq(cha.title, vim.fn.json_decode(res.body).title) + eq(200, res.status) + end) + end) + + describe("DELETE", function() ------------------------------------------------ + it("sends delete request", function() + local res = curl.delete "https://jsonplaceholder.typicode.com/posts/8" + eq(200, res.status) + end) + end) + + describe("DEBUG", function() -------------------------------------------------- + it("dry_run return the curl command to be ran.", function() + local res = curl.delete("https://jsonplaceholder.typicode.com/posts/8", { dry_run = true }) + assert(type(res) == "table") + end) + end) + + describe("Issue #601", function() -------------------------------------------- + it("should not use URL from previous call", function() + local url = "https://example.com" + local opts = { dry_run = true, dump = "" } -- dump would be random each time + local first = curl.get(url, opts) + eq(table.remove(first, #first), url, "expected url last") + + local success, second = pcall(curl.get, opts) + if success then + eq(first, second, "should be same, but without url") + else + -- Failure is also acceptable + end + end) + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/enum_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/enum_spec.lua new file mode 100644 index 0000000..d4dcf93 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/enum_spec.lua @@ -0,0 +1,93 @@ +local Enum = require "plenary.enum" + +local function should_fail(fun) + local stat = pcall(fun) + assert(not stat, "Function should fail") +end + +describe("Enum", function() + it("should be able to define specific values for members", function() + local E = Enum { + { "Foo", 2 }, + { "Bar", 4 }, + "Qux", + "Baz", + { "Another", 11 }, + } + + assert(E.Foo.value == 2) + assert(E.Bar.value == 4) + assert(E.Qux.value == 5) + assert(E.Baz.value == 6) + assert(E.Another.value == 11) + + assert(E[2] == "Foo") + assert(E[4] == "Bar") + assert(E[5] == "Qux") + assert(E[6] == "Baz") + assert(E[11] == "Another") + end) + it("should compare with itself", function() + local E1 = Enum { + "Foo", + { "Qux", 11 }, + "Bar", + "Baz", + } + + local E2 = Enum { + "Foo", + "Bar", + "Baz", + } + + assert(E1.Foo < E1.Qux) + assert(E1.Baz > E1.Bar) + + assert(not (E1.Foo == E2.Foo)) + + should_fail(function() + return E1.Foo > E2.Foo + end) + + should_fail(function() + return E2.Bar >= E1.Foo + end) + end) + it("should error when accessing invalid field", function() + local E = Enum { + "Foo", + "Bar", + "Baz", + } + + should_fail(function() + return E.foo + end) + + should_fail(function() + return E.bar + end) + end) + it("should fail if there is name or index clashing", function() + should_fail(function() + return Enum { + "Foo", + "Foo", + } + end) + should_fail(function() + return Enum { + "Foo", + { "Bar", 1 }, + } + end) + end) + it("should fail if there is a key that starts with lowercase", function() + should_fail(function() + return Enum { + "foo", + } + end) + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/filetype_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/filetype_spec.lua new file mode 100644 index 0000000..af85bd5 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/filetype_spec.lua @@ -0,0 +1,139 @@ +local filetype = require "plenary.filetype" + +describe("filetype", function() + describe("_get_extension_parts", function() + it("should find stuff with underscores", function() + assert.are.same({ "py" }, filetype._get_extension_parts "__init__.py") + end) + + it("should find all possibilities", function() + assert.are.same({ "rst.txt", "txt" }, filetype._get_extension_parts "example.rst.txt") + assert.are.same({ "emacs.desktop", "desktop" }, filetype._get_extension_parts "example.emacs.desktop") + end) + end) + + describe("detect_from_extension", function() + it("should work for md", function() + assert.are.same("markdown", filetype.detect_from_extension "Readme.md") + end) + + it("should work for CMakeList.txt", function() + assert.are.same("text", filetype.detect_from_extension "CMakeLists.txt") + end) + + it("should work with extensions with dot", function() + assert.are.same("rst", filetype.detect_from_extension "example.rst.txt") + assert.are.same("rst", filetype.detect_from_extension "example.rest.txt") + assert.are.same("yaml", filetype.detect_from_extension "example.yaml.sed") + assert.are.same("yaml", filetype.detect_from_extension "example.yml.mysql") + assert.are.same("erlang", filetype.detect_from_extension "asdf/example.app.src") + assert.are.same("cmake", filetype.detect_from_extension "/asdf/example.cmake.in") + assert.are.same("desktop", filetype.detect_from_extension "/asdf/asdf.desktop.in") + assert.are.same("xml", filetype.detect_from_extension "example.dll.config") + assert.are.same("haml", filetype.detect_from_extension "example.haml.deface") + assert.are.same("html", filetype.detect_from_extension "example.html.hl") + assert.are.same("yaml", filetype.detect_from_extension "example.model.lkml") + assert.are.same("rust", filetype.detect_from_extension "example.rs.in") + assert.are.same("sh", filetype.detect_from_extension "example.sh.in") + assert.are.same("json", filetype.detect_from_extension "example.tfstate.backup") + assert.are.same("yaml", filetype.detect_from_extension "example.view.lkml") + assert.are.same("xml", filetype.detect_from_extension "example.xml.dist") + assert.are.same("xml", filetype.detect_from_extension "example.xsp.metadata") + end) + + it("should work for ext==ft even without a table value", function() + assert.are.same("bib", filetype.detect_from_extension "file.bib") + assert.are.same("bst", filetype.detect_from_extension "file.bst") + end) + end) + + describe("detect_from_name", function() + it("should work for common filenames, like makefile", function() + assert.are.same("make", filetype.detect_from_name "Makefile") + assert.are.same("make", filetype.detect_from_name "makefile") + end) + + it("should work for CMakeList.txt", function() + assert.are.same("cmake", filetype.detect_from_name "CMakeLists.txt") + end) + end) + + describe("detect_from_modeline", function() + it("should work for modeline 2", function() + assert.are.same("help", filetype._parse_modeline " vim:tw=78:ts=8:noet:ft=help:norl:") + end) + + it("should return nothing if ft not found in modeline", function() + assert.are.same("", filetype._parse_modeline "/* vim: set ts=8 sw=4 tw=0 noet : */") + end) + + it("should return nothing for random line", function() + assert.are.same("", filetype._parse_modeline "return filetype") + end) + end) + + describe("detect_from_shebang", function() + it("should work for shell", function() + assert.are.same("sh", filetype._parse_shebang "#!/bin/sh") + end) + + it("should work for bash", function() + assert.are.same("sh", filetype._parse_shebang "#!/bin/bash") + end) + + it("should work for usr/bin/env shell", function() + assert.are.same("sh", filetype._parse_shebang "#!/usr/bin/env sh") + end) + + it("should work for env shell", function() + assert.are.same("sh", filetype._parse_shebang "#!/bin/env sh") + end) + + it("should work for python", function() + assert.are.same("python", filetype._parse_shebang "#!/bin/python") + end) + + it("should work for /usr/bin/python3", function() + assert.are.same("python", filetype._parse_shebang "#!/usr/bin/python3") + end) + + it("should work for python3", function() + assert.are.same("python", filetype._parse_shebang "#!/bin/python3") + end) + + it("should work for env python", function() + assert.are.same("python", filetype._parse_shebang "#!/bin/env python") + end) + + it("should not work for random line", function() + assert.are.same("", filetype._parse_shebang 'local path = require"plenary.path"') + end) + end) + + describe("detect", function() + it("should work for common filetypes, like python", function() + assert.are.same("python", filetype.detect "__init__.py") + end) + + it("should work for common filenames, like makefile", function() + assert.are.same("make", filetype.detect "Makefile") + assert.are.same("make", filetype.detect "makefile") + end) + + it("should work for CMakeList.txt", function() + assert.are.same("cmake", filetype.detect "CMakeLists.txt") + end) + + it("should work for common files, even with .s, like .bashrc", function() + assert.are.same("sh", filetype.detect ".bashrc") + end) + + it("should work fo custom filetypes, like fennel", function() + assert.are.same("fennel", filetype.detect "init.fnl") + end) + + it("should work for custom filenames, like Cakefile", function() + assert.are.same("coffee", filetype.detect "Cakefile") + end) + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/functional_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/functional_spec.lua new file mode 100644 index 0000000..9a34b22 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/functional_spec.lua @@ -0,0 +1,18 @@ +local f = require "plenary.functional" + +describe("functional", function() + describe("partial", function() + local function args(...) + assert.is.equal(4, select("#", ...)) + return table.concat({ ... }, ",") + end + it("should bind correct parameters", function() + local expected = args(1, 2, 3, 4) + assert.is.equal(expected, f.partial(args)(1, 2, 3, 4)) + assert.is.equal(expected, f.partial(args, 1)(2, 3, 4)) + assert.is.equal(expected, f.partial(args, 1, 2)(3, 4)) + assert.is.equal(expected, f.partial(args, 1, 2, 3)(4)) + assert.is.equal(expected, f.partial(args, 1, 2, 3, 4)()) + end) + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/iterators_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/iterators_spec.lua new file mode 100644 index 0000000..8d553bf --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/iterators_spec.lua @@ -0,0 +1,149 @@ +local i = require "plenary.iterators" +local f = require "plenary.functional" +local eq = assert.are.same + +local function check_keys(tbl, keys) + for _, key in ipairs(keys) do + if not tbl[key] then + error("Key " .. key .. " was not found") + end + end +end + +describe("iterators", function() + it("should be able to create iterator from table", function() + local tbl = { first = 1, second = 2, third = 3, fourth = 4 } + local results = i.iter(tbl):tolist() + eq(#results, 4) + check_keys(tbl, { "first", "second", "third", "fourth" }) + results = { + { "first", 1 }, + { "second", 2 }, + { "third", 3 }, + { "fourth", 4 }, + } + end) + + it("should be able to create iterator from array", function() + local tbl = { 1, 2, 3, 4 } + local results = i.iter(tbl):tolist() + eq(#results, 4) + check_keys(tbl, { 1, 2, 3, 4 }) + end) + + it("should be able to fold", function() + local numbers = { 1, 2, 3, 4 } + local result = i.iter(numbers):fold(0, function(a, v) + return a + v + end) + eq(result, 10) + + local strings = { "hello", "world", "this", "is", "a", "test" } + result = i.iter(strings):fold("", function(a, v) + return a .. v + end) + eq(result, "helloworldthisisatest") + end) + + it("should be able to enumerate", function() + local tbl = { 1, 2, 3, 4 } + local results = i.iter(tbl) + :enumerate() + :map(function(idx, v) + return { idx, v } + end) + :tolist() + eq(#results, 4) + eq(results[1], { 1, 1 }) + eq(results[2], { 2, 2 }) + eq(results[3], { 3, 3 }) + eq(results[4], { 4, 4 }) + end) + + it("should be able to find", function() + local tbl = { 1, 2, 3, 4 } + local tbl_iter = i.iter(tbl) + local res = tbl_iter:find(2) + eq(res, 2) + res = tbl_iter:find "will not find this" + assert(not res) + + tbl = { 1, 2, 3, 4, "some random string", 6 } + eq( + i.iter(tbl):find(function(x) + return type(x) == "string" + end), + "some random string" + ) + end) + + it("should be table to chain", function() + local first = i.iter { 1, 2, 3 } + local second = i.iter { 4, 5, 6, 7 } + local third = i.iter { 8, 9, 10 } + local res = (first .. second .. third):tolist() + eq(res, i.range(10):tolist()) + end) + + it("should make a range", function() + eq({ 1, 2, 3, 4, 5 }, i.range(5):tolist()) + end) + + it("should be able to make a stateful", function() + local iter = i.range(3):stateful() + eq(iter(), 1) + eq(iter(), 2) + eq(iter(), 3) + assert(not iter()) + assert(not iter()) + assert(not iter()) + + local iter = i.range(3):stateful() + -- dump(iter:tolist()) + eq(iter:tolist(), { 1, 2, 3 }) + end) + + it("should be able to flatten", function() + local iter = i.range(3) + :map(function(_) + return i.iter { 5, 7, 9 } + end) + :flatten() + :stateful() + eq(iter(), 5) + eq(iter(), 7) + eq(iter(), 9) + eq(iter(), 5) + eq(iter(), 7) + eq(iter(), 9) + eq(iter(), 5) + eq(iter(), 7) + eq(iter(), 9) + local iter = i.range(3) + :map(function(_) + return i.iter { 5, 7, 9 } + end) + :flatten() + eq(iter:tolist(), { 5, 7, 9, 5, 7, 9, 5, 7, 9 }) + end) + + it("should be able to flatten very nested stuff", function() + local iter = i.iter({ 5, 6, i.iter { i.iter { 5, 5 }, 7, 8 }, i.iter { 9, 10, i.iter { 1, 2 } } }):flatten() + eq(iter:tolist(), { 5, 6, 5, 5, 7, 8, 9, 10, 1, 2 }) + end) + + it("chaining nil should work", function() + local iter = i.iter(""):chain(i.iter { 5, 7, 9 }) + eq(#iter:tolist(), 3) + end) + + describe("generators", function() + it("should be able to split", function() + local iter = i.split(" hello person world ", " ") + eq(iter:tolist(), { "", "hello", "person", "world", "" }) + + iter = i.split("\n\n\nfirst\nsecond\nthird\n\n", "\n") + eq(iter:tolist(), { "", "", "", "first", "second", "third", "", "" }) + end) + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/job/validation_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/job/validation_spec.lua new file mode 100644 index 0000000..5c9f2d7 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/job/validation_spec.lua @@ -0,0 +1,28 @@ +local Job = require "plenary.job" + +describe("Job Validation", function() + it("does not require command when called with array method", function() + local ok, j = pcall(Job.new, Job, { "ls" }) + assert(ok, "Accepts positional arguments") + assert(j.command == "ls") + end) + + it("cannot use command and array syntax", function() + local ok = pcall(Job.new, Job, { "ls", command = "ls" }) + assert(not ok, "cannot use command and array syntax") + end) + + it("can parse command and args from array syntax", function() + local ok, j = pcall(Job.new, Job, { "ls", "-al" }) + assert(ok, "Accepts positional arguments") + assert(j.command == "ls") + assert.are.same({ "-al" }, j.args) + end) + + it("can parse command and multiple args from array syntax", function() + local ok, j = pcall(Job.new, Job, { "ls", "-al", "~" }) + assert(ok, "Accepts positional arguments") + assert(j.command == "ls") + assert.are.same({ "-al", "~" }, j.args) + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/job_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/job_spec.lua new file mode 100644 index 0000000..c2edde0 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/job_spec.lua @@ -0,0 +1,864 @@ +local Job = require "plenary.job" + +local has_all_executables = function(execs) + for _, e in ipairs(execs) do + if vim.fn.executable(e) == 0 then + return false + end + end + + return true +end + +local tables_equal = function(t1, t2) + if #t1 ~= #t2 then + return false + end + for i, t1_v in ipairs(t1) do + if t2[i] ~= t1_v then + return false + end + end + return true +end + +local wait_for_result = function(job, result) + if type(result) == "string" then + result = { result } + end + vim.wait(1000, function() + return tables_equal(job:result(), result) + end) +end + +describe("Job", function() + describe("> cat manually >", function() + it("should split simple stdin", function() + local results = {} + local job = Job:new { + command = "cat", + + on_stdout = function(_, data) + table.insert(results, data) + end, + } + + job:start() + job:send "hello\n" + job:send "world\n" + + wait_for_result(job, { "hello", "world" }) + job:shutdown() + + assert.are.same(job:result(), { "hello", "world" }) + assert.are.same(job:result(), results) + end) + + it("should allow empty strings", function() + local results = {} + local job = Job:new { + command = "cat", + + on_stdout = function(_, data) + table.insert(results, data) + end, + } + + job:start() + job:send "hello\n" + job:send "\n" + job:send "world\n" + job:send "\n" + + wait_for_result(job, { "hello", "", "world", "" }) + job:shutdown() + + assert.are.same(job:result(), { "hello", "", "world", "" }) + assert.are.same(job:result(), results) + end) + + it("should split stdin across newlines", function() + local results = {} + local job = Job:new { + -- writer = "hello\nword\nthis is\ntj", + command = "cat", + + on_stdout = function(_, data) + table.insert(results, data) + end, + } + + job:start() + job:send "hello\nwor" + job:send "ld\n" + + wait_for_result(job, { "hello", "world" }) + job:shutdown() + + assert.are.same(job:result(), { "hello", "world" }) + assert.are.same(job:result(), results) + end) + + it("should split stdin across newlines with no ending newline", function() + local results = {} + local job = Job:new { + -- writer = "hello\nword\nthis is\ntj", + command = "cat", + + on_stdout = function(_, data) + table.insert(results, data) + end, + } + + job:start() + job:send "hello\nwor" + job:send "ld" + + wait_for_result(job, { "hello", "world" }) + job:shutdown() + + assert.are.same(job:result(), { "hello", "world" }) + assert.are.same(job:result(), results) + end) + + it("should return last line when there is ending newline", function() + local results = {} + local job = Job:new { + command = "printf", + + args = { "test1\ntest2\n" }, + + on_stdout = function(_, data) + table.insert(results, data) + end, + } + + job:sync() + + assert.are.same(job:result(), { "test1", "test2" }) + assert.are.same(job:result(), results) + end) + + it("should return last line when there is no ending newline", function() + local results = {} + local job = Job:new { + command = "printf", + + args = { "test1\ntest2" }, + + on_stdout = function(_, data) + table.insert(results, data) + end, + } + + job:sync() + + assert.are.same(job:result(), { "test1", "test2" }) + assert.are.same(job:result(), results) + end) + end) + + describe("env", function() + it("should be possible to set one env variable with an array", function() + local results = {} + local job = Job:new { + command = "env", + env = { "A=100" }, + on_stdout = function(_, data) + table.insert(results, data) + end, + } + + job:sync() + + assert.are.same(job:result(), { "A=100" }) + assert.are.same(job:result(), results) + end) + + it("should be possible to set multiple env variables with an array", function() + local results = {} + local job = Job:new { + command = "env", + env = { "A=100", "B=test" }, + on_stdout = function(_, data) + table.insert(results, data) + end, + } + + job:sync() + + assert.are.same(job:result(), { "A=100", "B=test" }) + assert.are.same(job:result(), results) + end) + + it("should be possible to set one env variable with a map", function() + local results = {} + local job = Job:new { + command = "env", + env = { "A=100" }, + on_stdout = function(_, data) + table.insert(results, data) + end, + } + + job:sync() + + assert.are.same(job:result(), { "A=100" }) + assert.are.same(job:result(), results) + end) + + it("should be possible to set one env variable with spaces", function() + local results = {} + local job = Job:new { + command = "env", + env = { "A=This is a long env var" }, + on_stdout = function(_, data) + table.insert(results, data) + end, + } + + job:sync() + + assert.are.same(job:result(), { "A=This is a long env var" }) + assert.are.same(job:result(), results) + end) + + it("should be possible to set one env variable with spaces and a map", function() + local results = {} + local job = Job:new { + command = "env", + env = { ["A"] = "This is a long env var" }, + on_stdout = function(_, data) + table.insert(results, data) + end, + } + + job:sync() + + assert.are.same(job:result(), { "A=This is a long env var" }) + assert.are.same(job:result(), results) + end) + + it("should be possible to set multiple env variables with a map", function() + local results = {} + local job = Job:new { + command = "env", + env = { ["A"] = 100, ["B"] = "test" }, + on_stdout = function(_, data) + table.insert(results, data) + end, + } + + job:sync() + + local expected = { "A=100", "B=test" } + local found = { false, false } + for k, v in ipairs(job:result()) do + for _, w in ipairs(expected) do + if v == w then + found[k] = true + end + end + end + + assert.are.same({ true, true }, found) + assert.are.same(job:result(), results) + end) + + it("should be possible to set multiple env variables with both, array and map", function() + local results = {} + local job = Job:new { + command = "env", + env = { ["A"] = 100, "B=test" }, + on_stdout = function(_, data) + table.insert(results, data) + end, + } + + job:sync() + + local expected = { "A=100", "B=test" } + local found = { false, false } + for k, v in ipairs(job:result()) do + for _, w in ipairs(expected) do + if v == w then + found[k] = true + end + end + end + + assert.are.same({ true, true }, found) + assert.are.same(job:result(), results) + end) + end) + + describe("> simple ls >", function() + it("should match systemlist", function() + local ls_results = vim.fn.systemlist "ls -l" + + local job = Job:new { + command = "ls", + args = { "-l" }, + } + + job:sync() + assert.are.same(job:result(), ls_results) + end) + + it("should match larger systemlist", function() + local results = vim.fn.systemlist "find ." + local stdout_results = {} + + local job = Job:new { + command = "find", + args = { "." }, + + on_stdout = function(_, line) + table.insert(stdout_results, line) + end, + } + + job:sync() + assert.are.same(job:result(), results) + assert.are.same(job:result(), stdout_results) + end) + + it("should not timeout when completing fast jobs", function() + local start = vim.loop.hrtime() + + local job = Job:new { command = "ls" } + + job:sync() + + assert((vim.loop.hrtime() - start) / 1e9 < 1, "Should not take one second to complete") + end) + + it("should return the return code as well", function() + local job = Job:new { command = "false" } + local _, ret = job:sync() + + assert.are.same(1, job.code) + assert.are.same(1, ret) + end) + end) + + describe("chain", function() + it("should always run the next job when using and_then", function() + local results = {} + + local first_job = Job:new { + command = "env", + env = { ["a"] = "1" }, + on_stdout = function(_, line) + table.insert(results, line) + end, + } + + local second_job = Job:new { + command = "env", + env = { ["b"] = "2" }, + on_stdout = function(_, line) + table.insert(results, line) + end, + } + + local third_job = Job:new { command = "false" } + + local fourth_job = Job:new { + command = "env", + env = { ["c"] = "3" }, + on_stdout = function(_, line) + table.insert(results, line) + end, + } + + first_job:and_then(second_job) + second_job:and_then(third_job) + third_job:and_then(fourth_job) + + first_job:sync() + second_job:wait() + third_job:wait() + fourth_job:wait() + + assert.are.same({ "a=1", "b=2", "c=3" }, results) + assert.are.same({ "a=1" }, first_job:result()) + assert.are.same({ "b=2" }, second_job:result()) + assert.are.same(1, third_job.code) + assert.are.same({ "c=3" }, fourth_job:result()) + end) + + it("should only run the next job on success when using and_then_on_success", function() + local results = {} + + local first_job = Job:new { + command = "env", + env = { ["a"] = "1" }, + on_stdout = function(_, line) + table.insert(results, line) + end, + } + + local second_job = Job:new { + command = "env", + env = { ["b"] = "2" }, + on_stdout = function(_, line) + table.insert(results, line) + end, + } + + local third_job = Job:new { command = "false" } + + local fourth_job = Job:new { + command = "env", + env = { ["c"] = "3" }, + on_stdout = function(_, line) + table.insert(results, line) + end, + } + + first_job:and_then_on_success(second_job) + second_job:and_then_on_success(third_job) + third_job:and_then_on_success(fourth_job) + + first_job:sync() + second_job:wait() + third_job:wait() + + assert.are.same({ "a=1", "b=2" }, results) + assert.are.same({ "a=1" }, first_job:result()) + assert.are.same({ "b=2" }, second_job:result()) + assert.are.same(1, third_job.code) + assert.are.same(nil, fourth_job.handle, "Job never started") + end) + + it("should only run the next job on failure when using and_then_on_failure", function() + local results = {} + + local first_job = Job:new { + command = "false", + } + + local second_job = Job:new { + command = "env", + env = { ["a"] = "1" }, + on_stdout = function(_, line) + table.insert(results, line) + end, + } + + local third_job = Job:new { + command = "env", + env = { ["b"] = "2" }, + on_stdout = function(_, line) + table.insert(results, line) + end, + } + + first_job:and_then_on_failure(second_job) + second_job:and_then_on_failure(third_job) + + local _, ret = first_job:sync() + second_job:wait() + + assert.are.same(1, first_job.code) + assert.are.same(1, ret) + assert.are.same({ "a=1" }, results) + assert.are.same({ "a=1" }, second_job:result()) + assert.are.same(nil, third_job.handle, "Job never started") + end) + + it("should run all normal functions when using after", function() + local results = {} + local code = 0 + + local first_job = Job:new { + command = "env", + env = { ["a"] = "1" }, + on_stdout = function(_, line) + table.insert(results, line) + end, + } + + local second_job = Job:new { command = "false" } + + first_job + :after(function() + code = code + 10 + end) + :and_then(second_job) + second_job:after(function(_, c) + code = code + c + end) + + first_job:sync() + second_job:wait() + + assert.are.same({ "a=1" }, results) + assert.are.same({ "a=1" }, first_job:result()) + assert.are.same(1, second_job.code) + assert.are.same(11, code) + end) + + it("should run only on success normal functions when using after_success", function() + local results = {} + local code = 0 + + local first_job = Job:new { + command = "env", + env = { ["a"] = "1" }, + on_stdout = function(_, line) + table.insert(results, line) + end, + } + + local second_job = Job:new { command = "false" } + local third_job = Job:new { command = "true" } + + first_job:after_success(function() + code = code + 10 + end) + first_job:and_then_on_success(second_job) + second_job:after_success(function(_, c) + code = code + c + end) + second_job:and_then_on_success(third_job) + + first_job:sync() + second_job:wait() + + assert.are.same({ "a=1" }, results) + assert.are.same({ "a=1" }, first_job:result()) + assert.are.same(1, second_job.code) + assert.are.same(10, code) + assert.are.same(nil, third_job.handle) + end) + + it("should run only on failure normal functions when using after_failure", function() + local results = {} + local code = 0 + + local first_job = Job:new { command = "false" } + + local second_job = Job:new { + command = "env", + env = { ["a"] = "1" }, + on_stdout = function(_, line) + table.insert(results, line) + end, + } + + local third_job = Job:new { command = "true" } + + first_job:after_failure(function(_, c) + code = code + c + end) + first_job:and_then_on_failure(second_job) + second_job:after_failure(function() + code = code + 10 + end) + second_job:and_then_on_failure(third_job) + + local _, ret = first_job:sync() + second_job:wait() + + assert.are.same({ "a=1" }, results) + assert.are.same({ "a=1" }, second_job:result()) + assert.are.same(1, ret) + assert.are.same(1, first_job.code) + assert.are.same(1, code) + assert.are.same(0, second_job.code) + assert.are.same(nil, third_job.handle) + end) + end) + + describe(".writer", function() + pending("should allow using things like fzf", function() + if not has_all_executables { "fzf", "fdfind" } then + return + end + + local stdout_results = {} + + local fzf = Job:new { + writer = Job:new { + command = "fdfind", + cwd = vim.fn.expand "~/plugins/plenary.nvim/", + }, + + command = "fzf", + args = { "--filter", "job.lua" }, + + cwd = vim.fn.expand "~/plugins/plenary.nvim/", + + on_stdout = function(_, line) + table.insert(stdout_results, line) + end, + } + + local results = fzf:sync() + assert.are.same(results, stdout_results) + + -- 'job.lua' should be the best file from fzf. + -- So make sure we're processing correctly. + assert.are.same("lua/plenary/job.lua", results[1]) + end) + + it("should work with a table", function() + if not has_all_executables { "fzf" } then + return + end + + local stdout_results = {} + + local fzf = Job:new { + writer = { "hello", "world", "job.lua" }, + + command = "fzf", + args = { "--filter", "job.lua" }, + + on_stdout = function(_, line) + table.insert(stdout_results, line) + end, + } + + local results = fzf:sync() + assert.are.same(results, stdout_results) + + -- 'job.lua' should be the best file from fzf. + -- So make sure we're processing correctly. + assert.are.same("job.lua", results[1]) + assert.are.same(1, #results) + end) + + it("should work with a string", function() + if not has_all_executables { "fzf" } then + return + end + + local stdout_results = {} + + local fzf = Job:new { + writer = "hello\nworld\njob.lua", + + command = "fzf", + args = { "--filter", "job.lua" }, + + on_stdout = function(_, line) + table.insert(stdout_results, line) + end, + } + + local results = fzf:sync() + assert.are.same(results, stdout_results) + + -- 'job.lua' should be the best file from fzf. + -- So make sure we're processing correctly. + assert.are.same("job.lua", results[1]) + assert.are.same(1, #results) + end) + + it("should work with a pipe", function() + if not has_all_executables { "fzf" } then + return + end + + local input_pipe = vim.loop.new_pipe(false) + + local stdout_results = {} + local fzf = Job:new { + writer = input_pipe, + + command = "fzf", + args = { "--filter", "job.lua" }, + + on_stdout = function(_, line) + table.insert(stdout_results, line) + end, + } + + fzf:start() + + input_pipe:write "hello\n" + input_pipe:write "world\n" + input_pipe:write "job.lua\n" + input_pipe:close() + + wait_for_result(fzf, "job.lua") + fzf:shutdown() + + local results = fzf:result() + assert.are.same(results, stdout_results) + + -- 'job.lua' should be the best file from fzf. + -- So make sure we're processing correctly. + assert.are.same("job.lua", results[1]) + assert.are.same(1, #results) + end) + + it("should work with a pipe, but no final newline", function() + if not has_all_executables { "fzf" } then + return + end + + local input_pipe = vim.loop.new_pipe(false) + + local stdout_results = {} + local fzf = Job:new { + writer = input_pipe, + + command = "fzf", + args = { "--filter", "job.lua" }, + + on_stdout = function(_, line) + table.insert(stdout_results, line) + end, + } + + fzf:start() + + input_pipe:write "hello\n" + input_pipe:write "world\n" + input_pipe:write "job.lua" + input_pipe:close() + + wait_for_result(fzf, "job.lua") + fzf:shutdown() + + local results = fzf:result() + assert.are.same(results, stdout_results) + + -- 'job.lua' should be the best file from fzf. + -- So make sure we're processing correctly. + assert.are.same("job.lua", results[1]) + assert.are.same(1, #results) + end) + end) + + describe(":wait()", function() + it("should respect timeout", function() + local j = Job:new { + command = "sleep", + args = { "10" }, + } + + local ok = pcall(j.sync, j, 500) + assert(not ok, "Job should fail") + end) + end) + + describe("enable_.*", function() + it("should not add things to results when disabled", function() + local job = Job:new { + command = "ls", + args = { "-l" }, + + enable_recording = false, + } + + local res = job:sync() + assert(res == nil, "No results should exist") + assert(job._stdout_results == nil, "No result table") + end) + + it("should not call callbacks when disabled", function() + local was_called = false + local job = Job:new { + command = "ls", + args = { "-l" }, + + enable_handlers = false, + + on_stdout = function() + was_called = true + end, + } + + job:sync() + assert(not was_called, "Should not be called.") + assert(job._stdout_results == nil, "No result table") + end) + end) + + describe("enable_.*", function() + it("should not add things to results when disabled", function() + local job = Job:new { + command = "ls", + args = { "-l" }, + + enable_recording = false, + } + + local res = job:sync() + assert(res == nil, "No results should exist") + assert(job._stdout_results == nil, "No result table") + end) + + it("should not call callbacks when disbaled", function() + local was_called = false + local job = Job:new { + command = "ls", + args = { "-l" }, + + enable_handlers = false, + + on_stdout = function() + was_called = true + end, + } + + job:sync() + assert(not was_called, "Should not be called.") + assert(job._stdout_results == nil, "No result table") + end) + end) + + describe("validation", function() + it("requires options", function() + local ok = pcall(Job.new, { command = "ls" }) + assert(not ok, "Requires options") + end) + + it("requires command", function() + local ok = pcall(Job.new, Job, { cmd = "ls" }) + assert(not ok, "Requires command") + end) + + it("will not spawn jobs with invalid commands", function() + local ok = pcall(Job.new, Job, { command = "dasowlwl" }) + assert(not ok, "Should not allow invalid executables") + end) + end) + + describe("on_exit", function() + it("should only be called once for wait", function() + local count = 0 + + local job = Job:new { + command = "ls", + on_exit = function(...) + count = count + 1 + end, + } + job:start() + job:wait() + + assert.are.same(count, 1) + end) + + it("should only be called once for shutdown", function() + local count = 0 + + local job = Job:new { + command = "ls", + on_exit = function(...) + count = count + 1 + end, + } + job:start() + job:shutdown() + + assert.are.same(count, 1) + end) + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/json_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/json_spec.lua new file mode 100644 index 0000000..88c00c2 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/json_spec.lua @@ -0,0 +1,87 @@ +local json = require "plenary.json" +local eq = assert.are.same + +describe("json", function() + it("replace comments with whitespace", function() + eq(json.json_strip_comments '//comment\n{"a":"b"}', ' \n{"a":"b"}') + eq(json.json_strip_comments '/*//comment*/{"a":"b"}', ' {"a":"b"}') + eq(json.json_strip_comments '{"a":"b"//comment\n}', '{"a":"b" \n}') + eq(json.json_strip_comments '{"a":"b"/*comment*/}', '{"a":"b" }') + eq(json.json_strip_comments '{"a"/*\n\n\ncomment\r\n*/:"b"}', '{"a" \n\n\n \r\n :"b"}') + eq(json.json_strip_comments '/*!\n * comment\n */\n{"a":"b"}', ' \n \n \n{"a":"b"}') + eq(json.json_strip_comments '{/*comment*/"a":"b"}', '{ "a":"b"}') + end) + + it("remove comments", function() + local options = { whitespace = false } + eq(json.json_strip_comments('//comment\n{"a":"b"}', options), '\n{"a":"b"}') + eq(json.json_strip_comments('/*//comment*/{"a":"b"}', options), '{"a":"b"}') + eq(json.json_strip_comments('{"a":"b"//comment\n}', options), '{"a":"b"\n}') + eq(json.json_strip_comments('{"a":"b"/*comment*/}', options), '{"a":"b"}') + eq(json.json_strip_comments('{"a"/*\n\n\ncomment\r\n*/:"b"}', options), '{"a":"b"}') + eq(json.json_strip_comments('/*!\n * comment\n */\n{"a":"b"}', options), '\n{"a":"b"}') + eq(json.json_strip_comments('{/*comment*/"a":"b"}', options), '{"a":"b"}') + end) + + it("doesn't strip comments inside strings", function() + eq(json.json_strip_comments '{"a":"b//c"}', '{"a":"b//c"}') + eq(json.json_strip_comments '{"a":"b/*c*/"}', '{"a":"b/*c*/"}') + eq(json.json_strip_comments '{"/*a":"b"}', '{"/*a":"b"}') + eq(json.json_strip_comments '{"\\"/*a":"b"}', '{"\\"/*a":"b"}') + end) + + it("consider escaped slashes when checking for escaped string quote", function() + eq(json.json_strip_comments '{"\\\\":"https://foobar.com"}', '{"\\\\":"https://foobar.com"}') + eq(json.json_strip_comments '{"foo\\"":"https://foobar.com"}', '{"foo\\"":"https://foobar.com"}') + end) + + it("line endings - no comments", function() + eq(json.json_strip_comments '{"a":"b"\n}', '{"a":"b"\n}') + eq(json.json_strip_comments '{"a":"b"\r\n}', '{"a":"b"\r\n}') + end) + + it("line endings - single line comment", function() + eq(json.json_strip_comments '{"a":"b"//c\n}', '{"a":"b" \n}') + eq(json.json_strip_comments '{"a":"b"//c\r\n}', '{"a":"b" \r\n}') + end) + + it("line endings - single line block comment", function() + eq(json.json_strip_comments '{"a":"b"/*c*/\n}', '{"a":"b" \n}') + eq(json.json_strip_comments '{"a":"b"/*c*/\r\n}', '{"a":"b" \r\n}') + end) + + it("line endings - multi line block comment", function() + eq(json.json_strip_comments '{"a":"b",/*c\nc2*/"x":"y"\n}', '{"a":"b", \n "x":"y"\n}') + eq(json.json_strip_comments '{"a":"b",/*c\r\nc2*/"x":"y"\r\n}', '{"a":"b", \r\n "x":"y"\r\n}') + end) + + it("line endings - works at EOF", function() + local options = { whitespace = false } + eq(json.json_strip_comments '{\r\n\t"a":"b"\r\n} //EOF', '{\r\n\t"a":"b"\r\n} ') + eq(json.json_strip_comments('{\r\n\t"a":"b"\r\n} //EOF', options), '{\r\n\t"a":"b"\r\n} ') + end) + + it("handles weird escaping", function() + eq( + json.json_strip_comments [[{"x":"x \"sed -e \\\"s/^.\\\\{46\\\\}T//\\\" -e \\\"s/#033/\\\\x1b/g\\\"\""}]], + [[{"x":"x \"sed -e \\\"s/^.\\\\{46\\\\}T//\\\" -e \\\"s/#033/\\\\x1b/g\\\"\""}]] + ) + end) + + it("trailing commas", function() + eq(json.json_strip_comments '{"a":"b",}', '{"a":"b"}') + eq(json.json_strip_comments '{"a":{"b":"c",},}', '{"a":{"b":"c"}}') + eq(json.json_strip_comments '{"a":["b","c",],}', '{"a":["b","c"]}') + end) + + it("trailing commas - ignored in strings and comments", function() + eq(json.json_strip_comments '{"a":"b,}"}', '{"a":"b,}"}') + end) + + it("trailing commas - left when disabled in options", function() + local options = { trailing_commas = true } + eq(json.json_strip_comments('{"a":"b",}', options), '{"a":"b",}') + eq(json.json_strip_comments('{"a":{"b":"c",},}', options), '{"a":{"b":"c",},}') + eq(json.json_strip_comments('{"a":["b","c",],}', options), '{"a":["b","c",],}') + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/path_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/path_spec.lua new file mode 100644 index 0000000..0f02db0 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/path_spec.lua @@ -0,0 +1,762 @@ +local Path = require "plenary.path" +local path = Path.path +local compat = require "plenary.compat" + +describe("Path", function() + it("should find valid files", function() + local p = Path:new "README.md" + assert(p.filename == "README.md", p.filename) + assert.are.same(p.filename, "README.md") + end) + + describe("absolute", function() + it(".absolute()", function() + local p = Path:new { "README.md", sep = "/" } + assert.are.same(p:absolute(), vim.fn.fnamemodify("README.md", ":p")) + end) + + it("can determine absolute paths", function() + local p = Path:new { "/home/asdfasdf/", sep = "/" } + assert(p:is_absolute(), "Is absolute") + assert(p:absolute() == p.filename) + end) + + it("can determine non absolute paths", function() + local p = Path:new { "./home/tj/", sep = "/" } + assert(not p:is_absolute(), "Is absolute") + end) + + it("will normalize the path", function() + local p = Path:new { "lua", "..", "README.md", sep = "/" } + assert.are.same(p:absolute(), vim.fn.fnamemodify("README.md", ":p")) + end) + end) + + it("can join paths by constructor or join path", function() + assert.are.same(Path:new("lua", "plenary"), Path:new("lua"):joinpath "plenary") + end) + + it("can join paths with /", function() + assert.are.same(Path:new("lua", "plenary"), Path:new "lua" / "plenary") + end) + + it("can join paths with paths", function() + assert.are.same(Path:new("lua", "plenary"), Path:new("lua", Path:new "plenary")) + end) + + it("inserts slashes", function() + assert.are.same("lua" .. path.sep .. "plenary", Path:new("lua", "plenary").filename) + end) + + describe(".exists()", function() + it("finds files that exist", function() + assert.are.same(true, Path:new("README.md"):exists()) + end) + + it("returns false for files that do not exist", function() + assert.are.same(false, Path:new("asdf.md"):exists()) + end) + end) + + describe(".is_dir()", function() + it("should find directories that exist", function() + assert.are.same(true, Path:new("lua"):is_dir()) + end) + + it("should return false when the directory does not exist", function() + assert.are.same(false, Path:new("asdf"):is_dir()) + end) + + it("should not show files as directories", function() + assert.are.same(false, Path:new("README.md"):is_dir()) + end) + end) + + describe(".is_file()", function() + it("should not allow directories", function() + assert.are.same(true, not Path:new("lua"):is_file()) + end) + + it("should return false when the file does not exist", function() + assert.are.same(true, not Path:new("asdf"):is_file()) + end) + + it("should show files as file", function() + assert.are.same(true, Path:new("README.md"):is_file()) + end) + end) + + describe(":new", function() + it("can be called with or without colon", function() + -- This will work, cause we used a colon + local with_colon = Path:new "lua" + local no_colon = Path.new "lua" + + assert.are.same(with_colon, no_colon) + end) + end) + + describe(":make_relative", function() + it("can take absolute paths and make them relative to the cwd", function() + local p = Path:new { "lua", "plenary", "path.lua" } + local absolute = vim.loop.cwd() .. path.sep .. p.filename + local relative = Path:new(absolute):make_relative() + assert.are.same(relative, p.filename) + end) + + it("can take absolute paths and make them relative to a given path", function() + local root = path.sep == "\\" and "c:\\" or "/" + local r = Path:new { root, "home", "prime" } + local p = Path:new { "aoeu", "agen.lua" } + local absolute = r.filename .. path.sep .. p.filename + local relative = Path:new(absolute):make_relative(r.filename) + assert.are.same(relative, p.filename) + end) + + it("can take double separator absolute paths and make them relative to the cwd", function() + local p = Path:new { "lua", "plenary", "path.lua" } + local absolute = vim.loop.cwd() .. path.sep .. path.sep .. p.filename + local relative = Path:new(absolute):make_relative() + assert.are.same(relative, p.filename) + end) + + it("can take double separator absolute paths and make them relative to a given path", function() + local root = path.sep == "\\" and "c:\\" or "/" + local r = Path:new { root, "home", "prime" } + local p = Path:new { "aoeu", "agen.lua" } + local absolute = r.filename .. path.sep .. path.sep .. p.filename + local relative = Path:new(absolute):make_relative(r.filename) + assert.are.same(relative, p.filename) + end) + + it("can take absolute paths and make them relative to a given path with trailing separator", function() + local root = path.sep == "\\" and "c:\\" or "/" + local r = Path:new { root, "home", "prime" } + local p = Path:new { "aoeu", "agen.lua" } + local absolute = r.filename .. path.sep .. p.filename + local relative = Path:new(absolute):make_relative(r.filename .. path.sep) + assert.are.same(relative, p.filename) + end) + + it("can take absolute paths and make them relative to the root directory", function() + local root = path.sep == "\\" and "c:\\" or "/" + local p = Path:new { "home", "prime", "aoeu", "agen.lua" } + local absolute = root .. p.filename + local relative = Path:new(absolute):make_relative(root) + assert.are.same(relative, p.filename) + end) + + it("can take absolute paths and make them relative to themselves", function() + local root = path.sep == "\\" and "c:\\" or "/" + local p = Path:new { root, "home", "prime", "aoeu", "agen.lua" } + local relative = Path:new(p.filename):make_relative(p.filename) + assert.are.same(relative, ".") + end) + + it("should not truncate if path separator is not present after cwd", function() + local cwd = "tmp" .. path.sep .. "foo" + local p = Path:new { "tmp", "foo_bar", "fileb.lua" } + local relative = Path:new(p.filename):make_relative(cwd) + assert.are.same(p.filename, relative) + end) + + it("should not truncate if path separator is not present after cwd and cwd ends in path sep", function() + local cwd = "tmp" .. path.sep .. "foo" .. path.sep + local p = Path:new { "tmp", "foo_bar", "fileb.lua" } + local relative = Path:new(p.filename):make_relative(cwd) + assert.are.same(p.filename, relative) + end) + end) + + describe(":normalize", function() + it("can take path that has one character directories", function() + local orig = "/home/j/./p//path.lua" + local final = Path:new(orig):normalize() + assert.are.same(final, "/home/j/p/path.lua") + end) + + it("can take paths with double separators change them to single separators", function() + local orig = "/lua//plenary/path.lua" + local final = Path:new(orig):normalize() + assert.are.same(final, "/lua/plenary/path.lua") + end) + -- this may be redundant since normalize just calls make_relative which is tested above + it("can take absolute paths with double seps" .. "and make them relative with single seps", function() + local orig = "/lua//plenary/path.lua" + local final = Path:new(orig):normalize() + assert.are.same(final, "/lua/plenary/path.lua") + end) + + it("can remove the .. in paths", function() + local orig = "/lua//plenary/path.lua/foo/bar/../.." + local final = Path:new(orig):normalize() + assert.are.same(final, "/lua/plenary/path.lua") + end) + + it("can normalize relative paths", function() + assert.are.same(Path:new("lua/plenary/path.lua"):normalize(), "lua/plenary/path.lua") + end) + + it("can normalize relative paths containing ..", function() + assert.are.same(Path:new("lua/plenary/path.lua/../path.lua"):normalize(), "lua/plenary/path.lua") + end) + + it("can normalize relative paths with initial ..", function() + local p = Path:new "../lua/plenary/path.lua" + p._cwd = "/tmp/lua" + assert.are.same("lua/plenary/path.lua", p:normalize()) + end) + + it("can normalize relative paths to absolute when initial .. count matches cwd parts", function() + local p = Path:new "../../tmp/lua/plenary/path.lua" + p._cwd = "/tmp/lua" + assert.are.same("/tmp/lua/plenary/path.lua", p:normalize()) + end) + + it("can normalize ~ when file is within home directory (trailing slash)", function() + local home = "/home/test/" + local p = Path:new { home, "./test_file" } + p.path.home = home + p._cwd = "/tmp/lua" + assert.are.same("~/test_file", p:normalize()) + end) + + it("can normalize ~ when file is within home directory (no trailing slash)", function() + local home = "/home/test" + local p = Path:new { home, "./test_file" } + p.path.home = home + p._cwd = "/tmp/lua" + assert.are.same("~/test_file", p:normalize()) + end) + + it("handles usernames with a dash at the end", function() + local home = "/home/mattr-" + local p = Path:new { home, "test_file" } + p.path.home = home + p._cwd = "/tmp/lua" + assert.are.same("~/test_file", p:normalize()) + end) + + it("handles filenames with the same prefix as the home directory", function() + local p = Path:new "/home/test.old/test_file" + p.path.home = "/home/test" + assert.are.same("/home/test.old/test_file", p:normalize()) + end) + end) + + describe(":shorten", function() + it("can shorten a path", function() + local long_path = "/this/is/a/long/path" + local short_path = Path:new(long_path):shorten() + assert.are.same(short_path, "/t/i/a/l/path") + end) + + it("can shorten a path's components to a given length", function() + local long_path = "/this/is/a/long/path" + local short_path = Path:new(long_path):shorten(2) + assert.are.same(short_path, "/th/is/a/lo/path") + + -- without the leading / + long_path = "this/is/a/long/path" + short_path = Path:new(long_path):shorten(3) + assert.are.same(short_path, "thi/is/a/lon/path") + + -- where len is greater than the length of the final component + long_path = "this/is/an/extremely/long/path" + short_path = Path:new(long_path):shorten(5) + assert.are.same(short_path, "this/is/an/extre/long/path") + end) + + it("can shorten a path's components when excluding parts", function() + local long_path = "/this/is/a/long/path" + local short_path = Path:new(long_path):shorten(nil, { 1, -1 }) + assert.are.same(short_path, "/this/i/a/l/path") + + -- without the leading / + long_path = "this/is/a/long/path" + short_path = Path:new(long_path):shorten(nil, { 1, -1 }) + assert.are.same(short_path, "this/i/a/l/path") + + -- where excluding positions greater than the number of parts + long_path = "this/is/an/extremely/long/path" + short_path = Path:new(long_path):shorten(nil, { 2, 4, 6, 8 }) + assert.are.same(short_path, "t/is/a/extremely/l/path") + + -- where excluding positions less than the negation of the number of parts + long_path = "this/is/an/extremely/long/path" + short_path = Path:new(long_path):shorten(nil, { -2, -4, -6, -8 }) + assert.are.same(short_path, "this/i/an/e/long/p") + end) + + it("can shorten a path's components to a given length and exclude positions", function() + local long_path = "/this/is/a/long/path" + local short_path = Path:new(long_path):shorten(2, { 1, -1 }) + assert.are.same(short_path, "/this/is/a/lo/path") + + long_path = "this/is/a/long/path" + short_path = Path:new(long_path):shorten(3, { 2, -2 }) + assert.are.same(short_path, "thi/is/a/long/pat") + + long_path = "this/is/an/extremely/long/path" + short_path = Path:new(long_path):shorten(5, { 3, -3 }) + assert.are.same(short_path, "this/is/an/extremely/long/path") + end) + end) + + describe("mkdir / rmdir", function() + it("can create and delete directories", function() + local p = Path:new "_dir_not_exist" + + p:rmdir() + assert(not p:exists(), "After rmdir, it should not exist") + + p:mkdir() + assert(p:exists()) + + p:rmdir() + assert(not p:exists()) + end) + + it("fails when exists_ok is false", function() + local p = Path:new "lua" + assert(not pcall(p.mkdir, p, { exists_ok = false })) + end) + + it("fails when parents is not passed", function() + local p = Path:new("impossible", "dir") + assert(not pcall(p.mkdir, p, { parents = false })) + assert(not p:exists()) + end) + + it("can create nested directories", function() + local p = Path:new("impossible", "dir") + assert(pcall(p.mkdir, p, { parents = true })) + assert(p:exists()) + + p:rmdir() + Path:new("impossible"):rmdir() + assert(not p:exists()) + assert(not Path:new("impossible"):exists()) + end) + end) + + describe("touch", function() + it("can create and delete new files", function() + local p = Path:new "test_file.lua" + assert(pcall(p.touch, p)) + assert(p:exists()) + + p:rm() + assert(not p:exists()) + end) + + it("does not effect already created files but updates last access", function() + local p = Path:new "README.md" + local last_atime = p:_stat().atime.sec + local last_mtime = p:_stat().mtime.sec + + local lines = p:readlines() + + assert(pcall(p.touch, p)) + print(p:_stat().atime.sec > last_atime) + print(p:_stat().mtime.sec > last_mtime) + assert(p:exists()) + + assert.are.same(lines, p:readlines()) + end) + + it("does not create dirs if nested in none existing dirs and parents not set", function() + local p = Path:new { "nested", "nested2", "test_file.lua" } + assert(not pcall(p.touch, p, { parents = false })) + assert(not p:exists()) + end) + + it("does create dirs if nested in none existing dirs", function() + local p1 = Path:new { "nested", "nested2", "test_file.lua" } + local p2 = Path:new { "nested", "asdf", ".hidden" } + local d1 = Path:new { "nested", "dir", ".hidden" } + assert(pcall(p1.touch, p1, { parents = true })) + assert(pcall(p2.touch, p2, { parents = true })) + assert(pcall(d1.mkdir, d1, { parents = true })) + assert(p1:exists()) + assert(p2:exists()) + assert(d1:exists()) + + Path:new({ "nested" }):rm { recursive = true } + assert(not p1:exists()) + assert(not p2:exists()) + assert(not d1:exists()) + assert(not Path:new({ "nested" }):exists()) + end) + end) + + describe("rename", function() + it("can rename a file", function() + local p = Path:new "a_random_filename.lua" + assert(pcall(p.touch, p)) + assert(p:exists()) + + assert(pcall(p.rename, p, { new_name = "not_a_random_filename.lua" })) + assert.are.same("not_a_random_filename.lua", p.filename) + + p:rm() + end) + + it("can handle an invalid filename", function() + local p = Path:new "some_random_filename.lua" + assert(pcall(p.touch, p)) + assert(p:exists()) + + assert(not pcall(p.rename, p, { new_name = "" })) + assert(not pcall(p.rename, p)) + assert.are.same("some_random_filename.lua", p.filename) + + p:rm() + end) + + it("can move to parent dir", function() + local p = Path:new "some_random_filename.lua" + assert(pcall(p.touch, p)) + assert(p:exists()) + + assert(pcall(p.rename, p, { new_name = "../some_random_filename.lua" })) + assert.are.same(vim.loop.fs_realpath(Path:new("../some_random_filename.lua"):absolute()), p:absolute()) + + p:rm() + end) + + it("cannot rename to an existing filename", function() + local p1 = Path:new "a_random_filename.lua" + local p2 = Path:new "not_a_random_filename.lua" + assert(pcall(p1.touch, p1)) + assert(pcall(p2.touch, p2)) + assert(p1:exists()) + assert(p2:exists()) + + assert(not pcall(p1.rename, p1, { new_name = "not_a_random_filename.lua" })) + assert.are.same(p1.filename, "a_random_filename.lua") + + p1:rm() + p2:rm() + end) + end) + + describe("copy", function() + it("can copy a file", function() + local p1 = Path:new "a_random_filename.rs" + local p2 = Path:new "not_a_random_filename.rs" + assert(pcall(p1.touch, p1)) + assert(p1:exists()) + + assert(pcall(p1.copy, p1, { destination = "not_a_random_filename.rs" })) + assert.are.same(p1.filename, "a_random_filename.rs") + assert.are.same(p2.filename, "not_a_random_filename.rs") + + p1:rm() + p2:rm() + end) + + it("can copy to parent dir", function() + local p = Path:new "some_random_filename.lua" + assert(pcall(p.touch, p)) + assert(p:exists()) + + assert(pcall(p.copy, p, { destination = "../some_random_filename.lua" })) + assert(pcall(p.exists, p)) + + p:rm() + Path:new(vim.loop.fs_realpath "../some_random_filename.lua"):rm() + end) + + it("cannot copy an existing file if override false", function() + local p1 = Path:new "a_random_filename.rs" + local p2 = Path:new "not_a_random_filename.rs" + assert(pcall(p1.touch, p1)) + assert(pcall(p2.touch, p2)) + assert(p1:exists()) + assert(p2:exists()) + + assert(pcall(p1.copy, p1, { destination = "not_a_random_filename.rs", override = false })) + assert.are.same(p1.filename, "a_random_filename.rs") + assert.are.same(p2.filename, "not_a_random_filename.rs") + + p1:rm() + p2:rm() + end) + + it("fails when copying folders non-recursively", function() + local src_dir = Path:new "src" + src_dir:mkdir() + src_dir:joinpath("file1.lua"):touch() + + local trg_dir = Path:new "trg" + local status = xpcall(function() + src_dir:copy { destination = trg_dir, recursive = false } + end, function() end) + -- failed as intended + assert(status == false) + + src_dir:rm { recursive = true } + end) + + it("can copy directories recursively", function() + -- vim.tbl_flatten doesn't work here as copy doesn't return a list + local flatten + flatten = function(ret, t) + for _, v in pairs(t) do + if type(v) == "table" then + flatten(ret, v) + else + table.insert(ret, v) + end + end + end + + -- setup directories + local src_dir = Path:new "src" + local trg_dir = Path:new "trg" + src_dir:mkdir() + + -- set up sub directory paths for creation and testing + local sub_dirs = { "sub_dir1", "sub_dir1/sub_dir2" } + local src_dirs = { src_dir } + local trg_dirs = { trg_dir } + -- {src, trg}_dirs is a table with all directory levels by {src, trg} + for _, dir in ipairs(sub_dirs) do + table.insert(src_dirs, src_dir:joinpath(dir)) + table.insert(trg_dirs, trg_dir:joinpath(dir)) + end + + -- generate {file}_{level}.lua on every directory level in src + -- src + -- ├── file1_1.lua + -- ├── file2_1.lua + -- ├── .file3_1.lua + -- └── sub_dir1 + -- ├── file1_2.lua + -- ├── file2_2.lua + -- ├── .file3_2.lua + -- └── sub_dir2 + -- ├── file1_3.lua + -- ├── file2_3.lua + -- └── .file3_3.lua + local files = { "file1", "file2", ".file3" } + for _, file in ipairs(files) do + for level, dir in ipairs(src_dirs) do + local p = dir:joinpath(file .. "_" .. level .. ".lua") + assert(pcall(p.touch, p, { parents = true, exists_ok = true })) + assert(p:exists()) + end + end + + for _, hidden in ipairs { true, false } do + -- override = `false` should NOT copy as it was copied beforehand + for _, override in ipairs { true, false } do + local success = src_dir:copy { destination = trg_dir, recursive = true, override = override, hidden = hidden } + -- the files are already created because we iterate first with `override=true` + -- hence, we test here that no file ops have been committed: any value in tbl of tbls should be false + if not override then + local file_ops = {} + flatten(file_ops, success) + -- 3 layers with at at least 2 and at most 3 files (`hidden = true`) + local num_files = not hidden and 6 or 9 + assert(#file_ops == num_files) + for _, op in ipairs(file_ops) do + assert(op == false) + end + else + for _, file in ipairs(files) do + for level, dir in ipairs(trg_dirs) do + local p = dir:joinpath(file .. "_" .. level .. ".lua") + -- file 3 is hidden + if not (file == files[3]) then + assert(p:exists()) + else + assert(p:exists() == hidden) + end + end + end + end + -- only clean up once we tested that we dont want to copy + -- if `override=true` + if not override then + trg_dir:rm { recursive = true } + end + end + end + + src_dir:rm { recursive = true } + end) + end) + + describe("parents", function() + it("should extract the ancestors of the path", function() + local p = Path:new(vim.loop.cwd()) + local parents = p:parents() + assert(compat.islist(parents)) + for _, parent in pairs(parents) do + assert.are.same(type(parent), "string") + end + end) + it("should return itself if it corresponds to path.root", function() + local p = Path:new(Path.path.root(vim.loop.cwd())) + assert.are.same(p:parent(), p) + end) + end) + + describe("read parts", function() + it("should read head of file", function() + local p = Path:new "LICENSE" + local data = p:head() + local should = [[MIT License + +Copyright (c) 2020 TJ DeVries + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions:]] + assert.are.same(should, data) + end) + + it("should read the first line of file", function() + local p = Path:new "LICENSE" + local data = p:head(1) + local should = [[MIT License]] + assert.are.same(should, data) + end) + + it("head should max read whole file", function() + local p = Path:new "LICENSE" + local data = p:head(1000) + local should = [[MIT License + +Copyright (c) 2020 TJ DeVries + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.]] + assert.are.same(should, data) + end) + + it("should read tail of file", function() + local p = Path:new "LICENSE" + local data = p:tail() + local should = [[The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.]] + assert.are.same(should, data) + end) + + it("should read the last line of file", function() + local p = Path:new "LICENSE" + local data = p:tail(1) + local should = [[SOFTWARE.]] + assert.are.same(should, data) + end) + + it("tail should max read whole file", function() + local p = Path:new "LICENSE" + local data = p:tail(1000) + local should = [[MIT License + +Copyright (c) 2020 TJ DeVries + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.]] + assert.are.same(should, data) + end) + end) + + describe("readbyterange", function() + it("should read bytes at given offset", function() + local p = Path:new "LICENSE" + local data = p:readbyterange(13, 10) + local should = "Copyright " + assert.are.same(should, data) + end) + + it("supports negative offset", function() + local p = Path:new "LICENSE" + local data = p:readbyterange(-10, 10) + local should = "SOFTWARE.\n" + assert.are.same(should, data) + end) + end) + + describe(":find_upwards", function() + it("finds files that exist", function() + local p = Path:new(debug.getinfo(1, "S").source:sub(2)) + local found = p:find_upwards "README.md" + assert.are.same(found:absolute(), Path:new("README.md"):absolute()) + end) + + it("finds files that exist at the root", function() + local p = Path:new(debug.getinfo(1, "S").source:sub(2)) + + -- Temporarily set path.root to the root of this repository + local root = p.path.root + p.path.root = function(_) + return p:parent():parent():parent().filename + end + + local found = p:find_upwards "README.md" + assert.are.same(found:absolute(), Path:new("README.md"):absolute()) + p.path.root = root + end) + + it("returns nil if no file is found", function() + local p = Path:new(debug.getinfo(1, "S").source:sub(2)) + local found = p:find_upwards "MISSINGNO.md" + assert.are.same(found, nil) + end) + end) +end) + +-- function TestPath:testIsDir() +-- end + +-- function TestPath:testCanBeCalledWithoutColon() +-- end + +-- -- @sideeffect +-- function TestPath:testMkdir() +-- end diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/popup_requires_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/popup_requires_spec.lua new file mode 100644 index 0000000..2909f65 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/popup_requires_spec.lua @@ -0,0 +1,42 @@ +local scandir = require "plenary.scandir" +local Path = require "plenary.path" + +local eq = assert.are.same + +describe("plenary.popup", function() + local allowed_imports = { + ["plenary.window"] = true, + ["plenary.window.border"] = true, + } + + local matches_any_import = function(line) + local matched = string.match(line, [[require."(.*)"]]) + if matched and not vim.startswith(matched, "plenary.popup") then + if not allowed_imports[matched] then + return true, string.format("Not an allowed import for popup: %s. Line: %s", matched, line) + end + end + + return false, nil + end + + -- Tests to make sure that we're matching both types of requires + it("should match these kinds of patterns", function() + eq(true, matches_any_import [[local x = require "plenary.other"]]) + eq(true, matches_any_import [[local x = require("plenary.module").something]]) + end) + + it("must not require anything other than Window and Border from plenary", function() + local result = scandir.scan_dir("./lua/plenary/popup", { depth = 1 }) + + for _, file in ipairs(result) do + local popup_file = Path:new(file) + local lines = popup_file:readlines() + + for _, line in ipairs(lines) do + local matches, msg = matches_any_import(line) + eq(false, matches, msg) + end + end + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/popup_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/popup_spec.lua new file mode 100644 index 0000000..250ba0e --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/popup_spec.lua @@ -0,0 +1,162 @@ +local popup = require "plenary.popup" + +local eq = assert.are.same + +describe("plenary.popup", function() + before_each(function() + vim.cmd [[highlight PopupColor1 ctermbg=lightblue guibg=lightblue]] + vim.cmd [[highlight PopupColor2 ctermbg=lightcyan guibg=lightcyan]] + end) + + -- TODO: Probably want to clear all the popups between iterations + -- after_each(function() end) + + it("can create a very simple window", function() + local win_id = popup.create("hello there", { + line = 1, + col = 1, + width = 20, + }) + + local win_config = vim.api.nvim_win_get_config(win_id) + eq(20, win_config.width) + end) + + it("can create a very simple window after 'set nomodifiable'", function() + vim.o.modifiable = false + local win_id = popup.create("hello there", { + line = 1, + col = 1, + width = 20, + }) + + local win_config = vim.api.nvim_win_get_config(win_id) + eq(20, win_config.width) + vim.o.modifiable = true + end) + + it("can apply a highlight", function() + local win_id = popup.create("hello there", { + highlight = "PopupColor1", + }) + + eq("Normal:PopupColor1,EndOfBuffer:PopupColor1", vim.api.nvim_win_get_option(win_id, "winhl")) + end) + + it("can create a border", function() + local win_id, config = popup.create("hello border", { + line = 2, + col = 3, + border = {}, + }) + + eq(true, vim.api.nvim_win_is_valid(win_id)) + + local border_id = config.border.win_id + assert(border_id, "Has a border win id") + eq(true, vim.api.nvim_win_is_valid(border_id)) + end) + + it("can apply a border highlight", function() + local _, opts = popup.create("hello there", { + border = true, + borderhighlight = "PopupColor2", + }) + + local border_win_id = opts.border.win_id + eq("Normal:PopupColor2", vim.api.nvim_win_get_option(border_win_id, "winhl")) + end) + + it("can ignore border highlight with no border", function() + local _ = popup.create("hello there", { + border = false, + borderhighlight = "PopupColor3", + }) + end) + + it("can do basic padding", function() + local win_id = popup.create("12345", { + line = 1, + col = 1, + padding = {}, + }) + + local bufnr = vim.api.nvim_win_get_buf(win_id) + eq({ "", " 12345 ", "" }, vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)) + end) + + it("can do padding and border", function() + local win_id, config = popup.create("hello border", { + line = 2, + col = 2, + border = {}, + padding = {}, + }) + + local bufnr = vim.api.nvim_win_get_buf(win_id) + eq({ "", " hello border ", "" }, vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)) + + local border_id = config.border.win_id + assert(border_id, "Has a border win id") + eq(true, vim.api.nvim_win_is_valid(border_id)) + end) + + describe("borderchars", function() + local test_border = function(name, borderchars, expected) + it(name, function() + local _, config = popup.create("all the plus signs", { + line = 8, + col = 55, + padding = { 0, 3, 0, 3 }, + borderchars = borderchars, + }) + + local border_id = config.border.win_id + local border_bufnr = vim.api.nvim_win_get_buf(border_id) + eq(expected, vim.api.nvim_buf_get_lines(border_bufnr, 0, -1, false)) + end) + end + + test_border("can support multiple border patterns", { "+" }, { + "++++++++++++++++++++++++++", + "+ +", + "++++++++++++++++++++++++++", + }) + + test_border("can support multiple patterns inside the borderchars", { "-", "+" }, { + "+------------------------+", + "- -", + "+------------------------+", + }) + end) + + describe("what", function() + it("can be an existing bufnr", function() + local bufnr = vim.api.nvim_create_buf(false, false) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, { "pass bufnr 1", "pass bufnr 2" }) + local win_id = popup.create(bufnr, { + line = 8, + col = 55, + minwidth = 20, + }) + + eq(bufnr, vim.api.nvim_win_get_buf(win_id)) + eq({ "pass bufnr 1", "pass bufnr 2" }, vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)) + end) + end) + + describe("cursor", function() + pending("not yet tested", function() + popup.create({ "option 1", "option 2" }, { + line = "cursor+2", + col = "cursor+2", + border = { 1, 1, 1, 1 }, + enter = true, + cursorline = true, + callback = function(win_id, sel) + print(sel) + end, + }) + end) + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/py_list_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/py_list_spec.lua new file mode 100644 index 0000000..1ca48bf --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/py_list_spec.lua @@ -0,0 +1,60 @@ +local List = require "plenary.collections.py_list" +local Iter = require "plenary.iterators" + +describe("List", function() + it("should detect whether a value is an instance of List", function() + local l = List { 1, 2 } + local n = 42 + assert(List.is_list(l)) + assert(not List.is_list(n)) + end) + it("should be equal if all elements are equal", function() + local l1 = List { 1, 2, 3 } + local l2 = List { 1, 2, 3 } + local l3 = List { 4, 5, 6 } + assert.are.equal(l1, l2) + assert.are_not.equal(l1, l3) + end) + it("can be concatenated to other list-like tables", function() + local l1 = List { 1, 2, 3 } .. { 4 } + assert.are.equal(l1, List { 1, 2, 3, 4 }) + end) + it("can create a copy of itself with equal elements", function() + local l1 = List { 1, 2, 3 } + local l2 = l1:copy() + assert.are.equal(l1, l2) + end) + it("can create a slice between two indices", function() + local l1 = List { 1, 2, 3, 4 } + local l2 = l1:slice(2, 4) + assert.are.equal(l2, List { 2, 3, 4 }) + end) + it("can reverse itself in place", function() + local l = List { 1, 2, 3, 4 } + l:reverse() + assert.are.equal(l, List { 4, 3, 2, 1 }) + end) + it("can push elements to itself", function() + local l = List { 1, 2, 3 } + l:push(4) + assert.are.equal(l, List { 1, 2, 3, 4 }) + end) + it("can pop the n-th element from itself (last one by default)", function() + local l = List { 1, 2, 3, 4 } + local n = l:pop() + assert.are.equal(l, List { 1, 2, 3 }) + assert.are.equal(n, 4) + end) + it("can create a list from an iterable", function() + local l = List.from_iter(Iter.range(5, 10)) + assert.are.equal(l, List { 5, 6, 7, 8, 9, 10 }) + end) + it("can be partitioned based on a predicate", function() + local l = List.from_iter(Iter.range(1, 10)) + local evens, odds = l:iter():partition(function(e) + return e % 2 == 0 + end) + assert.are.equal(evens, List { 2, 4, 6, 8, 10 }) + assert.are.equal(odds, List { 1, 3, 5, 7, 9 }) + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/rotate_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/rotate_spec.lua new file mode 100644 index 0000000..825228b --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/rotate_spec.lua @@ -0,0 +1,33 @@ +local rotate = require("plenary.vararg").rotate + +local eq = function(a, b) + assert.is["true"](vim.deep_equal(a, b), true) +end + +describe("rotate", function() + it("should return as many values, as the first argument", function() + local args = {} + for _ = 0, 20 do + local n = select("#", unpack(args)) + assert.is.equal(n, select("#", rotate(n, unpack(args)))) + args[#args + 1] = n + end + end) + + it("should rotate varargs", function() + eq({ rotate(3, 1, 2, 3) }, { 2, 3, 1 }) + eq({ rotate(9, 1, 2, 3, 4, 5, 6, 7, 8, 9) }, { 2, 3, 4, 5, 6, 7, 8, 9, 1 }) + end) + + it("should rotate zero", function() + assert.is.equal(0, select("#", rotate(0))) + end) + + it("should rotate none", function() + assert.is.equal(0, select("#", rotate())) + end) + + it("should rotate one", function() + eq({ rotate(1, 1) }, { 1 }) + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/scandir_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/scandir_spec.lua new file mode 100644 index 0000000..9c15808 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/scandir_spec.lua @@ -0,0 +1,249 @@ +local scan = require "plenary.scandir" +local mock = require "luassert.mock" +local stub = require "luassert.stub" +local eq = assert.are.same + +local contains = function(tbl, str) + for _, v in ipairs(tbl) do + if v == str then + return true + end + end + return false +end + +local contains_match = function(tbl, str) + for _, v in ipairs(tbl) do + if v:match(str) then + return true + end + end + return false +end + +describe("scandir", function() + describe("can list all files recursive", function() + it("with cwd", function() + local dirs = scan.scan_dir "." + eq("table", type(dirs)) + eq(true, contains(dirs, "./README.md")) + eq(true, contains(dirs, "./LICENSE")) + eq(true, contains(dirs, "./lua/plenary/job.lua")) + eq(false, contains(dirs, "./asdf/asdf/adsf.lua")) + end) + + it("and callback gets called for each entry", function() + local count = 0 + local dirs = scan.scan_dir(".", { + on_insert = function() + count = count + 1 + end, + }) + eq("table", type(dirs)) + eq(true, contains(dirs, "./README.md")) + eq(true, contains(dirs, "./LICENSE")) + eq(true, contains(dirs, "./lua/plenary/job.lua")) + eq(false, contains(dirs, "./asdf/asdf/adsf.lua")) + eq(count, #dirs) + end) + + it("with multiple paths", function() + local dirs = scan.scan_dir { "./lua", "./tests" } + eq("table", type(dirs)) + eq(true, contains(dirs, "./lua/say.lua")) + eq(true, contains(dirs, "./lua/plenary/job.lua")) + eq(true, contains(dirs, "./tests/plenary/scandir_spec.lua")) + eq(false, contains(dirs, "./asdf/asdf/adsf.lua")) + end) + + it("with hidden files", function() + local dirs = scan.scan_dir(".", { hidden = true }) + eq("table", type(dirs)) + eq(true, contains(dirs, "./README.md")) + eq(true, contains(dirs, "./lua/plenary/job.lua")) + eq(true, contains(dirs, "./.gitignore")) + eq(false, contains(dirs, "./asdf/asdf/adsf.lua")) + end) + + it("with add directories", function() + local dirs = scan.scan_dir(".", { add_dirs = true }) + eq("table", type(dirs)) + eq(true, contains(dirs, "./README.md")) + eq(true, contains(dirs, "./lua/plenary/job.lua")) + eq(true, contains(dirs, "./lua")) + eq(true, contains(dirs, "./tests")) + eq(false, contains(dirs, "./asdf/asdf/adsf.lua")) + end) + + it("with only directories", function() + local dirs = scan.scan_dir(".", { only_dirs = true }) + eq("table", type(dirs)) + eq(false, contains(dirs, "./README.md")) + eq(false, contains(dirs, "./lua/plenary/job.lua")) + eq(true, contains(dirs, "./lua")) + eq(true, contains(dirs, "./tests")) + eq(false, contains(dirs, "./asdf/asdf/adsf.lua")) + end) + + it("until depth 1 is reached", function() + local dirs = scan.scan_dir(".", { depth = 1 }) + eq("table", type(dirs)) + eq(true, contains(dirs, "./README.md")) + eq(false, contains(dirs, "./lua")) + eq(false, contains(dirs, "./lua/say.lua")) + eq(false, contains(dirs, "./lua/plenary/job.lua")) + eq(false, contains(dirs, "./asdf/asdf/adsf.lua")) + end) + + it("until depth 1 is reached and with directories", function() + local dirs = scan.scan_dir(".", { depth = 1, add_dirs = true }) + eq("table", type(dirs)) + eq(true, contains(dirs, "./README.md")) + eq(true, contains(dirs, "./lua")) + eq(false, contains(dirs, "./lua/say.lua")) + eq(false, contains(dirs, "./lua/plenary/job.lua")) + eq(false, contains(dirs, "./asdf/asdf/adsf.lua")) + end) + + it("until depth 2 is reached", function() + local dirs = scan.scan_dir(".", { depth = 2 }) + eq("table", type(dirs)) + eq(true, contains(dirs, "./README.md")) + eq(true, contains(dirs, "./lua/say.lua")) + eq(false, contains(dirs, "./lua/plenary/job.lua")) + eq(false, contains(dirs, "./asdf/asdf/adsf.lua")) + end) + + it("with respect_gitignore", function() + vim.cmd ":silent !touch lua/test.so" + local dirs = scan.scan_dir(".", { respect_gitignore = true }) + vim.cmd ":silent !rm lua/test.so" + eq("table", type(dirs)) + eq(true, contains(dirs, "./README.md")) + eq(true, contains(dirs, "./LICENSE")) + eq(true, contains(dirs, "./lua/plenary/job.lua")) + eq(false, contains(dirs, "./lua/test.so")) + eq(false, contains(dirs, "./asdf/asdf/adsf.lua")) + end) + + it("with search pattern", function() + local dirs = scan.scan_dir(".", { search_pattern = "filetype" }) + eq("table", type(dirs)) + eq(true, contains(dirs, "./scripts/update_filetypes_from_github.lua")) + eq(true, contains(dirs, "./lua/plenary/filetype.lua")) + eq(true, contains(dirs, "./tests/plenary/filetype_spec.lua")) + eq(true, contains(dirs, "./data/plenary/filetypes/base.lua")) + eq(true, contains(dirs, "./data/plenary/filetypes/builtin.lua")) + eq(false, contains(dirs, "./README.md")) + end) + + it("with callback search pattern", function() + local dirs = scan.scan_dir(".", { + search_pattern = function(entry) + return entry:match "filetype" + end, + }) + eq("table", type(dirs)) + eq(true, contains(dirs, "./scripts/update_filetypes_from_github.lua")) + eq(true, contains(dirs, "./lua/plenary/filetype.lua")) + eq(true, contains(dirs, "./tests/plenary/filetype_spec.lua")) + eq(true, contains(dirs, "./data/plenary/filetypes/base.lua")) + eq(true, contains(dirs, "./data/plenary/filetypes/builtin.lua")) + eq(false, contains(dirs, "./README.md")) + end) + end) + + describe("gitignore", function() + local Path = require "plenary.path" + local mock_path, mock_gitignore + before_each(function() + mock_path = { + exists = stub.new().returns(true), + iter = function() + local i = 0 + local n = table.getn(mock_gitignore) + return function() + i = i + 1 + if i <= n then + return mock_gitignore[i] + end + end + end, + } + Path.new = stub.new().returns(mock_path) + end) + after_each(function() + Path.new:revert() + end) + + describe("ignores path", function() + it("when path matches pattern exactly", function() + mock_gitignore = { "ignored.txt" } + local should_add = scan.__make_gitignore { "path" } + eq(false, should_add({ "path" }, "./path/ignored.txt")) + end) + it("when path matches * pattern", function() + mock_gitignore = { "*.txt" } + local should_add = scan.__make_gitignore { "path" } + eq(false, should_add({ "path" }, "./path/dir/ignored.txt")) + end) + it("when path matches leading ** pattern", function() + mock_gitignore = { "**/ignored.txt" } + local should_add = scan.__make_gitignore { "path" } + eq(false, should_add({ "path" }, "./path/dir/subdir/ignored.txt")) + end) + it("when path matches trailing ** pattern", function() + mock_gitignore = { "/dir/**" } + local should_add = scan.__make_gitignore { "path" } + eq(false, should_add({ "path" }, "./path/dir/subdir/ignored.txt")) + end) + it("when path matches ? pattern", function() + mock_gitignore = { "ignore?.txt" } + local should_add = scan.__make_gitignore { "path" } + eq(false, should_add({ "path" }, "./path/ignored.txt")) + end) + end) + + describe("does not ignore path", function() + it("when path does not match", function() + mock_gitignore = { "ignored.txt" } + local should_add = scan.__make_gitignore { "path" } + eq(true, should_add({ "path" }, "./path/ok.txt")) + end) + it("when path is negated", function() + mock_gitignore = { "*.txt", "!ok.txt" } + local should_add = scan.__make_gitignore { "path" } + eq(true, should_add({ "path" }, "./path/ok.txt")) + end) + end) + end) + + describe("ls", function() + it("works for cwd", function() + local dirs = scan.ls "." + eq("table", type(dirs)) + eq(true, contains_match(dirs, "LICENSE")) + eq(true, contains_match(dirs, "README.md")) + eq(true, contains_match(dirs, "lua")) + eq(false, contains_match(dirs, "%.git$")) + end) + + it("works for another directory", function() + local dirs = scan.ls "./lua" + eq("table", type(dirs)) + eq(true, contains_match(dirs, "luassert")) + eq(true, contains_match(dirs, "plenary")) + eq(true, contains_match(dirs, "say.lua")) + eq(false, contains_match(dirs, "README.md")) + end) + + it("works with opts.hidden for cwd", function() + local dirs = scan.ls(".", { hidden = true }) + eq("table", type(dirs)) + eq(true, contains_match(dirs, "README.md")) + eq(true, contains_match(dirs, "LICENSE")) + eq(true, contains_match(dirs, "lua")) + eq(true, contains_match(dirs, "%.git$")) + end) + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/simple_busted_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/simple_busted_spec.lua new file mode 100644 index 0000000..ce81ebf --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/simple_busted_spec.lua @@ -0,0 +1,206 @@ +local eq = assert.are.same + +local tester_function = function() + error(7) +end + +describe("busted specs", function() + describe("nested", function() + it("should work", function() + assert(true) + end) + end) + + it("should not nest", function() + assert(true) + end) + + it("should not fail unless we unpcall this", function() + pcall(tester_function) + end) + + pending("other thing pending", function() + error() + end) +end) + +describe("before each", function() + local a = 2 + local b = 3 + it("is not cleared", function() + eq(2, a) + eq(3, b) + a = a + 1 + b = b + 1 + end) + describe("nested", function() + before_each(function() + a = 0 + end) + it("should clear a but not b", function() + eq(0, a) + eq(4, b) + a = a + 1 + b = b + 1 + end) + describe("nested nested", function() + before_each(function() + b = 0 + end) + it("should clear b as well", function() + eq(0, a) + eq(0, b) + a = a + 1 + b = b + 1 + end) + end) + it("should only clear a", function() + eq(0, a) + eq(1, b) + a = a + 1 + b = b + 1 + end) + end) + it("should clear nothing", function() + eq(1, a) + eq(2, b) + end) +end) + +describe("before_each ordering", function() + local order = "" + before_each(function() + order = order .. "1," + end) + before_each(function() + order = order .. "2," + end) + describe("nested 1 deep", function() + before_each(function() + order = order .. "3," + end) + before_each(function() + order = order .. "4," + end) + describe("nested 2 deep", function() + before_each(function() + order = order .. "5," + end) + it("runs before_each`s in order", function() + eq("1,2,3,4,5,", order) + end) + end) + end) + describe("adjacent nested 1 deep", function() + before_each(function() + order = order .. "3a," + end) + before_each(function() + order = order .. "4a," + end) + describe("nested 2 deep", function() + before_each(function() + order = order .. "5a," + end) + it("runs before_each`s in order", function() + eq("1,2,3,4,5,1,2,3a,4a,5a,", order) + end) + end) + end) +end) + +describe("after each", function() + local a = 2 + local b = 3 + it("is not cleared", function() + eq(2, a) + eq(3, b) + a = a + 1 + b = b + 1 + end) + describe("nested", function() + after_each(function() + a = 0 + end) + it("should not clear any at this point", function() + eq(3, a) + eq(4, b) + a = a + 1 + b = b + 1 + end) + describe("nested nested", function() + after_each(function() + b = 0 + end) + it("should have cleared a", function() + eq(0, a) + eq(5, b) + a = a + 1 + b = b + 1 + end) + end) + it("should have cleared a and b", function() + eq(0, a) + eq(0, b) + a = a + 1 + b = b + 1 + end) + end) + it("should only have cleared a", function() + eq(0, a) + eq(1, b) + end) +end) + +describe("after_each ordering", function() + local order = "" + describe("1st describe having after_each", function() + after_each(function() + order = order .. "1," + end) + after_each(function() + order = order .. "2," + end) + describe("nested 1 deep", function() + after_each(function() + order = order .. "3," + end) + after_each(function() + order = order .. "4," + end) + describe("nested 2 deep", function() + after_each(function() + order = order .. "5," + end) + it("a test to trigger the after_each`s", function() + assert(true) + end) + end) + end) + describe("adjacent nested 1 deep", function() + after_each(function() + order = order .. "3a," + end) + after_each(function() + order = order .. "4a," + end) + describe("nested 2 deep", function() + after_each(function() + order = order .. "5a," + end) + it("a test to trigger the adjacent after_each`s", function() + assert(true) + end) + end) + end) + end) + it("ran after_each`s in order", function() + eq("1,2,3,4,5,1,2,3a,4a,5a,", order) + end) +end) + +describe("another top level describe test", function() + it("should work", function() + eq(1, 1) + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/strings_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/strings_spec.lua new file mode 100644 index 0000000..d9abd6d --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/strings_spec.lua @@ -0,0 +1,305 @@ +local strings = require "plenary.strings" +local eq = assert.are.same + +describe("strings", function() + describe("strdisplaywidth", function() + for _, case in ipairs { + { str = "abcde", expected = { single = 5, double = 5 } }, + -- This space below is a tab (U+0009) + { str = "abc de", expected = { single = 10, double = 10 } }, + { str = "アイウエオ", expected = { single = 10, double = 10 } }, + { str = "├─┤", expected = { single = 3, double = 6 } }, + { str = 123, expected = { single = 3, double = 3 } }, + } do + for _, ambiwidth in ipairs { "single", "double" } do + local item = type(case.str) == "string" and '"%s"' or "%s" + local msg = ("ambiwidth = %s, " .. item .. " -> %d"):format(ambiwidth, case.str, case.expected[ambiwidth]) + local original = vim.o.ambiwidth + vim.o.ambiwidth = ambiwidth + it("lua: " .. msg, function() + eq(case.expected[ambiwidth], strings.strdisplaywidth(case.str)) + end) + it("vim: " .. msg, function() + eq(case.expected[ambiwidth], vim.fn.strdisplaywidth(case.str)) + end) + vim.o.ambiwidth = original + end + end + end) + + describe("strcharpart", function() + for _, case in ipairs { + { args = { "abcde", 2 }, expected = "cde" }, + { args = { "abcde", 2, 2 }, expected = "cd" }, + { args = { "アイウエオ", 2, 2 }, expected = "ウエ" }, + { args = { "├───┤", 2, 2 }, expected = "──" }, + } do + local msg = ('("%s", %d, %s) -> "%s"'):format(case.args[1], case.args[2], tostring(case.args[3]), case.expected) + it("lua: " .. msg, function() + eq(case.expected, strings.strcharpart(unpack(case.args))) + end) + it("vim: " .. msg, function() + eq(case.expected, vim.fn.strcharpart(unpack(case.args))) + end) + end + end) + + describe("truncate", function() + for _, case in ipairs { + -- truncations from the right + { args = { "abcde", 6, nil, 1 }, expected = { single = "abcde", double = "abcde" } }, + { args = { "abcde", 5, nil, 1 }, expected = { single = "abcde", double = "abcde" } }, + { args = { "abcde", 4, nil, 1 }, expected = { single = "abc…", double = "ab…" } }, + { + args = { "アイウエオ", 11, nil, 1 }, + expected = { single = "アイウエオ", double = "アイウエオ" }, + }, + { + args = { "アイウエオ", 10, nil, 1 }, + expected = { single = "アイウエオ", double = "アイウエオ" }, + }, + { + args = { "アイウエオ", 9, nil, 1 }, + expected = { single = "アイウエ…", double = "アイウ…" }, + }, + { args = { "アイウエオ", 8, nil, 1 }, expected = { single = "アイウ…", double = "アイウ…" } }, + { args = { "├─┤", 7, nil, 1 }, expected = { single = "├─┤", double = "├─┤" } }, + { args = { "├─┤", 6, nil, 1 }, expected = { single = "├─┤", double = "├─┤" } }, + { args = { "├─┤", 5, nil, 1 }, expected = { single = "├─┤", double = "├…" } }, + { args = { "├─┤", 4, nil, 1 }, expected = { single = "├─┤", double = "├…" } }, + { args = { "├─┤", 3, nil, 1 }, expected = { single = "├─┤", double = "…" } }, + { args = { "├─┤", 2, nil, 1 }, expected = { single = "├…", double = "…" } }, + -- truncations from the left + { args = { "abcde", 6, nil, -1 }, expected = { single = "abcde", double = "abcde" } }, + { args = { "abcde", 5, nil, -1 }, expected = { single = "abcde", double = "abcde" } }, + { args = { "abcde", 4, nil, -1 }, expected = { single = "…cde", double = "…de" } }, + { + args = { "アイウエオ", 11, nil, -1 }, + expected = { single = "アイウエオ", double = "アイウエオ" }, + }, + { + args = { "アイウエオ", 10, nil, -1 }, + expected = { single = "アイウエオ", double = "アイウエオ" }, + }, + { + args = { "アイウエオ", 9, nil, -1 }, + expected = { single = "…イウエオ", double = "…ウエオ" }, + }, + { args = { "アイウエオ", 8, nil, -1 }, expected = { single = "…ウエオ", double = "…ウエオ" } }, + { args = { "├─┤", 7, nil, -1 }, expected = { single = "├─┤", double = "├─┤" } }, + { args = { "├─┤", 6, nil, -1 }, expected = { single = "├─┤", double = "├─┤" } }, + { args = { "├─┤", 5, nil, -1 }, expected = { single = "├─┤", double = "…┤" } }, + { args = { "├─┤", 4, nil, -1 }, expected = { single = "├─┤", double = "…┤" } }, + { args = { "├─┤", 3, nil, -1 }, expected = { single = "├─┤", double = "…" } }, + { args = { "├─┤", 2, nil, -1 }, expected = { single = "…┤", double = "…" } }, + -- truncations from the middle + { args = { "abcde", 6, nil, 0 }, expected = { single = "abcde", double = "abcde" } }, + { args = { "abcde", 5, nil, 0 }, expected = { single = "abcde", double = "abcde" } }, + { args = { "abcde", 4, nil, 0 }, expected = { single = "a…de", double = "a…e" } }, + { + args = { "アイウエオ", 11, nil, 0 }, + expected = { single = "アイウエオ", double = "アイウエオ" }, + }, + { + args = { "アイウエオ", 10, nil, 0 }, + expected = { single = "アイウエオ", double = "アイウエオ" }, + }, + { + args = { "アイウエオ", 9, nil, 0 }, + expected = { single = "アイ…エオ", double = "ア…エオ" }, + }, + { args = { "アイウエオ", 8, nil, 0 }, expected = { single = "ア…エオ", double = "ア…エオ" } }, + { args = { "├─┤", 7, nil, 0 }, expected = { single = "├─┤", double = "├─┤" } }, + { args = { "├─┤", 6, nil, 0 }, expected = { single = "├─┤", double = "├─┤" } }, + { args = { "├─┤", 5, nil, 0 }, expected = { single = "├─┤", double = "…┤" } }, + { args = { "├─┤", 4, nil, 0 }, expected = { single = "├─┤", double = "…┤" } }, + { args = { "├─┤", 3, nil, 0 }, expected = { single = "├─┤", double = "…" } }, + { args = { "├─┤", 2, nil, 0 }, expected = { single = "…┤", double = "…" } }, + } do + for _, ambiwidth in ipairs { "single", "double" } do + local msg = ("ambiwidth = %s, direction = %s, [%s, %d] -> %s"):format( + ambiwidth, + (case.args[4] > 0) and "right" or (case.args[4] < 0) and "left" or "middle", + case.args[1], + case.args[2], + case.expected[ambiwidth] + ) + it(msg, function() + local original = vim.o.ambiwidth + vim.o.ambiwidth = ambiwidth + eq(case.expected[ambiwidth], strings.truncate(unpack(case.args))) + vim.o.ambiwidth = original + end) + end + end + end) + + describe("align_str", function() + for _, case in ipairs { + { args = { "abcde", 8 }, expected = { single = "abcde ", double = "abcde " } }, + { args = { "アイウ", 8 }, expected = { single = "アイウ ", double = "アイウ " } }, + { args = { "├─┤", 8 }, expected = { single = "├─┤ ", double = "├─┤ " } }, + { args = { "abcde", 8, true }, expected = { single = " abcde", double = " abcde" } }, + { args = { "アイウ", 8, true }, expected = { single = " アイウ", double = " アイウ" } }, + { args = { "├─┤", 8, true }, expected = { single = " ├─┤", double = " ├─┤" } }, + } do + for _, ambiwidth in ipairs { "single", "double" } do + local msg = ('ambiwidth = %s, [%s, %d, %s] -> "%s"'):format( + ambiwidth, + case.args[1], + case.args[2], + tostring(case.args[3]), + case.expected[ambiwidth] + ) + it(msg, function() + local original = vim.o.ambiwidth + vim.o.ambiwidth = ambiwidth + eq(case.expected[ambiwidth], strings.align_str(unpack(case.args))) + vim.o.ambiwidth = original + end) + end + end + end) + + describe("dedent", function() + local function lines(t) + return table.concat(t, "\n") + end + for _, case in ipairs { + { + msg = "empty string", + tabstop = 8, + args = { "" }, + expected = "", + }, + { + msg = "in case tabs are longer than spaces", + tabstop = 8, + args = { + lines { + " -> 13 spaces", + " 5 spaces -> 0 space", + }, + }, + expected = lines { + " -> 13 spaces", + "5 spaces -> 0 space", + }, + }, + { + msg = "in case tabs are shorter than spaces", + tabstop = 2, + args = { + lines { + " -> 0 space", + " 5spaces -> 1 space", + }, + }, + expected = lines { + " -> 0 space", + " 5spaces -> 1 space", + }, + }, + { + msg = "ignores empty lines", + tabstop = 2, + args = { + lines { + "", + "", + "", + " 8 spaces -> 3 spaces", + "", + "", + " 5 spaces -> 0 space", + "", + "", + "", + }, + }, + expected = lines { + "", + "", + "", + " 8 spaces -> 3 spaces", + "", + "", + "5 spaces -> 0 space", + "", + "", + "", + }, + }, + { + msg = "no indent", + tabstop = 2, + args = { + lines { + " -> 2 spaces", + "Here is no indent.", + " 4 spaces will remain", + }, + }, + expected = lines { + " -> 2 spaces", + "Here is no indent.", + " 4 spaces will remain", + }, + }, + { + msg = "leave_indent = 4", + tabstop = 2, + args = { + lines { + " -> 6 spaces", + "0 indent -> 4 spaces", + " 4 spaces -> 8 spaces", + }, + 4, + }, + expected = lines { + " -> 6 spaces", + " 0 indent -> 4 spaces", + " 4 spaces -> 8 spaces", + }, + }, + { + msg = "typical usecase: to 5 spaces", + tabstop = 4, + args = { + lines { + "", + " Chapter 1", + "", + " Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed", + " do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + "", + " Ut enim ad minim veniam, quis nostrud exercitation ullamco", + " laboris nisi ut aliquip ex ea commodo consequat.", + "", + }, + 5, + }, + expected = lines { + "", + " Chapter 1", + "", + " Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed", + " do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + "", + " Ut enim ad minim veniam, quis nostrud exercitation ullamco", + " laboris nisi ut aliquip ex ea commodo consequat.", + "", + }, + }, + } do + local msg = ("tabstop = %d, %s"):format(case.tabstop, case.msg) + it(msg, function() + local original = vim.bo.tabstop + vim.bo.tabstop = case.tabstop + eq(case.expected, strings.dedent(unpack(case.args))) + vim.bo.tabstop = original + end) + end + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/tbl_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/tbl_spec.lua new file mode 100644 index 0000000..3b5a936 --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/tbl_spec.lua @@ -0,0 +1,27 @@ +local tbl = require "plenary.tbl" + +local function should_fail(fun) + local stat = pcall(fun) + assert(not stat, "Function should have errored") +end + +describe("tbl utilities", function() + it("should be able to freeze a table", function() + local t = { 1, 2, 3 } + local frozen = tbl.freeze(t) + assert(t[1] == frozen[1]) + assert(t[2] == frozen[2]) + assert(t[3] == frozen[3]) + + should_fail(function() + frozen[4] = "thisthis" + end) + + should_fail(function() + frozen.hello = "asdfasdf" + end) + + assert(not frozen[5]) + assert(not frozen.hello) + end) +end) diff --git a/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/uses_nvim_spec.lua b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/uses_nvim_spec.lua new file mode 100644 index 0000000..61e4c5d --- /dev/null +++ b/.config/nvim/pack/vendor/start/plenary.nvim/tests/plenary/uses_nvim_spec.lua @@ -0,0 +1,5 @@ +describe("simple nvim test", function() + it("should work", function() + vim.cmd "let g:val = v:true" + end) +end) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/.DS_Store b/.config/nvim/pack/vendor/start/telescope.nvim/.DS_Store new file mode 100644 index 0000000..b355e93 Binary files /dev/null and b/.config/nvim/pack/vendor/start/telescope.nvim/.DS_Store differ diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/.github/FUNDING.yml b/.config/nvim/pack/vendor/start/telescope.nvim/.github/FUNDING.yml new file mode 100644 index 0000000..66237c4 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [tjdevries, Conni2461, fdschmidt93, jamestrew] diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/.github/ISSUE_TEMPLATE/bug_report.yml b/.config/nvim/pack/vendor/start/telescope.nvim/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..b1362c5 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,120 @@ +name: Bug report +description: Report a problem with Telescope +labels: [bug] +body: + - type: markdown + attributes: + value: | + Before reporting: search [existing issues](https://github.com/nvim-telescope/telescope.nvim/issues) and make sure that both Telescope and its dependencies are updated to the latest version. + - type: textarea + attributes: + label: "Description" + description: "A short description of the problem you are reporting." + validations: + required: true + - type: textarea + attributes: + label: "Neovim version" + description: "Output of `nvim --version`" + render: markdown + placeholder: | + NVIM v0.6.0-dev+209-g0603eba6e + Build type: Release + LuaJIT 2.1.0-beta3 + validations: + required: true + - type: input + attributes: + label: "Operating system and version" + placeholder: "macOS 11.5" + validations: + required: true + - type: input + attributes: + label: "Telescope version / branch / rev" + placeholder: "telescope 0.1.0" + validations: + required: true + - type: textarea + attributes: + label: "checkhealth telescope" + description: "Output of `:checkhealth telescope`" + render: markdown + placeholder: | + health#telescope#check + ======================================================================== + ## Checking for required plugins + - OK: plenary installed. + - OK: nvim-treesitter installed. + + ## Checking external dependencies + - OK: rg: found ripgrep 13.0.0 + - OK: fd: found fd 8.2.1 + + ## ===== Installed extensions ===== + validations: + required: true + - type: textarea + attributes: + label: "Steps to reproduce" + description: "Steps to reproduce using the minimal config provided below." + placeholder: | + 1. `nvim -nu minimal.lua` + 2. ... + validations: + required: true + - type: textarea + attributes: + label: "Expected behavior" + description: "A description of the behavior you expected:" + - type: textarea + attributes: + label: "Actual behavior" + description: "Observed behavior (may optionally include logs, images, or videos)." + validations: + required: true + - type: textarea + attributes: + label: "Minimal config" + description: "Minimal(!) configuration necessary to reproduce the issue. Save this as `minimal.lua` and run with `nvim -nu minimal.lua`. If _absolutely_ necessary, add plugins and config options from your `init.lua` at the indicated lines." + render: Lua + value: | + 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.uv.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 = { + { + "nvim-telescope/telescope.nvim", + dependencies = { + "nvim-lua/plenary.nvim", + }, + config = function() + -- ADD INIT.LUA SETTINGS THAT ARE _NECESSARY_ FOR REPRODUCING THE ISSUE + require("telescope").setup {} + end, + }, + } + + require("lazy").setup(plugins, { + root = root .. "/plugins", + }) + validations: + required: true diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/.github/ISSUE_TEMPLATE/config.yml b/.config/nvim/pack/vendor/start/telescope.nvim/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..a5d27cd --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,6 @@ +blank_issues_enabled: false +contact_links: + - name: Question + url: https://gitter.im/nvim-telescope/community + about: Usage questions and support requests are answered in the Telescope Gitter + diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/.github/ISSUE_TEMPLATE/feature_request.md b/.config/nvim/pack/vendor/start/telescope.nvim/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..11fc491 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/.github/PULL_REQUEST_TEMPLATE.md b/.config/nvim/pack/vendor/start/telescope.nvim/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..780091e --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,33 @@ +# Description + +Please include a summary of the change and which issue is fixed. Please also +include relevant motivation and context + +Fixes # (issue) + +## Type of change + +Please delete options that are not relevant. + +- Bug fix (non-breaking change which fixes an issue) +- New feature (non-breaking change which adds functionality) +- Breaking change (fix or feature that would cause existing functionality to not work as expected) +- This change requires a documentation update + +# How Has This Been Tested? + +Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list relevant details about your configuration + +- [ ] Test A +- [ ] Test B + +**Configuration**: +* Neovim version (nvim --version): +* Operating system and version: + +# Checklist: + +- [ ] My code follows the style guidelines of this project (stylua) +- [ ] I have performed a self-review of my own code +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation (lua annotations) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/.github/workflows/ci.yml b/.config/nvim/pack/vendor/start/telescope.nvim/.github/workflows/ci.yml new file mode 100644 index 0000000..83bc7a7 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/.github/workflows/ci.yml @@ -0,0 +1,41 @@ +name: Tests + +on: [push, pull_request] + +jobs: + unit_tests: + name: unit tests + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-22.04, macos-latest, windows-2022] + rev: [nightly, v0.9.5, v0.10.0] + include: + - os: ubuntu-22.04 + install-rg: sudo apt-get update && sudo apt-get install -y ripgrep + - os: macos-latest + install-rg: brew update && brew install ripgrep + - os: windows-2022 + install-rg: choco install ripgrep + + steps: + - uses: actions/checkout@v4 + + - uses: rhysd/action-setup-vim@v1 + with: + neovim: true + version: ${{ matrix.rev }} + + - name: Prepare + run: | + ${{ matrix.install-rg }} + rg --version + + git clone --depth 1 https://github.com/nvim-lua/plenary.nvim ../plenary.nvim + git clone --depth 1 https://github.com/nvim-tree/nvim-web-devicons ../nvim-web-devicons + + - name: Run tests + run: | + nvim --version + make test diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/.github/workflows/docgen.yml b/.config/nvim/pack/vendor/start/telescope.nvim/.github/workflows/docgen.yml new file mode 100644 index 0000000..92da895 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/.github/workflows/docgen.yml @@ -0,0 +1,69 @@ +name: Generate docs + +on: + push: + branches-ignore: + - master + pull_request_target: + branches: + - master + +jobs: + build-sources: + name: Generate docs + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-22.04 + url: https://github.com/neovim/neovim/releases/download/v0.9.5/nvim-linux64.tar.gz + steps: + - uses: actions/checkout@v4 + - run: date +%F > todays-date + - name: Restore cache for today's nightly. + uses: actions/cache@v4 + with: + path: _neovim + key: ${{ runner.os }}-${{ matrix.url }}-${{ hashFiles('todays-date') }} + + - name: Prepare + run: | + test -d _neovim || { + mkdir -p _neovim + curl -sL ${{ matrix.url }} | tar xzf - --strip-components=1 -C "${PWD}/_neovim" + } + mkdir -p ~/.local/share/nvim/site/pack/vendor/start + git clone --depth 1 https://github.com/nvim-lua/plenary.nvim ~/.local/share/nvim/site/pack/vendor/start/plenary.nvim + git clone https://github.com/tjdevries/tree-sitter-lua ~/.local/share/nvim/site/pack/vendor/start/tree-sitter-lua + ln -s $(pwd) ~/.local/share/nvim/site/pack/vendor/start + + - name: Build parser + run: | + # We have to build the parser every single time to keep up with parser changes + cd ~/.local/share/nvim/site/pack/vendor/start/tree-sitter-lua + git checkout 86f74dfb69c570f0749b241f8f5489f8f50adbea + make dist + cd - + + - name: Generating docs + run: | + export PATH="${PWD}/_neovim/bin:${PATH}" + export VIM="${PWD}/_neovim/share/nvim/runtime" + nvim --version + make docgen + + # inspired by nvim-lspconfigs + - name: Update documentation + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COMMIT_MSG: | + [docgen] Update doc/telescope.txt + skip-checks: true + run: | + git config user.email "actions@github" + git config user.name "Github Actions" + git remote set-url origin https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git + git add doc/ + # Only commit and push if we have changes + git diff --quiet && git diff --staged --quiet || (git commit -m "${COMMIT_MSG}"; git push origin HEAD:${GITHUB_REF}) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/.github/workflows/lint.yml b/.config/nvim/pack/vendor/start/telescope.nvim/.github/workflows/lint.yml new file mode 100644 index 0000000..c1afb24 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/.github/workflows/lint.yml @@ -0,0 +1,31 @@ +name: Linting and style checking + +on: [push, pull_request] + +jobs: + luacheck: + name: Luacheck + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + + - name: Prepare + run: | + sudo apt-get update + sudo apt-get install -y luarocks + sudo luarocks install luacheck + + - name: Lint + run: sudo make lint + + stylua: + name: stylua + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: JohnnyMorganz/stylua-action@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + version: latest + # CLI arguments + args: --color always --check lua/ diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/.github/workflows/release.yml b/.config/nvim/pack/vendor/start/telescope.nvim/.github/workflows/release.yml new file mode 100644 index 0000000..819a147 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/.github/workflows/release.yml @@ -0,0 +1,29 @@ +name: "release" +on: + push: + tags: + - '*' +jobs: + luarocks-upload: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - name: LuaRocks Upload + uses: nvim-neorocks/luarocks-tag-release@v1.0.2 + env: + LUAROCKS_API_KEY: ${{ secrets.LUAROCKS_API_KEY }} + with: + summary: "Find, Filter, Preview, Pick. All lua, all the time." + detailed_description: | + A highly extendable fuzzy finder over lists. + Built on the latest awesome features from neovim core. + Telescope is centered around modularity, allowing for easy customization. + dependencies: | + plenary.nvim + copy_directories: | + doc + ftplugin + plugin + scripts + autoload + data diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/.gitignore b/.config/nvim/pack/vendor/start/telescope.nvim/.gitignore new file mode 100644 index 0000000..ddeae0d --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/.gitignore @@ -0,0 +1,4 @@ +build/ +doc/tags + +.luacheckcache diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/.luacheckrc b/.config/nvim/pack/vendor/start/telescope.nvim/.luacheckrc new file mode 100644 index 0000000..02c0b89 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/.luacheckrc @@ -0,0 +1,33 @@ +-- Rerun tests only if their modification time changed. +cache = true + +std = luajit +codes = true + +self = false + +-- Glorious list of warnings: https://luacheck.readthedocs.io/en/stable/warnings.html +ignore = { + "212", -- Unused argument, In the case of callback function, _arg_name is easier to understand than _, so this option is set to off. + "122", -- Indirectly setting a readonly global +} + +globals = { + "_", + "TelescopeGlobalState", + "_TelescopeConfigurationValues", + "_TelescopeConfigurationPickers", +} + +-- Global objects defined by the C code +read_globals = { + "vim", +} + +files = { + ["lua/telescope/builtin/init.lua"] = { + ignore = { + "631", -- allow line len > 120 + } + }, +} diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/.stylua.toml b/.config/nvim/pack/vendor/start/telescope.nvim/.stylua.toml new file mode 100644 index 0000000..ecb6dca --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/.stylua.toml @@ -0,0 +1,6 @@ +column_width = 120 +line_endings = "Unix" +indent_type = "Spaces" +indent_width = 2 +quote_style = "AutoPreferDouble" +call_parentheses = "None" diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/CONTRIBUTING.md b/.config/nvim/pack/vendor/start/telescope.nvim/CONTRIBUTING.md new file mode 100644 index 0000000..2869a41 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/CONTRIBUTING.md @@ -0,0 +1,81 @@ +# Contributing + +Thanks for taking the time to submit code to Telescope if you're reading this! +We love having new contributors and love seeing the Neovim community come around this plugin and keep making it better. + +At this time, we are content with the number and functionality of the pickers we offer built +in with Telescope and so we are currently not accepting new pickers +(see this [issue](https://github.com/nvim-telescope/telescope.nvim/issues/1228) for a discussion on this). + +We are also conservative with integrating picker specific actions and features. +If you're still interested in filling a particular picker need, we encourage packaging it up as its own Telescope extension. +Read our [Bundling as extension](https://github.com/nvim-telescope/telescope.nvim/blob/master/developers.md#bundling-as-extension) guide here for more info on this. +See other Telescope extensions (and add yours) [here](https://github.com/nvim-telescope/telescope.nvim/wiki/Extensions). + +That said, we welcome bug fixes, documentation improvements and non-picker specific features. +If you're submitting a new feature, it is a good idea to create an issue first to gauge interest and feasibility. + +To learn how we go about writing documentation for this project, keep reading below! + +## Documentation with treesitter + +We are generating docs based on the tree sitter syntax tree. TJ wrote a grammar that includes the documentation in this syntax tree so we can do take this function header documentation and transform it into vim documentation. All documentation that is part of the returning module will be exported. For example: + +```lua +local m = {} + +--- Test Header +--@return 1: Returns always 1 +function m.a() -- or m:a() + return 1 +end + +--- Documentation +function m.__b() -- or m:__b() + return 2 +end + +--- Documentation +local c = function() + return 2 +end + +return m +``` + +This will export function `a` with header documentation and the return value. Module function `b` and local function `c` will not be exported. + +For a more in-depth look at how to write documentation take a look at this guide: [how to](https://github.com/tjdevries/tree-sitter-lua/blob/master/HOWTO.md) +This guide contains all annotations and we will update it when we add new annotations. + +## What is missing? + +The docgen has some problems on which people can work. This would happen in [tree-sitter-lua](https://github.com/tjdevries/tree-sitter-lua) and documentation of some modules here. +I would suggest we are documenting lua/telescope/builtin/init.lua rather than the files itself. We can use that init.lua file as "header" file, so we are not cluttering the other files. +How to help out with documentation: + +## Auto-updates from CI + +The easy way would be: + +- write some docs +- commit, push and create draft PR +- wait a minute until the CI generates a new commit with the changes +- Look at this commit and the changes +- Modify documentation until its perfect. You can do `git commit --amend` and `git push --force` to remove the github ci commit again + +## Generate on your local machine + +The other option would be setting up + +- Install Treesitter, either with package manager or with github release +- Install plugin as usual +- cd to plugin +- `mkdir -p build parser` sadly those don't exist +- `make build_parser` +- `ln -s ../build/parser.so parser/lua.so` We need the shared object in parser/ so it gets picked up by neovim. Either copy or symbolic link +- Make sure that nvim-treesitter lua parser is not installed and also delete the lua queries in that repository. `queries/lua/*`. If you are not doing that you will have a bad time! +- cd into this project +- Write doc +- Run `make docgen` +- Repeat last two steps diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/LICENSE b/.config/nvim/pack/vendor/start/telescope.nvim/LICENSE new file mode 100644 index 0000000..9117664 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-2021 nvim-telescope + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/Makefile b/.config/nvim/pack/vendor/start/telescope.nvim/Makefile new file mode 100644 index 0000000..e520d60 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/Makefile @@ -0,0 +1,10 @@ +.PHONY: test lint docgen + +test: + nvim --headless --noplugin -u scripts/minimal_init.vim -c "PlenaryBustedDirectory lua/tests/automated/ { minimal_init = './scripts/minimal_init.vim' }" + +lint: + luacheck lua/telescope + +docgen: + nvim --headless --noplugin -u scripts/minimal_init.vim -c "luafile ./scripts/gendocs.lua" -c 'qa' diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/README.md b/.config/nvim/pack/vendor/start/telescope.nvim/README.md new file mode 100644 index 0000000..e5bfdfd --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/README.md @@ -0,0 +1,640 @@ +# telescope.nvim + +[![Gitter](https://badges.gitter.im/nvim-telescope/community.svg)](https://gitter.im/nvim-telescope/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) +[![LuaRocks](https://img.shields.io/luarocks/v/Conni2461/telescope.nvim?logo=lua&color=purple)](https://luarocks.org/modules/Conni2461/telescope.nvim) + +Gaze deeply into unknown regions using the power of the moon. + +## What Is Telescope? + +`telescope.nvim` is a highly extendable fuzzy finder over lists. Built on the +latest awesome features from `neovim` core. Telescope is centered around +modularity, allowing for easy customization. + +Community driven builtin [pickers](#pickers), [sorters](#sorters) and +[previewers](#previewers). + +![Preview](https://i.imgur.com/TTTja6t.gif) +For more showcases of Telescope, please visit the [Showcase +section](https://github.com/nvim-telescope/telescope.nvim/wiki/Showcase) in the +Telescope Wiki + +## Telescope Table of Contents + +- [Getting Started](#getting-started) +- [Usage](#usage) +- [Customization](#customization) +- [Default Mappings](#default-mappings) +- [Pickers](#pickers) +- [Previewers](#previewers) +- [Sorters](#sorters) +- [Layout](#layout-display) +- [Themes](#themes) +- [Commands](#vim-commands) +- [Autocmds](#autocmds) +- [Extensions](#extensions) +- [API](#api) +- [Media](#media) +- [Contributing](#contributing) +- [Changelog](https://github.com/nvim-telescope/telescope.nvim/blob/master/doc/telescope_changelog.txt) + +## Getting Started + +This section should guide you to run your first builtin pickers. + +[Neovim (v0.9.0)](https://github.com/neovim/neovim/releases/tag/v0.9.0) or the +latest neovim nightly commit is required for `telescope.nvim` to work. +The neovim version also needs to be compiled with LuaJIT, we currently do not +support Lua5.1 because of some ongoing issues. + +### Required dependencies + +- [nvim-lua/plenary.nvim](https://github.com/nvim-lua/plenary.nvim) is required. + +### Suggested dependencies + +- [BurntSushi/ripgrep](https://github.com/BurntSushi/ripgrep) is required for + `live_grep` and `grep_string` and is the first priority for `find_files`. + +We also suggest you install one native telescope sorter to significantly improve +sorting performance. Take a look at either +[telescope-fzf-native.nvim](https://github.com/nvim-telescope/telescope-fzf-native.nvim) +or +[telescope-fzy-native.nvim](https://github.com/nvim-telescope/telescope-fzy-native.nvim). +For more information and a performance benchmark take a look at the +[Extensions](https://github.com/nvim-telescope/telescope.nvim/wiki/Extensions) +wiki. + +### Optional dependencies + +- [sharkdp/fd](https://github.com/sharkdp/fd) (finder) +- [nvim-treesitter/nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter) (finder/preview) +- [neovim LSP](https://neovim.io/doc/user/lsp.html) (picker) +- [devicons](https://github.com/nvim-tree/nvim-web-devicons) (icons) + +### Installation + +It is suggested to either use the latest release +[tag](https://github.com/nvim-telescope/telescope.nvim/tags) or our release +branch (which will get consistent updates) +[0.1.x](https://github.com/nvim-telescope/telescope.nvim/tree/0.1.x). + +It is not suggested to run latest master. + +Using [vim-plug](https://github.com/junegunn/vim-plug) + +```viml +Plug 'nvim-lua/plenary.nvim' +Plug 'nvim-telescope/telescope.nvim', { 'tag': '0.1.8' } +" or , { 'branch': '0.1.x' } +``` + +Using [dein](https://github.com/Shougo/dein.vim) + +```viml +call dein#add('nvim-lua/plenary.nvim') +call dein#add('nvim-telescope/telescope.nvim', { 'rev': '0.1.8' }) +" or , { 'rev': '0.1.x' }) +``` + +Using [packer.nvim](https://github.com/wbthomason/packer.nvim) + +```lua +use { + 'nvim-telescope/telescope.nvim', tag = '0.1.8', +-- or , branch = '0.1.x', + requires = { {'nvim-lua/plenary.nvim'} } +} +``` + +Using [lazy.nvim](https://github.com/folke/lazy.nvim) + +```lua +-- init.lua: + { + 'nvim-telescope/telescope.nvim', tag = '0.1.8', +-- or , branch = '0.1.x', + dependencies = { 'nvim-lua/plenary.nvim' } + } + +-- plugins/telescope.lua: +return { + 'nvim-telescope/telescope.nvim', tag = '0.1.8', +-- or , branch = '0.1.x', + dependencies = { 'nvim-lua/plenary.nvim' } + } +``` + +### checkhealth + +Make sure you call `:checkhealth telescope` after installing telescope to ensure +everything is set up correctly. + +After this setup you can continue reading here or switch to `:help telescope` +to get an understanding of how to use Telescope and how to configure it. + +## Usage + +Try the command `:Telescope find_files` +to see if `telescope.nvim` is installed correctly. + +Using VimL: + +```viml +" Find files using Telescope command-line sugar. +nnoremap ff Telescope find_files +nnoremap fg Telescope live_grep +nnoremap fb Telescope buffers +nnoremap fh Telescope help_tags + +" Using Lua functions +nnoremap ff lua require('telescope.builtin').find_files() +nnoremap fg lua require('telescope.builtin').live_grep() +nnoremap fb lua require('telescope.builtin').buffers() +nnoremap fh lua require('telescope.builtin').help_tags() +``` + +Using Lua: + +```lua +local builtin = require('telescope.builtin') +vim.keymap.set('n', 'ff', builtin.find_files, { desc = 'Telescope find files' }) +vim.keymap.set('n', 'fg', builtin.live_grep, { desc = 'Telescope live grep' }) +vim.keymap.set('n', 'fb', builtin.buffers, { desc = 'Telescope buffers' }) +vim.keymap.set('n', 'fh', builtin.help_tags, { desc = 'Telescope help tags' }) +``` + +See [builtin pickers](#pickers) for a list of all builtin functions. + +## Customization + +This section should help you explore available options to configure and +customize your `telescope.nvim`. + +Unlike most vim plugins, `telescope.nvim` can be customized by either applying +customizations globally, or individually per picker. + +- **Global Customization** affecting all pickers can be done through the main + `setup()` method (see defaults below) +- **Individual Customization** affecting a single picker by passing `opts` to + builtin pickers (e.g. `builtin.find_files(opts)`) see + [Configuration recipes](https://github.com/nvim-telescope/telescope.nvim/wiki/Configuration-Recipes) + wiki page for ideas. + +### Telescope setup structure + +```lua +require('telescope').setup{ + defaults = { + -- Default configuration for telescope goes here: + -- config_key = value, + mappings = { + i = { + -- map actions.which_key to (default: ) + -- actions.which_key shows the mappings for your picker, + -- e.g. git_{create, delete, ...}_branch for the git_branches picker + [""] = "which_key" + } + } + }, + pickers = { + -- Default configuration for builtin pickers goes here: + -- picker_name = { + -- picker_config_key = value, + -- ... + -- } + -- Now the picker_config_key will be applied every time you call this + -- builtin picker + }, + extensions = { + -- Your extension configuration goes here: + -- extension_name = { + -- extension_config_key = value, + -- } + -- please take a look at the readme of the extension you want to configure + } +} +``` + +To look at what default configuration options exist please read: `:help +telescope.setup()`. For picker specific `opts` please read: `:help +telescope.builtin`. + +To embed the above code snippet in a `.vim` file +(for example in `after/plugin/telescope.nvim.vim`), +wrap it in `lua << EOF code-snippet EOF`: + +```lua +lua << EOF +require('telescope').setup{ + -- ... +} +EOF +``` + +## Default Mappings + +Mappings are fully customizable. +Many familiar mapping patterns are set up as defaults. + +| Mappings | Action | +| -------------- | --------------------------------------------------------- | +| `/` | Next item | +| `/` | Previous item | +| `j/k` | Next/previous (in normal mode) | +| `H/M/L` | Select High/Middle/Low (in normal mode) | +| `gg/G` | Select the first/last item (in normal mode) | +| `` | Confirm selection | +| `` | Go to file selection as a split | +| `` | Go to file selection as a vsplit | +| `` | Go to a file in a new tab | +| `` | Scroll up in preview window | +| `` | Scroll down in preview window | +| `` | Scroll left in preview window | +| `` | Scroll right in preview window | +| `` | Scroll left in results window | +| `` | Scroll right in results window | +| `` | Show mappings for picker actions (insert mode) | +| `?` | Show mappings for picker actions (normal mode) | +| `` | Close telescope (insert mode) | +| `` | Close telescope (in normal mode) | +| `` | Toggle selection and move to next selection | +| `` | Toggle selection and move to prev selection | +| `` | Send all items not filtered to quickfixlist (qflist) | +| `` | Send all selected items to qflist | +| `` | Insert cword in original window into prompt (insert mode) | +| `` | Insert cWORD in original window into prompt (insert mode) | +| `` | Insert cfile in original window into prompt (insert mode) | +| `` | Insert cline in original window into prompt (insert mode) | + +To see the full list of mappings, check out `lua/telescope/mappings.lua` and the +`default_mappings` table. + +**Tip**: you can use `` and `?` in insert and normal mode, respectively, to show the actions mapped to your picker. + +Much like [builtin pickers](#pickers), there are a number of +[actions](https://github.com/nvim-telescope/telescope.nvim/blob/master/lua/telescope/actions/init.lua) +you can pick from to remap your telescope buffer mappings, or create a new +custom action: + +```lua +-- Built-in actions +local transform_mod = require('telescope.actions.mt').transform_mod + +-- or create your custom action +local my_cool_custom_action = transform_mod({ + x = function(prompt_bufnr) + print("This function ran after another action. Prompt_bufnr: " .. prompt_bufnr) + -- Enter your function logic here. You can take inspiration from lua/telescope/actions.lua + end, +}) +``` + +To remap telescope mappings, please read `:help telescope.defaults.mappings`. +To do picker specific mappings, its suggested to do this with the `pickers` +table in `telescope.setup`. Each picker accepts a `mappings` table like its +explained in `:help telescope.defaults.mappings`. + +## Pickers + +Built-in functions. Ready to be bound to any key you like. + +```vim +:lua require'telescope.builtin'.planets{} + +:nnoremap pp :lua require'telescope.builtin'.planets{} +``` + +### File Pickers + +| Functions | Description | +| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `builtin.find_files` | Lists files in your current working directory, respects .gitignore | +| `builtin.git_files` | Fuzzy search through the output of `git ls-files` command, respects .gitignore | +| `builtin.grep_string` | Searches for the string under your cursor or selection in your current working directory | +| `builtin.live_grep` | Search for a string in your current working directory and get results live as you type, respects .gitignore. (Requires [ripgrep](https://github.com/BurntSushi/ripgrep)) | + +### Vim Pickers + +| Functions | Description | +| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `builtin.buffers` | Lists open buffers in current neovim instance | +| `builtin.oldfiles` | Lists previously open files | +| `builtin.commands` | Lists available plugin/user commands and runs them on `` | +| `builtin.tags` | Lists tags in current directory with tag location file preview (users are required to run ctags -R to generate tags or update when introducing new changes) | +| `builtin.command_history` | Lists commands that were executed recently, and reruns them on `` | +| `builtin.search_history` | Lists searches that were executed recently, and reruns them on `` | +| `builtin.help_tags` | Lists available help tags and opens a new window with the relevant help info on `` | +| `builtin.man_pages` | Lists manpage entries, opens them in a help window on `` | +| `builtin.marks` | Lists vim marks and their value | +| `builtin.colorscheme` | Lists available colorschemes and applies them on `` | +| `builtin.quickfix` | Lists items in the quickfix list | +| `builtin.quickfixhistory` | Lists all quickfix lists in your history and open them with `builtin.quickfix` or quickfix window | +| `builtin.loclist` | Lists items from the current window's location list | +| `builtin.jumplist` | Lists Jump List entries | +| `builtin.vim_options` | Lists vim options, allows you to edit the current value on `` | +| `builtin.registers` | Lists vim registers, pastes the contents of the register on `` | +| `builtin.autocommands` | Lists vim autocommands and goes to their declaration on `` | +| `builtin.spell_suggest` | Lists spelling suggestions for the current word under the cursor, replaces word with selected suggestion on `` | +| `builtin.keymaps` | Lists normal mode keymappings | +| `builtin.filetypes` | Lists all available filetypes | +| `builtin.highlights` | Lists all available highlights | +| `builtin.current_buffer_fuzzy_find` | Live fuzzy search inside of the currently open buffer | +| `builtin.current_buffer_tags` | Lists all of the tags for the currently open buffer, with a preview | +| `builtin.resume` | Lists the results incl. multi-selections of the previous picker | +| `builtin.pickers` | Lists the previous pickers incl. multi-selections (see `:h telescope.defaults.cache_picker`) | + +### Neovim LSP Pickers + +| Functions | Description | +| --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | +| `builtin.lsp_references` | Lists LSP references for word under the cursor | +| `builtin.lsp_incoming_calls` | Lists LSP incoming calls for word under the cursor | +| `builtin.lsp_outgoing_calls` | Lists LSP outgoing calls for word under the cursor | +| `builtin.lsp_document_symbols` | Lists LSP document symbols in the current buffer | +| `builtin.lsp_workspace_symbols` | Lists LSP document symbols in the current workspace | +| `builtin.lsp_dynamic_workspace_symbols` | Dynamically Lists LSP for all workspace symbols | +| `builtin.diagnostics` | Lists Diagnostics for all open buffers or a specific buffer. Use option `bufnr=0` for current buffer. | +| `builtin.lsp_implementations` | Goto the implementation of the word under the cursor if there's only one, otherwise show all options in Telescope | +| `builtin.lsp_definitions` | Goto the definition of the word under the cursor, if there's only one, otherwise show all options in Telescope | +| `builtin.lsp_type_definitions` | Goto the definition of the type of the word under the cursor, if there's only one, otherwise show all options in Telescope | + +### Git Pickers + +| Functions | Description | +| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `builtin.git_commits` | Lists git commits with diff preview, checkout action ``, reset mixed `m`, reset soft `s` and reset hard `h` | +| `builtin.git_bcommits` | Lists buffer's git commits with diff preview and checks them out on `` | +| `builtin.git_bcommits_range` | Lists buffer's git commits in a range of lines. Use options `from` and `to` to specify the range. In visual mode, lists commits for the selected lines | +| `builtin.git_branches` | Lists all branches with log preview, checkout action ``, track action ``, rebase action``, create action ``, switch action ``, delete action `` and merge action `` | +| `builtin.git_status` | Lists current changes per file with diff preview and add action. (Multi-selection still WIP) | +| `builtin.git_stash` | Lists stash items in current repository with ability to apply them on `` | + +### Treesitter Picker + +| Functions | Description | +| -------------------- | ------------------------------------------------- | +| `builtin.treesitter` | Lists Function names, variables, from Treesitter! | + +### Lists Picker + +| Functions | Description | +| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `builtin.planets` | Use the telescope... | +| `builtin.builtin` | Lists Built-in pickers and run them on ``. | +| `builtin.reloader` | Lists Lua modules and reload them on ``. | +| `builtin.symbols` | Lists symbols inside a file `data/telescope-sources/*.json` found in your rtp. More info and symbol sources can be found [here](https://github.com/nvim-telescope/telescope-symbols.nvim) | + +## Previewers + +| Previewers | Description | +| ----------------------------------- | --------------------------------------------------------- | +| `previewers.vim_buffer_cat.new` | Default previewer for files. Uses vim buffers | +| `previewers.vim_buffer_vimgrep.new` | Default previewer for grep and similar. Uses vim buffers | +| `previewers.vim_buffer_qflist.new` | Default previewer for qflist. Uses vim buffers | +| `previewers.cat.new` | Terminal previewer for files. Uses `cat`/`bat` | +| `previewers.vimgrep.new` | Terminal previewer for grep and similar. Uses `cat`/`bat` | +| `previewers.qflist.new` | Terminal previewer for qflist. Uses `cat`/`bat` | + +The default previewers are from now on `vim_buffer_` previewers. They use vim +buffers for displaying files and use tree-sitter or regex for file highlighting. + +These previewers are using `vim.filetype` to guess the filetype for the +selected file. The guessing is done by inspecting the filename, the head of the +file(shebang) and the tail of the file (modeline). If you have trouble with +filetype detection you should read `:help vim.filetype`. + +We need to do it manually because we can't determine the filetype in the +traditional way: We don't do `bufload` and instead read the file asynchronously +with `vim.loop.fs_` and attach only a highlighter; otherwise the speed of the +previewer would slow down considerably. + +If you want to configure the `vim_buffer_` previewer (e.g. you want the line to wrap), do this: + +```lua +vim.api.nvim_create_autocmd("User", { + pattern = "TelescopePreviewerLoaded", + callback = function(args) + if args.data.filetype ~= "help" then + vim.wo.number = true + elseif args.data.bufname:match("*.csv") then + vim.wo.wrap = false + end + end, +}) +``` + +A data field is passed to the callback, which contains the filetype and the buffer name. + +```lua +{ + title: string, # preview window title + filetype: string, + bufname: string, +} +``` + +## Sorters + +| Sorters | Description | +| ---------------------------------- | --------------------------------------------------------------- | +| `sorters.get_fuzzy_file` | Telescope's default sorter for files | +| `sorters.get_generic_fuzzy_sorter` | Telescope's default sorter for everything else | +| `sorters.get_levenshtein_sorter` | Using Levenshtein distance algorithm (don't use :D) | +| `sorters.get_fzy_sorter` | Using fzy algorithm | +| `sorters.fuzzy_with_index_bias` | Used to list stuff with consideration to when the item is added | + +A `Sorter` is called by the `Picker` on each item returned by the `Finder`. It +returns a number, which is equivalent to the "distance" between the current +`prompt` and the `entry` returned by a `finder`. + +## Layout (display) + +Layout can be configured by choosing a specific `layout_strategy` and +specifying a particular `layout_config` for that strategy. +For more details on available strategies and configuration options, +see `:help telescope.layout`. + +Some options for configuring sizes in layouts are "resolvable". This means that +they can take different forms, and will be interpreted differently according to +which form they take. +For example, if we wanted to set the `width` of a picker using the `vertical` +layout strategy to 50% of the screen width, we would specify that width +as `0.5`, but if we wanted to specify the `width` to be exactly 80 +characters wide, we would specify it as `80`. +For more details on resolving sizes, see `:help telescope.resolve`. + +As an example, if we wanted to specify the layout strategy and width, +but only for this instance, we could do something like: + +``` +:lua require('telescope.builtin').find_files({layout_strategy='vertical',layout_config={width=0.5}}) +``` + +If we wanted to change the width for every time we use the `vertical` +layout strategy, we could add the following to our `setup()` call: + +```lua +require('telescope').setup({ + defaults = { + layout_config = { + vertical = { width = 0.5 } + -- other layout configuration here + }, + -- other defaults configuration here + }, + -- other configuration values here +}) +``` + +## Themes + +Common groups of settings can be set up to allow for themes. +We have some built in themes but are looking for more cool options. + +![dropdown](https://i.imgur.com/SorAcXv.png) + +| Themes | Description | +| --------------------- | ------------------------------------------------------------------------------------------- | +| `themes.get_dropdown` | A list like centered list. [dropdown](https://i.imgur.com/SorAcXv.png) | +| `themes.get_cursor` | [A cursor relative list.](https://github.com/nvim-telescope/telescope.nvim/pull/878) | +| `themes.get_ivy` | Bottom panel overlay. [Ivy #771](https://github.com/nvim-telescope/telescope.nvim/pull/771) | + +To use a theme, simply append it to a builtin function: + +```vim +nnoremap f :lua require'telescope.builtin'.find_files(require('telescope.themes').get_dropdown({})) +" Change an option +nnoremap f :lua require'telescope.builtin'.find_files(require('telescope.themes').get_dropdown({ winblend = 10 })) +``` + +Or use with a command: + +```vim +Telescope find_files theme=dropdown +``` + +Or you can configure it in the pickers table in `telescope.setup`: + +```lua +require('telescope').setup{ + defaults = { + -- ... + }, + pickers = { + find_files = { + theme = "dropdown", + } + }, + extensions = { + -- ... + } +} +``` + +Themes should work with every `telescope.builtin` function. If you wish to make +a theme, check out `lua/telescope/themes.lua`. + +## Vim Commands + +All `telescope.nvim` functions are wrapped in `vim` commands for easy access, +tab completions and setting options. + +```viml +" Show all builtin pickers +:Telescope + +" Tab completion +:Telescope | +:Telescope find_files + +" Setting options +:Telescope find_files prompt_prefix=🔍 + +" If the option accepts a Lua table as its value, you can use, to connect each +" command string, e.g.: find_command, vimgrep_arguments are both options that +" accept a Lua table as a value. So, you can configure them on the command line +"like so: +:Telescope find_files find_command=rg,--ignore,--hidden,--files prompt_prefix=🔍 +``` + +for more information and how to realize more complex commands please read +`:help telescope.command`. + +## Autocmds + +Telescope user autocmds: + +| Event | Description | +| ------------------------------- | ------------------------------------------------------- | +| `User TelescopeFindPre` | Do it before Telescope creates all the floating windows | +| `User TelescopePreviewerLoaded` | Do it after Telescope previewer window is created | +| `User TelescopeResumePost` | Do it after Telescope resume action is fully completed | + +## Extensions + +Telescope provides the capabilities to create & register extensions, which +improves telescope in a variety of ways. + +Some extensions provide integration with external tools, outside of the scope of +`builtins`. Others provide performance enhancements by using compiled C and +interfacing directly with Lua over LuaJIT's FFI library. + +A list of community extensions can be found in the +[Extensions](https://github.com/nvim-telescope/telescope.nvim/wiki/Extensions) +wiki. Always read the README of the extension you want to install, but here is a +general overview of how most extensions work. + +### Loading extensions + +To load an extension, use the `load_extension` function as shown in the example +below: + +```lua +-- This will load fzy_native and have it override the default file sorter +require('telescope').load_extension('fzy_native') +``` + +You may skip explicitly loading extensions (they will then be lazy-loaded), but +tab completions will not be available right away. + +### Accessing pickers from extensions + +Pickers from extensions are added to the `:Telescope` command under their +respective name. For example: + +```viml +" Run the `configurations` picker from nvim-dap +:Telescope dap configurations +``` + +They can also be called directly from Lua: + +```lua +-- Run the `configurations` picker from nvim-dap +require('telescope').extensions.dap.configurations() +``` + +## API + +For writing your own picker and for information about the API please read the +[Developers Documentation](developers.md). + +## Media + +- [What is Telescope? (Video)](https://www.twitch.tv/teej_dv/clip/RichDistinctPlumberPastaThat) +- [More advanced configuration (Video)](https://www.twitch.tv/videos/756229115) +- [telescope.nvim 0.1 reflection (Video)](https://www.youtube.com/watch?v=3WEAjCXFiiM) +- [Why Telescope? (Video)](https://www.youtube.com/watch?v=8SqFt5h2Lsg) +- [Telescope and Nvim 0.5 Intro (Video)](https://www.youtube.com/watch?v=guxLXcG1kzQ) + +## Contributing + +All contributions are welcome! Just open a pull request. +Please read [CONTRIBUTING.md](./CONTRIBUTING.md) + +## Related Projects + +- [fzf.vim](https://github.com/junegunn/fzf.vim) +- [denite.nvim](https://github.com/Shougo/denite.nvim) +- [vim-clap](https://github.com/liuchengxu/vim-clap) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/autoload/health/telescope.vim b/.config/nvim/pack/vendor/start/telescope.nvim/autoload/health/telescope.vim new file mode 100644 index 0000000..46cc928 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/autoload/health/telescope.vim @@ -0,0 +1,3 @@ +function! health#telescope#check() + lua require 'telescope.health'.check() +endfunction diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/earth b/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/earth new file mode 100644 index 0000000..aa624e1 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/earth @@ -0,0 +1,36 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓   ▓▓▓▓▓▓▓▓▓▓▓▒▒▒░  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▒▓▓▓▓▓▒▒   ░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓▒▒▓▒▒░   ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓ ░▓▓▓▒▒▒▒▒  ▒░ ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▒▓▓▓▓▓▓▒▒ ▒▓▒░░▒▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓░▓▓▓▒▒▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▓░░▒▓▓▓▓▓▓▓▓▓   ▒░░  ░ ▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▒▓▒▒▓▓▓▓▒▓▒▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▒▓▓▓▓▓▓▓▓ ▒░  ░▓  ░ ░ ▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▒▒▒▓▓▒░░▓▒▓▓▓▓▓▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓     ░▒▓      ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▒▒▓▒▒░░░▓▒▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░    ▓       ▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▒▒▒▒▒▒▒░▒▓▓▓▓▒▓▒▒▓▒▓▓▓▒▓▓▓▓▓▓▓▓▓▓▒▒▓▒▒░▓▓▓░▓▓▓▓▓▒▓    ░▒▓▒▓░       ▓▓▓▓▓▓ +▓▓▓▓▓▓ ▒▒▒▒░▒▒▒░░▒▓▓▒▓░▓▓▓░▒▓▓▓▓▓▓▓▓▒▓▓▒▒▒▒▒░░░▓▓ ▓▓░▓▓▓▒▒░  ▓ ░▒▓         ▓▓▓▓▓ +▓▓▓▓▓▓▒▒▒▒░▒▒▒░▒▓▒▒▓▓▓▓▓▓▒▓▓ ▓░▓▓▓▒▒▒▓▓▓▓▒░░░░▒▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒         ▓▓▓▓▓ +▓▓▓▓▓ ▒▒▒▓▓▒▒▒▒▒▓▓▒▒▓▓▓▓▓▓▓▒ ▓▓▓▓▓▓▒▒ ▓▓▒▓▒▒░▒ ▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▒░▓   ░░   ▒ ▓▓▓▓▓ +▓▓▓▓▓▒▒▒▒▒▒▒▒▒▓▒▓▓▒▓▓▓▓▓▓▒▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▒▒▒▓▓▒▓▓▓▓▓▓▓▓ ░▓▓▓▓▒▒░   ▒ ░   ▒░ ▓▓▓▓ +▓▓▓▓▓▒▒▒▒▒▒▒▒▒▓▓▒▓▓▓▒▓▓▓▓▒▓▓▓▓▒▓▒░ ▓▒▓▒▒▓▓▓▓▓▒▓▓▓▓▓▓▓▓  ▒ ▒░▒░░    ░ ▒░     ▓▓▓▓ +▓▓▓▓▓▒▒▒▒▒▒▓▒▒▒▒▒▒▒▒▓▒▒▓▓▓▓▓▓▓▒▓▓▓▒▓▓▓▓▓▒▒▓░▓▓▓▒░▒   ░▒  ░ ░    ▒    ░▒░    ▓▓▓▓ +▓▓▓▓▓▒▒▒▒▒░░▒▓▒▒▒▒▒▒▒▓ ▓▓▓▒▓▓▓▓▓▓ ▓▒░░▓▓░▒▓▓            ░   ░ ░▒ ░░░░▒░ ░░▒ ▓▓▓▓ +▓▓▓▓▓▒▒▒▒▓▒▒▒▒░▒░▒▓  ▓▒▓▓▒▓▓▓▓▓▓▓▓▓▓▓▒ ▓▓▓▓░          ▒▒▓▒░ ▒░░ ▒▓▓▓▒▒▒░▒ ▓ ▓▓▓▓ +▓▓▓▓▓ ▒░▒▓▒▒░░▒░░░▒▒▓ ░ ░▒▒▒▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▒ ▒▓   ▓▓  ░░░▒░▒   ▒▓▓▓▓▒░░    ▓▓▓▓▓ +▓▓▓▓▓▓▒▒░  ▒▓▒▒▒▒▓▒░ ▒ ░▒ ░▓▓▓▓▓▓▓▓▓▓▓▓░▒░░▓▓▓▓ ▓▓▓ ░▓▒░  ░ ░▒▒░▒▓▓░░░▒    ▓▓▓▓▓ +▓▓▓▓▓▓░▒▒▒▒ ▒  ▒▒░▓▒░▒ ▒░▒▓░▒▒▓▓▓▓▓▓▓▓▓▒▒░ ▒▓▓▒▓▓▓▓▓▓▓▓▓▒░▓▒ ▓▓▒▓▓▓▓▓▓▒░ ▒▓▓▓▓▓▓ +▓▓▓▓▓▓▓▒▒▒▒▒▒▒░▒▒▒░░▒▓░░▒▒░▒░▒░▒▓▓▓▒▒▒▒▓░ ░ ░       ▓▓▒▒▒░▓▓▓▓▓▓▓▓▓▓▓▓▒▒░ ▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▒▒░▒▒▒▒░▒▒░▒ ▒ ▓▓▓░▒▓░▒▓▓▒░░░░▒▒  ░░▓ ▒░ ▒░░▒░▓▓░▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒ ▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▒░▒▒▒▒▒░▒▒▒░▒▒▒▒  ▒▒░ ▒▒▒▓░▒▒   ▒▓▓░▒      ▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒ ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒░▒▒░▒░▒▒▓░▒▓▓░▒▒░▒░▒▓▒▓▓▓▓▒▒▓░▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▒▒▒▒ ▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒░▒▒▒▒░▒░░▓▒▓▒▒▓░▓▓░▓▓ ▓▒▓ ▒  ░▓ ▒░▓░▓▓▓▓▓▓▓▒▒▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓ ▒ ▒▒▒▒▒▒▒░▒░▒░░░▒▒░░░░ ▒ ░░▓▒░▓▓▓▓▓▒▓▓▓ ▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▒▒▒▒▒▒▓░▒▒░▒░▒▒░░▒▓▒▒▓▒▒▒▓▒░▓▒▒▓▓▓▓▓▓▓▓░░▓▓▓▓▓▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒▒░░▒▓▓▓▒▓▓▓▒▓▓▓▒▒▒▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒ ▒▒▒▒░░▒▒▒▓▒▒▓▒░▒▒▒▒░▒▒▓▓▒▒▓▓▒▓▓▓▓▓▓▓▓▓▓░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▒░▒▒▒▒▒▒▒▒▒▒▓▒▒▒▓▓▒▓▒▒▒▒▓▓▒▒▓▓▓▓▓▒▓░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▒▓▓▒▓▓▓▒░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▒▒▒▒▒▒▓▒▒▒▒▒▒▒▒▓▒░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/jupiter b/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/jupiter new file mode 100644 index 0000000..dac1487 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/jupiter @@ -0,0 +1,36 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  ░░░ ░    ░  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒ ▒▒▒▒▒▒▒░░ ▒ ░▒░ ░░▓▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░▒▓▒▒░▒▒ ▒▒▒░▒▒░▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒ ▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▒▒▓▓▒▓▒▓▓▓▓▓▓▓▓▒▓▒▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒   ▓ ░   ░▒▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▒▒▓▒▒▓▓▒▓▓▓▒▒▒▒▓▒▒▒▒▒▒▓▒▓▓▒▓▒▒▒▒▓▓▓▓▓▒▓▓▓▓▓▓▓▓▒▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▓▒▒▒▒▓▒▒▓▓▓▒▒▒▒▒▒▒▒▓░░░▒▒▒▒░░░░░▒▒▒░░░░░▒▒▒░░░▒░▒▒▒▒▒▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▒▒▓▓▓▓▓▓▒▓▓▓▓▒▒▓▓▒▒▒▓▓▒▒▒▒▓▓▓▒▓▓▓▓▓▒▓▒▓▓▓▓▓▓▓▓ ▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▓▓▓▒░▒▓▒▒▓▓▓▒▓▓▒▓▓▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▓▓▓▓▒░▓▒▓▓   ▒ ▓▓▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▒▒░▓▓░░░  ▒   ▒▒▓▓▒  ▓▓░▓░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▒▓▓▓▒▒▓▓░▒░ ▓▓▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓ ▓▓▓ +▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▓▓▓▓▒▓▒▓▓▒▒▒▒▒▒▒▒▒▒▒▒▓▓▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▒▓▓▓▓▓▒▒▓▓▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓░░▓▒▒▓▒▓ ▒▒▓▓▒▓▓▒▓▓▒▓▓▓▓▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▒▒▒▒▒▒▓▒▓▓▓▒▒▓▓▒▒    ░░▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▒▒▓▓▓▒▓▓▓▓▒      ░▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▒▓▓▓▒▓▒▓▓▓▓▒▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▓▓▓▓▓▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/mars b/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/mars new file mode 100644 index 0000000..2eb8cfd --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/mars @@ -0,0 +1,27 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒░▒   ▓▒ ░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░  ░░░▒▒ ░ ▒░  ░░░░░░░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░ ░░  ░ ░░▒▓░░░░░░░ ░░ ░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░  ░░░░░░░░ ░▒░▒░░░▒▒░░░░░░░░░ ░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░ ░░░░░░░░░░░░░░░▒▒▒▓▒▒▒▒▒▒▒░░░░░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░ ░░ ░  ░░░░░░░░░░ ░░░▒▒▒▒▒▒▒▓▒▒▒▒░▒▒░░░▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░ ░ ░░         ░  ░  ░ ░░▒▓▒▒▒▒▒▒▒▓▒▒░▒░▒▒░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░ ░░                  ░ ░░▒▒▒▒▒▓▒▒▓▒▒░░ ▒▒░ ░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░ ░                        ░░░▒▒▒▒▒▒▒▒▒░ ░░▒░░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                          ░▒░░▒▒▒▒▒▒░░░░░░░░░░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                        ░░ ░░░░░▒▒░▒░▒▒▒░░░░░ ░ ░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                        ░ ░▒▒ ░ ░▒░░░░░░░░░  ░▒░░ ░▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓                        ░░  ░░ ░░ ░░░░░░░ ░░░░  ░░  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓        ░           ░░ ░              ░     ▒░░░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                      ░░░░     ░░     ░░ ░░░░░ ▒░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                       ░░░             ░ ░░▒░ ░▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                        ░░  ░  ░       ░░░  ░▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░                       ░ ▒        ░  ░░░▒▒░▒▒▒░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░        ░     ░ ░    ▒  ░░░ ░  ░░░░▒░░▓░▒░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░        ░          ░░ ▒▓▒░▒▒▒ ░░░░▒▒░▒▒ ░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒░░ ░   ░░░          ░▒ ▒▒▒▓▒▒░░░░░░░░░▒░▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░ ░░░░░░░░░░ ░░░░ ▓▒▒▒▒▒▒▒▒▒▒░░░░░░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒░░░░░░▒▒▒▒▒░▒▒▒░░▒▒▓▓▓▓▒▓▒▒▒▒▒░▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒░   ░▒▒░░░░░▒▒░░▒▒▒▓▓▒▒▒▒▒▒░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░ ░ ░ ░░░░░░░░░▒▒░░░░░░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░░░░░░░ ░ ░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/mercury b/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/mercury new file mode 100644 index 0000000..fcad37d --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/mercury @@ -0,0 +1,36 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓            ░  ░░    ░   ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓    ░░            ░░   ░░  ░░ ░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓      ░░            ░   ░ ░░ ░ ░     ░   ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓    ░░ ░          ░ ░        ░░ ░  ░░░     ░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                 ░░    ░          ░░  ░  ░ ░  ░ ░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓             ░░     ░░░░      ░ ░  ░ ░░  ░ ░░░░ ░░░░  ▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓             ░░  ░     ░ ░░░ ░░ ░ ░░░    ░ ░░░░░░░ ░░░░░ ▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓ ░    ░   ░ ░░░  ░░░ ░░  ░░░░░░   ░░░░░  ░ ░ ░░ ░░░ ░░░░░░░ ▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓░   ░░ ░░  ░  ░  ░░░  ░░░ ░░ ░░  ░  ░░░░░░ ░░░ ░░░   ░░ ░░░░░░░▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓    ░░           ░░ ░   ░ ░░  ░░░░░░ ░░░░░░ ░    ░ ░  ░  ░░░░░ ░ ▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓ ░   ░ ░░░    ░  ░░░░ ░ ░ ░░░  ░░░░░░░░░░░░░░░░░░░ ░ ░ ░ ░░░░░░ ░░░▓▓▓▓▓▓ +▓▓▓▓▓▓    ░ ░     ░     ░░░░      ░   ░░░   ░ ░░░░░░░░░░░ ░░  ░░░  ░  ░ ░░▓▓▓▓▓▓ +▓▓▓▓▓▓ ░   ░  ░░░ ░   ░░░░░     ░   ░░  ░░   ░░░   ░░░░░░░░  ░░░░░░░░░░░░ ░▓▓▓▓▓ +▓▓▓▓▓░ ░  ░░        ░ ░░░ ░ ░░ ░  ░░░░░░░░ ░  ░░░ ░ ░░░░░░    ░░░ ░░░░░░░░  ▓▓▓▓ +▓▓▓▓▓   ░░░ ░░░░░  ░░  ░░░░░   ░░░░ ░░  ░ ░░░ ░░░░░░ ░ ░░░░ ░  ░░░░░░   ░░ ░▓▓▓▓ +▓▓▓▓▓ ░░░░░░░ ░░░     ░░░░ ░░░░░░░░     ░░░ ░      ░   ░░░░ ░░░░░░░░░░  ░░░ ▓▓▓▓ +▓▓▓▓ ░ ░░░░░ ░░░   ░░░░░ ░ ░░░░░░░░ ░   ░  ░  ░ ░░ ░░  ░░░░░░░░░░░░░░░░░░░░░▓▓▓▓ +▓▓▓▓░░░░░░░░░░░░░░░░░   ░    ░░ ░░░░░░  ░      ░ ░░░     ░ ░░░░░░░░░░░░░░░░ ▓▓▓▓ +▓▓▓▓ ░  ░░ ░░  ░░░░░░  ░ ░░    ░ ░░░░░         ░░ ░░░░░░░░░ ░░░░░░░░░░░░░ ░ ▓▓▓▓ +▓▓▓▓▓ ░░░       ░░░░░░ ░  ░     ░  ░░░░░ ░░░ ░░░░ ░░░░░░░░ ░░░░░░░░░░░░░░ ░░▓▓▓▓ +▓▓▓▓▓ ░░░░░░ ░  ░░░ ░░ ░ ░░░  ░     ░░░░ ░ ░░░░  ░░░ ░  ░░░  ░░░░░░░░   ░░  ▓▓▓▓ +▓▓▓▓▓░░░  ░░ ░░  ░░░ ░░░░░░░░░░░   ░  ░░░ ░░ ░ ░░░░░    ░ ░░ ░░ ░░░ ░ ░░ ░░░▓▓▓▓ +▓▓▓▓▓▓░░  ░░ ░  ░░░░░░░░░░░░░░░░░░░░░░░ ░  ░░░░       ░░░  ░░░░░░░░ ░░░ ░░ ▓▓▓▓▓ +▓▓▓▓▓▓▓  ░░ ░░░░░░░ ░ ░░░░ ░░░░░ ░░░░░   ░░ ░░░    ░  ░░ ░░░ ░ ░  ░░░░  ░░▓▓▓▓▓▓ +▓▓▓▓▓▓▓  ░░░░░░░░░░░░░░░░░░░ ░   ░░░░░░ ░  ░░ ░    ░  ░     ░  ░░░░  ░░░░░▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓ ░░░░ ░░░░░      ░░░░░  ░░         ░ ░ ░                  ░ ░ ░ ░▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓ ░░░░  ░░░░░░░░░░░░░   ░           ░░             ░    ░░     ░▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓      ░░░░  ░  ░░                ░ ░ ░  ░░        ░      ░░ ▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓    ░░░░░░░░        ░                                 ░░ ▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓   ░░░░░░░                     ░░░    ░              ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓       ░           ░         ░  ░     ░          ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░                      ░  ░    ░          ░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░ ░                      ░           ░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓    ░   ░        ░              ░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                   ░  ░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/moon b/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/moon new file mode 100644 index 0000000..2943e28 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/moon @@ -0,0 +1,35 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/neptune b/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/neptune new file mode 100644 index 0000000..9c2954e --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/neptune @@ -0,0 +1,36 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓       ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░▒▒▒░░░▒▒░░▒░▒░▒░░▒▒░░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░░░░░░░░░░░░░░▒░░░░▒░▒░░▒▒▒░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒░░░░   ░░░░░░░░░░░░▒░▒░▒░░░▒▒░░▒░▒░░  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒░░▒░▒░▒░░░  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒░▒░▒░░░░▒▒▒░░░░░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒░░░░░░░░░░░░░      ░░░░░░░░░░░░░░░░░▒░░▒▒░▒░░░░   ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▒░░░▒░▒░░░░░░                 ░ ░░░░░░░░░░░▒░▒░░░░░░░   ▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓░▒░▒░░░░░                           ░░░░░░▒░░▒▒░░░▒░░░░░  ▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓ ▒▒▒░░░░░░                              ░░░░▒░▒░▒▒░▒▒░░░░░   ▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓░▒▒░░░░░░                                ░ ░░░░░░▒░░▒░░░░░░░   ▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓ ▒▒▒░░░░░                                   ░░░░░░▒░░▒░░▒▒░░░░   ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▒▒░░░░░░                                    ░░░░░▒░░░▒░░▒░▒░░░   ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓░░░▒░░░                                       ░░░░░▒░░▒░▒░▒░░░░░   ▓▓▓▓▓▓▓ +▓▓▓▓▓▓▒░▒▒░░                         ░ ░░░░░░░░░     ░░░░░░░▒▒░░░▒░░░    ▓▓▓▓▓▓▓ +▓▓▓▓▓░▒░▒░░░░                          ░░░░░░░░░░░░░░░░░░░▒░▒░▒░░░░░░░░  ▓▓▓▓▓▓▓ +▓▓▓▓▓▒▒▒░░░░░                              ░ ░░░░░░░░░░░ ░▒▒░▒▒▒░░░░░░   ▓▓▓▓▓▓▓ +▓▓▓▓▓▒▒▒▒░░░░                                     ░░░░░░▒░▒▒▒▒▒░░░░░░    ▓▓▓▓▓▓▓ +▓▓▓▓▓░░▒░░░░░░                                   ░░░░░░  ░▒░▒░░░▒░░░░░   ▓▓▓▓▓▓▓ +▓▓▓▓▓░░▒░░░░░░                                   ░░░░░▒▒░░░░░▒▒░▒░▒░░░   ▓▓▓▓▓▓▓ +▓▓▓▓▓ ▒▒▒▒░░░░░░                                ░░░░░░░░░░░▒░▒░░▒░░░░░   ▓▓▓▓▓▓▓ +▓▓▓▓▓▓░▒░░▒░░░░░                            ░ ░░░░░░░░░░░░▒░▒░░░░░░░░   ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓░▒▒░▒▒░░░░░ ░      ░     ░   ░   ░░░░░░░░░░░░▒░░░░░░▒░░░░░░░░     ▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓░▒▒░▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▒░░░░░░░░░▒░░▒░░░░░░░░░   ▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓ ▒▒▒░▒ ░░░░░░░░░░░░░░░░░░░▒░░░░░░░░░▒░░░▒░░░▒░▒░▒░░░░░▒░░ ░    ▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░▒░░░▒░░░░░░░░░░░▒░░░░░░░░░░░░▒░▒░░░░░░░░░░    ▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓ ▒░░░░░░░░░░░░░░░░░░▒░░░░░░░░░░░▒░▒░░▒░░░▒░▒░░░░░░░░░ ░░   ▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓░▒░░░░▒░░░░░░░░░░░░░░░░░░░░░░░▒▒▒░░░░░▒░░░░░░░░░░░░░    ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓░░▒░░░▒░░░░░░▒░░░░░░░░░░▒░░░░░░░▒░░░░░▒░░░░░░ ░      ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░░░░▒░░░▒░░░░░░▒▒░░░░▒░▒░░░▒▒▒░░░░░░░░░  ░     ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░░░▒░░░░░░░░░░░░░░░░░░░░░░▒░░░░░░░░░░      ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░▒░░░░░░░░░▒░░░░░░▒░░▒░░▒░░░░░░░ ░    ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░ ░░░░░░░░░░░░░░░░░░░▒░░░░░░░       ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓     ░░░░░░░░░░░░░░░░        ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                    ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/pluto b/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/pluto new file mode 100644 index 0000000..cfcde6f --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/pluto @@ -0,0 +1,39 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▒▒▒▒▒▒▒░░░░ ░░░▒░▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▒▒▒▒░░ ░░░░░░ ░░░▒▒▒░▒▒▒▒▒▒▒▒▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▒▒▒  ░░  ░     ░░░ ░ ░▒▒▒▒▒▒▒▓▓▒▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▓▒▒▒░           ░        ░░▒▒▒▒▒▒▓▓▓▓▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒░                  ░░░▒▒░▒▒▒▒▒▓▓▓▒▓▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▒▒▒░░     ▒   ░       ░░░░░░▒▒▒▒▒▓▓▓▓▓▓▓▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▒▒▒░░░▒ ░░▒▒▒░░      ░░░░░░▒▒▒▒▒▓▓▓▓▓▓▓▒▒▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▒▒░▒▒▒▒▒▒▒▒▒▒░░░░░░▒▒░▒▒▒▒▒▒▒▒▓▓▒▓▓▓▓▓▓▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▒▒▓░▒▒▒▒▒▒▒▒░░▒▒▒▒▒░▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▒▓▓▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▒▒▒▒▓▒▒▒▒░░▒ ░░▒▒▒▒▒▓▓▒▓▒▒▒▒▒▒▓▓▓▓▓▓▓▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▒▓▓▓▓▒▓▒▒▒▒▒       ░▒▒▒▓▓▒▓▓▒▒▒▒▒▓▒▒▓▓▓▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▓▓▓▓▓▒▓▓▒▒▒▒ ░         ░▒▓▒▒▒▒▒▒▒░▒▓▒▒▓▓▓▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▓▒▓▓▓▓▒▒▓▓▒░             ░▒░░░▒▒▒▒▒▒▒▓▓▓▒▒▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒ ▒▓▓▒▒▓▓▓▓▓░░░░          ░░░░ ░▒▒▒▒░▓▒▒▓▓▓▓  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓    ░░▒ ▒▓▓▒▓▓▒░         ░░░░░░░▒▒▒▒▒▒▓▓▓▒▒  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒      ▒▒▒ ▒▒▒▒░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▓▓▓▒▒  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒          ▒▓▒▒▒░░░░░▒▒▒░▒▒▒▒▒▒▒▓▓▓▓▓▒▒  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓    ▒ ▒   ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓░░▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒  ▒       ▓▓▓▓▒▒▒▒▒▒▒▓▓▓▓▓▓░      ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▒░▓▒▓▒▓▓▓▓▓▓▓▓▓▓▓▒▒▒░▒░     ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▒▒▒▒▒    ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▓▓▓▓▓▓▒▒▓▒░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/saturn b/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/saturn new file mode 100644 index 0000000..68a7ffa --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/saturn @@ -0,0 +1,36 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▒▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒  ▒ ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▒▒░▒▒▒▒▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒ ▒▒▓▒▓▓▒▓░▓░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▒▒    ░▒▒▒ ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░▒▒  ▓▓  ▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░▒ ▓▓▓▓▓▓  ▒▓▓▓▓▒▒▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒ ▓▓▓▓▓▓▓▒ ▒▒▓▓▒▓▓▓▓▓▓▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░ ▒▒ ▓▓▓▓▓▓▓▒ ▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▓▒▒ ▒▓▓▒▒▒▓▒░▓▒▓▒▓▒▓░▒▓▓▓▓▓▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒░▒▒▒▒▒▒▒▒▒▒▒░▓▓▓▓▓▓▓▓▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▒▒▒▒▒▒▒░░▒▒▒▒▒▒▒▒▒▒▒░▒▓▓▓▓▓▓▓▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒  ▓▒▒▒ ▒░░░░░▒▒▒▒▒▒▒▒▒▒░░▓▓▓▓▓▓▓▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░  ▒▓▒▒▒▒░▒░░░░▒▒▒▒▒▒▒▒▒▒▒░▒▓▓▓▓▓▓▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  ▒  ▓▓▒▒▒▒▒░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒     ▓▓▒▒▒░░░░░░░▒▒▒▒▒▒▒▒▒▒▓▓▓▓▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒ ▓  ░▒▒▓▒▒▒▓▒░░░░░▒▒▒▒▒▒▒▓▒▓▒▓▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▓▓▒  ▒▓▒▒▒▒▒░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓░░▒▒▓▒▒▒ ░▒▒▒▒░▒▒▒▒▒▓▒▒▒▒▒▒░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▒▒░  ▓ ░▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▓▓▒▓   ▓▒▒▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓░▓░  ▓▓▓▓▓▒▒▒░▒▓▒▒▒▒▓▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▒▒▒ ▒▓▒▒▒▓▒▒▒▒▒▓▒▒▒▒▒▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▒▒▒▒▒▓▒▓▓▒▒▒▒░░▓▓▓▓▓ ▓▓ ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▒▒▒▒▒▒▓▓▒▓▒ ▓▓▓▓   ▓▒▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▓      ▒▒░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒   ░▒▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▒▒▒▓▓▓▒▒░ ░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒░▒▒▒▒▒░▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▒▒▒▒░▒▒ ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/uranus b/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/uranus new file mode 100644 index 0000000..f5a8b36 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/uranus @@ -0,0 +1,39 @@ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓       ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  ░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░▒▓▓▓▓▓▓▒▒░     ░░▒▒▓▓▓▓░ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░▒▓▓▓▓▓▓▒░              ▒▒▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▓▓▓▓▓▓▓▓░                 ░▒▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▓▒▓▓▓▒▒▓▓▓░                   ░▒▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▒▒▓▒▒▒▒▒▒▒▓ ▓▓▓▓▓▓▓▓▓           ▒▓▓▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▓▓▒▒▒▒▒▒▒░░░░░  ▓▓▓▓▓▓▓▓▓▓          ▒▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▓▓▒▒▒░▒░░░░░░    ▓▓▓▓▓▓▓▓▓▓          ▒▓▓░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▓▓▓▒▒▒░░░░░░░      ▓▓▓▓▓▓▓▓▓▓▓       ▒▓▓▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▓▓▒▒▒▒░░░░░░░         ▓▓▓▓▓▓▓▓▓      ▒▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▓▓▒▒▒░░ ░░                ▓▓▓▓    ░▒▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒▒▒▒▒▒░░░                    ░▒▒▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒░░▒▒▒░░░░░            ░░░░░░░▒▒▒▒▓▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒░░  ░▒▒░░░░░░            ░░░░░░░▒▒▒▓▓▓▓▓░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒░░   ░▒▒░░░░    ░    ░  ░░░░░░▒▒▒▓▓▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░     ░▒░░░░░░░ ░░░░░░░░░░░░▒▒▒▒▓▓▒▓▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒░      ░▒░░░░░░░░░░░░░░░░▒▒▒▒▒▓▓▓▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒░       ░░▒▒▒▒░▒░▒▒▒▒▒▒▒▒▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒░         ░▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▒▒▒░░         ░░▒▒▒▒▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░▒▒▒▒▒▒▒░░▒▒▒▒▒▒▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ +▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ + diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/venus b/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/venus new file mode 100644 index 0000000..b95aff1 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/data/memes/planets/venus @@ -0,0 +1,35 @@ +                          ▓   ▓  ░░        ░░░░ ▓  ▓                             +                           ▒   ░        ░░░░░░  ░   ▒                            +                  ▓ ▓▓ ░  ░      ░░░     ░  ░░   ░ ░ ░  ▒    ▓                   +                ▓   ░░░  ░      ░     ░ ░             ░░   ░                     +                 ▓ ░░░   ░      ░  ░ ░ ░         ░ ░  ▒ ░ ░ ░░   ▓▓              +              ▓    ░  ░░ ░       ░░   ░                        ░                 +             ▓░  ░░ ░ ░ ░  ░░  ░  ░ ░░  ░    ░  ░     ░          ▒               +          ▓▓ ░   ░  ░    ░  ░ ░    ░      ░ ░ ░    ░     ░ ░░ ░                  +          ▓  ░  ░  ░░  ░ ▒  ░ ░░░░        ▒  ░░ ░  ░░  ░░   ░         ▓          +           ░ ░  ░ ░░░   ░░  ░  ▒▒    ░  ░ ░   ░             ▒ ░  ░  ░            +        ▓     ░ ░ ░    ░       ░░      ░░▒     ░░▒▒          ░ ░  ░    ▒         +        ▓░   ░    ░  ░     ░  ░   ░   ░             ░  ░░       ▒  ░   ▓         +         ░     ░ ░    ░ ░  ░            ░░      ░  ░░░░░         ░               +        ░  ░     ░  ░░    ░ ░░   ░    ░ ░     ░░  ░ ▒  ░ ░  ░ ░  ░░    ░         +    ▓                   ░   ░ ▓░     ░           ░ ░         ░     ░  ░░         +    ▓ ▓░          ░  ░ ░░    ░  ░ ░  ░░░        ░░░             ░░               +       ▒     ░░ ░ ░  ░░  ░ ░ ░░           ░        ░    ░           ░ ░  ▓       +                  ░   ░░░░ ░  ░ ░  ░               ░                ░░   ▓       +        ░ ░      ░     ░    ░           ░     ░         ░           ░░           +     ▓ ▓     ░  ░ ░         ░            ░          ░      ░ ░          ▓        +         ░        ░    ░        ░    ░   ░         ░  ░                ▒         +        ▓ ░ ░  ░░   ░  ░    ░   ░  ░░   ░       ░                      ▓         +        ▓    ░  ░          ░   ░░ ░  ░░  ░ ░   ░░ ░              ░               +              ░  ░░     ░ ░░          ░   ░   ░░                ░   ░            +             ░    ░  ░  ░ ░    ░    ░ ░ ░░░░ ░                                   +             ░ ░       ░ ░      ░ ░░  ░   ░ ░   ░       ░░        ░▓             +              ▒░░       ░           ░        ░   ░   ░          ░░  ▓            +                ░                    ░░        ░    ░░ ░       ░                 +                  ▒ ░         ░        ░   ░      ░       ░  ░                   +                    ▒  ░ ░        ░    ░  ░     ░           ▓                    +                    ▓  ░░  ░        ░░  ░     ░  ░  ░   ░                        +                     ▓▓    ▒░      ░           ░   ░░   ▓                        +                              ▓  ░░░░       ░░   ▓                               + + diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/developers.md b/.config/nvim/pack/vendor/start/telescope.nvim/developers.md new file mode 100644 index 0000000..fcbcc3a --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/developers.md @@ -0,0 +1,418 @@ +# Developers + +- [Introduction](#introduction) +- [Guide to your first Picker](#guide-to-your-first-picker) + - [Requires](#requires) + - [First Picker](#first-picker) + - [Replacing Actions](#replacing-actions) + - [Entry Maker](#entry-maker) + - [Oneshot job](#oneshot-job) + - [Previewer](#previewer) + - [More examples](#more-examples) + - [Bundling as Extension](#bundling-as-extension) +- [Technical](#technical) + - [picker](#picker) + - [finders](#finders) + - [actions](#actions) + - [previewers](#previewers) + +## Introduction + +So you want to develop your own picker and/or extension for telescope? Then you +are in the right place! This file will first present an introduction on how to +do this. After that, this document will present a technical explanation of +pickers, finders, actions and the previewer. Should you now yet have an idea of +the general telescope architecture and its components, it is first recommend to +familiarize yourself with the architectural flow-chart that is provided in +vim docs (`:h telescope.nvim`). You can find more information in specific help +pages and we will probably move some of the technical stuff to our vim help docs +in the future. + +This guide is mainly for telescope so it will assume that you already have some knowledge of the Lua +programming language. If not then you can find information for Lua here: +- [Lua 5.1 Manual](https://www.lua.org/manual/5.1/) +- [Getting started using Lua in Neovim](https://github.com/nanotee/nvim-lua-guide) + +## Guide to your first Picker + +To guide you along the way to your first picker we will open an empty lua +scratch file, in which we will develop the picker and run it each time using +`:luafile %`. Later we will bundle this file as an extension. + +### Requires + +The most important includes are the following modules: +```lua +local pickers = require "telescope.pickers" +local finders = require "telescope.finders" +local conf = require("telescope.config").values +``` + +- `pickers`: main module which is used to create a new picker. +- `finders`: provides interfaces to fill the picker with items. +- `config`: `values` table which holds the user's configuration. +So to make it easier we access this table directly in `conf`. + +### First Picker + +We will now make the simplest color picker. (We will approach this example step by step, +you will still need to have the previous requires section above this code.) + +```lua +-- our picker function: colors +local colors = function(opts) + opts = opts or {} + pickers.new(opts, { + prompt_title = "colors", + finder = finders.new_table { + results = { "red", "green", "blue" } + }, + sorter = conf.generic_sorter(opts), + }):find() +end + +-- to execute the function +colors() +``` + +Running this code with `:luafile %` should open a telescope picker with the entries `red`, +`green`, `blue`. Selecting a color and pressing enter will open a new file. In this case +it's not what we want, so we will address this after explaining this snippet. + +We will define a new function `colors` which accepts a table `opts`. This is good +practice because now the user can change how telescope behaves by passing in their +own `opts` table when calling `colors`. + +For example the user can pass in a configuration in `opts` which allows them to change +the theme used for the picker. To allow this, we make sure to pass the `opts` table +as the first argument to `pickers.new`. The second argument is a table +which defines the default behavior of the picker. + +We have defined a `prompt_title` but this isn't required. This will default to use +the text `Prompt` if not set. + +`finder` is a required field that needs to be set to the result of a `finders` +function. In this case we take `new_table` which allows us to define a static +set of values, `results`, which is an array of elements, in this case our colors +as strings. It doesn't have to be an array of strings, it can also be an array of +tables. More on this later. + +`sorter` on the other hand is not a required field but it's good practice to +define it, because the default value will set it to `empty()`, meaning no sorter +is attached and you can't filter the results. Good practice is to set the sorter +to either `conf.generic_sorter(opts)` or `conf.file_sorter(opts)`. + +Setting it to a value from `conf` will respect the user's configuration, so if a user has set-up +`fzf-native` as the sorter then this decision will be respected and the `fzf-native` sorter +will be attached. It's also suggested to pass in `opts` here because the sorter +could make use of it. As an example the fzf sorter can be configured to be case +sensitive or insensitive. A user can set-up a default behavior and then alter +this behavior with the `opts` table. + +After the picker is defined you need to call `find()` to actually start the +picker. + +### Replacing Actions + +Now calling `colors()` will result in the opening of telescope with the values: +`red`, `green` and `blue`. The default theme isn't optimal for this picker so we +want to change it and thanks to the acceptance of `opts` we can. We will replace +the last line with the following to open the picker with the `dropdown` theme. + +```lua +colors(require("telescope.themes").get_dropdown{}) +``` + +Now let's address the issue that selecting a color opens a new buffer. For that +we need to replace the default select action. The benefit of replacing rather than +mapping a new function to `` is that it will respect the user's configuration. So +if a user has remapped `select_default` to another key then this decision will +be respected and it works as expected for the user. + +To make this work we need more requires at the top of the file. + +```lua +local actions = require "telescope.actions" +local action_state = require "telescope.actions.state" +``` + +- `actions`: holds all actions that can be mapped by a user. We also need it to + access the default action so we can replace it. Also see `:help + telescope.actions` + +- `action_state`: gives us a few utility functions we can use to get the + current picker, current selection or current line. Also see `:help + telescope.actions.state` + +So let's replace the default action. For that we need to define a new key value +pair in our table that we pass into `pickers.new`, for example after `sorter`. + +```lua + attach_mappings = function(prompt_bufnr, map) + actions.select_default:replace(function() + actions.close(prompt_bufnr) + local selection = action_state.get_selected_entry() + -- print(vim.inspect(selection)) + vim.api.nvim_put({ selection[1] }, "", false, true) + end) + return true + end, +``` + +We do this by setting the `attach_mappings` key to a function. This function +needs to return either `true` or `false`. If it returns false it means that only +the actions defined in the function should be attached. In this case it would +remove the default actions to move the selected item in the picker, +`move_selection_{next,previous}`. So in most cases you'll want to return `true`. +If the function does not return anything then an error is thrown. + +The `attach_mappings` function has two parameters, `prompt_bufnr` is the buffer number +of the prompt buffer, which we can use to get the pickers object and `map` is a function +we can use to map actions or functions to arbitrary key sequences. + +Now we are replacing `select_default` the default action, which is mapped to `` +by default. To do this we need to call `actions.select_default:replace` and +pass in a new function. + +In this new function we first close the picker with `actions.close` and then +get the `selection` with `action_state`. It's important +to notice that you can still get the selection and current prompt input +(`action_state.get_current_line()`) with `action_state` even after the picker is +closed. + +You can look at the selection with `print(vim.inspect(selection))` and see that it differs from our input +(string), this is because internally we pack it into a table with different +keys. You can specify this behavior and we'll talk about that in the next +section. Now all that is left is to do something with the selection we have. In +this case we just put the text in the current buffer with `vim.api.nvim_put`. + +### Entry Maker + +Entry maker is a function used to transform an item from the finder to an +internal entry table, which has a few required keys. It allows us to display +one string but match something completely different. It also allows us to set +an absolute path when working with files (so the file will always be found) +and a relative file path for display and sorting. This means the relative file +path doesn't even need to be valid in the context of the current working directory. + +We will now try to define our entry maker for our example by providing an +`entry_maker` to `finders.new_table` and changing our table to be a little bit +more interesting. We will end up with the following new code for `finders.new_table`: + +```lua + finder = finders.new_table { + results = { + { "red", "#ff0000" }, + { "green", "#00ff00" }, + { "blue", "#0000ff" }, + }, + entry_maker = function(entry) + return { + value = entry, + display = entry[1], + ordinal = entry[1], + } + end + }, +``` + +With the new snippet, we no longer have an array of strings but an array of +tables. Each table has a color name and the color's hex value. + +`entry_maker` is a function that will receive each table and then we can set the +values we need. It's best practice to have a `value` reference to the +original entry, that way we will always have access to the complete table in our +action. + +The `display` key is required and is either a string or a `function(tbl)`, +where `tbl` is the table returned by `entry_maker`. So in this example `tbl` would +give our `display` function access to `value` and `ordinal`. + +If our picker will have a lot of values it's suggested to use a function for `display`, +especially if you are modifying the text to display. This way the function will only be executed +for the entries being displayed. For an example of an entry maker take a look at +`lua/telescope/make_entry.lua`. + +A good way to make your `display` more like a table is to use a `displayer` which can be found in +`lua/telescope/pickers/entry_display.lua`. A simpler example of `displayer` is the +function `gen_from_git_commits` in `make_entry.lua`. + +The `ordinal` is also required, which is used for sorting. As already mentioned +this allows us to have different display and sorting values. This allows `display` +to be more complex with icons and special indicators but `ordinal` could be a simpler +sorting key. + +There are other important keys which can be set, but do not make sense in the +current context as we are not dealing with files: +- `path`: to set the absolute path of the file to make sure it's always found +- `lnum`: to specify a line number in the file. This will allow the + `conf.grep_previewer` to show that line and the default action to jump to + that line. + +### Previewer + +We will not write a previewer for this picker because it isn't required for +basic colors and is a more advanced topic. It's already well documented in `:help +telescope.previewers` so you can read this section if you want to write your +own `previewer`. If you want a file previewer without columns you should +default to `conf.file_previewer` or `conf.grep_previewer`. + +### Oneshot Job + +The `oneshot_job` finder can be used to have an asynchronous external process which will +find results and call `entry_maker` for each entry. An example usage would be +`find`. + +```lua +finder = finders.new_oneshot_job({ "find" }, opts ), +``` + +### More examples + +A good way to find more examples is to look into the [lua/telescope/builtin](https://github.com/nvim-telescope/telescope.nvim/tree/master/lua/telescope/builtin) +directory which contains all of the builtin pickers. Another way to find more examples +is to take a look at the [extension wiki page](https://github.com/nvim-telescope/telescope.nvim/wiki/Extensions) +as this provides many extensions people have already written which use these concepts. + +If you still have any questions after reading this guide please feel free to ask us for +more information on [gitter](https://gitter.im/nvim-telescope/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) +and we will happily answer your questions and hopefully allow us to improve this guide. You can also +help us to improve this guide by sending a PR. + +### Bundling as extension + +If you now want to bundle your picker as extension, so it is available as +picker via the `:Telescope` command, the following has to be done. + +Structure your plugin as follows, so it can be found by telescope: + +``` +. +└── lua + ├── plugin_name # Your actual plugin code + │ ├── init.lua + │ └── some_file.lua + └── telescope + └── _extensions # The underscore is significant + └─ plugin_name.lua # Init and register your extension +``` + +The `lua/telescope/_extensions/plugin_name.lua` file needs to return the +following: (see `:help telescope.register_extension`) + +```lua +return require("telescope").register_extension { + setup = function(ext_config, config) + -- access extension config and user config + end, + exports = { + stuff = require("plugin_name").stuff + }, +} +``` + +The setup function can be used to access the extension config and setup +extension specific global configuration. You also have access to the user +telescope default config, so you can override specific internal function. For +example sorters if you have an extension that provides a replacement sorter, +like +[telescope-fzf-native](https://github.com/nvim-telescope/telescope-fzf-native.nvim). + +The exports table declares the exported pickers that can then be accessed via +`Telescope plugin_name stuff`. If you only provide one export it is suggested +that you name the key like the plugin, so you can access it with `Telescope +plugin_name`. + + +## Technical + +### Picker + +This section is an overview of how custom pickers can be created and configured. + +```lua +-- lua/telescope/pickers.lua +Picker:new{ + prompt_title = "", + finder = FUNCTION, -- see lua/telescope/finders.lua + sorter = FUNCTION, -- see lua/telescope/sorters.lua + previewer = FUNCTION, -- see lua/telescope/previewers/previewer.lua + selection_strategy = "reset", -- follow, reset, row + border = {}, + borderchars = {"─", "│", "─", "│", "┌", "┐", "┘", "└"}, + default_selection_index = 1, -- Change the index of the initial selection row +} +``` + +### Finders + +```lua +-- lua/telescope/finders.lua +Finder:new{ + entry_maker = function(line) end, + fn_command = function() { command = "", args = { "ls-files" } } end, + static = false, + maximum_results = false +} +``` + +### Actions + +#### Overriding actions/action_set + +How to override what different functions / keys do. + +TODO: Talk about what actions vs actions sets are + +##### Relevant Files + +- `lua/telescope/actions/init.lua` + - The most "user-facing" of the files, which has the builtin actions that we provide +- `lua/telescope/actions/set.lua` + - The second most "user-facing" of the files. This provides actions that are consumed by several builtin actions, which allows for only overriding ONE item, instead of copying the same configuration / function several times. +- `lua/telescope/actions/state.lua` + - Provides APIs for interacting with the state of telescope from within actions. + - These are useful for writing your own actions and interacting with telescope +- `lua/telescope/actions/mt.lua` + - You probably don't need to look at this, but it defines the behavior of actions. + +##### `:replace(function)` + +Directly override an action with a new function + +```lua +local actions = require('telescope.actions') +actions.select_default:replace(git_checkout_function) +``` + +##### `:replace_if(conditional, function)` + +Override an action only when `conditional` returns true. + +```lua +local action_set = require('telescope.actions.set') +action_set.select:replace_if( + function() + return action_state.get_selected_entry().path:sub(-1) == os_sep + end, function(_, type) + -- type is { "default", "horizontal", "vertical", "tab" } + local path = actions.get_selected_entry().path + action_state.get_current_picker(prompt_bufnr):refresh(gen_new_finder(new_cwd), { reset_prompt = true}) + end +) +``` + +##### `:replace_map(configuration)` + +```lua +local action_set = require('telescope.actions.set') +-- Use functions as keys to map to which function to execute when called. +action_set.select:replace_map { + [function(e) return e > 0 end] = function(e) return (e / 10) end, + [function(e) return e == 0 end] = function(e) return (e + 10) end, +} +``` + +### Previewers + +See `:help telescope.previewers` diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/doc/secret.txt b/.config/nvim/pack/vendor/start/telescope.nvim/doc/secret.txt new file mode 100644 index 0000000..e872ca4 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/doc/secret.txt @@ -0,0 +1,32 @@ +================================================================================ + *telescope.theprimeagen* + +To The Viewers: ~ + +Oh why hello, I didn't see you there. So nice of you to join us. The Primeagen +must have sent you here. + +The places you want to look for help are: (you can do `:help ` below) + - |telescope.nvim| + - |telescope.setup| + - |telescope.builtin| + - |telescope.layout| + - |telescope.actions| + +I hope you enjoy telescope & Neovim. May your programming always be fun and +your vimming be quick. + + + +To The Primeagen: ~ + +Cyrnfr ernq guvf uryc znahny orsber pnyyvat zr ng 3 NZ jvgu gryrfpbcr +rzretrapvrf. V xabj ynfg gvzr jr fnirq gur ragver fgernzvat vaqhfgel, ohg +V unir n lbhat fba jub xrrcf zr hc ng avtug nyy ol uvzfrys. OGJ, unir lbh +pbafvqrerq fraqvat culfvpny QIQf sbe znkvzhz dhnyvgl naq rneyl npprff gb arj +pbagrag? Vg frrzf yvxr vg pbhyq or n cerggl pbby vqrn. + +#FunzryrffFrysCebzbgvba: uggcf://tvguho.pbz/fcbafbef/gwqrievrf + + + vim:tw=78:ts=8:ft=help:norl: diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/doc/telescope.txt b/.config/nvim/pack/vendor/start/telescope.nvim/doc/telescope.txt new file mode 100644 index 0000000..bb43329 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/doc/telescope.txt @@ -0,0 +1,4228 @@ +================================================================================ +INTRODUCTION *telescope.nvim* + +Telescope.nvim is a plugin for fuzzy finding and neovim. It helps you search, +filter, find and pick things in Lua. + +Getting started with telescope: + 1. Run `:checkhealth telescope` to make sure everything is installed. + 2. Evaluate it is working with `:Telescope find_files` or `:lua + require("telescope.builtin").find_files()` + 3. Put a `require("telescope").setup()` call somewhere in your neovim config. + 4. Read |telescope.setup| to check what config keys are available and what + you can put inside the setup call + 5. Read |telescope.builtin| to check which builtin pickers are offered and + what options these implement + 6. Profit + +The below flow chart illustrates a simplified telescope architecture: +┌───────────────────────────────────────────────────────────┐ +│ ┌────────┐ │ +│ │ Multi │ ┌───────+ │ +│ │ Select │ ┌───────┐ │ Entry │ │ +│ └─────┬──* │ Entry │ ┌────────+ │ Maker │ │ +│ │ ┌───│Manager│────│ Sorter │┐ └───┬───* │ +│ ▼ ▼ └───────* └────────┘│ │ │ +│ 1────────┐ 2───┴──┐ │ │ +│ ┌─────│ Picker │ │Finder│◀────┘ │ +│ ▼ └───┬────┘ └──────* │ +│ ┌────────┐ │ 3────────+ ▲ │ +│ │Selected│ └───────│ Prompt │─────────┘ │ +│ │ Entry │ └───┬────┘ │ +│ └────────* ┌───┴────┐ ┌────────┐ ┌────────┐ │ +│ │ ▲ 4─────────┐│ Prompt │ │(Attach)│ │Actions │ │ +│ ▼ └──▶ │ Results ││ Buffer │◀─┤Mappings│◀─┤User Fn │ │ +│5─────────┐ └─────────┘└────────┘ └────────┘ └────────┘ │ +││Previewer│ │ +│└─────────┘ telescope.nvim architecture │ +└───────────────────────────────────────────────────────────┘ + + + The `Entry Maker` at least defines + - value: "raw" result of the finder + - ordinal: string to be sorted derived from value + - display: line representation of entry in results buffer + + * The finder, entry manager, selected entry, and multi selections + comprises `entries` constructed by the `Entry Maker` from + raw results of the finder (`value`s) + + Primary components: + 1 Picker: central UI dedicated to varying use cases + (finding files, grepping, diagnostics, etc.) + see :h telescope.builtin + 2 Finder: pipe or interactively generates results to pick over + 3 Prompt: user input that triggers the finder which sorts results + in order into the entry manager + 4 Results: listed entries scored by sorter from finder results + 5 Previewer: preview of context of selected entry + see :h telescope.previewers + +A practical introduction into telescope customization is our `developers.md` +(top-level of repo) and `:h telescope.actions` that showcase how to access +information about the state of the picker (current selection, etc.). +To find out more: +https://github.com/nvim-telescope/telescope.nvim + + :h telescope.setup + :h telescope.command + :h telescope.builtin + :h telescope.themes + :h telescope.layout + :h telescope.resolve + :h telescope.actions + :h telescope.actions.state + :h telescope.actions.set + :h telescope.actions.utils + :h telescope.actions.generate + :h telescope.actions.history + :h telescope.previewers + +telescope.setup({opts}) *telescope.setup()* + Setup function to be run by user. Configures the defaults, pickers and + extensions of telescope. + + Usage: + > + require('telescope').setup{ + defaults = { + -- Default configuration for telescope goes here: + -- config_key = value, + -- .. + }, + pickers = { + -- Default configuration for builtin pickers goes here: + -- picker_name = { + -- picker_config_key = value, + -- ... + -- } + -- Now the picker_config_key will be applied every time you call this + -- builtin picker + }, + extensions = { + -- Your extension configuration goes here: + -- extension_name = { + -- extension_config_key = value, + -- } + -- please take a look at the readme of the extension you want to configure + } + } +< + + + Valid keys for {opts.defaults} + + *telescope.defaults.sorting_strategy* + sorting_strategy: ~ + Determines the direction "better" results are sorted towards. + + Available options are: + - "descending" (default) + - "ascending" + + *telescope.defaults.selection_strategy* + selection_strategy: ~ + Determines how the cursor acts after each sort iteration. + + Available options are: + - "reset" (default) + - "follow" + - "row" + - "closest" + - "none" + + *telescope.defaults.scroll_strategy* + scroll_strategy: ~ + Determines what happens if you try to scroll past the view of the + picker. + + Available options are: + - "cycle" (default) + - "limit" + + *telescope.defaults.layout_strategy* + layout_strategy: ~ + Determines the default layout of Telescope pickers. + See |telescope.layout| for details of the available strategies. + + Default: 'horizontal' + + *telescope.defaults.create_layout* + create_layout: ~ + Configure the layout of Telescope pickers. + See |telescope.pickers.layout| for details. + + Default: 'nil' + + *telescope.defaults.layout_config* + layout_config: ~ + Determines the default configuration values for layout strategies. + See |telescope.layout| for details of the configurations options for + each strategy. + + Allows setting defaults for all strategies as top level options and + for overriding for specific options. + For example, the default values below set the default width to 80% of + the screen width for all strategies except 'center', which has width + of 50% of the screen width. + + Default: { + bottom_pane = { + height = 25, + preview_cutoff = 120, + prompt_position = "top" + }, + center = { + height = 0.4, + preview_cutoff = 40, + prompt_position = "top", + width = 0.5 + }, + cursor = { + height = 0.9, + preview_cutoff = 40, + width = 0.8 + }, + horizontal = { + height = 0.9, + preview_cutoff = 120, + prompt_position = "bottom", + width = 0.8 + }, + vertical = { + height = 0.9, + preview_cutoff = 40, + prompt_position = "bottom", + width = 0.8 + } + } + + + *telescope.defaults.cycle_layout_list* + cycle_layout_list: ~ + Determines the layouts to cycle through when using `actions.layout.cycle_layout_next` + and `actions.layout.cycle_layout_prev`. + Should be a list of "layout setups". + Each "layout setup" can take one of two forms: + 1. string + This is interpreted as the name of a `layout_strategy` + 2. table + A table with possible keys `layout_strategy`, `layout_config` and `previewer` + + Default: { "horizontal", "vertical" } + + + *telescope.defaults.winblend* + winblend: ~ + Configure winblend for telescope floating windows. See |winblend| for + more information. Type can be a number or a function returning a + number + + Default: function() return vim.o.winblend end + + *telescope.defaults.wrap_results* + wrap_results: ~ + Word wrap the search results + + Default: false + + *telescope.defaults.prompt_prefix* + prompt_prefix: ~ + The character(s) that will be shown in front of Telescope's prompt. + + Default: '> ' + + *telescope.defaults.selection_caret* + selection_caret: ~ + The character(s) that will be shown in front of the current selection. + + Default: '> ' + + *telescope.defaults.entry_prefix* + entry_prefix: ~ + Prefix in front of each result entry. Current selection not included. + + Default: ' ' + + *telescope.defaults.multi_icon* + multi_icon: ~ + Symbol to add in front of a multi-selected result entry. + Replaces final character of |telescope.defaults.selection_caret| and + |telescope.defaults.entry_prefix| as appropriate. + To have no icon, set to the empty string. + + Default: '+' + + *telescope.defaults.initial_mode* + initial_mode: ~ + Determines in which mode telescope starts. Valid Keys: + `insert` and `normal`. + + Default: "insert" + + *telescope.defaults.border* + border: ~ + Boolean defining if borders are added to Telescope windows. + + Default: true + + *telescope.defaults.path_display* + path_display: ~ + Determines how file paths are displayed. + + path_display can be set to an array with a combination of: + - "hidden" hide file names + - "tail" only display the file name, and not the path + - "absolute" display absolute paths + - "smart" remove as much from the path as possible to only show + the difference between the displayed paths. + Warning: The nature of the algorithm might have a negative + performance impact! + - "shorten" only display the first character of each directory in + the path + - "truncate" truncates the start of the path when the whole path will + not fit. To increase the gap between the path and the edge, + set truncate to number `truncate = 3` + - "filename_first" shows filenames first and then the directories + + You can also specify the number of characters of each directory name + to keep by setting `path_display.shorten = num`. + e.g. for a path like + `alpha/beta/gamma/delta.txt` + setting `path_display.shorten = 1` will give a path like: + `a/b/g/delta.txt` + Similarly, `path_display.shorten = 2` will give a path like: + `al/be/ga/delta.txt` + + You can also further customise the shortening behaviour by + setting `path_display.shorten = { len = num, exclude = list }`, + where `len` acts as above, and `exclude` is a list of positions + that are not shortened. Negative numbers in the list are considered + relative to the end of the path. + e.g. for a path like + `alpha/beta/gamma/delta.txt` + setting `path_display.shorten = { len = 1, exclude = {1, -1} }` + will give a path like: + `alpha/b/g/delta.txt` + setting `path_display.shorten = { len = 2, exclude = {2, -2} }` + will give a path like: + `al/beta/gamma/de` + + path_display can also be set to 'filename_first' to put the filename + in front. + + path_display = { + "filename_first" + }, + + The directory structure can be reversed as follows: + + path_display = { + filename_first = { + reverse_directories = true + } + }, + + path_display can also be set to 'hidden' string to hide file names + + path_display can also be set to a function for custom formatting of + the path display with the following signature + + Signature: fun(opts: table, path: string): string, table? + + The optional table is an list of positions and highlight groups to + set the highlighting of the return path string. + + Example: + + -- Format path as "file.txt (path\to\file\)" + path_display = function(opts, path) + local tail = require("telescope.utils").path_tail(path) + return string.format("%s (%s)", tail, path) + end, + + -- Format path and add custom highlighting + path_display = function(opts, path) + local tail = require("telescope.utils").path_tail(path) + path = string.format("%s (%s)", tail, path) + + local highlights = { + { + { + 0, -- highlight start position + #path, -- highlight end position + }, + "Comment", -- highlight group name + }, + } + + return path, highlights + end + + Default: {} + + *telescope.defaults.borderchars* + borderchars: ~ + Set the borderchars of telescope floating windows. It has to be a + table of 8 string values. + + Default: { "─", "│", "─", "│", "╭", "╮", "╯", "╰" } + + *telescope.defaults.get_status_text* + get_status_text: ~ + A function that determines what the virtual text looks like. + Signature: function(picker) -> str + + Default: function that shows current count / all + + *telescope.defaults.hl_result_eol* + hl_result_eol: ~ + Changes if the highlight for the selected item in the results + window is always the full width of the window + + Default: true + + *telescope.defaults.dynamic_preview_title* + dynamic_preview_title: ~ + Will change the title of the preview window dynamically, where it + is supported. For example, the preview window's title could show up as + the full filename. + + Default: false + + *telescope.defaults.results_title* + results_title: ~ + Defines the default title of the results window. A false value + can be used to hide the title altogether. + + Default: "Results" + + *telescope.defaults.prompt_title* + prompt_title: ~ + Defines the default title of the prompt window. A false value + can be used to hide the title altogether. Most of the times builtins + define a prompt_title which will be preferred over this default. + + Default: "Prompt" + + *telescope.defaults.mappings* + mappings: ~ + Your mappings to override telescope's default mappings. + + See: ~ + |telescope.mappings| + + + *telescope.defaults.default_mappings* + default_mappings: ~ + Not recommended to use except for advanced users. + + Will allow you to completely remove all of telescope's default maps + and use your own. + + Default: nil + + + *telescope.defaults.history* + history: ~ + This field handles the configuration for prompt history. + By default it is a table, with default values (more below). + To disable history, set it to false. + + Currently mappings still need to be added, Example: + mappings = { + i = { + [""] = require('telescope.actions').cycle_history_next, + [""] = require('telescope.actions').cycle_history_prev, + }, + }, + + Fields: + - path: The path to the telescope history as string. + Default: stdpath("data")/telescope_history + - limit: The amount of entries that will be written in the + history. + Warning: If limit is set to nil it will grow unbound. + Default: 100 + - handler: A lua function that implements the history. + This is meant as a developer setting for extensions to + override the history handling, e.g., + https://github.com/nvim-telescope/telescope-smart-history.nvim, + which allows context sensitive (cwd + picker) history. + + Default: + require('telescope.actions.history').get_simple_history + - cycle_wrap: Indicates whether the cycle_history_next and + cycle_history_prev functions should wrap around to the + beginning or end of the history entries on reaching + their respective ends + Default: false + + *telescope.defaults.cache_picker* + cache_picker: ~ + This field handles the configuration for picker caching. + By default it is a table, with default values (more below). + To disable caching, set it to false. + + Caching preserves all previous multi selections and results and + therefore may result in slowdown or increased RAM occupation + if too many pickers (`cache_picker.num_pickers`) or entries + ('cache_picker.limit_entries`) are cached. + + Fields: + - num_pickers: The number of pickers to be cached. + Set to -1 to preserve all pickers of your + session. If passed to a picker, the cached + pickers with indices larger than + `cache_picker.num_pickers` will be cleared. + Default: 1 + - limit_entries: The amount of entries that will be saved for + each picker. + Default: 1000 + - ignore_empty_prompt: If true, the picker will not be cached if + the prompt is empty (i.e., no text has been + typed at the time of closing the prompt). + Default: false + + + *telescope.defaults.preview* + preview: ~ + This field handles the global configuration for previewers. + By default it is a table, with default values (more below). + To disable previewing, set it to false. If you have disabled previewers + globally, but want to opt in to previewing for single pickers, you will have to + pass `preview = true` or `preview = {...}` (your config) to the `opts` of + your picker. + + Fields: + - check_mime_type: Use `file` if available to try to infer whether the + file to preview is a binary if filetype + detection fails. + Windows users get `file` from: + https://github.com/julian-r/file-windows + Set to false to attempt to preview any mime type. + Default: true for all OS excl. Windows + - filesize_limit: The maximum file size in MB attempted to be previewed. + Set to false to attempt to preview any file size. + Default: 25 + - highlight_limit: The maximum file size in MB attempted to be highlighted. + Set to false to attempt to highlight any file size. + Default: 1 + - timeout: Timeout the previewer if the preview did not + complete within `timeout` milliseconds. + Set to false to not timeout preview. + Default: 250 + - hook(s): Function(s) that takes `(filepath, bufnr, opts)`, where opts + exposes winid and ft (filetype). + Available hooks (in order of priority): + {filetype, mime, filesize, timeout}_hook + Important: the filetype_hook must return true or false + to indicate whether to continue (true) previewing or not (false), + respectively. + Two examples: + local putils = require("telescope.previewers.utils") + ... -- preview is called in telescope.setup { ... } + preview = { + -- 1) Do not show previewer for certain files + filetype_hook = function(filepath, bufnr, opts) + -- you could analogously check opts.ft for filetypes + local excluded = vim.tbl_filter(function(ending) + return filepath:match(ending) + end, { + ".*%.csv", + ".*%.toml", + }) + if not vim.tbl_isempty(excluded) then + putils.set_preview_message( + bufnr, + opts.winid, + string.format("I don't like %s files!", + excluded[1]:sub(5, -1)) + ) + return false + end + return true + end, + -- 2) Truncate lines to preview window for too large files + filesize_hook = function(filepath, bufnr, opts) + local path = require("plenary.path"):new(filepath) + -- opts exposes winid + local height = vim.api.nvim_win_get_height(opts.winid) + local lines = vim.split(path:head(height), "[\r]?\n") + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) + end, + } + The configuration recipes for relevant examples. + Note: we use vim.filetype filetype detection, + so if you have troubles with files not + highlighting correctly, please read + |vim.filetype| + Default: nil + - treesitter: Determines whether the previewer performs treesitter + highlighting, which falls back to regex-based highlighting. + `true`: treesitter highlighting for all available filetypes + `false`: regex-based highlighting for all filetypes + `table`: may contain the following keys: + - enable boolean|table: if boolean, enable ts + highlighting for all supported + filetypes. + if table, ts highlighting is only + enabled for given filetypes. + - disable table: list of filetypes for which ts highlighting + is not used if `enable = true`. + Default: true + - msg_bg_fillchar: Character to fill background of unpreviewable buffers with + Default: "╱" + - hide_on_startup: Hide previewer when picker starts. Previewer can be toggled + with actions.layout.toggle_preview. + Default: false + - ls_short: Determines whether to use the `--short` flag for the `ls` + command when previewing directories. Otherwise will result + to using `--long`. + Default: false + + + *telescope.defaults.vimgrep_arguments* + vimgrep_arguments: ~ + Defines the command that will be used for `live_grep` and `grep_string` + pickers. + Hint: Make sure that color is currently set to `never` because we do + not yet interpret color codes + Hint 2: Make sure that these options are in your changes arguments: + "--no-heading", "--with-filename", "--line-number", "--column" + because we need them so the ripgrep output is in the correct format. + + Default: { + "rg", + "--color=never", + "--no-heading", + "--with-filename", + "--line-number", + "--column", + "--smart-case" + } + + *telescope.defaults.use_less* + use_less: ~ + Boolean if less should be enabled in term_previewer (deprecated and + currently no longer used in the builtin pickers). + + Default: true + + *telescope.defaults.set_env* + set_env: ~ + Set an environment for term_previewer. A table of key values: + Example: { COLORTERM = "truecolor", ... } + Hint: Empty table is not allowed. + + Default: nil + + *telescope.defaults.color_devicons* + color_devicons: ~ + Boolean if devicons should be enabled or not. If set to false, the + text highlight group is used. + Hint: Coloring only works if |termguicolors| is enabled. + + Default: true + + *telescope.defaults.file_sorter* + file_sorter: ~ + A function pointer that specifies the file_sorter. This sorter will + be used for find_files, git_files and similar. + Hint: If you load a native sorter, you don't need to change this value, + the native sorter will override it anyway. + + Default: require("telescope.sorters").get_fzy_sorter + + *telescope.defaults.generic_sorter* + generic_sorter: ~ + A function pointer to the generic sorter. The sorter that should be + used for everything that is not a file. + Hint: If you load a native sorter, you don't need to change this value, + the native sorter will override it anyway. + + Default: require("telescope.sorters").get_fzy_sorter + + *telescope.defaults.prefilter_sorter* + prefilter_sorter: ~ + This points to a wrapper sorter around the generic_sorter that is able + to do prefiltering. + It's usually used for lsp_*_symbols and lsp_*_diagnostics + + Default: require("telescope.sorters").prefilter + + *telescope.defaults.tiebreak* + tiebreak: ~ + A function that determines how to break a tie when two entries have + the same score. + Having a function that always returns false would keep the entries in + the order they are found, so existing_entry before current_entry. + Vice versa always returning true would place the current_entry + before the existing_entry. + + Signature: function(current_entry, existing_entry, prompt) -> boolean + + Default: function that breaks the tie based on the length of the + entry's ordinal + + *telescope.defaults.file_ignore_patterns* + file_ignore_patterns: ~ + A table of lua regex that define the files that should be ignored. + Example: { "^scratch/" } -- ignore all files in scratch directory + Example: { "%.npz" } -- ignore all npz files + See: https://www.lua.org/manual/5.1/manual.html#5.4.1 for more + information about lua regex + Note: `file_ignore_patterns` will be used in all pickers that have a + file associated. This might lead to the problem that lsp_ pickers + aren't displaying results because they might be ignored by + `file_ignore_patterns`. For example, setting up node_modules as ignored + will never show node_modules in any results, even if you are + interested in lsp_ results. + + If you only want `file_ignore_patterns` for `find_files` and + `grep_string`/`live_grep` it is suggested that you setup `gitignore` + and have fd and or ripgrep installed because both tools will not show + `gitignore`d files on default. + + Default: nil + + *telescope.defaults.get_selection_window* + get_selection_window: ~ + Function that takes function(picker, entry) and returns a window id. + The window ID will be used to decide what window the chosen file will + be opened in and the cursor placed in upon leaving the picker. + + Default: `function() return 0 end` + + + *telescope.defaults.git_worktrees* + git_worktrees: ~ + A table of arrays of detached working trees with keys `gitdir` and `toplevel`. + Used to pass `--git-dir` and `--work-tree` flags to git commands when telescope fails + to infer the top-level directory of a given working tree based on cwd. + Example: + git_worktrees = { + { + toplevel = vim.env.HOME, + gitdir = vim.env.HOME .. '/.cfg' + } + } + + Default: nil + + + *telescope.defaults.file_previewer* + file_previewer: ~ + Function pointer to the default file_previewer. It is mostly used + for find_files, git_files and similar. + You can change this function pointer to either use your own + previewer or use the command-line program bat as the previewer: + require("telescope.previewers").cat.new + + Default: require("telescope.previewers").vim_buffer_cat.new + + *telescope.defaults.grep_previewer* + grep_previewer: ~ + Function pointer to the default vim_grep previewer. It is mostly + used for live_grep, grep_string and similar. + You can change this function pointer to either use your own + previewer or use the command-line program bat as the previewer: + require("telescope.previewers").vimgrep.new + + Default: require("telescope.previewers").vim_buffer_vimgrep.new + + *telescope.defaults.qflist_previewer* + qflist_previewer: ~ + Function pointer to the default qflist previewer. It is mostly + used for qflist, loclist and lsp. + You can change this function pointer to either use your own + previewer or use the command-line program bat as the previewer: + require("telescope.previewers").qflist.new + + Default: require("telescope.previewers").vim_buffer_qflist.new + + *telescope.defaults.buffer_previewer_maker* + buffer_previewer_maker: ~ + Developer option that defines the underlining functionality + of the buffer previewer. + For interesting configuration examples take a look at + https://github.com/nvim-telescope/telescope.nvim/wiki/Configuration-Recipes + + Default: require("telescope.previewers").buffer_previewer_maker + + Parameters: ~ + {opts} (table) Configuration opts. Keys: defaults, pickers, + extensions + + +telescope.load_extension({name}) *telescope.load_extension()* + Load an extension. + - Notes: + - Loading triggers ext setup via the config passed in |telescope.setup| + + + Parameters: ~ + {name} (string) Name of the extension + + +telescope.register_extension({mod}) *telescope.register_extension()* + Register an extension. To be used by plugin authors. + + + Parameters: ~ + {mod} (table) Module + + +telescope.extensions() *telescope.extensions()* + Use telescope.extensions to reference any extensions within your + configuration. + While the docs currently generate this as a function, it's actually a + table. Sorry. + + + + +================================================================================ +COMMAND *telescope.command* + +Telescope commands can be called through two apis, the lua api and the viml +api. + +The lua api is the more direct way to interact with Telescope, as you directly +call the lua functions that Telescope defines. It can be called in a lua file +using commands like: +`require("telescope.builtin").find_files({hidden=true, layout_config={prompt_position="top"}})` +If you want to use this api from a vim file you should prepend `lua` to the +command, as below: +`lua require("telescope.builtin").find_files({hidden=true, layout_config={prompt_position="top"}})` +If you want to use this api from a neovim command line you should prepend +`:lua` to the command, as below: +`:lua require("telescope.builtin").find_files({hidden=true, layout_config={prompt_position="top"}})` + +The viml api is more indirect, as first the command must be parsed to the +relevant lua equivalent, which brings some limitations. The viml api can be +called using commands like: +`:Telescope find_files hidden=true layout_config={"prompt_position":"top"}` +This involves setting options using an `=` and using viml syntax for lists and +dictionaries when the corresponding lua function requires a table. + +One limitation of the viml api is that there can be no spaces in any of the +options. For example, if you want to use the `cwd` option for `find_files` to +specify that you only want to search within the folder `/foo bar/subfolder/` +you could not do that using the viml api, as the path name contains a space. +Similarly, you could NOT set the `prompt_position` to `"top"` using the +following command: +`:Telescope find_files layout_config={ "prompt_position" : "top" }` +as there are spaces in the option. + + + +================================================================================ +BUILTIN *telescope.builtin* + +Telescope Builtins is a collection of community maintained pickers to support +common workflows. It can be used as reference when writing PRs, Telescope +extensions, your own custom pickers, or just as a discovery tool for all of the +amazing pickers already shipped with Telescope! + +Any of these functions can just be called directly by doing: + +:lua require('telescope.builtin').$NAME_OF_PICKER() + +To use any of Telescope's default options or any picker-specific options, call +your desired picker by passing a lua table to the picker with all of the +options you want to use. Here's an example with the live_grep picker: + +> + :lua require('telescope.builtin').live_grep({ + prompt_title = 'find string in open buffers...', + grep_open_files = true + }) + -- or with dropdown theme + :lua require('telescope.builtin').find_files(require('telescope.themes').get_dropdown{ + previewer = false + }) +< + +builtin.live_grep({opts}) *telescope.builtin.live_grep()* + Search for a string and get results live as you type, respects .gitignore + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) root dir to search from + (default: cwd, use + utils.buffer_dir() to search + relative to open buffer) + {grep_open_files} (boolean) if true, restrict search to + open files only, mutually + exclusive with `search_dirs` + {search_dirs} (table) directory/directories/files to + search, mutually exclusive + with `grep_open_files` + {glob_pattern} (string|table) argument to be used with + `--glob`, e.g. "*.toml", can + use the opposite "!*.toml" + {type_filter} (string) argument to be used with + `--type`, e.g. "rust", see `rg + --type-list` + {additional_args} (function|table) additional arguments to be + passed on. Can be fn(opts) -> + tbl + {max_results} (number) define a upper result value + {disable_coordinates} (boolean) don't show the line & row + numbers (default: false) + {file_encoding} (string) file encoding for the entry & + previewer + + +builtin.grep_string({opts}) *telescope.builtin.grep_string()* + Searches for the string under your cursor or the visual selection in your + current working directory + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) root dir to search from + (default: cwd, use + utils.buffer_dir() to search + relative to open buffer) + {search} (string) the query to search + {grep_open_files} (boolean) if true, restrict search to + open files only, mutually + exclusive with `search_dirs` + {search_dirs} (table) directory/directories/files to + search, mutually exclusive + with `grep_open_files` + {use_regex} (boolean) if true, special characters + won't be escaped, allows for + using regex (default: false) + {word_match} (string) can be set to `-w` to enable + exact word matches + {additional_args} (function|table) additional arguments to be + passed on. Can be fn(opts) -> + tbl + {disable_coordinates} (boolean) don't show the line and row + numbers (default: false) + {only_sort_text} (boolean) only sort the text, not the + file, line or row (default: + false) + {file_encoding} (string) file encoding for the entry & + previewer + + +builtin.find_files({opts}) *telescope.builtin.find_files()* + Search for files (respecting .gitignore) + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) root dir to search from + (default: cwd, use + utils.buffer_dir() to search + relative to open buffer) + {find_command} (function|table) cmd to use for the search. Can + be a fn(opts) -> tbl (default: + autodetect) + {file_entry_encoding} (string) encoding of output of + `find_command` + {follow} (boolean) if true, follows symlinks + (i.e. uses `-L` flag for the + `find` command) (default: + false) + {hidden} (boolean) determines whether to show + hidden files or not (default: + false) + {no_ignore} (boolean) show files ignored by + .gitignore, .ignore, etc. + (default: false) + {no_ignore_parent} (boolean) show files ignored by + .gitignore, .ignore, etc. in + parent dirs. (default: false) + {search_dirs} (table) directory/directories/files to + search + {search_file} (string) specify a filename to search + for + {file_encoding} (string) file encoding for the + previewer + + +builtin.fd() *telescope.builtin.fd()* + This is an alias for the `find_files` picker + + + +builtin.treesitter() *telescope.builtin.treesitter()* + Lists function names, variables, and other symbols from treesitter queries + - Default keymaps: + - ``: show autocompletion menu to prefilter your query by kind of ts + node you want to see (i.e. `:var:`) + + + Options: ~ + {show_line} (boolean) if true, shows the row:column that + the result is found at (default: + true) + {bufnr} (number) specify the buffer number where + treesitter should run. (default: + current buffer) + {symbol_width} (number) defines the width of the symbol + section (default: 25) + {symbols} (string|table) filter results by symbol kind(s) + {ignore_symbols} (string|table) list of symbols to ignore + {symbol_highlights} (table) string -> string. Matches symbol + with hl_group + {file_encoding} (string) file encoding for the previewer + + +builtin.current_buffer_fuzzy_find({opts}) *telescope.builtin.current_buffer_fuzzy_find()* + Live fuzzy search inside of the currently open buffer + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {skip_empty_lines} (boolean) if true we don't display empty lines + (default: false) + {results_ts_highlight} (boolean) highlight result entries with + treesitter (default: true) + {file_encoding} (string) file encoding for the previewer + + +builtin.tags({opts}) *telescope.builtin.tags()* + Lists tags in current directory with tag location file preview (users are + required to run ctags -R to generate tags or update when introducing new + changes) + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) root dir to search from (default: cwd, use + utils.buffer_dir() to search relative to + open buffer) + {ctags_file} (string) specify a particular ctags file to use + {show_line} (boolean) if true, shows the content of the line the + tag is found on in the picker (default: + true) + {show_kind} (boolean) if true and kind info is available, show + the kind of the tag (default: true) + {only_sort_tags} (boolean) if true we will only sort tags (default: + false) + {fname_width} (number) defines the width of the filename section + (default: 30) + + +builtin.current_buffer_tags({opts}) *telescope.builtin.current_buffer_tags()* + Lists all of the tags for the currently open buffer, with a preview + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) root dir to search from (default: cwd, use + utils.buffer_dir() to search relative to + open buffer) + {ctags_file} (string) specify a particular ctags file to use + {show_line} (boolean) if true, shows the content of the line the + tag is found on in the picker (default: + true) + {show_kind} (boolean) if true and kind info is available, show + the kind of the tag (default: true) + {only_sort_tags} (boolean) if true we will only sort tags (default: + false) + {fname_width} (number) defines the width of the filename section + (default: 30) + + +builtin.git_files({opts}) *telescope.builtin.git_files()* + Fuzzy search for files tracked by Git. This command lists the output of the + `git ls-files` command, respects .gitignore + - Default keymaps: + - ``: opens the currently selected file + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify the path of the repo + {use_file_path} (boolean) if we should use the current buffer + git root (default: false) + {use_git_root} (boolean) if we should use git root as cwd or + the cwd (important for submodule) + (default: true) + {show_untracked} (boolean) if true, adds `--others` flag to + command and shows untracked files + (default: false) + {recurse_submodules} (boolean) if true, adds the + `--recurse-submodules` flag to command + (default: false) + {git_command} (table) command that will be executed. + {"git","ls-files","--exclude-standard","--cached"} + {file_encoding} (string) file encoding for the previewer + + +builtin.git_commits({opts}) *telescope.builtin.git_commits()* + Lists commits for current directory with diff preview + - Default keymaps: + - ``: checks out the currently selected commit + - `m`: resets current branch to selected commit using mixed mode + - `s`: resets current branch to selected commit using soft mode + - `h`: resets current branch to selected commit using hard mode + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify the path of the repo + {use_file_path} (boolean) if we should use the current buffer git + root (default: false) + {use_git_root} (boolean) if we should use git root as cwd or the cwd + (important for submodule) (default: true) + {git_command} (table) command that will be executed. + {"git","log","--pretty=oneline","--abbrev-commit","--","."} + + +builtin.git_bcommits({opts}) *telescope.builtin.git_bcommits()* + Lists commits for current buffer with diff preview + - Default keymaps or your overridden `select_` keys: + - ``: checks out the currently selected commit + - ``: opens a diff in a vertical split + - ``: opens a diff in a horizontal split + - ``: opens a diff in a new tab + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify the path of the repo + {use_file_path} (boolean) if we should use the current buffer git + root (default: false) + {use_git_root} (boolean) if we should use git root as cwd or the cwd + (important for submodule) (default: true) + {current_file} (string) specify the current file that should be + used for bcommits (default: current buffer) + {git_command} (table) command that will be executed. + {"git","log","--pretty=oneline","--abbrev-commit"} + + +builtin.git_bcommits_range({opts}) *telescope.builtin.git_bcommits_range()* + Lists commits for a range of lines in the current buffer with diff preview + In visual mode, lists commits for the selected lines With operator mode + enabled, lists commits inside the text object/motion + - Default keymaps or your overridden `select_` keys: + - ``: checks out the currently selected commit + - ``: opens a diff in a vertical split + - ``: opens a diff in a horizontal split + - ``: opens a diff in a new tab + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify the path of the repo + {use_git_root} (boolean) if we should use git root as cwd or the cwd + (important for submodule) (default: true) + {current_file} (string) specify the current file that should be used + for bcommits (default: current buffer) + {git_command} (table) command that will be executed. the last + element must be "-L". + {"git","log","--pretty=oneline","--abbrev-commit","--no-patch","-L"} + {from} (number) the first line number in the range (default: + current line) + {to} (number) the last line number in the range (default: + the value of `from`) + {operator} (boolean) select lines in operator-pending mode + (default: false) + + +builtin.git_branches({opts}) *telescope.builtin.git_branches()* + List branches for current directory, with output from `git log --oneline` + shown in the preview window + - Default keymaps: + - ``: checks out the currently selected branch + - ``: tracks currently selected branch + - ``: rebases currently selected branch + - ``: creates a new branch, with confirmation prompt before creation + - ``: deletes the currently selected branch, with confirmation + prompt before deletion + - ``: merges the currently selected branch, with confirmation prompt + before deletion + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify the path of the + repo + {use_file_path} (boolean) if we should use the + current buffer git root + (default: false) + {use_git_root} (boolean) if we should use git root + as cwd or the cwd + (important for submodule) + (default: true) + {show_remote_tracking_branches} (boolean) show remote tracking + branches like origin/main + (default: true) + {pattern} (string) specify the pattern to + match all refs + + +builtin.git_status({opts}) *telescope.builtin.git_status()* + Lists git status for current directory + - Default keymaps: + - ``: stages or unstages the currently selected file + - ``: opens the currently selected file + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify the path of the repo + {use_file_path} (boolean) if we should use the current buffer git + root (default: false) + {use_git_root} (boolean) if we should use git root as cwd or the cwd + (important for submodule) (default: true) + {git_icons} (table) string -> string. Matches name with icon + (see source code, make_entry.lua + git_icon_defaults) + {expand_dir} (boolean) pass flag `-uall` to show files in + untracked directories (default: true) + + +builtin.git_stash({opts}) *telescope.builtin.git_stash()* + Lists stash items in current repository + - Default keymaps: + - ``: runs `git apply` for currently selected stash + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify the path of the repo + {use_file_path} (boolean) if we should use the current buffer git + root (default: false) + {use_git_root} (boolean) if we should use git root as cwd or the cwd + (important for submodule) (default: true) + {show_branch} (boolean) if we should display the branch name for + git stash entries (default: true) + + +builtin.builtin({opts}) *telescope.builtin.builtin()* + Lists all of the community maintained pickers built into Telescope + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {include_extensions} (boolean) if true will show the pickers of the + installed extensions (default: false) + {use_default_opts} (boolean) if the selected picker should use its + default options (default: false) + + +builtin.resume({opts}) *telescope.builtin.resume()* + Opens the previous picker in the identical state (incl. multi selections) + - Notes: + - Requires `cache_picker` in setup or when having invoked pickers, see + |telescope.defaults.cache_picker| + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cache_index} (number) what picker to resume, where 1 denotes most + recent (default: 1) + + +builtin.pickers({opts}) *telescope.builtin.pickers()* + Opens a picker over previously cached pickers in their preserved states + (incl. multi selections) + - Default keymaps: + - ``: delete the selected cached picker + - Notes: + - Requires `cache_picker` in setup or when having invoked pickers, see + |telescope.defaults.cache_picker| + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.planets({opts}) *telescope.builtin.planets()* + Use the telescope... + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_pluto} (boolean) we love Pluto (default: false, because its a + hidden feature) + {show_moon} (boolean) we love the Moon (default: false, because its + a hidden feature) + + +builtin.symbols({opts}) *telescope.builtin.symbols()* + Lists symbols inside of `data/telescope-sources/*.json` found in your + runtime path or found in `stdpath("data")/telescope/symbols/*.json`. The + second path can be customized. We provide a couple of default symbols which + can be found in https://github.com/nvim-telescope/telescope-symbols.nvim. + This repos README also provides more information about the format in which + the symbols have to be. + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {symbol_path} (string) specify the second path. Default: + `stdpath("data")/telescope/symbols/*.json` + {sources} (table) specify a table of sources you want to load + this time + + +builtin.commands({opts}) *telescope.builtin.commands()* + Lists available plugin/user commands and runs them on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_buf_command} (boolean) show buf local command (Default: true) + + +builtin.quickfix({opts}) *telescope.builtin.quickfix()* + Lists items in the quickfix list, jumps to location on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + {nr} (number) specify the quickfix list number + + +builtin.quickfixhistory({opts}) *telescope.builtin.quickfixhistory()* + Lists all quickfix lists in your history and open them with + `builtin.quickfix`. It seems that neovim only keeps the full history for 10 + lists + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.loclist({opts}) *telescope.builtin.loclist()* + Lists items from the current window's location list, jumps to location on + `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + + +builtin.oldfiles({opts}) *telescope.builtin.oldfiles()* + Lists previously open files, opens on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify a working directory to filter + oldfiles by + {only_cwd} (boolean) show only files in the cwd (default: false) + {cwd_only} (boolean) alias for only_cwd + {file_encoding} (string) file encoding for the previewer + + +builtin.command_history({opts}) *telescope.builtin.command_history()* + Lists commands that were executed recently, and reruns them on `` + - Default keymaps: + - ``: open the command line with the text of the currently selected + result populated in it + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {filter_fn} (function) filter fn(cmd:string). true if the history + command should be presented. + + +builtin.search_history({opts}) *telescope.builtin.search_history()* + Lists searches that were executed recently, and reruns them on `` + - Default keymaps: + - ``: open a search window with the text of the currently selected + search result populated in it + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.vim_options({opts}) *telescope.builtin.vim_options()* + Lists vim options, allows you to edit the current value on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.help_tags({opts}) *telescope.builtin.help_tags()* + Lists available help tags and opens a new window with the relevant help + info on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {lang} (string) specify language (default: vim.o.helplang) + {fallback} (boolean) fallback to en if language isn't installed + (default: true) + + +builtin.man_pages({opts}) *telescope.builtin.man_pages()* + Lists manpage entries, opens them in a help window on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {sections} (table) a list of sections to search, use `{ "ALL" }` + to search in all sections (default: { "1" }) + {man_cmd} (function) that returns the man command. (Default: + `apropos ""` on linux, `apropos " "` on macos) + + +builtin.reloader({opts}) *telescope.builtin.reloader()* + Lists lua modules and reloads them on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {column_len} (number) define the max column len for the module name + (default: dynamic, longest module name) + + +builtin.buffers({opts}) *telescope.builtin.buffers()* + Lists open buffers in current neovim instance, opens selected buffer on + `` + - Default keymaps: + - ``: delete the currently selected buffer + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {cwd} (string) specify a working directory to + filter buffers list by + {show_all_buffers} (boolean) if true, show all buffers, + including unloaded buffers + (default: true) + {ignore_current_buffer} (boolean) if true, don't show the current + buffer in the list (default: + false) + {only_cwd} (boolean) if true, only show buffers in the + current working directory + (default: false) + {cwd_only} (boolean) alias for only_cwd + {sort_lastused} (boolean) Sorts current and last buffer to + the top and selects the lastused + (default: false) + {sort_mru} (boolean) Sorts all buffers after most + recent used. Not just the current + and last one (default: false) + {bufnr_width} (number) Defines the width of the buffer + numbers in front of the filenames + (default: dynamic) + {file_encoding} (string) file encoding for the previewer + {sort_buffers} (function) sort fn(bufnr_a, bufnr_b). true if + bufnr_a should go first. Runs + after sorting by most recent (if + specified) + {select_current} (boolean) select current buffer (default: + false) + + +builtin.colorscheme({opts}) *telescope.builtin.colorscheme()* + Lists available colorschemes and applies them on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {colors} (table) a list of additional colorschemes to + explicitly make available to telescope + (default: {}) + {enable_preview} (boolean) if true, will preview the selected color + {ignore_builtins} (boolean) if true, builtin colorschemes are not + listed + + +builtin.marks({opts}) *telescope.builtin.marks()* + Lists vim marks and their value, jumps to the mark on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {file_encoding} (string) file encoding for the previewer + {mark_type} (string) filter marks by type (default: "all", + options: "all"|"global"|"local") + + +builtin.registers({opts}) *telescope.builtin.registers()* + Lists vim registers, pastes the contents of the register on `` + - Default keymaps: + - ``: edit the contents of the currently selected register + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.keymaps({opts}) *telescope.builtin.keymaps()* + Lists normal mode keymappings, runs the selected keymap on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {modes} (table) a list of short-named keymap modes to search + (default: { "n", "i", "c", "x" }) + {show_plug} (boolean) if true, the keymaps for which the lhs + contains "" are also shown (default: + true) + {only_buf} (boolean) if true, only show the buffer-local keymaps + (default: false) + {lhs_filter} (function) filter(lhs:string) -> boolean. true for + keymap.lhs if the keymap should be shown + (optional) + {filter} (function) filter(km:keymap) -> boolean. true for the + keymap if it should be shown (optional) + + +builtin.filetypes({opts}) *telescope.builtin.filetypes()* + Lists all available filetypes, sets currently open buffer's filetype to + selected filetype in Telescope on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.highlights({opts}) *telescope.builtin.highlights()* + Lists all available highlights + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.autocommands({opts}) *telescope.builtin.autocommands()* + Lists vim autocommands and goes to their declaration on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.spell_suggest({opts}) *telescope.builtin.spell_suggest()* + Lists spelling suggestions for the current word under the cursor, replaces + word with selected suggestion on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + +builtin.tagstack({opts}) *telescope.builtin.tagstack()* + Lists the tag stack for the current window, jumps to tag on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + + +builtin.jumplist({opts}) *telescope.builtin.jumplist()* + Lists items from Vim's jumplist, jumps to location on `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + + +builtin.lsp_references({opts}) *telescope.builtin.lsp_references()* + Lists LSP references for word under the cursor, jumps to reference on + `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {include_declaration} (boolean) include symbol declaration in the + lsp references (default: true) + {include_current_line} (boolean) include current line (default: + false) + {jump_type} (string) how to goto reference if there is + only one and the definition file is + different from the current file, + values: "tab", "tab drop", "split", + "vsplit", "never" + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + {reuse_win} (boolean) jump to existing window if buffer is + already opened (default: false) + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_incoming_calls({opts}) *telescope.builtin.lsp_incoming_calls()* + Lists LSP incoming calls for word under the cursor, jumps to reference on + `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_outgoing_calls({opts}) *telescope.builtin.lsp_outgoing_calls()* + Lists LSP outgoing calls for word under the cursor, jumps to reference on + `` + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_definitions({opts}) *telescope.builtin.lsp_definitions()* + Goto the definition of the word under the cursor, if there's only one, + otherwise show all options in Telescope + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {jump_type} (string) how to goto definition if there is only one + and the definition file is different from + the current file, values: "tab", "tab + drop", "split", "vsplit", "never" + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + {reuse_win} (boolean) jump to existing window if buffer is + already opened (default: false) + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_type_definitions({opts}) *telescope.builtin.lsp_type_definitions()* + Goto the definition of the type of the word under the cursor, if there's + only one, otherwise show all options in Telescope + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {jump_type} (string) how to goto definition if there is only one + and the definition file is different from + the current file, values: "tab", "tab + drop", "split", "vsplit", "never" + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + {reuse_win} (boolean) jump to existing window if buffer is + already opened (default: false) + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_implementations({opts}) *telescope.builtin.lsp_implementations()* + Goto the implementation of the word under the cursor if there's only one, + otherwise show all options in Telescope + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {jump_type} (string) how to goto implementation if there is only + one and the definition file is different + from the current file, values: "tab", "tab + drop", "split", "vsplit", "never" + {show_line} (boolean) show results text (default: true) + {trim_text} (boolean) trim results text (default: false) + {reuse_win} (boolean) jump to existing window if buffer is + already opened (default: false) + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_document_symbols({opts}) *telescope.builtin.lsp_document_symbols()* + Lists LSP document symbols in the current buffer + - Default keymaps: + - ``: show autocompletion menu to prefilter your query by type of + symbol you want to see (i.e. `:variable:`) + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {fname_width} (number) defines the width of the filename + section (default: 30) + {symbol_width} (number) defines the width of the symbol + section (default: 25) + {symbol_type_width} (number) defines the width of the symbol + type section (default: 8) + {show_line} (boolean) if true, shows the content of the + line the tag is found on (default: + false) + {symbols} (string|table) filter results by symbol kind(s) + {ignore_symbols} (string|table) list of symbols to ignore + {symbol_highlights} (table) string -> string. Matches symbol + with hl_group + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_workspace_symbols({opts}) *telescope.builtin.lsp_workspace_symbols()* + Lists LSP document symbols in the current workspace + - Default keymaps: + - ``: show autocompletion menu to prefilter your query by type of + symbol you want to see (i.e. `:variable:`) + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {query} (string) for what to query the workspace + (default: "") + {fname_width} (number) defines the width of the filename + section (default: 30) + {symbol_width} (number) defines the width of the symbol + section (default: 25) + {symbol_type_width} (number) defines the width of the symbol + type section (default: 8) + {show_line} (boolean) if true, shows the content of the + line the tag is found on (default: + false) + {symbols} (string|table) filter results by symbol kind(s) + {ignore_symbols} (string|table) list of symbols to ignore + {symbol_highlights} (table) string -> string. Matches symbol + with hl_group + {file_encoding} (string) file encoding for the previewer + + +builtin.lsp_dynamic_workspace_symbols({opts}) *telescope.builtin.lsp_dynamic_workspace_symbols()* + Dynamically lists LSP for all workspace symbols + - Default keymaps: + - ``: show autocompletion menu to prefilter your query by type of + symbol you want to see (i.e. `:variable:`), only works after refining + to fuzzy search using + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {fname_width} (number) defines the width of the filename + section (default: 30) + {show_line} (boolean) if true, shows the content of the + line the symbol is found on + (default: false) + {symbols} (string|table) filter results by symbol kind(s) + {ignore_symbols} (string|table) list of symbols to ignore + {symbol_highlights} (table) string -> string. Matches symbol + with hl_group + {file_encoding} (string) file encoding for the previewer + + +builtin.diagnostics({opts}) *telescope.builtin.diagnostics()* + Lists diagnostics + - Fields: + - `All severity flags can be passed as `string` or `number` as per + `:vim.diagnostic.severity:` + - Default keymaps: + - ``: show autocompletion menu to prefilter your query with the + diagnostic you want to see (i.e. `:warning:`) + - sort_by option: + - "buffer": order by bufnr (prioritizing current bufnr), severity, lnum + - "severity": order by severity, bufnr (prioritizing current bufnr), lnum + + + Parameters: ~ + {opts} (table) options to pass to the picker + + Options: ~ + {bufnr} (number|nil) Buffer number to get + diagnostics from. Use 0 for + current buffer or nil for all + buffers + {severity} (string|number) filter diagnostics by severity + name (string) or id (number) + {severity_limit} (string|number) keep diagnostics equal or more + severe wrt severity name + (string) or id (number) + {severity_bound} (string|number) keep diagnostics equal or less + severe wrt severity name + (string) or id (number) + {root_dir} (string|boolean) if set to string, get + diagnostics only for buffers + under this dir otherwise cwd + {no_unlisted} (boolean) if true, get diagnostics only + for listed buffers + {no_sign} (boolean) hide DiagnosticSigns from + Results (default: false) + {line_width} (string|number) set length of diagnostic entry + text in Results. Use 'full' + for full untruncated text + {namespace} (number) limit your diagnostics to a + specific namespace + {disable_coordinates} (boolean) don't show the line & row + numbers (default: false) + {sort_by} (string) sort order of the diagnostics + results; see above notes + (default: "buffer") + + + +================================================================================ +THEMES *telescope.themes* + +Themes are ways to combine several elements of styling together. + +They are helpful for managing the several different UI aspects for telescope +and provide a simple interface for users to get a particular "style" of picker. + +themes.get_dropdown() *telescope.themes.get_dropdown()* + Dropdown style theme. + + Usage: + > + local opts = {...} -- picker options + local builtin = require('telescope.builtin') + local themes = require('telescope.themes') + builtin.find_files(themes.get_dropdown(opts)) +< + + + +themes.get_cursor() *telescope.themes.get_cursor()* + Cursor style theme. + + Usage: + > + local opts = {...} -- picker options + local builtin = require('telescope.builtin') + local themes = require('telescope.themes') + builtin.find_files(themes.get_cursor(opts)) +< + + + +themes.get_ivy() *telescope.themes.get_ivy()* + Ivy style theme. + + Usage: + > + local opts = {...} -- picker options + local builtin = require('telescope.builtin') + local themes = require('telescope.themes') + builtin.find_files(themes.get_ivy(opts)) +< + + + + +================================================================================ +MAPPINGS *telescope.mappings* + +|telescope.mappings| is used to configure the keybindings within a telescope +picker. These key binds are only local to the picker window and will be cleared +once you exit the picker. + +We provide multiple configuration options to make it easy for you to adjust +telescope's default key bindings and create your own custom key binds. + +To see many of the builtin actions that you can use as values for this table, +see |telescope.actions| + +Format is: +> + { + mode = { ..keys } + } +< + +where {mode} is the one character letter for a mode ('i' for insert, 'n' for +normal). + +For example: +> + mappings = { + i = { + [""] = require('telescope.actions').close, + }, + } +< + +To disable a keymap, put `[map] = false` +For example: +> + { + ..., + [""] = false, + ..., + } +< + +To override behavior of a key, simply set the value to be a function (either by +requiring an action or by writing your own function) +> + { + ..., + [""] = require('telescope.actions').select_default, + ..., + } +< + +If the function you want is part of `telescope.actions`, then you can simply +supply the function name as a string. For example, the previous option is +equivalent to: +> + { + ..., + [""] = "select_default", + ..., + } +< + +You can also add other mappings using tables with `type = "command"`. For +example: +> + { + ..., + ["jj"] = { "", type = "command" }, + ["kk"] = { "echo \"Hello, World!\"", type = "command" },) + ..., + } +< + +You can also add additional options for mappings of any type ("action" and +"command"). For example: +> + { + ..., + [""] = { + actions.move_selection_next, type = "action", + opts = { nowait = true, silent = true } + }, + ..., + } +< + +There are three main places you can configure |telescope.mappings|. These are +ordered from the lowest priority to the highest priority. + +1. |telescope.defaults.mappings| +2. In the |telescope.setup()| table, inside a picker with a given name, use the + `mappings` key +> + require("telescope").setup { + pickers = { + find_files = { + mappings = { + n = { + ["kj"] = "close", + }, + }, + }, + }, + } +< +3. `attach_mappings` function for a particular picker. +> + require("telescope.builtin").find_files { + attach_mappings = function(_, map) + map("i", "asdf", function(_prompt_bufnr) + print "You typed asdf" + end) + + map({"i", "n"}, "", function(_prompt_bufnr) + print "You typed " + end, { desc = "desc for which key"}) + + -- needs to return true if you want to map default_mappings and + -- false if not + return true + end, + } +< + + +================================================================================ +LAYOUT *telescope.pickers.layout* + +The telescope pickers layout can be configured using the +|telescope.defaults.create_layout| option. + +Parameters: ~ + - picker : A Picker object. + +Return: ~ + - layout : instance of `TelescopeLayout` class. + +Example: ~ +> +local Layout = require "telescope.pickers.layout" + +require("telescope").setup { + create_layout = function(picker) + local function create_window(enter, width, height, row, col, title) + local bufnr = vim.api.nvim_create_buf(false, true) + local winid = vim.api.nvim_open_win(bufnr, enter, { + style = "minimal", + relative = "editor", + width = width, + height = height, + row = row, + col = col, + border = "single", + title = title, + }) + + vim.wo[winid].winhighlight = "Normal:Normal" + + return Layout.Window { + bufnr = bufnr, + winid = winid, + } + end + + local function destory_window(window) + if window then + if vim.api.nvim_win_is_valid(window.winid) then + vim.api.nvim_win_close(window.winid, true) + end + if vim.api.nvim_buf_is_valid(window.bufnr) then + vim.api.nvim_buf_delete(window.bufnr, { force = true }) + end + end + end + + local layout = Layout { + picker = picker, + mount = function(self) + self.results = create_window(false, 40, 20, 0, 0, "Results") + self.preview = create_window(false, 40, 23, 0, 42, "Preview") + self.prompt = create_window(true, 40, 1, 22, 0, "Prompt") + end, + unmount = function(self) + destory_window(self.results) + destory_window(self.preview) + destory_window(self.prompt) + end, + update = function(self) end, + } + + return layout + end, +} +< + +TelescopeWindowBorder.config *TelescopeWindowBorder.config* + + + Fields: ~ + {bufnr} (integer) + {winid} (integer|nil) + {change_title} (nil|function) (self: TelescopeWindowBorder, title: + string, pos?: + "NW"|"N"|"NE"|"SW"|"S"|"SE"):nil + + +TelescopeWindowBorder *TelescopeWindowBorder* + + + Fields: ~ + {bufnr} (integer|nil) + {winid} (integer|nil) + + +TelescopeWindow.config *TelescopeWindow.config* + + + Fields: ~ + {bufnr} (integer) + {winid} (integer|nil) + {border} (TelescopeWindowBorder.config|nil) + + +TelescopeWindow *TelescopeWindow* + + + Fields: ~ + {border} (TelescopeWindowBorder) + {bufnr} (integer) + {winid} (integer) + + +TelescopeLayout.config *TelescopeLayout.config* + + + Fields: ~ + {mount} (function) (self: TelescopeLayout):nil + {unmount} (function) (self: TelescopeLayout):nil + {update} (function) (self: TelescopeLayout):nil + {prompt} (TelescopeWindow|nil) + {results} (TelescopeWindow|nil) + {preview} (TelescopeWindow|nil) + + +TelescopeLayout *TelescopeLayout* + + + Fields: ~ + {prompt} (TelescopeWindow) + {results} (TelescopeWindow) + {preview} (TelescopeWindow|nil) + + +Layout:mount() *telescope.pickers.layout:mount()* + Create the layout. This needs to ensure the required properties are + populated. + + + +Layout:unmount() *telescope.pickers.layout:unmount()* + Destroy the layout. This is responsible for performing clean-up, for + example: + - deleting buffers + - closing windows + - clearing autocmds + + + +Layout:update() *telescope.pickers.layout:update()* + Refresh the layout. This is called when, for example, vim is resized. + + + + +================================================================================ +LAYOUT *telescope.layout* + +The layout of telescope pickers can be adjusted using the +|telescope.defaults.layout_strategy| and |telescope.defaults.layout_config| +options. For example, the following configuration changes the default layout +strategy and the default size of the picker: +> + require('telescope').setup{ + defaults = { + layout_strategy = 'vertical', + layout_config = { height = 0.95 }, + }, + } +< + + +──────────────────────────────────────────────────────────────────────────────── + +Layout strategies are different functions to position telescope. + +All layout strategies are functions with the following signature: + +> + function(picker, columns, lines, layout_config) + -- Do some calculations here... + return { + preview = preview_configuration + results = results_configuration, + prompt = prompt_configuration, + } + end +< + + Parameters: ~ + - picker : A Picker object. (docs coming soon) + - columns : (number) Columns in the vim window + - lines : (number) Lines in the vim window + - layout_config : (table) The configuration values specific to the picker. + +This means you can create your own layout strategy if you want! Just be aware +for now that we may change some APIs or interfaces, so they may break if you +create your own. + +A good method for creating your own would be to copy one of the strategies that +most resembles what you want from +"./lua/telescope/pickers/layout_strategies.lua" in the telescope repo. + + +layout_strategies.horizontal() *telescope.layout.horizontal()* + Horizontal layout has two columns, one for the preview and one for the + prompt and results. + + ┌──────────────────────────────────────────────────┐ + │ │ + │ ┌───────────────────┐┌───────────────────┐ │ + │ │ ││ │ │ + │ │ ││ │ │ + │ │ ││ │ │ + │ │ Results ││ │ │ + │ │ ││ Preview │ │ + │ │ ││ │ │ + │ │ ││ │ │ + │ └───────────────────┘│ │ │ + │ ┌───────────────────┐│ │ │ + │ │ Prompt ││ │ │ + │ └───────────────────┘└───────────────────┘ │ + │ │ + └──────────────────────────────────────────────────┘ + + `picker.layout_config` shared options: + - anchor: + - Which edge/corner to pin the picker to + - See |resolver.resolve_anchor_pos()| + - anchor_padding: + - Specifies an amount of additional padding around the anchor + - Values should be a positive integer + - height: + - How tall to make Telescope's entire layout + - See |resolver.resolve_height()| + - mirror: Flip the location of the results/prompt and preview windows + - prompt_position: + - Where to place prompt window. + - Available Values: 'bottom', 'top' + - scroll_speed: The number of lines to scroll through the previewer + - width: + - How wide to make Telescope's entire layout + - See |resolver.resolve_width()| + + `picker.layout_config` unique options: + - preview_cutoff: When columns are less than this value, the preview will be disabled + - preview_width: + - Change the width of Telescope's preview window + - See |resolver.resolve_width()| + + +layout_strategies.center() *telescope.layout.center()* + Centered layout with a combined block of the prompt and results aligned to + the middle of the screen. The preview window is then placed in the + remaining space above or below, according to `anchor` or `mirror`. + Particularly useful for creating dropdown menus (see |telescope.themes| and + |themes.get_dropdown()|). + + Note that vertical anchoring, i.e. `anchor` containing `"N"` or `"S"`, will + override `mirror` config. For `"N"` anchoring preview will be placed below + prompt/result block. For `"S"` anchoring preview will be placed above + prompt/result block. For horizontal only anchoring preview will be placed + according to `mirror` config, default is above the prompt/result block. + + ┌──────────────────────────────────────────────────┐ + │ ┌────────────────────────────────────────┐ │ + │ │ Preview │ │ + │ │ Preview │ │ + │ └────────────────────────────────────────┘ │ + │ ┌────────────────────────────────────────┐ │ + │ │ Prompt │ │ + │ ├────────────────────────────────────────┤ │ + │ │ Result │ │ + │ │ Result │ │ + │ └────────────────────────────────────────┘ │ + │ │ + │ │ + │ │ + │ │ + └──────────────────────────────────────────────────┘ + + `picker.layout_config` shared options: + - anchor: + - Which edge/corner to pin the picker to + - See |resolver.resolve_anchor_pos()| + - anchor_padding: + - Specifies an amount of additional padding around the anchor + - Values should be a positive integer + - height: + - How tall to make Telescope's entire layout + - See |resolver.resolve_height()| + - mirror: Flip the location of the results/prompt and preview windows + - prompt_position: + - Where to place prompt window. + - Available Values: 'bottom', 'top' + - scroll_speed: The number of lines to scroll through the previewer + - width: + - How wide to make Telescope's entire layout + - See |resolver.resolve_width()| + + `picker.layout_config` unique options: + - preview_cutoff: When lines are less than this value, the preview will be disabled + + +layout_strategies.cursor() *telescope.layout.cursor()* + Cursor layout dynamically positioned below the cursor if possible. If there + is no place below the cursor it will be placed above. + + ┌──────────────────────────────────────────────────┐ + │ │ + │ █ │ + │ ┌──────────────┐┌─────────────────────┐ │ + │ │ Prompt ││ Preview │ │ + │ ├──────────────┤│ Preview │ │ + │ │ Result ││ Preview │ │ + │ │ Result ││ Preview │ │ + │ └──────────────┘└─────────────────────┘ │ + │ █ │ + │ │ + │ │ + │ │ + │ │ + │ │ + └──────────────────────────────────────────────────┘ + + `picker.layout_config` shared options: + - height: + - How tall to make Telescope's entire layout + - See |resolver.resolve_height()| + - scroll_speed: The number of lines to scroll through the previewer + - width: + - How wide to make Telescope's entire layout + - See |resolver.resolve_width()| + + `picker.layout_config` unique options: + - preview_cutoff: When columns are less than this value, the preview will be disabled + - preview_width: + - Change the width of Telescope's preview window + - See |resolver.resolve_width()| + + +layout_strategies.vertical() *telescope.layout.vertical()* + Vertical layout stacks the items on top of each other. Particularly useful + with thinner windows. + + ┌──────────────────────────────────────────────────┐ + │ │ + │ ┌────────────────────────────────────────┐ │ + │ │ Preview │ │ + │ │ Preview │ │ + │ │ Preview │ │ + │ └────────────────────────────────────────┘ │ + │ ┌────────────────────────────────────────┐ │ + │ │ Result │ │ + │ │ Result │ │ + │ └────────────────────────────────────────┘ │ + │ ┌────────────────────────────────────────┐ │ + │ │ Prompt │ │ + │ └────────────────────────────────────────┘ │ + │ │ + └──────────────────────────────────────────────────┘ + + `picker.layout_config` shared options: + - anchor: + - Which edge/corner to pin the picker to + - See |resolver.resolve_anchor_pos()| + - anchor_padding: + - Specifies an amount of additional padding around the anchor + - Values should be a positive integer + - height: + - How tall to make Telescope's entire layout + - See |resolver.resolve_height()| + - mirror: Flip the location of the results/prompt and preview windows + - prompt_position: + - Where to place prompt window. + - Available Values: 'bottom', 'top' + - scroll_speed: The number of lines to scroll through the previewer + - width: + - How wide to make Telescope's entire layout + - See |resolver.resolve_width()| + + `picker.layout_config` unique options: + - preview_cutoff: When lines are less than this value, the preview will be disabled + - preview_height: + - Change the height of Telescope's preview window + - See |resolver.resolve_height()| + + +layout_strategies.flex() *telescope.layout.flex()* + Flex layout swaps between `horizontal` and `vertical` strategies based on + the window width + - Supports |layout_strategies.vertical| or |layout_strategies.horizontal| + features + + + `picker.layout_config` shared options: + - anchor: + - Which edge/corner to pin the picker to + - See |resolver.resolve_anchor_pos()| + - anchor_padding: + - Specifies an amount of additional padding around the anchor + - Values should be a positive integer + - height: + - How tall to make Telescope's entire layout + - See |resolver.resolve_height()| + - mirror: Flip the location of the results/prompt and preview windows + - prompt_position: + - Where to place prompt window. + - Available Values: 'bottom', 'top' + - scroll_speed: The number of lines to scroll through the previewer + - width: + - How wide to make Telescope's entire layout + - See |resolver.resolve_width()| + + `picker.layout_config` unique options: + - flip_columns: The number of columns required to move to horizontal mode + - flip_lines: The number of lines required to move to horizontal mode + - horizontal: Options to pass when switching to horizontal layout + - vertical: Options to pass when switching to vertical layout + + +layout_strategies.bottom_pane() *telescope.layout.bottom_pane()* + Bottom pane can be used to create layouts similar to "ivy". + + For an easy ivy configuration, see |themes.get_ivy()| + + + + +================================================================================ +RESOLVE *telescope.resolve* + +Provides "resolver functions" to allow more customisable inputs for options. + +resolver.resolve_height() *telescope.resolve.resolve_height()* + Converts input to a function that returns the height. The input must take + one of five forms: + 1. 0 <= number < 1 + This means total height as a percentage. + 2. 1 <= number + This means total height as a fixed number. + 3. function + Must have signature: function(self, max_columns, max_lines): number + 4. table of the form: { val, max = ..., min = ... } + val has to be in the first form 0 <= val < 1 and only one is given, + `min` or `max` as fixed number + 5. table of the form: {padding = `foo`} + where `foo` has one of the previous three forms. + The height is then set to be the remaining space after padding. For + example, if the window has height 50, and the input is {padding = 5}, + the height returned will be `40 = 50 - 2*5` + + The returned function will have signature: function(self, max_columns, + max_lines): number + + + +resolver.resolve_width() *telescope.resolve.resolve_width()* + Converts input to a function that returns the width. The input must take + one of five forms: + 1. 0 <= number < 1 + This means total width as a percentage. + 2. 1 <= number + This means total width as a fixed number. + 3. function + Must have signature: function(self, max_columns, max_lines): number + 4. table of the form: { val, max = ..., min = ... } + val has to be in the first form 0 <= val < 1 and only one is given, + `min` or `max` as fixed number + 5. table of the form: {padding = `foo`} + where `foo` has one of the previous three forms. + The width is then set to be the remaining space after padding. For + example, if the window has width 100, and the input is {padding = 5}, + the width returned will be `90 = 100 - 2*5` + + The returned function will have signature: function(self, max_columns, + max_lines): number + + + +resolver.resolve_anchor_pos() *telescope.resolve.resolve_anchor_pos()* + Calculates the adjustment required to move the picker from the middle of + the screen to an edge or corner. + The `anchor` can be any of the following strings: + - "", "CENTER", "NW", "N", "NE", "E", "SE", "S", "SW", "W" The anchors + have the following meanings: + - "" or "CENTER": + the picker will remain in the middle of the screen. + - Compass directions: + the picker will move to the corresponding edge/corner e.g. "NW" -> "top + left corner", "E" -> "right edge", "S" -> "bottom edge" + + + + +================================================================================ +MAKE_ENTRY *telescope.make_entry* + +Each picker has a finder made up of two parts, the results which are the data +to be displayed, and the entry_maker. These entry_makers are functions returned +from make_entry functions. These will be referred to as entry_makers in the +following documentation. + +Every entry maker returns a function that accepts the data to be used for an +entry. This function will return an entry table (or nil, meaning skip this +entry) which contains the following important keys: +- value any: value key can be anything but still required +- valid bool (optional): is an optional key because it defaults to true but if + the key is set to false it will not be displayed by the picker +- ordinal string: is the text that is used for filtering +- display string|function: is either a string of the text that is being + displayed or a function receiving the entry at a later stage, when the entry + is actually being displayed. A function can be useful here if a complex + calculation has to be done. `make_entry` can also return a second value - a + highlight array which will then apply to the line. Highlight entry in this + array has the following signature `{ { start_col, end_col }, hl_group }` +- filename string (optional): will be interpreted by the default `` action + as open this file +- bufnr number (optional): will be interpreted by the default `` action as + open this buffer +- lnum number (optional): lnum value which will be interpreted by the default + `` action as a jump to this line +- col number (optional): col value which will be interpreted by the default + `` action as a jump to this column + +For more information on easier displaying, see +|telescope.pickers.entry_display| + +TODO: Document something we call `entry_index` + + +================================================================================ +ENTRY_DISPLAY *telescope.pickers.entry_display* + +Entry Display is used to format each entry shown in the result panel. + +Entry Display create() will give us a function based on the configuration of +column widths we pass into it. We then can use this function n times to return +a string based on structured input. + +Note that if you call `create()` inside `make_display` it will be called for +every single entry. So it is suggested to do this outside of `make_display` for +the best performance. + +The create function will use the column widths passed to it in +configuration.items. Each item in that table is the number of characters in the +column. It's also possible for the final column to not have a fixed width, this +will be shown in the configuration as 'remaining = true'. + +An example of this configuration is shown for the buffers picker: +> +local displayer = entry_display.create { + separator = " ", + items = { + { width = opts.bufnr_width }, + { width = 4 }, + { width = icon_width }, + { remaining = true }, + }, +} +< + +This shows 4 columns, the first is defined in the opts as the width we'll use +when display_string is the number of the buffer. The second has a fixed width +of 4 and the third column's width will be decided by the width of the icons we +use. The fourth column will use the remaining space. Finally, we have also +defined the separator between each column will be the space " ". + +An example of how the display reference will be used is shown, again for the +buffers picker: +> +return displayer { + { entry.bufnr, "TelescopeResultsNumber" }, + { entry.indicator, "TelescopeResultsComment" }, + { icon, hl_group }, + display_bufname .. ":" .. entry.lnum, +} +< + +There are two types of values each column can have. Either a simple String or a +table containing the String as well as the hl_group. + +The displayer can return values, string and an optional highlights. The string +is all the text to be displayed for this entry as a single string. If parts of +the string are to be highlighted they will be described in the highlights +table. + +For a better understanding of how create() and displayer are used it's best to +look at the code in make_entry.lua. + + +================================================================================ +UTILS *telescope.utils* + +Utilities for writing telescope pickers + +utils.str_byteindex() *telescope.utils.str_byteindex()* + + + Return: ~ + integer + + +utils.path_expand({path}) *telescope.utils.path_expand()* + Hybrid of `vim.fn.expand()` and custom `vim.fs.normalize()` + + Paths starting with '%', '#' or '<' are expanded with `vim.fn.expand()`. + Otherwise avoids using `vim.fn.expand()` due to its overly aggressive + expansion behavior which can sometimes lead to errors or the creation of + non-existent paths when dealing with valid absolute paths. + + Other paths will have '~' and environment variables expanded. Unlike + `vim.fs.normalize()`, backslashes are preserved. This has better + compatibility with `plenary.path` and also avoids mangling valid Unix paths + with literal backslashes. + + Trailing slashes are trimmed. With the exception of root paths. eg. `/` on + Unix or `C:\` on Windows + + + + Parameters: ~ + {path} (string) + + Return: ~ + string + + +utils.transform_path({opts}, {path}) *telescope.utils.transform_path()* + Transform path is a util function that formats a path based on path_display + found in `opts` or the default value from config. It is meant to be used in + make_entry to have a uniform interface for builtins as well as extensions + utilizing the same user configuration Note: It is only supported inside + `make_entry`/`make_display` the use of this function outside of telescope + might yield to undefined behavior and will not be addressed by us + + + Parameters: ~ + {opts} (table) The opts the users passed into the picker. Might + contains a path_display key + {path} (string|nil) The path that should be formatted + + Return: ~ + string: path to be displayed + table: The transformed path ready to be displayed with the styling + + +utils.has_ts_parser({lang}) *telescope.utils.has_ts_parser()* + Checks if treesitter parser for language is installed + + + Parameters: ~ + {lang} (string) + + +utils.notify({funname}, {opts}) *telescope.utils.notify()* + Telescope Wrapper around vim.notify + + + Parameters: ~ + {funname} (string) name of the function that will be + {opts} (table) opts.level string, opts.msg string, opts.once bool + + + +================================================================================ +ACTIONS *telescope.actions* + +These functions are useful for people creating their own mappings. + +Actions can be either normal functions that expect the `prompt_bufnr` as first +argument (1) or they can be a custom telescope type called "action" (2). + +(1) The `prompt_bufnr` of a normal function denotes the identifier of your +picker which can be used to access the picker state. In practice, users most +commonly access from both picker and global state via the following: +> + -- for utility functions + local action_state = require "telescope.actions.state" + + local actions = {} + actions.do_stuff = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) -- picker state + local entry = action_state.get_selected_entry() + end +< + +See |telescope.actions.state| for more information. + +(2) To transform a module of functions into a module of "action"s, you need to +do the following: +> + local transform_mod = require("telescope.actions.mt").transform_mod + + local mod = {} + mod.a1 = function(prompt_bufnr) + -- your code goes here + -- You can access the picker/global state as described above in (1). + end + + mod.a2 = function(prompt_bufnr) + -- your code goes here + end + mod = transform_mod(mod) + + -- Now the following is possible. This means that actions a2 will be executed + -- after action a1. You can chain as many actions as you want. + local action = mod.a1 + mod.a2 + action(bufnr) +< + +Another interesting thing to do is that these actions now have functions you +can call. These functions include `:replace(f)`, `:replace_if(f, c)`, +`replace_map(tbl)` and `enhance(tbl)`. More information on these functions can +be found in the `developers.md` and `lua/tests/automated/action_spec.lua` file. + +actions.move_selection_next({prompt_bufnr}) *telescope.actions.move_selection_next()* + Move the selection to the next entry + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.move_selection_previous({prompt_bufnr}) *telescope.actions.move_selection_previous()* + Move the selection to the previous entry + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.move_selection_worse({prompt_bufnr}) *telescope.actions.move_selection_worse()* + Move the selection to the entry that has a worse score + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.move_selection_better({prompt_bufnr}) *telescope.actions.move_selection_better()* + Move the selection to the entry that has a better score + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.move_to_top({prompt_bufnr}) *telescope.actions.move_to_top()* + Move to the top of the picker + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.move_to_middle({prompt_bufnr}) *telescope.actions.move_to_middle()* + Move to the middle of the picker + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.move_to_bottom({prompt_bufnr}) *telescope.actions.move_to_bottom()* + Move to the bottom of the picker + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.add_selection({prompt_bufnr}) *telescope.actions.add_selection()* + Add current entry to multi select + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.remove_selection({prompt_bufnr}) *telescope.actions.remove_selection()* + Remove current entry from multi select + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.toggle_selection({prompt_bufnr}) *telescope.actions.toggle_selection()* + Toggle current entry status for multi select + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.select_all({prompt_bufnr}) *telescope.actions.select_all()* + Multi select all entries. + - Note: selected entries may include results not visible in the results pop + up. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.drop_all({prompt_bufnr}) *telescope.actions.drop_all()* + Drop all entries from the current multi selection. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.toggle_all({prompt_bufnr}) *telescope.actions.toggle_all()* + Toggle multi selection for all entries. + - Note: toggled entries may include results not visible in the results pop + up. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.preview_scrolling_up({prompt_bufnr}) *telescope.actions.preview_scrolling_up()* + Scroll the preview window up + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.preview_scrolling_down({prompt_bufnr}) *telescope.actions.preview_scrolling_down()* + Scroll the preview window down + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.preview_scrolling_left({prompt_bufnr}) *telescope.actions.preview_scrolling_left()* + Scroll the preview window to the left + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.preview_scrolling_right({prompt_bufnr}) *telescope.actions.preview_scrolling_right()* + Scroll the preview window to the right + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.results_scrolling_up({prompt_bufnr}) *telescope.actions.results_scrolling_up()* + Scroll the results window up + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.results_scrolling_down({prompt_bufnr}) *telescope.actions.results_scrolling_down()* + Scroll the results window down + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.results_scrolling_left({prompt_bufnr}) *telescope.actions.results_scrolling_left()* + Scroll the results window to the left + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.results_scrolling_right({prompt_bufnr}) *telescope.actions.results_scrolling_right()* + Scroll the results window to the right + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.center({prompt_bufnr}) *telescope.actions.center()* + Center the cursor in the window, can be used after selecting a file to edit + You can just map `actions.select_default + actions.center` + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.select_default({prompt_bufnr}) *telescope.actions.select_default()* + Perform default action on selection, usually something like + `:edit ` + + i.e. open the selection in the current buffer + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.select_horizontal({prompt_bufnr}) *telescope.actions.select_horizontal()* + Perform 'horizontal' action on selection, usually something like + `:new ` + + i.e. open the selection in a new horizontal split + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.select_vertical({prompt_bufnr}) *telescope.actions.select_vertical()* + Perform 'vertical' action on selection, usually something like + `:vnew ` + + i.e. open the selection in a new vertical split + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.select_tab({prompt_bufnr}) *telescope.actions.select_tab()* + Perform 'tab' action on selection, usually something like + `:tabedit ` + + i.e. open the selection in a new tab + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.select_drop({prompt_bufnr}) *telescope.actions.select_drop()* + Perform 'drop' action on selection, usually something like + `:drop ` + + i.e. open the selection in a window + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.select_tab_drop({prompt_bufnr}) *telescope.actions.select_tab_drop()* + Perform 'tab drop' action on selection, usually something like + `:tab drop ` + + i.e. open the selection in a new tab + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.file_edit({prompt_bufnr}) *telescope.actions.file_edit()* + Perform file edit on selection, usually something like + `:edit ` + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.file_split({prompt_bufnr}) *telescope.actions.file_split()* + Perform file split on selection, usually something like + `:new ` + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.file_vsplit({prompt_bufnr}) *telescope.actions.file_vsplit()* + Perform file vsplit on selection, usually something like + `:vnew ` + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.file_tab({prompt_bufnr}) *telescope.actions.file_tab()* + Perform file tab on selection, usually something like + `:tabedit ` + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.close({prompt_bufnr}) *telescope.actions.close()* + Close the Telescope window, usually used within an action + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions._close({prompt_bufnr}) *telescope.actions._close()* + Close the Telescope window, usually used within an action + Deprecated and no longer needed, does the same as + |telescope.actions.close|. Might be removed in the future + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.edit_command_line({prompt_bufnr}) *telescope.actions.edit_command_line()* + Set a value in the command line and don't run it, making it editable. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.set_command_line({prompt_bufnr}) *telescope.actions.set_command_line()* + Set a value in the command line and run it + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.edit_search_line({prompt_bufnr}) *telescope.actions.edit_search_line()* + Set a value in the search line and don't search for it, making it editable. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.set_search_line({prompt_bufnr}) *telescope.actions.set_search_line()* + Set a value in the search line and search for it + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.edit_register({prompt_bufnr}) *telescope.actions.edit_register()* + Edit a register + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.paste_register({prompt_bufnr}) *telescope.actions.paste_register()* + Paste the selected register into the buffer + + Note: only meant to be used inside builtin.registers + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.insert_symbol({prompt_bufnr}) *telescope.actions.insert_symbol()* + Insert a symbol into the current buffer (while switching to normal mode) + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.insert_symbol_i({prompt_bufnr}) *telescope.actions.insert_symbol_i()* + Insert a symbol into the current buffer and keeping the insert mode. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_create_branch({prompt_bufnr}) *telescope.actions.git_create_branch()* + Create and checkout a new git branch if it doesn't already exist + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_apply_stash({prompt_bufnr}) *telescope.actions.git_apply_stash()* + Applies an existing git stash + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_checkout({prompt_bufnr}) *telescope.actions.git_checkout()* + Checkout an existing git branch + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_switch_branch({prompt_bufnr}) *telescope.actions.git_switch_branch()* + Switch to git branch. + If the branch already exists in local, switch to that. If the branch is + only in remote, create new branch tracking remote and switch to new one. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_rename_branch() *telescope.actions.git_rename_branch()* + Action to rename selected git branch + + + +actions.git_track_branch({prompt_bufnr}) *telescope.actions.git_track_branch()* + Tell git to track the currently selected remote branch in Telescope + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_delete_branch({prompt_bufnr}) *telescope.actions.git_delete_branch()* + Delete all currently selected branches + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_merge_branch({prompt_bufnr}) *telescope.actions.git_merge_branch()* + Merge the currently selected branch + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_rebase_branch({prompt_bufnr}) *telescope.actions.git_rebase_branch()* + Rebase to selected git branch + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_reset_mixed({prompt_bufnr}) *telescope.actions.git_reset_mixed()* + Reset to selected git commit using mixed mode + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_reset_soft({prompt_bufnr}) *telescope.actions.git_reset_soft()* + Reset to selected git commit using soft mode + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_reset_hard({prompt_bufnr}) *telescope.actions.git_reset_hard()* + Reset to selected git commit using hard mode + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_checkout_current_buffer({prompt_bufnr}) *telescope.actions.git_checkout_current_buffer()* + Checkout a specific file for a given sha + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.git_staging_toggle({prompt_bufnr}) *telescope.actions.git_staging_toggle()* + Stage/unstage selected file + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.send_selected_to_qflist({prompt_bufnr}) *telescope.actions.send_selected_to_qflist()* + Sends the selected entries to the quickfix list, replacing the previous + entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.add_selected_to_qflist({prompt_bufnr}) *telescope.actions.add_selected_to_qflist()* + Adds the selected entries to the quickfix list, keeping the previous + entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.send_to_qflist({prompt_bufnr}) *telescope.actions.send_to_qflist()* + Sends all entries to the quickfix list, replacing the previous entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.add_to_qflist({prompt_bufnr}) *telescope.actions.add_to_qflist()* + Adds all entries to the quickfix list, keeping the previous entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.send_selected_to_loclist({prompt_bufnr}) *telescope.actions.send_selected_to_loclist()* + Sends the selected entries to the location list, replacing the previous + entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.add_selected_to_loclist({prompt_bufnr}) *telescope.actions.add_selected_to_loclist()* + Adds the selected entries to the location list, keeping the previous + entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.send_to_loclist({prompt_bufnr}) *telescope.actions.send_to_loclist()* + Sends all entries to the location list, replacing the previous entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.add_to_loclist({prompt_bufnr}) *telescope.actions.add_to_loclist()* + Adds all entries to the location list, keeping the previous entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.smart_send_to_qflist({prompt_bufnr}) *telescope.actions.smart_send_to_qflist()* + Sends the selected entries to the quickfix list, replacing the previous + entries. If no entry was selected, sends all entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.smart_add_to_qflist({prompt_bufnr}) *telescope.actions.smart_add_to_qflist()* + Adds the selected entries to the quickfix list, keeping the previous + entries. If no entry was selected, adds all entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.smart_send_to_loclist({prompt_bufnr}) *telescope.actions.smart_send_to_loclist()* + Sends the selected entries to the location list, replacing the previous + entries. If no entry was selected, sends all entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.smart_add_to_loclist({prompt_bufnr}) *telescope.actions.smart_add_to_loclist()* + Adds the selected entries to the location list, keeping the previous + entries. If no entry was selected, adds all entries. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.complete_tag({prompt_bufnr}) *telescope.actions.complete_tag()* + Open completion menu containing the tags which can be used to filter the + results in a faster way + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.cycle_history_next({prompt_bufnr}) *telescope.actions.cycle_history_next()* + Cycle to the next search prompt in the history + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.cycle_history_prev({prompt_bufnr}) *telescope.actions.cycle_history_prev()* + Cycle to the previous search prompt in the history + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.open_qflist({prompt_bufnr}) *telescope.actions.open_qflist()* + Open the quickfix list. It makes sense to use this in combination with one + of the send_to_qflist actions `actions.smart_send_to_qflist + + actions.open_qflist` + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.open_loclist({prompt_bufnr}) *telescope.actions.open_loclist()* + Open the location list. It makes sense to use this in combination with one + of the send_to_loclist actions `actions.smart_send_to_qflist + + actions.open_qflist` + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.delete_buffer({prompt_bufnr}) *telescope.actions.delete_buffer()* + Delete the selected buffer or all the buffers selected using multi + selection. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.cycle_previewers_next({prompt_bufnr}) *telescope.actions.cycle_previewers_next()* + Cycle to the next previewer if there is one available. + This action is not mapped on default. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.cycle_previewers_prev({prompt_bufnr}) *telescope.actions.cycle_previewers_prev()* + Cycle to the previous previewer if there is one available. + This action is not mapped on default. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.remove_selected_picker({prompt_bufnr}) *telescope.actions.remove_selected_picker()* + Removes the selected picker in |builtin.pickers|. + This action is not mapped by default and only intended for + |builtin.pickers|. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.which_key({prompt_bufnr}) *telescope.actions.which_key()* + Display the keymaps of registered actions similar to which-key.nvim. + + - Notes: + - The defaults can be overridden via |action_generate.which_key|. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.to_fuzzy_refine({prompt_bufnr}) *telescope.actions.to_fuzzy_refine()* + Move from a none fuzzy search to a fuzzy one + This action is meant to be used in live_grep and + lsp_dynamic_workspace_symbols + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.delete_mark({prompt_bufnr}) *telescope.actions.delete_mark()* + Delete the selected mark or all the marks selected using multi selection. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.insert_original_cword({prompt_bufnr}) *telescope.actions.insert_original_cword()* + Insert the word under the cursor of the original (pre-Telescope) window + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.insert_original_cWORD({prompt_bufnr}) *telescope.actions.insert_original_cWORD()* + Insert the WORD under the cursor of the original (pre-Telescope) window + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.insert_original_cfile({prompt_bufnr}) *telescope.actions.insert_original_cfile()* + Insert the file under the cursor of the original (pre-Telescope) window + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +actions.insert_original_cline({prompt_bufnr}) *telescope.actions.insert_original_cline()* + Insert the line under the cursor of the original (pre-Telescope) window + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + + +================================================================================ +ACTIONS_STATE *telescope.actions.state* + +Functions to be used to determine the current state of telescope. + +Generally used from within other |telescope.actions| + +action_state.get_selected_entry() *telescope.actions.state.get_selected_entry()* + Get the current entry + + + +action_state.get_current_line() *telescope.actions.state.get_current_line()* + Gets the current line in the search prompt + + + +action_state.get_current_picker({prompt_bufnr}) *telescope.actions.state.get_current_picker()* + Gets the current picker + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + + +================================================================================ +ACTIONS_SET *telescope.actions.set* + +Telescope action sets are used to provide an interface for managing actions +that all primarily do the same thing, but with slight tweaks. + +For example, when editing files you may want it in the current split, a +vertical split, etc. Instead of making users have to overwrite EACH of those +every time they want to change this behavior, they can instead replace the +`set` itself and then it will work great and they're done. + +action_set.shift_selection({prompt_bufnr}, {change}) *telescope.actions.set.shift_selection()* + Move the current selection of a picker {change} rows. Handles not + overflowing / underflowing the list. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {change} (number) The amount to shift the selection by + + +action_set.select({prompt_bufnr}, {type}) *telescope.actions.set.select()* + Select the current entry. This is the action set to overwrite common + actions by the user. + + By default maps to editing a file. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {type} (string) The type of selection to make + + +action_set.edit({prompt_bufnr}, {command}) *telescope.actions.set.edit()* + Edit a file based on the current selection. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {command} (string) The command to use to open the file. + + +action_set.scroll_previewer({prompt_bufnr}, {direction}) *telescope.actions.set.scroll_previewer()* + Scrolls the previewer up or down. Defaults to a half page scroll, but can + be overridden using the `scroll_speed` option in `layout_config`. See + |telescope.layout| for more details. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {direction} (number) The direction of the scrolling + + +action_set.scroll_horizontal_previewer({prompt_bufnr}, {direction}) *telescope.actions.set.scroll_horizontal_previewer()* + Scrolls the previewer to the left or right. Defaults to a half page scroll, + but can be overridden using the `scroll_speed` option in `layout_config`. + See |telescope.layout| for more details. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {direction} (number) The direction of the scrolling + + +action_set.scroll_results({prompt_bufnr}, {direction}) *telescope.actions.set.scroll_results()* + Scrolls the results up or down. Defaults to a half page scroll, but can be + overridden using the `scroll_speed` option in `layout_config`. See + |telescope.layout| for more details. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {direction} (number) The direction of the scrolling + + +action_set.scroll_horizontal_results({prompt_bufnr}, {direction}) *telescope.actions.set.scroll_horizontal_results()* + Scrolls the results to the left or right. Defaults to a half page scroll, + but can be overridden using the `scroll_speed` option in `layout_config`. + See |telescope.layout| for more details. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {direction} (number) The direction of the scrolling + + + +================================================================================ +ACTIONS_LAYOUT *telescope.actions.layout* + +The layout actions are actions to be used to change the layout of a picker. + +action_layout.toggle_preview({prompt_bufnr}) *telescope.actions.layout.toggle_preview()* + Toggle preview window. + - Note: preview window can be toggled even if preview is set to false. + + This action is not mapped by default. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +action_layout.toggle_prompt_position({prompt_bufnr}) *telescope.actions.layout.toggle_prompt_position()* + Toggles the `prompt_position` option between "top" and "bottom". Checks if + `prompt_position` is an option for the current layout. + + This action is not mapped by default. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +action_layout.toggle_mirror({prompt_bufnr}) *telescope.actions.layout.toggle_mirror()* + Toggles the `mirror` option between `true` and `false`. Checks if `mirror` + is an option for the current layout. + + This action is not mapped by default. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +action_layout.cycle_layout_next({prompt_bufnr}) *telescope.actions.layout.cycle_layout_next()* + Cycles to the next layout in `cycle_layout_list`. + + This action is not mapped by default. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + +action_layout.cycle_layout_prev({prompt_bufnr}) *telescope.actions.layout.cycle_layout_prev()* + Cycles to the previous layout in `cycle_layout_list`. + + This action is not mapped by default. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + + +================================================================================ +ACTIONS_UTILS *telescope.actions.utils* + +Utilities to wrap functions around picker selections and entries. + +Generally used from within other |telescope.actions| + +utils.map_entries({prompt_bufnr}, {f}) *telescope.actions.utils.map_entries()* + Apply `f` to the entries of the current picker. + - Notes: + - Mapped entries include all currently filtered results, not just the + visible ones. + - Indices are 1-indexed, whereas rows are 0-indexed. + - Warning: `map_entries` has no return value. + - The below example showcases how to collect results + + Usage: + > + local action_state = require "telescope.actions.state" + local action_utils = require "telescope.actions.utils" + function entry_value_by_row() + local prompt_bufnr = vim.api.nvim_get_current_buf() + local current_picker = action_state.get_current_picker(prompt_bufnr) + local results = {} + action_utils.map_entries(prompt_bufnr, function(entry, index, row) + results[row] = entry.value + end) + return results + end +< + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {f} (function) Function to map onto entries of picker that + takes (entry, index, row) as viable + arguments + + +utils.map_selections({prompt_bufnr}, {f}) *telescope.actions.utils.map_selections()* + Apply `f` to the multi selections of the current picker and return a table + of mapped selections. + - Notes: + - Mapped selections may include results not visible in the results pop + up. + - Selected entries are returned in order of their selection. + - Warning: `map_selections` has no return value. + - The below example showcases how to collect results + + Usage: + > + local action_state = require "telescope.actions.state" + local action_utils = require "telescope.actions.utils" + function selection_by_index() + local prompt_bufnr = vim.api.nvim_get_current_buf() + local current_picker = action_state.get_current_picker(prompt_bufnr) + local results = {} + action_utils.map_selections(prompt_bufnr, function(entry, index) + results[index] = entry.value + end) + return results + end +< + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + {f} (function) Function to map onto selection of picker + that takes (selection) as a viable argument + + +utils.get_registered_mappings({prompt_bufnr}) *telescope.actions.utils.get_registered_mappings()* + Utility to collect mappings of prompt buffer in array of `{mode, keybind, + name}`. + + + Parameters: ~ + {prompt_bufnr} (number) The prompt bufnr + + + +================================================================================ +ACTIONS_GENERATE *telescope.actions.generate* + +Module for convenience to override defaults of corresponding +|telescope.actions| at |telescope.setup()|. + +General usage: +> + require("telescope").setup { + defaults = { + mappings = { + n = { + ["?"] = action_generate.which_key { + name_width = 20, -- typically leads to smaller floats + max_height = 0.5, -- increase potential maximum height + separator = " > ", -- change sep between mode, keybind, and name + close_with_action = false, -- do not close float on action + }, + }, + }, + }, + } +< + +action_generate.which_key({opts}) *telescope.actions.generate.which_key()* + Display the keymaps of registered actions similar to which-key.nvim. + + - Floating window: + - Appears on the opposite side of the prompt. + - Resolves to minimum required number of lines to show hints with `opts` + or truncates entries at `max_height`. + - Closes automatically on action call and can be disabled with by setting + `close_with_action` to false. + + + Parameters: ~ + {opts} (table) options to pass to toggling registered actions + + Fields: ~ + {max_height} (number) % of max. height or no. of rows + for hints (default: 0.4), see + |resolver.resolve_height()| + {only_show_current_mode} (boolean) only show keymaps for the current + mode (default: true) + {mode_width} (number) fixed width of mode to be shown + (default: 1) + {keybind_width} (number) fixed width of keybind to be shown + (default: 7) + {name_width} (number) fixed width of action name to be + shown (default: 30) + {column_padding} (string) string to split; can be used for + vertical separator (default: " ") + {mode_hl} (string) hl group of mode (default: + TelescopeResultsConstant) + {keybind_hl} (string) hl group of keybind (default: + TelescopeResultsVariable) + {name_hl} (string) hl group of action name (default: + TelescopeResultsFunction) + {column_indent} (number) number of left-most spaces before + keybinds are shown (default: 4) + {line_padding} (number) row padding in top and bottom of + float (default: 1) + {separator} (string) separator string between mode, key + bindings, and action (default: " + -> ") + {close_with_action} (boolean) registered action will close + keymap float (default: true) + {normal_hl} (string) winhl of "Normal" for keymap hints + floating window (default: + "TelescopePrompt") + {border_hl} (string) winhl of "Normal" for keymap + borders (default: + "TelescopePromptBorder") + {winblend} (number) pseudo-transparency of keymap + hints floating window + {zindex} (number) z-index of keymap hints floating + window (default: 100) + + + +================================================================================ +PREVIEWERS *telescope.previewers* + +Provides a Previewer table that has to be implemented by each previewer. To +achieve this, this module also provides two wrappers that abstract most of the +work and make it really easy to create new previewers. + - `previewers.new_termopen_previewer` + - `previewers.new_buffer_previewer` + +Furthermore, there are a collection of previewers already defined which can be +used for every picker, as long as the entries of the picker provide the +necessary fields. The more important ones are + - `previewers.cat` + - `previewers.vimgrep` + - `previewers.qflist` + - `previewers.vim_buffer_cat` + - `previewers.vim_buffer_vimgrep` + - `previewers.vim_buffer_qflist` + +Previewers can be disabled for any builtin or custom picker by doing :Telescope +find_files previewer=false + +previewers.Previewer() *telescope.previewers.Previewer()* + This is the base table all previewers have to implement. It's possible to + write a wrapper for this because most previewers need to have the same keys + set. Examples of wrappers are: + - `new_buffer_previewer` + - `new_termopen_previewer` + + To create a new table do following: + - `local new_previewer = Previewer:new(opts)` + + What `:new` expects is listed below + + The interface provides the following set of functions. All of them, besides + `new`, will be handled by telescope pickers. + - `:new(opts)` + - `:preview(entry, status)` + - `:teardown()` + - `:send_input(input)` + - `:scroll_fn(direction)` + - `:scroll_horizontal_fn(direction)` + + `Previewer:new()` expects a table as input with following keys: + - `setup` function(self): Will be called the first time preview will be + called. + - `teardown` function(self): Will be called on clean up. + - `preview_fn` function(self, entry, status): Will be called each time a + new entry was selected. + - `title` function(self): Will return the static title of the previewer. + - `dynamic_title` function(self, entry): Will return the dynamic title of + the previewer. Will only be called when config value + dynamic_preview_title is true. + - `send_input` function(self, input): This is meant for + `termopen_previewer` and it can be used to send input to the terminal + application, like less. + - `scroll_fn` function(self, direction): Used to make scrolling work. + - `scroll_horizontal_fn` function(self, direction): Used to make + horizontal scrolling work. + + + +previewers.new() *telescope.previewers.new()* + A shorthand for creating a new Previewer. The provided table will be + forwarded to `Previewer:new(...)` + + + +previewers.new_termopen_previewer() *telescope.previewers.new_termopen_previewer()* + Is a wrapper around Previewer and helps with creating a new + `termopen_previewer`. + + It requires you to specify one table entry `get_command(entry, status)`. + This `get_command` function has to return the terminal command that will be + executed for each entry. Example: + > + get_command = function(entry, status) + return { 'bat', entry.path } + end +< + + Additionally you can define: + - `title` a static title for example "File Preview" + - `dyn_title(self, entry)` a dynamic title function which gets called when + config value `dynamic_preview_title = true` + - `env` table: define environment variables to forward to the terminal + process. Example: + - `{ ['PAGER'] = '', ['MANWIDTH'] = 50 }` + + It's an easy way to get your first previewer going and it integrates well + with `bat` and `less`. Providing out of the box scrolling if the command + uses less. + + Furthermore, if `env` is not set, it will forward all `config.set_env` + environment variables to that terminal process. + + + +previewers.cat() *telescope.previewers.cat()* + Provides a `termopen_previewer` which has the ability to display files. It + will always show the top of the file and has support for `bat`(prioritized) + and `cat`. Each entry has to provide either the field `path` or `filename` + in order to make this previewer work. + + The preferred way of using this previewer is like this + `require('telescope.config').values.cat_previewer` This will respect user + configuration and will use `buffer_previewers` in case it's configured that + way. + + + +previewers.vimgrep() *telescope.previewers.vimgrep()* + Provides a `termopen_previewer` which has the ability to display files at + the provided line. It has support for `bat`(prioritized) and `cat`. Each + entry has to provide either the field `path` or `filename` and a `lnum` + field in order to make this previewer work. + + The preferred way of using this previewer is like this + `require('telescope.config').values.grep_previewer` This will respect user + configuration and will use `buffer_previewers` in case it's configured that + way. + + + +previewers.qflist() *telescope.previewers.qflist()* + Provides a `termopen_previewer` which has the ability to display files at + the provided line or range. It has support for `bat`(prioritized) and + `cat`. Each entry has to provide either the field `path` or `filename`, + `lnum` and a `start` and `finish` range in order to make this previewer + work. + + The preferred way of using this previewer is like this + `require('telescope.config').values.qflist_previewer` This will respect + user configuration and will use buffer previewers in case it's configured + that way. + + + +previewers.new_buffer_previewer() *telescope.previewers.new_buffer_previewer()* + An interface to instantiate a new `buffer_previewer`. That means that the + content actually lives inside a vim buffer which enables us more control + over the actual content. For example, we can use `vim.fn.search` to jump to + a specific line or reuse buffers/already opened files more easily. This + interface is more complex than `termopen_previewer` but offers more + flexibility over your content. It was designed to display files but was + extended to also display the output of terminal commands. + + In the following options, state table and general tips are mentioned to + make your experience with this previewer more seamless. + + + options: + - `define_preview = function(self, entry, status)` (required) Is called + for each selected entry, after each selection_move (up or down) and is + meant to handle things like reading file, jump to line or attach a + highlighter. + - `setup = function(self)` (optional) Is called once at the beginning, + before the preview for the first entry is displayed. You can return a + table of vars that will be available in `self.state` in each + `define_preview` call. + - `teardown = function(self)` (optional) Will be called at the end, when + the picker is being closed and is meant to clean up everything that was + allocated by the previewer. The `buffer_previewer` will automatically + clean up all created buffers. So you only need to handle things that + were introduced by you. + - `keep_last_buf = true` (optional) Will not delete the last selected + buffer. This would allow you to reuse that buffer in the select action. + For example, that buffer can be opened in a new split, rather than + recreating that buffer in an action. To access the last buffer number: + `require('telescope.state').get_global_key("last_preview_bufnr")` + - `get_buffer_by_name = function(self, entry)` Allows you to set a unique + name for each buffer. This is used for caching purposes. + `self.state.bufname` will be nil if the entry was never loaded or the + unique name when it was loaded once. For example, useful if you have + one file but multiple entries. This happens for grep and lsp builtins. + So to make the cache work only load content if `self.state.bufname ~= + entry.your_unique_key` + - `title` a static title for example "File Preview" + - `dyn_title(self, entry)` a dynamic title function which gets called + when config value `dynamic_preview_title = true` + + `self.state` table: + - `self.state.bufnr` Is the current buffer number, in which you have to + write the loaded content. Don't create a buffer yourself, otherwise + it's not managed by the buffer_previewer interface and you will + probably be better off writing your own interface. + - self.state.winid Current window id. Useful if you want to set the + cursor to a provided line number. + - self.state.bufname Will return the current buffer name, if + `get_buffer_by_name` is defined. nil will be returned if the entry was + never loaded or when `get_buffer_by_name` is not set. + + Tips: + - If you want to display content of a terminal job, use: + `require('telescope.previewers.utils').job_maker(cmd, bufnr, opts)` + - `cmd` table: for example { 'git', 'diff', entry.value } + - `bufnr` number: in which the content will be written + - `opts` table: with following keys + - `bufname` string: used for cache + - `value` string: used for cache + - `mode` string: either "insert" or "append". "insert" is default + - `env` table: define environment variables. Example: + - `{ ['PAGER'] = '', ['MANWIDTH'] = 50 }` + - `cwd` string: define current working directory for job + - `callback` function(bufnr, content): will be called when job is + done. Content will be nil if job is already loaded. So you can do + highlighting only the first time the previewer is created for + that entry. Use the returned `bufnr` and not `self.state.bufnr` + in callback, because state can already be changed at this point + in time. + - If you want to attach a highlighter use: + - `require('telescope.previewers.utils').highlighter(bufnr, ft)` + - This will prioritize tree sitter highlighting if available for + environment and language. + - `require('telescope.previewers.utils').regex_highlighter(bufnr, ft)` + - `require('telescope.previewers.utils').ts_highlighter(bufnr, ft)` + - If you want to use `vim.fn.search` or similar you need to run it in + that specific buffer context. Do + > + vim.api.nvim_buf_call(bufnr, function() + -- for example `search` and `matchadd` + end) +< + to achieve that. + - If you want to read a file into the buffer it's best to use + `buffer_previewer_maker`. But access this function with + `require('telescope.config').values.buffer_previewer_maker` because it + can be redefined by users. + + + +previewers.buffer_previewer_maker({filepath}, {bufnr}, {opts}) *telescope.previewers.buffer_previewer_maker()* + A universal way of reading a file into a buffer previewer. It handles async + reading, cache, highlighting, displaying directories and provides a + callback which can be used, to jump to a line in the buffer. + + + Parameters: ~ + {filepath} (string) String to the filepath, will be expanded + {bufnr} (number) Where the content will be written + {opts} (table) keys: `use_ft_detect`, `bufname` and `callback` + + +previewers.vim_buffer_cat() *telescope.previewers.vim_buffer_cat()* + A previewer that is used to display a file. It uses the `buffer_previewer` + interface and won't jump to the line. To integrate this one into your own + picker make sure that the field `path` or `filename` is set for each entry. + The preferred way of using this previewer is like this + `require('telescope.config').values.file_previewer` This will respect user + configuration and will use `termopen_previewer` in case it's configured + that way. + + + +previewers.vim_buffer_vimgrep() *telescope.previewers.vim_buffer_vimgrep()* + A previewer that is used to display a file and jump to the provided line. + It uses the `buffer_previewer` interface. To integrate this one into your + own picker make sure that the field `path` or `filename` and `lnum` is set + in each entry. If the latter is not present, it will default to the first + line. Additionally, `lnend`, `col` and `colend` can be set to highlight a + text range instead of a single line. All line/column values are 1-indexed. + The preferred way of using this previewer is like this + `require('telescope.config').values.grep_previewer` This will respect user + configuration and will use `termopen_previewer` in case it's configured + that way. + + + +previewers.vim_buffer_qflist() *telescope.previewers.vim_buffer_qflist()* + Is the same as `vim_buffer_vimgrep` and only exists for consistency with + `term_previewers`. + + The preferred way of using this previewer is like this + `require('telescope.config').values.qflist_previewer` This will respect + user configuration and will use `termopen_previewer` in case it's + configured that way. + + + +previewers.git_branch_log() *telescope.previewers.git_branch_log()* + A previewer that shows a log of a branch as graph + + + +previewers.git_stash_diff() *telescope.previewers.git_stash_diff()* + A previewer that shows a diff of a stash + + + +previewers.git_commit_diff_to_parent() *telescope.previewers.git_commit_diff_to_parent()* + A previewer that shows a diff of a commit to a parent commit. + The run command is `git --no-pager diff SHA^! -- $CURRENT_FILE` + + The current file part is optional. So is only uses it with bcommits. + + + +previewers.git_commit_diff_to_head() *telescope.previewers.git_commit_diff_to_head()* + A previewer that shows a diff of a commit to head. + The run command is `git --no-pager diff --cached $SHA -- $CURRENT_FILE` + + The current file part is optional. So is only uses it with bcommits. + + + +previewers.git_commit_diff_as_was() *telescope.previewers.git_commit_diff_as_was()* + A previewer that shows a diff of a commit as it was. + The run command is `git --no-pager show $SHA:$CURRENT_FILE` or `git + --no-pager show $SHA` + + + +previewers.git_commit_message() *telescope.previewers.git_commit_message()* + A previewer that shows the commit message of a diff. + The run command is `git --no-pager log -n 1 $SHA` + + + +previewers.git_file_diff() *telescope.previewers.git_file_diff()* + A previewer that shows the current diff of a file. Used in git_status. + The run command is `git --no-pager diff $FILE` + + + +previewers.display_content() *telescope.previewers.display_content()* + A deprecated way of displaying content more easily. Was written at a time, + where the buffer_previewer interface wasn't present. Nowadays it's easier + to just use this. We will keep it around for backwards compatibility + because some extensions use it. It doesn't use cache or some other clever + tricks. + + + + +================================================================================ +HISTORY *telescope.actions.history* + +A base implementation of a prompt history that provides a simple history and +can be replaced with a custom implementation. + +For example: We provide an extension for a smart history that uses sql.nvim to +map histories to metadata, like the calling picker or cwd. + +So you have a history for: +- find_files project_1 +- grep_string project_1 +- live_grep project_1 +- find_files project_2 +- grep_string project_2 +- live_grep project_2 +- etc + +See https://github.com/nvim-telescope/telescope-smart-history.nvim + +histories.History() *telescope.actions.history.History()* + Manages prompt history + + + Fields: ~ + {enabled} (boolean) Will indicate if History is enabled or + disabled + {path} (string) Will point to the location of the history file + {limit} (string) Will have the limit of the history. Can be + nil, if limit is disabled. + {content} (table) History table. Needs to be filled by your own + History implementation + {index} (number) Used to keep track of the next or previous + index. Default is #content + 1 + {cycle_wrap} (boolean) Controls if history will wrap on reaching + beginning or end + + +histories.History:new({opts}) *telescope.actions.history.History:new()* + Create a new History + + + Parameters: ~ + {opts} (table) Defines the behavior of History + + Fields: ~ + {init} (function) Will be called after handling configuration + (required) + {append} (function) How to append a new prompt item (required) + {reset} (function) What happens on reset. Will be called when + telescope closes (required) + {pre_get} (function) Will be called before a next or previous item + will be returned (optional) + + +histories.new() *telescope.actions.history.new()* + Shorthand to create a new history + + + +histories.History:reset() *telescope.actions.history.History:reset()* + Will reset the history index to the default initial state. Will happen + after the picker closed + + + +histories.History:append({line}, {picker}, {no_reset}) *telescope.actions.history.History:append()* + Append a new line to the history + + + Parameters: ~ + {line} (string) current line that will be appended + {picker} (table) the current picker object + {no_reset} (boolean) On default it will reset the state at the end. + If you don't want to do this set to true + + +histories.History:get_next({line}, {picker}) *telescope.actions.history.History:get_next()* + Will return the next history item. Can be nil if there are no next items + + + Parameters: ~ + {line} (string) the current line + {picker} (table) the current picker object + + Return: ~ + string: the next history item + + +histories.History:get_prev({line}, {picker}) *telescope.actions.history.History:get_prev()* + Will return the previous history item. Can be nil if there are no previous + items + + + Parameters: ~ + {line} (string) the current line + {picker} (table) the current picker object + + Return: ~ + string: the previous history item + + +histories.get_simple_history() *telescope.actions.history.get_simple_history()* + A simple implementation of history. + + It will keep one unified history across all pickers. + + + + + vim:tw=78:ts=8:ft=help:norl: diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/doc/telescope_changelog.txt b/.config/nvim/pack/vendor/start/telescope.nvim/doc/telescope_changelog.txt new file mode 100644 index 0000000..08750a7 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/doc/telescope_changelog.txt @@ -0,0 +1,282 @@ +================================================================================ + *telescope.changelog* + +# Changelog + + *telescope.changelog-922* + +Date: May 17, 2021 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/922 + +This is one of our largest breaking changes thus far, so I (TJ) am adding some +information here so that you can more easily update (without having to track +down the commit, etc.). + +The goal of these breaking changes is to greatly simplify the way +configuration for layouts happen. This should make it much easier to configure +each picker, layout_strategy, and more. Please report any bugs or behavior +that is broken / confusing upstream and we can try and make the configuration +better. + +|telescope.setup()| has changed `layout_defaults` -> `layout_config`. + This makes it so that the setup and the pickers share the same key, + otherwise it is too confusing which key is for which. + + +`picker:find()` now has different values available for configuring the UI. + All configuration for the layout must be passed in the key: + `layout_config`. + + Previously, these keys were passed via `picker:find(opts)`, but should be + passed via `opts.layout_config` now. + - {height} + - {width} + - {prompt_position} + - {preview_cutoff} + + These keys are removed: + - {results_height}: This key is no longer valid. Instead, use `height` + and the corresponding `preview_*` options for the layout strategy to + get the correct results height. This simplifies the configuration + for many of the existing strategies. + + - {results_width}: This key actually never did anything. It was + leftover from some hacking that I had attempted before. Instead you + should be using something like the `preview_width` configuration + option for |layout_strategies.horizontal()| + + You should get error messages when you try and use any of the above keys now. + + *telescope.changelog-839* + +Date: July 7, 2021 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/839 + +Small breaking change regarding `shorten_path` and `hide_filename`. +This allows to configure path displays on a global level and offers a way for +extension developers to make use of the same configuration, offering a better +overall experience. + +The new way to configure to configure path displays is with: + `path_display`: It is a table and accepts multiple values: + - "hidden" hide file names + - "tail" only display the file name, and not the path + - "absolute" display absolute paths + - "shorten" only display the first character of each directory in + the path + see |telescope.defaults.path_display| + +Example would be for a global configuration: + require("telescope").setup{ + defaults = { + path_display = { + "shorten", + "absolute", + }, + } + } + +You can also still pass this to a single builtin call: + require("telescope.builtin").find_files { + path_display = { "shorten" } + } + +For extension developers there is a new util function that can be used to +display a path: + local filename = utils.transform_path(opts, entry.filename) + + *telescope.changelog-473* + +Date: July 14, 2021 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/473 + +Deprecation of telescope.path + +Extension developers need to move to plenary.path, because we will remove the +telescope.path module soon. + +Guide to switch over to plenary.path + - separator + before: require("telescope.path").separator + now: require("plenary.path").path.sep + - home + before: require("telescope.path").home + now: require("plenary.path").path.home + - make_relative + before: require("telescope.path").make_relative(filepath, cwd) + now: require("plenary.path"):new(filepath):make_relative(cwd) + - shorten + before: require("telescope.path").shorten(filepath) + now: require("plenary.path"):new(filepath):shorten() + with optional len, default is 1 + - normalize + before: require("telescope.path").normalize(filepath, cwd) + now: require("plenary.path"):new(filepath):normalize(cwd) + - read_file + before: require("telescope.path").read_file(filepath) + now: require("plenary.path"):new(filepath):read() + - read_file_async + before: require("telescope.path").read_file_async(filepath, callback) + now: require("plenary.path"):new(filepath):read(callback) + + *telescope.changelog-1406* + +Date: November 4, 2021 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/1406 + +Telescope requires Neovim release 0.5.1 or a recent nightly + +Due to making use of newly implemented extmark features, Telescope now +requires users to be on Neovim 0.5.1 (the most recent stable version) or on +the LATEST version of Neovim nightly. + + + *telescope.changelog-1549* + +Date: December 10, 2021 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/1549 + +Telescope requires now Neovim release 0.6.0 or a more recent nightly. +If you are running neovim nightly, you need to make sure that you are on the +LATEST version. Every other commit is not supported. So make sure you build +the newest nightly before reporting issues. + + + *telescope.changelog-1553* + +Date: December 10, 2021 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/1553 + +Move from `vim.lsp.diagnostic` to `vim.diagnostic`. + +Because the newly added `vim.diagnostic` has no longer anything to do with lsp +we also decided to rename our diagnostic functions: + Telescope lsp_document_diagnostics -> Telescope diagnostics bufnr=0 + Telescope lsp_workspace_diagnostics -> Telescope diagnostics +Because of that the `lsp_*_diagnostics` inside Telescope will be deprecated +and removed soon. The new `diagnostics` works almost identical to the previous +functions. Note that there is no longer a workspace diagnostics. You can only +get all diagnostics for all open buffers. + + + *telescope.changelog-1851* + +Date: April 22, 2022 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/1851 + +Telescope requires now Neovim release 0.7.0 or a more recent nightly. +If you are running Neovim nightly, you need to make sure that you are on the +LATEST version. Every other commit is not supported. So make sure you build +the newest nightly before reporting issues. +In the future, we will adopt a different release strategy. This release +strategy follows the approach that the latest telescope.nvim master will only +work with latest Neovim nightly and we will provide tags for specific Neovim +versions. You can read more about this strategy here: +https://github.com/nvim-telescope/telescope.nvim/issues/1772 + + + *telescope.changelog-1866* + +Date: April 25, 2022 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/1866 + +We decided to remove both `lsp_code_actions` and `lsp_range_code_actions`. +Currently, both functions are highly duplicated code from neovim, with fewer +features, because it's out of date. So rather that we copy over the required +changes to fix some bugs or implement client side code actions, we decided to +remove both of them and suggest you use `vim.lsp.buf.code_action` and +`vim.lsp.buf.range_code_action`. The transition to it is easy thanks to +`vim.ui.select` which allows you to override the select UI. We provide a small +extension for quite some time that make it easy to use telescope for +`vim.ui.select`. You can found the code here +https://github.com/nvim-telescope/telescope-ui-select.nvim. It offers the same +displaying as the current version of `lsp_code_actions`. An alternative is +https://github.com/stevearc/dressing.nvim which has support for multiple +different backends including telescope. + + + *telescope.changelog-1945* + +Date: July 01, 2022 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/1945 + +This is our dev branch which contains a lot of PRs, a lot of fixes, +refactoring and general quality of life improvements. It also contains new +features, the most noteworthy are the following (mostly developed by the +community): +- feat: none strategy & control attachment (#1867) +- feat: force buffer delete for terminal and improvements for + Picker:delete_selection (#1943) +- feat(tags): process tagfiles on the fly (#1989) +- feat(builtin.lsp): implement builtin handlers for + lsp.(incoming|outgoing)_calls (#1484) +- feat: clear previewer if no item is selected (#2004) +- feat: add min max boundary to width, height resolver (#2002) +- feat: Add entry_index for entry_makers (#1850) +- feat: refine with new_table (#1115) + +The last one is one of the most exciting new features, because it allows you +to go from live_grep into a fuzzy environment with the following mapping +``. It's a general interface we now implemented for `live_grep` and +`lsp_dynamic_workspace_symbols` but it could also be easily implemented for +other builtins, by us or the user. It's now available for extension developers. +We will add documentation in the next couple of days and improve it by adding +more options to configure it after the initial 0.1 release. + +But as with all longer development phases, there are also some breaking +changes. This is the main reason we moved development to a separate branch, for +the past two months. We can't promise that there won't be more breaking +changes, but it is the plan that this is the last set of breaking changes prior +to the 0.1 release on July, 12. We are deeply sorry for the inconvenience. The +following breaking changes are included in this PR: +- break(git_files): change `show_untracked` default to false. Can be changed + back with `:Telescope git_files show_untracked=true` +- break: deprecate `utils.get_default` `utils.if_nil`, will be removed prior + to 0.1, so if you use it in your config, please move to `vim.F.if_nil` +- break: drops `ignore_filename` option, use `path_display= { "hidden" }` + instead +- break: prefix internal interfaces with __ so + `require("telescope.builtin.files").find_files` will show a notify error but + still works for now. The error will be removed prior to 0.1! You should use + `require("telescope.builtin").find_files` because we wrap all the functions + that are exposed in this module. +- break: defaults.preview.treesitter rework that allows you to either enable a + list of languages, or enable all and disable some. Please read + `:help telescope.defaults.preview` for more information. + Something like this is now possible: + > + treesitter = { + enable = false, + -- or + enable = { "c" }, + -- disable can be set if enable isn't set + disable = { "perl", "javascript" }, + }, +< + + + *telescope.changelog-2499* + +Date: May 24, 2023 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/2499 + +We decided to bump the minimum Neovim version to 0.9.0, in order to remove a +couple of no longer required workarounds. That includes using upstream +treesitter implementation in favor of nvim-treesitter. +If you still have a requirement for Neovim 0.7 or 0.8, we also have a stable +branch 0.1.x (or version, currently 0.1.1) which will not receive this version +bump and will continue to offer support for older Neovim versions. + + + *telescope.changelog-2529* + +Date: June 09, 2023 +PR: https://github.com/nvim-telescope/telescope.nvim/pull/2529 + +We finally removed usage of `plenary.filetype` to determine filetypes for +previewing and replaced it with `vim.filetype`. So if you have highlighting +issues you no longer have to configure `plenary`, but rather read +|vim.filetype|. + + + vim:tw=78:ts=8:ft=help:norl: diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/ftplugin/TelescopePrompt.lua b/.config/nvim/pack/vendor/start/telescope.nvim/ftplugin/TelescopePrompt.lua new file mode 100644 index 0000000..8888a88 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/ftplugin/TelescopePrompt.lua @@ -0,0 +1,12 @@ +-- Don't wrap textwidth things +vim.opt_local.formatoptions:remove "t" +vim.opt_local.formatoptions:remove "c" + +-- Don't include `showbreak` when calculating strdisplaywidth +vim.opt_local.wrap = false + +-- There's also no reason to enable textwidth here anyway +vim.opt_local.textwidth = 0 +vim.opt_local.scrollbind = false + +vim.opt_local.signcolumn = "no" diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/ftplugin/TelescopeResults.lua b/.config/nvim/pack/vendor/start/telescope.nvim/ftplugin/TelescopeResults.lua new file mode 100644 index 0000000..08e9dcc --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/ftplugin/TelescopeResults.lua @@ -0,0 +1,5 @@ +-- Don't have scrolloff, it makes things weird. +vim.opt_local.scrolloff = 0 +vim.opt_local.scrollbind = false + +vim.opt_local.signcolumn = "no" diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/.DS_Store b/.config/nvim/pack/vendor/start/telescope.nvim/lua/.DS_Store new file mode 100644 index 0000000..9892ce4 Binary files /dev/null and b/.config/nvim/pack/vendor/start/telescope.nvim/lua/.DS_Store differ diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/_.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/_.lua new file mode 100644 index 0000000..6adc5a6 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/_.lua @@ -0,0 +1,324 @@ +local uv = vim.loop + +local Object = require "plenary.class" +local log = require "plenary.log" + +local async = require "plenary.async" +local channel = require("plenary.async").control.channel +local utils = require "telescope.utils" + +local M = {} + +local AsyncJob = {} +AsyncJob.__index = AsyncJob + +function AsyncJob.new(opts) + local self = setmetatable({}, AsyncJob) + + self.command, self.uv_opts = M.convert_opts(opts) + + self.stdin = opts.stdin or M.NullPipe() + self.stdout = opts.stdout or M.NullPipe() + self.stderr = opts.stderr or M.NullPipe() + + if opts.cwd and opts.cwd ~= "" then + self.uv_opts.cwd = utils.path_expand(opts.cwd) + -- this is a "illegal" hack for windows. E.g. If the git command returns `/` rather than `\` as delimiter, + -- vim.fn.expand might just end up returning an empty string. Weird + -- Because empty string is not allowed in libuv the job will not spawn. Solution is we just set it to opts.cwd + if self.uv_opts.cwd == "" then + self.uv_opts.cwd = opts.cwd + end + end + + self.uv_opts.stdio = { + self.stdin.handle, + self.stdout.handle, + self.stderr.handle, + } + + return self +end + +function AsyncJob:_for_each_pipe(f, ...) + for _, pipe in ipairs { self.stdin, self.stdout, self.stderr } do + f(pipe, ...) + end +end + +function AsyncJob:close(force) + if force == nil then + force = true + end + + self:_for_each_pipe(function(p) + p:close(force) + end) + + uv.process_kill(self.handle, "sigterm") + + log.debug "[async_job] closed" +end + +M.spawn = function(opts) + local self = AsyncJob.new(opts) + self.handle, self.pid = uv.spawn( + self.command, + self.uv_opts, + async.void(function() + self:close(false) + if not self.handle:is_closing() then + self.handle:close() + end + end) + ) + + if not self.handle then + error(debug.traceback("Failed to spawn process: " .. vim.inspect(self))) + end + + return self +end + +---@class uv_pipe_t +--- A pipe handle from libuv +---@field read_start function: Start reading +---@field read_stop function: Stop reading +---@field close function: Close the handle +---@field is_closing function: Whether handle is currently closing +---@field is_active function: Whether the handle is currently reading + +---@class BasePipe +---@field super Object: Always available +---@field handle uv_pipe_t: A pipe handle +---@field extend function: Extend +local BasePipe = Object:extend() + +function BasePipe:new() + self.eof_tx, self.eof_rx = channel.oneshot() +end + +function BasePipe:close(force) + if force == nil then + force = true + end + + assert(self.handle, "Must have a pipe to close. Otherwise it's weird!") + + if self.handle:is_closing() then + return + end + + -- If we're not forcing the stop, allow waiting for eof + -- This ensures that we don't end up with weird race conditions + if not force then + self.eof_rx() + end + + self.handle:read_stop() + if not self.handle:is_closing() then + self.handle:close() + end + + self._closed = true +end + +---@class LinesPipe : BasePipe +local LinesPipe = BasePipe:extend() + +function LinesPipe:new() + LinesPipe.super.new(self) + self.handle = uv.new_pipe(false) +end + +function LinesPipe:read() + local read_tx, read_rx = channel.oneshot() + + self.handle:read_start(function(err, data) + assert(not err, err) + self.handle:read_stop() + + read_tx(data) + if data == nil then + self.eof_tx() + end + end) + + return read_rx() +end + +function LinesPipe:iter(schedule) + if schedule == nil then + schedule = true + end + + local text = nil + local index = nil + + local get_next_text = function(previous) + index = nil + + local read = self:read() + if previous == nil and read == nil then + return + end + + read = string.gsub(read or "", "\r", "") + return (previous or "") .. read + end + + local next_value = nil + next_value = function() + if schedule then + async.util.scheduler() + end + + if text == nil or (text == "" and index == nil) then + return nil + end + + local start = index + index = string.find(text, "\n", index, true) + + if index == nil then + text = get_next_text(string.sub(text, start or 1)) + return next_value() + end + + index = index + 1 + + return string.sub(text, start or 1, index - 2) + end + + text = get_next_text() + + return function() + return next_value() + end +end + +---@class NullPipe : BasePipe +local NullPipe = BasePipe:extend() + +function NullPipe:new() + NullPipe.super.new(self) + self.start = function() end + self.read_start = function() end + self.close = function() end + + -- This always has eof tx done, so can just call it now + self.eof_tx() +end + +---@class ChunkPipe : BasePipe +local ChunkPipe = BasePipe:extend() + +function ChunkPipe:new() + ChunkPipe.super.new(self) + self.handle = uv.new_pipe(false) +end + +function ChunkPipe:read() + local read_tx, read_rx = channel.oneshot() + + self.handle:read_start(function(err, data) + assert(not err, err) + self.handle:read_stop() + + read_tx(data) + if data == nil then + self.eof_tx() + end + end) + + return read_rx() +end + +function ChunkPipe:iter() + return function() + if self._closed then + return nil + end + + return self:read() + end +end + +---@class ErrorPipe : BasePipe +local ErrorPipe = BasePipe:extend() + +function ErrorPipe:new() + ErrorPipe.super.new(self) + self.handle = uv.new_pipe(false) +end + +function ErrorPipe:start() + self.handle:read_start(function(err, data) + if not err and not data then + return + end + + self.handle:read_stop() + self.handle:close() + + error(string.format("Err: %s, Data: '%s'", err, data)) + end) +end + +M.NullPipe = NullPipe +M.LinesPipe = LinesPipe +M.ChunkPipe = ChunkPipe +M.ErrorPipe = ErrorPipe + +M.convert_opts = function(o) + if not o then + error(debug.traceback "Options are required for Job:new") + end + + local command = o.command + if not command then + if o[1] then + command = o[1] + else + error(debug.traceback "'command' is required for Job:new") + end + elseif o[1] then + error(debug.traceback "Cannot pass both 'command' and array args") + end + + local args = o.args + if not args then + if #o > 1 then + args = { select(2, unpack(o)) } + end + end + + local ok, is_exe = pcall(vim.fn.executable, command) + if not o.skip_validation and ok and 1 ~= is_exe then + error(debug.traceback(command .. ": Executable not found")) + end + + local obj = {} + + obj.args = args + + if o.env then + if type(o.env) ~= "table" then + error(debug.traceback "'env' has to be a table") + end + + local transform = {} + for k, v in pairs(o.env) do + if type(k) == "number" then + table.insert(transform, v) + elseif type(k) == "string" then + table.insert(transform, k .. "=" .. tostring(v)) + end + end + obj.env = transform + end + + return command, obj +end + +return M diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/_extensions/init.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/_extensions/init.lua new file mode 100644 index 0000000..c31205a --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/_extensions/init.lua @@ -0,0 +1,73 @@ +local extensions = {} + +extensions._loaded = {} +extensions._config = {} +extensions._health = {} + +local load_extension = function(name) + local ok, ext = pcall(require, "telescope._extensions." .. name) + if not ok then + error(string.format("'%s' extension doesn't exist or isn't installed: %s", name, ext)) + end + return ext +end + +extensions.manager = setmetatable({}, { + __index = function(t, k) + local ext = load_extension(k) + t[k] = ext.exports or {} + if ext.setup then + ext.setup(extensions._config[k] or {}, require("telescope.config").values) + end + extensions._health[k] = ext.health + + return t[k] + end, +}) + +--- Register an extension module. +--- +--- Extensions have several important keys. +--- - setup: +--- function(ext_config, config) -> nil +--- +--- Called when first loading the extension. +--- The first parameter is the config passed by the user +--- in telescope setup. The second parameter is the resulting +--- config.values after applying the users setup defaults. +--- +--- It is acceptable for a plugin to override values in config, +--- as some plugins will be installed simply to manage some setup, +--- install some sorter, etc. +--- +--- - exports: +--- table +--- +--- Only the items in `exports` will be exposed on the resulting +--- module that users can access via require('telescope').extensions.foo +--- Also, any top-level key-value pairs in exports where the value is a function and the +--- key doesn't start with an underscore will be included when calling the `builtin` picker +--- with the `include_extensions` option enabled. +--- +--- Other things in the module will not be accessible. This is the public API +--- for your extension. Consider not breaking it a lot :laugh: +--- +--- TODO: +--- - actions +extensions.register = function(mod) + return mod +end + +extensions.load = function(name) + local ext = load_extension(name) + if ext.setup then + ext.setup(extensions._config[name] or {}, require("telescope.config").values) + end + return extensions.manager[name] +end + +extensions.set_config = function(extensions_config) + extensions._config = extensions_config or {} +end + +return extensions diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/generate.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/generate.lua new file mode 100644 index 0000000..a557969 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/generate.lua @@ -0,0 +1,119 @@ +---@tag telescope.actions.generate +---@config { ["module"] = "telescope.actions.generate", ["name"] = "ACTIONS_GENERATE" } + +---@brief [[ +--- Module for convenience to override defaults of corresponding |telescope.actions| at |telescope.setup()|. +--- +--- General usage: +--- +--- require("telescope").setup { +--- defaults = { +--- mappings = { +--- n = { +--- ["?"] = action_generate.which_key { +--- name_width = 20, -- typically leads to smaller floats +--- max_height = 0.5, -- increase potential maximum height +--- separator = " > ", -- change sep between mode, keybind, and name +--- close_with_action = false, -- do not close float on action +--- }, +--- }, +--- }, +--- }, +--- } +--- +---@brief ]] + +local actions = require "telescope.actions" +local config = require "telescope.config" +local action_state = require "telescope.actions.state" +local finders = require "telescope.finders" + +local action_generate = {} + +--- Display the keymaps of registered actions similar to which-key.nvim.
+--- - Floating window: +--- - Appears on the opposite side of the prompt. +--- - Resolves to minimum required number of lines to show hints with `opts` or truncates entries at `max_height`. +--- - Closes automatically on action call and can be disabled with by setting `close_with_action` to false. +---@param opts table: options to pass to toggling registered actions +---@field max_height number: % of max. height or no. of rows for hints (default: 0.4), see |resolver.resolve_height()| +---@field only_show_current_mode boolean: only show keymaps for the current mode (default: true) +---@field mode_width number: fixed width of mode to be shown (default: 1) +---@field keybind_width number: fixed width of keybind to be shown (default: 7) +---@field name_width number: fixed width of action name to be shown (default: 30) +---@field column_padding string: string to split; can be used for vertical separator (default: " ") +---@field mode_hl string: hl group of mode (default: TelescopeResultsConstant) +---@field keybind_hl string: hl group of keybind (default: TelescopeResultsVariable) +---@field name_hl string: hl group of action name (default: TelescopeResultsFunction) +---@field column_indent number: number of left-most spaces before keybinds are shown (default: 4) +---@field line_padding number: row padding in top and bottom of float (default: 1) +---@field separator string: separator string between mode, key bindings, and action (default: " -> ") +---@field close_with_action boolean: registered action will close keymap float (default: true) +---@field normal_hl string: winhl of "Normal" for keymap hints floating window (default: "TelescopePrompt") +---@field border_hl string: winhl of "Normal" for keymap borders (default: "TelescopePromptBorder") +---@field winblend number: pseudo-transparency of keymap hints floating window +---@field zindex number: z-index of keymap hints floating window (default: 100) +action_generate.which_key = function(opts) + local which_key = function(prompt_bufnr) + actions.which_key(prompt_bufnr, opts) + end + return which_key +end + +action_generate.refine = function(prompt_bufnr, opts) + opts = opts or {} + opts.prompt_to_prefix = vim.F.if_nil(opts.prompt_to_prefix, false) + opts.prefix_hl_group = vim.F.if_nil(opts.prompt_hl_group, "TelescopePromptPrefix") + opts.prompt_prefix = vim.F.if_nil(opts.prompt_prefix, config.values.prompt_prefix) + opts.reset_multi_selection = vim.F.if_nil(opts.reset_multi_selection, false) + opts.reset_prompt = vim.F.if_nil(opts.reset_prompt, true) + opts.sorter = vim.F.if_nil(opts.sorter, config.values.generic_sorter {}) + local push_history = vim.F.if_nil(opts.push_history, true) + + local current_picker = action_state.get_current_picker(prompt_bufnr) + local current_line = action_state.get_current_line() + if push_history then + action_state.get_current_history():append(current_line, current_picker) + end + + -- title + if opts.prompt_title and current_picker.layout.prompt.border then + current_picker.layout.prompt.border:change_title(opts.prompt_title) + end + + if opts.results_title and current_picker.layout.results.border then + current_picker.layout.results.border:change_title(opts.results_title) + end + + local results = {} + for entry in current_picker.manager:iter() do + table.insert(results, entry) + end + + -- if opts.sorter == false, keep older sorter + if opts.sorter then + current_picker.sorter:_destroy() + current_picker.sorter = opts.sorter + current_picker.sorter:_init() + end + + local new_finder = finders.new_table { + results = results, + entry_maker = function(x) + return x + end, + } + + if not opts.reset_multi_selection and current_line ~= "" then + opts.multi = current_picker._multi + end + + if opts.prompt_to_prefix then + local current_prefix = current_picker.prompt_prefix + local suffix = current_prefix ~= opts.prompt_prefix and current_prefix or "" + opts.new_prefix = suffix .. current_line .. " " .. opts.prompt_prefix + end + current_picker:refresh(new_finder, opts) +end + +return action_generate diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/history.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/history.lua new file mode 100644 index 0000000..2b08689 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/history.lua @@ -0,0 +1,217 @@ +local conf = require("telescope.config").values +local Path = require "plenary.path" +local utils = require "telescope.utils" + +local uv = vim.loop + +---@tag telescope.actions.history +---@config { ["module"] = "telescope.actions.history" } + +---@brief [[ +--- A base implementation of a prompt history that provides a simple history +--- and can be replaced with a custom implementation. +--- +--- For example: We provide an extension for a smart history that uses sql.nvim +--- to map histories to metadata, like the calling picker or cwd. +--- +--- So you have a history for: +--- - find_files project_1 +--- - grep_string project_1 +--- - live_grep project_1 +--- - find_files project_2 +--- - grep_string project_2 +--- - live_grep project_2 +--- - etc +--- +--- See https://github.com/nvim-telescope/telescope-smart-history.nvim +---@brief ]] + +-- TODO(conni2461): currently not present in plenary path only sync. +-- But sync is just unnecessary here +local write_async = function(path, txt, flag) + uv.fs_open(path, flag, 438, function(open_err, fd) + assert(not open_err, open_err) + uv.fs_write(fd, txt, -1, function(write_err) + assert(not write_err, write_err) + uv.fs_close(fd, function(close_err) + assert(not close_err, close_err) + end) + end) + end) +end + +local append_async = function(path, txt) + write_async(path, txt, "a") +end + +local histories = {} + +--- Manages prompt history +---@class History @Manages prompt history +---@field enabled boolean: Will indicate if History is enabled or disabled +---@field path string: Will point to the location of the history file +---@field limit string: Will have the limit of the history. Can be nil, if limit is disabled. +---@field content table: History table. Needs to be filled by your own History implementation +---@field index number: Used to keep track of the next or previous index. Default is #content + 1 +---@field cycle_wrap boolean: Controls if history will wrap on reaching beginning or end +histories.History = {} +histories.History.__index = histories.History + +--- Create a new History +---@param opts table: Defines the behavior of History +---@field init function: Will be called after handling configuration (required) +---@field append function: How to append a new prompt item (required) +---@field reset function: What happens on reset. Will be called when telescope closes (required) +---@field pre_get function: Will be called before a next or previous item will be returned (optional) +function histories.History:new(opts) + local obj = {} + if conf.history == false or type(conf.history) ~= "table" then + obj.enabled = false + return setmetatable(obj, self) + end + obj.enabled = true + if conf.history.limit then + obj.limit = conf.history.limit + end + obj.path = utils.path_expand(conf.history.path) + obj.content = {} + obj.index = 1 + obj.cycle_wrap = conf.history.cycle_wrap + + opts.init(obj) + obj._reset = opts.reset + obj._append = opts.append + obj._pre_get = opts.pre_get + + return setmetatable(obj, self) +end + +--- Shorthand to create a new history +function histories.new(...) + return histories.History:new(...) +end + +--- Will reset the history index to the default initial state. Will happen after the picker closed +function histories.History:reset() + if not self.enabled then + return + end + self._reset(self) +end + +--- Append a new line to the history +---@param line string: current line that will be appended +---@param picker table: the current picker object +---@param no_reset boolean: On default it will reset the state at the end. If you don't want to do this set to true +function histories.History:append(line, picker, no_reset) + if not self.enabled then + return + end + self._append(self, line, picker, no_reset) +end + +--- Will return the next history item. Can be nil if there are no next items +---@param line string: the current line +---@param picker table: the current picker object +---@return string: the next history item +function histories.History:get_next(line, picker) + if not self.enabled then + utils.notify("History:get_next", { + msg = "You are cycling to next the history item but history is disabled. Read ':help telescope.defaults.history'", + level = "WARN", + }) + return false + end + if self._pre_get then + self._pre_get(self, line, picker) + end + + local next_idx = self.index + 1 + if next_idx > #self.content and self.cycle_wrap then + next_idx = 1 + end + + if next_idx <= #self.content then + self.index = next_idx + return self.content[next_idx] + end + self.index = #self.content + 1 + return nil +end + +--- Will return the previous history item. Can be nil if there are no previous items +---@param line string: the current line +---@param picker table: the current picker object +---@return string: the previous history item +function histories.History:get_prev(line, picker) + if not self.enabled then + utils.notify("History:get_prev", { + msg = "You are cycling to next the history item but history is disabled. Read ':help telescope.defaults.history'", + level = "WARN", + }) + return false + end + if self._pre_get then + self._pre_get(self, line, picker) + end + + local next_idx = self.index - 1 + if next_idx < 1 and self.cycle_wrap then + next_idx = #self.content + end + + if self.index == #self.content + 1 then + if line ~= "" then + self:append(line, picker, true) + end + end + if next_idx >= 1 then + self.index = next_idx + return self.content[next_idx] + end + return nil +end + +--- A simple implementation of history. +--- +--- It will keep one unified history across all pickers. +histories.get_simple_history = function() + return histories.new { + init = function(obj) + local p = Path:new(obj.path) + if not p:exists() then + p:touch { parents = true } + end + + obj.content = Path:new(obj.path):readlines() + obj.index = #obj.content + table.remove(obj.content, obj.index) + end, + reset = function(self) + self.index = #self.content + 1 + end, + append = function(self, line, _, no_reset) + if line ~= "" then + if self.content[#self.content] ~= line then + table.insert(self.content, line) + + local len = #self.content + if self.limit and len > self.limit then + local diff = len - self.limit + for i = diff, 1, -1 do + table.remove(self.content, i) + end + write_async(self.path, table.concat(self.content, "\n") .. "\n", "w") + else + append_async(self.path, line .. "\n") + end + end + end + if not no_reset then + self:reset() + end + end, + } +end + +return histories diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/init.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/init.lua new file mode 100644 index 0000000..4666816 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/init.lua @@ -0,0 +1,1584 @@ +---@tag telescope.actions +---@config { ["module"] = "telescope.actions" } + +---@brief [[ +--- These functions are useful for people creating their own mappings. +--- +--- Actions can be either normal functions that expect the `prompt_bufnr` as +--- first argument (1) or they can be a custom telescope type called "action" (2). +--- +--- (1) The `prompt_bufnr` of a normal function denotes the identifier of your +--- picker which can be used to access the picker state. In practice, users +--- most commonly access from both picker and global state via the following: +--- +--- -- for utility functions +--- local action_state = require "telescope.actions.state" +--- +--- local actions = {} +--- actions.do_stuff = function(prompt_bufnr) +--- local current_picker = action_state.get_current_picker(prompt_bufnr) -- picker state +--- local entry = action_state.get_selected_entry() +--- end +--- +--- +--- See |telescope.actions.state| for more information. +--- +--- (2) To transform a module of functions into a module of "action"s, you need +--- to do the following: +--- +--- local transform_mod = require("telescope.actions.mt").transform_mod +--- +--- local mod = {} +--- mod.a1 = function(prompt_bufnr) +--- -- your code goes here +--- -- You can access the picker/global state as described above in (1). +--- end +--- +--- mod.a2 = function(prompt_bufnr) +--- -- your code goes here +--- end +--- mod = transform_mod(mod) +--- +--- -- Now the following is possible. This means that actions a2 will be executed +--- -- after action a1. You can chain as many actions as you want. +--- local action = mod.a1 + mod.a2 +--- action(bufnr) +--- +--- +--- Another interesting thing to do is that these actions now have functions you +--- can call. These functions include `:replace(f)`, `:replace_if(f, c)`, +--- `replace_map(tbl)` and `enhance(tbl)`. More information on these functions +--- can be found in the `developers.md` and `lua/tests/automated/action_spec.lua` +--- file. +---@brief ]] + +local a = vim.api + +local conf = require("telescope.config").values +local state = require "telescope.state" +local utils = require "telescope.utils" +local popup = require "plenary.popup" +local p_scroller = require "telescope.pickers.scroller" + +local action_state = require "telescope.actions.state" +local action_utils = require "telescope.actions.utils" +local action_set = require "telescope.actions.set" +local entry_display = require "telescope.pickers.entry_display" +local from_entry = require "telescope.from_entry" + +local transform_mod = require("telescope.actions.mt").transform_mod +local resolver = require "telescope.config.resolve" + +local actions = setmetatable({}, { + __index = function(_, k) + error("Key does not exist for 'telescope.actions': " .. tostring(k)) + end, +}) + +local append_to_history = function(prompt_bufnr) + action_state + .get_current_history() + :append(action_state.get_current_line(), action_state.get_current_picker(prompt_bufnr)) +end + +--- Move the selection to the next entry +---@param prompt_bufnr number: The prompt bufnr +actions.move_selection_next = function(prompt_bufnr) + action_set.shift_selection(prompt_bufnr, 1) +end + +--- Move the selection to the previous entry +---@param prompt_bufnr number: The prompt bufnr +actions.move_selection_previous = function(prompt_bufnr) + action_set.shift_selection(prompt_bufnr, -1) +end + +--- Move the selection to the entry that has a worse score +---@param prompt_bufnr number: The prompt bufnr +actions.move_selection_worse = function(prompt_bufnr) + local picker = action_state.get_current_picker(prompt_bufnr) + action_set.shift_selection(prompt_bufnr, p_scroller.worse(picker.sorting_strategy)) +end + +--- Move the selection to the entry that has a better score +---@param prompt_bufnr number: The prompt bufnr +actions.move_selection_better = function(prompt_bufnr) + local picker = action_state.get_current_picker(prompt_bufnr) + action_set.shift_selection(prompt_bufnr, p_scroller.better(picker.sorting_strategy)) +end + +--- Move to the top of the picker +---@param prompt_bufnr number: The prompt bufnr +actions.move_to_top = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + current_picker:set_selection( + p_scroller.top(current_picker.sorting_strategy, current_picker.max_results, current_picker.manager:num_results()) + ) +end + +--- Move to the middle of the picker +---@param prompt_bufnr number: The prompt bufnr +actions.move_to_middle = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + current_picker:set_selection( + p_scroller.middle(current_picker.sorting_strategy, current_picker.max_results, current_picker.manager:num_results()) + ) +end + +--- Move to the bottom of the picker +---@param prompt_bufnr number: The prompt bufnr +actions.move_to_bottom = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + current_picker:set_selection( + p_scroller.bottom(current_picker.sorting_strategy, current_picker.max_results, current_picker.manager:num_results()) + ) +end + +--- Add current entry to multi select +---@param prompt_bufnr number: The prompt bufnr +actions.add_selection = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + current_picker:add_selection(current_picker:get_selection_row()) +end + +--- Remove current entry from multi select +---@param prompt_bufnr number: The prompt bufnr +actions.remove_selection = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + current_picker:remove_selection(current_picker:get_selection_row()) +end + +--- Toggle current entry status for multi select +---@param prompt_bufnr number: The prompt bufnr +actions.toggle_selection = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + current_picker:toggle_selection(current_picker:get_selection_row()) +end + +--- Multi select all entries. +--- - Note: selected entries may include results not visible in the results pop up. +---@param prompt_bufnr number: The prompt bufnr +actions.select_all = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + action_utils.map_entries(prompt_bufnr, function(entry, _, row) + if not current_picker._multi:is_selected(entry) then + current_picker._multi:add(entry) + if current_picker:can_select_row(row) then + local caret = current_picker:update_prefix(entry, row) + if current_picker._selection_entry == entry and current_picker._selection_row == row then + current_picker.highlighter:hi_selection(row, caret:match "(.*%S)") + end + current_picker.highlighter:hi_multiselect(row, current_picker._multi:is_selected(entry)) + end + end + end) + current_picker:get_status_updater(current_picker.prompt_win, current_picker.prompt_bufnr)() +end + +--- Drop all entries from the current multi selection. +---@param prompt_bufnr number: The prompt bufnr +actions.drop_all = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + action_utils.map_entries(prompt_bufnr, function(entry, _, row) + current_picker._multi:drop(entry) + if current_picker:can_select_row(row) then + local caret = current_picker:update_prefix(entry, row) + if current_picker._selection_entry == entry and current_picker._selection_row == row then + current_picker.highlighter:hi_selection(row, caret:match "(.*%S)") + end + current_picker.highlighter:hi_multiselect(row, current_picker._multi:is_selected(entry)) + end + end) + current_picker:get_status_updater(current_picker.prompt_win, current_picker.prompt_bufnr)() +end + +--- Toggle multi selection for all entries. +--- - Note: toggled entries may include results not visible in the results pop up. +---@param prompt_bufnr number: The prompt bufnr +actions.toggle_all = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + action_utils.map_entries(prompt_bufnr, function(entry, _, row) + current_picker._multi:toggle(entry) + if current_picker:can_select_row(row) then + local caret = current_picker:update_prefix(entry, row) + if current_picker._selection_entry == entry and current_picker._selection_row == row then + current_picker.highlighter:hi_selection(row, caret:match "(.*%S)") + end + current_picker.highlighter:hi_multiselect(row, current_picker._multi:is_selected(entry)) + end + end) + current_picker:get_status_updater(current_picker.prompt_win, current_picker.prompt_bufnr)() +end + +--- Scroll the preview window up +---@param prompt_bufnr number: The prompt bufnr +actions.preview_scrolling_up = function(prompt_bufnr) + action_set.scroll_previewer(prompt_bufnr, -1) +end + +--- Scroll the preview window down +---@param prompt_bufnr number: The prompt bufnr +actions.preview_scrolling_down = function(prompt_bufnr) + action_set.scroll_previewer(prompt_bufnr, 1) +end + +--- Scroll the preview window to the left +---@param prompt_bufnr number: The prompt bufnr +actions.preview_scrolling_left = function(prompt_bufnr) + action_set.scroll_horizontal_previewer(prompt_bufnr, -1) +end + +--- Scroll the preview window to the right +---@param prompt_bufnr number: The prompt bufnr +actions.preview_scrolling_right = function(prompt_bufnr) + action_set.scroll_horizontal_previewer(prompt_bufnr, 1) +end + +--- Scroll the results window up +---@param prompt_bufnr number: The prompt bufnr +actions.results_scrolling_up = function(prompt_bufnr) + action_set.scroll_results(prompt_bufnr, -1) +end + +--- Scroll the results window down +---@param prompt_bufnr number: The prompt bufnr +actions.results_scrolling_down = function(prompt_bufnr) + action_set.scroll_results(prompt_bufnr, 1) +end + +--- Scroll the results window to the left +---@param prompt_bufnr number: The prompt bufnr +actions.results_scrolling_left = function(prompt_bufnr) + action_set.scroll_horizontal_results(prompt_bufnr, -1) +end + +--- Scroll the results window to the right +---@param prompt_bufnr number: The prompt bufnr +actions.results_scrolling_right = function(prompt_bufnr) + action_set.scroll_horizontal_results(prompt_bufnr, 1) +end + +--- Center the cursor in the window, can be used after selecting a file to edit +--- You can just map `actions.select_default + actions.center` +---@param prompt_bufnr number: The prompt bufnr +actions.center = function(prompt_bufnr) + vim.cmd ":normal! zz" +end + +--- Perform default action on selection, usually something like
+--- `:edit ` +--- +--- i.e. open the selection in the current buffer +---@param prompt_bufnr number: The prompt bufnr +actions.select_default = { + pre = append_to_history, + action = function(prompt_bufnr) + return action_set.select(prompt_bufnr, "default") + end, +} + +--- Perform 'horizontal' action on selection, usually something like
+---`:new ` +--- +--- i.e. open the selection in a new horizontal split +---@param prompt_bufnr number: The prompt bufnr +actions.select_horizontal = { + pre = append_to_history, + action = function(prompt_bufnr) + return action_set.select(prompt_bufnr, "horizontal") + end, +} + +--- Perform 'vertical' action on selection, usually something like
+---`:vnew ` +--- +--- i.e. open the selection in a new vertical split +---@param prompt_bufnr number: The prompt bufnr +actions.select_vertical = { + pre = append_to_history, + action = function(prompt_bufnr) + return action_set.select(prompt_bufnr, "vertical") + end, +} + +--- Perform 'tab' action on selection, usually something like
+---`:tabedit ` +--- +--- i.e. open the selection in a new tab +---@param prompt_bufnr number: The prompt bufnr +actions.select_tab = { + pre = append_to_history, + action = function(prompt_bufnr) + return action_set.select(prompt_bufnr, "tab") + end, +} + +--- Perform 'drop' action on selection, usually something like
+---`:drop ` +--- +--- i.e. open the selection in a window +---@param prompt_bufnr number: The prompt bufnr +actions.select_drop = { + pre = append_to_history, + action = function(prompt_bufnr) + return action_set.select(prompt_bufnr, "drop") + end, +} + +--- Perform 'tab drop' action on selection, usually something like
+---`:tab drop ` +--- +--- i.e. open the selection in a new tab +---@param prompt_bufnr number: The prompt bufnr +actions.select_tab_drop = { + pre = append_to_history, + action = function(prompt_bufnr) + return action_set.select(prompt_bufnr, "tab drop") + end, +} + +-- TODO: consider adding float! +-- https://github.com/nvim-telescope/telescope.nvim/issues/365 + +--- Perform file edit on selection, usually something like
+--- `:edit ` +---@param prompt_bufnr number: The prompt bufnr +actions.file_edit = function(prompt_bufnr) + return action_set.edit(prompt_bufnr, "edit") +end + +--- Perform file split on selection, usually something like
+--- `:new ` +---@param prompt_bufnr number: The prompt bufnr +actions.file_split = function(prompt_bufnr) + return action_set.edit(prompt_bufnr, "new") +end + +--- Perform file vsplit on selection, usually something like
+--- `:vnew ` +---@param prompt_bufnr number: The prompt bufnr +actions.file_vsplit = function(prompt_bufnr) + return action_set.edit(prompt_bufnr, "vnew") +end + +--- Perform file tab on selection, usually something like
+--- `:tabedit ` +---@param prompt_bufnr number: The prompt bufnr +actions.file_tab = function(prompt_bufnr) + return action_set.edit(prompt_bufnr, "tabedit") +end + +actions.close_pum = function(_) + if 0 ~= vim.fn.pumvisible() then + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("", true, true, true), "n", true) + end +end + +--- Close the Telescope window, usually used within an action +---@param prompt_bufnr number: The prompt bufnr +actions.close = function(prompt_bufnr) + local picker = action_state.get_current_picker(prompt_bufnr) + local original_win_id = picker.original_win_id + local cursor_valid, original_cursor = pcall(a.nvim_win_get_cursor, original_win_id) + + actions.close_pum(prompt_bufnr) + + require("telescope.pickers").on_close_prompt(prompt_bufnr) + pcall(a.nvim_set_current_win, original_win_id) + if cursor_valid and a.nvim_get_mode().mode == "i" and picker._original_mode ~= "i" then + pcall(a.nvim_win_set_cursor, original_win_id, { original_cursor[1], original_cursor[2] + 1 }) + end +end + +--- Close the Telescope window, usually used within an action
+--- Deprecated and no longer needed, does the same as |telescope.actions.close|. Might be removed in the future +---@deprecated +---@param prompt_bufnr number: The prompt bufnr +actions._close = function(prompt_bufnr) + actions.close(prompt_bufnr) +end + +local set_edit_line = function(prompt_bufnr, fname, prefix, postfix) + postfix = vim.F.if_nil(postfix, "") + postfix = a.nvim_replace_termcodes(postfix, true, false, true) + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection(fname) + return + end + actions.close(prompt_bufnr) + a.nvim_feedkeys(prefix .. selection.value .. postfix, "n", true) +end + +--- Set a value in the command line and don't run it, making it editable. +---@param prompt_bufnr number: The prompt bufnr +actions.edit_command_line = function(prompt_bufnr) + set_edit_line(prompt_bufnr, "actions.edit_command_line", ":") +end + +--- Set a value in the command line and run it +---@param prompt_bufnr number: The prompt bufnr +actions.set_command_line = function(prompt_bufnr) + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "actions.set_command_line" + return + end + actions.close(prompt_bufnr) + vim.fn.histadd("cmd", selection.value) + vim.cmd(selection.value) +end + +--- Set a value in the search line and don't search for it, making it editable. +---@param prompt_bufnr number: The prompt bufnr +actions.edit_search_line = function(prompt_bufnr) + set_edit_line(prompt_bufnr, "actions.edit_search_line", "/") +end + +--- Set a value in the search line and search for it +---@param prompt_bufnr number: The prompt bufnr +actions.set_search_line = function(prompt_bufnr) + set_edit_line(prompt_bufnr, "actions.set_search_line", "/", "") +end + +--- Edit a register +---@param prompt_bufnr number: The prompt bufnr +actions.edit_register = function(prompt_bufnr) + local selection = action_state.get_selected_entry() + local picker = action_state.get_current_picker(prompt_bufnr) + + vim.fn.inputsave() + local updated_value = vim.fn.input("Edit [" .. selection.value .. "] ❯ ", selection.content) + vim.fn.inputrestore() + if updated_value ~= selection.content then + vim.fn.setreg(selection.value, updated_value) + selection.content = updated_value + end + + -- update entry in results table + -- TODO: find way to redraw finder content + for _, v in pairs(picker.finder.results) do + if v == selection then + v.content = updated_value + end + end +end + +--- Paste the selected register into the buffer +--- +--- Note: only meant to be used inside builtin.registers +---@param prompt_bufnr number: The prompt bufnr +actions.paste_register = function(prompt_bufnr) + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "actions.paste_register" + return + end + + actions.close(prompt_bufnr) + + -- ensure that the buffer can be written to + if vim.api.nvim_buf_get_option(vim.api.nvim_get_current_buf(), "modifiable") then + vim.api.nvim_paste(selection.content, true, -1) + end +end + +--- Insert a symbol into the current buffer (while switching to normal mode) +---@param prompt_bufnr number: The prompt bufnr +actions.insert_symbol = function(prompt_bufnr) + local symbol = action_state.get_selected_entry().value[1] + actions.close(prompt_bufnr) + vim.schedule(function() + vim.api.nvim_put({ symbol }, "", true, true) + end) +end + +--- Insert a symbol into the current buffer and keeping the insert mode. +---@param prompt_bufnr number: The prompt bufnr +actions.insert_symbol_i = function(prompt_bufnr) + local symbol = action_state.get_selected_entry().value[1] + actions.close(prompt_bufnr) + vim.schedule(function() + vim.cmd [[startinsert]] + vim.api.nvim_put({ symbol }, "", true, true) + end) +end + +-- TODO: Think about how to do this. +actions.insert_value = function(prompt_bufnr) + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "actions.insert_value" + return + end + + vim.schedule(function() + actions.close(prompt_bufnr) + end) + + return selection.value +end + +--- Ask user to confirm an action +---@param prompt string: The prompt for confirmation +---@param default_value string: The default value of user input +---@param yes_values table: List of positive user confirmations ({"y", "yes"} by default) +---@return boolean: Whether user confirmed the prompt +local function ask_to_confirm(prompt, default_value, yes_values) + yes_values = yes_values or { "y", "yes" } + default_value = default_value or "" + local confirmation = vim.fn.input(prompt, default_value) + confirmation = string.lower(confirmation) + if string.len(confirmation) == 0 then + return false + end + for _, v in pairs(yes_values) do + if v == confirmation then + return true + end + end + return false +end + +--- Create and checkout a new git branch if it doesn't already exist +---@param prompt_bufnr number: The prompt bufnr +actions.git_create_branch = function(prompt_bufnr) + local cwd = action_state.get_current_picker(prompt_bufnr).cwd + local new_branch = action_state.get_current_line() + + if new_branch == "" then + utils.notify("actions.git_create_branch", { + msg = "Missing the new branch name", + level = "ERROR", + }) + else + local confirmation = ask_to_confirm(string.format("Create new branch '%s'? [y/n]: ", new_branch)) + if not confirmation then + utils.notify("actions.git_create_branch", { + msg = string.format("branch creation canceled: '%s'", new_branch), + level = "INFO", + }) + return + end + + actions.close(prompt_bufnr) + + local _, ret, stderr = utils.get_os_command_output({ "git", "checkout", "-b", new_branch }, cwd) + if ret == 0 then + utils.notify("actions.git_create_branch", { + msg = string.format("Switched to a new branch: %s", new_branch), + level = "INFO", + }) + else + utils.notify("actions.git_create_branch", { + msg = string.format( + "Error when creating new branch: '%s' Git returned '%s'", + new_branch, + table.concat(stderr, " ") + ), + level = "INFO", + }) + end + end +end + +--- Applies an existing git stash +---@param prompt_bufnr number: The prompt bufnr +actions.git_apply_stash = function(prompt_bufnr) + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "actions.git_apply_stash" + return + end + actions.close(prompt_bufnr) + local _, ret, stderr = utils.get_os_command_output { "git", "stash", "apply", "--index", selection.value } + if ret == 0 then + utils.notify("actions.git_apply_stash", { + msg = string.format("applied: '%s' ", selection.value), + level = "INFO", + }) + else + utils.notify("actions.git_apply_stash", { + msg = string.format("Error when applying: %s. Git returned: '%s'", selection.value, table.concat(stderr, " ")), + level = "ERROR", + }) + end +end + +--- Checkout an existing git branch +---@param prompt_bufnr number: The prompt bufnr +actions.git_checkout = function(prompt_bufnr) + local cwd = action_state.get_current_picker(prompt_bufnr).cwd + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "actions.git_checkout" + return + end + actions.close(prompt_bufnr) + local _, ret, stderr = utils.get_os_command_output({ "git", "checkout", selection.value }, cwd) + if ret == 0 then + utils.notify("actions.git_checkout", { + msg = string.format("Checked out: %s", selection.value), + level = "INFO", + }) + vim.cmd "checktime" + else + utils.notify("actions.git_checkout", { + msg = string.format( + "Error when checking out: %s. Git returned: '%s'", + selection.value, + table.concat(stderr, " ") + ), + level = "ERROR", + }) + end +end + +--- Switch to git branch.
+--- If the branch already exists in local, switch to that. +--- If the branch is only in remote, create new branch tracking remote and switch to new one. +---@param prompt_bufnr number: The prompt bufnr +actions.git_switch_branch = function(prompt_bufnr) + local cwd = action_state.get_current_picker(prompt_bufnr).cwd + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "actions.git_switch_branch" + return + end + actions.close(prompt_bufnr) + local pattern = "^refs/remotes/%w+/" + local branch = selection.value + if string.match(selection.refname, pattern) then + branch = string.gsub(selection.refname, pattern, "") + end + local _, ret, stderr = utils.get_os_command_output({ "git", "switch", branch }, cwd) + if ret == 0 then + utils.notify("actions.git_switch_branch", { + msg = string.format("Switched to: '%s'", branch), + level = "INFO", + }) + else + utils.notify("actions.git_switch_branch", { + msg = string.format( + "Error when switching to: %s. Git returned: '%s'", + selection.value, + table.concat(stderr, " ") + ), + level = "ERROR", + }) + end +end + +--- Action to rename selected git branch +--- @param prompt_bufnr number: The prompt bufnr +actions.git_rename_branch = function(prompt_bufnr) + local cwd = action_state.get_current_picker(prompt_bufnr).cwd + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "actions.git_rename_branch" + return + end + -- Keeps the selected branch name for the input that asks for the new branch name + local new_branch = vim.fn.input("New branch name: ", selection.value) + if new_branch == "" then + utils.notify("actions.git_rename_branch", { + msg = "Missing the new branch name", + level = "ERROR", + }) + else + actions.close(prompt_bufnr) + local _, ret, stderr = utils.get_os_command_output({ "git", "branch", "-m", selection.value, new_branch }, cwd) + if ret == 0 then + utils.notify("actions.git_rename_branch", { + msg = string.format("Renamed branch: '%s'", selection.value), + level = "INFO", + }) + else + utils.notify("actions.git_rename_branch", { + msg = string.format( + "Error when renaming branch: %s. Git returned: '%s'", + selection.value, + table.concat(stderr, " ") + ), + level = "ERROR", + }) + end + end +end + +local function make_git_branch_action(opts) + return function(prompt_bufnr) + local cwd = action_state.get_current_picker(prompt_bufnr).cwd + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection(opts.action_name) + return + end + + local should_confirm = opts.should_confirm + if should_confirm then + local confirmation = ask_to_confirm(string.format(opts.confirmation_question, selection.value), "y") + if not confirmation then + utils.notify(opts.action_name, { + msg = "action canceled", + level = "INFO", + }) + return + end + end + + actions.close(prompt_bufnr) + local _, ret, stderr = utils.get_os_command_output(opts.command(selection.value), cwd) + if ret == 0 then + utils.notify(opts.action_name, { + msg = string.format(opts.success_message, selection.value), + level = "INFO", + }) + else + utils.notify(opts.action_name, { + msg = string.format(opts.error_message, selection.value, table.concat(stderr, " ")), + level = "ERROR", + }) + end + end +end + +--- Tell git to track the currently selected remote branch in Telescope +---@param prompt_bufnr number: The prompt bufnr +actions.git_track_branch = make_git_branch_action { + should_confirm = false, + action_name = "actions.git_track_branch", + success_message = "Tracking branch: %s", + error_message = "Error when tracking branch: %s. Git returned: '%s'", + command = function(branch_name) + return { "git", "checkout", "--track", branch_name } + end, +} + +--- Delete all currently selected branches +---@param prompt_bufnr number: The prompt bufnr +actions.git_delete_branch = function(prompt_bufnr) + local confirmation = ask_to_confirm("Do you really want to delete the selected branches? [Y/n] ", "y") + if not confirmation then + utils.notify("actions.git_delete_branch", { + msg = "action canceled", + level = "INFO", + }) + return + end + + local picker = action_state.get_current_picker(prompt_bufnr) + local action_name = "actions.git_delete_branch" + picker:delete_selection(function(selection) + local branch = selection.value + print("Deleting branch " .. branch) + local _, ret, stderr = utils.get_os_command_output({ "git", "branch", "-D", branch }, picker.cwd) + if ret == 0 then + utils.notify(action_name, { + msg = string.format("Deleted branch: %s", branch), + level = "INFO", + }) + else + utils.notify(action_name, { + msg = string.format("Error when deleting branch: %s. Git returned: '%s'", branch, table.concat(stderr, " ")), + level = "ERROR", + }) + end + return ret == 0 + end) +end + +--- Merge the currently selected branch +---@param prompt_bufnr number: The prompt bufnr +actions.git_merge_branch = make_git_branch_action { + should_confirm = true, + action_name = "actions.git_merge_branch", + confirmation_question = "Do you really wanna merge branch %s? [Y/n] ", + success_message = "Merged branch: %s", + error_message = "Error when merging branch: %s. Git returned: '%s'", + command = function(branch_name) + return { "git", "merge", branch_name } + end, +} + +--- Rebase to selected git branch +---@param prompt_bufnr number: The prompt bufnr +actions.git_rebase_branch = make_git_branch_action { + should_confirm = true, + action_name = "actions.git_rebase_branch", + confirmation_question = "Do you really wanna rebase branch %s? [Y/n] ", + success_message = "Rebased branch: %s", + error_message = "Error when rebasing branch: %s. Git returned: '%s'", + command = function(branch_name) + return { "git", "rebase", branch_name } + end, +} + +local git_reset_branch = function(prompt_bufnr, mode) + local cwd = action_state.get_current_picker(prompt_bufnr).cwd + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "actions.git_reset_branch" + return + end + + local confirmation = + ask_to_confirm("Do you really wanna " .. mode .. " reset to " .. selection.value .. "? [Y/n] ", "y") + if not confirmation then + utils.notify("actions.git_reset_branch", { + msg = "action canceled", + level = "INFO", + }) + return + end + + actions.close(prompt_bufnr) + local _, ret, stderr = utils.get_os_command_output({ "git", "reset", mode, selection.value }, cwd) + if ret == 0 then + utils.notify("actions.git_rebase_branch", { + msg = string.format("Reset to: '%s'", selection.value), + level = "INFO", + }) + else + utils.notify("actions.git_rebase_branch", { + msg = string.format("Rest to: %s. Git returned: '%s'", selection.value, table.concat(stderr, " ")), + level = "ERROR", + }) + end +end + +--- Reset to selected git commit using mixed mode +---@param prompt_bufnr number: The prompt bufnr +actions.git_reset_mixed = function(prompt_bufnr) + git_reset_branch(prompt_bufnr, "--mixed") +end + +--- Reset to selected git commit using soft mode +---@param prompt_bufnr number: The prompt bufnr +actions.git_reset_soft = function(prompt_bufnr) + git_reset_branch(prompt_bufnr, "--soft") +end + +--- Reset to selected git commit using hard mode +---@param prompt_bufnr number: The prompt bufnr +actions.git_reset_hard = function(prompt_bufnr) + git_reset_branch(prompt_bufnr, "--hard") +end + +--- Checkout a specific file for a given sha +---@param prompt_bufnr number: The prompt bufnr +actions.git_checkout_current_buffer = function(prompt_bufnr) + local cwd = action_state.get_current_picker(prompt_bufnr).cwd + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "actions.git_checkout_current_buffer" + + return + end + actions.close(prompt_bufnr) + utils.get_os_command_output({ "git", "checkout", selection.value, "--", selection.current_file }, cwd) + vim.cmd "checktime" +end + +--- Stage/unstage selected file +---@param prompt_bufnr number: The prompt bufnr +actions.git_staging_toggle = function(prompt_bufnr) + local cwd = action_state.get_current_picker(prompt_bufnr).cwd + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "actions.git_staging_toggle" + return + end + if selection.status:sub(2) == " " then + utils.get_os_command_output({ "git", "restore", "--staged", selection.value }, cwd) + else + utils.get_os_command_output({ "git", "add", selection.value }, cwd) + end +end + +local entry_to_qf = function(entry) + local text = entry.text + + if not text then + if type(entry.value) == "table" then + text = entry.value.text + else + text = entry.value + end + end + + return { + bufnr = entry.bufnr, + filename = from_entry.path(entry, false, false), + lnum = vim.F.if_nil(entry.lnum, 1), + col = vim.F.if_nil(entry.col, 1), + text = text, + type = entry.qf_type, + } +end + +local send_selected_to_qf = function(prompt_bufnr, mode, target) + local picker = action_state.get_current_picker(prompt_bufnr) + + local qf_entries = {} + for _, entry in ipairs(picker:get_multi_selection()) do + table.insert(qf_entries, entry_to_qf(entry)) + end + + local prompt = picker:_get_prompt() + actions.close(prompt_bufnr) + + vim.api.nvim_exec_autocmds("QuickFixCmdPre", {}) + if target == "loclist" then + vim.fn.setloclist(picker.original_win_id, qf_entries, mode) + else + local qf_title = string.format([[%s (%s)]], picker.prompt_title, prompt) + vim.fn.setqflist(qf_entries, mode) + vim.fn.setqflist({}, "a", { title = qf_title }) + end + vim.api.nvim_exec_autocmds("QuickFixCmdPost", {}) +end + +local send_all_to_qf = function(prompt_bufnr, mode, target) + local picker = action_state.get_current_picker(prompt_bufnr) + local manager = picker.manager + + local qf_entries = {} + for entry in manager:iter() do + table.insert(qf_entries, entry_to_qf(entry)) + end + + local prompt = picker:_get_prompt() + actions.close(prompt_bufnr) + + vim.api.nvim_exec_autocmds("QuickFixCmdPre", {}) + local qf_title = string.format([[%s (%s)]], picker.prompt_title, prompt) + if target == "loclist" then + vim.fn.setloclist(picker.original_win_id, qf_entries, mode) + vim.fn.setloclist(picker.original_win_id, {}, "a", { title = qf_title }) + else + vim.fn.setqflist(qf_entries, mode) + vim.fn.setqflist({}, "a", { title = qf_title }) + end + vim.api.nvim_exec_autocmds("QuickFixCmdPost", {}) +end + +--- Sends the selected entries to the quickfix list, replacing the previous entries. +---@param prompt_bufnr number: The prompt bufnr +actions.send_selected_to_qflist = { + pre = append_to_history, + action = function(prompt_bufnr) + send_selected_to_qf(prompt_bufnr, " ") + end, +} +--- Adds the selected entries to the quickfix list, keeping the previous entries. +---@param prompt_bufnr number: The prompt bufnr +actions.add_selected_to_qflist = { + pre = append_to_history, + action = function(prompt_bufnr) + send_selected_to_qf(prompt_bufnr, "a") + end, +} +--- Sends all entries to the quickfix list, replacing the previous entries. +---@param prompt_bufnr number: The prompt bufnr +actions.send_to_qflist = { + pre = append_to_history, + action = function(prompt_bufnr) + send_all_to_qf(prompt_bufnr, " ") + end, +} +--- Adds all entries to the quickfix list, keeping the previous entries. +---@param prompt_bufnr number: The prompt bufnr +actions.add_to_qflist = { + pre = append_to_history, + action = function(prompt_bufnr) + send_all_to_qf(prompt_bufnr, "a") + end, +} +--- Sends the selected entries to the location list, replacing the previous entries. +---@param prompt_bufnr number: The prompt bufnr +actions.send_selected_to_loclist = { + pre = append_to_history, + action = function(prompt_bufnr) + send_selected_to_qf(prompt_bufnr, " ", "loclist") + end, +} +--- Adds the selected entries to the location list, keeping the previous entries. +---@param prompt_bufnr number: The prompt bufnr +actions.add_selected_to_loclist = { + pre = append_to_history, + action = function(prompt_bufnr) + send_selected_to_qf(prompt_bufnr, "a", "loclist") + end, +} +--- Sends all entries to the location list, replacing the previous entries. +---@param prompt_bufnr number: The prompt bufnr +actions.send_to_loclist = { + pre = append_to_history, + action = function(prompt_bufnr) + send_all_to_qf(prompt_bufnr, " ", "loclist") + end, +} +--- Adds all entries to the location list, keeping the previous entries. +---@param prompt_bufnr number: The prompt bufnr +actions.add_to_loclist = { + pre = append_to_history, + action = function(prompt_bufnr) + send_all_to_qf(prompt_bufnr, "a", "loclist") + end, +} + +local smart_send = function(prompt_bufnr, mode, target) + local picker = action_state.get_current_picker(prompt_bufnr) + if #picker:get_multi_selection() > 0 then + send_selected_to_qf(prompt_bufnr, mode, target) + else + send_all_to_qf(prompt_bufnr, mode, target) + end +end + +--- Sends the selected entries to the quickfix list, replacing the previous entries. +--- If no entry was selected, sends all entries. +---@param prompt_bufnr number: The prompt bufnr +actions.smart_send_to_qflist = { + pre = append_to_history, + action = function(prompt_bufnr) + smart_send(prompt_bufnr, " ") + end, +} +--- Adds the selected entries to the quickfix list, keeping the previous entries. +--- If no entry was selected, adds all entries. +---@param prompt_bufnr number: The prompt bufnr +actions.smart_add_to_qflist = { + pre = append_to_history, + action = function(prompt_bufnr) + smart_send(prompt_bufnr, "a") + end, +} +--- Sends the selected entries to the location list, replacing the previous entries. +--- If no entry was selected, sends all entries. +---@param prompt_bufnr number: The prompt bufnr +actions.smart_send_to_loclist = { + pre = append_to_history, + action = function(prompt_bufnr) + smart_send(prompt_bufnr, " ", "loclist") + end, +} +--- Adds the selected entries to the location list, keeping the previous entries. +--- If no entry was selected, adds all entries. +---@param prompt_bufnr number: The prompt bufnr +actions.smart_add_to_loclist = { + pre = append_to_history, + action = function(prompt_bufnr) + smart_send(prompt_bufnr, "a", "loclist") + end, +} +--- Open completion menu containing the tags which can be used to filter the results in a faster way +---@param prompt_bufnr number: The prompt bufnr +actions.complete_tag = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + local tags = current_picker.sorter.tags + local delimiter = current_picker.sorter._delimiter + + if not tags then + utils.notify("actions.complete_tag", { + msg = "No tag pre-filtering set for this picker", + level = "ERROR", + }) + + return + end + + -- format tags to match filter_function + local prefilter_tags = {} + for tag, _ in pairs(tags) do + table.insert(prefilter_tags, string.format("%s%s%s ", delimiter, tag:lower(), delimiter)) + end + + local line = action_state.get_current_line() + local filtered_tags = {} + -- retrigger completion with already selected tag anew + -- trim and add space since we can match [[:pattern: ]] with or without space at the end + if vim.tbl_contains(prefilter_tags, vim.trim(line) .. " ") then + filtered_tags = prefilter_tags + else + -- match tag by substring + for _, tag in pairs(prefilter_tags) do + local start, _ = tag:find(line) + if start then + table.insert(filtered_tags, tag) + end + end + end + + if vim.tbl_isempty(filtered_tags) then + utils.notify("complete_tag", { + msg = "No matches found", + level = "INFO", + }) + return + end + + -- incremental completion by substituting string starting from col - #line byte offset + local col = vim.api.nvim_win_get_cursor(0)[2] + 1 + vim.fn.complete(col - #line, filtered_tags) +end + +--- Cycle to the next search prompt in the history +---@param prompt_bufnr number: The prompt bufnr +actions.cycle_history_next = function(prompt_bufnr) + local history = action_state.get_current_history() + local current_picker = action_state.get_current_picker(prompt_bufnr) + local line = action_state.get_current_line() + + local entry = history:get_next(line, current_picker) + if entry == false then + return + end + + current_picker:reset_prompt() + if entry ~= nil then + current_picker:set_prompt(entry) + end +end + +--- Cycle to the previous search prompt in the history +---@param prompt_bufnr number: The prompt bufnr +actions.cycle_history_prev = function(prompt_bufnr) + local history = action_state.get_current_history() + local current_picker = action_state.get_current_picker(prompt_bufnr) + local line = action_state.get_current_line() + + local entry = history:get_prev(line, current_picker) + if entry == false then + return + end + if entry ~= nil then + current_picker:reset_prompt() + current_picker:set_prompt(entry) + end +end + +--- Open the quickfix list. It makes sense to use this in combination with one of the send_to_qflist actions +--- `actions.smart_send_to_qflist + actions.open_qflist` +---@param prompt_bufnr number: The prompt bufnr +actions.open_qflist = function(prompt_bufnr) + vim.cmd [[botright copen]] +end + +--- Open the location list. It makes sense to use this in combination with one of the send_to_loclist actions +--- `actions.smart_send_to_qflist + actions.open_qflist` +---@param prompt_bufnr number: The prompt bufnr +actions.open_loclist = function(prompt_bufnr) + vim.cmd [[lopen]] +end + +--- Delete the selected buffer or all the buffers selected using multi selection. +---@param prompt_bufnr number: The prompt bufnr +actions.delete_buffer = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + + current_picker:delete_selection(function(selection) + local force = vim.api.nvim_buf_get_option(selection.bufnr, "buftype") == "terminal" + local ok = pcall(vim.api.nvim_buf_delete, selection.bufnr, { force = force }) + + -- If the current buffer is deleted, switch to the previous buffer + -- according to bdelete behavior + if ok and selection.bufnr == current_picker.original_bufnr then + if vim.api.nvim_win_is_valid(current_picker.original_win_id) then + local jumplist = vim.fn.getjumplist(current_picker.original_win_id)[1] + for i = #jumplist, 1, -1 do + if jumplist[i].bufnr ~= selection.bufnr and vim.fn.bufloaded(jumplist[i].bufnr) == 1 then + vim.api.nvim_win_set_buf(current_picker.original_win_id, jumplist[i].bufnr) + current_picker.original_bufnr = jumplist[i].bufnr + return ok + end + end + + -- no more valid buffers in jumplist, create an empty buffer + local empty_buf = vim.api.nvim_create_buf(true, true) + vim.api.nvim_win_set_buf(current_picker.original_win_id, empty_buf) + current_picker.original_bufnr = empty_buf + vim.api.nvim_buf_delete(selection.bufnr, { force = true }) + return ok + end + + -- window of the selected buffer got wiped, switch to first valid window + local win_id = vim.fn.win_getid(1, current_picker.original_tabpage) + current_picker.original_win_id = win_id + current_picker.original_bufnr = vim.api.nvim_win_get_buf(win_id) + end + return ok + end) +end + +--- Cycle to the next previewer if there is one available.
+--- This action is not mapped on default. +---@param prompt_bufnr number: The prompt bufnr +actions.cycle_previewers_next = function(prompt_bufnr) + action_state.get_current_picker(prompt_bufnr):cycle_previewers(1) +end + +--- Cycle to the previous previewer if there is one available.
+--- This action is not mapped on default. +---@param prompt_bufnr number: The prompt bufnr +actions.cycle_previewers_prev = function(prompt_bufnr) + action_state.get_current_picker(prompt_bufnr):cycle_previewers(-1) +end + +--- Removes the selected picker in |builtin.pickers|.
+--- This action is not mapped by default and only intended for |builtin.pickers|. +---@param prompt_bufnr number: The prompt bufnr +actions.remove_selected_picker = function(prompt_bufnr) + local curr_picker = action_state.get_current_picker(prompt_bufnr) + local curr_entry = action_state.get_selected_entry() + local cached_pickers = state.get_global_key "cached_pickers" + + if not curr_entry then + return + end + + local selection_index, _ = utils.list_find(function(v) + if curr_entry.value == v.value then + return true + end + return false + end, curr_picker.finder.results) + + curr_picker:delete_selection(function() + table.remove(cached_pickers, selection_index) + end) + + if #cached_pickers == 0 then + actions.close(prompt_bufnr) + end +end + +--- Display the keymaps of registered actions similar to which-key.nvim.
+--- - Notes: +--- - The defaults can be overridden via |action_generate.which_key|. +---@param prompt_bufnr number: The prompt bufnr +actions.which_key = function(prompt_bufnr, opts) + opts = opts or {} + opts.max_height = vim.F.if_nil(opts.max_height, 0.4) + opts.only_show_current_mode = vim.F.if_nil(opts.only_show_current_mode, true) + opts.mode_width = vim.F.if_nil(opts.mode_width, 1) + opts.keybind_width = vim.F.if_nil(opts.keybind_width, 7) + opts.name_width = vim.F.if_nil(opts.name_width, 30) + opts.line_padding = vim.F.if_nil(opts.line_padding, 1) + opts.separator = vim.F.if_nil(opts.separator, " -> ") + opts.close_with_action = vim.F.if_nil(opts.close_with_action, true) + opts.normal_hl = vim.F.if_nil(opts.normal_hl, "TelescopePrompt") + opts.border_hl = vim.F.if_nil(opts.border_hl, "TelescopePromptBorder") + opts.winblend = vim.F.if_nil(opts.winblend, conf.winblend) + if type(opts.winblend) == "function" then + opts.winblend = opts.winblend() + end + opts.zindex = vim.F.if_nil(opts.zindex, 100) + opts.column_padding = vim.F.if_nil(opts.column_padding, " ") + + -- Assigning into 'opts.column_indent' would override a number with a string and + -- cause issues with subsequent calls, keep a local copy of the string instead + local column_indent = table.concat(utils.repeated_table(vim.F.if_nil(opts.column_indent, 4), " ")) + + -- close on repeated keypress + local km_bufs = (function() + local ret = {} + local bufs = a.nvim_list_bufs() + for _, buf in ipairs(bufs) do + for _, bufname in ipairs { "_TelescopeWhichKey", "_TelescopeWhichKeyBorder" } do + if string.find(a.nvim_buf_get_name(buf), bufname) then + table.insert(ret, buf) + end + end + end + return ret + end)() + if not vim.tbl_isempty(km_bufs) then + for _, buf in ipairs(km_bufs) do + utils.buf_delete(buf) + local win_ids = vim.fn.win_findbuf(buf) + for _, win_id in ipairs(win_ids) do + pcall(a.nvim_win_close, win_id, true) + end + end + return + end + + local displayer = entry_display.create { + separator = opts.separator, + items = { + { width = opts.mode_width }, + { width = opts.keybind_width }, + { width = opts.name_width }, + }, + } + + local make_display = function(mapping) + return displayer { + { mapping.mode, vim.F.if_nil(opts.mode_hl, "TelescopeResultsConstant") }, + { mapping.keybind, vim.F.if_nil(opts.keybind_hl, "TelescopeResultsVariable") }, + { mapping.name, vim.F.if_nil(opts.name_hl, "TelescopeResultsFunction") }, + } + end + + local mappings = {} + local mode = a.nvim_get_mode().mode + for _, v in pairs(action_utils.get_registered_mappings(prompt_bufnr)) do + if v.desc and v.desc ~= "which_key" and v.desc ~= "nop" then + if not opts.only_show_current_mode or mode == v.mode then + table.insert(mappings, { mode = v.mode, keybind = v.keybind, name = v.desc }) + if v.desc == "" then + utils.notify("actions.which_key", { + msg = "No name available for anonymous functions.", + level = "INFO", + once = true, + }) + end + end + end + end + + table.sort(mappings, function(x, y) + if x.name < y.name then + return true + elseif x.name == y.name then + -- show normal mode as the standard mode first + if x.mode > y.mode then + return true + else + return false + end + else + return false + end + end) + + local entry_width = #opts.column_padding + + opts.mode_width + + opts.keybind_width + + opts.name_width + + (3 * #opts.separator) + local num_total_columns = math.floor((vim.o.columns - #column_indent) / entry_width) + opts.num_rows = + math.min(math.ceil(#mappings / num_total_columns), resolver.resolve_height(opts.max_height)(_, _, vim.o.lines)) + local total_available_entries = opts.num_rows * num_total_columns + local winheight = opts.num_rows + 2 * opts.line_padding + + -- place hints at top or bottom relative to prompt + local win_central_row = function(win_nr) + return a.nvim_win_get_position(win_nr)[1] + 0.5 * a.nvim_win_get_height(win_nr) + end + -- TODO(fdschmidt93|l-kershaw): better generalization of where to put which key float + local picker = action_state.get_current_picker(prompt_bufnr) + local prompt_row = win_central_row(picker.prompt_win) + local results_row = win_central_row(picker.results_win) + local preview_row = picker.preview_win and win_central_row(picker.preview_win) or results_row + local prompt_pos = prompt_row < 0.4 * vim.o.lines + or prompt_row < 0.6 * vim.o.lines and results_row + preview_row < vim.o.lines + + local modes = { n = "Normal", i = "Insert" } + local title_mode = opts.only_show_current_mode and modes[mode] .. " Mode " or "" + local title_text = title_mode .. "Keymaps" + local popup_opts = { + relative = "editor", + enter = false, + minwidth = vim.o.columns, + maxwidth = vim.o.columns, + minheight = winheight, + maxheight = winheight, + line = prompt_pos == true and vim.o.lines - winheight + 1 or 1, + col = 0, + border = { prompt_pos and 1 or 0, 0, not prompt_pos and 1 or 0, 0 }, + borderchars = { prompt_pos and "─" or " ", "", not prompt_pos and "─" or " ", "", "", "", "", "" }, + noautocmd = true, + title = { { text = title_text, pos = prompt_pos and "N" or "S" } }, + zindex = opts.zindex, + } + local km_win_id, km_opts = popup.create("", popup_opts) + local km_buf = a.nvim_win_get_buf(km_win_id) + a.nvim_buf_set_name(km_buf, "_TelescopeWhichKey") + a.nvim_buf_set_name(km_opts.border.bufnr, "_TelescopeTelescopeWhichKeyBorder") + a.nvim_win_set_option(km_win_id, "winhl", "Normal:" .. opts.normal_hl) + a.nvim_win_set_option(km_opts.border.win_id, "winhl", "Normal:" .. opts.border_hl) + a.nvim_win_set_option(km_win_id, "winblend", opts.winblend) + a.nvim_win_set_option(km_win_id, "foldenable", false) + + vim.api.nvim_create_autocmd("BufLeave", { + buffer = km_buf, + once = true, + callback = function() + pcall(vim.api.nvim_win_close, km_win_id, true) + pcall(vim.api.nvim_win_close, km_opts.border.win_id, true) + require("telescope.utils").buf_delete(km_buf) + end, + }) + + a.nvim_buf_set_lines(km_buf, 0, -1, false, utils.repeated_table(opts.num_rows + 2 * opts.line_padding, column_indent)) + + local keymap_highlights = a.nvim_create_namespace "telescope_whichkey" + local highlights = {} + for index, mapping in ipairs(mappings) do + local row = utils.cycle(index, opts.num_rows) - 1 + opts.line_padding + local prev_line = a.nvim_buf_get_lines(km_buf, row, row + 1, false)[1] + if index == total_available_entries and total_available_entries > #mappings then + local new_line = prev_line .. "..." + a.nvim_buf_set_lines(km_buf, row, row + 1, false, { new_line }) + break + end + local display, display_hl = make_display(mapping) + local new_line = prev_line .. display .. opts.column_padding -- incl. padding + a.nvim_buf_set_lines(km_buf, row, row + 1, false, { new_line }) + table.insert(highlights, { hl = display_hl, row = row, col = #prev_line }) + end + + -- highlighting only after line setting as vim.api.nvim_buf_set_lines removes hl otherwise + for _, highlight_tbl in pairs(highlights) do + local highlight = highlight_tbl.hl + local row_ = highlight_tbl.row + local col = highlight_tbl.col + for _, hl_block in ipairs(highlight) do + a.nvim_buf_add_highlight(km_buf, keymap_highlights, hl_block[2], row_, col + hl_block[1][1], col + hl_block[1][2]) + end + end + + -- if close_with_action is true, close the which_key window when any action is triggered + -- otherwise close the window when the prompt buffer is closed + local close_event, close_pattern, close_buffer + if opts.close_with_action then + close_event, close_pattern, close_buffer = "User", "TelescopeKeymap", nil + else + close_event, close_pattern, close_buffer = "BufWinLeave", nil, prompt_bufnr + end + -- only set up autocommand after showing preview completed + vim.schedule(function() + vim.api.nvim_create_autocmd(close_event, { + pattern = close_pattern, + buffer = close_buffer, + once = true, + callback = function() + vim.schedule(function() + pcall(vim.api.nvim_win_close, km_win_id, true) + pcall(vim.api.nvim_win_close, km_opts.border.win_id, true) + utils.buf_delete(km_buf) + end) + end, + }) + end) +end + +--- Move from a none fuzzy search to a fuzzy one
+--- This action is meant to be used in live_grep and lsp_dynamic_workspace_symbols +---@param prompt_bufnr number: The prompt bufnr +actions.to_fuzzy_refine = function(prompt_bufnr) + local line = action_state.get_current_line() + local opts = (function() + local opts = { + sorter = conf.generic_sorter {}, + } + + local title = action_state.get_current_picker(prompt_bufnr).prompt_title + if title == "Live Grep" then + opts.prefix = "Find Word" + elseif title == "LSP Dynamic Workspace Symbols" then + opts.prefix = "LSP Workspace Symbols" + opts.sorter = conf.prefilter_sorter { + tag = "symbol_type", + sorter = opts.sorter, + } + else + opts.prefix = "Fuzzy over" + end + + return opts + end)() + + require("telescope.actions.generate").refine(prompt_bufnr, { + prompt_title = string.format("%s (%s)", opts.prefix, line), + sorter = opts.sorter, + }) +end + +--- Delete the selected mark or all the marks selected using multi selection. +---@param prompt_bufnr number: The prompt bufnr +actions.delete_mark = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + current_picker:delete_selection(function(selection) + local bufname = selection.filename + local bufnr = vim.fn.bufnr(bufname) + local mark = selection.ordinal:sub(1, 1) + + local success + if mark:match "%u" then + success = pcall(vim.api.nvim_del_mark, mark) + else + success = pcall(vim.api.nvim_buf_del_mark, bufnr, mark) + end + return success + end) +end + +--- Insert the word under the cursor of the original (pre-Telescope) window +---@param prompt_bufnr number: The prompt bufnr +actions.insert_original_cword = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + current_picker:set_prompt(current_picker.original_cword, false) +end + +--- Insert the WORD under the cursor of the original (pre-Telescope) window +---@param prompt_bufnr number: The prompt bufnr +actions.insert_original_cWORD = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + current_picker:set_prompt(current_picker.original_cWORD, false) +end + +--- Insert the file under the cursor of the original (pre-Telescope) window +---@param prompt_bufnr number: The prompt bufnr +actions.insert_original_cfile = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + current_picker:set_prompt(current_picker.original_cfile, false) +end + +--- Insert the line under the cursor of the original (pre-Telescope) window +---@param prompt_bufnr number: The prompt bufnr +actions.insert_original_cline = function(prompt_bufnr) + local current_picker = action_state.get_current_picker(prompt_bufnr) + current_picker:set_prompt(current_picker.original_cline, false) +end + +actions.nop = function(_) end + +actions.mouse_click = function(prompt_bufnr) + local picker = action_state.get_current_picker(prompt_bufnr) + + local pos = vim.fn.getmousepos() + if pos.winid == picker.results_win then + vim.schedule(function() + picker:set_selection(pos.line - 1) + end) + elseif pos.winid == picker.preview_win then + vim.schedule(function() + actions.select_default(prompt_bufnr) + end) + end + return "" +end + +actions.double_mouse_click = function(prompt_bufnr) + local picker = action_state.get_current_picker(prompt_bufnr) + + local pos = vim.fn.getmousepos() + if pos.winid == picker.results_win then + vim.schedule(function() + picker:set_selection(pos.line - 1) + actions.select_default(prompt_bufnr) + end) + end + return "" +end + +-- ================================================== +-- Transforms modules and sets the correct metatables. +-- ================================================== +actions = transform_mod(actions) +return actions diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/layout.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/layout.lua new file mode 100644 index 0000000..18afb3b --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/layout.lua @@ -0,0 +1,152 @@ +---@tag telescope.actions.layout +---@config { ["module"] = "telescope.actions.layout", ["name"] = "ACTIONS_LAYOUT" } + +---@brief [[ +--- The layout actions are actions to be used to change the layout of a picker. +---@brief ]] + +local action_state = require "telescope.actions.state" +local state = require "telescope.state" +local layout_strats = require "telescope.pickers.layout_strategies" + +local transform_mod = require("telescope.actions.mt").transform_mod + +local action_layout = setmetatable({}, { + __index = function(_, k) + error("'telescope.actions.layout' does not have a value: " .. tostring(k)) + end, +}) + +--- Toggle preview window. +--- - Note: preview window can be toggled even if preview is set to false. +--- +--- This action is not mapped by default. +---@param prompt_bufnr number: The prompt bufnr +action_layout.toggle_preview = function(prompt_bufnr) + local picker = action_state.get_current_picker(prompt_bufnr) + local status = state.get_status(picker.prompt_bufnr) + + local preview_winid = status.layout.preview and status.layout.preview.winid + if picker.previewer and preview_winid then + picker.hidden_previewer = picker.previewer + picker.previewer = nil + elseif picker.hidden_previewer and not preview_winid then + picker.previewer = picker.hidden_previewer + picker.hidden_previewer = nil + else + return + end + picker:full_layout_update() +end + +-- TODO IMPLEMENT (mentored project available, contact @l-kershaw) +action_layout.toggle_padding = function(prompt_bufnr) + local picker = action_state.get_current_picker(prompt_bufnr) + -- if padding ~= 0 + -- 1. Save `height` and `width` of picker + -- 2. Set both to `{padding = 0}` + -- else + -- 1. Lookup previous `height` and `width` of picker + -- 2. Set both to previous values + picker:full_layout_update() +end + +--- Toggles the `prompt_position` option between "top" and "bottom". +--- Checks if `prompt_position` is an option for the current layout. +--- +--- This action is not mapped by default. +---@param prompt_bufnr number: The prompt bufnr +action_layout.toggle_prompt_position = function(prompt_bufnr) + local picker = action_state.get_current_picker(prompt_bufnr) + picker.layout_config = picker.layout_config or {} + picker.layout_config[picker.layout_strategy] = picker.layout_config[picker.layout_strategy] or {} + -- flex layout is weird and needs handling separately + if picker.layout_strategy == "flex" then + picker.layout_config.flex.horizontal = picker.layout_config.flex.horizontal or {} + picker.layout_config.flex.vertical = picker.layout_config.flex.vertical or {} + local old_pos = vim.F.if_nil( + picker.layout_config.flex[picker.__flex_strategy].prompt_position, + picker.layout_config[picker.__flex_strategy].prompt_position + ) + local new_pos = old_pos == "top" and "bottom" or "top" + picker.layout_config[picker.__flex_strategy].prompt_position = new_pos + picker.layout_config.flex[picker.__flex_strategy].prompt_position = new_pos + picker:full_layout_update() + elseif layout_strats._configurations[picker.layout_strategy].prompt_position then + if picker.layout_config.prompt_position == "top" then + picker.layout_config.prompt_position = "bottom" + picker.layout_config[picker.layout_strategy].prompt_position = "bottom" + else + picker.layout_config.prompt_position = "top" + picker.layout_config[picker.layout_strategy].prompt_position = "top" + end + picker:full_layout_update() + end +end + +--- Toggles the `mirror` option between `true` and `false`. +--- Checks if `mirror` is an option for the current layout. +--- +--- This action is not mapped by default. +---@param prompt_bufnr number: The prompt bufnr +action_layout.toggle_mirror = function(prompt_bufnr) + local picker = action_state.get_current_picker(prompt_bufnr) + -- flex layout is weird and needs handling separately + if picker.layout_strategy == "flex" then + picker.layout_config.flex.horizontal = picker.layout_config.flex.horizontal or {} + picker.layout_config.flex.vertical = picker.layout_config.flex.vertical or {} + local new_mirror = not picker.layout_config.flex[picker.__flex_strategy].mirror + picker.layout_config[picker.__flex_strategy].mirror = new_mirror + picker.layout_config.flex[picker.__flex_strategy].mirror = new_mirror + picker:full_layout_update() + elseif layout_strats._configurations[picker.layout_strategy].mirror then + picker.layout_config = picker.layout_config or {} + local new_mirror = not picker.layout_config.mirror + picker.layout_config.mirror = new_mirror + picker.layout_config[picker.layout_strategy] = picker.layout_config[picker.layout_strategy] or {} + picker.layout_config[picker.layout_strategy].mirror = new_mirror + picker:full_layout_update() + end +end + +-- Helper function for `cycle_layout_next` and `cycle_layout_prev`. +local get_cycle_layout = function(dir) + return function(prompt_bufnr) + local picker = action_state.get_current_picker(prompt_bufnr) + if picker.__layout_index then + picker.__layout_index = ((picker.__layout_index + dir - 1) % #picker.__cycle_layout_list) + 1 + else + picker.__layout_index = 1 + end + local new_layout = picker.__cycle_layout_list[picker.__layout_index] + if type(new_layout) == "string" then + picker.layout_strategy = new_layout + picker.layout_config = {} + picker.previewer = picker.all_previewers and picker.all_previewers[1] or nil + elseif type(new_layout) == "table" then + picker.layout_strategy = new_layout.layout_strategy + picker.layout_config = new_layout.layout_config or {} + picker.previewer = (new_layout.previewer == nil and picker.all_previewers[picker.current_previewer_index]) + or new_layout.previewer + else + error("Not a valid layout setup: " .. vim.inspect(new_layout) .. "\nShould be a string or a table") + end + + picker:full_layout_update() + end +end + +--- Cycles to the next layout in `cycle_layout_list`. +--- +--- This action is not mapped by default. +---@param prompt_bufnr number: The prompt bufnr +action_layout.cycle_layout_next = get_cycle_layout(1) + +--- Cycles to the previous layout in `cycle_layout_list`. +--- +--- This action is not mapped by default. +---@param prompt_bufnr number: The prompt bufnr +action_layout.cycle_layout_prev = get_cycle_layout(-1) + +action_layout = transform_mod(action_layout) +return action_layout diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/mt.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/mt.lua new file mode 100644 index 0000000..07b1e42 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/mt.lua @@ -0,0 +1,210 @@ +local action_mt = {} + +--- Checks all replacement combinations to determine which function to run. +--- If no replacement can be found, then it will run the original function +local run_replace_or_original = function(replacements, original_func, ...) + for _, replacement_map in ipairs(replacements or {}) do + for condition, replacement in pairs(replacement_map) do + if condition == true or condition(...) then + return replacement(...) + end + end + end + + return original_func(...) +end + +local append_action_copy = function(new, v, old) + table.insert(new, v) + new._func[v] = old._func[v] + new._static_pre[v] = old._static_pre[v] + new._pre[v] = old._pre[v] + new._replacements[v] = old._replacements[v] + new._static_post[v] = old._static_post[v] + new._post[v] = old._post[v] +end + +-- TODO(conni2461): Not a fan of this solution/hack. Needs to be addressed +local all_mts = {} + +--TODO(conni2461): It gets worse. This is so bad but because we have now n mts for n actions +-- We have to check all actions for relevant mts to set replace and before, after +-- Its not bad for performance because its being called on startup when we attach mappings. +-- Its just a bad solution +local find_all_relevant_mts = function(action_name, f) + for _, mt in ipairs(all_mts) do + for fun, _ in pairs(mt._func) do + if fun == action_name then + f(mt) + end + end + end +end + +--- an action is metatable which allows replacement(prepend or append) of the function +---@class Action +---@field _func table: the original action function +---@field _static_pre table: will allways run before the function even if its replaced +---@field _pre table: the functions that will run before the action +---@field _replacements table: the function that replaces this action +---@field _static_post table: will allways run after the function even if its replaced +---@field _post table: the functions that will run after the action +action_mt.create = function() + local mt = { + __call = function(t, ...) + local values = {} + for _, action_name in ipairs(t) do + if t._static_pre[action_name] then + t._static_pre[action_name](...) + end + if vim.tbl_isempty(t._replacements) and t._pre[action_name] then + t._pre[action_name](...) + end + + local result = { + run_replace_or_original(t._replacements[action_name], t._func[action_name], ...), + } + for _, res in ipairs(result) do + table.insert(values, res) + end + + if t._static_post[action_name] then + t._static_post[action_name](...) + end + if vim.tbl_isempty(t._replacements) and t._post[action_name] then + t._post[action_name](...) + end + end + + return unpack(values) + end, + + __add = function(lhs, rhs) + local new_action = setmetatable({}, action_mt.create()) + for _, v in ipairs(lhs) do + append_action_copy(new_action, v, lhs) + end + + for _, v in ipairs(rhs) do + append_action_copy(new_action, v, rhs) + end + new_action.clear = function() + lhs.clear() + rhs.clear() + end + + return new_action + end, + + _func = {}, + _static_pre = {}, + _pre = {}, + _replacements = {}, + _static_post = {}, + _post = {}, + } + + mt.__index = mt + + mt.clear = function() + mt._pre = {} + mt._replacements = {} + mt._post = {} + end + + --- Replace the reference to the function with a new one temporarily + function mt:replace(v) + assert(#self == 1, "Cannot replace an already combined action") + + return self:replace_map { [true] = v } + end + + function mt:replace_if(condition, replacement) + assert(#self == 1, "Cannot replace an already combined action") + + return self:replace_map { [condition] = replacement } + end + + --- Replace table with + -- Example: + -- + -- actions.select:replace_map { + -- [function() return filetype == 'lua' end] = actions.file_split, + -- [function() return filetype == 'other' end] = actions.file_split_edit, + -- } + function mt:replace_map(tbl) + assert(#self == 1, "Cannot replace an already combined action") + + local action_name = self[1] + find_all_relevant_mts(action_name, function(another) + if not another._replacements[action_name] then + another._replacements[action_name] = {} + end + + table.insert(another._replacements[action_name], 1, tbl) + end) + + return self + end + + function mt:enhance(opts) + assert(#self == 1, "Cannot enhance already combined actions") + + local action_name = self[1] + find_all_relevant_mts(action_name, function(another) + if opts.pre then + another._pre[action_name] = opts.pre + end + + if opts.post then + another._post[action_name] = opts.post + end + end) + + return self + end + + table.insert(all_mts, mt) + return mt +end + +action_mt.transform = function(k, mt, _, v) + local res = setmetatable({ k }, mt) + if type(v) == "table" then + res._static_pre[k] = v.pre + res._static_post[k] = v.post + res._func[k] = v.action + else + res._func[k] = v + end + return res +end + +action_mt.transform_mod = function(mod) + -- Pass the metatable of the module if applicable. + -- This allows for custom errors, lookups, etc. + local redirect = setmetatable({}, getmetatable(mod) or {}) + + for k, v in pairs(mod) do + local mt = action_mt.create() + redirect[k] = action_mt.transform(k, mt, _, v) + end + + redirect._clear = function() + for k, v in pairs(redirect) do + if k ~= "_clear" then + pcall(v.clear) + end + end + end + + return redirect +end + +action_mt.clear_all = function() + for _, v in ipairs(all_mts) do + pcall(v.clear) + end +end + +return action_mt diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/set.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/set.lua new file mode 100644 index 0000000..b37c1e8 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/set.lua @@ -0,0 +1,319 @@ +---@tag telescope.actions.set +---@config { ["module"] = "telescope.actions.set", ["name"] = "ACTIONS_SET" } + +---@brief [[ +--- Telescope action sets are used to provide an interface for managing +--- actions that all primarily do the same thing, but with slight tweaks. +--- +--- For example, when editing files you may want it in the current split, +--- a vertical split, etc. Instead of making users have to overwrite EACH +--- of those every time they want to change this behavior, they can instead +--- replace the `set` itself and then it will work great and they're done. +---@brief ]] + +local a = vim.api + +local log = require "telescope.log" +local Path = require "plenary.path" +local state = require "telescope.state" +local utils = require "telescope.utils" + +local action_state = require "telescope.actions.state" + +local transform_mod = require("telescope.actions.mt").transform_mod + +local action_set = setmetatable({}, { + __index = function(_, k) + error("'telescope.actions.set' does not have a value: " .. tostring(k)) + end, +}) + +--- Move the current selection of a picker {change} rows. +--- Handles not overflowing / underflowing the list. +---@param prompt_bufnr number: The prompt bufnr +---@param change number: The amount to shift the selection by +action_set.shift_selection = function(prompt_bufnr, change) + local count = vim.v.count + count = count == 0 and 1 or count + count = a.nvim_get_mode().mode == "n" and count or 1 + action_state.get_current_picker(prompt_bufnr):move_selection(change * count) +end + +--- Select the current entry. This is the action set to overwrite common +--- actions by the user. +--- +--- By default maps to editing a file. +---@param prompt_bufnr number: The prompt bufnr +---@param type string: The type of selection to make +-- Valid types include: "default", "horizontal", "vertical", "tabedit" +action_set.select = function(prompt_bufnr, type) + return action_set.edit(prompt_bufnr, action_state.select_key_to_edit_key(type)) +end + +-- goal: currently we have a workaround in actions/init.lua where we do this for all files +-- action_set.select = { +-- -- Will not be called if `select_default` is replaced rather than `action_set.select` because we never get here +-- pre = function(prompt_bufnr) +-- action_state.get_current_history():append( +-- action_state.get_current_line(), +-- action_state.get_current_picker(prompt_bufnr) +-- ) +-- end, +-- action = function(prompt_bufnr, type) +-- return action_set.edit(prompt_bufnr, action_state.select_key_to_edit_key(type)) +-- end +-- } + +local edit_buffer +do + local map = { + drop = "drop", + ["tab drop"] = "tab drop", + edit = "buffer", + new = "sbuffer", + vnew = "vert sbuffer", + ["leftabove new"] = "leftabove sbuffer", + ["leftabove vnew"] = "leftabove vert sbuffer", + ["rightbelow new"] = "rightbelow sbuffer", + ["rightbelow vnew"] = "rightbelow vert sbuffer", + ["topleft new"] = "topleft sbuffer", + ["topleft vnew"] = "topleft vert sbuffer", + ["botright new"] = "botright sbuffer", + ["botright vnew"] = "botright vert sbuffer", + tabedit = "tab sb", + } + + edit_buffer = function(command, bufnr) + local buf_command = map[command] + if buf_command == nil then + local valid_commands = vim.tbl_map(function(cmd) + return string.format("%q", cmd) + end, vim.tbl_keys(map)) + table.sort(valid_commands) + error( + string.format( + "There was no associated buffer command for %q.\nValid commands are: %s.", + command, + table.concat(valid_commands, ", ") + ) + ) + end + if buf_command ~= "drop" and buf_command ~= "tab drop" then + vim.cmd(string.format("%s %d", buf_command, bufnr)) + else + vim.cmd(string.format("%s %s", buf_command, vim.fn.fnameescape(vim.api.nvim_buf_get_name(bufnr)))) + end + end +end + +--- Edit a file based on the current selection. +---@param prompt_bufnr number: The prompt bufnr +---@param command string: The command to use to open the file. +-- Valid commands are: +-- - "edit" +-- - "new" +-- - "vedit" +-- - "tabedit" +-- - "drop" +-- - "tab drop" +-- - "leftabove new" +-- - "leftabove vnew" +-- - "rightbelow new" +-- - "rightbelow vnew" +-- - "topleft new" +-- - "topleft vnew" +-- - "botright new" +-- - "botright vnew" +action_set.edit = function(prompt_bufnr, command) + local entry = action_state.get_selected_entry() + + if not entry then + utils.notify("actions.set.edit", { + msg = "Nothing currently selected", + level = "WARN", + }) + return + end + + local filename, row, col + + if entry.path or entry.filename then + filename = entry.path or entry.filename + + -- TODO: Check for off-by-one + row = entry.row or entry.lnum + col = entry.col + elseif not entry.bufnr then + -- TODO: Might want to remove this and force people + -- to put stuff into `filename` + local value = entry.value + if not value then + utils.notify("actions.set.edit", { + msg = "Could not do anything with blank line...", + level = "WARN", + }) + return + end + + if type(value) == "table" then + value = entry.display + end + + local sections = vim.split(value, ":") + + filename = sections[1] + row = tonumber(sections[2]) + col = tonumber(sections[3]) + end + + local entry_bufnr = entry.bufnr + + local picker = action_state.get_current_picker(prompt_bufnr) + require("telescope.pickers").on_close_prompt(prompt_bufnr) + pcall(vim.api.nvim_set_current_win, picker.original_win_id) + local win_id = picker.get_selection_window(picker, entry) + + if picker.push_cursor_on_edit then + vim.cmd "normal! m'" + end + + if picker.push_tagstack_on_edit then + local from = { vim.fn.bufnr "%", vim.fn.line ".", vim.fn.col ".", 0 } + local items = { { tagname = vim.fn.expand "", from = from } } + vim.fn.settagstack(vim.fn.win_getid(), { items = items }, "t") + end + + if win_id ~= 0 and a.nvim_get_current_win() ~= win_id then + vim.api.nvim_set_current_win(win_id) + end + + if entry_bufnr then + if not vim.api.nvim_buf_get_option(entry_bufnr, "buflisted") then + vim.api.nvim_buf_set_option(entry_bufnr, "buflisted", true) + end + edit_buffer(command, entry_bufnr) + else + -- check if we didn't pick a different buffer + -- prevents restarting lsp server + if vim.api.nvim_buf_get_name(0) ~= filename or command ~= "edit" then + filename = Path:new(filename):normalize(vim.loop.cwd()) + pcall(vim.cmd, string.format("%s %s", command, vim.fn.fnameescape(filename))) + end + end + + -- HACK: fixes folding: https://github.com/nvim-telescope/telescope.nvim/issues/699 + if vim.wo.foldmethod == "expr" then + vim.schedule(function() + vim.opt.foldmethod = "expr" + end) + end + + local pos = vim.api.nvim_win_get_cursor(0) + if col == nil then + if row == pos[1] then + col = pos[2] + 1 + elseif row == nil then + row, col = pos[1], pos[2] + 1 + else + col = 1 + end + end + + if row and col then + if vim.api.nvim_buf_get_name(0) == filename then + vim.cmd [[normal! m']] + end + local ok, err_msg = pcall(a.nvim_win_set_cursor, 0, { row, col }) + if not ok then + log.debug("Failed to move to cursor:", err_msg, row, col) + end + end +end + +---@param prompt_bufnr integer +---@return table? previewer +---@return number? speed +local __scroll_previewer = function(prompt_bufnr) + local previewer = action_state.get_current_picker(prompt_bufnr).previewer + local status = state.get_status(prompt_bufnr) + local preview_winid = status.layout.preview and status.layout.preview.winid + + -- Check if we actually have a previewer and a preview window + if type(previewer) ~= "table" or not preview_winid then + return + end + + local default_speed = vim.api.nvim_win_get_height(preview_winid) / 2 + local speed = status.picker.layout_config.scroll_speed or default_speed + return previewer, speed +end + +--- Scrolls the previewer up or down. +--- Defaults to a half page scroll, but can be overridden using the `scroll_speed` +--- option in `layout_config`. See |telescope.layout| for more details. +---@param prompt_bufnr number: The prompt bufnr +---@param direction number: The direction of the scrolling +-- Valid directions include: "1", "-1" +action_set.scroll_previewer = function(prompt_bufnr, direction) + local previewer, speed = __scroll_previewer(prompt_bufnr) + if previewer and previewer.scroll_fn then + previewer:scroll_fn(math.floor(speed * direction)) + end +end + +--- Scrolls the previewer to the left or right. +--- Defaults to a half page scroll, but can be overridden using the `scroll_speed` +--- option in `layout_config`. See |telescope.layout| for more details. +---@param prompt_bufnr number: The prompt bufnr +---@param direction number: The direction of the scrolling +-- Valid directions include: "1", "-1" +action_set.scroll_horizontal_previewer = function(prompt_bufnr, direction) + local previewer, speed = __scroll_previewer(prompt_bufnr) + if previewer and previewer.scroll_horizontal_fn then + previewer:scroll_horizontal_fn(math.floor(speed * direction)) + end +end + +--- Scrolls the results up or down. +--- Defaults to a half page scroll, but can be overridden using the `scroll_speed` +--- option in `layout_config`. See |telescope.layout| for more details. +---@param prompt_bufnr number: The prompt bufnr +---@param direction number: The direction of the scrolling +-- Valid directions include: "1", "-1" +action_set.scroll_results = function(prompt_bufnr, direction) + local status = state.get_status(prompt_bufnr) + local default_speed = vim.api.nvim_win_get_height(status.layout.results.winid) / 2 + local speed = status.picker.layout_config.scroll_speed or default_speed + + local input = direction > 0 and [[]] or [[]] + + vim.api.nvim_win_call(status.layout.results.winid, function() + vim.cmd([[normal! ]] .. math.floor(speed) .. input) + end) + + action_set.shift_selection(prompt_bufnr, math.floor(speed) * direction) +end + +--- Scrolls the results to the left or right. +--- Defaults to a half page scroll, but can be overridden using the `scroll_speed` +--- option in `layout_config`. See |telescope.layout| for more details. +---@param prompt_bufnr number: The prompt bufnr +---@param direction number: The direction of the scrolling +-- Valid directions include: "1", "-1" +action_set.scroll_horizontal_results = function(prompt_bufnr, direction) + local status = state.get_status(prompt_bufnr) + local default_speed = vim.api.nvim_win_get_height(status.results_win) / 2 + local speed = status.picker.layout_config.scroll_speed or default_speed + + local input = direction > 0 and [[zl]] or [[zh]] + + vim.api.nvim_win_call(status.results_win, function() + vim.cmd([[normal! ]] .. math.floor(speed) .. input) + end) +end + +-- ================================================== +-- Transforms modules and sets the corect metatables. +-- ================================================== +action_set = transform_mod(action_set) +return action_set diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/state.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/state.lua new file mode 100644 index 0000000..539d97c --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/state.lua @@ -0,0 +1,58 @@ +---@tag telescope.actions.state +---@config { ["module"] = "telescope.actions.state", ["name"] = "ACTIONS_STATE" } + +---@brief [[ +--- Functions to be used to determine the current state of telescope. +--- +--- Generally used from within other |telescope.actions| +---@brief ]] + +local global_state = require "telescope.state" +local conf = require("telescope.config").values + +local action_state = {} + +--- Get the current entry +function action_state.get_selected_entry() + return global_state.get_global_key "selected_entry" +end + +--- Gets the current line in the search prompt +function action_state.get_current_line() + return global_state.get_global_key "current_line" or "" +end + +--- Gets the current picker +---@param prompt_bufnr number: The prompt bufnr +function action_state.get_current_picker(prompt_bufnr) + return global_state.get_status(prompt_bufnr).picker +end + +local select_to_edit_map = { + default = "edit", + horizontal = "new", + vertical = "vnew", + tab = "tabedit", + drop = "drop", + ["tab drop"] = "tab drop", +} +function action_state.select_key_to_edit_key(type) + return select_to_edit_map[type] +end + +function action_state.get_current_history() + local history = global_state.get_global_key "history" + if not history then + if conf.history == false or type(conf.history) ~= "table" then + history = require("telescope.actions.history").get_simple_history() + global_state.set_global_key("history", history) + else + history = conf.history.handler() + global_state.set_global_key("history", history) + end + end + + return history +end + +return action_state diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/utils.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/utils.lua new file mode 100644 index 0000000..81bd870 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/actions/utils.lua @@ -0,0 +1,150 @@ +---@tag telescope.actions.utils +---@config { ["module"] = "telescope.actions.utils", ["name"] = "ACTIONS_UTILS" } + +---@brief [[ +--- Utilities to wrap functions around picker selections and entries. +--- +--- Generally used from within other |telescope.actions| +---@brief ]] + +local action_state = require "telescope.actions.state" + +local utils = {} + +--- Apply `f` to the entries of the current picker. +--- - Notes: +--- - Mapped entries include all currently filtered results, not just the visible ones. +--- - Indices are 1-indexed, whereas rows are 0-indexed. +--- - Warning: `map_entries` has no return value. +--- - The below example showcases how to collect results +--- +--- Usage: +--- +--- local action_state = require "telescope.actions.state" +--- local action_utils = require "telescope.actions.utils" +--- function entry_value_by_row() +--- local prompt_bufnr = vim.api.nvim_get_current_buf() +--- local current_picker = action_state.get_current_picker(prompt_bufnr) +--- local results = {} +--- action_utils.map_entries(prompt_bufnr, function(entry, index, row) +--- results[row] = entry.value +--- end) +--- return results +--- end +--- +---@param prompt_bufnr number: The prompt bufnr +---@param f function: Function to map onto entries of picker that takes (entry, index, row) as viable arguments +function utils.map_entries(prompt_bufnr, f) + vim.validate { + f = { f, "function" }, + } + local current_picker = action_state.get_current_picker(prompt_bufnr) + local index = 1 + -- indices are 1-indexed, rows are 0-indexed + for entry in current_picker.manager:iter() do + local row = current_picker:get_row(index) + f(entry, index, row) + index = index + 1 + end +end + +--- Apply `f` to the multi selections of the current picker and return a table of mapped selections. +--- - Notes: +--- - Mapped selections may include results not visible in the results pop up. +--- - Selected entries are returned in order of their selection. +--- - Warning: `map_selections` has no return value. +--- - The below example showcases how to collect results +--- +--- Usage: +--- +--- local action_state = require "telescope.actions.state" +--- local action_utils = require "telescope.actions.utils" +--- function selection_by_index() +--- local prompt_bufnr = vim.api.nvim_get_current_buf() +--- local current_picker = action_state.get_current_picker(prompt_bufnr) +--- local results = {} +--- action_utils.map_selections(prompt_bufnr, function(entry, index) +--- results[index] = entry.value +--- end) +--- return results +--- end +--- +---@param prompt_bufnr number: The prompt bufnr +---@param f function: Function to map onto selection of picker that takes (selection) as a viable argument +function utils.map_selections(prompt_bufnr, f) + vim.validate { + f = { f, "function" }, + } + local current_picker = action_state.get_current_picker(prompt_bufnr) + for _, selection in ipairs(current_picker:get_multi_selection()) do + f(selection) + end +end + +--- Utility to collect mappings of prompt buffer in array of `{mode, keybind, name}`. +---@param prompt_bufnr number: The prompt bufnr +function utils.get_registered_mappings(prompt_bufnr) + local ret = {} + for _, mode in ipairs { "n", "i" } do + for _, mapping in ipairs(vim.api.nvim_buf_get_keymap(prompt_bufnr, mode)) do + -- ensure only telescope mappings + if mapping.desc then + if mapping.desc:sub(1, 10) == "telescope|" then + table.insert(ret, { mode = mode, keybind = mapping.lhs, desc = mapping.desc:sub(11) }) + elseif mapping.desc:sub(1, 11) == "telescopej|" then + local fname = utils._get_anon_function_name(vim.json.decode(mapping.desc:sub(12))) + fname = fname:lower() == mapping.lhs:lower() and "" or fname + table.insert(ret, { + mode = mode, + keybind = mapping.lhs, + desc = fname, + }) + end + end + end + end + return ret +end + +-- Best effort to infer function names for actions.which_key +function utils._get_anon_function_name(info) + local Path = require "plenary.path" + local fname + -- if fn defined in string (ie loadstring) source is string + -- if fn defined in file, source is file name prefixed with a `@´ + local path = Path:new((info.source:gsub("@", ""))) + if not path:exists() then + return "" + end + for i, line in ipairs(path:readlines()) do + if i == info.linedefined then + fname = line + break + end + end + + -- test if assignment or named function, otherwise anon + if (fname:match "=" == nil) and (fname:match "function %S+%(" == nil) then + return "" + else + local patterns = { + { "function", "" }, -- remove function + { "local", "" }, -- remove local + { "[%s=]", "" }, -- remove whitespace and = + { [=[%[["']]=], "" }, -- remove left-hand bracket of table assignment + { [=[["']%]]=], "" }, -- remove right-ahnd bracket of table assignment + { "%((.+)%)", "" }, -- remove function arguments + { "(.+)%.", "" }, -- remove TABLE. prefix if available + } + for _, tbl in ipairs(patterns) do + fname = (fname:gsub(tbl[1], tbl[2])) -- make sure only string is returned + end + -- not sure if this can happen, catch all just in case + if fname == nil or fname == "" then + return "" + end + return fname + end +end + +return utils diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/algos/fzy.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/algos/fzy.lua new file mode 100644 index 0000000..bf322ab --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/algos/fzy.lua @@ -0,0 +1,197 @@ +-- The fzy matching algorithm +-- +-- by Seth Warn +-- a lua port of John Hawthorn's fzy +-- +-- > fzy tries to find the result the user intended. It does this by favouring +-- > matches on consecutive letters and starts of words. This allows matching +-- > using acronyms or different parts of the path." - J Hawthorn + +local has_path, Path = pcall(require, "plenary.path") +if not has_path then + Path = { + path = { + separator = "/", + }, + } +end + +local SCORE_GAP_LEADING = -0.005 +local SCORE_GAP_TRAILING = -0.005 +local SCORE_GAP_INNER = -0.01 +local SCORE_MATCH_CONSECUTIVE = 1.0 +local SCORE_MATCH_SLASH = 0.9 +local SCORE_MATCH_WORD = 0.8 +local SCORE_MATCH_CAPITAL = 0.7 +local SCORE_MATCH_DOT = 0.6 +local SCORE_MAX = math.huge +local SCORE_MIN = -math.huge +local MATCH_MAX_LENGTH = 1024 + +local fzy = {} + +function fzy.has_match(needle, haystack) + needle = string.lower(needle) + haystack = string.lower(haystack) + + local j = 1 + for i = 1, string.len(needle) do + j = string.find(haystack, needle:sub(i, i), j, true) + if not j then + return false + else + j = j + 1 + end + end + + return true +end + +local function is_lower(c) + return c:match "%l" +end + +local function is_upper(c) + return c:match "%u" +end + +local function precompute_bonus(haystack) + local match_bonus = {} + + local last_char = Path.path.sep + for i = 1, string.len(haystack) do + local this_char = haystack:sub(i, i) + if last_char == Path.path.sep then + match_bonus[i] = SCORE_MATCH_SLASH + elseif last_char == "-" or last_char == "_" or last_char == " " then + match_bonus[i] = SCORE_MATCH_WORD + elseif last_char == "." then + match_bonus[i] = SCORE_MATCH_DOT + elseif is_lower(last_char) and is_upper(this_char) then + match_bonus[i] = SCORE_MATCH_CAPITAL + else + match_bonus[i] = 0 + end + + last_char = this_char + end + + return match_bonus +end + +local function compute(needle, haystack, D, M) + local match_bonus = precompute_bonus(haystack) + local n = string.len(needle) + local m = string.len(haystack) + local lower_needle = string.lower(needle) + local lower_haystack = string.lower(haystack) + + -- Because lua only grants access to chars through substring extraction, + -- get all the characters from the haystack once now, to reuse below. + local haystack_chars = {} + for i = 1, m do + haystack_chars[i] = lower_haystack:sub(i, i) + end + + for i = 1, n do + D[i] = {} + M[i] = {} + + local prev_score = SCORE_MIN + local gap_score = i == n and SCORE_GAP_TRAILING or SCORE_GAP_INNER + local needle_char = lower_needle:sub(i, i) + + for j = 1, m do + if needle_char == haystack_chars[j] then + local score = SCORE_MIN + if i == 1 then + score = ((j - 1) * SCORE_GAP_LEADING) + match_bonus[j] + elseif j > 1 then + local a = M[i - 1][j - 1] + match_bonus[j] + local b = D[i - 1][j - 1] + SCORE_MATCH_CONSECUTIVE + score = math.max(a, b) + end + D[i][j] = score + prev_score = math.max(score, prev_score + gap_score) + M[i][j] = prev_score + else + D[i][j] = SCORE_MIN + prev_score = prev_score + gap_score + M[i][j] = prev_score + end + end + end +end + +function fzy.score(needle, haystack) + local n = string.len(needle) + local m = string.len(haystack) + + if n == 0 or m == 0 or m > MATCH_MAX_LENGTH or n > MATCH_MAX_LENGTH then + return SCORE_MIN + elseif n == m then + return SCORE_MAX + else + local D = {} + local M = {} + compute(needle, haystack, D, M) + return M[n][m] + end +end + +function fzy.positions(needle, haystack) + local n = string.len(needle) + local m = string.len(haystack) + + if n == 0 or m == 0 or m > MATCH_MAX_LENGTH or n > MATCH_MAX_LENGTH then + return {} + elseif n == m then + local consecutive = {} + for i = 1, n do + consecutive[i] = i + end + return consecutive + end + + local D = {} + local M = {} + compute(needle, haystack, D, M) + + local positions = {} + local match_required = false + local j = m + for i = n, 1, -1 do + while j >= 1 do + if D[i][j] ~= SCORE_MIN and (match_required or D[i][j] == M[i][j]) then + match_required = (i ~= 1) and (j ~= 1) and (M[i][j] == D[i - 1][j - 1] + SCORE_MATCH_CONSECUTIVE) + positions[i] = j + j = j - 1 + break + else + j = j - 1 + end + end + end + + return positions +end + +-- If strings a or b are empty or too long, `fzy.score(a, b) == fzy.get_score_min()`. +function fzy.get_score_min() + return SCORE_MIN +end + +-- For exact matches, `fzy.score(s, s) == fzy.get_score_max()`. +function fzy.get_score_max() + return SCORE_MAX +end + +-- For all strings a and b that +-- - are not covered by either `fzy.get_score_min()` or fzy.get_score_max()`, and +-- - are matched, such that `fzy.has_match(a, b) == true`, +-- then `fzy.score(a, b) > fzy.get_score_floor()` will be true. +function fzy.get_score_floor() + return (MATCH_MAX_LENGTH + 1) * SCORE_GAP_INNER +end + +return fzy diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/algos/linked_list.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/algos/linked_list.lua new file mode 100644 index 0000000..2da6a6e --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/algos/linked_list.lua @@ -0,0 +1,255 @@ +local LinkedList = {} +LinkedList.__index = LinkedList + +function LinkedList:new(opts) + opts = opts or {} + local track_at = opts.track_at + + return setmetatable({ + size = 0, + head = false, + tail = false, + + -- track_at: Track at can track a particular node + -- Use to keep a node tracked at a particular index + -- This greatly decreases looping for checking values at this location. + track_at = track_at, + _tracked_node = nil, + tracked = nil, + }, self) +end + +function LinkedList:_increment() + self.size = self.size + 1 + return self.size +end + +local create_node = function(item) + return { + item = item, + } +end + +function LinkedList:append(item) + local final_size = self:_increment() + + local node = create_node(item) + + if not self.head then + self.head = node + end + + if self.tail then + self.tail.next = node + node.prev = self.tail + end + + self.tail = node + + if self.track_at then + if final_size == self.track_at then + self.tracked = item + self._tracked_node = node + end + end +end + +function LinkedList:prepend(item) + local final_size = self:_increment() + local node = create_node(item) + + if not self.tail then + self.tail = node + end + + if self.head then + self.head.prev = node + node.next = self.head + end + + self.head = node + + if self.track_at then + if final_size == self.track_at then + self._tracked_node = self.tail + elseif final_size > self.track_at then + self._tracked_node = self._tracked_node.prev + else + return + end + + self.tracked = self._tracked_node.item + end +end + +-- [a, b, c] +-- b.prev = a +-- b.next = c +-- +-- a.next = b +-- c.prev = c +-- +-- insert d after b +-- [a, b, d, c] +-- +-- b.next = d +-- b.prev = a +-- +-- Place "item" after "node" (which is at index `index`) +function LinkedList:place_after(index, node, item) + local new_node = create_node(item) + + assert(node.prev ~= node) + assert(node.next ~= node) + local final_size = self:_increment() + + -- Update tail to be the next node. + if self.tail == node then + self.tail = new_node + end + + new_node.prev = node + new_node.next = node.next + + node.next = new_node + + if new_node.prev then + new_node.prev.next = new_node + end + + if new_node.next then + new_node.next.prev = new_node + end + + if self.track_at then + if index == self.track_at then + self._tracked_node = new_node + elseif index < self.track_at then + if final_size == self.track_at then + self._tracked_node = self.tail + elseif final_size > self.track_at then + self._tracked_node = self._tracked_node.prev + else + return + end + end + + self.tracked = self._tracked_node.item + end +end + +function LinkedList:place_before(index, node, item) + local new_node = create_node(item) + + assert(node.prev ~= node) + assert(node.next ~= node) + local final_size = self:_increment() + + -- Update head to be the node we are inserting. + if self.head == node then + self.head = new_node + end + + new_node.prev = node.prev + new_node.next = node + + node.prev = new_node + -- node.next = node.next + + if new_node.prev then + new_node.prev.next = new_node + end + + if new_node.next then + new_node.next.prev = new_node + end + + if self.track_at then + if index == self.track_at - 1 then + self._tracked_node = node + elseif index < self.track_at then + if final_size == self.track_at then + self._tracked_node = self.tail + elseif final_size > self.track_at then + self._tracked_node = self._tracked_node.prev + else + return + end + end + + self.tracked = self._tracked_node.item + end +end + +-- Do you even do this in linked lists...? +-- function LinkedList:remove(item) +-- end + +function LinkedList:iter() + local current_node = self.head + + return function() + local node = current_node + if not node then + return nil + end + + current_node = current_node.next + return node.item + end +end + +function LinkedList:ipairs() + local index = 0 + local current_node = self.head + + return function() + local node = current_node + if not node then + return nil + end + + current_node = current_node.next + index = index + 1 + return index, node.item, node + end +end + +function LinkedList:truncate(max_results) + if max_results >= self.size then + return + end + + local current_node + if max_results < self.size - max_results then + local index = 1 + current_node = self.head + while index < max_results do + local node = current_node + if not node.next then + break + end + current_node = current_node.next + index = index + 1 + end + self.size = max_results + else + current_node = self.tail + while self.size > max_results do + if current_node.prev == nil then + break + end + current_node = current_node.prev + self.size = self.size - 1 + end + end + self.tail = current_node + self.tail.next = nil + if max_results < self.track_at then + self.track_at = max_results + self.tracked = current_node.item + self._tracked_node = current_node + end +end + +return LinkedList diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/algos/string_distance.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/algos/string_distance.lua new file mode 100644 index 0000000..c2c5ead --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/algos/string_distance.lua @@ -0,0 +1,52 @@ +local function min(a, b, c) + local min_val = a + + if b < min_val then + min_val = b + end + if c < min_val then + min_val = c + end + + return min_val +end + +---------------------------------- +--- Levenshtein distance function. +-- @tparam string s1 +-- @tparam string s2 +-- @treturn number the levenshtein distance +-- @within Metrics +return function(s1, s2) + if s1 == s2 then + return 0 + end + if s1:len() == 0 then + return s2:len() + end + if s2:len() == 0 then + return s1:len() + end + if s1:len() < s2:len() then + s1, s2 = s2, s1 + end + + local t = {} + for i = 1, #s1 + 1 do + t[i] = { i - 1 } + end + + for i = 1, #s2 + 1 do + t[1][i] = i - 1 + end + + local cost + for i = 2, #s1 + 1 do + for j = 2, #s2 + 1 do + cost = (s1:sub(i - 1, i - 1) == s2:sub(j - 1, j - 1) and 0) or 1 + t[i][j] = min(t[i - 1][j] + 1, t[i][j - 1] + 1, t[i - 1][j - 1] + cost) + end + end + + return t[#s1 + 1][#s2 + 1] +end diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/builtin/__diagnostics.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/builtin/__diagnostics.lua new file mode 100644 index 0000000..ec129d6 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/builtin/__diagnostics.lua @@ -0,0 +1,187 @@ +local conf = require("telescope.config").values +local finders = require "telescope.finders" +local make_entry = require "telescope.make_entry" +local pickers = require "telescope.pickers" +local utils = require "telescope.utils" + +local diagnostics = {} + +local sorting_comparator = function(opts) + local current_buf = vim.api.nvim_get_current_buf() + local comparators = { + -- sort results by bufnr (prioritize cur buf), severity, lnum + buffer = function(a, b) + if a.bufnr == b.bufnr then + if a.type == b.type then + return a.lnum < b.lnum + else + return a.type < b.type + end + else + if a.bufnr == current_buf then + return true + end + if b.bufnr == current_buf then + return false + end + return a.bufnr < b.bufnr + end + end, + severity = function(a, b) + if a.type < b.type then + return true + elseif a.type > b.type then + return false + end + + if a.bufnr == b.bufnr then + return a.lnum < b.lnum + elseif a.bufnr == current_buf then + return true + elseif b.bufnr == current_buf then + return false + else + return a.bufnr < b.bufnr + end + end, + } + + local sort_by = vim.F.if_nil(opts.sort_by, "buffer") + return comparators[sort_by] +end + +local convert_diagnostic_type = function(severities, severity) + -- convert from string to int + if type(severity) == "string" then + -- make sure that e.g. error is uppercased to ERROR + return severities[severity:upper()] + end + -- otherwise keep original value, incl. nil + return severity +end + +local diagnostics_to_tbl = function(opts) + opts = vim.F.if_nil(opts, {}) + local items = {} + local severities = vim.diagnostic.severity + + opts.severity = convert_diagnostic_type(severities, opts.severity) + opts.severity_limit = convert_diagnostic_type(severities, opts.severity_limit) + opts.severity_bound = convert_diagnostic_type(severities, opts.severity_bound) + + local diagnosis_opts = { severity = {}, namespace = opts.namespace } + if opts.severity ~= nil then + if opts.severity_limit ~= nil or opts.severity_bound ~= nil then + utils.notify("builtin.diagnostics", { + msg = "Invalid severity parameters. Both a specific severity and a limit/bound is not allowed", + level = "ERROR", + }) + return {} + end + diagnosis_opts.severity = opts.severity + else + if opts.severity_limit ~= nil then + diagnosis_opts.severity["min"] = opts.severity_limit + end + if opts.severity_bound ~= nil then + diagnosis_opts.severity["max"] = opts.severity_bound + end + if vim.version().minor > 9 and vim.tbl_isempty(diagnosis_opts.severity) then + diagnosis_opts.severity = nil + end + end + + opts.root_dir = opts.root_dir == true and vim.loop.cwd() or opts.root_dir + + local bufnr_name_map = {} + local filter_diag = function(diagnostic) + if bufnr_name_map[diagnostic.bufnr] == nil then + bufnr_name_map[diagnostic.bufnr] = vim.api.nvim_buf_get_name(diagnostic.bufnr) + end + + local root_dir_test = not opts.root_dir + or string.sub(bufnr_name_map[diagnostic.bufnr], 1, #opts.root_dir) == opts.root_dir + local listed_test = not opts.no_unlisted or vim.api.nvim_buf_get_option(diagnostic.bufnr, "buflisted") + + return root_dir_test and listed_test + end + + local preprocess_diag = function(diagnostic) + return { + bufnr = diagnostic.bufnr, + filename = bufnr_name_map[diagnostic.bufnr], + lnum = diagnostic.lnum + 1, + col = diagnostic.col + 1, + text = vim.trim(diagnostic.message:gsub("[\n]", "")), + type = severities[diagnostic.severity] or severities[1], + } + end + + for _, d in ipairs(vim.diagnostic.get(opts.bufnr, diagnosis_opts)) do + if filter_diag(d) then + table.insert(items, preprocess_diag(d)) + end + end + + table.sort(items, sorting_comparator(opts)) + + return items +end + +diagnostics.get = function(opts) + if opts.bufnr ~= 0 then + opts.bufnr = nil + end + if type(opts.bufnr) == "string" then + opts.bufnr = tonumber(opts.bufnr) + end + if opts.bufnr ~= nil then + opts.path_display = vim.F.if_nil(opts.path_display, "hidden") + end + + local locations = diagnostics_to_tbl(opts) + + if vim.tbl_isempty(locations) then + utils.notify("builtin.diagnostics", { + msg = "No diagnostics found", + level = "INFO", + }) + return + end + + if type(opts.line_width) == "string" and opts.line_width ~= "full" then + utils.notify("builtin.diagnostics", { + msg = string.format("'%s' is not a valid value for line_width", opts.line_width), + level = "ERROR", + }) + return + end + + pickers + .new(opts, { + prompt_title = opts.bufnr == nil and "Workspace Diagnostics" or "Document Diagnostics", + finder = finders.new_table { + results = locations, + entry_maker = opts.entry_maker or make_entry.gen_from_diagnostics(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.prefilter_sorter { + tag = "type", + sorter = conf.generic_sorter(opts), + }, + }) + :find() +end + +local function apply_checks(mod) + for k, v in pairs(mod) do + mod[k] = function(opts) + opts = opts or {} + v(opts) + end + end + + return mod +end + +return apply_checks(diagnostics) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/builtin/__files.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/builtin/__files.lua new file mode 100644 index 0000000..27b8d1c --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/builtin/__files.lua @@ -0,0 +1,652 @@ +local action_state = require "telescope.actions.state" +local action_set = require "telescope.actions.set" +local actions = require "telescope.actions" +local finders = require "telescope.finders" +local make_entry = require "telescope.make_entry" +local pickers = require "telescope.pickers" +local previewers = require "telescope.previewers" +local sorters = require "telescope.sorters" +local utils = require "telescope.utils" +local conf = require("telescope.config").values +local log = require "telescope.log" + +local Path = require "plenary.path" + +local flatten = utils.flatten +local filter = vim.tbl_filter + +local files = {} + +---@param s string +---@return string +local escape_chars = function(s) + return ( + s:gsub("[%(|%)|\\|%[|%]|%-|%{%}|%?|%+|%*|%^|%$|%.]", { + ["\\"] = "\\\\", + ["-"] = "\\-", + ["("] = "\\(", + [")"] = "\\)", + ["["] = "\\[", + ["]"] = "\\]", + ["{"] = "\\{", + ["}"] = "\\}", + ["?"] = "\\?", + ["+"] = "\\+", + ["*"] = "\\*", + ["^"] = "\\^", + ["$"] = "\\$", + ["."] = "\\.", + }) + ) +end + +local has_rg_program = function(picker_name, program) + if vim.fn.executable(program) == 1 then + return true + end + + utils.notify(picker_name, { + msg = string.format( + "'ripgrep', or similar alternative, is a required dependency for the %s picker. " + .. "Visit https://github.com/BurntSushi/ripgrep#installation for installation instructions.", + picker_name + ), + level = "ERROR", + }) + return false +end + +local get_open_filelist = function(grep_open_files, cwd) + if not grep_open_files then + return nil + end + + local bufnrs = filter(function(b) + if 1 ~= vim.fn.buflisted(b) then + return false + end + return true + end, vim.api.nvim_list_bufs()) + if not next(bufnrs) then + return + end + + local filelist = {} + for _, bufnr in ipairs(bufnrs) do + local file = vim.api.nvim_buf_get_name(bufnr) + table.insert(filelist, Path:new(file):make_relative(cwd)) + end + return filelist +end + +local opts_contain_invert = function(args) + local invert = false + local files_with_matches = false + + for _, v in ipairs(args) do + if v == "--invert-match" then + invert = true + elseif v == "--files-with-matches" or v == "--files-without-match" then + files_with_matches = true + end + + if #v >= 2 and v:sub(1, 1) == "-" and v:sub(2, 2) ~= "-" then + local non_option = false + for i = 2, #v do + local vi = v:sub(i, i) + if vi == "=" then -- ignore option -g=xxx + break + elseif vi == "g" or vi == "f" or vi == "m" or vi == "e" or vi == "r" or vi == "t" or vi == "T" then + non_option = true + elseif non_option == false and vi == "v" then + invert = true + elseif non_option == false and vi == "l" then + files_with_matches = true + end + end + end + end + return invert, files_with_matches +end + +-- Special keys: +-- opts.search_dirs -- list of directory to search in +-- opts.grep_open_files -- boolean to restrict search to open files +files.live_grep = function(opts) + local vimgrep_arguments = opts.vimgrep_arguments or conf.vimgrep_arguments + if not has_rg_program("live_grep", vimgrep_arguments[1]) then + return + end + local search_dirs = opts.search_dirs + local grep_open_files = opts.grep_open_files + opts.cwd = opts.cwd and utils.path_expand(opts.cwd) or vim.loop.cwd() + + local filelist = get_open_filelist(grep_open_files, opts.cwd) + if search_dirs then + for i, path in ipairs(search_dirs) do + search_dirs[i] = utils.path_expand(path) + end + end + + local additional_args = {} + if opts.additional_args ~= nil then + if type(opts.additional_args) == "function" then + additional_args = opts.additional_args(opts) + elseif type(opts.additional_args) == "table" then + additional_args = opts.additional_args + end + end + + if opts.type_filter then + additional_args[#additional_args + 1] = "--type=" .. opts.type_filter + end + + if type(opts.glob_pattern) == "string" then + additional_args[#additional_args + 1] = "--glob=" .. opts.glob_pattern + elseif type(opts.glob_pattern) == "table" then + for i = 1, #opts.glob_pattern do + additional_args[#additional_args + 1] = "--glob=" .. opts.glob_pattern[i] + end + end + + if opts.file_encoding then + additional_args[#additional_args + 1] = "--encoding=" .. opts.file_encoding + end + + local args = flatten { vimgrep_arguments, additional_args } + opts.__inverted, opts.__matches = opts_contain_invert(args) + + local live_grepper = finders.new_job(function(prompt) + if not prompt or prompt == "" then + return nil + end + + local search_list = {} + + if grep_open_files then + search_list = filelist + elseif search_dirs then + search_list = search_dirs + end + + return flatten { args, "--", prompt, search_list } + end, opts.entry_maker or make_entry.gen_from_vimgrep(opts), opts.max_results, opts.cwd) + + pickers + .new(opts, { + prompt_title = "Live Grep", + finder = live_grepper, + previewer = conf.grep_previewer(opts), + -- TODO: It would be cool to use `--json` output for this + -- and then we could get the highlight positions directly. + sorter = sorters.highlighter_only(opts), + attach_mappings = function(_, map) + map("i", "", actions.to_fuzzy_refine) + return true + end, + push_cursor_on_edit = true, + }) + :find() +end + +files.grep_string = function(opts) + local vimgrep_arguments = vim.F.if_nil(opts.vimgrep_arguments, conf.vimgrep_arguments) + if not has_rg_program("grep_string", vimgrep_arguments[1]) then + return + end + local word + local visual = vim.fn.mode() == "v" + + if visual == true then + local saved_reg = vim.fn.getreg "v" + vim.cmd [[noautocmd sil norm! "vy]] + local sele = vim.fn.getreg "v" + vim.fn.setreg("v", saved_reg) + word = vim.F.if_nil(opts.search, sele) + else + word = vim.F.if_nil(opts.search, vim.fn.expand "") + end + + word = tostring(word) + local search = opts.use_regex and word or escape_chars(word) + local search_args = search == "" and { "-v", "--", "^[[:space:]]*$" } or { "--", search } + + local additional_args = {} + if opts.additional_args ~= nil then + if type(opts.additional_args) == "function" then + additional_args = opts.additional_args(opts) + elseif type(opts.additional_args) == "table" then + additional_args = opts.additional_args + end + end + + if opts.file_encoding then + additional_args[#additional_args + 1] = "--encoding=" .. opts.file_encoding + end + + local args + if visual == true then + args = flatten { + vimgrep_arguments, + additional_args, + search_args, + } + else + args = flatten { + vimgrep_arguments, + additional_args, + opts.word_match, + search_args, + } + end + + opts.__inverted, opts.__matches = opts_contain_invert(args) + + if opts.grep_open_files then + for _, file in ipairs(get_open_filelist(opts.grep_open_files, opts.cwd) or {}) do + table.insert(args, file) + end + elseif opts.search_dirs then + for _, path in ipairs(opts.search_dirs) do + table.insert(args, utils.path_expand(path)) + end + end + + opts.entry_maker = opts.entry_maker or make_entry.gen_from_vimgrep(opts) + pickers + .new(opts, { + prompt_title = "Find Word (" .. word:gsub("\n", "\\n") .. ")", + finder = finders.new_oneshot_job(args, opts), + previewer = conf.grep_previewer(opts), + sorter = conf.generic_sorter(opts), + push_cursor_on_edit = true, + }) + :find() +end + +files.find_files = function(opts) + local find_command = (function() + if opts.find_command then + if type(opts.find_command) == "function" then + return opts.find_command(opts) + end + return opts.find_command + elseif 1 == vim.fn.executable "rg" then + return { "rg", "--files", "--color", "never" } + elseif 1 == vim.fn.executable "fd" then + return { "fd", "--type", "f", "--color", "never" } + elseif 1 == vim.fn.executable "fdfind" then + return { "fdfind", "--type", "f", "--color", "never" } + elseif 1 == vim.fn.executable "find" and vim.fn.has "win32" == 0 then + return { "find", ".", "-type", "f" } + elseif 1 == vim.fn.executable "where" then + return { "where", "/r", ".", "*" } + end + end)() + + if not find_command then + utils.notify("builtin.find_files", { + msg = "You need to install either find, fd, or rg", + level = "ERROR", + }) + return + end + + local command = find_command[1] + local hidden = opts.hidden + local no_ignore = opts.no_ignore + local no_ignore_parent = opts.no_ignore_parent + local follow = opts.follow + local search_dirs = opts.search_dirs + local search_file = opts.search_file + + if search_dirs then + for k, v in pairs(search_dirs) do + search_dirs[k] = utils.path_expand(v) + end + end + + if command == "fd" or command == "fdfind" or command == "rg" then + if hidden then + find_command[#find_command + 1] = "--hidden" + end + if no_ignore then + find_command[#find_command + 1] = "--no-ignore" + end + if no_ignore_parent then + find_command[#find_command + 1] = "--no-ignore-parent" + end + if follow then + find_command[#find_command + 1] = "-L" + end + if search_file then + if command == "rg" then + find_command[#find_command + 1] = "-g" + find_command[#find_command + 1] = "*" .. search_file .. "*" + else + find_command[#find_command + 1] = search_file + end + end + if search_dirs then + if command ~= "rg" and not search_file then + find_command[#find_command + 1] = "." + end + vim.list_extend(find_command, search_dirs) + end + elseif command == "find" then + if not hidden then + table.insert(find_command, { "-not", "-path", "*/.*" }) + find_command = flatten(find_command) + end + if no_ignore ~= nil then + log.warn "The `no_ignore` key is not available for the `find` command in `find_files`." + end + if no_ignore_parent ~= nil then + log.warn "The `no_ignore_parent` key is not available for the `find` command in `find_files`." + end + if follow then + table.insert(find_command, 2, "-L") + end + if search_file then + table.insert(find_command, "-name") + table.insert(find_command, "*" .. search_file .. "*") + end + if search_dirs then + table.remove(find_command, 2) + for _, v in pairs(search_dirs) do + table.insert(find_command, 2, v) + end + end + elseif command == "where" then + if hidden ~= nil then + log.warn "The `hidden` key is not available for the Windows `where` command in `find_files`." + end + if no_ignore ~= nil then + log.warn "The `no_ignore` key is not available for the Windows `where` command in `find_files`." + end + if no_ignore_parent ~= nil then + log.warn "The `no_ignore_parent` key is not available for the Windows `where` command in `find_files`." + end + if follow ~= nil then + log.warn "The `follow` key is not available for the Windows `where` command in `find_files`." + end + if search_dirs ~= nil then + log.warn "The `search_dirs` key is not available for the Windows `where` command in `find_files`." + end + if search_file ~= nil then + log.warn "The `search_file` key is not available for the Windows `where` command in `find_files`." + end + end + + if opts.cwd then + opts.cwd = utils.path_expand(opts.cwd) + end + + opts.entry_maker = opts.entry_maker or make_entry.gen_from_file(opts) + + pickers + .new(opts, { + prompt_title = "Find Files", + __locations_input = true, + finder = finders.new_oneshot_job(find_command, opts), + previewer = conf.grep_previewer(opts), + sorter = conf.file_sorter(opts), + }) + :find() +end + +local function prepare_match(entry, kind) + local entries = {} + + if entry.node then + table.insert(entries, entry) + else + for name, item in pairs(entry) do + vim.list_extend(entries, prepare_match(item, name)) + end + end + + return entries +end + +-- TODO: finish docs for opts.show_line +files.treesitter = function(opts) + opts.show_line = vim.F.if_nil(opts.show_line, true) + + local has_nvim_treesitter, _ = pcall(require, "nvim-treesitter") + if not has_nvim_treesitter then + utils.notify("builtin.treesitter", { + msg = "This picker requires nvim-treesitter", + level = "ERROR", + }) + return + end + + local parsers = require "nvim-treesitter.parsers" + if not parsers.has_parser(parsers.get_buf_lang(opts.bufnr)) then + utils.notify("builtin.treesitter", { + msg = "No parser for the current buffer", + level = "ERROR", + }) + return + end + + local ts_locals = require "nvim-treesitter.locals" + local results = {} + for _, definition in ipairs(ts_locals.get_definitions(opts.bufnr)) do + local entries = prepare_match(ts_locals.get_local_nodes(definition)) + for _, entry in ipairs(entries) do + entry.kind = vim.F.if_nil(entry.kind, "") + table.insert(results, entry) + end + end + + results = utils.filter_symbols(results, opts) + if vim.tbl_isempty(results) then + -- error message already printed in `utils.filter_symbols` + return + end + + if vim.tbl_isempty(results) then + return + end + + pickers + .new(opts, { + prompt_title = "Treesitter Symbols", + finder = finders.new_table { + results = results, + entry_maker = opts.entry_maker or make_entry.gen_from_treesitter(opts), + }, + previewer = conf.grep_previewer(opts), + sorter = conf.prefilter_sorter { + tag = "kind", + sorter = conf.generic_sorter(opts), + }, + push_cursor_on_edit = true, + }) + :find() +end + +files.current_buffer_fuzzy_find = function(opts) + -- All actions are on the current buffer + local filename = vim.api.nvim_buf_get_name(opts.bufnr) + local filetype = vim.api.nvim_buf_get_option(opts.bufnr, "filetype") + + local lines = vim.api.nvim_buf_get_lines(opts.bufnr, 0, -1, false) + local lines_with_numbers = {} + + for lnum, line in ipairs(lines) do + table.insert(lines_with_numbers, { + lnum = lnum, + bufnr = opts.bufnr, + filename = filename, + text = line, + }) + end + + opts.results_ts_highlight = vim.F.if_nil(opts.results_ts_highlight, true) + local lang = vim.treesitter.language.get_lang(filetype) or filetype + if opts.results_ts_highlight and lang and utils.has_ts_parser(lang) then + local parser = vim.treesitter.get_parser(opts.bufnr, lang) + local query = vim.treesitter.query.get(lang, "highlights") + local root = parser:parse()[1]:root() + + local line_highlights = setmetatable({}, { + __index = function(t, k) + local obj = {} + rawset(t, k, obj) + return obj + end, + }) + + for id, node in query:iter_captures(root, opts.bufnr, 0, -1) do + local hl = "@" .. query.captures[id] + if hl and type(hl) ~= "number" then + local row1, col1, row2, col2 = node:range() + + if row1 == row2 then + local row = row1 + 1 + + for index = col1, col2 do + line_highlights[row][index] = hl + end + else + local row = row1 + 1 + for index = col1, #lines[row] do + line_highlights[row][index] = hl + end + + while row < row2 + 1 do + row = row + 1 + + for index = 0, #(lines[row] or {}) do + line_highlights[row][index] = hl + end + end + end + end + end + + opts.line_highlights = line_highlights + end + + pickers + .new(opts, { + prompt_title = "Current Buffer Fuzzy", + finder = finders.new_table { + results = lines_with_numbers, + entry_maker = opts.entry_maker or make_entry.gen_from_buffer_lines(opts), + }, + sorter = conf.generic_sorter(opts), + previewer = conf.grep_previewer(opts), + attach_mappings = function() + actions.select_default:replace(function(prompt_bufnr) + local selection = action_state.get_selected_entry() + if not selection then + utils.__warn_no_selection "builtin.current_buffer_fuzzy_find" + return + end + local current_picker = action_state.get_current_picker(prompt_bufnr) + local searched_for = require("telescope.actions.state").get_current_line() + + ---@type number[] | {start:number, end:number?, highlight:string?}[] + local highlights = current_picker.sorter:highlighter(searched_for, selection.ordinal) or {} + highlights = vim.tbl_map(function(hl) + if type(hl) == "table" and hl.start then + return hl.start + elseif type(hl) == "number" then + return hl + end + error "Invalid higlighter fn" + end, highlights) + + local first_col = 0 + if #highlights > 0 then + first_col = math.min(unpack(highlights)) - 1 + end + + actions.close(prompt_bufnr) + vim.schedule(function() + vim.cmd "normal! m'" + vim.api.nvim_win_set_cursor(0, { selection.lnum, first_col }) + end) + end) + + return true + end, + }) + :find() +end + +files.tags = function(opts) + local tagfiles = opts.ctags_file and { opts.ctags_file } or vim.fn.tagfiles() + for i, ctags_file in ipairs(tagfiles) do + tagfiles[i] = vim.fn.expand(ctags_file, true) + end + if vim.tbl_isempty(tagfiles) then + utils.notify("builtin.tags", { + msg = "No tags file found. Create one with ctags -R", + level = "ERROR", + }) + return + end + opts.entry_maker = vim.F.if_nil(opts.entry_maker, make_entry.gen_from_ctags(opts)) + + pickers + .new(opts, { + prompt_title = "Tags", + finder = finders.new_oneshot_job(flatten { "cat", tagfiles }, opts), + previewer = previewers.ctags.new(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function() + action_set.select:enhance { + post = function() + local selection = action_state.get_selected_entry() + if not selection then + return + end + + if selection.scode then + -- un-escape / then escape required + -- special chars for vim.fn.search() + -- ] ~ * + local scode = selection.scode:gsub([[\/]], "/"):gsub("[%]~*]", function(x) + return "\\" .. x + end) + + vim.cmd "keepjumps norm! gg" + vim.fn.search(scode) + vim.cmd "norm! zz" + else + vim.api.nvim_win_set_cursor(0, { selection.lnum, 0 }) + end + end, + } + return true + end, + }) + :find() +end + +files.current_buffer_tags = function(opts) + return files.tags(vim.tbl_extend("force", { + prompt_title = "Current Buffer Tags", + only_current_file = true, + path_display = "hidden", + }, opts)) +end + +local function apply_checks(mod) + for k, v in pairs(mod) do + mod[k] = function(opts) + opts = opts or {} + + v(opts) + end + end + + return mod +end + +return apply_checks(files) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/builtin/__git.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/builtin/__git.lua new file mode 100644 index 0000000..538d99c --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/builtin/__git.lua @@ -0,0 +1,513 @@ +local actions = require "telescope.actions" +local action_state = require "telescope.actions.state" +local finders = require "telescope.finders" +local make_entry = require "telescope.make_entry" +local operators = require "telescope.operators" +local pickers = require "telescope.pickers" +local previewers = require "telescope.previewers" +local utils = require "telescope.utils" +local entry_display = require "telescope.pickers.entry_display" +local strings = require "plenary.strings" +local Path = require "plenary.path" + +local conf = require("telescope.config").values +local git_command = utils.__git_command + +local git = {} + +local get_git_command_output = function(args, opts) + return utils.get_os_command_output(git_command(args, opts), opts.cwd) +end + +git.files = function(opts) + if opts.is_bare then + utils.notify("builtin.git_files", { + msg = "This operation must be run in a work tree", + level = "ERROR", + }) + return + end + + local show_untracked = vim.F.if_nil(opts.show_untracked, false) + local recurse_submodules = vim.F.if_nil(opts.recurse_submodules, false) + if show_untracked and recurse_submodules then + utils.notify("builtin.git_files", { + msg = "Git does not support both --others and --recurse-submodules", + level = "ERROR", + }) + return + end + + -- By creating the entry maker after the cwd options, + -- we ensure the maker uses the cwd options when being created. + opts.entry_maker = vim.F.if_nil(opts.entry_maker, make_entry.gen_from_file(opts)) + opts.git_command = vim.F.if_nil( + opts.git_command, + git_command({ "-c", "core.quotepath=false", "ls-files", "--exclude-standard", "--cached" }, opts) + ) + + pickers + .new(opts, { + prompt_title = "Git Files", + __locations_input = true, + finder = finders.new_oneshot_job( + utils.flatten { + opts.git_command, + show_untracked and "--others" or nil, + recurse_submodules and "--recurse-submodules" or nil, + }, + opts + ), + previewer = conf.grep_previewer(opts), + sorter = conf.file_sorter(opts), + }) + :find() +end + +git.commits = function(opts) + opts.entry_maker = vim.F.if_nil(opts.entry_maker, make_entry.gen_from_git_commits(opts)) + opts.git_command = + vim.F.if_nil(opts.git_command, git_command({ "log", "--pretty=oneline", "--abbrev-commit", "--", "." }, opts)) + + pickers + .new(opts, { + prompt_title = "Git Commits", + finder = finders.new_oneshot_job(opts.git_command, opts), + previewer = { + previewers.git_commit_diff_to_parent.new(opts), + previewers.git_commit_diff_to_head.new(opts), + previewers.git_commit_diff_as_was.new(opts), + previewers.git_commit_message.new(opts), + }, + sorter = conf.file_sorter(opts), + attach_mappings = function(_, map) + actions.select_default:replace(actions.git_checkout) + map({ "i", "n" }, "m", actions.git_reset_mixed) + map({ "i", "n" }, "s", actions.git_reset_soft) + map({ "i", "n" }, "h", actions.git_reset_hard) + return true + end, + }) + :find() +end + +git.stash = function(opts) + opts.show_branch = vim.F.if_nil(opts.show_branch, true) + opts.entry_maker = vim.F.if_nil(opts.entry_maker, make_entry.gen_from_git_stash(opts)) + opts.git_command = vim.F.if_nil(opts.git_command, git_command({ "--no-pager", "stash", "list" }, opts)) + + pickers + .new(opts, { + prompt_title = "Git Stash", + finder = finders.new_oneshot_job(opts.git_command, opts), + previewer = previewers.git_stash_diff.new(opts), + sorter = conf.file_sorter(opts), + attach_mappings = function() + actions.select_default:replace(actions.git_apply_stash) + return true + end, + }) + :find() +end + +local get_current_buf_line = function(winnr) + local lnum = vim.api.nvim_win_get_cursor(winnr)[1] + return vim.trim(vim.api.nvim_buf_get_lines(vim.api.nvim_win_get_buf(winnr), lnum - 1, lnum, false)[1]) +end + +local bcommits_picker = function(opts, title, finder) + return pickers.new(opts, { + prompt_title = title, + finder = finder, + previewer = { + previewers.git_commit_diff_to_parent.new(opts), + previewers.git_commit_diff_to_head.new(opts), + previewers.git_commit_diff_as_was.new(opts), + previewers.git_commit_message.new(opts), + }, + sorter = conf.file_sorter(opts), + attach_mappings = function() + actions.select_default:replace(actions.git_checkout_current_buffer) + local transfrom_file = function() + return opts.current_file and Path:new(opts.current_file):make_relative(opts.cwd) or "" + end + + local get_buffer_of_orig = function(selection) + local value = selection.value .. ":" .. transfrom_file() + local content = utils.get_os_command_output({ "git", "--no-pager", "show", value }, opts.cwd) + + local bufnr = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, content) + vim.api.nvim_buf_set_name(bufnr, "Original") + return bufnr + end + + local vimdiff = function(selection, command) + local ft = vim.bo.filetype + vim.cmd "diffthis" + + local bufnr = get_buffer_of_orig(selection) + vim.cmd(string.format("%s %s", command, bufnr)) + vim.bo.filetype = ft + vim.cmd "diffthis" + + vim.api.nvim_create_autocmd("WinClosed", { + buffer = bufnr, + nested = true, + once = true, + callback = function() + vim.api.nvim_buf_delete(bufnr, { force = true }) + end, + }) + end + + actions.select_vertical:replace(function(prompt_bufnr) + actions.close(prompt_bufnr) + local selection = action_state.get_selected_entry() + vimdiff(selection, "leftabove vert sbuffer") + end) + + actions.select_horizontal:replace(function(prompt_bufnr) + actions.close(prompt_bufnr) + local selection = action_state.get_selected_entry() + vimdiff(selection, "belowright sbuffer") + end) + + actions.select_tab:replace(function(prompt_bufnr) + actions.close(prompt_bufnr) + local selection = action_state.get_selected_entry() + vim.cmd("tabedit " .. transfrom_file()) + vimdiff(selection, "leftabove vert sbuffer") + end) + return true + end, + }) +end + +git.bcommits = function(opts) + opts.current_line = (opts.current_file == nil) and get_current_buf_line(opts.winnr) or nil + opts.current_file = vim.F.if_nil(opts.current_file, vim.api.nvim_buf_get_name(opts.bufnr)) + opts.entry_maker = vim.F.if_nil(opts.entry_maker, make_entry.gen_from_git_commits(opts)) + opts.git_command = + vim.F.if_nil(opts.git_command, git_command({ "log", "--pretty=oneline", "--abbrev-commit", "--follow" }, opts)) + + local title = "Git BCommits" + local finder = finders.new_oneshot_job( + utils.flatten { + opts.git_command, + opts.current_file, + }, + opts + ) + bcommits_picker(opts, title, finder):find() +end + +git.bcommits_range = function(opts) + opts.current_line = (opts.current_file == nil) and get_current_buf_line(opts.winnr) or nil + opts.current_file = vim.F.if_nil(opts.current_file, vim.api.nvim_buf_get_name(opts.bufnr)) + opts.entry_maker = vim.F.if_nil(opts.entry_maker, make_entry.gen_from_git_commits(opts)) + opts.git_command = vim.F.if_nil( + opts.git_command, + git_command({ "log", "--pretty=oneline", "--abbrev-commit", "--no-patch", "-L" }, opts) + ) + local visual = string.find(vim.fn.mode(), "[vV]") ~= nil + + local line_number_first = opts.from + local line_number_last = vim.F.if_nil(opts.to, line_number_first) + if visual then + line_number_first = vim.F.if_nil(line_number_first, vim.fn.line "v") + line_number_last = vim.F.if_nil(line_number_last, vim.fn.line ".") + elseif opts.operator then + opts.operator = false + opts.operator_callback = true + operators.run_operator(git.bcommits_range, opts) + return + elseif opts.operator_callback then + line_number_first = vim.fn.line "'[" + line_number_last = vim.fn.line "']" + elseif line_number_first == nil then + line_number_first = vim.F.if_nil(line_number_first, vim.fn.line ".") + line_number_last = vim.F.if_nil(line_number_last, vim.fn.line ".") + end + local line_range = + string.format("%d,%d:%s", line_number_first, line_number_last, Path:new(opts.current_file):make_relative(opts.cwd)) + + local title = "Git BCommits in range" + local finder = finders.new_oneshot_job( + utils.flatten { + opts.git_command, + line_range, + }, + opts + ) + bcommits_picker(opts, title, finder):find() +end + +git.branches = function(opts) + local format = "%(HEAD)" + .. "%(refname)" + .. "%(authorname)" + .. "%(upstream:lstrip=2)" + .. "%(committerdate:format-local:%Y/%m/%d %H:%M:%S)" + + local output = get_git_command_output( + { "for-each-ref", "--perl", "--format", format, "--sort", "-authordate", opts.pattern }, + opts + ) + + local show_remote_tracking_branches = vim.F.if_nil(opts.show_remote_tracking_branches, true) + + local results = {} + local widths = { + name = 0, + authorname = 0, + upstream = 0, + committerdate = 0, + } + local unescape_single_quote = function(v) + return string.gsub(v, "\\([\\'])", "%1") + end + local parse_line = function(line) + local fields = vim.split(string.sub(line, 2, -2), "''") + local entry = { + head = fields[1], + refname = unescape_single_quote(fields[2]), + authorname = unescape_single_quote(fields[3]), + upstream = unescape_single_quote(fields[4]), + committerdate = fields[5], + } + local prefix + if vim.startswith(entry.refname, "refs/remotes/") then + if show_remote_tracking_branches then + prefix = "refs/remotes/" + else + return + end + elseif vim.startswith(entry.refname, "refs/heads/") then + prefix = "refs/heads/" + else + return + end + local index = 1 + if entry.head ~= "*" then + index = #results + 1 + end + + entry.name = string.sub(entry.refname, string.len(prefix) + 1) + for key, value in pairs(widths) do + widths[key] = math.max(value, strings.strdisplaywidth(entry[key] or "")) + end + if string.len(entry.upstream) > 0 then + widths.upstream_indicator = 2 + end + table.insert(results, index, entry) + end + for _, line in ipairs(output) do + parse_line(line) + end + if #results == 0 then + return + end + + local displayer = entry_display.create { + separator = " ", + items = { + { width = 1 }, + { width = widths.name }, + { width = widths.authorname }, + { width = widths.upstream_indicator }, + { width = widths.upstream }, + { width = widths.committerdate }, + }, + } + + local make_display = function(entry) + return displayer { + { entry.head }, + { entry.name, "TelescopeResultsIdentifier" }, + { entry.authorname }, + { string.len(entry.upstream) > 0 and "=>" or "" }, + { entry.upstream, "TelescopeResultsIdentifier" }, + { entry.committerdate }, + } + end + + pickers + .new(opts, { + prompt_title = "Git Branches", + finder = finders.new_table { + results = results, + entry_maker = function(entry) + entry.value = entry.name + entry.ordinal = entry.name + entry.display = make_display + return make_entry.set_default_entry_mt(entry, opts) + end, + }, + previewer = previewers.git_branch_log.new(opts), + sorter = conf.file_sorter(opts), + attach_mappings = function(_, map) + actions.select_default:replace(actions.git_checkout) + map({ "i", "n" }, "", actions.git_track_branch) + map({ "i", "n" }, "", actions.git_rebase_branch) + map({ "i", "n" }, "", actions.git_create_branch) + map({ "i", "n" }, "", actions.git_switch_branch) + map({ "i", "n" }, "", actions.git_delete_branch) + map({ "i", "n" }, "", actions.git_merge_branch) + return true + end, + }) + :find() +end + +git.status = function(opts) + if opts.is_bare then + utils.notify("builtin.git_status", { + msg = "This operation must be run in a work tree", + level = "ERROR", + }) + return + end + + local args = { "status", "--porcelain=v1", "--", "." } + + local gen_new_finder = function() + if vim.F.if_nil(opts.expand_dir, true) then + table.insert(args, #args - 1, "-uall") + end + local git_cmd = git_command(args, opts) + opts.entry_maker = vim.F.if_nil(opts.entry_maker, make_entry.gen_from_git_status(opts)) + return finders.new_oneshot_job(git_cmd, opts) + end + + local initial_finder = gen_new_finder() + if not initial_finder then + return + end + + pickers + .new(opts, { + prompt_title = "Git Status", + finder = initial_finder, + previewer = previewers.git_file_diff.new(opts), + sorter = conf.file_sorter(opts), + on_complete = { + function(self) + local prompt = action_state.get_current_line() + + -- HACK: self.manager:num_results() can return 0 despite having results + -- due to some async/event loop shenanigans (#3316) + local count = 0 + for _, entry in pairs(self.finder.results) do + if entry and entry.valid ~= false then + count = count + 1 + end + end + + if count == 0 and prompt == "" then + utils.notify("builtin.git_status", { + msg = "No changes found", + level = "INFO", + }) + end + end, + }, + attach_mappings = function(prompt_bufnr, map) + actions.git_staging_toggle:enhance { + post = function() + local picker = action_state.get_current_picker(prompt_bufnr) + + -- temporarily register a callback which keeps selection on refresh + local selection = picker:get_selection_row() + local callbacks = { unpack(picker._completion_callbacks) } -- shallow copy + picker:register_completion_callback(function(self) + self:set_selection(selection) + self._completion_callbacks = callbacks + end) + + -- refresh + picker:refresh(gen_new_finder(), { reset_prompt = false }) + end, + } + + map({ "i", "n" }, "", actions.git_staging_toggle) + return true + end, + }) + :find() +end + +local try_worktrees = function(opts) + local worktrees = conf.git_worktrees + + if utils.islist(worktrees) then + for _, wt in ipairs(worktrees) do + if vim.startswith(opts.cwd, wt.toplevel) then + opts.toplevel = wt.toplevel + opts.gitdir = wt.gitdir + if opts.use_git_root then + opts.cwd = wt.toplevel + end + return + end + end + end + + error(opts.cwd .. " is not a git directory") +end + +local current_path_toplevel = function() + local gitdir = vim.fn.finddir(".git", vim.fn.expand "%:p" .. ";") + if gitdir == "" then + return nil + end + return Path:new(gitdir):parent():absolute() +end + +local set_opts_cwd = function(opts) + opts.use_git_root = vim.F.if_nil(opts.use_git_root, true) + if opts.cwd then + opts.cwd = utils.path_expand(opts.cwd) + elseif opts.use_file_path then + opts.cwd = current_path_toplevel() + if not opts.cwd then + opts.cwd = vim.fn.expand "%:p:h" + try_worktrees(opts) + return + end + else + opts.cwd = vim.loop.cwd() + end + + local toplevel, ret = utils.get_os_command_output({ "git", "rev-parse", "--show-toplevel" }, opts.cwd) + + if ret ~= 0 then + local in_worktree = utils.get_os_command_output({ "git", "rev-parse", "--is-inside-work-tree" }, opts.cwd) + local in_bare = utils.get_os_command_output({ "git", "rev-parse", "--is-bare-repository" }, opts.cwd) + + if in_worktree[1] ~= "true" and in_bare[1] ~= "true" then + try_worktrees(opts) + elseif in_worktree[1] ~= "true" and in_bare[1] == "true" then + opts.is_bare = true + end + else + if opts.use_git_root then + opts.cwd = toplevel[1] + end + end +end + +local function apply_checks(mod) + for k, v in pairs(mod) do + mod[k] = function(opts) + opts = vim.F.if_nil(opts, {}) + + set_opts_cwd(opts) + v(opts) + end + end + + return mod +end + +return apply_checks(git) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/builtin/__internal.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/builtin/__internal.lua new file mode 100644 index 0000000..1687401 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/builtin/__internal.lua @@ -0,0 +1,1522 @@ +local actions = require "telescope.actions" +local action_set = require "telescope.actions.set" +local action_state = require "telescope.actions.state" +local finders = require "telescope.finders" +local make_entry = require "telescope.make_entry" +local Path = require "plenary.path" +local pickers = require "telescope.pickers" +local previewers = require "telescope.previewers" +local p_window = require "telescope.pickers.window" +local state = require "telescope.state" +local utils = require "telescope.utils" + +local conf = require("telescope.config").values + +-- Makes sure aliased options are set correctly +local function apply_cwd_only_aliases(opts) + local has_cwd_only = opts.cwd_only ~= nil + local has_only_cwd = opts.only_cwd ~= nil + + if has_only_cwd and not has_cwd_only then + -- Internally, use cwd_only + opts.cwd_only = opts.only_cwd + opts.only_cwd = nil + end + + return opts +end + +---@return boolean +local function buf_in_cwd(bufname, cwd) + if cwd:sub(-1) ~= Path.path.sep then + cwd = cwd .. Path.path.sep + end + local bufname_prefix = bufname:sub(1, #cwd) + return bufname_prefix == cwd +end + +local internal = {} + +internal.builtin = function(opts) + opts.include_extensions = vim.F.if_nil(opts.include_extensions, false) + opts.use_default_opts = vim.F.if_nil(opts.use_default_opts, false) + + local objs = {} + + for k, v in pairs(require "telescope.builtin") do + local debug_info = debug.getinfo(v) + table.insert(objs, { + filename = string.sub(debug_info.source, 2), + text = k, + }) + end + + local title = "Telescope Builtin" + + if opts.include_extensions then + title = "Telescope Pickers" + for ext, funcs in pairs(require("telescope").extensions) do + for func_name, func_obj in pairs(funcs) do + -- Only include exported functions whose name doesn't begin with an underscore + if type(func_obj) == "function" and string.sub(func_name, 0, 1) ~= "_" then + local debug_info = debug.getinfo(func_obj) + table.insert(objs, { + filename = string.sub(debug_info.source, 2), + text = string.format("%s : %s", ext, func_name), + }) + end + end + end + end + + table.sort(objs, function(a, b) + return a.text < b.text + end) + + opts.bufnr = vim.api.nvim_get_current_buf() + opts.winnr = vim.api.nvim_get_current_win() + pickers + .new(opts, { + prompt_title = title, + finder = finders.new_table { + results = objs, + entry_maker = function(entry) + return make_entry.set_default_entry_mt({ + value = entry, + text = entry.text, + display = entry.text, + ordinal = entry.text, + filename = entry.filename, + }, opts) + end, + }, + previewer = previewers.builtin.new(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function(_) + actions.select_default:replace(function(prompt_bufnr) + local selection = action_state.get_selected_entry() + if not selection then + utils.__warn_no_selection "builtin.builtin" + return + end + + -- we do this to avoid any surprises + opts.include_extensions = nil + + local picker_opts + if not opts.use_default_opts then + picker_opts = opts + end + + actions.close(prompt_bufnr) + vim.schedule(function() + if string.match(selection.text, " : ") then + -- Call appropriate function from extensions + local split_string = vim.split(selection.text, " : ") + local ext = split_string[1] + local func = split_string[2] + require("telescope").extensions[ext][func](picker_opts) + else + -- Call appropriate telescope builtin + require("telescope.builtin")[selection.text](picker_opts) + end + end) + end) + return true + end, + }) + :find() +end + +internal.resume = function(opts) + opts = opts or {} + opts.cache_index = vim.F.if_nil(opts.cache_index, 1) + + local cached_pickers = state.get_global_key "cached_pickers" + if cached_pickers == nil or vim.tbl_isempty(cached_pickers) then + utils.notify("builtin.resume", { + msg = "No cached picker(s).", + level = "INFO", + }) + return + end + local picker = cached_pickers[opts.cache_index] + if picker == nil then + utils.notify("builtin.resume", { + msg = string.format("Index too large as there are only '%s' pickers cached", #cached_pickers), + level = "ERROR", + }) + return + end + -- reset layout strategy and get_window_options if default as only one is valid + -- and otherwise unclear which was actually set + if picker.layout_strategy == conf.layout_strategy then + picker.layout_strategy = nil + end + if picker.get_window_options == p_window.get_window_options then + picker.get_window_options = nil + end + picker.cache_picker.index = opts.cache_index + + -- avoid partial `opts.cache_picker` at picker creation + if opts.cache_picker ~= false then + picker.cache_picker = vim.tbl_extend("keep", opts.cache_picker or {}, picker.cache_picker) + else + picker.cache_picker.disabled = true + end + opts.cache_picker = nil + picker.previewer = picker.all_previewers + if picker.hidden_previewer then + picker.hidden_previewer = nil + opts.previewer = vim.F.if_nil(opts.previewer, false) + end + opts.resumed_picker = true + pickers.new(opts, picker):find() +end + +internal.pickers = function(opts) + local cached_pickers = state.get_global_key "cached_pickers" + if cached_pickers == nil or vim.tbl_isempty(cached_pickers) then + utils.notify("builtin.pickers", { + msg = "No cached picker(s).", + level = "INFO", + }) + return + end + + opts = opts or {} + + -- clear cache picker for immediate pickers.new and pass option to resumed picker + if opts.cache_picker ~= nil then + opts._cache_picker = opts.cache_picker + opts.cache_picker = nil + end + + pickers + .new(opts, { + prompt_title = "Pickers", + finder = finders.new_table { + results = cached_pickers, + entry_maker = make_entry.gen_from_picker(opts), + }, + previewer = previewers.pickers.new(opts), + sorter = conf.generic_sorter(opts), + cache_picker = false, + attach_mappings = function(_, map) + actions.select_default:replace(function(prompt_bufnr) + local curr_picker = action_state.get_current_picker(prompt_bufnr) + local curr_entry = action_state.get_selected_entry() + if not curr_entry then + return + end + + actions.close(prompt_bufnr) + + local selection_index, _ = utils.list_find(function(v) + if curr_entry.value == v.value then + return true + end + return false + end, curr_picker.finder.results) + + opts.cache_picker = opts._cache_picker + opts["cache_index"] = selection_index + opts["initial_mode"] = cached_pickers[selection_index].initial_mode + internal.resume(opts) + end) + map({ "i", "n" }, "", actions.remove_selected_picker) + return true + end, + }) + :find() +end + +internal.planets = function(opts) + local show_pluto = opts.show_pluto or false + local show_moon = opts.show_moon or false + + local sourced_file = require("plenary.debug_utils").sourced_filepath() + local base_directory = vim.fn.fnamemodify(sourced_file, ":h:h:h:h") + + local globbed_files = vim.fn.globpath(base_directory .. "/data/memes/planets/", "*", true, true) + local acceptable_files = {} + for _, v in ipairs(globbed_files) do + if (show_pluto or not v:find "pluto") and (show_moon or not v:find "moon") then + table.insert(acceptable_files, vim.fn.fnamemodify(v, ":t")) + end + end + + pickers + .new(opts, { + prompt_title = "Planets", + finder = finders.new_table { + results = acceptable_files, + entry_maker = function(line) + return make_entry.set_default_entry_mt({ + ordinal = line, + display = line, + filename = base_directory .. "/data/memes/planets/" .. line, + }, opts) + end, + }, + previewer = previewers.cat.new(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.planets" + return + end + + actions.close(prompt_bufnr) + print("Enjoy astronomy! You viewed:", selection.display) + end) + + return true + end, + }) + :find() +end + +internal.symbols = function(opts) + local initial_mode = vim.fn.mode() + local files = vim.api.nvim_get_runtime_file("data/telescope-sources/*.json", true) + local data_path = (function() + if not opts.symbol_path then + return Path:new { vim.fn.stdpath "data", "telescope", "symbols" } + else + return Path:new { opts.symbol_path } + end + end)() + if data_path:exists() then + for _, v in ipairs(require("plenary.scandir").scan_dir(data_path:absolute(), { search_pattern = "%.json$" })) do + table.insert(files, v) + end + end + + if #files == 0 then + utils.notify("builtin.symbols", { + msg = "No sources found! Check out https://github.com/nvim-telescope/telescope-symbols.nvim " + .. "for some prebuild symbols or how to create you own symbol source.", + level = "ERROR", + }) + return + end + + local sources = {} + if opts.sources then + for _, v in ipairs(files) do + for _, s in ipairs(opts.sources) do + if v:find(s) then + table.insert(sources, v) + end + end + end + else + sources = files + end + + local results = {} + for _, source in ipairs(sources) do + local data = vim.json.decode(Path:new(source):read()) + for _, entry in ipairs(data) do + table.insert(results, entry) + end + end + + pickers + .new(opts, { + prompt_title = "Symbols", + finder = finders.new_table { + results = results, + entry_maker = function(entry) + return make_entry.set_default_entry_mt({ + value = entry, + ordinal = entry[1] .. " " .. entry[2], + display = entry[1] .. " " .. entry[2], + }, opts) + end, + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(_) + if initial_mode == "i" then + actions.select_default:replace(actions.insert_symbol_i) + else + actions.select_default:replace(actions.insert_symbol) + end + return true + end, + }) + :find() +end + +internal.commands = function(opts) + pickers + .new(opts, { + prompt_title = "Commands", + finder = finders.new_table { + results = (function() + local command_iter = vim.api.nvim_get_commands {} + local commands = {} + + for _, cmd in pairs(command_iter) do + table.insert(commands, cmd) + end + + local need_buf_command = vim.F.if_nil(opts.show_buf_command, true) + + if need_buf_command then + local buf_command_iter = vim.api.nvim_buf_get_commands(0, {}) + buf_command_iter[true] = nil -- remove the redundant entry + for _, cmd in pairs(buf_command_iter) do + table.insert(commands, cmd) + end + end + return commands + end)(), + + entry_maker = opts.entry_maker or make_entry.gen_from_commands(opts), + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.commands" + return + end + + actions.close(prompt_bufnr) + local val = selection.value + local cmd = string.format([[:%s ]], val.name) + + if val.nargs == "0" then + local cr = vim.api.nvim_replace_termcodes("", true, false, true) + cmd = cmd .. cr + end + vim.cmd [[stopinsert]] + vim.api.nvim_feedkeys(cmd, "nt", false) + end) + + return true + end, + }) + :find() +end + +internal.quickfix = function(opts) + local qf_identifier = opts.id or vim.F.if_nil(opts.nr, "$") + local locations = vim.fn.getqflist({ [opts.id and "id" or "nr"] = qf_identifier, items = true }).items + + if vim.tbl_isempty(locations) then + utils.notify("builtin.quickfix", { msg = "No quickfix items", level = "INFO" }) + return + end + + pickers + .new(opts, { + prompt_title = "Quickfix", + finder = finders.new_table { + results = locations, + entry_maker = opts.entry_maker or make_entry.gen_from_quickfix(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.generic_sorter(opts), + }) + :find() +end + +internal.quickfixhistory = function(opts) + local qflists = {} + for i = 1, 10 do -- (n)vim keeps at most 10 quickfix lists in full + -- qf weirdness: id = 0 gets id of quickfix list nr + local qflist = vim.fn.getqflist { nr = i, id = 0, title = true, items = true } + if not vim.tbl_isempty(qflist.items) then + table.insert(qflists, qflist) + end + end + local entry_maker = opts.make_entry + or function(entry) + return make_entry.set_default_entry_mt({ + value = entry.title or "Untitled", + ordinal = entry.title or "Untitled", + display = entry.title or "Untitled", + nr = entry.nr, + id = entry.id, + items = entry.items, + }, opts) + end + local qf_entry_maker = make_entry.gen_from_quickfix(opts) + pickers + .new(opts, { + prompt_title = "Quickfix History", + finder = finders.new_table { + results = qflists, + entry_maker = entry_maker, + }, + previewer = previewers.new_buffer_previewer { + title = "Quickfix List Preview", + dyn_title = function(_, entry) + return entry.title + end, + + get_buffer_by_name = function(_, entry) + return "quickfixlist_" .. tostring(entry.nr) + end, + + define_preview = function(self, entry) + if self.state.bufname then + return + end + local entries = vim.tbl_map(function(i) + return qf_entry_maker(i):display() + end, entry.items) + vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, entries) + end, + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(_, map) + action_set.select:replace(function(prompt_bufnr) + local nr = action_state.get_selected_entry().nr + actions.close(prompt_bufnr) + internal.quickfix { nr = nr } + end) + + map({ "i", "n" }, "", function(prompt_bufnr) + local nr = action_state.get_selected_entry().nr + actions.close(prompt_bufnr) + vim.cmd(nr .. "chistory") + vim.cmd "botright copen" + end) + return true + end, + }) + :find() +end + +internal.loclist = function(opts) + local locations = vim.fn.getloclist(0) + local filenames = {} + for _, value in pairs(locations) do + local bufnr = value.bufnr + if filenames[bufnr] == nil then + filenames[bufnr] = vim.api.nvim_buf_get_name(bufnr) + end + value.filename = filenames[bufnr] + end + + if vim.tbl_isempty(locations) then + utils.notify("builtin.loclist", { msg = "No loclist items", level = "INFO" }) + return + end + + pickers + .new(opts, { + prompt_title = "Loclist", + finder = finders.new_table { + results = locations, + entry_maker = opts.entry_maker or make_entry.gen_from_quickfix(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.generic_sorter(opts), + }) + :find() +end + +internal.oldfiles = function(opts) + opts = apply_cwd_only_aliases(opts) + opts.include_current_session = vim.F.if_nil(opts.include_current_session, true) + + local current_buffer = vim.api.nvim_get_current_buf() + local current_file = vim.api.nvim_buf_get_name(current_buffer) + local results = {} + + if utils.iswin then -- for slash problem in windows + current_file = current_file:gsub("/", "\\") + end + + if opts.include_current_session then + for _, buffer in ipairs(utils.split_lines(vim.fn.execute ":buffers! t")) do + local match = tonumber(string.match(buffer, "%s*(%d+)")) + local open_by_lsp = string.match(buffer, "line 0$") + if match and not open_by_lsp then + local file = vim.api.nvim_buf_get_name(match) + if utils.iswin then + file = file:gsub("/", "\\") + end + if vim.loop.fs_stat(file) and match ~= current_buffer then + table.insert(results, file) + end + end + end + end + + for _, file in ipairs(vim.v.oldfiles) do + if utils.iswin then + file = file:gsub("/", "\\") + end + local file_stat = vim.loop.fs_stat(file) + if file_stat and file_stat.type == "file" and not vim.tbl_contains(results, file) and file ~= current_file then + table.insert(results, file) + end + end + + if opts.cwd_only or opts.cwd then + local cwd = opts.cwd_only and vim.loop.cwd() or opts.cwd + results = vim.tbl_filter(function(file) + return buf_in_cwd(file, cwd) + end, results) + end + + pickers + .new(opts, { + prompt_title = "Oldfiles", + __locations_input = true, + finder = finders.new_table { + results = results, + entry_maker = opts.entry_maker or make_entry.gen_from_file(opts), + }, + sorter = conf.file_sorter(opts), + previewer = conf.grep_previewer(opts), + }) + :find() +end + +internal.command_history = function(opts) + local history_string = vim.fn.execute "history cmd" + local history_list = utils.split_lines(history_string) + + local results = {} + local filter_fn = opts.filter_fn + + for i = #history_list, 3, -1 do + local item = history_list[i] + local _, finish = string.find(item, "%d+ +") + local cmd = string.sub(item, finish + 1) + + if filter_fn then + if filter_fn(cmd) then + table.insert(results, cmd) + end + else + table.insert(results, cmd) + end + end + + pickers + .new(opts, { + prompt_title = "Command History", + finder = finders.new_table(results), + sorter = conf.generic_sorter(opts), + + attach_mappings = function(_, map) + actions.select_default:replace(actions.set_command_line) + map({ "i", "n" }, "", actions.edit_command_line) + + -- TODO: Find a way to insert the text... it seems hard. + -- map('i', '', actions.insert_value, { expr = true }) + + return true + end, + }) + :find() +end + +internal.search_history = function(opts) + local search_string = vim.fn.execute "history search" + local search_list = utils.split_lines(search_string) + + local results = {} + for i = #search_list, 3, -1 do + local item = search_list[i] + local _, finish = string.find(item, "%d+ +") + table.insert(results, string.sub(item, finish + 1)) + end + + pickers + .new(opts, { + prompt_title = "Search History", + finder = finders.new_table(results), + sorter = conf.generic_sorter(opts), + + attach_mappings = function(_, map) + actions.select_default:replace(actions.set_search_line) + map({ "i", "n" }, "", actions.edit_search_line) + + -- TODO: Find a way to insert the text... it seems hard. + -- map('i', '', actions.insert_value, { expr = true }) + + return true + end, + }) + :find() +end + +internal.vim_options = function(opts) + local res = {} + for _, v in pairs(vim.api.nvim_get_all_options_info()) do + local ok, value = pcall(vim.api.nvim_get_option_value, v.name, {}) + if ok then + v.value = value + table.insert(res, v) + end + end + table.sort(res, function(left, right) + return left.name < right.name + end) + + pickers + .new(opts, { + prompt_title = "options", + finder = finders.new_table { + results = res, + entry_maker = opts.entry_maker or make_entry.gen_from_vimoptions(opts), + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function() + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.vim_options" + return + end + + local esc = "" + if vim.fn.mode() == "i" then + esc = vim.api.nvim_replace_termcodes("", true, false, true) + end + + vim.api.nvim_feedkeys( + selection.value.type == "boolean" and string.format("%s:set %s!", esc, selection.value.name) + or string.format("%s:set %s=%s", esc, selection.value.name, selection.value.value), + "m", + true + ) + end) + + return true + end, + }) + :find() +end + +internal.help_tags = function(opts) + opts.lang = vim.F.if_nil(opts.lang, vim.o.helplang) + opts.fallback = vim.F.if_nil(opts.fallback, true) + opts.file_ignore_patterns = {} + + local langs = vim.split(opts.lang, ",", { trimempty = true }) + if opts.fallback and not vim.tbl_contains(langs, "en") then + table.insert(langs, "en") + end + local langs_map = {} + for _, lang in ipairs(langs) do + langs_map[lang] = true + end + + local tag_files = {} + local function add_tag_file(lang, file) + if langs_map[lang] then + if tag_files[lang] then + table.insert(tag_files[lang], file) + else + tag_files[lang] = { file } + end + end + end + + local help_files = {} + + local rtp = vim.o.runtimepath + -- extend the runtime path with all plugins not loaded by lazy.nvim + local lazy = package.loaded["lazy.core.util"] + if lazy and lazy.get_unloaded_rtp then + local paths = lazy.get_unloaded_rtp "" + if #paths > 0 then + rtp = rtp .. "," .. table.concat(paths, ",") + end + end + local all_files = vim.fn.globpath(rtp, "doc/*", 1, 1) + for _, fullpath in ipairs(all_files) do + local file = utils.path_tail(fullpath) + if file == "tags" then + add_tag_file("en", fullpath) + elseif file:match "^tags%-..$" then + local lang = file:sub(-2) + add_tag_file(lang, fullpath) + else + help_files[file] = fullpath + end + end + + local tags = {} + local tags_map = {} + local delimiter = string.char(9) + for _, lang in ipairs(langs) do + for _, file in ipairs(tag_files[lang] or {}) do + local lines = utils.split_lines(Path:new(file):read(), { trimempty = true }) + for _, line in ipairs(lines) do + -- TODO: also ignore tagComment starting with ';' + if not line:match "^!_TAG_" then + local fields = vim.split(line, delimiter, { trimempty = true }) + if #fields == 3 and not tags_map[fields[1]] then + if fields[1] ~= "help-tags" or fields[2] ~= "tags" then + table.insert(tags, { + name = fields[1], + filename = help_files[fields[2]], + cmd = fields[3], + lang = lang, + }) + tags_map[fields[1]] = true + end + end + end + end + end + end + + pickers + .new(opts, { + prompt_title = "Help", + finder = finders.new_table { + results = tags, + entry_maker = function(entry) + return make_entry.set_default_entry_mt({ + value = entry.name .. "@" .. entry.lang, + display = entry.name, + ordinal = entry.name, + filename = entry.filename, + cmd = entry.cmd, + }, opts) + end, + }, + previewer = previewers.help.new(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + action_set.select:replace(function(_, cmd) + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.help_tags" + return + end + + actions.close(prompt_bufnr) + if cmd == "default" or cmd == "horizontal" then + vim.cmd("help " .. selection.value) + elseif cmd == "vertical" then + vim.cmd("vert help " .. selection.value) + elseif cmd == "tab" then + vim.cmd("tab help " .. selection.value) + end + end) + + return true + end, + }) + :find() +end + +internal.man_pages = function(opts) + opts.sections = vim.F.if_nil(opts.sections, { "1" }) + assert(utils.islist(opts.sections), "sections should be a list") + opts.man_cmd = utils.get_lazy_default(opts.man_cmd, function() + local uname = vim.loop.os_uname() + local sysname = string.lower(uname.sysname) + if sysname == "darwin" then + local major_version = tonumber(vim.fn.matchlist(uname.release, [[^\(\d\+\)\..*]])[2]) or 0 + return major_version >= 22 and { "apropos", "." } or { "apropos", " " } + elseif sysname == "freebsd" then + return { "apropos", "." } + else + return { "apropos", "" } + end + end) + opts.entry_maker = opts.entry_maker or make_entry.gen_from_apropos(opts) + opts.env = { PATH = vim.env.PATH, MANPATH = vim.env.MANPATH } + + pickers + .new(opts, { + prompt_title = "Man", + finder = finders.new_oneshot_job(opts.man_cmd, opts), + previewer = previewers.man.new(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + action_set.select:replace(function(_, cmd) + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.man_pages" + return + end + + local args = selection.section .. " " .. selection.value + actions.close(prompt_bufnr) + if cmd == "default" or cmd == "horizontal" then + vim.cmd("Man " .. args) + elseif cmd == "vertical" then + vim.cmd("vert Man " .. args) + elseif cmd == "tab" then + vim.cmd("tab Man " .. args) + end + end) + + return true + end, + }) + :find() +end + +internal.reloader = function(opts) + local package_list = vim.tbl_keys(package.loaded) + + -- filter out packages we don't want and track the longest package name + local column_len = 0 + for index, module_name in pairs(package_list) do + if + type(require(module_name)) ~= "table" + or module_name:sub(1, 1) == "_" + or package.searchpath(module_name, package.path) == nil + then + table.remove(package_list, index) + elseif #module_name > column_len then + column_len = #module_name + end + end + opts.column_len = vim.F.if_nil(opts.column_len, column_len) + + pickers + .new(opts, { + prompt_title = "Packages", + finder = finders.new_table { + results = package_list, + entry_maker = opts.entry_maker or make_entry.gen_from_packages(opts), + }, + -- previewer = previewers.vim_buffer.new(opts), + sorter = conf.generic_sorter(opts), + + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.reloader" + return + end + + actions.close(prompt_bufnr) + require("plenary.reload").reload_module(selection.value) + utils.notify("builtin.reloader", { + msg = string.format("[%s] - module reloaded", selection.value), + level = "INFO", + }) + end) + + return true + end, + }) + :find() +end + +internal.buffers = function(opts) + opts = apply_cwd_only_aliases(opts) + + local bufnrs = vim.tbl_filter(function(bufnr) + if 1 ~= vim.fn.buflisted(bufnr) then + return false + end + -- only hide unloaded buffers if opts.show_all_buffers is false, keep them listed if true or nil + if opts.show_all_buffers == false and not vim.api.nvim_buf_is_loaded(bufnr) then + return false + end + if opts.ignore_current_buffer and bufnr == vim.api.nvim_get_current_buf() then + return false + end + + local bufname = vim.api.nvim_buf_get_name(bufnr) + + if opts.cwd_only and not buf_in_cwd(bufname, vim.loop.cwd()) then + return false + end + if not opts.cwd_only and opts.cwd and not buf_in_cwd(bufname, opts.cwd) then + return false + end + return true + end, vim.api.nvim_list_bufs()) + + if not next(bufnrs) then + utils.notify("builtin.buffers", { msg = "No buffers found with the provided options", level = "INFO" }) + return + end + + if opts.sort_mru then + table.sort(bufnrs, function(a, b) + return vim.fn.getbufinfo(a)[1].lastused > vim.fn.getbufinfo(b)[1].lastused + end) + end + + if type(opts.sort_buffers) == "function" then + table.sort(bufnrs, opts.sort_buffers) + end + + local buffers = {} + local default_selection_idx = 1 + for i, bufnr in ipairs(bufnrs) do + local flag = bufnr == vim.fn.bufnr "" and "%" or (bufnr == vim.fn.bufnr "#" and "#" or " ") + + if opts.sort_lastused and not opts.ignore_current_buffer and flag == "#" then + default_selection_idx = 2 + end + + local element = { + bufnr = bufnr, + flag = flag, + info = vim.fn.getbufinfo(bufnr)[1], + } + + if opts.sort_lastused and (flag == "#" or flag == "%") then + local idx = ((buffers[1] ~= nil and buffers[1].flag == "%") and 2 or 1) + table.insert(buffers, idx, element) + else + if opts.select_current and flag == "%" then + default_selection_idx = i + end + table.insert(buffers, element) + end + end + + if not opts.bufnr_width then + local max_bufnr = math.max(unpack(bufnrs)) + opts.bufnr_width = #tostring(max_bufnr) + end + + pickers + .new(opts, { + prompt_title = "Buffers", + finder = finders.new_table { + results = buffers, + entry_maker = opts.entry_maker or make_entry.gen_from_buffer(opts), + }, + previewer = conf.grep_previewer(opts), + sorter = conf.generic_sorter(opts), + default_selection_index = default_selection_idx, + attach_mappings = function(_, map) + map({ "i", "n" }, "", actions.delete_buffer) + return true + end, + }) + :find() +end + +internal.colorscheme = function(opts) + local before_background = vim.o.background + local before_color = vim.api.nvim_exec2("colorscheme", { output = true }).output + local need_restore = not not opts.enable_preview + + local colors = opts.colors or { before_color } + if not vim.tbl_contains(colors, before_color) then + table.insert(colors, 1, before_color) + end + + colors = vim.list_extend( + colors, + vim.tbl_filter(function(color) + return not vim.tbl_contains(colors, color) + end, vim.fn.getcompletion("", "color")) + ) + + -- if lazy is available, extend the colors list with unloaded colorschemes + local lazy = package.loaded["lazy.core.util"] + if lazy and lazy.get_unloaded_rtp then + local paths = lazy.get_unloaded_rtp "" + local all_files = vim.fn.globpath(table.concat(paths, ","), "colors/*", 1, 1) + for _, f in ipairs(all_files) do + local color = vim.fn.fnamemodify(f, ":t:r") + if not vim.tbl_contains(colors, color) then + table.insert(colors, color) + end + end + end + + if opts.ignore_builtins then + -- stylua: ignore + local builtins = { + "blue", "darkblue", "default", "delek", "desert", "elflord", "evening", + "habamax", "industry", "koehler", "lunaperche", "morning", "murphy", + "pablo", "peachpuff", "quiet", "retrobox", "ron", "shine", "slate", + "sorbet", "torte", "vim", "wildcharm", "zaibatsu", "zellner", + } + colors = vim.tbl_filter(function(color) + return not vim.tbl_contains(builtins, color) + end, colors) + end + + local previewer + if opts.enable_preview then + -- define previewer + local bufnr = vim.api.nvim_get_current_buf() + local p = vim.api.nvim_buf_get_name(bufnr) + + -- show current buffer content in previewer + previewer = previewers.new_buffer_previewer { + get_buffer_by_name = function() + return p + end, + define_preview = function(self) + if vim.loop.fs_stat(p) then + conf.buffer_previewer_maker(p, self.state.bufnr, { bufname = self.state.bufname }) + else + local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) + vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, lines) + end + end, + } + end + + local picker = pickers.new(opts, { + prompt_title = "Change Colorscheme", + finder = finders.new_table { + results = colors, + }, + sorter = conf.generic_sorter(opts), + previewer = previewer, + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.colorscheme" + return + end + + need_restore = false + actions.close(prompt_bufnr) + vim.cmd.colorscheme(selection.value) + end) + return true + end, + on_complete = { + function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.colorscheme" + return + end + if opts.enable_preview then + vim.cmd.colorscheme(selection.value) + end + end, + }, + }) + + if opts.enable_preview then + -- rewrite picker.close_windows. restore color if needed + local close_windows = picker.close_windows + picker.close_windows = function(status) + close_windows(status) + if need_restore then + vim.o.background = before_background + vim.cmd.colorscheme(before_color) + end + end + + -- rewrite picker.set_selection so that color schemes can be previewed when the current + -- selection is shifted using the keyboard or if an item is clicked with the mouse + local set_selection = picker.set_selection + picker.set_selection = function(self, row) + set_selection(self, row) + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.colorscheme" + return + end + if opts.enable_preview then + vim.cmd.colorscheme(selection.value) + end + end + end + + picker:find() +end + +internal.marks = function(opts) + local local_marks = { + items = vim.fn.getmarklist(opts.bufnr), + name_func = function(_, line) + return vim.api.nvim_buf_get_lines(opts.bufnr, line - 1, line, false)[1] + end, + } + local global_marks = { + items = vim.fn.getmarklist(), + name_func = function(mark, _) + -- get buffer name if it is opened, otherwise get file name + return vim.api.nvim_get_mark(mark, {})[4] + end, + } + local marks_table = {} + local marks_others = {} + local bufname = vim.api.nvim_buf_get_name(opts.bufnr) + local all_marks = {} + opts.mark_type = vim.F.if_nil(opts.mark_type, "all") + if opts.mark_type == "all" then + all_marks = { local_marks, global_marks } + elseif opts.mark_type == "local" then + all_marks = { local_marks } + elseif opts.mark_type == "global" then + all_marks = { global_marks } + end + + for _, cnf in ipairs(all_marks) do + for _, v in ipairs(cnf.items) do + -- strip the first single quote character + local mark = string.sub(v.mark, 2, 3) + local _, lnum, col, _ = unpack(v.pos) + local name = cnf.name_func(mark, lnum) + -- same format to :marks command + local line = string.format("%s %6d %4d %s", mark, lnum, col - 1, name) + local row = { + line = line, + lnum = lnum, + col = col, + filename = utils.path_expand(v.file or bufname), + } + -- non alphanumeric marks goes to last + if mark:match "%w" then + table.insert(marks_table, row) + else + table.insert(marks_others, row) + end + end + end + marks_table = vim.fn.extend(marks_table, marks_others) + + pickers + .new(opts, { + prompt_title = "Marks", + finder = finders.new_table { + results = marks_table, + entry_maker = opts.entry_maker or make_entry.gen_from_marks(opts), + }, + previewer = conf.grep_previewer(opts), + sorter = conf.generic_sorter(opts), + push_cursor_on_edit = true, + push_tagstack_on_edit = true, + }) + :find() +end + +internal.registers = function(opts) + local registers_table = { '"', "-", "#", "=", "/", "*", "+", ":", ".", "%" } + + -- named + for i = 0, 9 do + table.insert(registers_table, tostring(i)) + end + + -- alphabetical + for i = 65, 90 do + table.insert(registers_table, string.char(i)) + end + + pickers + .new(opts, { + prompt_title = "Registers", + finder = finders.new_table { + results = registers_table, + entry_maker = opts.entry_maker or make_entry.gen_from_registers(opts), + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(_, map) + actions.select_default:replace(actions.paste_register) + map({ "i", "n" }, "", actions.edit_register) + + return true + end, + }) + :find() +end + +internal.keymaps = function(opts) + opts.modes = vim.F.if_nil(opts.modes, { "n", "i", "c", "x" }) + opts.show_plug = vim.F.if_nil(opts.show_plug, true) + opts.only_buf = vim.F.if_nil(opts.only_buf, false) + + local keymap_encountered = {} -- used to make sure no duplicates are inserted into keymaps_table + local keymaps_table = {} + local max_len_lhs = 0 + + -- helper function to populate keymaps_table and determine max_len_lhs + local function extract_keymaps(keymaps) + for _, keymap in pairs(keymaps) do + local keymap_key = keymap.buffer .. keymap.mode .. keymap.lhs -- should be distinct for every keymap + if not keymap_encountered[keymap_key] then + keymap_encountered[keymap_key] = true + if + (opts.show_plug or not string.find(keymap.lhs, "")) + and (not opts.lhs_filter or opts.lhs_filter(keymap.lhs)) + and (not opts.filter or opts.filter(keymap)) + then + table.insert(keymaps_table, keymap) + max_len_lhs = math.max(max_len_lhs, #utils.display_termcodes(keymap.lhs)) + end + end + end + end + + for _, mode in pairs(opts.modes) do + local global = vim.api.nvim_get_keymap(mode) + local buf_local = vim.api.nvim_buf_get_keymap(0, mode) + if not opts.only_buf then + extract_keymaps(global) + end + extract_keymaps(buf_local) + end + opts.width_lhs = max_len_lhs + 1 + + pickers + .new(opts, { + prompt_title = "Key Maps", + finder = finders.new_table { + results = keymaps_table, + entry_maker = opts.entry_maker or make_entry.gen_from_keymaps(opts), + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.keymaps" + return + end + + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(selection.value.lhs, true, false, true), "t", true) + return actions.close(prompt_bufnr) + end) + return true + end, + }) + :find() +end + +internal.filetypes = function(opts) + local filetypes = vim.fn.getcompletion("", "filetype") + + pickers + .new(opts, { + prompt_title = "Filetypes", + finder = finders.new_table { + results = filetypes, + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + print "[telescope] Nothing currently selected" + return + end + + actions.close(prompt_bufnr) + vim.cmd("setfiletype " .. selection[1]) + end) + return true + end, + }) + :find() +end + +internal.highlights = function(opts) + local highlights = vim.fn.getcompletion("", "highlight") + + pickers + .new(opts, { + prompt_title = "Highlights", + finder = finders.new_table { + results = highlights, + entry_maker = opts.entry_maker or make_entry.gen_from_highlights(opts), + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.highlights" + return + end + + actions.close(prompt_bufnr) + vim.cmd("hi " .. selection.value) + end) + return true + end, + previewer = previewers.highlights.new(opts), + }) + :find() +end + +internal.autocommands = function(opts) + local autocmds = vim.api.nvim_get_autocmds {} + table.sort(autocmds, function(lhs, rhs) + return lhs.event < rhs.event + end) + pickers + .new(opts, { + prompt_title = "autocommands", + finder = finders.new_table { + results = autocmds, + entry_maker = opts.entry_maker or make_entry.gen_from_autocommands(opts), + }, + previewer = previewers.autocommands.new(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + action_set.select:replace_if(function() + local selection = action_state.get_selected_entry() + if selection == nil then + return false + end + local val = selection.value + local cb = val.callback + if vim.is_callable(cb) then + if type(cb) ~= "string" then + local f = type(cb) == "function" and cb or rawget(getmetatable(cb), "__call") + local info = debug.getinfo(f, "S") + local file = info.source:match "^@(.+)" + local lnum = info.linedefined + if file and (lnum or 0) > 0 then + selection.filename, selection.lnum, selection.col = file, lnum, 1 + return false + end + end + end + local group_name = val.group_name ~= "" and val.group_name or "" + local output = + vim.fn.execute("verb autocmd " .. group_name .. " " .. val.event .. " " .. val.pattern, "silent") + for line in output:gmatch "[^\r\n]+" do + local source_file = line:match "Last set from (.*) line %d*$" or line:match "Last set from (.*)$" + if source_file and source_file ~= "Lua" then + selection.filename = source_file + local source_lnum = line:match "line (%d*)$" or "1" + selection.lnum = tonumber(source_lnum) + selection.col = 1 + return false + end + end + return true + end, function() + local selection = action_state.get_selected_entry() + actions.close(prompt_bufnr) + print("You selected autocmd: " .. vim.inspect(selection.value)) + end) + + return true + end, + }) + :find() +end + +internal.spell_suggest = function(opts) + local cursor_word = vim.fn.expand "" + local suggestions = vim.fn.spellsuggest(cursor_word) + + pickers + .new(opts, { + prompt_title = "Spelling Suggestions", + finder = finders.new_table { + results = suggestions, + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.spell_suggest" + return + end + + action_state.get_current_picker(prompt_bufnr)._original_mode = "i" + actions.close(prompt_bufnr) + vim.cmd('normal! "_ciw' .. selection[1]) + vim.cmd "stopinsert" + end) + return true + end, + }) + :find() +end + +internal.tagstack = function(opts) + opts = opts or {} + local tagstack = vim.fn.gettagstack().items + + local tags = {} + for i = #tagstack, 1, -1 do + local tag = tagstack[i] + tag.bufnr = tag.from[1] + if vim.api.nvim_buf_is_valid(tag.bufnr) then + tags[#tags + 1] = tag + tag.filename = vim.fn.bufname(tag.bufnr) + tag.lnum = tag.from[2] + tag.col = tag.from[3] + + tag.text = vim.api.nvim_buf_get_lines(tag.bufnr, tag.lnum - 1, tag.lnum, false)[1] or "" + end + end + + if vim.tbl_isempty(tags) then + utils.notify("builtin.tagstack", { + msg = "No tagstack available", + level = "WARN", + }) + return + end + + pickers + .new(opts, { + prompt_title = "TagStack", + finder = finders.new_table { + results = tags, + entry_maker = make_entry.gen_from_quickfix(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.generic_sorter(opts), + }) + :find() +end + +internal.jumplist = function(opts) + opts = opts or {} + local jumplist = vim.fn.getjumplist()[1] + + -- reverse the list + local sorted_jumplist = {} + for i = #jumplist, 1, -1 do + if vim.api.nvim_buf_is_valid(jumplist[i].bufnr) then + jumplist[i].text = vim.api.nvim_buf_get_lines(jumplist[i].bufnr, jumplist[i].lnum - 1, jumplist[i].lnum, false)[1] + or "" + table.insert(sorted_jumplist, jumplist[i]) + end + end + + pickers + .new(opts, { + prompt_title = "Jumplist", + finder = finders.new_table { + results = sorted_jumplist, + entry_maker = make_entry.gen_from_quickfix(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.generic_sorter(opts), + }) + :find() +end + +local function apply_checks(mod) + for k, v in pairs(mod) do + mod[k] = function(opts) + opts = opts or {} + + v(opts) + end + end + + return mod +end + +return apply_checks(internal) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/builtin/__lsp.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/builtin/__lsp.lua new file mode 100644 index 0000000..d51bb6c --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/builtin/__lsp.lua @@ -0,0 +1,555 @@ +local channel = require("plenary.async.control").channel +local actions = require "telescope.actions" +local sorters = require "telescope.sorters" +local conf = require("telescope.config").values +local finders = require "telescope.finders" +local make_entry = require "telescope.make_entry" +local pickers = require "telescope.pickers" +local utils = require "telescope.utils" + +local lsp = {} + +local function call_hierarchy(opts, method, title, direction, item) + vim.lsp.buf_request(opts.bufnr, method, { item = item }, function(err, result) + if err then + vim.api.nvim_err_writeln("Error handling " .. title .. ": " .. err.message) + return + end + + if not result or vim.tbl_isempty(result) then + return + end + + local locations = {} + for _, ch_call in pairs(result) do + local ch_item = ch_call[direction] + for _, rng in pairs(ch_call.fromRanges) do + table.insert(locations, { + filename = vim.uri_to_fname(ch_item.uri), + text = ch_item.name, + lnum = rng.start.line + 1, + col = rng.start.character + 1, + }) + end + end + + pickers + .new(opts, { + prompt_title = title, + finder = finders.new_table { + results = locations, + entry_maker = opts.entry_maker or make_entry.gen_from_quickfix(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.generic_sorter(opts), + push_cursor_on_edit = true, + push_tagstack_on_edit = true, + }) + :find() + end) +end + +local function pick_call_hierarchy_item(call_hierarchy_items) + if not call_hierarchy_items or vim.tbl_isempty(call_hierarchy_items) then + return + end + if #call_hierarchy_items == 1 then + return call_hierarchy_items[1] + end + local items = {} + for i, item in pairs(call_hierarchy_items) do + local entry = item.detail or item.name + table.insert(items, string.format("%d. %s", i, entry)) + end + local choice = vim.fn.inputlist(items) + if choice < 1 or choice > #items then + return + end + return call_hierarchy_items[choice] +end + +---@param win number? Window handler +---@param extra table? Extra fields in params +---@return table|(fun(client: vim.lsp.Client): table) parmas to send to the server +local function client_position_params(win, extra) + win = win or vim.api.nvim_get_current_win() + if vim.fn.has "nvim-0.11" == 0 then + local params = vim.lsp.util.make_position_params(win) + if extra then + params = vim.tbl_extend("force", params, extra) + end + return params + end + return function(client) + local params = vim.lsp.util.make_position_params(win, client.offset_encoding) + if extra then + params = vim.tbl_extend("force", params, extra) + end + return params + end +end + +local function calls(opts, direction) + local params = client_position_params() + vim.lsp.buf_request(opts.bufnr, "textDocument/prepareCallHierarchy", params, function(err, result) + if err then + vim.api.nvim_err_writeln("Error when preparing call hierarchy: " .. err) + return + end + + local call_hierarchy_item = pick_call_hierarchy_item(result) + if not call_hierarchy_item then + return + end + + if direction == "from" then + call_hierarchy(opts, "callHierarchy/incomingCalls", "LSP Incoming Calls", direction, call_hierarchy_item) + else + call_hierarchy(opts, "callHierarchy/outgoingCalls", "LSP Outgoing Calls", direction, call_hierarchy_item) + end + end) +end + +lsp.incoming_calls = function(opts) + calls(opts, "from") +end + +lsp.outgoing_calls = function(opts) + calls(opts, "to") +end + +--- convert `item` type back to something we can pass to `vim.lsp.util.jump_to_location` +--- stopgap for pre-nvim 0.10 - after which we can simply use the `user_data` +--- field on the items in `vim.lsp.util.locations_to_items` +---@param item vim.quickfix.entry +---@param offset_encoding string|nil utf-8|utf-16|utf-32 +---@return lsp.Location +local function item_to_location(item, offset_encoding) + local line = math.max(0, item.lnum - 1) + local character = math.max(0, utils.str_byteindex(item.text, item.col, offset_encoding or "utf-16") - 1) + local uri + if utils.is_uri(item.filename) then + uri = item.filename + else + uri = vim.uri_from_fname(item.filename) + end + return { + uri = uri, + range = { + start = { + line = line, + character = character, + }, + ["end"] = { + line = line, + character = character, + }, + }, + } +end + +---@alias telescope.lsp.list_or_jump_action +---| "textDocument/references" +---| "textDocument/definition" +---| "textDocument/typeDefinition" +---| "textDocument/implementation" + +---@param action telescope.lsp.list_or_jump_action +---@param items vim.quickfix.entry[] +---@param opts table +---@return vim.quickfix.entry[] +local apply_action_handler = function(action, items, opts) + if action == "textDocument/references" and not opts.include_current_line then + local lnum = vim.api.nvim_win_get_cursor(opts.winnr)[1] + items = vim.tbl_filter(function(v) + return not (v.filename == opts.curr_filepath and v.lnum == lnum) + end, items) + end + + return items +end + +---@param items vim.quickfix.entry[] +---@param opts table +---@return vim.quickfix.entry[] +local function filter_file_ignore_patters(items, opts) + local file_ignore_patterns = vim.F.if_nil(opts.file_ignore_patterns, conf.file_ignore_patterns) + file_ignore_patterns = file_ignore_patterns or {} + if vim.tbl_isempty(file_ignore_patterns) then + return items + end + + return vim.tbl_filter(function(item) + for _, patt in ipairs(file_ignore_patterns) do + if string.match(item.filename, patt) then + return false + end + end + return true + end, items) +end + +---@param action telescope.lsp.list_or_jump_action +---@param title string prompt title +---@param funname string: name of the calling function +---@param params lsp.TextDocumentPositionParams|(fun(client: vim.lsp.Client, bufnr: integer): table?) +---@param opts table +local function list_or_jump(action, title, funname, params, opts) + opts.reuse_win = vim.F.if_nil(opts.reuse_win, false) + opts.curr_filepath = vim.api.nvim_buf_get_name(opts.bufnr) + + vim.lsp.buf_request_all(opts.bufnr, action, params, function(results_per_client) + local items = {} + local first_encoding + local errors = {} + + for client_id, result_or_error in pairs(results_per_client) do + local error, result = result_or_error.error, result_or_error.result + if error then + errors[client_id] = error + else + if result ~= nil then + local locations = {} + + if not utils.islist(result) then + vim.list_extend(locations, { result }) + else + vim.list_extend(locations, result) + end + + local offset_encoding = vim.lsp.get_client_by_id(client_id).offset_encoding + + if not vim.tbl_isempty(result) then + first_encoding = offset_encoding + end + + vim.list_extend(items, vim.lsp.util.locations_to_items(locations, offset_encoding)) + end + end + end + + for _, error in pairs(errors) do + vim.api.nvim_err_writeln("Error when executing " .. action .. " : " .. error.message) + end + + items = apply_action_handler(action, items, opts) + items = filter_file_ignore_patters(items, opts) + + if vim.tbl_isempty(items) then + utils.notify(funname, { + msg = string.format("No %s found", title), + level = "INFO", + }) + return + end + + if #items == 1 and opts.jump_type ~= "never" then + local item = items[1] + if opts.curr_filepath ~= item.filename then + local cmd + if opts.jump_type == "tab" then + cmd = "tabedit" + elseif opts.jump_type == "split" then + cmd = "new" + elseif opts.jump_type == "vsplit" then + cmd = "vnew" + elseif opts.jump_type == "tab drop" then + cmd = "tab drop" + end + + if cmd then + vim.cmd(string.format("%s %s", cmd, item.filename)) + end + end + + local location = item_to_location(item, first_encoding) + vim.lsp.util.show_document(location, first_encoding, { reuse_win = opts.reuse_win }) + else + pickers + .new(opts, { + prompt_title = title, + finder = finders.new_table { + results = items, + entry_maker = opts.entry_maker or make_entry.gen_from_quickfix(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.generic_sorter(opts), + push_cursor_on_edit = true, + push_tagstack_on_edit = true, + }) + :find() + end + end) +end + +lsp.references = function(opts) + opts.include_current_line = vim.F.if_nil(opts.include_current_line, false) + local params = client_position_params(opts.winnr, { + context = { includeDeclaration = vim.F.if_nil(opts.include_declaration, true) }, + }) + return list_or_jump("textDocument/references", "LSP References", "builtin.lsp_references", params, opts) +end + +lsp.definitions = function(opts) + local params = client_position_params(opts.winnr) + return list_or_jump("textDocument/definition", "LSP Definitions", "builtin.lsp_definitions", params, opts) +end + +lsp.type_definitions = function(opts) + local params = client_position_params(opts.winnr) + return list_or_jump( + "textDocument/typeDefinition", + "LSP Type Definitions", + "builtin.lsp_type_definitions", + params, + opts + ) +end + +lsp.implementations = function(opts) + local params = client_position_params(opts.winnr) + return list_or_jump("textDocument/implementation", "LSP Implementations", "builtin.lsp_implementations", params, opts) +end + +local symbols_sorter = function(symbols) + if vim.tbl_isempty(symbols) then + return symbols + end + + local current_buf = vim.api.nvim_get_current_buf() + + -- sort adequately for workspace symbols + local filename_to_bufnr = {} + for _, symbol in ipairs(symbols) do + if filename_to_bufnr[symbol.filename] == nil then + filename_to_bufnr[symbol.filename] = vim.uri_to_bufnr(vim.uri_from_fname(symbol.filename)) + end + symbol.bufnr = filename_to_bufnr[symbol.filename] + end + + table.sort(symbols, function(a, b) + if a.bufnr == b.bufnr then + return a.lnum < b.lnum + end + if a.bufnr == current_buf then + return true + end + if b.bufnr == current_buf then + return false + end + return a.bufnr < b.bufnr + end) + + return symbols +end + +lsp.document_symbols = function(opts) + local params = client_position_params(opts.winnr) + vim.lsp.buf_request(opts.bufnr, "textDocument/documentSymbol", params, function(err, result, ctx, _) + if err then + vim.api.nvim_err_writeln("Error when finding document symbols: " .. err.message) + return + end + + if not result or vim.tbl_isempty(result) then + utils.notify("builtin.lsp_document_symbols", { + msg = "No results from textDocument/documentSymbol", + level = "INFO", + }) + return + end + + local locations + if vim.fn.has "nvim-0.11" == 1 then + local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) + locations = vim.lsp.util.symbols_to_items(result or {}, opts.bufnr, client.offset_encoding) or {} + else + locations = vim.lsp.util.symbols_to_items(result or {}, opts.bufnr) or {} + end + + locations = utils.filter_symbols(locations, opts, symbols_sorter) + if vim.tbl_isempty(locations) then + -- error message already printed in `utils.filter_symbols` + return + end + + if vim.tbl_isempty(locations) then + utils.notify("builtin.lsp_document_symbols", { + msg = "No document_symbol locations found", + level = "INFO", + }) + return + end + + opts.path_display = { "hidden" } + pickers + .new(opts, { + prompt_title = "LSP Document Symbols", + finder = finders.new_table { + results = locations, + entry_maker = opts.entry_maker or make_entry.gen_from_lsp_symbols(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.prefilter_sorter { + tag = "symbol_type", + sorter = conf.generic_sorter(opts), + }, + push_cursor_on_edit = true, + push_tagstack_on_edit = true, + }) + :find() + end) +end + +lsp.workspace_symbols = function(opts) + local params = { query = opts.query or "" } + vim.lsp.buf_request(opts.bufnr, "workspace/symbol", params, function(err, server_result, ctx, _) + if err then + vim.api.nvim_err_writeln("Error when finding workspace symbols: " .. err.message) + return + end + + local locations + if vim.fn.has "nvim-0.11" == 1 then + local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) + locations = vim.lsp.util.symbols_to_items(server_result or {}, opts.bufnr, client.offset_encoding) or {} + else + locations = vim.lsp.util.symbols_to_items(server_result or {}, opts.bufnr) or {} + end + + locations = utils.filter_symbols(locations, opts, symbols_sorter) + if vim.tbl_isempty(locations) then + -- error message already printed in `utils.filter_symbols` + return + end + + if vim.tbl_isempty(locations) then + utils.notify("builtin.lsp_workspace_symbols", { + msg = "No results from workspace/symbol. Maybe try a different query: " + .. "'Telescope lsp_workspace_symbols query=example'", + level = "INFO", + }) + return + end + + opts.ignore_filename = vim.F.if_nil(opts.ignore_filename, false) + + pickers + .new(opts, { + prompt_title = "LSP Workspace Symbols", + finder = finders.new_table { + results = locations, + entry_maker = opts.entry_maker or make_entry.gen_from_lsp_symbols(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.prefilter_sorter { + tag = "symbol_type", + sorter = conf.generic_sorter(opts), + }, + }) + :find() + end) +end + +local function get_workspace_symbols_requester(bufnr, opts) + local cancel = function() end + + return function(prompt) + local tx, rx = channel.oneshot() + cancel() + cancel = vim.lsp.buf_request_all(bufnr, "workspace/symbol", { query = prompt }, tx) + + local results = rx() ---@type table + local locations = {} ---@type vim.quickfix.entry[] + + for client_id, client_res in pairs(results) do + if client_res.error then + vim.api.nvim_err_writeln("Error when executing workspace/symbol : " .. client_res.error.message) + elseif client_res.result ~= nil then + if vim.fn.has "nvim-0.11" == 1 then + local client = assert(vim.lsp.get_client_by_id(client_id)) + vim.list_extend(locations, vim.lsp.util.symbols_to_items(client_res.result, bufnr, client.offset_encoding)) + else + vim.list_extend(locations, vim.lsp.util.symbols_to_items(client_res.result, bufnr)) + end + end + end + + if not vim.tbl_isempty(locations) then + locations = utils.filter_symbols(locations, opts, symbols_sorter) or {} + end + return locations + end +end + +lsp.dynamic_workspace_symbols = function(opts) + pickers + .new(opts, { + prompt_title = "LSP Dynamic Workspace Symbols", + finder = finders.new_dynamic { + entry_maker = opts.entry_maker or make_entry.gen_from_lsp_symbols(opts), + fn = get_workspace_symbols_requester(opts.bufnr, opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = sorters.highlighter_only(opts), + attach_mappings = function(_, map) + map("i", "", actions.to_fuzzy_refine) + return true + end, + }) + :find() +end + +local function check_capabilities(method, bufnr) + --TODO(clason): remove when dropping support for Nvim 0.9 + local get_clients = vim.fn.has "nvim-0.10" == 1 and vim.lsp.get_clients or vim.lsp.get_active_clients + local clients = get_clients { bufnr = bufnr } + + for _, client in pairs(clients) do + if client.supports_method(method, { bufnr = bufnr }) then + return true + end + end + + if #clients == 0 then + utils.notify("builtin.lsp_*", { + msg = "no client attached", + level = "INFO", + }) + else + utils.notify("builtin.lsp_*", { + msg = "server does not support " .. method, + level = "INFO", + }) + end + return false +end + +local feature_map = { + ["document_symbols"] = "textDocument/documentSymbol", + ["references"] = "textDocument/references", + ["definitions"] = "textDocument/definition", + ["type_definitions"] = "textDocument/typeDefinition", + ["implementations"] = "textDocument/implementation", + ["workspace_symbols"] = "workspace/symbol", + ["incoming_calls"] = "callHierarchy/incomingCalls", + ["outgoing_calls"] = "callHierarchy/outgoingCalls", +} + +local function apply_checks(mod) + for k, v in pairs(mod) do + mod[k] = function(opts) + opts = opts or {} + + local method = feature_map[k] + if method and not check_capabilities(method, opts.bufnr) then + return + end + v(opts) + end + end + + return mod +end + +return apply_checks(lsp) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/builtin/init.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/builtin/init.lua new file mode 100644 index 0000000..6e27c27 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/builtin/init.lua @@ -0,0 +1,596 @@ +---@tag telescope.builtin + +---@config { ['field_heading'] = "Options", ["module"] = "telescope.builtin" } + +---@brief [[ +--- Telescope Builtins is a collection of community maintained pickers to support common workflows. It can be used as +--- reference when writing PRs, Telescope extensions, your own custom pickers, or just as a discovery tool for all of +--- the amazing pickers already shipped with Telescope! +--- +--- Any of these functions can just be called directly by doing: +--- +--- :lua require('telescope.builtin').$NAME_OF_PICKER() +--- +--- To use any of Telescope's default options or any picker-specific options, call your desired picker by passing a lua +--- table to the picker with all of the options you want to use. Here's an example with the live_grep picker: +--- +--- +--- :lua require('telescope.builtin').live_grep({ +--- prompt_title = 'find string in open buffers...', +--- grep_open_files = true +--- }) +--- -- or with dropdown theme +--- :lua require('telescope.builtin').find_files(require('telescope.themes').get_dropdown{ +--- previewer = false +--- }) +--- +---@brief ]] + +local builtin = {} + +-- Ref: https://github.com/tjdevries/lazy.nvim +local function require_on_exported_call(mod) + return setmetatable({}, { + __index = function(_, picker) + return function(...) + return require(mod)[picker](...) + end + end, + }) +end + +-- +-- +-- File-related Pickers +-- +-- + +--- Search for a string and get results live as you type, respects .gitignore +---@param opts table: options to pass to the picker +---@field cwd string: root dir to search from (default: cwd, use utils.buffer_dir() to search relative to open buffer) +---@field grep_open_files boolean: if true, restrict search to open files only, mutually exclusive with `search_dirs` +---@field search_dirs table: directory/directories/files to search, mutually exclusive with `grep_open_files` +---@field glob_pattern string|table: argument to be used with `--glob`, e.g. "*.toml", can use the opposite "!*.toml" +---@field type_filter string: argument to be used with `--type`, e.g. "rust", see `rg --type-list` +---@field additional_args function|table: additional arguments to be passed on. Can be fn(opts) -> tbl +---@field max_results number: define a upper result value +---@field disable_coordinates boolean: don't show the line & row numbers (default: false) +---@field file_encoding string: file encoding for the entry & previewer +builtin.live_grep = require_on_exported_call("telescope.builtin.__files").live_grep + +--- Searches for the string under your cursor or the visual selection in your current working directory +---@param opts table: options to pass to the picker +---@field cwd string: root dir to search from (default: cwd, use utils.buffer_dir() to search relative to open buffer) +---@field search string: the query to search +---@field grep_open_files boolean: if true, restrict search to open files only, mutually exclusive with `search_dirs` +---@field search_dirs table: directory/directories/files to search, mutually exclusive with `grep_open_files` +---@field use_regex boolean: if true, special characters won't be escaped, allows for using regex (default: false) +---@field word_match string: can be set to `-w` to enable exact word matches +---@field additional_args function|table: additional arguments to be passed on. Can be fn(opts) -> tbl +---@field disable_coordinates boolean: don't show the line and row numbers (default: false) +---@field only_sort_text boolean: only sort the text, not the file, line or row (default: false) +---@field file_encoding string: file encoding for the entry & previewer +builtin.grep_string = require_on_exported_call("telescope.builtin.__files").grep_string + +--- Search for files (respecting .gitignore) +---@param opts table: options to pass to the picker +---@field cwd string: root dir to search from (default: cwd, use utils.buffer_dir() to search relative to open buffer) +---@field find_command function|table: cmd to use for the search. Can be a fn(opts) -> tbl (default: autodetect) +---@field file_entry_encoding string: encoding of output of `find_command` +---@field follow boolean: if true, follows symlinks (i.e. uses `-L` flag for the `find` command) (default: false) +---@field hidden boolean: determines whether to show hidden files or not (default: false) +---@field no_ignore boolean: show files ignored by .gitignore, .ignore, etc. (default: false) +---@field no_ignore_parent boolean: show files ignored by .gitignore, .ignore, etc. in parent dirs. (default: false) +---@field search_dirs table: directory/directories/files to search +---@field search_file string: specify a filename to search for +---@field file_encoding string: file encoding for the previewer +builtin.find_files = require_on_exported_call("telescope.builtin.__files").find_files + +--- This is an alias for the `find_files` picker +builtin.fd = builtin.find_files + +--- Lists function names, variables, and other symbols from treesitter queries +--- - Default keymaps: +--- - ``: show autocompletion menu to prefilter your query by kind of ts node you want to see (i.e. `:var:`) +---@field show_line boolean: if true, shows the row:column that the result is found at (default: true) +---@field bufnr number: specify the buffer number where treesitter should run. (default: current buffer) +---@field symbol_width number: defines the width of the symbol section (default: 25) +---@field symbols string|table: filter results by symbol kind(s) +---@field ignore_symbols string|table: list of symbols to ignore +---@field symbol_highlights table: string -> string. Matches symbol with hl_group +---@field file_encoding string: file encoding for the previewer +builtin.treesitter = require_on_exported_call("telescope.builtin.__files").treesitter + +--- Live fuzzy search inside of the currently open buffer +---@param opts table: options to pass to the picker +---@field skip_empty_lines boolean: if true we don't display empty lines (default: false) +---@field results_ts_highlight boolean: highlight result entries with treesitter (default: true) +---@field file_encoding string: file encoding for the previewer +builtin.current_buffer_fuzzy_find = require_on_exported_call("telescope.builtin.__files").current_buffer_fuzzy_find + +--- Lists tags in current directory with tag location file preview (users are required to run ctags -R to generate tags +--- or update when introducing new changes) +---@param opts table: options to pass to the picker +---@field cwd string: root dir to search from (default: cwd, use utils.buffer_dir() to search relative to open buffer) +---@field ctags_file string: specify a particular ctags file to use +---@field show_line boolean: if true, shows the content of the line the tag is found on in the picker (default: true) +---@field show_kind boolean: if true and kind info is available, show the kind of the tag (default: true) +---@field only_sort_tags boolean: if true we will only sort tags (default: false) +---@field fname_width number: defines the width of the filename section (default: 30) +builtin.tags = require_on_exported_call("telescope.builtin.__files").tags + +--- Lists all of the tags for the currently open buffer, with a preview +---@param opts table: options to pass to the picker +---@field cwd string: root dir to search from (default: cwd, use utils.buffer_dir() to search relative to open buffer) +---@field ctags_file string: specify a particular ctags file to use +---@field show_line boolean: if true, shows the content of the line the tag is found on in the picker (default: true) +---@field show_kind boolean: if true and kind info is available, show the kind of the tag (default: true) +---@field only_sort_tags boolean: if true we will only sort tags (default: false) +---@field fname_width number: defines the width of the filename section (default: 30) +builtin.current_buffer_tags = require_on_exported_call("telescope.builtin.__files").current_buffer_tags + +-- +-- +-- Git-related Pickers +-- +-- + +--- Fuzzy search for files tracked by Git. This command lists the output of the `git ls-files` command, +--- respects .gitignore +--- - Default keymaps: +--- - ``: opens the currently selected file +---@param opts table: options to pass to the picker +---@field cwd string: specify the path of the repo +---@field use_file_path boolean: if we should use the current buffer git root (default: false) +---@field use_git_root boolean: if we should use git root as cwd or the cwd (important for submodule) (default: true) +---@field show_untracked boolean: if true, adds `--others` flag to command and shows untracked files (default: false) +---@field recurse_submodules boolean: if true, adds the `--recurse-submodules` flag to command (default: false) +---@field git_command table: command that will be executed. {"git","ls-files","--exclude-standard","--cached"} +---@field file_encoding string: file encoding for the previewer +builtin.git_files = require_on_exported_call("telescope.builtin.__git").files + +--- Lists commits for current directory with diff preview +--- - Default keymaps: +--- - ``: checks out the currently selected commit +--- - `m`: resets current branch to selected commit using mixed mode +--- - `s`: resets current branch to selected commit using soft mode +--- - `h`: resets current branch to selected commit using hard mode +---@param opts table: options to pass to the picker +---@field cwd string: specify the path of the repo +---@field use_file_path boolean: if we should use the current buffer git root (default: false) +---@field use_git_root boolean: if we should use git root as cwd or the cwd (important for submodule) (default: true) +---@field git_command table: command that will be executed. {"git","log","--pretty=oneline","--abbrev-commit","--","."} +builtin.git_commits = require_on_exported_call("telescope.builtin.__git").commits + +--- Lists commits for current buffer with diff preview +--- - Default keymaps or your overridden `select_` keys: +--- - ``: checks out the currently selected commit +--- - ``: opens a diff in a vertical split +--- - ``: opens a diff in a horizontal split +--- - ``: opens a diff in a new tab +---@param opts table: options to pass to the picker +---@field cwd string: specify the path of the repo +---@field use_file_path boolean: if we should use the current buffer git root (default: false) +---@field use_git_root boolean: if we should use git root as cwd or the cwd (important for submodule) (default: true) +---@field current_file string: specify the current file that should be used for bcommits (default: current buffer) +---@field git_command table: command that will be executed. {"git","log","--pretty=oneline","--abbrev-commit"} +builtin.git_bcommits = require_on_exported_call("telescope.builtin.__git").bcommits + +--- Lists commits for a range of lines in the current buffer with diff preview +--- In visual mode, lists commits for the selected lines +--- With operator mode enabled, lists commits inside the text object/motion +--- - Default keymaps or your overridden `select_` keys: +--- - ``: checks out the currently selected commit +--- - ``: opens a diff in a vertical split +--- - ``: opens a diff in a horizontal split +--- - ``: opens a diff in a new tab +---@param opts table: options to pass to the picker +---@field cwd string: specify the path of the repo +---@field use_git_root boolean: if we should use git root as cwd or the cwd (important for submodule) (default: true) +---@field current_file string: specify the current file that should be used for bcommits (default: current buffer) +---@field git_command table: command that will be executed. the last element must be "-L". {"git","log","--pretty=oneline","--abbrev-commit","--no-patch","-L"} +---@field from number: the first line number in the range (default: current line) +---@field to number: the last line number in the range (default: the value of `from`) +---@field operator boolean: select lines in operator-pending mode (default: false) +builtin.git_bcommits_range = require_on_exported_call("telescope.builtin.__git").bcommits_range + +--- List branches for current directory, with output from `git log --oneline` shown in the preview window +--- - Default keymaps: +--- - ``: checks out the currently selected branch +--- - ``: tracks currently selected branch +--- - ``: rebases currently selected branch +--- - ``: creates a new branch, with confirmation prompt before creation +--- - ``: deletes the currently selected branch, with confirmation prompt before deletion +--- - ``: merges the currently selected branch, with confirmation prompt before deletion +---@param opts table: options to pass to the picker +---@field cwd string: specify the path of the repo +---@field use_file_path boolean: if we should use the current buffer git root (default: false) +---@field use_git_root boolean: if we should use git root as cwd or the cwd (important for submodule) (default: true) +---@field show_remote_tracking_branches boolean: show remote tracking branches like origin/main (default: true) +---@field pattern string: specify the pattern to match all refs +builtin.git_branches = require_on_exported_call("telescope.builtin.__git").branches + +--- Lists git status for current directory +--- - Default keymaps: +--- - ``: stages or unstages the currently selected file +--- - ``: opens the currently selected file +---@param opts table: options to pass to the picker +---@field cwd string: specify the path of the repo +---@field use_file_path boolean: if we should use the current buffer git root (default: false) +---@field use_git_root boolean: if we should use git root as cwd or the cwd (important for submodule) (default: true) +---@field git_icons table: string -> string. Matches name with icon (see source code, make_entry.lua git_icon_defaults) +---@field expand_dir boolean: pass flag `-uall` to show files in untracked directories (default: true) +builtin.git_status = require_on_exported_call("telescope.builtin.__git").status + +--- Lists stash items in current repository +--- - Default keymaps: +--- - ``: runs `git apply` for currently selected stash +---@param opts table: options to pass to the picker +---@field cwd string: specify the path of the repo +---@field use_file_path boolean: if we should use the current buffer git root (default: false) +---@field use_git_root boolean: if we should use git root as cwd or the cwd (important for submodule) (default: true) +---@field show_branch boolean: if we should display the branch name for git stash entries (default: true) +builtin.git_stash = require_on_exported_call("telescope.builtin.__git").stash + +-- +-- +-- Internal and Vim-related Pickers +-- +-- + +--- Lists all of the community maintained pickers built into Telescope +---@param opts table: options to pass to the picker +---@field include_extensions boolean: if true will show the pickers of the installed extensions (default: false) +---@field use_default_opts boolean: if the selected picker should use its default options (default: false) +builtin.builtin = require_on_exported_call("telescope.builtin.__internal").builtin + +--- Opens the previous picker in the identical state (incl. multi selections) +--- - Notes: +--- - Requires `cache_picker` in setup or when having invoked pickers, see |telescope.defaults.cache_picker| +---@param opts table: options to pass to the picker +---@field cache_index number: what picker to resume, where 1 denotes most recent (default: 1) +builtin.resume = require_on_exported_call("telescope.builtin.__internal").resume + +--- Opens a picker over previously cached pickers in their preserved states (incl. multi selections) +--- - Default keymaps: +--- - ``: delete the selected cached picker +--- - Notes: +--- - Requires `cache_picker` in setup or when having invoked pickers, see |telescope.defaults.cache_picker| +---@param opts table: options to pass to the picker +builtin.pickers = require_on_exported_call("telescope.builtin.__internal").pickers + +--- Use the telescope... +---@param opts table: options to pass to the picker +---@field show_pluto boolean: we love Pluto (default: false, because its a hidden feature) +---@field show_moon boolean: we love the Moon (default: false, because its a hidden feature) +builtin.planets = require_on_exported_call("telescope.builtin.__internal").planets + +--- Lists symbols inside of `data/telescope-sources/*.json` found in your runtime path +--- or found in `stdpath("data")/telescope/symbols/*.json`. The second path can be customized. +--- We provide a couple of default symbols which can be found in +--- https://github.com/nvim-telescope/telescope-symbols.nvim. This repos README also provides more +--- information about the format in which the symbols have to be. +---@param opts table: options to pass to the picker +---@field symbol_path string: specify the second path. Default: `stdpath("data")/telescope/symbols/*.json` +---@field sources table: specify a table of sources you want to load this time +builtin.symbols = require_on_exported_call("telescope.builtin.__internal").symbols + +--- Lists available plugin/user commands and runs them on `` +---@param opts table: options to pass to the picker +---@field show_buf_command boolean: show buf local command (Default: true) +builtin.commands = require_on_exported_call("telescope.builtin.__internal").commands + +--- Lists items in the quickfix list, jumps to location on `` +---@param opts table: options to pass to the picker +---@field show_line boolean: show results text (default: true) +---@field trim_text boolean: trim results text (default: false) +---@field nr number: specify the quickfix list number +builtin.quickfix = require_on_exported_call("telescope.builtin.__internal").quickfix + +--- Lists all quickfix lists in your history and open them with `builtin.quickfix`. It seems that neovim +--- only keeps the full history for 10 lists +---@param opts table: options to pass to the picker +builtin.quickfixhistory = require_on_exported_call("telescope.builtin.__internal").quickfixhistory + +--- Lists items from the current window's location list, jumps to location on `` +---@param opts table: options to pass to the picker +---@field show_line boolean: show results text (default: true) +---@field trim_text boolean: trim results text (default: false) +builtin.loclist = require_on_exported_call("telescope.builtin.__internal").loclist + +--- Lists previously open files, opens on `` +---@param opts table: options to pass to the picker +---@field cwd string: specify a working directory to filter oldfiles by +---@field only_cwd boolean: show only files in the cwd (default: false) +---@field cwd_only boolean: alias for only_cwd +---@field file_encoding string: file encoding for the previewer +builtin.oldfiles = require_on_exported_call("telescope.builtin.__internal").oldfiles + +--- Lists commands that were executed recently, and reruns them on `` +--- - Default keymaps: +--- - ``: open the command line with the text of the currently selected result populated in it +---@param opts table: options to pass to the picker +---@field filter_fn function: filter fn(cmd:string). true if the history command should be presented. +builtin.command_history = require_on_exported_call("telescope.builtin.__internal").command_history + +--- Lists searches that were executed recently, and reruns them on `` +--- - Default keymaps: +--- - ``: open a search window with the text of the currently selected search result populated in it +---@param opts table: options to pass to the picker +builtin.search_history = require_on_exported_call("telescope.builtin.__internal").search_history + +--- Lists vim options, allows you to edit the current value on `` +---@param opts table: options to pass to the picker +builtin.vim_options = require_on_exported_call("telescope.builtin.__internal").vim_options + +--- Lists available help tags and opens a new window with the relevant help info on `` +---@param opts table: options to pass to the picker +---@field lang string: specify language (default: vim.o.helplang) +---@field fallback boolean: fallback to en if language isn't installed (default: true) +builtin.help_tags = require_on_exported_call("telescope.builtin.__internal").help_tags + +--- Lists manpage entries, opens them in a help window on `` +---@param opts table: options to pass to the picker +---@field sections table: a list of sections to search, use `{ "ALL" }` to search in all sections (default: { "1" }) +---@field man_cmd function: that returns the man command. (Default: `apropos ""` on linux, `apropos " "` on macos) +builtin.man_pages = require_on_exported_call("telescope.builtin.__internal").man_pages + +--- Lists lua modules and reloads them on `` +---@param opts table: options to pass to the picker +---@field column_len number: define the max column len for the module name (default: dynamic, longest module name) +builtin.reloader = require_on_exported_call("telescope.builtin.__internal").reloader + +--- Lists open buffers in current neovim instance, opens selected buffer on `` +--- - Default keymaps: +--- - ``: delete the currently selected buffer +---@param opts table: options to pass to the picker +---@field cwd string: specify a working directory to filter buffers list by +---@field show_all_buffers boolean: if true, show all buffers, including unloaded buffers (default: true) +---@field ignore_current_buffer boolean: if true, don't show the current buffer in the list (default: false) +---@field only_cwd boolean: if true, only show buffers in the current working directory (default: false) +---@field cwd_only boolean: alias for only_cwd +---@field sort_lastused boolean: Sorts current and last buffer to the top and selects the lastused (default: false) +---@field sort_mru boolean: Sorts all buffers after most recent used. Not just the current and last one (default: false) +---@field bufnr_width number: Defines the width of the buffer numbers in front of the filenames (default: dynamic) +---@field file_encoding string: file encoding for the previewer +---@field sort_buffers function: sort fn(bufnr_a, bufnr_b). true if bufnr_a should go first. Runs after sorting by most recent (if specified) +---@field select_current boolean: select current buffer (default: false) +builtin.buffers = require_on_exported_call("telescope.builtin.__internal").buffers + +--- Lists available colorschemes and applies them on `` +---@param opts table: options to pass to the picker +---@field colors table: a list of additional colorschemes to explicitly make available to telescope (default: {}) +---@field enable_preview boolean: if true, will preview the selected color +---@field ignore_builtins boolean: if true, builtin colorschemes are not listed +builtin.colorscheme = require_on_exported_call("telescope.builtin.__internal").colorscheme + +--- Lists vim marks and their value, jumps to the mark on `` +---@param opts table: options to pass to the picker +---@field file_encoding string: file encoding for the previewer +---@field mark_type string: filter marks by type (default: "all", options: "all"|"global"|"local") +builtin.marks = require_on_exported_call("telescope.builtin.__internal").marks + +--- Lists vim registers, pastes the contents of the register on `` +--- - Default keymaps: +--- - ``: edit the contents of the currently selected register +---@param opts table: options to pass to the picker +builtin.registers = require_on_exported_call("telescope.builtin.__internal").registers + +--- Lists normal mode keymappings, runs the selected keymap on `` +---@param opts table: options to pass to the picker +---@field modes table: a list of short-named keymap modes to search (default: { "n", "i", "c", "x" }) +---@field show_plug boolean: if true, the keymaps for which the lhs contains "" are also shown (default: true) +---@field only_buf boolean: if true, only show the buffer-local keymaps (default: false) +---@field lhs_filter function: filter(lhs:string) -> boolean. true for keymap.lhs if the keymap should be shown (optional) +---@field filter function: filter(km:keymap) -> boolean. true for the keymap if it should be shown (optional) +builtin.keymaps = require_on_exported_call("telescope.builtin.__internal").keymaps + +--- Lists all available filetypes, sets currently open buffer's filetype to selected filetype in Telescope on `` +---@param opts table: options to pass to the picker +builtin.filetypes = require_on_exported_call("telescope.builtin.__internal").filetypes + +--- Lists all available highlights +---@param opts table: options to pass to the picker +builtin.highlights = require_on_exported_call("telescope.builtin.__internal").highlights + +--- Lists vim autocommands and goes to their declaration on `` +---@param opts table: options to pass to the picker +builtin.autocommands = require_on_exported_call("telescope.builtin.__internal").autocommands + +--- Lists spelling suggestions for the current word under the cursor, replaces word with selected suggestion on `` +---@param opts table: options to pass to the picker +builtin.spell_suggest = require_on_exported_call("telescope.builtin.__internal").spell_suggest + +--- Lists the tag stack for the current window, jumps to tag on `` +---@param opts table: options to pass to the picker +---@field show_line boolean: show results text (default: true) +---@field trim_text boolean: trim results text (default: false) +builtin.tagstack = require_on_exported_call("telescope.builtin.__internal").tagstack + +--- Lists items from Vim's jumplist, jumps to location on `` +---@param opts table: options to pass to the picker +---@field show_line boolean: show results text (default: true) +---@field trim_text boolean: trim results text (default: false) +builtin.jumplist = require_on_exported_call("telescope.builtin.__internal").jumplist + +-- +-- +-- LSP-related Pickers +-- +-- + +--- Lists LSP references for word under the cursor, jumps to reference on `` +---@param opts table: options to pass to the picker +---@field include_declaration boolean: include symbol declaration in the lsp references (default: true) +---@field include_current_line boolean: include current line (default: false) +---@field jump_type string: how to goto reference if there is only one and the definition file is different from the current file, values: "tab", "tab drop", "split", "vsplit", "never" +---@field show_line boolean: show results text (default: true) +---@field trim_text boolean: trim results text (default: false) +---@field reuse_win boolean: jump to existing window if buffer is already opened (default: false) +---@field file_encoding string: file encoding for the previewer +builtin.lsp_references = require_on_exported_call("telescope.builtin.__lsp").references + +--- Lists LSP incoming calls for word under the cursor, jumps to reference on `` +---@param opts table: options to pass to the picker +---@field show_line boolean: show results text (default: true) +---@field trim_text boolean: trim results text (default: false) +---@field file_encoding string: file encoding for the previewer +builtin.lsp_incoming_calls = require_on_exported_call("telescope.builtin.__lsp").incoming_calls + +--- Lists LSP outgoing calls for word under the cursor, jumps to reference on `` +---@param opts table: options to pass to the picker +---@field show_line boolean: show results text (default: true) +---@field trim_text boolean: trim results text (default: false) +---@field file_encoding string: file encoding for the previewer +builtin.lsp_outgoing_calls = require_on_exported_call("telescope.builtin.__lsp").outgoing_calls + +--- Goto the definition of the word under the cursor, if there's only one, otherwise show all options in Telescope +---@param opts table: options to pass to the picker +---@field jump_type string: how to goto definition if there is only one and the definition file is different from the current file, values: "tab", "tab drop", "split", "vsplit", "never" +---@field show_line boolean: show results text (default: true) +---@field trim_text boolean: trim results text (default: false) +---@field reuse_win boolean: jump to existing window if buffer is already opened (default: false) +---@field file_encoding string: file encoding for the previewer +builtin.lsp_definitions = require_on_exported_call("telescope.builtin.__lsp").definitions + +--- Goto the definition of the type of the word under the cursor, if there's only one, +--- otherwise show all options in Telescope +---@param opts table: options to pass to the picker +---@field jump_type string: how to goto definition if there is only one and the definition file is different from the current file, values: "tab", "tab drop", "split", "vsplit", "never" +---@field show_line boolean: show results text (default: true) +---@field trim_text boolean: trim results text (default: false) +---@field reuse_win boolean: jump to existing window if buffer is already opened (default: false) +---@field file_encoding string: file encoding for the previewer +builtin.lsp_type_definitions = require_on_exported_call("telescope.builtin.__lsp").type_definitions + +--- Goto the implementation of the word under the cursor if there's only one, otherwise show all options in Telescope +---@param opts table: options to pass to the picker +---@field jump_type string: how to goto implementation if there is only one and the definition file is different from the current file, values: "tab", "tab drop", "split", "vsplit", "never" +---@field show_line boolean: show results text (default: true) +---@field trim_text boolean: trim results text (default: false) +---@field reuse_win boolean: jump to existing window if buffer is already opened (default: false) +---@field file_encoding string: file encoding for the previewer +builtin.lsp_implementations = require_on_exported_call("telescope.builtin.__lsp").implementations + +--- Lists LSP document symbols in the current buffer +--- - Default keymaps: +--- - ``: show autocompletion menu to prefilter your query by type of symbol you want to see (i.e. `:variable:`) +---@param opts table: options to pass to the picker +---@field fname_width number: defines the width of the filename section (default: 30) +---@field symbol_width number: defines the width of the symbol section (default: 25) +---@field symbol_type_width number: defines the width of the symbol type section (default: 8) +---@field show_line boolean: if true, shows the content of the line the tag is found on (default: false) +---@field symbols string|table: filter results by symbol kind(s) +---@field ignore_symbols string|table: list of symbols to ignore +---@field symbol_highlights table: string -> string. Matches symbol with hl_group +---@field file_encoding string: file encoding for the previewer +builtin.lsp_document_symbols = require_on_exported_call("telescope.builtin.__lsp").document_symbols + +--- Lists LSP document symbols in the current workspace +--- - Default keymaps: +--- - ``: show autocompletion menu to prefilter your query by type of symbol you want to see (i.e. `:variable:`) +---@param opts table: options to pass to the picker +---@field query string: for what to query the workspace (default: "") +---@field fname_width number: defines the width of the filename section (default: 30) +---@field symbol_width number: defines the width of the symbol section (default: 25) +---@field symbol_type_width number: defines the width of the symbol type section (default: 8) +---@field show_line boolean: if true, shows the content of the line the tag is found on (default: false) +---@field symbols string|table: filter results by symbol kind(s) +---@field ignore_symbols string|table: list of symbols to ignore +---@field symbol_highlights table: string -> string. Matches symbol with hl_group +---@field file_encoding string: file encoding for the previewer +builtin.lsp_workspace_symbols = require_on_exported_call("telescope.builtin.__lsp").workspace_symbols + +--- Dynamically lists LSP for all workspace symbols +--- - Default keymaps: +--- - ``: show autocompletion menu to prefilter your query by type of symbol you want to see (i.e. `:variable:`), only works after refining to fuzzy search using +---@param opts table: options to pass to the picker +---@field fname_width number: defines the width of the filename section (default: 30) +---@field show_line boolean: if true, shows the content of the line the symbol is found on (default: false) +---@field symbols string|table: filter results by symbol kind(s) +---@field ignore_symbols string|table: list of symbols to ignore +---@field symbol_highlights table: string -> string. Matches symbol with hl_group +---@field file_encoding string: file encoding for the previewer +builtin.lsp_dynamic_workspace_symbols = require_on_exported_call("telescope.builtin.__lsp").dynamic_workspace_symbols + +-- +-- +-- Diagnostics Pickers +-- +-- + +--- Lists diagnostics +--- - Fields: +--- - `All severity flags can be passed as `string` or `number` as per `:vim.diagnostic.severity:` +--- - Default keymaps: +--- - ``: show autocompletion menu to prefilter your query with the diagnostic you want to see (i.e. `:warning:`) +--- - sort_by option: +--- - "buffer": order by bufnr (prioritizing current bufnr), severity, lnum +--- - "severity": order by severity, bufnr (prioritizing current bufnr), lnum +---@param opts table: options to pass to the picker +---@field bufnr number|nil: Buffer number to get diagnostics from. Use 0 for current buffer or nil for all buffers +---@field severity string|number: filter diagnostics by severity name (string) or id (number) +---@field severity_limit string|number: keep diagnostics equal or more severe wrt severity name (string) or id (number) +---@field severity_bound string|number: keep diagnostics equal or less severe wrt severity name (string) or id (number) +---@field root_dir string|boolean: if set to string, get diagnostics only for buffers under this dir otherwise cwd +---@field no_unlisted boolean: if true, get diagnostics only for listed buffers +---@field no_sign boolean: hide DiagnosticSigns from Results (default: false) +---@field line_width string|number: set length of diagnostic entry text in Results. Use 'full' for full untruncated text +---@field namespace number: limit your diagnostics to a specific namespace +---@field disable_coordinates boolean: don't show the line & row numbers (default: false) +---@field sort_by string: sort order of the diagnostics results; see above notes (default: "buffer") +builtin.diagnostics = require_on_exported_call("telescope.builtin.__diagnostics").get + +local apply_config = function(mod) + for k, v in pairs(mod) do + mod[k] = function(opts) + local pickers_conf = require("telescope.config").pickers + + opts = opts or {} + opts.bufnr = opts.bufnr or vim.api.nvim_get_current_buf() + opts.winnr = opts.winnr or vim.api.nvim_get_current_win() + local pconf = pickers_conf[k] or {} + local defaults = (function() + if pconf.theme then + return require("telescope.themes")["get_" .. pconf.theme](pconf) + end + return vim.deepcopy(pconf) + end)() + + if pconf.mappings then + defaults.attach_mappings = function(_, map) + for mode, tbl in pairs(pconf.mappings) do + for key, action in pairs(tbl) do + map(mode, key, action) + end + end + return true + end + end + + if pconf.attach_mappings and opts.attach_mappings then + local opts_attach = opts.attach_mappings + opts.attach_mappings = function(prompt_bufnr, map) + pconf.attach_mappings(prompt_bufnr, map) + return opts_attach(prompt_bufnr, map) + end + end + + if defaults.attach_mappings and opts.attach_mappings then + local opts_attach = opts.attach_mappings + opts.attach_mappings = function(prompt_bufnr, map) + defaults.attach_mappings(prompt_bufnr, map) + return opts_attach(prompt_bufnr, map) + end + end + + v(vim.tbl_extend("force", defaults, opts)) + end + end + + return mod +end + +-- We can't do this in one statement because tree-sitter-lua docgen gets confused if we do +builtin = apply_config(builtin) +return builtin diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/command.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/command.lua new file mode 100644 index 0000000..0da84ba --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/command.lua @@ -0,0 +1,262 @@ +---@tag telescope.command +---@config { ["module"] = "telescope.command" } + +---@brief [[ +--- +--- Telescope commands can be called through two apis, +--- the lua api and the viml api. +--- +--- The lua api is the more direct way to interact with Telescope, as you directly call the +--- lua functions that Telescope defines. +--- It can be called in a lua file using commands like: +---
+--- `require("telescope.builtin").find_files({hidden=true, layout_config={prompt_position="top"}})`
+--- 
+--- If you want to use this api from a vim file you should prepend `lua` to the command, as below: +---
+--- `lua require("telescope.builtin").find_files({hidden=true, layout_config={prompt_position="top"}})`
+--- 
+--- If you want to use this api from a neovim command line you should prepend `:lua` to +--- the command, as below: +---
+--- `:lua require("telescope.builtin").find_files({hidden=true, layout_config={prompt_position="top"}})`
+--- 
+--- +--- The viml api is more indirect, as first the command must be parsed to the relevant lua +--- equivalent, which brings some limitations. +--- The viml api can be called using commands like: +---
+--- `:Telescope find_files hidden=true layout_config={"prompt_position":"top"}`
+--- 
+--- This involves setting options using an `=` and using viml syntax for lists and +--- dictionaries when the corresponding lua function requires a table. +--- +--- One limitation of the viml api is that there can be no spaces in any of the options. +--- For example, if you want to use the `cwd` option for `find_files` to specify that you +--- only want to search within the folder `/foo bar/subfolder/` you could not do that using the +--- viml api, as the path name contains a space. +--- Similarly, you could NOT set the `prompt_position` to `"top"` using the following command: +---
+--- `:Telescope find_files layout_config={ "prompt_position" : "top" }`
+--- 
+--- as there are spaces in the option. +--- +---@brief ]] +local themes = require "telescope.themes" +local builtin = require "telescope.builtin" +local extensions = require("telescope._extensions").manager +local config = require "telescope.config" +local utils = require "telescope.utils" +local command = {} + +local arg_value = { + ["nil"] = nil, + ['""'] = "", + ['"'] = "", +} + +local bool_type = { + ["false"] = false, + ["true"] = true, +} + +local split_keywords = { + ["find_command"] = true, + ["vimgrep_arguments"] = true, + ["sections"] = true, + ["search_dirs"] = true, + ["symbols"] = true, + ["ignore_symbols"] = true, +} + +-- convert command line string arguments to +-- lua number boolean type and nil value +command.convert_user_opts = function(user_opts) + local default_opts = config.values + + local _switch = { + ["boolean"] = function(key, val) + if val == "false" then + user_opts[key] = false + return + end + user_opts[key] = true + end, + ["number"] = function(key, val) + user_opts[key] = tonumber(val) + end, + ["string"] = function(key, val) + if arg_value[val] ~= nil then + user_opts[key] = arg_value[val] + return + end + + if bool_type[val] ~= nil then + user_opts[key] = bool_type[val] + end + end, + ["table"] = function(key, val) + local ok, eval = pcall(vim.fn.eval, val) + if ok then + user_opts[key] = eval + else + local err + eval, err = loadstring("return " .. val) + if err ~= nil then + -- discard invalid lua expression + user_opts[key] = nil + elseif eval ~= nil then + ok, eval = pcall(eval) + if ok and type(eval) == "table" then + -- allow if return a table only + user_opts[key] = eval + else + -- otherwise return nil (allows split check later) + user_opts[key] = nil + end + end + end + end, + } + + local _switch_metatable = { + __index = function(_, k) + utils.notify("command", { + msg = string.format("Type of '%s' does not match", k), + level = "WARN", + }) + end, + } + + setmetatable(_switch, _switch_metatable) + + for key, val in pairs(user_opts) do + if split_keywords[key] then + _switch["table"](key, val) + if user_opts[key] == nil then + user_opts[key] = vim.split(val, ",") + end + elseif default_opts[key] ~= nil then + _switch[type(default_opts[key])](key, val) + elseif tonumber(val) ~= nil then + _switch["number"](key, val) + else + _switch["string"](key, val) + end + end +end + +-- receive the viml command args +-- it should be a table value like +-- { +-- cmd = 'find_files', +-- theme = 'dropdown', +-- extension_type = 'command' +-- opts = { +-- cwd = '***', +-- } +local function run_command(args) + local user_opts = args or {} + if next(user_opts) == nil and not user_opts.cmd then + utils.notify("command", { + msg = "Command missing arguments", + level = "ERROR", + }) + return + end + + local cmd = user_opts.cmd + local opts = user_opts.opts or {} + local extension_type = user_opts.extension_type or "" + local theme = user_opts.theme or "" + + if next(opts) ~= nil then + command.convert_user_opts(opts) + end + + if string.len(theme) > 0 then + local func = themes[theme] or themes["get_" .. theme] + opts = func(opts) + end + + if string.len(extension_type) > 0 and extension_type ~= '"' then + extensions[cmd][extension_type](opts) + return + end + + if builtin[cmd] then + builtin[cmd](opts) + return + end + + if rawget(extensions, cmd) then + extensions[cmd][cmd](opts) + return + end + + local ok = pcall(require("telescope").load_extension, cmd) + if ok then + extensions[cmd][cmd](opts) + return + end + + utils.notify("run_command", { + msg = "Unknown command", + level = "ERROR", + }) +end + +-- @Summary get extensions sub command +-- register extensions dap gh etc. +-- input in command line `Telescope gh ` +-- Returns a list for each extension. +function command.get_extensions_subcommand() + local exts = require("telescope._extensions").manager + local complete_ext_table = {} + for cmd, value in pairs(exts) do + if type(value) == "table" then + local subcmds = {} + for key, _ in pairs(value) do + table.insert(subcmds, key) + end + complete_ext_table[cmd] = subcmds + end + end + return complete_ext_table +end + +function command.register_keyword(keyword) + split_keywords[keyword] = true +end + +function command.load_command(cmd, ...) + local args = { ... } + if cmd == nil then + run_command { cmd = "builtin" } + return + end + + local user_opts = { + cmd = cmd, + opts = {}, + } + + for _, arg in ipairs(args) do + if arg:find("=", 1) == nil then + user_opts["extension_type"] = arg + else + local param = vim.split(arg, "=") + local key = table.remove(param, 1) + param = table.concat(param, "=") + if key == "theme" then + user_opts["theme"] = param + else + user_opts.opts[key] = param + end + end + end + + run_command(user_opts) +end + +return command diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/config.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/config.lua new file mode 100644 index 0000000..7161416 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/config.lua @@ -0,0 +1,991 @@ +local strings = require "plenary.strings" +local deprecated = require "telescope.deprecated" +local sorters = require "telescope.sorters" +local os_sep = require("plenary.path").path.sep +local has_win = vim.fn.has "win32" == 1 + +-- Keep the values around between reloads +_TelescopeConfigurationValues = _TelescopeConfigurationValues or {} +_TelescopeConfigurationPickers = _TelescopeConfigurationPickers or {} + +local function first_non_null(...) + local n = select("#", ...) + for i = 1, n do + local value = select(i, ...) + + if value ~= nil then + return value + end + end +end + +-- A function that creates an amended copy of the `base` table, +-- by replacing keys at "level 2" that match keys in "level 1" in `priority`, +-- and then performs a deep_extend. +-- May give unexpected results if used with tables of "depth" +-- greater than 2. +local smarter_depth_2_extend = function(priority, base) + local result = {} + for key, val in pairs(base) do + if type(val) ~= "table" then + result[key] = first_non_null(priority[key], val) + else + result[key] = {} + for k, v in pairs(val) do + result[key][k] = first_non_null(priority[k], v) + end + end + end + for key, val in pairs(priority) do + if type(val) ~= "table" then + result[key] = first_non_null(val, result[key]) + else + result[key] = vim.tbl_extend("keep", val, result[key] or {}) + end + end + return result +end + +local resolve_table_opts = function(priority, base) + if priority == false or (priority == nil and base == false) then + return false + end + if priority == nil and type(base) == "table" then + return base + end + return smarter_depth_2_extend(priority, base) +end + +-- TODO: Add other major configuration points here. +-- selection_strategy + +local config = {} +config.smarter_depth_2_extend = smarter_depth_2_extend +config.resolve_table_opts = resolve_table_opts + +config.values = _TelescopeConfigurationValues +config.descriptions = {} +config.pickers = _TelescopeConfigurationPickers + +function config.set_pickers(pickers) + pickers = vim.F.if_nil(pickers, {}) + + for k, v in pairs(pickers) do + config.pickers[k] = v + end +end + +local layout_config_defaults = { + + horizontal = { + width = 0.8, + height = 0.9, + prompt_position = "bottom", + preview_cutoff = 120, + }, + + vertical = { + width = 0.8, + height = 0.9, + prompt_position = "bottom", + preview_cutoff = 40, + }, + + center = { + width = 0.5, + height = 0.4, + preview_cutoff = 40, + prompt_position = "top", + }, + + cursor = { + width = 0.8, + height = 0.9, + preview_cutoff = 40, + }, + + bottom_pane = { + height = 25, + prompt_position = "top", + preview_cutoff = 120, + }, +} + +local layout_config_description = string.format( + [[ + Determines the default configuration values for layout strategies. + See |telescope.layout| for details of the configurations options for + each strategy. + + Allows setting defaults for all strategies as top level options and + for overriding for specific options. + For example, the default values below set the default width to 80%% of + the screen width for all strategies except 'center', which has width + of 50%% of the screen width. + + Default: %s +]], + vim.inspect(layout_config_defaults, { newline = "\n ", indent = " " }) +) + +-- A table of all the usual defaults for telescope. +-- Keys will be the name of the default, +-- values will be a list where: +-- - first entry is the value +-- - second entry is the description of the option + +local telescope_defaults = {} +config.descriptions_order = {} +local append = function(name, val, doc) + telescope_defaults[name] = { val, doc } + table.insert(config.descriptions_order, name) +end + +append( + "sorting_strategy", + "descending", + [[ + Determines the direction "better" results are sorted towards. + + Available options are: + - "descending" (default) + - "ascending"]] +) + +append( + "selection_strategy", + "reset", + [[ + Determines how the cursor acts after each sort iteration. + + Available options are: + - "reset" (default) + - "follow" + - "row" + - "closest" + - "none"]] +) + +append( + "scroll_strategy", + "cycle", + [[ + Determines what happens if you try to scroll past the view of the + picker. + + Available options are: + - "cycle" (default) + - "limit"]] +) + +append( + "layout_strategy", + "horizontal", + [[ + Determines the default layout of Telescope pickers. + See |telescope.layout| for details of the available strategies. + + Default: 'horizontal']] +) + +append( + "create_layout", + nil, + [[ + Configure the layout of Telescope pickers. + See |telescope.pickers.layout| for details. + + Default: 'nil']] +) + +append("layout_config", layout_config_defaults, layout_config_description) + +append( + "cycle_layout_list", + { "horizontal", "vertical" }, + [[ + Determines the layouts to cycle through when using `actions.layout.cycle_layout_next` + and `actions.layout.cycle_layout_prev`. + Should be a list of "layout setups". + Each "layout setup" can take one of two forms: + 1. string + This is interpreted as the name of a `layout_strategy` + 2. table + A table with possible keys `layout_strategy`, `layout_config` and `previewer` + + Default: { "horizontal", "vertical" } + ]] +) + +append( + "winblend", + function() + return vim.o.winblend + end, + [[ + Configure winblend for telescope floating windows. See |winblend| for + more information. Type can be a number or a function returning a + number + + Default: function() return vim.o.winblend end]] +) + +append( + "wrap_results", + false, + [[ + Word wrap the search results + + Default: false]] +) + +append( + "prompt_prefix", + "> ", + [[ + The character(s) that will be shown in front of Telescope's prompt. + + Default: '> ']] +) + +append( + "selection_caret", + "> ", + [[ + The character(s) that will be shown in front of the current selection. + + Default: '> ']] +) + +append( + "entry_prefix", + " ", + [[ + Prefix in front of each result entry. Current selection not included. + + Default: ' ']] +) + +append( + "multi_icon", + "+", + [[ + Symbol to add in front of a multi-selected result entry. + Replaces final character of |telescope.defaults.selection_caret| and + |telescope.defaults.entry_prefix| as appropriate. + To have no icon, set to the empty string. + + Default: '+']] +) + +append( + "initial_mode", + "insert", + [[ + Determines in which mode telescope starts. Valid Keys: + `insert` and `normal`. + + Default: "insert"]] +) + +append( + "border", + true, + [[ + Boolean defining if borders are added to Telescope windows. + + Default: true]] +) + +append( + "path_display", + {}, + [[ + Determines how file paths are displayed. + + path_display can be set to an array with a combination of: + - "hidden" hide file names + - "tail" only display the file name, and not the path + - "absolute" display absolute paths + - "smart" remove as much from the path as possible to only show + the difference between the displayed paths. + Warning: The nature of the algorithm might have a negative + performance impact! + - "shorten" only display the first character of each directory in + the path + - "truncate" truncates the start of the path when the whole path will + not fit. To increase the gap between the path and the edge, + set truncate to number `truncate = 3` + - "filename_first" shows filenames first and then the directories + + You can also specify the number of characters of each directory name + to keep by setting `path_display.shorten = num`. + e.g. for a path like + `alpha/beta/gamma/delta.txt` + setting `path_display.shorten = 1` will give a path like: + `a/b/g/delta.txt` + Similarly, `path_display.shorten = 2` will give a path like: + `al/be/ga/delta.txt` + + You can also further customise the shortening behaviour by + setting `path_display.shorten = { len = num, exclude = list }`, + where `len` acts as above, and `exclude` is a list of positions + that are not shortened. Negative numbers in the list are considered + relative to the end of the path. + e.g. for a path like + `alpha/beta/gamma/delta.txt` + setting `path_display.shorten = { len = 1, exclude = {1, -1} }` + will give a path like: + `alpha/b/g/delta.txt` + setting `path_display.shorten = { len = 2, exclude = {2, -2} }` + will give a path like: + `al/beta/gamma/de` + + path_display can also be set to 'filename_first' to put the filename + in front. + + path_display = { + "filename_first" + }, + + The directory structure can be reversed as follows: + + path_display = { + filename_first = { + reverse_directories = true + } + }, + + path_display can also be set to 'hidden' string to hide file names + + path_display can also be set to a function for custom formatting of + the path display with the following signature + + Signature: fun(opts: table, path: string): string, table? + + The optional table is an list of positions and highlight groups to + set the highlighting of the return path string. + + Example: + + -- Format path as "file.txt (path\to\file\)" + path_display = function(opts, path) + local tail = require("telescope.utils").path_tail(path) + return string.format("%s (%s)", tail, path) + end, + + -- Format path and add custom highlighting + path_display = function(opts, path) + local tail = require("telescope.utils").path_tail(path) + path = string.format("%s (%s)", tail, path) + + local highlights = { + { + { + 0, -- highlight start position + #path, -- highlight end position + }, + "Comment", -- highlight group name + }, + } + + return path, highlights + end + + Default: {}]] +) + +append( + "borderchars", + { "─", "│", "─", "│", "╭", "╮", "╯", "╰" }, + [[ + Set the borderchars of telescope floating windows. It has to be a + table of 8 string values. + + Default: { "─", "│", "─", "│", "╭", "╮", "╯", "╰" }]] +) + +append( + "get_status_text", + function(self, opts) + local multi_select_cnt = #(self:get_multi_selection()) + local showing_cnt = (self.stats.processed or 0) - (self.stats.filtered or 0) + local total_cnt = self.stats.processed or 0 + + local status_icon = "" + local status_text + if opts and not opts.completed then + status_icon = "*" + end + + if showing_cnt == 0 and total_cnt == 0 then + status_text = status_icon + elseif multi_select_cnt == 0 then + status_text = string.format("%s %s / %s", status_icon, showing_cnt, total_cnt) + else + status_text = string.format("%s %s / %s / %s", status_icon, multi_select_cnt, showing_cnt, total_cnt) + end + + -- quick workaround for extmark right_align side-scrolling limitation + -- https://github.com/nvim-telescope/telescope.nvim/issues/2929 + local prompt_width = vim.api.nvim_win_get_width(self.prompt_win) + local cursor_col = vim.api.nvim_win_get_cursor(self.prompt_win)[2] + local prefix_display_width = strings.strdisplaywidth(self.prompt_prefix) --[[@as integer]] + local prefix_width = #self.prompt_prefix + local prefix_shift = 0 + if prefix_display_width ~= prefix_width then + prefix_shift = prefix_display_width + end + + local cursor_occluded = (prompt_width - cursor_col - #status_text + prefix_shift) < 0 + if cursor_occluded then + return "" + else + return status_text + end + end, + [[ + A function that determines what the virtual text looks like. + Signature: function(picker) -> str + + Default: function that shows current count / all]] +) + +append( + "hl_result_eol", + true, + [[ + Changes if the highlight for the selected item in the results + window is always the full width of the window + + Default: true]] +) + +append( + "dynamic_preview_title", + false, + [[ + Will change the title of the preview window dynamically, where it + is supported. For example, the preview window's title could show up as + the full filename. + + Default: false]] +) + +append( + "results_title", + "Results", + [[ + Defines the default title of the results window. A false value + can be used to hide the title altogether. + + Default: "Results"]] +) + +append( + "prompt_title", + "Prompt", + [[ + Defines the default title of the prompt window. A false value + can be used to hide the title altogether. Most of the times builtins + define a prompt_title which will be preferred over this default. + + Default: "Prompt"]] +) + +append( + "mappings", + {}, + [[ + Your mappings to override telescope's default mappings. + + See: ~ + |telescope.mappings| + ]] +) + +append( + "default_mappings", + nil, + [[ + Not recommended to use except for advanced users. + + Will allow you to completely remove all of telescope's default maps + and use your own. + + Default: nil + ]] +) + +append( + "history", + { + path = vim.fn.stdpath "data" .. os_sep .. "telescope_history", + limit = 100, + handler = function(...) + return require("telescope.actions.history").get_simple_history(...) + end, + cycle_wrap = false, + }, + [[ + This field handles the configuration for prompt history. + By default it is a table, with default values (more below). + To disable history, set it to false. + + Currently mappings still need to be added, Example: + mappings = { + i = { + [""] = require('telescope.actions').cycle_history_next, + [""] = require('telescope.actions').cycle_history_prev, + }, + }, + + Fields: + - path: The path to the telescope history as string. + Default: stdpath("data")/telescope_history + - limit: The amount of entries that will be written in the + history. + Warning: If limit is set to nil it will grow unbound. + Default: 100 + - handler: A lua function that implements the history. + This is meant as a developer setting for extensions to + override the history handling, e.g., + https://github.com/nvim-telescope/telescope-smart-history.nvim, + which allows context sensitive (cwd + picker) history. + + Default: + require('telescope.actions.history').get_simple_history + - cycle_wrap: Indicates whether the cycle_history_next and + cycle_history_prev functions should wrap around to the + beginning or end of the history entries on reaching + their respective ends + Default: false]] +) + +append( + "cache_picker", + { + num_pickers = 1, + limit_entries = 1000, + ignore_empty_prompt = false, + }, + [[ + This field handles the configuration for picker caching. + By default it is a table, with default values (more below). + To disable caching, set it to false. + + Caching preserves all previous multi selections and results and + therefore may result in slowdown or increased RAM occupation + if too many pickers (`cache_picker.num_pickers`) or entries + ('cache_picker.limit_entries`) are cached. + + Fields: + - num_pickers: The number of pickers to be cached. + Set to -1 to preserve all pickers of your + session. If passed to a picker, the cached + pickers with indices larger than + `cache_picker.num_pickers` will be cleared. + Default: 1 + - limit_entries: The amount of entries that will be saved for + each picker. + Default: 1000 + - ignore_empty_prompt: If true, the picker will not be cached if + the prompt is empty (i.e., no text has been + typed at the time of closing the prompt). + Default: false + ]] +) + +append( + "preview", + { + check_mime_type = not has_win, + filesize_limit = 25, + highlight_limit = 1, + timeout = 250, + treesitter = true, + msg_bg_fillchar = "╱", + hide_on_startup = false, + }, + [[ + This field handles the global configuration for previewers. + By default it is a table, with default values (more below). + To disable previewing, set it to false. If you have disabled previewers + globally, but want to opt in to previewing for single pickers, you will have to + pass `preview = true` or `preview = {...}` (your config) to the `opts` of + your picker. + + Fields: + - check_mime_type: Use `file` if available to try to infer whether the + file to preview is a binary if filetype + detection fails. + Windows users get `file` from: + https://github.com/julian-r/file-windows + Set to false to attempt to preview any mime type. + Default: true for all OS excl. Windows + - filesize_limit: The maximum file size in MB attempted to be previewed. + Set to false to attempt to preview any file size. + Default: 25 + - highlight_limit: The maximum file size in MB attempted to be highlighted. + Set to false to attempt to highlight any file size. + Default: 1 + - timeout: Timeout the previewer if the preview did not + complete within `timeout` milliseconds. + Set to false to not timeout preview. + Default: 250 + - hook(s): Function(s) that takes `(filepath, bufnr, opts)`, where opts + exposes winid and ft (filetype). + Available hooks (in order of priority): + {filetype, mime, filesize, timeout}_hook + Important: the filetype_hook must return true or false + to indicate whether to continue (true) previewing or not (false), + respectively. + Two examples: + local putils = require("telescope.previewers.utils") + ... -- preview is called in telescope.setup { ... } + preview = { + -- 1) Do not show previewer for certain files + filetype_hook = function(filepath, bufnr, opts) + -- you could analogously check opts.ft for filetypes + local excluded = vim.tbl_filter(function(ending) + return filepath:match(ending) + end, { + ".*%.csv", + ".*%.toml", + }) + if not vim.tbl_isempty(excluded) then + putils.set_preview_message( + bufnr, + opts.winid, + string.format("I don't like %s files!", + excluded[1]:sub(5, -1)) + ) + return false + end + return true + end, + -- 2) Truncate lines to preview window for too large files + filesize_hook = function(filepath, bufnr, opts) + local path = require("plenary.path"):new(filepath) + -- opts exposes winid + local height = vim.api.nvim_win_get_height(opts.winid) + local lines = vim.split(path:head(height), "[\r]?\n") + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) + end, + } + The configuration recipes for relevant examples. + Note: we use vim.filetype filetype detection, + so if you have troubles with files not + highlighting correctly, please read + |vim.filetype| + Default: nil + - treesitter: Determines whether the previewer performs treesitter + highlighting, which falls back to regex-based highlighting. + `true`: treesitter highlighting for all available filetypes + `false`: regex-based highlighting for all filetypes + `table`: may contain the following keys: + - enable boolean|table: if boolean, enable ts + highlighting for all supported + filetypes. + if table, ts highlighting is only + enabled for given filetypes. + - disable table: list of filetypes for which ts highlighting + is not used if `enable = true`. + Default: true + - msg_bg_fillchar: Character to fill background of unpreviewable buffers with + Default: "╱" + - hide_on_startup: Hide previewer when picker starts. Previewer can be toggled + with actions.layout.toggle_preview. + Default: false + - ls_short: Determines whether to use the `--short` flag for the `ls` + command when previewing directories. Otherwise will result + to using `--long`. + Default: false + ]] +) + +append( + "vimgrep_arguments", + { "rg", "--color=never", "--no-heading", "--with-filename", "--line-number", "--column", "--smart-case" }, + [[ + Defines the command that will be used for `live_grep` and `grep_string` + pickers. + Hint: Make sure that color is currently set to `never` because we do + not yet interpret color codes + Hint 2: Make sure that these options are in your changes arguments: + "--no-heading", "--with-filename", "--line-number", "--column" + because we need them so the ripgrep output is in the correct format. + + Default: { + "rg", + "--color=never", + "--no-heading", + "--with-filename", + "--line-number", + "--column", + "--smart-case" + }]] +) + +append( + "use_less", + true, + [[ + Boolean if less should be enabled in term_previewer (deprecated and + currently no longer used in the builtin pickers). + + Default: true]] +) + +append( + "set_env", + nil, + [[ + Set an environment for term_previewer. A table of key values: + Example: { COLORTERM = "truecolor", ... } + Hint: Empty table is not allowed. + + Default: nil]] +) + +append( + "color_devicons", + true, + [[ + Boolean if devicons should be enabled or not. If set to false, the + text highlight group is used. + Hint: Coloring only works if |termguicolors| is enabled. + + Default: true]] +) + +append( + "file_sorter", + sorters.get_fzy_sorter, + [[ + A function pointer that specifies the file_sorter. This sorter will + be used for find_files, git_files and similar. + Hint: If you load a native sorter, you don't need to change this value, + the native sorter will override it anyway. + + Default: require("telescope.sorters").get_fzy_sorter]] +) + +append( + "generic_sorter", + sorters.get_fzy_sorter, + [[ + A function pointer to the generic sorter. The sorter that should be + used for everything that is not a file. + Hint: If you load a native sorter, you don't need to change this value, + the native sorter will override it anyway. + + Default: require("telescope.sorters").get_fzy_sorter]] +) + +--TODO(conni2461): Why is this even configurable??? +append( + "prefilter_sorter", + sorters.prefilter, + [[ + This points to a wrapper sorter around the generic_sorter that is able + to do prefiltering. + It's usually used for lsp_*_symbols and lsp_*_diagnostics + + Default: require("telescope.sorters").prefilter]] +) + +append( + "tiebreak", + function(current_entry, existing_entry, _) + return #current_entry.ordinal < #existing_entry.ordinal + end, + [[ + A function that determines how to break a tie when two entries have + the same score. + Having a function that always returns false would keep the entries in + the order they are found, so existing_entry before current_entry. + Vice versa always returning true would place the current_entry + before the existing_entry. + + Signature: function(current_entry, existing_entry, prompt) -> boolean + + Default: function that breaks the tie based on the length of the + entry's ordinal]] +) + +append( + "file_ignore_patterns", + nil, + [[ + A table of lua regex that define the files that should be ignored. + Example: { "^scratch/" } -- ignore all files in scratch directory + Example: { "%.npz" } -- ignore all npz files + See: https://www.lua.org/manual/5.1/manual.html#5.4.1 for more + information about lua regex + Note: `file_ignore_patterns` will be used in all pickers that have a + file associated. This might lead to the problem that lsp_ pickers + aren't displaying results because they might be ignored by + `file_ignore_patterns`. For example, setting up node_modules as ignored + will never show node_modules in any results, even if you are + interested in lsp_ results. + + If you only want `file_ignore_patterns` for `find_files` and + `grep_string`/`live_grep` it is suggested that you setup `gitignore` + and have fd and or ripgrep installed because both tools will not show + `gitignore`d files on default. + + Default: nil]] +) + +append( + "get_selection_window", + function() + return 0 + end, + [[ + Function that takes function(picker, entry) and returns a window id. + The window ID will be used to decide what window the chosen file will + be opened in and the cursor placed in upon leaving the picker. + + Default: `function() return 0 end` + ]] +) + +append( + "git_worktrees", + nil, + [[ + A table of arrays of detached working trees with keys `gitdir` and `toplevel`. + Used to pass `--git-dir` and `--work-tree` flags to git commands when telescope fails + to infer the top-level directory of a given working tree based on cwd. + Example: + git_worktrees = { + { + toplevel = vim.env.HOME, + gitdir = vim.env.HOME .. '/.cfg' + } + } + + Default: nil + ]] +) + +append( + "file_previewer", + function(...) + return require("telescope.previewers").vim_buffer_cat.new(...) + end, + [[ + Function pointer to the default file_previewer. It is mostly used + for find_files, git_files and similar. + You can change this function pointer to either use your own + previewer or use the command-line program bat as the previewer: + require("telescope.previewers").cat.new + + Default: require("telescope.previewers").vim_buffer_cat.new]] +) + +append( + "grep_previewer", + function(...) + return require("telescope.previewers").vim_buffer_vimgrep.new(...) + end, + [[ + Function pointer to the default vim_grep previewer. It is mostly + used for live_grep, grep_string and similar. + You can change this function pointer to either use your own + previewer or use the command-line program bat as the previewer: + require("telescope.previewers").vimgrep.new + + Default: require("telescope.previewers").vim_buffer_vimgrep.new]] +) + +append( + "qflist_previewer", + function(...) + return require("telescope.previewers").vim_buffer_qflist.new(...) + end, + [[ + Function pointer to the default qflist previewer. It is mostly + used for qflist, loclist and lsp. + You can change this function pointer to either use your own + previewer or use the command-line program bat as the previewer: + require("telescope.previewers").qflist.new + + Default: require("telescope.previewers").vim_buffer_qflist.new]] +) + +append( + "buffer_previewer_maker", + function(...) + return require("telescope.previewers").buffer_previewer_maker(...) + end, + [[ + Developer option that defines the underlining functionality + of the buffer previewer. + For interesting configuration examples take a look at + https://github.com/nvim-telescope/telescope.nvim/wiki/Configuration-Recipes + + Default: require("telescope.previewers").buffer_previewer_maker]] +) + +-- @param user_defaults table: a table where keys are the names of options, +-- and values are the ones the user wants +-- @param tele_defaults table: (optional) a table containing all of the defaults +-- for telescope [defaults to `telescope_defaults`] +function config.set_defaults(user_defaults, tele_defaults) + user_defaults = vim.F.if_nil(user_defaults, {}) + tele_defaults = vim.F.if_nil(tele_defaults, telescope_defaults) + + -- Check if using layout keywords outside of `layout_config` + deprecated.options(user_defaults) + + local function get(name, default_val) + if name == "layout_config" then + return smarter_depth_2_extend( + vim.F.if_nil(user_defaults[name], {}), + vim.tbl_deep_extend("keep", vim.F.if_nil(config.values[name], {}), vim.F.if_nil(default_val, {})) + ) + end + if name == "history" or name == "cache_picker" or name == "preview" then + if user_defaults[name] == false or config.values[name] == false then + return false + end + if user_defaults[name] == true then + return vim.F.if_nil(config.values[name], {}) + end + + return smarter_depth_2_extend( + vim.F.if_nil(user_defaults[name], {}), + vim.tbl_deep_extend("keep", vim.F.if_nil(config.values[name], {}), vim.F.if_nil(default_val, {})) + ) + end + return first_non_null(user_defaults[name], config.values[name], default_val) + end + + local function set(name, default_val, description) + assert(description, "Config values must always have a description") + + config.values[name] = get(name, default_val) + config.descriptions[name] = strings.dedent(description) + end + + for key, info in pairs(tele_defaults) do + set(key, info[1], info[2]) + end + + local M = {} + M.get = get + return M +end + +function config.clear_defaults() + for k, _ in pairs(config.values) do + config.values[k] = nil + end +end + +config.set_defaults() + +return config diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/config/resolve.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/config/resolve.lua new file mode 100644 index 0000000..40baaf1 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/config/resolve.lua @@ -0,0 +1,323 @@ +---@tag telescope.resolve +---@config { ["module"] = "telescope.resolve" } + +---@brief [[ +--- Provides "resolver functions" to allow more customisable inputs for options. +---@brief ]] + +--[[ + +Ultimately boils down to getting `height` and `width` for: +- prompt +- preview +- results + +No matter what you do, I will not make prompt have more than one line (atm) + +Result of `resolve` should be a table with: + +{ + preview = { + get_width = function(self, max_columns, max_lines) end + get_height = function(self, max_columns, max_lines) end + }, + + result = { + get_width = function(self, max_columns, max_lines) end + get_height = function(self, max_columns, max_lines) end + }, + + prompt = { + get_width = function(self, max_columns, max_lines) end + get_height = function(self, max_columns, max_lines) end + }, + + total ? +} + +!!NOT IMPLEMENTED YET!! + +height = + 1. 0 <= number < 1 + This means total height as a percentage + + 2. 1 <= number + This means total height as a fixed number + + 3. function(picker, columns, lines) + -> returns one of the above options + return math.min(110, max_rows * .5) + + if columns > 120 then + return 110 + else + return 0.6 + end + + 3. { + previewer = x, + results = x, + prompt = x, + }, this means I do my best guess I can for these, given your options + +width = + exactly the same, but switch to width + + +{ + height = 0.5, + width = { + previewer = 0.25, + results = 30, + } +} + +https://github.com/nvim-lua/telescope.nvim/pull/43 + +After we get layout, we should try and make top-down sorting work. +That's the next step to scrolling. + +{ + vertical = { + }, + horizontal = { + }, + + height = ... + width = ... +} + + + +--]] + +local resolver = {} +local _resolve_map = {} + +local throw_invalid_config_option = function(key, value) + error(string.format("Invalid configuration option for '%s': '%s'", key, tostring(value)), 2) +end + +-- Booleans +_resolve_map[function(val) + return val == false +end] = function(_, val) + return function(...) + return val + end +end + +-- Percentages +_resolve_map[function(val) + return type(val) == "number" and val >= 0 and val < 1 +end] = function(selector, val) + return function(...) + local selected = select(selector, ...) + return math.floor(val * selected) + end +end + +-- Numbers +_resolve_map[function(val) + return type(val) == "number" and val >= 1 +end] = function(selector, val) + return function(...) + local selected = select(selector, ...) + return math.min(val, selected) + end +end + +-- function: +-- Function must have same signature as get_window_layout +-- function(self, max_columns, max_lines): number +-- +-- Resulting number is used for this configuration value. +_resolve_map[function(val) + return type(val) == "function" +end] = function(_, val) + return val +end + +_resolve_map[function(val) + return type(val) == "table" and val["max"] ~= nil and val[1] ~= nil and val[1] >= 0 and val[1] < 1 +end] = function( + selector, + val +) + return function(...) + local selected = select(selector, ...) + return math.min(math.floor(val[1] * selected), val["max"]) + end +end + +_resolve_map[function(val) + return type(val) == "table" and val["min"] ~= nil and val[1] ~= nil and val[1] >= 0 and val[1] < 1 +end] = function( + selector, + val +) + return function(...) + local selected = select(selector, ...) + return math.max(math.floor(val[1] * selected), val["min"]) + end +end + +-- Add padding option +_resolve_map[function(val) + return type(val) == "table" and val["padding"] ~= nil +end] = function(selector, val) + local resolve_pad = function(value) + for k, v in pairs(_resolve_map) do + if k(value) then + return v(selector, value) + end + end + throw_invalid_config_option("padding", value) + end + + return function(...) + local selected = select(selector, ...) + local padding = resolve_pad(val["padding"]) + return math.floor(selected - 2 * padding(...)) + end +end + +--- Converts input to a function that returns the height. +--- The input must take one of five forms: +--- 1. 0 <= number < 1
+--- This means total height as a percentage. +--- 2. 1 <= number
+--- This means total height as a fixed number. +--- 3. function
+--- Must have signature: +--- function(self, max_columns, max_lines): number +--- 4. table of the form: { val, max = ..., min = ... }
+--- val has to be in the first form 0 <= val < 1 and only one is given, +--- `min` or `max` as fixed number +--- 5. table of the form: {padding = `foo`}
+--- where `foo` has one of the previous three forms.
+--- The height is then set to be the remaining space after padding. +--- For example, if the window has height 50, and the input is {padding = 5}, +--- the height returned will be `40 = 50 - 2*5` +--- +--- The returned function will have signature: +--- function(self, max_columns, max_lines): number +resolver.resolve_height = function(val) + for k, v in pairs(_resolve_map) do + if k(val) then + return v(3, val) + end + end + throw_invalid_config_option("height", val) +end + +--- Converts input to a function that returns the width. +--- The input must take one of five forms: +--- 1. 0 <= number < 1
+--- This means total width as a percentage. +--- 2. 1 <= number
+--- This means total width as a fixed number. +--- 3. function
+--- Must have signature: +--- function(self, max_columns, max_lines): number +--- 4. table of the form: { val, max = ..., min = ... }
+--- val has to be in the first form 0 <= val < 1 and only one is given, +--- `min` or `max` as fixed number +--- 5. table of the form: {padding = `foo`}
+--- where `foo` has one of the previous three forms.
+--- The width is then set to be the remaining space after padding. +--- For example, if the window has width 100, and the input is {padding = 5}, +--- the width returned will be `90 = 100 - 2*5` +--- +--- The returned function will have signature: +--- function(self, max_columns, max_lines): number +resolver.resolve_width = function(val) + for k, v in pairs(_resolve_map) do + if k(val) then + return v(2, val) + end + end + + throw_invalid_config_option("width", val) +end + +--- Calculates the adjustment required to move the picker from the middle of the screen to +--- an edge or corner.
+--- The `anchor` can be any of the following strings: +--- - "", "CENTER", "NW", "N", "NE", "E", "SE", "S", "SW", "W" +--- The anchors have the following meanings: +--- - "" or "CENTER":
+--- the picker will remain in the middle of the screen. +--- - Compass directions:
+--- the picker will move to the corresponding edge/corner +--- e.g. "NW" -> "top left corner", "E" -> "right edge", "S" -> "bottom edge" +resolver.resolve_anchor_pos = function(anchor, p_width, p_height, max_columns, max_lines, anchor_padding) + anchor = anchor:upper() + local pos = { 0, 0 } + if anchor == "CENTER" then + return pos + end + if anchor:find "W" then + pos[1] = math.ceil((p_width - max_columns) / 2) + anchor_padding + elseif anchor:find "E" then + pos[1] = math.ceil((max_columns - p_width) / 2) - anchor_padding + end + if anchor:find "N" then + pos[2] = math.ceil((p_height - max_lines) / 2) + anchor_padding + elseif anchor:find "S" then + pos[2] = math.ceil((max_lines - p_height) / 2) - anchor_padding + end + return pos +end + +-- duplicate from utils.lua to keep self-contained +-- Win option always returns a table with preview, results, and prompt. +-- It handles many different ways. Some examples are as follows: +-- +-- -- Disable +-- borderchars = false +-- +-- -- All three windows share the same +-- borderchars = { '─', '│', '─', '│', '┌', '┐', '┘', '└'}, +-- +-- -- Each window gets it's own configuration +-- borderchars = { +-- preview = {...}, +-- results = {...}, +-- prompt = {...}, +-- } +-- +-- -- Default to [1] but override with specific items +-- borderchars = { +-- {...} +-- prompt = {...}, +-- } +resolver.win_option = function(val, default) + local islist = require("telescope.utils").islist + if type(val) ~= "table" or islist(val) then + if val == nil then + val = default + end + + return { + preview = val, + results = val, + prompt = val, + } + elseif type(val) == "table" then + assert(not islist(val)) + + local val_to_set = val[1] + if val_to_set == nil then + val_to_set = default + end + + return { + preview = vim.F.if_nil(val.preview, val_to_set), + results = vim.F.if_nil(val.results, val_to_set), + prompt = vim.F.if_nil(val.prompt, val_to_set), + } + end +end + +return resolver diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/debounce.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/debounce.lua new file mode 100644 index 0000000..5afbb2f --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/debounce.lua @@ -0,0 +1,171 @@ +-- Credit: https://gist.github.com/runiq/31aa5c4bf00f8e0843cd267880117201 +-- + +local M = {} + +---Validates args for `throttle()` and `debounce()`. +local function td_validate(fn, ms) + vim.validate { + fn = { fn, "f" }, + ms = { + ms, + function(v) + return type(v) == "number" and v > 0 + end, + "number > 0", + }, + } +end + +--- Throttles a function on the leading edge. Automatically `schedule_wrap()`s. +---@param fn fun(...) Function to throttle +---@param ms number Timeout in ms +---@return fun(...) wrapped_fn Throttled function +---@return uv_timer_t timer Remember to call `timer.close()` at the end or you will leak memory! +function M.throttle_leading(fn, ms) + td_validate(fn, ms) + local timer = vim.loop.new_timer() + local running = false + + local function wrapped_fn(...) + if not running then + timer:start(ms, 0, function() + running = false + end) + running = true + pcall(vim.schedule_wrap(fn), select(1, ...)) + end + end + return wrapped_fn, timer +end + +--- Throttles a function on the trailing edge. Automatically `schedule_wrap()`s. +---@param fn fun(...) Function to throttle +---@param ms number Timeout in ms +---@param last? boolean Whether to use the arguments of the last call to `fn` within the timeframe. +--- Default: Use arguments of the first call. +---@return fun(...) wrapped_fn Throttled function +---@return uv_timer_t timer Remember to call `timer.close()` at the end or you will leak memory! +function M.throttle_trailing(fn, ms, last) + td_validate(fn, ms) + local timer = vim.loop.new_timer() + local running = false + + local wrapped_fn + if not last then + function wrapped_fn(...) + if not running then + local argv = { ... } + local argc = select("#", ...) + + timer:start(ms, 0, function() + running = false + pcall(vim.schedule_wrap(fn), unpack(argv, 1, argc)) + end) + running = true + end + end + else + local argv, argc + function wrapped_fn(...) + argv = { ... } + argc = select("#", ...) + + if not running then + timer:start(ms, 0, function() + running = false + pcall(vim.schedule_wrap(fn), unpack(argv, 1, argc)) + end) + running = true + end + end + end + return wrapped_fn, timer +end + +--- Debounces a function on the leading edge. Automatically `schedule_wrap()`s. +---@param fn fun(...) Function to debounce +---@param ms number Timeout in ms +---@return fun(...) wrapped_fn Debounced function +---@return uv_timer_t timer Remember to call `timer.close()` at the end or you will leak memory! +function M.debounce_leading(fn, ms) + td_validate(fn, ms) + local timer = vim.loop.new_timer() + local running = false + + local function wrapped_fn(...) + timer:start(ms, 0, function() + running = false + end) + + if not running then + running = true + pcall(vim.schedule_wrap(fn), select(1, ...)) + end + end + return wrapped_fn, timer +end + +--- Debounces a function on the trailing edge. Automatically `schedule_wrap()`s. +---@param fn fun(...) Function to debounce +---@param ms number Timeout in ms +---@param first? boolean Whether to use the arguments of the first call to `fn` within the timeframe. +--- Default: Use arguments of the last call. +---@return fun(...) wrapped_fn Debounced function +---@return uv_timer_t timer Remember to call `timer.close()` at the end or you will leak memory! +function M.debounce_trailing(fn, ms, first) + td_validate(fn, ms) + local timer = vim.loop.new_timer() + local wrapped_fn + + if not first then + function wrapped_fn(...) + local argv = { ... } + local argc = select("#", ...) + + timer:start(ms, 0, function() + pcall(vim.schedule_wrap(fn), unpack(argv, 1, argc)) + end) + end + else + local argv, argc + function wrapped_fn(...) + argv = argv or { ... } + argc = argc or select("#", ...) + + timer:start(ms, 0, function() + pcall(vim.schedule_wrap(fn), unpack(argv, 1, argc)) + end) + end + end + return wrapped_fn, timer +end + +--- Test deferment methods (`{throttle,debounce}_{leading,trailing}()`). +---@param bouncer string Bouncer function to test +---@param ms? number Timeout in ms, default 2000. +---@param firstlast? boolean Whether to use the 'other' fn call strategy. +function M.test_defer(bouncer, ms, firstlast) + local bouncers = { + tl = M.throttle_leading, + tt = M.throttle_trailing, + dl = M.debounce_leading, + dt = M.debounce_trailing, + } + + local timeout = ms or 2000 + + local bounced = bouncers[bouncer](function(i) + vim.cmd('echom "' .. bouncer .. ": " .. i .. '"') + end, timeout, firstlast) + + for i, _ in ipairs { 1, 2, 3, 4, 5 } do + bounced(i) + vim.schedule(function() + vim.cmd("echom " .. i) + end) + vim.fn.call("wait", { 1000, "v:false" }) + end +end + +return M diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/deprecated.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/deprecated.lua new file mode 100644 index 0000000..b1cbf2d --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/deprecated.lua @@ -0,0 +1,12 @@ +local deprecated = {} + +deprecated.options = function(opts) + local messages = {} + + if #messages > 0 then + table.insert(messages, 1, "Deprecated options. Please see ':help telescope.changelog'") + vim.api.nvim_err_write(table.concat(messages, "\n \n ") .. "\n \nPress to continue\n") + end +end + +return deprecated diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/entry_manager.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/entry_manager.lua new file mode 100644 index 0000000..a8331e4 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/entry_manager.lua @@ -0,0 +1,168 @@ +local log = require "telescope.log" + +local LinkedList = require "telescope.algos.linked_list" + +local EntryManager = {} +EntryManager.__index = EntryManager + +function EntryManager:new(max_results, set_entry, info) + log.trace "Creating entry_manager..." + + info = info or {} + info.looped = 0 + info.inserted = 0 + info.find_loop = 0 + + -- state contains list of + -- { entry, score } + -- Stored directly in a table, accessed as [1], [2] + set_entry = set_entry or function() end + + return setmetatable({ + linked_states = LinkedList:new { track_at = max_results }, + info = info, + max_results = max_results, + set_entry = set_entry, + worst_acceptable_score = math.huge, + }, self) +end + +function EntryManager:num_results() + return self.linked_states.size +end + +function EntryManager:get_container(index) + local count = 0 + for val in self.linked_states:iter() do + count = count + 1 + + if count == index then + return val + end + end + + return {} +end + +function EntryManager:get_entry(index) + return self:get_container(index)[1] +end + +function EntryManager:get_score(index) + return self:get_container(index)[2] +end + +function EntryManager:get_ordinal(index) + return self:get_entry(index).ordinal +end + +function EntryManager:find_entry(entry) + local info = self.info + + local count = 0 + for container in self.linked_states:iter() do + count = count + 1 + + if container[1] == entry then + info.find_loop = info.find_loop + count + + return count + end + end + + info.find_loop = info.find_loop + count + return nil +end + +function EntryManager:_update_score_from_tracked() + local linked = self.linked_states + + if linked.tracked then + self.worst_acceptable_score = math.min(self.worst_acceptable_score, linked.tracked[2]) + end +end + +function EntryManager:_insert_container_before(picker, index, linked_node, new_container) + self.linked_states:place_before(index, linked_node, new_container) + self.set_entry(picker, index, new_container[1], new_container[2], true) + + self:_update_score_from_tracked() +end + +function EntryManager:_insert_container_after(picker, index, linked_node, new_container) + self.linked_states:place_after(index, linked_node, new_container) + self.set_entry(picker, index, new_container[1], new_container[2], true) + + self:_update_score_from_tracked() +end + +function EntryManager:_append_container(picker, new_container, should_update) + self.linked_states:append(new_container) + self.worst_acceptable_score = math.min(self.worst_acceptable_score, new_container[2]) + + if should_update then + self.set_entry(picker, self.linked_states.size, new_container[1], new_container[2]) + end +end + +function EntryManager:add_entry(picker, score, entry, prompt) + score = score or 0 + + local max_res = self.max_results + local worst_score = self.worst_acceptable_score + local size = self.linked_states.size + + local info = self.info + info.maxed = info.maxed or 0 + + local new_container = { entry, score } + + -- Short circuit for bad scores -- they never need to be displayed. + -- Just save them and we'll deal with them later. + if score >= worst_score then + return self.linked_states:append(new_container) + end + + -- Short circuit for first entry. + if size == 0 then + self.linked_states:prepend(new_container) + self.set_entry(picker, 1, entry, score) + return + end + + for index, container, node in self.linked_states:ipairs() do + info.looped = info.looped + 1 + + if container[2] > score then + return self:_insert_container_before(picker, index, node, new_container) + end + + if score < 1 and container[2] == score and picker.tiebreak(entry, container[1], prompt) then + return self:_insert_container_before(picker, index, node, new_container) + end + + -- Don't add results that are too bad. + if index >= max_res then + info.maxed = info.maxed + 1 + return self:_append_container(picker, new_container, false) + end + end + + if self.linked_states.size >= max_res then + self.worst_acceptable_score = math.min(self.worst_acceptable_score, score) + end + + return self:_insert_container_after(picker, size + 1, self.linked_states.tail, new_container) +end + +function EntryManager:iter() + local iterator = self.linked_states:iter() + return function() + local val = iterator() + if val then + return val[1] + end + end +end + +return EntryManager diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/finders.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/finders.lua new file mode 100644 index 0000000..20698ca --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/finders.lua @@ -0,0 +1,231 @@ +local Job = require "plenary.job" + +local make_entry = require "telescope.make_entry" +local log = require "telescope.log" + +local async_static_finder = require "telescope.finders.async_static_finder" +local async_oneshot_finder = require "telescope.finders.async_oneshot_finder" +local async_job_finder = require "telescope.finders.async_job_finder" + +local finders = {} + +local _callable_obj = function() + local obj = {} + + obj.__index = obj + obj.__call = function(t, ...) + return t:_find(...) + end + + obj.close = function() end + + return obj +end + +--[[ ============================================================= + + JobFinder + +Uses an external Job to get results. Processes results as they arrive. + +For more information about how Jobs are implemented, checkout 'plenary.job' + +-- ============================================================= ]] +local JobFinder = _callable_obj() + +--- Create a new finder command +--- +---@param opts table Keys: +-- fn_command function The function to call +function JobFinder:new(opts) + opts = opts or {} + + assert(not opts.results, "`results` should be used with finder.new_table") + assert(not opts.static, "`static` should be used with finder.new_oneshot_job") + + local obj = setmetatable({ + entry_maker = opts.entry_maker or make_entry.gen_from_string(opts), + fn_command = opts.fn_command, + cwd = opts.cwd, + writer = opts.writer, + + -- Maximum number of results to process. + -- Particularly useful for live updating large queries. + maximum_results = opts.maximum_results, + }, self) + + return obj +end + +function JobFinder:_find(prompt, process_result, process_complete) + log.trace "Finding..." + + if self.job and not self.job.is_shutdown then + log.debug "Shutting down old job" + self.job:shutdown() + end + + local line_num = 0 + local on_output = function(_, line, _) + line_num = line_num + 1 + if not line or line == "" then + return + end + + local entry + if self.entry_maker then + entry = self.entry_maker(line) + if entry then + entry.index = line_num + end + else + entry = line + end + + process_result(entry) + end + + local opts = self:fn_command(prompt) + if not opts then + process_complete() + return + end + + local writer = nil + if opts.writer and Job.is_job(opts.writer) then + writer = opts.writer + elseif opts.writer then + writer = Job:new(opts.writer) + end + + self.job = Job:new { + command = opts.command, + args = opts.args, + cwd = opts.cwd or self.cwd, + + maximum_results = self.maximum_results, + + writer = writer, + + enable_recording = false, + + on_stdout = on_output, + -- on_stderr = on_output, + + on_exit = function() + process_complete() + end, + } + + self.job:start() +end + +local DynamicFinder = _callable_obj() + +function DynamicFinder:new(opts) + opts = opts or {} + + assert(not opts.results, "`results` should be used with finder.new_table") + assert(not opts.static, "`static` should be used with finder.new_oneshot_job") + + local obj = setmetatable({ + curr_buf = opts.curr_buf, + fn = opts.fn, + entry_maker = opts.entry_maker or make_entry.gen_from_string(opts), + }, self) + + return obj +end + +function DynamicFinder:_find(prompt, process_result, process_complete) + local results = self.fn(prompt) + + local result_num = 0 + for _, result in ipairs(results) do + result_num = result_num + 1 + local entry = self.entry_maker(result) + if entry then + entry.index = result_num + end + if process_result(entry) then + return + end + end + + process_complete() +end + +--- Return a new Finder +-- +-- Use at your own risk. +-- This opts dictionary is likely to change, but you are welcome to use it right now. +-- I will try not to change it needlessly, but I will change it sometimes and I won't feel bad. +finders._new = function(opts) + assert(not opts.results, "finder.new is deprecated with `results`. You should use `finder.new_table`") + return JobFinder:new(opts) +end + +finders.new_async_job = function(opts) + if opts.writer then + return finders._new(opts) + end + + return async_job_finder(opts) +end + +finders.new_job = function(command_generator, entry_maker, _, cwd) + return async_job_finder { + command_generator = command_generator, + entry_maker = entry_maker, + cwd = cwd, + } +end + +--- One shot job +---@param command_list string[]: Command list to execute. +---@param opts table: stuff +-- @key entry_maker function Optional: function(line: string) => table +-- @key cwd string +finders.new_oneshot_job = function(command_list, opts) + opts = opts or {} + + assert(not opts.results, "`results` should be used with finder.new_table") + + command_list = vim.deepcopy(command_list) + local command = table.remove(command_list, 1) + + return async_oneshot_finder { + entry_maker = opts.entry_maker or make_entry.gen_from_string(opts), + + cwd = opts.cwd, + maximum_results = opts.maximum_results, + + fn_command = function() + return { + command = command, + args = command_list, + } + end, + } +end + +--- Used to create a finder for a Lua table. +-- If you only pass a table of results, then it will use that as the entries. +-- +-- If you pass a table, and then a function, it's used as: +-- results table, the results to run on +-- entry_maker function, the function to convert results to entries. +finders.new_table = function(t) + return async_static_finder(t) +end + +--- Used to create a finder from a function. +-- +---@param opts table: stuff +-- @key fn function() => list[string] +-- @key entry_maker function Optional: function(line: string) => table +finders.new_dynamic = function(opts) + return DynamicFinder:new(opts) +end + +return finders diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/finders/async_job_finder.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/finders/async_job_finder.lua new file mode 100644 index 0000000..23444e7 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/finders/async_job_finder.lua @@ -0,0 +1,84 @@ +local async_job = require "telescope._" +local LinesPipe = require("telescope._").LinesPipe + +local make_entry = require "telescope.make_entry" +local log = require "telescope.log" + +return function(opts) + log.trace("Creating async_job:", opts) + local entry_maker = opts.entry_maker or make_entry.gen_from_string(opts) + + local fn_command = function(prompt) + local command_list = opts.command_generator(prompt) + if command_list == nil then + return nil + end + + local command = table.remove(command_list, 1) + + local res = { + command = command, + args = command_list, + } + + return res + end + + local job + + local callable = function(_, prompt, process_result, process_complete) + if job then + job:close(true) + end + + local job_opts = fn_command(prompt) + if not job_opts then + process_complete() + return + end + + local writer = nil + -- if job_opts.writer and Job.is_job(job_opts.writer) then + -- writer = job_opts.writer + if opts.writer then + error "async_job_finder.writer is not yet implemented" + writer = async_job.writer(opts.writer) + end + + local stdout = LinesPipe() + + job = async_job.spawn { + command = job_opts.command, + args = job_opts.args, + cwd = job_opts.cwd or opts.cwd, + env = job_opts.env or opts.env, + writer = writer, + + stdout = stdout, + } + + local line_num = 0 + for line in stdout:iter(true) do + line_num = line_num + 1 + local entry = entry_maker(line) + if entry then + entry.index = line_num + end + if process_result(entry) then + return + end + end + + process_complete() + end + + return setmetatable({ + close = function() + if job then + job:close(true) + end + end, + }, { + __call = callable, + }) +end diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/finders/async_oneshot_finder.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/finders/async_oneshot_finder.lua new file mode 100644 index 0000000..cacc0b9 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/finders/async_oneshot_finder.lua @@ -0,0 +1,104 @@ +local async = require "plenary.async" +local async_job = require "telescope._" +local LinesPipe = require("telescope._").LinesPipe + +local make_entry = require "telescope.make_entry" + +local await_count = 1000 + +return function(opts) + opts = opts or {} + + local entry_maker = opts.entry_maker or make_entry.gen_from_string(opts) + local cwd = opts.cwd + local env = opts.env + local fn_command = assert(opts.fn_command, "Must pass `fn_command`") + + local results = vim.F.if_nil(opts.results, {}) + local num_results = #results + + local job_started = false + local job_completed = false + local stdout = nil + + local job + + return setmetatable({ + close = function() + if job then + job:close() + end + end, + results = results, + entry_maker = entry_maker, + }, { + __call = function(_, prompt, process_result, process_complete) + if not job_started then + local job_opts = fn_command() + + -- TODO: Handle writers. + -- local writer + -- if job_opts.writer and Job.is_job(job_opts.writer) then + -- writer = job_opts.writer + -- elseif job_opts.writer then + -- writer = Job:new(job_opts.writer) + -- end + + stdout = LinesPipe() + job = async_job.spawn { + command = job_opts.command, + args = job_opts.args, + cwd = cwd, + env = env, + + stdout = stdout, + } + + job_started = true + end + + if not job_completed then + if not vim.tbl_isempty(results) then + for _, v in ipairs(results) do + process_result(v) + end + end + for line in stdout:iter(false) do + num_results = num_results + 1 + + if num_results % await_count then + async.util.scheduler() + end + + local entry = entry_maker(line) + if entry then + entry.index = num_results + end + results[num_results] = entry + process_result(entry) + end + + process_complete() + job_completed = true + + return + end + + local current_count = num_results + for index = 1, current_count do + -- TODO: Figure out scheduling... + if index % await_count then + async.util.scheduler() + end + + if process_result(results[index]) then + break + end + end + + if job_completed then + process_complete() + end + end, + }) +end diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/finders/async_static_finder.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/finders/async_static_finder.lua new file mode 100644 index 0000000..7db3e99 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/finders/async_static_finder.lua @@ -0,0 +1,44 @@ +local scheduler = require("plenary.async").util.scheduler + +local make_entry = require "telescope.make_entry" + +return function(opts) + local input_results + if require("telescope.utils").islist(opts) then + input_results = opts + else + input_results = opts.results + end + + local entry_maker = opts.entry_maker or make_entry.gen_from_string(opts) + + local results = {} + for k, v in ipairs(input_results) do + local entry = entry_maker(v) + + if entry then + entry.index = k + table.insert(results, entry) + end + end + + return setmetatable({ + results = results, + entry_maker = entry_maker, + close = function() end, + }, { + __call = function(_, _, process_result, process_complete) + for i, v in ipairs(results) do + if process_result(v) then + break + end + + if i % 1000 == 0 then + scheduler() + end + end + + process_complete() + end, + }) +end diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/from_entry.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/from_entry.lua new file mode 100644 index 0000000..4a66d47 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/from_entry.lua @@ -0,0 +1,45 @@ +--[[ ============================================================================= + +Get metadata from entries. + +This file is still WIP, so expect some changes if you're trying to consume these APIs. + +This will provide standard mechanism for accessing information from an entry. + +--============================================================================= ]] +local utils = require "telescope.utils" + +local from_entry = {} + +function from_entry.path(entry, validate, escape) + escape = vim.F.if_nil(escape, true) + local path = entry.path + if path == nil then + path = entry.filename + end + if path == nil then + path = entry.value + end + if path == nil then + require("telescope.log").error(string.format("Invalid Entry: '%s'", vim.inspect(entry))) + return + end + + -- only 0 if neither filereadable nor directory + if validate then + -- We need to expand for filereadable and isdirectory + -- TODO(conni2461): we are not going to return the expanded path because + -- this would lead to cache misses in the perviewer. + -- Requires overall refactoring in previewer interface + local expanded = utils.path_expand(path) + if (vim.fn.filereadable(expanded) + vim.fn.isdirectory(expanded)) == 0 then + return + end + end + if escape then + return vim.fn.fnameescape(path) + end + return path +end + +return from_entry diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/health.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/health.lua new file mode 100644 index 0000000..70e5254 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/health.lua @@ -0,0 +1,133 @@ +local health = vim.health or require "health" +local start = health.start or health.report_start +local ok = health.ok or health.report_ok +local warn = health.warn or health.report_warn +local error = health.error or health.report_error +local info = health.info or health.report_info + +local extension_module = require "telescope._extensions" +local extension_info = require("telescope").extensions +local is_win = vim.api.nvim_call_function("has", { "win32" }) == 1 + +local optional_dependencies = { + { + finder_name = "live-grep", + package = { + { + name = "rg", + url = "[BurntSushi/ripgrep](https://github.com/BurntSushi/ripgrep)", + optional = false, + }, + }, + }, + { + finder_name = "find-files", + package = { + { + name = "fd", + binaries = { "fdfind", "fd" }, + url = "[sharkdp/fd](https://github.com/sharkdp/fd)", + optional = true, + }, + }, + }, +} + +local required_plugins = { + { lib = "plenary", optional = false }, + { + lib = "nvim-treesitter", + optional = true, + info = "(Required for `:Telescope treesitter`.)", + }, +} + +local check_binary_installed = function(package) + local binaries = package.binaries or { package.name } + for _, binary in ipairs(binaries) do + local found = vim.fn.executable(binary) == 1 + if not found and is_win then + binary = binary .. ".exe" + found = vim.fn.executable(binary) == 1 + end + if found then + local handle = io.popen(binary .. " --version") + local binary_version = handle:read "*a" + handle:close() + return true, binary_version + end + end +end + +local function lualib_installed(lib_name) + local res, _ = pcall(require, lib_name) + return res +end + +local M = {} + +M.check = function() + -- Required lua libs + start "Checking for required plugins" + for _, plugin in ipairs(required_plugins) do + if lualib_installed(plugin.lib) then + ok(plugin.lib .. " installed.") + else + local lib_not_installed = plugin.lib .. " not found." + if plugin.optional then + warn(("%s %s"):format(lib_not_installed, plugin.info)) + else + error(lib_not_installed) + end + end + end + + -- external dependencies + -- TODO: only perform checks if user has enabled dependency in their config + start "Checking external dependencies" + + for _, opt_dep in pairs(optional_dependencies) do + for _, package in ipairs(opt_dep.package) do + local installed, version = check_binary_installed(package) + if not installed then + local err_msg = ("%s: not found."):format(package.name) + if package.optional then + warn(("%s %s"):format(err_msg, ("Install %s for extended capabilities"):format(package.url))) + else + error( + ("%s %s"):format( + err_msg, + ("`%s` finder will not function without %s installed."):format(opt_dep.finder_name, package.url) + ) + ) + end + else + local eol = version:find "\n" + local ver = eol and version:sub(0, eol - 1) or "(unknown version)" + ok(("%s: found %s"):format(package.name, ver)) + end + end + end + + -- Extensions + start "===== Installed extensions =====" + + local installed = {} + for extension_name, _ in pairs(extension_info) do + installed[#installed + 1] = extension_name + end + table.sort(installed) + + for _, installed_ext in ipairs(installed) do + local extension_healthcheck = extension_module._health[installed_ext] + + start(string.format("Telescope Extension: `%s`", installed_ext)) + if extension_healthcheck then + extension_healthcheck() + else + info "No healthcheck provided" + end + end +end + +return M diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/init.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/init.lua new file mode 100644 index 0000000..acb56e7 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/init.lua @@ -0,0 +1,176 @@ +local _extensions = require "telescope._extensions" + +local telescope = {} + +-- TODO(conni2461): also table of contents for tree-sitter-lua +-- TODO: Add pre to the works +-- ---@pre [[ +-- ---@pre ]] + +---@brief [[ +--- Telescope.nvim is a plugin for fuzzy finding and neovim. It helps you search, +--- filter, find and pick things in Lua. +--- +--- Getting started with telescope: +--- 1. Run `:checkhealth telescope` to make sure everything is installed. +--- 2. Evaluate it is working with +--- `:Telescope find_files` or +--- `:lua require("telescope.builtin").find_files()` +--- 3. Put a `require("telescope").setup()` call somewhere in your neovim config. +--- 4. Read |telescope.setup| to check what config keys are available and what you can put inside the setup call +--- 5. Read |telescope.builtin| to check which builtin pickers are offered and what options these implement +--- 6. Profit +--- +--- The below flow chart illustrates a simplified telescope architecture: +---
+--- ┌───────────────────────────────────────────────────────────┐
+--- │      ┌────────┐                                           │
+--- │      │ Multi  │                                ┌───────+  │
+--- │      │ Select │    ┌───────┐                   │ Entry │  │
+--- │      └─────┬──*    │ Entry │    ┌────────+     │ Maker │  │
+--- │            │   ┌───│Manager│────│ Sorter │┐    └───┬───*  │
+--- │            ▼   ▼   └───────*    └────────┘│        │      │
+--- │            1────────┐                 2───┴──┐     │      │
+--- │      ┌─────│ Picker │                 │Finder│◀────┘      │
+--- │      ▼     └───┬────┘                 └──────*            │
+--- │ ┌────────┐     │       3────────+         ▲               │
+--- │ │Selected│     └───────│ Prompt │─────────┘               │
+--- │ │ Entry  │             └───┬────┘                         │
+--- │ └────────*             ┌───┴────┐  ┌────────┐  ┌────────┐ │
+--- │     │  ▲    4─────────┐│ Prompt │  │(Attach)│  │Actions │ │
+--- │     ▼  └──▶ │ Results ││ Buffer │◀─┤Mappings│◀─┤User Fn │ │
+--- │5─────────┐  └─────────┘└────────┘  └────────┘  └────────┘ │
+--- ││Previewer│                                                │
+--- │└─────────┘                   telescope.nvim architecture  │
+--- └───────────────────────────────────────────────────────────┘
+---
+---   + The `Entry Maker` at least defines
+---     - value: "raw" result of the finder
+---     - ordinal: string to be sorted derived from value
+---     - display: line representation of entry in results buffer
+---
+---   * The finder, entry manager, selected entry, and multi selections
+---     comprises `entries` constructed by the `Entry Maker` from
+---     raw results of the finder (`value`s)
+---
+---  Primary components:
+---   1 Picker: central UI dedicated to varying use cases
+---             (finding files, grepping, diagnostics, etc.)
+---             see :h telescope.builtin
+---   2 Finder: pipe or interactively generates results to pick over
+---   3 Prompt: user input that triggers the finder which sorts results
+---             in order into the entry manager
+---   4 Results: listed entries scored by sorter from finder results
+---   5 Previewer: preview of context of selected entry
+---                see :h telescope.previewers
+--- 
+--- +--- A practical introduction into telescope customization is our +--- `developers.md` (top-level of repo) and `:h telescope.actions` that +--- showcase how to access information about the state of the picker (current +--- selection, etc.). +---
+--- To find out more:
+--- https://github.com/nvim-telescope/telescope.nvim
+---
+---   :h telescope.setup
+---   :h telescope.command
+---   :h telescope.builtin
+---   :h telescope.themes
+---   :h telescope.layout
+---   :h telescope.resolve
+---   :h telescope.actions
+---   :h telescope.actions.state
+---   :h telescope.actions.set
+---   :h telescope.actions.utils
+---   :h telescope.actions.generate
+---   :h telescope.actions.history
+---   :h telescope.previewers
+--- 
+---@brief ]] + +---@tag telescope.nvim +---@config { ["name"] = "INTRODUCTION" } + +--- Setup function to be run by user. Configures the defaults, pickers and +--- extensions of telescope. +--- +--- Usage: +--- +--- require('telescope').setup{ +--- defaults = { +--- -- Default configuration for telescope goes here: +--- -- config_key = value, +--- -- .. +--- }, +--- pickers = { +--- -- Default configuration for builtin pickers goes here: +--- -- picker_name = { +--- -- picker_config_key = value, +--- -- ... +--- -- } +--- -- Now the picker_config_key will be applied every time you call this +--- -- builtin picker +--- }, +--- extensions = { +--- -- Your extension configuration goes here: +--- -- extension_name = { +--- -- extension_config_key = value, +--- -- } +--- -- please take a look at the readme of the extension you want to configure +--- } +--- } +--- +---@param opts table: Configuration opts. Keys: defaults, pickers, extensions +---@eval { ["description"] = require('telescope').__format_setup_keys() } +function telescope.setup(opts) + opts = opts or {} + + if opts.default then + error "'default' is not a valid value for setup. See 'defaults'" + end + + require("telescope.config").set_defaults(opts.defaults) + require("telescope.config").set_pickers(opts.pickers) + _extensions.set_config(opts.extensions) +end + +--- Load an extension. +--- - Notes: +--- - Loading triggers ext setup via the config passed in |telescope.setup| +---@param name string: Name of the extension +function telescope.load_extension(name) + return _extensions.load(name) +end + +--- Register an extension. To be used by plugin authors. +---@param mod table: Module +function telescope.register_extension(mod) + return _extensions.register(mod) +end + +--- Use telescope.extensions to reference any extensions within your configuration.
+--- While the docs currently generate this as a function, it's actually a table. Sorry. +telescope.extensions = require("telescope._extensions").manager + +telescope.__format_setup_keys = function() + local names = require("telescope.config").descriptions_order + local descriptions = require("telescope.config").descriptions + + local result = { "
", "", "Valid keys for {opts.defaults}" }
+  for _, name in ipairs(names) do
+    local desc = descriptions[name]
+
+    table.insert(result, "")
+    table.insert(result, string.format("%s*telescope.defaults.%s*", string.rep(" ", 70 - 20 - #name), name))
+    table.insert(result, string.format("%s: ~", name))
+    for _, line in ipairs(vim.split(desc, "\n")) do
+      table.insert(result, string.format("    %s", line))
+    end
+  end
+
+  table.insert(result, "
") + return result +end + +return telescope diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/log.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/log.lua new file mode 100644 index 0000000..c74378f --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/log.lua @@ -0,0 +1,4 @@ +return require("plenary.log").new { + plugin = "telescope", + level = "info", +} diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/make_entry.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/make_entry.lua new file mode 100644 index 0000000..e629eea --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/make_entry.lua @@ -0,0 +1,1400 @@ +---@tag telescope.make_entry + +---@brief [[ +--- +--- Each picker has a finder made up of two parts, the results which are the +--- data to be displayed, and the entry_maker. These entry_makers are functions +--- returned from make_entry functions. These will be referred to as +--- entry_makers in the following documentation. +--- +--- Every entry maker returns a function that accepts the data to be used for +--- an entry. This function will return an entry table (or nil, meaning skip +--- this entry) which contains the following important keys: +--- - value any: value key can be anything but still required +--- - valid bool (optional): is an optional key because it defaults to true but if the key +--- is set to false it will not be displayed by the picker +--- - ordinal string: is the text that is used for filtering +--- - display string|function: is either a string of the text that is being +--- displayed or a function receiving the entry at a later stage, when the entry +--- is actually being displayed. A function can be useful here if a complex +--- calculation has to be done. `make_entry` can also return a second value - +--- a highlight array which will then apply to the line. Highlight entry in +--- this array has the following signature `{ { start_col, end_col }, hl_group }` +--- - filename string (optional): will be interpreted by the default `` action as +--- open this file +--- - bufnr number (optional): will be interpreted by the default `` action as open +--- this buffer +--- - lnum number (optional): lnum value which will be interpreted by the default `` +--- action as a jump to this line +--- - col number (optional): col value which will be interpreted by the default `` +--- action as a jump to this column +--- +--- For more information on easier displaying, see |telescope.pickers.entry_display| +--- +--- TODO: Document something we call `entry_index` +---@brief ]] + +local entry_display = require "telescope.pickers.entry_display" +local utils = require "telescope.utils" +local strings = require "plenary.strings" +local Path = require "plenary.path" + +local treesitter_type_highlight = { + ["associated"] = "TSConstant", + ["constant"] = "TSConstant", + ["field"] = "TSField", + ["function"] = "TSFunction", + ["method"] = "TSMethod", + ["parameter"] = "TSParameter", + ["property"] = "TSProperty", + ["struct"] = "Struct", + ["var"] = "TSVariableBuiltin", +} + +local lsp_type_highlight = { + ["Class"] = "TelescopeResultsClass", + ["Constant"] = "TelescopeResultsConstant", + ["Field"] = "TelescopeResultsField", + ["Function"] = "TelescopeResultsFunction", + ["Method"] = "TelescopeResultsMethod", + ["Property"] = "TelescopeResultsOperator", + ["Struct"] = "TelescopeResultsStruct", + ["Variable"] = "TelescopeResultsVariable", +} + +local get_filename_fn = function() + local bufnr_name_cache = {} + return function(bufnr) + bufnr = vim.F.if_nil(bufnr, 0) + local c = bufnr_name_cache[bufnr] + if c then + return c + end + + local n = vim.api.nvim_buf_get_name(bufnr) + bufnr_name_cache[bufnr] = n + return n + end +end + +local handle_entry_index = function(opts, t, k) + local override = ((opts or {}).entry_index or {})[k] + if not override then + return + end + + local val, save = override(t, opts) + if save then + rawset(t, k, val) + end + return val +end + +local make_entry = {} + +make_entry.set_default_entry_mt = function(tbl, opts) + return setmetatable({}, { + __index = function(t, k) + local override = handle_entry_index(opts, t, k) + if override then + return override + end + + -- Only hit tbl once + local val = tbl[k] + if val then + rawset(t, k, val) + end + + return val + end, + }) +end + +do + local lookup_keys = { + display = 1, + ordinal = 1, + value = 1, + } + + function make_entry.gen_from_string(opts) + local mt_string_entry = { + __index = function(t, k) + local override = handle_entry_index(opts, t, k) + if override then + return override + end + + return rawget(t, rawget(lookup_keys, k)) + end, + } + + return function(line) + return setmetatable({ + line, + }, mt_string_entry) + end + end +end + +do + local lookup_keys = { + ordinal = 1, + value = 1, + filename = 1, + cwd = 2, + } + + function make_entry.gen_from_file(opts) + opts = opts or {} + + local cwd = utils.path_expand(opts.cwd or vim.loop.cwd()) + + local disable_devicons = opts.disable_devicons + + local mt_file_entry = {} + + mt_file_entry.cwd = cwd + mt_file_entry.display = function(entry) + local hl_group, icon + local display, path_style = utils.transform_path(opts, entry.value) + + display, hl_group, icon = utils.transform_devicons(entry.value, display, disable_devicons) + + if hl_group then + local style = { { { 0, #icon + 1 }, hl_group } } + style = utils.merge_styles(style, path_style, #icon + 1) + return display, style + else + return display, path_style + end + end + + mt_file_entry.__index = function(t, k) + local override = handle_entry_index(opts, t, k) + if override then + return override + end + + local raw = rawget(mt_file_entry, k) + if raw then + return raw + end + + if k == "path" then + local retpath = Path:new({ t.cwd, t.value }):absolute() + if not vim.loop.fs_access(retpath, "R") then + retpath = t.value + end + return retpath + end + + return rawget(t, rawget(lookup_keys, k)) + end + + if opts.file_entry_encoding then + return function(line) + line = vim.iconv(line, opts.file_entry_encoding, "utf8") + return setmetatable({ line }, mt_file_entry) + end + else + return function(line) + return setmetatable({ line }, mt_file_entry) + end + end + end +end + +do + local lookup_keys = { + value = 1, + ordinal = 1, + } + + -- Gets called only once to parse everything out for the vimgrep, after that looks up directly. + local parse_with_col = function(t) + local _, _, filename, lnum, col, text = string.find(t.value, [[(..-):(%d+):(%d+):(.*)]]) + + local ok + ok, lnum = pcall(tonumber, lnum) + if not ok then + lnum = nil + end + + ok, col = pcall(tonumber, col) + if not ok then + col = nil + end + + t.filename = filename + t.lnum = lnum + t.col = col + t.text = text + + return { filename, lnum, col, text } + end + + local parse_without_col = function(t) + local _, _, filename, lnum, text = string.find(t.value, [[(..-):(%d+):(.*)]]) + + local ok + ok, lnum = pcall(tonumber, lnum) + if not ok then + lnum = nil + end + + t.filename = filename + t.lnum = lnum + t.col = nil + t.text = text + + return { filename, lnum, nil, text } + end + + local parse_only_filename = function(t) + t.filename = t.value + t.lnum = nil + t.col = nil + t.text = "" + + return { t.filename, nil, nil, "" } + end + + function make_entry.gen_from_vimgrep(opts) + opts = opts or {} + + local mt_vimgrep_entry + local parse = parse_with_col + if opts.__matches == true then + parse = parse_only_filename + elseif opts.__inverted == true then + parse = parse_without_col + end + + local disable_devicons = opts.disable_devicons + local disable_coordinates = opts.disable_coordinates + local only_sort_text = opts.only_sort_text + + local execute_keys = { + path = function(t) + if Path:new(t.filename):is_absolute() then + return t.filename, false + else + return Path:new({ t.cwd, t.filename }):absolute(), false + end + end, + + filename = function(t) + return parse(t)[1], true + end, + + lnum = function(t) + return parse(t)[2], true + end, + + col = function(t) + return parse(t)[3], true + end, + + text = function(t) + return parse(t)[4], true + end, + } + + -- For text search only, the ordinal value is actually the text. + if only_sort_text then + execute_keys.ordinal = function(t) + return t.text + end + end + + local display_string = "%s%s%s" + + mt_vimgrep_entry = { + cwd = utils.path_expand(opts.cwd or vim.loop.cwd()), + + display = function(entry) + local display_filename, path_style = utils.transform_path(opts, entry.filename) + + local coordinates = ":" + if not disable_coordinates then + if entry.lnum then + if entry.col then + coordinates = string.format(":%s:%s:", entry.lnum, entry.col) + else + coordinates = string.format(":%s:", entry.lnum) + end + end + end + + local display, hl_group, icon = utils.transform_devicons( + entry.filename, + string.format(display_string, display_filename, coordinates, entry.text), + disable_devicons + ) + + if hl_group then + local style = { { { 0, #icon }, hl_group } } + style = utils.merge_styles(style, path_style, #icon + 1) + return display, style + else + return display, path_style + end + end, + + __index = function(t, k) + local override = handle_entry_index(opts, t, k) + if override then + return override + end + + local raw = rawget(mt_vimgrep_entry, k) + if raw then + return raw + end + + local executor = rawget(execute_keys, k) + if executor then + local val, save = executor(t) + if save then + rawset(t, k, val) + end + return val + end + + return rawget(t, rawget(lookup_keys, k)) + end, + } + + return function(line) + return setmetatable({ line }, mt_vimgrep_entry) + end + end +end + +function make_entry.gen_from_git_stash(opts) + local displayer = entry_display.create { + separator = " ", + items = { + { width = 10 }, + opts.show_branch and { width = 15 } or "", + { remaining = true }, + }, + } + + local make_display = function(entry) + return displayer { + { entry.value, "TelescopeResultsLineNr" }, + opts.show_branch and { entry.branch_name, "TelescopeResultsIdentifier" } or "", + entry.commit_info, + } + end + + return function(entry) + if entry == "" then + return nil + end + + local splitted = utils.max_split(entry, ": ", 2) + local stash_idx = splitted[1] + local _, branch_name = string.match(splitted[2], "^([WIP on|On]+) (.+)") + local commit_info = splitted[3] + + return make_entry.set_default_entry_mt({ + value = stash_idx, + ordinal = commit_info, + branch_name = branch_name, + commit_info = commit_info, + display = make_display, + }, opts) + end +end + +function make_entry.gen_from_git_commits(opts) + opts = opts or {} + + local displayer = entry_display.create { + separator = " ", + items = { + { width = 8 }, + { remaining = true }, + }, + } + + local make_display = function(entry) + return displayer { + { entry.value, "TelescopeResultsIdentifier" }, + entry.msg, + } + end + + return function(entry) + if entry == "" then + return nil + end + + local sha, msg = string.match(entry, "([^ ]+) (.+)") + + if not msg then + sha = entry + msg = "" + end + + return make_entry.set_default_entry_mt({ + value = sha, + ordinal = sha .. " " .. msg, + msg = msg, + display = make_display, + current_file = opts.current_file, + }, opts) + end +end + +function make_entry.gen_from_quickfix(opts) + opts = opts or {} + local show_line = vim.F.if_nil(opts.show_line, true) + + local hidden = utils.is_path_hidden(opts) + + local make_display = function(entry) + local display_filename, path_style = utils.transform_path(opts, entry.filename) + local display_string = string.format("%s:%d:%d", display_filename, entry.lnum, entry.col) + if hidden then + display_string = string.format("%4d:%2d", entry.lnum, entry.col) + end + + if show_line then + local text = entry.text + if opts.trim_text then + text = vim.trim(text) + end + text = text:gsub(".* | ", "") + display_string = display_string .. ":" .. text + end + + return display_string, path_style + end + + local get_filename = get_filename_fn() + return function(entry) + local filename = vim.F.if_nil(entry.filename, get_filename(entry.bufnr)) + + return make_entry.set_default_entry_mt({ + value = entry, + ordinal = (not hidden and filename or "") .. " " .. entry.text, + display = make_display, + + bufnr = entry.bufnr, + filename = filename, + lnum = entry.lnum, + col = entry.col, + text = entry.text, + start = entry.start, + finish = entry.finish, + }, opts) + end +end + +function make_entry.gen_from_lsp_symbols(opts) + opts = opts or {} + + local bufnr = opts.bufnr or vim.api.nvim_get_current_buf() + + -- Default we have two columns, symbol and type(unbound) + -- If path is not hidden then its, filepath, symbol and type(still unbound) + -- If show_line is also set, type is bound to len 8 + local display_items = { + { width = opts.symbol_width or 25 }, + { remaining = true }, + } + + local hidden = utils.is_path_hidden(opts) + if not hidden then + table.insert(display_items, 1, { width = vim.F.if_nil(opts.fname_width, 30) }) + end + + if opts.show_line then + -- bound type to len 8 or custom + table.insert(display_items, #display_items, { width = opts.symbol_type_width or 8 }) + end + + local displayer = entry_display.create { + separator = " ", + hl_chars = { ["["] = "TelescopeBorder", ["]"] = "TelescopeBorder" }, + items = display_items, + } + local type_highlight = vim.F.if_nil(opts.symbol_highlights or lsp_type_highlight) + + local make_display = function(entry) + local msg + + if opts.show_line then + msg = vim.trim(vim.F.if_nil(vim.api.nvim_buf_get_lines(bufnr, entry.lnum - 1, entry.lnum, false)[1], "")) + end + + if hidden then + return displayer { + entry.symbol_name, + { entry.symbol_type:lower(), type_highlight[entry.symbol_type] }, + msg, + } + else + local display_path, path_style = utils.transform_path(opts, entry.filename) + return displayer { + { + display_path, + function() + return path_style + end, + }, + entry.symbol_name, + { entry.symbol_type:lower(), type_highlight[entry.symbol_type] }, + msg, + } + end + end + + local get_filename = get_filename_fn() + return function(entry) + local filename = vim.F.if_nil(entry.filename, get_filename(entry.bufnr)) + local symbol_msg = entry.text + local symbol_type, symbol_name = symbol_msg:match "%[(.+)%]%s+(.*)" + local ordinal = "" + if not hidden and filename then + ordinal = filename .. " " + end + ordinal = ordinal .. symbol_name .. " " .. (symbol_type or "unknown") + return make_entry.set_default_entry_mt({ + value = entry, + ordinal = ordinal, + display = make_display, + + filename = filename, + lnum = entry.lnum, + col = entry.col, + symbol_name = symbol_name, + symbol_type = symbol_type, + start = entry.start, + finish = entry.finish, + }, opts) + end +end + +function make_entry.gen_from_buffer(opts) + opts = opts or {} + + local disable_devicons = opts.disable_devicons + + local icon_width = 0 + if not disable_devicons then + local icon, _ = utils.get_devicons("fname", disable_devicons) + icon_width = strings.strdisplaywidth(icon) + end + + local displayer = entry_display.create { + separator = " ", + items = { + { width = opts.bufnr_width }, + { width = 4 }, + { width = icon_width }, + { remaining = true }, + }, + } + + local cwd = utils.path_expand(opts.cwd or vim.loop.cwd()) + + local make_display = function(entry) + -- bufnr_width + modes + icon + 3 spaces + : + lnum + opts.__prefix = opts.bufnr_width + 4 + icon_width + 3 + 1 + #tostring(entry.lnum) + local display_bufname, path_style = utils.transform_path(opts, entry.filename) + local icon, hl_group = utils.get_devicons(entry.filename, disable_devicons) + + return displayer { + { entry.bufnr, "TelescopeResultsNumber" }, + { entry.indicator, "TelescopeResultsComment" }, + { icon, hl_group }, + { + display_bufname .. ":" .. entry.lnum, + function() + return path_style + end, + }, + } + end + + return function(entry) + local filename = entry.info.name ~= "" and entry.info.name or nil + local bufname = filename and Path:new(filename):normalize(cwd) or "[No Name]" + + local hidden = entry.info.hidden == 1 and "h" or "a" + local readonly = vim.api.nvim_buf_get_option(entry.bufnr, "readonly") and "=" or " " + local changed = entry.info.changed == 1 and "+" or " " + local indicator = entry.flag .. hidden .. readonly .. changed + local lnum = 1 + + -- account for potentially stale lnum as getbufinfo might not be updated or from resuming buffers picker + if entry.info.lnum ~= 0 then + -- but make sure the buffer is loaded, otherwise line_count is 0 + if vim.api.nvim_buf_is_loaded(entry.bufnr) then + local line_count = vim.api.nvim_buf_line_count(entry.bufnr) + lnum = math.max(math.min(entry.info.lnum, line_count), 1) + else + lnum = entry.info.lnum + end + end + + return make_entry.set_default_entry_mt({ + value = bufname, + ordinal = entry.bufnr .. " : " .. bufname, + display = make_display, + bufnr = entry.bufnr, + path = filename, + filename = bufname, + lnum = lnum, + indicator = indicator, + }, opts) + end +end + +function make_entry.gen_from_treesitter(opts) + opts = opts or {} + + local bufnr = opts.bufnr or vim.api.nvim_get_current_buf() + + local display_items = { + { width = opts.symbol_width or 25 }, + { width = 10 }, + { remaining = true }, + } + + if opts.show_line then + table.insert(display_items, 2, { width = 6 }) + end + + local displayer = entry_display.create { + separator = " ", + items = display_items, + } + + local type_highlight = opts.symbol_highlights or treesitter_type_highlight + + local make_display = function(entry) + local msg = vim.api.nvim_buf_get_lines(bufnr, entry.lnum, entry.lnum, false)[1] or "" + msg = vim.trim(msg) + + local display_columns = { + entry.text, + { entry.kind, type_highlight[entry.kind], type_highlight[entry.kind] }, + msg, + } + if opts.show_line then + table.insert(display_columns, 2, { entry.lnum .. ":" .. entry.col, "TelescopeResultsLineNr" }) + end + + return displayer(display_columns) + end + + local get_filename = get_filename_fn() + return function(entry) + local start_row, start_col, end_row, _ = vim.treesitter.get_node_range(entry.node) + local node_text = vim.treesitter.get_node_text(entry.node, bufnr) + return make_entry.set_default_entry_mt({ + value = entry.node, + kind = entry.kind, + ordinal = node_text .. " " .. (entry.kind or "unknown"), + display = make_display, + + node_text = node_text, + + filename = get_filename(bufnr), + -- need to add one since the previewer substacts one + lnum = start_row + 1, + col = start_col, + text = node_text, + start = start_row, + finish = end_row, + }, opts) + end +end + +function make_entry.gen_from_packages(opts) + opts = opts or {} + + local make_display = function(module_name) + local p_path = package.searchpath(module_name, package.path) or "" + local display = string.format("%-" .. opts.column_len .. "s : %s", module_name, vim.fn.fnamemodify(p_path, ":~:.")) + + return display + end + + return function(module_name) + return make_entry.set_default_entry_mt({ + valid = module_name ~= "", + value = module_name, + ordinal = module_name, + display = make_display(module_name), + }, opts) + end +end + +function make_entry.gen_from_apropos(opts) + local sections = {} + if #opts.sections == 1 and opts.sections[1] == "ALL" then + setmetatable(sections, { + __index = function() + return true + end, + }) + else + for _, section in ipairs(opts.sections) do + sections[section] = true + end + end + + local displayer = entry_display.create { + separator = " ", + items = { + { width = 30 }, + { remaining = true }, + }, + } + + local make_display = function(entry) + return displayer { + { entry.keyword, "TelescopeResultsFunction" }, + entry.description, + } + end + + return function(line) + local keyword, cmd, section, desc = line:match "^((.-)%s*%(([^)]+)%).-)%s+%-%s+(.*)$" + -- apropos might return alternatives for the cmd which are split on `,` and breaks everything else + -- for example on void linux it will return `alacritty, Alacritty` which will later result in + -- `man 1 alacritty, Alacritty`. So we just take the first one. + -- doing this outside of regex because of obvious reasons + cmd = vim.split(cmd, ",")[1] + return keyword + and sections[section] + and make_entry.set_default_entry_mt({ + value = cmd, + description = desc, + ordinal = cmd, + display = make_display, + section = section, + keyword = keyword, + }, opts) + or nil + end +end + +function make_entry.gen_from_marks(opts) + return function(item) + return make_entry.set_default_entry_mt({ + value = item.line, + ordinal = item.line, + display = item.line, + lnum = item.lnum, + col = item.col, + start = item.lnum, + filename = item.filename, + }, opts) + end +end + +function make_entry.gen_from_registers(opts) + local displayer = entry_display.create { + separator = " ", + hl_chars = { ["["] = "TelescopeBorder", ["]"] = "TelescopeBorder" }, + items = { + { width = 3 }, + { remaining = true }, + }, + } + + local make_display = function(entry) + local content = entry.content + return displayer { + { "[" .. entry.value .. "]", "TelescopeResultsNumber" }, + type(content) == "string" and content:gsub("\n", "\\n") or content, + } + end + + return function(entry) + local contents = vim.fn.getreg(entry, 1) + return make_entry.set_default_entry_mt({ + value = entry, + ordinal = string.format("%s %s", entry, contents), + content = contents, + display = make_display, + }, opts) + end +end + +function make_entry.gen_from_keymaps(opts) + local function get_desc(entry) + if entry.callback and not entry.desc then + return require("telescope.actions.utils")._get_anon_function_name(debug.getinfo(entry.callback)) + end + return vim.F.if_nil(entry.desc, entry.rhs):gsub("\n", "\\n") + end + + local function get_lhs(entry) + return utils.display_termcodes(entry.lhs) + end + + local function get_attr(entry) + local ret = "" + if entry.value.noremap ~= 0 then + ret = ret .. "*" + end + if entry.value.buffer ~= 0 then + ret = ret .. "@" + end + return ret + end + + local displayer = require("telescope.pickers.entry_display").create { + separator = "▏", + items = { + { width = 3 }, + { width = opts.width_lhs }, + { width = 2 }, + { remaining = true }, + }, + } + local make_display = function(entry) + return displayer { + entry.mode, + get_lhs(entry), + get_attr(entry), + get_desc(entry), + } + end + + return function(entry) + local desc = get_desc(entry) + local lhs = get_lhs(entry) + return make_entry.set_default_entry_mt({ + mode = entry.mode, + lhs = lhs, + desc = desc, + valid = entry ~= "", + value = entry, + ordinal = entry.mode .. " " .. lhs .. " " .. desc, + display = make_display, + }, opts) + end +end + +function make_entry.gen_from_highlights(opts) + local make_display = function(entry) + local display = entry.value + return display, { { { 0, #display }, display } } + end + + return function(entry) + return make_entry.set_default_entry_mt({ + value = entry, + display = make_display, + ordinal = entry, + }, opts) + end +end + +function make_entry.gen_from_picker(opts) + local displayer = entry_display.create { + separator = " │ ", + items = { + { width = 0.5 }, + { remaining = true }, + }, + } + + local make_display = function(entry) + return displayer { + entry.value.prompt_title, + entry.value.default_text, + } + end + + return function(entry) + return make_entry.set_default_entry_mt({ + value = entry, + text = entry.prompt_title, + ordinal = string.format("%s %s", entry.prompt_title, vim.F.if_nil(entry.default_text, "")), + display = make_display, + }, opts) + end +end + +function make_entry.gen_from_buffer_lines(opts) + local displayer = entry_display.create { + separator = " │ ", + items = { + { width = 5 }, + { remaining = true }, + }, + } + + local make_display = function(entry) + return displayer { + { entry.lnum, opts.lnum_highlight_group or "TelescopeResultsSpecialComment" }, + { + entry.text, + function() + if not opts.line_highlights then + return {} + end + + local line_hl = opts.line_highlights[entry.lnum] or {} + -- TODO: We could probably squash these together if the are the same... + -- But I don't think that it's worth it at the moment. + local result = {} + + for col, hl in pairs(line_hl) do + table.insert(result, { { col, col + 1 }, hl }) + end + + return result + end, + }, + } + end + + return function(entry) + if opts.skip_empty_lines and string.match(entry.text, "^$") then + return + end + + return make_entry.set_default_entry_mt({ + ordinal = entry.text, + display = make_display, + filename = entry.filename, + lnum = entry.lnum, + text = entry.text, + }, opts) + end +end + +function make_entry.gen_from_vimoptions(opts) + local displayer = entry_display.create { + separator = "", + hl_chars = { ["["] = "TelescopeBorder", ["]"] = "TelescopeBorder" }, + items = { + { width = 25 }, + { width = 12 }, + { width = 11 }, + { remaining = true }, + }, + } + + local make_display = function(entry) + return displayer { + { entry.value.name, "Keyword" }, + { "[" .. entry.value.type .. "]", "Type" }, + { "[" .. entry.value.scope .. "]", "Identifier" }, + utils.display_termcodes(tostring(entry.value.value)), + } + end + + return function(o) + local entry = { + display = make_display, + value = { + name = o.name, + value = o.value, + type = o.type, + scope = o.scope, + }, + ordinal = string.format("%s %s %s %s", o.name, o.type, o.scope, utils.display_termcodes(tostring(o.value))), + } + + return make_entry.set_default_entry_mt(entry, opts) + end +end + +function make_entry.gen_from_ctags(opts) + opts = opts or {} + + local show_kind = vim.F.if_nil(opts.show_kind, true) + local cwd = utils.path_expand(opts.cwd or vim.loop.cwd()) + local current_file = Path:new(vim.api.nvim_buf_get_name(opts.bufnr)):normalize(cwd) + + local display_items = { + { width = 16 }, + { remaining = true }, + } + + local idx = 1 + local hidden = utils.is_path_hidden(opts) + if not hidden then + table.insert(display_items, idx, { width = vim.F.if_nil(opts.fname_width, 30) }) + idx = idx + 1 + end + + if opts.show_line then + table.insert(display_items, idx, { width = 30 }) + end + + local displayer = entry_display.create { + separator = " │ ", + items = display_items, + } + + local make_display = function(entry) + local display_path, path_style = utils.transform_path(opts, entry.filename) + + local scode + if opts.show_line then + scode = entry.scode + end + + if hidden then + return displayer { + entry.tag, + scode, + } + else + return displayer { + { + display_path, + function() + return path_style + end, + }, + entry.tag, + entry.kind, + scode, + } + end + end + + local mt = {} + mt.__index = function(t, k) + local override = handle_entry_index(opts, t, k) + if override then + return override + end + + if k == "path" then + local retpath = Path:new({ t.filename }):absolute() + if not vim.loop.fs_access(retpath, "R") then + retpath = t.filename + end + return retpath + end + end + + local current_file_cache = {} + return function(line) + if line == "" or line:sub(1, 1) == "!" then + return nil + end + + local tag, file, scode, lnum, extension_fields + -- ctags gives us: 'tags\tfile\tsource;"extension_fields' + tag, file, scode, extension_fields = string.match(line, '([^\t]+)\t([^\t]+)\t/^?\t?(.*)/;"\t+(.*)') + if not tag then + -- hasktags gives us: 'tags\tfile\tlnum' + tag, file, lnum = string.match(line, "([^\t]+)\t([^\t]+)\t(%d+).*") + end + local kind = string.match(extension_fields or "", "kind:(%S+)") + + if Path.path.sep == "\\" then + file = string.gsub(file, "/", "\\") + end + + if opts.only_current_file then + if current_file_cache[file] == nil then + current_file_cache[file] = Path:new(file):normalize(cwd) == current_file + end + + if current_file_cache[file] == false then + return nil + end + end + + local tag_entry = {} + if opts.only_sort_tags then + tag_entry.ordinal = tag + else + tag_entry.ordinal = file .. ": " .. tag + end + + tag_entry.display = make_display + tag_entry.scode = scode + tag_entry.tag = tag + tag_entry.filename = file + tag_entry.col = 1 + tag_entry.lnum = lnum and tonumber(lnum) or 1 + if show_kind then + tag_entry.kind = kind + end + + return setmetatable(tag_entry, mt) + end +end + +function make_entry.gen_from_diagnostics(opts) + opts = opts or {} + + local type_diagnostic = vim.diagnostic.severity + local signs = (function() + if opts.no_sign then + return + end + local signs = {} + for _, severity in ipairs(type_diagnostic) do + local status, sign = pcall(function() + -- only the first char is upper all others are lowercalse + return vim.trim(vim.fn.sign_getdefined("DiagnosticSign" .. severity:lower():gsub("^%l", string.upper))[1].text) + end) + if not status then + sign = severity:sub(1, 1) + end + signs[severity] = sign + end + return signs + end)() + + local sign_width + if opts.disable_coordinates then + sign_width = signs ~= nil and 2 or 0 + else + sign_width = signs ~= nil and 10 or 8 + end + + local display_items = { + { width = sign_width }, + { remaining = true }, + } + local line_width = vim.F.if_nil(opts.line_width, 0.5) + local line_width_opts = { width = line_width } + if type(line_width) == "string" and line_width == "full" then + line_width_opts = {} + end + local hidden = utils.is_path_hidden(opts) + if not hidden then + table.insert(display_items, 2, line_width_opts) + end + local displayer = entry_display.create { + separator = "▏", + items = display_items, + } + + local make_display = function(entry) + local display_path, path_style = utils.transform_path(opts, entry.filename) + + -- add styling of entries + local pos = string.format("%4d:%2d", entry.lnum, entry.col) + local line_info_text = signs and signs[entry.type] .. " " or "" + local line_info = { + opts.disable_coordinates and line_info_text or line_info_text .. pos, + "DiagnosticSign" .. entry.type, + } + + return displayer { + line_info, + entry.text, + { + display_path, + function() + return path_style + end, + }, + } + end + + local errlist_type_map = { + [type_diagnostic.ERROR] = "E", + [type_diagnostic.WARN] = "W", + [type_diagnostic.INFO] = "I", + [type_diagnostic.HINT] = "N", + } + + return function(entry) + return make_entry.set_default_entry_mt({ + value = entry, + ordinal = ("%s %s"):format(not hidden and entry.filename or "", entry.text), + display = make_display, + filename = entry.filename, + type = entry.type, + lnum = entry.lnum, + col = entry.col, + text = entry.text, + qf_type = errlist_type_map[type_diagnostic[entry.type]], + }, opts) + end +end + +function make_entry.gen_from_autocommands(opts) + local displayer = entry_display.create { + separator = "▏", + items = { + { width = 14 }, + { width = 18 }, + { width = 16 }, + { remaining = true }, + }, + } + + local make_display = function(entry) + return displayer { + { entry.value.event, "vimAutoEvent" }, + { entry.value.group_name, "vimAugroup" }, + { entry.value.pattern, "vimAutoCmdSfxList" }, + entry.value.command, + } + end + + return function(entry) + local group_name = vim.F.if_nil(entry.group_name, "") + local command = entry.command + if entry.desc and (entry.callback or vim.startswith(command, " +--- { +--- mode = { ..keys } +--- } +--- +--- +--- where {mode} is the one character letter for a mode ('i' for insert, 'n' for normal). +--- +--- For example: +--- +--- mappings = { +--- i = { +--- [""] = require('telescope.actions').close, +--- }, +--- } +--- +--- +--- To disable a keymap, put `[map] = false`
+--- For example: +--- +--- { +--- ..., +--- [""] = false, +--- ..., +--- } +--- +--- +--- To override behavior of a key, simply set the value +--- to be a function (either by requiring an action or by writing +--- your own function) +--- +--- { +--- ..., +--- [""] = require('telescope.actions').select_default, +--- ..., +--- } +--- +--- +--- If the function you want is part of `telescope.actions`, then you can +--- simply supply the function name as a string. +--- For example, the previous option is equivalent to: +--- +--- { +--- ..., +--- [""] = "select_default", +--- ..., +--- } +--- +--- +--- You can also add other mappings using tables with `type = "command"`. +--- For example: +--- +--- { +--- ..., +--- ["jj"] = { "", type = "command" }, +--- ["kk"] = { "echo \"Hello, World!\"", type = "command" },) +--- ..., +--- } +--- +--- +--- You can also add additional options for mappings of any type ("action" and "command"). +--- For example: +--- +--- { +--- ..., +--- [""] = { +--- actions.move_selection_next, type = "action", +--- opts = { nowait = true, silent = true } +--- }, +--- ..., +--- } +--- +--- +--- There are three main places you can configure |telescope.mappings|. These are +--- ordered from the lowest priority to the highest priority. +--- +--- 1. |telescope.defaults.mappings| +--- 2. In the |telescope.setup()| table, inside a picker with a given name, use the `mappings` key +--- +--- require("telescope").setup { +--- pickers = { +--- find_files = { +--- mappings = { +--- n = { +--- ["kj"] = "close", +--- }, +--- }, +--- }, +--- }, +--- } +--- +--- 3. `attach_mappings` function for a particular picker. +--- +--- require("telescope.builtin").find_files { +--- attach_mappings = function(_, map) +--- map("i", "asdf", function(_prompt_bufnr) +--- print "You typed asdf" +--- end) +--- +--- map({"i", "n"}, "", function(_prompt_bufnr) +--- print "You typed " +--- end, { desc = "desc for which key"}) +--- +--- -- needs to return true if you want to map default_mappings and +--- -- false if not +--- return true +--- end, +--- } +--- +---@brief ]] + +local a = vim.api + +local actions = require "telescope.actions" +local config = require "telescope.config" + +local mappings = {} + +mappings.default_mappings = config.values.default_mappings + or { + i = { + [""] = { + actions.mouse_click, + type = "action", + opts = { expr = true }, + }, + ["<2-LeftMouse>"] = { + actions.double_mouse_click, + type = "action", + opts = { expr = true }, + }, + + [""] = actions.move_selection_next, + [""] = actions.move_selection_previous, + + [""] = actions.close, + + [""] = actions.move_selection_next, + [""] = actions.move_selection_previous, + + [""] = actions.select_default, + [""] = actions.select_horizontal, + [""] = actions.select_vertical, + [""] = actions.select_tab, + + [""] = actions.preview_scrolling_up, + [""] = actions.preview_scrolling_down, + [""] = actions.preview_scrolling_left, + [""] = actions.preview_scrolling_right, + + [""] = actions.results_scrolling_up, + [""] = actions.results_scrolling_down, + [""] = actions.results_scrolling_left, + [""] = actions.results_scrolling_right, + + [""] = actions.toggle_selection + actions.move_selection_worse, + [""] = actions.toggle_selection + actions.move_selection_better, + [""] = actions.send_to_qflist + actions.open_qflist, + [""] = actions.send_selected_to_qflist + actions.open_qflist, + [""] = actions.complete_tag, + [""] = actions.which_key, + [""] = actions.which_key, -- keys from pressing + [""] = { "", type = "command" }, + [""] = actions.insert_original_cword, + [""] = actions.insert_original_cWORD, + [""] = actions.insert_original_cfile, + [""] = actions.insert_original_cline, + + -- disable c-j because we dont want to allow new lines #2123 + [""] = actions.nop, + }, + n = { + [""] = { + actions.mouse_click, + type = "action", + opts = { expr = true }, + }, + ["<2-LeftMouse>"] = { + actions.double_mouse_click, + type = "action", + opts = { expr = true }, + }, + + [""] = actions.close, + [""] = actions.select_default, + [""] = actions.select_horizontal, + [""] = actions.select_vertical, + [""] = actions.select_tab, + + [""] = actions.toggle_selection + actions.move_selection_worse, + [""] = actions.toggle_selection + actions.move_selection_better, + [""] = actions.send_to_qflist + actions.open_qflist, + [""] = actions.send_selected_to_qflist + actions.open_qflist, + + -- TODO: This would be weird if we switch the ordering. + ["j"] = actions.move_selection_next, + ["k"] = actions.move_selection_previous, + ["H"] = actions.move_to_top, + ["M"] = actions.move_to_middle, + ["L"] = actions.move_to_bottom, + + [""] = actions.move_selection_next, + [""] = actions.move_selection_previous, + ["gg"] = actions.move_to_top, + ["G"] = actions.move_to_bottom, + + [""] = actions.preview_scrolling_up, + [""] = actions.preview_scrolling_down, + [""] = actions.preview_scrolling_left, + [""] = actions.preview_scrolling_right, + + [""] = actions.results_scrolling_up, + [""] = actions.results_scrolling_down, + [""] = actions.results_scrolling_left, + [""] = actions.results_scrolling_right, + + ["?"] = actions.which_key, + }, + } + +-- normal names are prefixed with telescope| +-- encoded objects are prefixed with telescopej| +---@param key_func table|fun() +---@param opts table +---@return string? +local get_desc_for_keyfunc = function(key_func, opts) + if opts and opts.desc then + return "telescope|" .. opts.desc + end + + if type(key_func) == "table" then + local name = "" + for _, action in ipairs(key_func) do + if type(action) == "string" then + name = name == "" and action or name .. " + " .. action + end + end + return "telescope|" .. name + elseif type(key_func) == "function" then + local info = debug.getinfo(key_func) + return "telescopej|" .. vim.json.encode { source = info.source, linedefined = info.linedefined } + end +end + +local telescope_map = function(prompt_bufnr, mode, key_bind, key_func, opts) + if not key_func then + return + end + + opts = opts or {} + if opts.noremap == nil then + opts.noremap = true + end + if opts.silent == nil then + opts.silent = true + end + + if type(key_func) == "string" then + key_func = actions[key_func] + elseif type(key_func) == "table" then + if key_func.type == "command" then + vim.keymap.set( + mode, + key_bind, + key_func[1], + vim.tbl_extend("force", opts or { + silent = true, + }, { buffer = prompt_bufnr }) + ) + return + elseif key_func.type == "action_key" then + key_func = actions[key_func[1]] + elseif key_func.type == "action" then + key_func = key_func[1] + end + end + + vim.keymap.set(mode, key_bind, function() + local ret = key_func(prompt_bufnr) + vim.api.nvim_exec_autocmds("User", { pattern = "TelescopeKeymap" }) + return ret + end, vim.tbl_extend("force", opts, { buffer = prompt_bufnr, desc = get_desc_for_keyfunc(key_func, opts) })) +end + +local extract_keymap_opts = function(key_func) + if type(key_func) == "table" and key_func.opts ~= nil then + -- we can't clear this because key_func could be a table from the config. + -- If we clear it the table ref would lose opts after the first bind + -- We need to copy it so noremap and silent won't be part of the table ref after the first bind + return vim.deepcopy(key_func.opts) + end + return {} +end + +mappings.apply_keymap = function(prompt_bufnr, attach_mappings, buffer_keymap) + local applied_mappings = { n = {}, i = {} } + + local map = function(modes, key_bind, key_func, opts) + if type(modes) == "string" then + modes = { modes } + end + + for _, mode in pairs(modes) do + mode = string.lower(mode) + local key_bind_internal = a.nvim_replace_termcodes(key_bind, true, true, true) + applied_mappings[mode][key_bind_internal] = true + + telescope_map(prompt_bufnr, mode, key_bind, key_func, opts) + end + end + + if attach_mappings then + local attach_results = attach_mappings(prompt_bufnr, map) + + if attach_results == nil then + error( + "Attach mappings must always return a value. `true` means use default mappings, " + .. "`false` means only use attached mappings" + ) + end + + if not attach_results then + return + end + end + + for mode, mode_map in pairs(buffer_keymap or {}) do + mode = string.lower(mode) + + for key_bind, key_func in pairs(mode_map) do + local key_bind_internal = a.nvim_replace_termcodes(key_bind, true, true, true) + if not applied_mappings[mode][key_bind_internal] then + applied_mappings[mode][key_bind_internal] = true + telescope_map(prompt_bufnr, mode, key_bind, key_func, extract_keymap_opts(key_func)) + end + end + end + + -- TODO: Probably should not overwrite any keymaps + for mode, mode_map in pairs(mappings.default_mappings) do + mode = string.lower(mode) + + for key_bind, key_func in pairs(mode_map) do + local key_bind_internal = a.nvim_replace_termcodes(key_bind, true, true, true) + if not applied_mappings[mode][key_bind_internal] then + applied_mappings[mode][key_bind_internal] = true + telescope_map(prompt_bufnr, mode, key_bind, key_func, extract_keymap_opts(key_func)) + end + end + end +end + +return mappings diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/operators.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/operators.lua new file mode 100644 index 0000000..e7ee8b5 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/operators.lua @@ -0,0 +1,23 @@ +local operators = {} + +local last_operator = { callback = function(_) end, opts = {} } + +--- Execute the last saved operator callback and options +operators.operator_callback = function() + last_operator.callback(last_operator.opts) +end + +--- Enters operator-pending mode, then executes callback. +--- See `:h map-operator` +--- +---@param callback function: the function to call after exiting operator-pending +---@param opts table: options to pass to the callback +operators.run_operator = function(callback, opts) + last_operator = { callback = callback, opts = opts } + vim.o.operatorfunc = "v:lua.require'telescope.operators'.operator_callback" + -- feed g@ to enter operator-pending mode + -- 'i' required for which-key compatibility, etc. + vim.api.nvim_feedkeys("g@", "mi", false) +end + +return operators diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers.lua new file mode 100644 index 0000000..b353c8a --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers.lua @@ -0,0 +1,1729 @@ +require "telescope" + +local a = vim.api + +local async = require "plenary.async" +local await_schedule = async.util.scheduler +local channel = require("plenary.async.control").channel +local popup = require "plenary.popup" + +local actions = require "telescope.actions" +local config = require "telescope.config" +local debounce = require "telescope.debounce" +local deprecated = require "telescope.deprecated" +local log = require "telescope.log" +local mappings = require "telescope.mappings" +local state = require "telescope.state" +local utils = require "telescope.utils" + +local entry_display = require "telescope.pickers.entry_display" +local p_highlighter = require "telescope.pickers.highlights" +local p_scroller = require "telescope.pickers.scroller" +local p_window = require "telescope.pickers.window" +local Layout = require "telescope.pickers.layout" + +local EntryManager = require "telescope.entry_manager" +local MultiSelect = require "telescope.pickers.multi" + +local truncate = require("plenary.strings").truncate +local strdisplaywidth = require("plenary.strings").strdisplaywidth + +local ns_telescope_matching = a.nvim_create_namespace "telescope_matching" +local ns_telescope_prompt = a.nvim_create_namespace "telescope_prompt" +local ns_telescope_prompt_prefix = a.nvim_create_namespace "telescope_prompt_prefix" + +---@class telescope_popup_options +---@field border table<1|2|3|4, integer> +---@field borderchars table<1|2|3|4|5|6|7|8, string> +---@field borderhighlight string +---@field col integer +---@field enter boolean +---@field height integer +---@field highlight string +---@field line integer +---@field minheight integer +---@field title integer +---@field titlehighlight integer +---@field width integer + +-- Create three windows: +-- 1. Prompt window +-- 2. Options window +-- 3. Preview window +-- +---@param picker Picker +local function default_create_layout(picker) + local function make_border(border) + if not border then + return nil + end + border.winid = border.win_id + return border + end + + local layout = Layout { + picker = picker, + ---@param self TelescopeLayout + mount = function(self) + local line_count = vim.o.lines - vim.o.cmdheight + if vim.o.laststatus ~= 0 then + line_count = line_count - 1 + end + + local popup_opts = picker:get_window_options(vim.o.columns, line_count) + + -- `popup.nvim` massaging so people don't have to remember minheight shenanigans + popup_opts.results.focusable = true + popup_opts.results.minheight = popup_opts.results.height + popup_opts.results.highlight = "TelescopeResultsNormal" + popup_opts.results.borderhighlight = "TelescopeResultsBorder" + popup_opts.results.titlehighlight = "TelescopeResultsTitle" + popup_opts.prompt.minheight = popup_opts.prompt.height + popup_opts.prompt.highlight = "TelescopePromptNormal" + popup_opts.prompt.borderhighlight = "TelescopePromptBorder" + popup_opts.prompt.titlehighlight = "TelescopePromptTitle" + + if popup_opts.preview then + popup_opts.preview.focusable = true + popup_opts.preview.minheight = popup_opts.preview.height + popup_opts.preview.highlight = "TelescopePreviewNormal" + popup_opts.preview.borderhighlight = "TelescopePreviewBorder" + popup_opts.preview.titlehighlight = "TelescopePreviewTitle" + end + + local results_win, results_opts = picker:_create_window("", popup_opts.results) + local results_bufnr = a.nvim_win_get_buf(results_win) + + self.results = Layout.Window { + winid = results_win, + bufnr = results_bufnr, + border = make_border(results_opts.border), + } + + if popup_opts.preview then + local preview_win, preview_opts = picker:_create_window("", popup_opts.preview) + local preview_bufnr = a.nvim_win_get_buf(preview_win) + + self.preview = Layout.Window { + winid = preview_win, + bufnr = preview_bufnr, + border = make_border(preview_opts.border), + } + end + + local prompt_win, prompt_opts = picker:_create_window("", popup_opts.prompt) + local prompt_bufnr = a.nvim_win_get_buf(prompt_win) + + self.prompt = Layout.Window { + winid = prompt_win, + bufnr = prompt_bufnr, + border = make_border(prompt_opts.border), + } + end, + ---@param self TelescopeLayout + unmount = function(self) + utils.win_delete("results_win", self.results.winid, true, true) + if self.preview then + utils.win_delete("preview_win", self.preview.winid, true, true) + end + + utils.win_delete("prompt_border_win", self.prompt.border.winid, true, true) + utils.win_delete("results_border_win", self.results.border.winid, true, true) + if self.preview then + utils.win_delete("preview_border_win", self.preview.border.winid, true, true) + end + + -- we cant use win_delete. We first need to close and then delete the buffer + if vim.api.nvim_win_is_valid(self.prompt.winid) then + vim.api.nvim_win_close(self.prompt.winid, true) + end + vim.schedule(function() + utils.buf_delete(self.prompt.bufnr) + end) + end, + ---@param self TelescopeLayout + update = function(self) + local line_count = vim.o.lines - vim.o.cmdheight + if vim.o.laststatus ~= 0 then + line_count = line_count - 1 + end + + local popup_opts = picker:get_window_options(vim.o.columns, line_count) + -- `popup.nvim` massaging so people don't have to remember minheight shenanigans + popup_opts.results.minheight = popup_opts.results.height + popup_opts.prompt.minheight = popup_opts.prompt.height + if popup_opts.preview then + popup_opts.preview.minheight = popup_opts.preview.height + end + + local prompt_win = self.prompt.winid + local results_win = self.results.winid + local preview_win = self.preview and self.preview.winid + + local preview_opts + if popup_opts.preview then + if preview_win ~= nil then + -- Move all popups at the same time + popup.move(prompt_win, popup_opts.prompt) + popup.move(results_win, popup_opts.results) + popup.move(preview_win, popup_opts.preview) + else + popup_opts.preview.focusable = true + popup_opts.preview.highlight = "TelescopePreviewNormal" + popup_opts.preview.borderhighlight = "TelescopePreviewBorder" + popup_opts.preview.titlehighlight = "TelescopePreviewTitle" + local preview_bufnr = (self.preview and self.preview.bufnr ~= nil) + and vim.api.nvim_buf_is_valid(self.preview.bufnr) + and self.preview.bufnr + or "" + preview_win, preview_opts = picker:_create_window(preview_bufnr, popup_opts.preview) + if preview_bufnr == "" then + preview_bufnr = a.nvim_win_get_buf(preview_win) + end + self.preview = Layout.Window { + winid = preview_win, + bufnr = preview_bufnr, + border = make_border(preview_opts.border), + } + if picker.previewer and picker.previewer.state and picker.previewer.state.winid then + picker.previewer.state.winid = preview_win + end + + -- Move prompt and results after preview created + vim.defer_fn(function() + popup.move(prompt_win, popup_opts.prompt) + popup.move(results_win, popup_opts.results) + end, 0) + end + elseif preview_win ~= nil then + popup.move(prompt_win, popup_opts.prompt) + popup.move(results_win, popup_opts.results) + + -- Remove preview after the prompt and results are moved + vim.defer_fn(function() + utils.win_delete("preview_win", preview_win, true) + utils.win_delete("preview_win", self.preview.border.winid, true) + self.preview = nil + end, 0) + else + popup.move(prompt_win, popup_opts.prompt) + popup.move(results_win, popup_opts.results) + end + end, + } + + return layout +end + +local pickers = {} + +-- TODO: Add overscroll option for results buffer + +---@class Picker +--- Picker is the main UI that shows up to interact w/ your results. +-- Takes a filter & a previewer +local Picker = {} +Picker.__index = Picker + +--- Create new picker +function Picker:new(opts) + opts = opts or {} + + if opts.layout_strategy and opts.get_window_options then + error "layout_strategy and get_window_options are not compatible keys" + end + + if vim.fn.win_gettype() == "command" then + error "Can't open telescope from command-line window. See E11" + end + + deprecated.options(opts) + + -- We need to clear at the beginning not on close because after close we can still have select:post + -- etc ... + require("telescope.actions.mt").clear_all() + -- TODO(conni2461): This seems like the better solution but it won't clear actions that were never mapped + -- for _, v in ipairs(keymap_store[prompt_bufnr]) do + -- pcall(v.clear) + -- end + + local layout_strategy = vim.F.if_nil(opts.layout_strategy, config.values.layout_strategy) + local winblend = + vim.F.if_nil(opts.winblend, type(opts.window) == "table" and opts.window.winblend or config.values.winblend) + if type(winblend) == "function" then + winblend = winblend() + end + + local obj = setmetatable({ + prompt_title = vim.F.if_nil(opts.prompt_title, config.values.prompt_title), + results_title = vim.F.if_nil(opts.results_title, config.values.results_title), + -- either whats passed in by the user or whats defined by the previewer + preview_title = opts.preview_title, + + prompt_prefix = vim.F.if_nil(opts.prompt_prefix, config.values.prompt_prefix), + wrap_results = vim.F.if_nil(opts.wrap_results, config.values.wrap_results), + selection_caret = vim.F.if_nil(opts.selection_caret, config.values.selection_caret), + entry_prefix = vim.F.if_nil(opts.entry_prefix, config.values.entry_prefix), + multi_icon = vim.F.if_nil(opts.multi_icon, config.values.multi_icon), + + initial_mode = vim.F.if_nil(opts.initial_mode, config.values.initial_mode), + _original_mode = vim.api.nvim_get_mode().mode, + debounce = vim.F.if_nil(tonumber(opts.debounce), nil), + + _finder_attached = true, + default_text = opts.default_text, + get_status_text = vim.F.if_nil(opts.get_status_text, config.values.get_status_text), + _on_input_filter_cb = opts.on_input_filter_cb or function() end, + + finder = assert(opts.finder, "Finder is required."), + sorter = opts.sorter or require("telescope.sorters").empty(), + + all_previewers = opts.previewer, + current_previewer_index = opts.current_previewer_index or 1, + + default_selection_index = opts.default_selection_index, + + get_selection_window = vim.F.if_nil(opts.get_selection_window, config.values.get_selection_window), + + cwd = opts.cwd, + + _find_id = 0, + _completion_callbacks = type(opts._completion_callbacks) == "table" and opts._completion_callbacks or {}, + manager = (type(opts.manager) == "table" and getmetatable(opts.manager) == EntryManager) and opts.manager, + _multi = (type(opts._multi) == "table" and getmetatable(opts._multi) == getmetatable(MultiSelect:new())) + and opts._multi + or MultiSelect:new(), + + track = vim.F.if_nil(opts.track, false), + stats = {}, + + attach_mappings = opts.attach_mappings, + file_ignore_patterns = vim.F.if_nil(opts.file_ignore_patterns, config.values.file_ignore_patterns), + + scroll_strategy = vim.F.if_nil(opts.scroll_strategy, config.values.scroll_strategy), + sorting_strategy = vim.F.if_nil(opts.sorting_strategy, config.values.sorting_strategy), + tiebreak = vim.F.if_nil(opts.tiebreak, config.values.tiebreak), + selection_strategy = vim.F.if_nil(opts.selection_strategy, config.values.selection_strategy), + + push_cursor_on_edit = vim.F.if_nil(opts.push_cursor_on_edit, false), + push_tagstack_on_edit = vim.F.if_nil(opts.push_tagstack_on_edit, false), + + layout_strategy = layout_strategy, + layout_config = config.smarter_depth_2_extend(opts.layout_config or {}, config.values.layout_config or {}), + + __cycle_layout_list = vim.F.if_nil(opts.cycle_layout_list, config.values.cycle_layout_list), + + window = { + winblend = winblend, + border = vim.F.if_nil(opts.border, type(opts.window) == "table" and opts.window.border or config.values.border), + borderchars = vim.F.if_nil( + opts.borderchars, + type(opts.window) == "table" and opts.window.borderchars or config.values.borderchars + ), + }, + + cache_picker = config.resolve_table_opts(opts.cache_picker, vim.deepcopy(config.values.cache_picker)), + + __scrolling_limit = tonumber(vim.F.if_nil(opts.temp__scrolling_limit, 250)), + + __locations_input = vim.F.if_nil(opts.__locations_input, false), + }, self) + + obj.create_layout = opts.create_layout or config.values.create_layout or default_create_layout + obj.get_window_options = opts.get_window_options or p_window.get_window_options + + if obj.all_previewers ~= nil and obj.all_previewers ~= false then + if obj.all_previewers[1] == nil then + obj.all_previewers = { obj.all_previewers } + end + obj.previewer = obj.all_previewers[obj.current_previewer_index] + if + obj.preview_title == nil + or #obj.all_previewers > 1 + or opts.resumed_picker and opts.fix_preview_title ~= true + then + obj.preview_title = obj.previewer:title(nil, config.values.dynamic_preview_title) + else + obj.fix_preview_title = true + end + else + obj.previewer = false + end + + local __hide_previewer = opts.__hide_previewer + if __hide_previewer then + obj.hidden_previewer = obj.previewer + obj.previewer = nil + else + obj.hidden_previewer = nil + end + + -- TODO: It's annoying that this is create and everything else is "new" + obj.scroller = p_scroller.create(obj.scroll_strategy, obj.sorting_strategy) + + obj.highlighter = p_highlighter.new(obj) + + if opts.on_complete then + for _, on_complete_item in ipairs(opts.on_complete) do + obj:register_completion_callback(on_complete_item) + end + end + + return obj +end + +--- Take an index and get a row. +---@note: Rows are 0-indexed, and `index` is 1 indexed (table index) +---@param index number: the index in line_manager +---@return number: the row for the picker to display in +function Picker:get_row(index) + if self.sorting_strategy == "ascending" then + return index - 1 + else + return self.max_results - index + end +end + +--- Take a row and get an index +---@note: Rows are 0-indexed, and `index` is 1 indexed (table index) +---@param row number: The row being displayed +---@return number: The index in line_manager +function Picker:get_index(row) + if self.sorting_strategy == "ascending" then + return row + 1 + else + return self.max_results - row + end +end + +--- Get the row number of the "best" entry +---@return number: the number of the "reset" row +function Picker:get_reset_row() + if self.sorting_strategy == "ascending" then + return 0 + else + return self.max_results - 1 + end +end + +--- Check if the picker is no longer in use +---@return boolean|nil: `true` if picker is closed, `nil` otherwise +function Picker:is_done() + if not self.manager then + return true + end +end + +--- Clear rows that are after the final remaining entry +---@note: useful when number of remaining results is narrowed down +---@param results_bufnr number: the buffer number of the results buffer +function Picker:clear_extra_rows(results_bufnr) + if self:is_done() then + log.trace "Not clearing due to being already complete" + return + end + + if not vim.api.nvim_buf_is_valid(results_bufnr) then + log.debug("Invalid results_bufnr for clearing:", results_bufnr) + return + end + + local worst_line, ok, msg + if self.sorting_strategy == "ascending" then + local num_results = self.manager:num_results() + worst_line = math.min(num_results, self.max_results) + ok, msg = pcall(vim.api.nvim_buf_set_lines, results_bufnr, worst_line, -1, false, {}) + else + worst_line = self:get_row(self.manager:num_results()) + if worst_line <= 0 then + return + end + + local empty_lines = utils.repeated_table(worst_line, "") + ok, msg = pcall(vim.api.nvim_buf_set_lines, results_bufnr, 0, worst_line, false, empty_lines) + end + + if not ok then + log.debug("Failed to set lines:", msg) + end + + log.trace("Clearing:", worst_line) +end + +--- Highlight the entry corresponding to the given row +---@param results_bufnr number: the buffer number of the results buffer +---@param prompt table: table with information about the prompt buffer +---@param display string: the text corresponding to the given row +---@param row number: the number of the chosen row +function Picker:highlight_one_row(results_bufnr, prompt, display, row) + if not self.sorter.highlighter then + return + end + + local highlights = self.sorter:highlighter(prompt, display) + + if highlights then + for _, hl in ipairs(highlights) do + local highlight, start, finish + if type(hl) == "table" then + highlight = hl.highlight or "TelescopeMatching" + start = hl.start + finish = hl.finish or hl.start + elseif type(hl) == "number" then + highlight = "TelescopeMatching" + start = hl + finish = hl + else + error "Invalid higlighter fn" + end + + self:_increment "highlights" + + vim.api.nvim_buf_add_highlight(results_bufnr, ns_telescope_matching, highlight, row, start - 1, finish) + end + end + + local entry = self.manager:get_entry(self:get_index(row)) + self.highlighter:hi_multiselect(row, self:is_multi_selected(entry)) +end + +--- Check if the given row number can be selected +---@param row number: the number of the chosen row in the results buffer +---@return boolean +function Picker:can_select_row(row) + if self.sorting_strategy == "ascending" then + return row <= self.manager:num_results() and row < self.max_results + else + return row >= 0 and row <= self.max_results and row >= self.max_results - self.manager:num_results() + end +end + +--TODO: document what `find_id` is for +function Picker:_next_find_id() + local find_id = self._find_id + 1 + self._find_id = find_id + + return find_id +end + +--- A helper function for creating each of the windows in a picker +---@param bufnr number: the buffer number to be used in the window +---@param popup_opts table: options to pass to `popup.create` +function Picker:_create_window(bufnr, popup_opts) + local what = bufnr or "" + local win, opts = popup.create(what, popup_opts) + + a.nvim_win_set_option(win, "winblend", self.window.winblend) + local border_win = opts and opts.border and opts.border.win_id + if border_win then + a.nvim_win_set_option(border_win, "winblend", self.window.winblend) + end + return win, opts, border_win +end + +--- Opens the given picker for the user to interact with +---@note: this is the main function for pickers, as it actually creates the interface for users +function Picker:find() + self:close_existing_pickers() + self:reset_selection() + + self.__original_mousemoveevent = vim.o.mousemoveevent + vim.o.mousemoveevent = true + + self.original_bufnr = a.nvim_get_current_buf() + self.original_win_id = a.nvim_get_current_win() + self.original_tabpage = a.nvim_get_current_tabpage() + _, self.original_cword = pcall(vim.fn.expand, "") + _, self.original_cWORD = pcall(vim.fn.expand, "") + _, self.original_cfile = pcall(vim.fn.expand, "") + _, self.original_cline = pcall(vim.api.nvim_get_current_line) + _, self.original_cline = pcall(vim.trim, self.original_cline) + + -- User autocmd run it before create Telescope window + vim.api.nvim_exec_autocmds("User", { pattern = "TelescopeFindPre" }) + + local layout = self:create_layout() + layout:mount() + + self.layout = layout + self.prompt_win, self.prompt_bufnr, self.prompt_border = + layout.prompt.winid, layout.prompt.bufnr, layout.prompt.border + self.results_win, self.results_bufnr, self.results_border = + layout.results.winid, layout.results.bufnr, layout.results.border + if layout.preview then + self.preview_win, self.preview_bufnr, self.preview_border = + layout.preview.winid, layout.preview.bufnr, layout.preview.border + else + self.preview_win, self.preview_bufnr, self.preview_border = nil, nil, nil + end + + pcall(a.nvim_buf_set_option, self.results_bufnr, "tabstop", 1) -- #1834 + pcall(a.nvim_buf_set_option, self.prompt_bufnr, "tabstop", 1) -- #1834 + a.nvim_buf_set_option(self.prompt_bufnr, "buftype", "prompt") + a.nvim_win_set_option(self.results_win, "wrap", self.wrap_results) + a.nvim_win_set_option(self.prompt_win, "wrap", true) + if self.preview_win then + a.nvim_win_set_option(self.preview_win, "wrap", true) + end + + -- Prompt prefix + local prompt_prefix = self.prompt_prefix + vim.fn.prompt_setprompt(self.prompt_bufnr, prompt_prefix) + self:_reset_prefix_color() + + -- TODO: This could be configurable in the future, but I don't know why you would + -- want to scroll through more than 10,000 items. + -- + -- This just lets us stop doing stuff after tons of things. + self.max_results = self.__scrolling_limit + + vim.api.nvim_buf_set_lines(self.results_bufnr, 0, self.max_results, false, utils.repeated_table(self.max_results, "")) + + local status_updater = self:get_status_updater(self.prompt_win, self.prompt_bufnr) + local debounced_status = debounce.throttle_leading(status_updater, 50) + + local tx, rx = channel.mpsc() + self._on_lines = tx.send + + local find_id = self:_next_find_id() + + if self.default_text then + self:set_prompt(self.default_text) + end + + if vim.tbl_contains({ "insert", "normal" }, self.initial_mode) then + local mode = vim.fn.mode() + local keys + if self.initial_mode == "normal" then + -- n: A makes sure cursor is at always at end of prompt w/o default_text + keys = mode ~= "n" and "A" or "A" + else + -- always fully retrigger insert mode: required for going from one picker to next + keys = mode ~= "n" and "A" or "A" + end + a.nvim_feedkeys(a.nvim_replace_termcodes(keys, true, false, true), "ni", true) + else + utils.notify("pickers.find", { + msg = "`initial_mode` should be one of ['normal', 'insert'] but passed " .. self.initial_mode, + level = "ERROR", + }) + end + + local main_loop = async.void(function() + self.sorter:_init() + + -- Do filetype last, so that users can register at the last second. + pcall(a.nvim_buf_set_option, self.prompt_bufnr, "filetype", "TelescopePrompt") + pcall(a.nvim_buf_set_option, self.results_bufnr, "filetype", "TelescopeResults") + + await_schedule() + + while true do + -- Wait for the next input + rx.last() + await_schedule() + + self:_reset_track() + + if not vim.api.nvim_buf_is_valid(self.prompt_bufnr) then + log.debug("ON_LINES: Invalid prompt_bufnr", self.prompt_bufnr) + return + end + + -- we kinda always wanna reset the color, because of `cc` and `dd` commands, + -- which also delete the prefix and after prefix deletion we need to reapply highlighting + self:_reset_prefix_color() + + local start_time = vim.loop.hrtime() + + local prompt = self:_get_next_filtered_prompt() + state.set_global_key("current_line", prompt) + + if self.__locations_input == true then + local filename, line_number, column_number = utils.__separate_file_path_location(prompt) + + if line_number or column_number then + state.set_global_key("prompt_location", { row = line_number, col = column_number }) + elseif state.get_global_key "prompt_location" then + state.set_global_key("prompt_location", nil) + end + + -- it is important to continue behaving as if there is no location in prompt + prompt = filename + elseif state.get_global_key "prompt_location" then + -- in case new picker that does not support locations is opened clear the location + state.set_global_key("prompt_location", nil) + end + + -- TODO: Entry manager should have a "bulk" setter. This can prevent a lot of redraws from display + if self.cache_picker == false or self.cache_picker.is_cached ~= true then + self.sorter:_start(prompt) + self.manager = EntryManager:new(self.max_results, self.entry_adder, self.stats) + + self:_reset_highlights() + local process_result = self:get_result_processor(find_id, prompt, debounced_status) + local process_complete = self:get_result_completor(self.results_bufnr, find_id, prompt, status_updater) + + local ok, msg = pcall(function() + self.finder(prompt, process_result, process_complete) + end) + + if not ok then + log.warn("Finder failed with msg: ", msg) + end + + local diff_time = (vim.loop.hrtime() - start_time) / 1e6 + if self.debounce and diff_time < self.debounce then + async.util.sleep(self.debounce - diff_time) + end + else + -- TODO(scroll): This can only happen once, I don't like where it is. + self:_resume_picker() + end + end + end) + + -- Register attach + vim.api.nvim_buf_attach(self.prompt_bufnr, false, { + on_lines = function(...) + if self._finder_attached then + find_id = self:_next_find_id() + + status_updater { completed = false } + self._on_lines(...) + end + end, + + on_detach = function() + self:_detach() + end, + }) + + vim.api.nvim_create_augroup("PickerInsert", {}) + -- TODO: Use WinLeave as well? + vim.api.nvim_create_autocmd("BufLeave", { + buffer = self.prompt_bufnr, + group = "PickerInsert", + nested = true, + once = true, + callback = function() + require("telescope.pickers").on_close_prompt(self.prompt_bufnr) + end, + }) + vim.api.nvim_create_autocmd("VimResized", { + buffer = self.prompt_bufnr, + group = "PickerInsert", + nested = true, + callback = function() + require("telescope.pickers").on_resize_window(self.prompt_bufnr) + end, + }) + + state.set_status( + self.prompt_bufnr, + setmetatable({ + layout = layout, + picker = self, + + -- compatibility + prompt_bufnr = self.prompt_bufnr, + prompt_win = self.prompt_win, + prompt_border_win = self.prompt_border.winid, + results_bufnr = self.results_bufnr, + results_win = self.results_win, + results_border_win = self.results_border.winid, + preview_bufnr = self.preview_bufnr, + preview_win = self.preview_win, + preview_border_win = self.preview_border and self.preview_border.winid, + }, { + __mode = "kv", + }) + ) + + mappings.apply_keymap(self.prompt_bufnr, self.attach_mappings, config.values.mappings) + + tx.send() + main_loop() +end + +--- A helper function to update picker windows when layout options are changed +function Picker:recalculate_layout() + local status = state.get_status(self.prompt_bufnr) + + status.layout:update() + + local layout = status.layout + self.prompt_win, self.prompt_bufnr, self.prompt_border = + layout.prompt.winid, layout.prompt.bufnr, layout.prompt.border + self.results_win, self.results_bufnr, self.results_border = + layout.results.winid, layout.results.bufnr, layout.results.border + if layout.preview then + self.preview_win, self.preview_bufnr, self.preview_border = + layout.preview.winid, layout.preview.bufnr, layout.preview.border + else + self.preview_win, self.preview_bufnr, self.preview_border = nil, nil, nil + end + + -- Temporarily disabled: Draw the screen ASAP. This makes things feel speedier. + -- vim.cmd [[redraw]] + + -- self.max_results = popup_opts.results.height +end + +local update_scroll = function(win, oldinfo, oldcursor, strategy, buf_maxline) + if strategy == "ascending" then + vim.api.nvim_win_set_cursor(win, { buf_maxline, 0 }) + vim.api.nvim_win_set_cursor(win, { oldinfo.topline, 0 }) + vim.api.nvim_win_set_cursor(win, oldcursor) + elseif strategy == "descending" then + vim.api.nvim_win_set_cursor(win, { 1, 0 }) + vim.api.nvim_win_set_cursor(win, { oldinfo.botline, 0 }) + vim.api.nvim_win_set_cursor(win, oldcursor) + else + error(debug.traceback("Unknown sorting strategy: " .. (strategy or ""))) + end +end + +--- A wrapper for `Picker:recalculate_layout()` that also handles maintaining cursor position +function Picker:full_layout_update() + local oldinfo = vim.fn.getwininfo(self.results_win)[1] + local oldcursor = vim.api.nvim_win_get_cursor(self.results_win) + self:recalculate_layout() + self:refresh_previewer() + + -- update scrolled position + local buf_maxline = #vim.api.nvim_buf_get_lines(self.results_bufnr, 0, -1, false) + update_scroll(self.results_win, oldinfo, oldcursor, self.sorting_strategy, buf_maxline) +end + +-- TODO: update multi-select with the correct tag name when available +--- A simple interface to remove an entry from the results window without +--- closing telescope. This either deletes the current selection or all the +--- selections made using multi-select. It can be used to define actions +--- such as deleting buffers or files. +--- +--- Example usage: +--- +--- actions.delete_something = function(prompt_bufnr) +--- local current_picker = action_state.get_current_picker(prompt_bufnr) +--- current_picker:delete_selection(function(selection) +--- -- delete the selection outside of telescope +--- end) +--- end +--- +--- +--- Example usage in telescope: +--- - `actions.delete_buffer()` +---@param delete_cb function: called for each selection fn(s) -> bool|nil (true|nil removes the entry from the results) +function Picker:delete_selection(delete_cb) + vim.validate { delete_cb = { delete_cb, "f" } } + local original_selection_strategy = self.selection_strategy + self.selection_strategy = "row" + + local delete_selections = self._multi:get() + local used_multi_select = true + if vim.tbl_isempty(delete_selections) then + table.insert(delete_selections, self:get_selection()) + used_multi_select = false + end + + local selection_index = {} + for result_index, result_entry in pairs(self.finder.results) do + if vim.tbl_contains(delete_selections, result_entry) then + table.insert(selection_index, result_index) + end + end + + -- Sort in reverse order as removing an entry from the table shifts down the + -- other elements to close the hole. + table.sort(selection_index, function(x, y) + return x > y + end) + for _, index in ipairs(selection_index) do + local delete_cb_return = delete_cb(self.finder.results[index]) + if delete_cb_return == nil or delete_cb_return == true then + table.remove(self.finder.results, index) + end + end + + if used_multi_select then + self._multi = MultiSelect:new() + end + + self:refresh() + vim.defer_fn(function() + self.selection_strategy = original_selection_strategy + end, 50) +end + +---@param text string text to set as prompt +---@param reset boolean? whether to replace prompt with text entirely or just append +function Picker:set_prompt(text, reset) + reset = vim.F.if_nil(reset, true) + if not reset then + text = self:_get_prompt() .. text + end + self:reset_prompt(text) +end + +--- Closes the windows for the prompt, results and preview +---@param status table: table containing information on the picker +--- and associated windows. Generally obtained from `state.get_status` +function Picker.close_windows(status) + local prompt_bufnr = status.layout.prompt.bufnr + status.layout:unmount() + state.clear_status(prompt_bufnr) +end + +--- Get the entry table of the current selection +---@return table +function Picker:get_selection() + return self._selection_entry +end + +--- Get the row number of the current selection +---@return number +function Picker:get_selection_row() + if self._selection_row then + -- If the current row is no longer selectable than reduce it to num_results - 1, so the next selectable row. + -- This makes selection_strategy `row` work much better if the selected row is no longer part of the output. + --TODO(conni2461): Maybe this can be moved to scroller. (currently in a hotfix so not viable) + if self.selection_strategy == "row" then + local num_results = self.manager:num_results() + if self.sorting_strategy == "ascending" then + if self._selection_row >= num_results then + return num_results - 1 + end + else + local max = self.max_results - num_results + if self._selection_row < max then + return self.max_results - num_results + end + end + end + return self._selection_row + end + return self:get_reset_row() +end + +--- Move the current selection by `change` steps +---@param change number +function Picker:move_selection(change) + self:set_selection(self:get_selection_row() + change) +end + +--- Add the entry of the given row to the multi-select object +---@param row number: the number of the chosen row +function Picker:add_selection(row) + local entry = self.manager:get_entry(self:get_index(row)) + self._multi:add(entry) + + self:update_prefix(entry, row) + self:get_status_updater(self.prompt_win, self.prompt_bufnr)() + self.highlighter:hi_multiselect(row, true) +end + +--- Remove the entry of the given row to the multi-select object +---@param row number: the number of the chosen row +function Picker:remove_selection(row) + local entry = self.manager:get_entry(self:get_index(row)) + self._multi:drop(entry) + + self:update_prefix(entry, row) + self:get_status_updater(self.prompt_win, self.prompt_bufnr)() + self.highlighter:hi_multiselect(row, false) +end + +--- Check if the given row is in the multi-select object +---@param entry table: table with information about the chosen entry +---@return number: the "count" associated to the entry in the multi-select +--- object (if present), `nil` otherwise +function Picker:is_multi_selected(entry) + return self._multi:is_selected(entry) +end + +--- Get a table containing all of the currently selected entries +---@return table: an integer indexed table of selected entries +function Picker:get_multi_selection() + return self._multi:get() +end + +--- Toggle the given row in and out of the multi-select object. +--- Also updates the highlighting for the given entry +---@param row number: the number of the chosen row +function Picker:toggle_selection(row) + local entry = self.manager and self.manager:get_entry(self:get_index(row)) + if entry == nil then + return + end + self._multi:toggle(entry) + + self:update_prefix(entry, row) + self:get_status_updater(self.prompt_win, self.prompt_bufnr)() + self.highlighter:hi_multiselect(row, self._multi:is_selected(entry)) +end + +--- Set the current selection to `nil` +---@note: generally used when a picker is first activated with `find()` +function Picker:reset_selection() + self._selection_entry = nil + self._selection_row = nil +end + +function Picker:_reset_prefix_color(hl_group) + self._current_prefix_hl_group = hl_group or nil + + if self.prompt_prefix ~= "" and a.nvim_buf_is_valid(self.prompt_bufnr) then + vim.api.nvim_buf_add_highlight( + self.prompt_bufnr, + ns_telescope_prompt_prefix, + self._current_prefix_hl_group or "TelescopePromptPrefix", + 0, + 0, + #self.prompt_prefix + ) + end +end + +-- TODO(conni2461): Maybe _ prefix these next two functions +-- TODO(conni2461): Next two functions only work together otherwise color doesn't work +-- Probably a issue with prompt buffers +--- Change the prefix in the prompt to be `new_prefix` and apply `hl_group` +---@param new_prefix string: the string to be used as the new prefix +---@param hl_group string: the name of the chosen highlight +function Picker:change_prompt_prefix(new_prefix, hl_group) + if not new_prefix then + return + end + + if new_prefix ~= "" then + vim.fn.prompt_setprompt(self.prompt_bufnr, new_prefix) + else + vim.api.nvim_buf_set_text(self.prompt_bufnr, 0, 0, 0, #self.prompt_prefix, {}) + vim.api.nvim_buf_set_option(self.prompt_bufnr, "buftype", "") + end + self.prompt_prefix = new_prefix + self:_reset_prefix_color(hl_group) +end + +--- Reset the prompt to the provided `text` +---@param text string +function Picker:reset_prompt(text) + local prompt_text = self.prompt_prefix .. (text or "") + vim.api.nvim_buf_set_lines(self.prompt_bufnr, 0, -1, false, { prompt_text }) + self:_reset_prefix_color(self._current_prefix_hl_group) + + if text then + vim.api.nvim_win_set_cursor(self.prompt_win, { 1, #prompt_text }) + end +end + +---@param finder finder: telescope finder (see telescope/finders.lua) +---@param opts table: options to pass when refreshing the picker +---@field new_prefix string|table: either as string or { new_string, hl_group } +---@field reset_prompt bool: whether to reset the prompt +---@field multi MultiSelect: multi-selection to persist upon renewing finder (see telescope/pickers/multi.lua) +function Picker:refresh(finder, opts) + opts = opts or {} + if opts.new_prefix then + local handle = type(opts.new_prefix) == "table" and unpack or function(x) + return x + end + self:change_prompt_prefix(handle(opts.new_prefix), opts.prefix_hl_group) + end + + if finder then + self.finder:close() + self.finder = finder + self._multi = vim.F.if_nil(opts.multi, MultiSelect:new()) + end + + -- reset already triggers finder loop + if opts.reset_prompt then + self:reset_prompt() + else + self._on_lines(nil, nil, nil, 0, 1) + end +end + +---Set the selection to the provided `row` +---@param row number +function Picker:set_selection(row) + if not self.manager then + return + end + + row = self.scroller(self.max_results, self.manager:num_results(), row) + + if not self:can_select_row(row) then + -- If the current selected row exceeds number of currently displayed + -- elements we have to reset it. Affects sorting_strategy = 'row'. + if not self:can_select_row(self:get_selection_row()) then + row = self:get_row(self.manager:num_results()) + else + log.trace("Cannot select row:", row, self.manager:num_results(), self.max_results) + return + end + end + + local results_bufnr = self.results_bufnr + if not a.nvim_buf_is_valid(results_bufnr) then + return + end + + if row > a.nvim_buf_line_count(results_bufnr) then + log.debug( + string.format("Should not be possible to get row this large %s %s", row, a.nvim_buf_line_count(results_bufnr)) + ) + + return + end + + local entry = self.manager:get_entry(self:get_index(row)) + + local prompt_location = state.get_global_key "prompt_location" + if entry and prompt_location then + entry.lnum = prompt_location.row or 0 + if prompt_location.col and prompt_location.col > 0 then + entry.col = prompt_location.col + entry.colend = prompt_location.col + 1 + else + entry.col = 1 -- we do + 1 here because previewer does -1 + entry.colend = 0 + end + end + + state.set_global_key("selected_entry", entry) + + if not entry then + -- also refresh previewer when there is no entry selected, so the preview window is cleared + self._selection_entry = entry + self:refresh_previewer() + return + end + + local old_entry + + -- TODO: Probably should figure out what the rows are that made this happen... + -- Probably something with setting a row that's too high for this? + -- Not sure. + local set_ok, set_errmsg = pcall(function() + local prompt = self:_get_prompt() + + -- Check if previous selection is still visible + if self._selection_entry and self.manager:find_entry(self._selection_entry) then + -- Find old selection, and update prefix and highlights + old_entry = self._selection_entry + local old_row = self:get_row(self.manager:find_entry(old_entry)) + + self._selection_entry = entry + + if old_row >= 0 then + self:update_prefix(old_entry, old_row) + self.highlighter:hi_multiselect(old_row, self:is_multi_selected(old_entry)) + end + else + self._selection_entry = entry + end + + local caret = self:update_prefix(entry, row) + + local display, _ = entry_display.resolve(self, entry) + display = caret .. display + + -- TODO: You should go back and redraw the highlights for this line from the sorter. + -- That's the only smart thing to do. + if not a.nvim_buf_is_valid(results_bufnr) then + log.debug "Invalid buf somehow..." + return + end + + -- don't highlight any whitespace at the end of caret + self.highlighter:hi_selection(row, caret:match "(.*%S)") + self.highlighter:hi_sorter(row, prompt, display) + + self.highlighter:hi_multiselect(row, self:is_multi_selected(entry)) + end) + + if not set_ok then + log.debug(set_errmsg) + return + end + + self:refresh_previewer() + if old_entry == entry and self._selection_row == row then + return + end + + -- TODO: Get row & text in the same obj + self._selection_entry = entry + self._selection_row = row + + vim.api.nvim_win_set_cursor(self.results_win, { row + 1, 0 }) +end + +--- Update prefix for entry on a given row +function Picker:update_prefix(entry, row) + local prefix = function(sel, multi) + local t + if sel then + t = self.selection_caret + else + t = self.entry_prefix + end + if multi and type(self.multi_icon) == "string" then + t = truncate(t, strdisplaywidth(t) - strdisplaywidth(self.multi_icon), "") .. self.multi_icon + end + return t + end + + local line = vim.api.nvim_buf_get_lines(self.results_bufnr, row, row + 1, false)[1] + if not line then + log.trace(string.format("no line found at row %d in buffer %d", row, self.results_bufnr)) + return + end + + local old_caret = string.sub(line, 0, #prefix(true)) == prefix(true) and prefix(true) + or string.sub(line, 0, #prefix(true, true)) == prefix(true, true) and prefix(true, true) + or string.sub(line, 0, #prefix(false)) == prefix(false) and prefix(false) + or string.sub(line, 0, #prefix(false, true)) == prefix(false, true) and prefix(false, true) + if old_caret == false then + log.warn(string.format("can't identify old caret in line: %s", line)) + return + end + + local pre = prefix(entry == self._selection_entry, self:is_multi_selected(entry)) + -- Only change the first couple characters, nvim_buf_set_text leaves the existing highlights + a.nvim_buf_set_text(self.results_bufnr, row, 0, row, #old_caret, { pre }) + return pre +end + +--- Refresh the previewer based on the current `status` of the picker +function Picker:refresh_previewer() + local status = state.get_status(self.prompt_bufnr) + if + self.previewer + and status.layout.preview + and status.layout.preview.winid + and a.nvim_win_is_valid(status.layout.preview.winid) + then + self:_increment "previewed" + + self.previewer:preview(self._selection_entry, status) + if self.preview_border then + if self.fix_preview_title then + return + end + + local new_title = self.previewer:title(self._selection_entry, config.values.dynamic_preview_title) + if new_title ~= nil and new_title ~= self.preview_title then + self.preview_title = new_title + self.layout.preview.border:change_title(new_title) + end + end + end +end + +function Picker:cycle_previewers(next) + local size = #self.all_previewers + if size == 1 then + return + end + + self.current_previewer_index = self.current_previewer_index + next + if self.current_previewer_index > size then + self.current_previewer_index = 1 + elseif self.current_previewer_index < 1 then + self.current_previewer_index = size + end + + if self.previewer then + self.previewer = self.all_previewers[self.current_previewer_index] + self:refresh_previewer() + elseif self.hidden_previewer then + self.hidden_previewer = self.all_previewers[self.current_previewer_index] + end +end + +--- Handler for when entries are added by `self.manager` +---@param index number: the index to add the entry at +---@param entry table: the entry that has been added to the manager +---@param insert boolean: whether the entry has been "inserted" or not +function Picker:entry_adder(index, entry, _, insert) + if not entry then + return + end + + local row = self:get_row(index) + + -- If it's less than 0, then we don't need to show it at all. + if row < 0 then + log.debug("ON_ENTRY: Weird row", row) + return + end + + local display, display_highlights = entry_display.resolve(self, entry) + if not display then + log.info("Weird entry", entry) + return + end + + -- This is the two spaces to manage the '> ' stuff. + -- Maybe someday we can use extmarks or floaty text or something to draw this and not insert here. + -- until then, insert two spaces + local prefix = self.entry_prefix + display = prefix .. display + + self:_increment "displayed" + + local offset = insert and 0 or 1 + if not vim.api.nvim_buf_is_valid(self.results_bufnr) then + log.debug "ON_ENTRY: Invalid buffer" + return + end + + -- TODO: Does this every get called? + -- local line_count = vim.api.nvim_win_get_height(self.results_win) + local line_count = vim.api.nvim_buf_line_count(self.results_bufnr) + if row > line_count then + return + end + + if insert then + if self.sorting_strategy == "descending" then + vim.api.nvim_buf_set_lines(self.results_bufnr, 0, 1, false, {}) + end + end + + local set_ok, msg = pcall(vim.api.nvim_buf_set_lines, self.results_bufnr, row, row + offset, false, { display }) + if set_ok then + if display_highlights then + self.highlighter:hi_display(row, prefix, display_highlights) + end + self:update_prefix(entry, row) + self:highlight_one_row(self.results_bufnr, self:_get_prompt(), display, row) + end + + if not set_ok then + log.debug("Failed to set lines...", msg) + end + + -- This pretty much only fails when people leave newlines in their results. + -- So we'll clean it up for them if it fails. + if not set_ok and display:find "\n" then + display = display:gsub("\n", " | ") + vim.api.nvim_buf_set_lines(self.results_bufnr, row, row + 1, false, { display }) + end +end + +--- Reset tracked information for this picker +function Picker:_reset_track() + self.stats.processed = 0 + self.stats.displayed = 0 + self.stats.display_fn = 0 + self.stats.previewed = 0 + self.stats.status = 0 + + self.stats.filtered = 0 + self.stats.highlights = 0 +end + +--- Increment the count of the tracked info at `self.stats[key]` +---@param key string +function Picker:_increment(key) + self.stats[key] = (self.stats[key] or 0) + 1 +end + +--- Decrement the count of the tracked info at `self.stats[key]` +---@param key string +function Picker:_decrement(key) + self.stats[key] = (self.stats[key] or 0) - 1 +end + +-- TODO: Decide how much we want to use this. +-- Would allow for better debugging of items. +function Picker:register_completion_callback(cb) + table.insert(self._completion_callbacks, cb) +end + +function Picker:clear_completion_callbacks() + self._completion_callbacks = {} +end + +function Picker:_on_complete() + for _, v in ipairs(self._completion_callbacks) do + pcall(v, self) + end +end + +--- Close all open Telescope pickers +function Picker:close_existing_pickers() + for _, prompt_bufnr in ipairs(state.get_existing_prompt_bufnrs()) do + pcall(actions.close, prompt_bufnr) + end +end + +--- Returns a function that sets virtual text for the count indicator +--- e.g. "10/50" as "filtered"/"processed" +---@param prompt_win number +---@param prompt_bufnr number +---@return function +function Picker:get_status_updater(prompt_win, prompt_bufnr) + return function(opts) + if self.closed or not vim.api.nvim_buf_is_valid(prompt_bufnr) then + return + end + + local current_prompt = self:_get_prompt() + if not current_prompt then + return + end + + if not vim.api.nvim_win_is_valid(prompt_win) then + return + end + + local text = self:get_status_text(opts) + vim.api.nvim_buf_clear_namespace(prompt_bufnr, ns_telescope_prompt, 0, -1) + vim.api.nvim_buf_set_extmark(prompt_bufnr, ns_telescope_prompt, 0, 0, { + virt_text = { { text, "TelescopePromptCounter" } }, + virt_text_pos = "right_align", + }) + + self:_increment "status" + end +end + +--- Returns a function that will process an element. +--- Returned function handles updating the "filtered" and "processed" counts +--- as appropriate and runs the sorters score function +---@param find_id number +---@param prompt string +---@param status_updater function +---@return function +function Picker:get_result_processor(find_id, prompt, status_updater) + local count = 0 + + local cb_add = function(score, entry) + -- may need the prompt for tiebreak + self.manager:add_entry(self, score, entry, prompt) + status_updater { completed = false } + end + + local cb_filter = function(_) + self:_increment "filtered" + end + + return function(entry) + if find_id ~= self._find_id then + return true + end + + if not entry or entry.valid == false then + return + end + + self:_increment "processed" + + count = count + 1 + + -- TODO: Probably should asyncify this / cache this / do something because this probably takes + -- a ton of time on large results. + log.trace("Processing result... ", entry) + for _, v in ipairs(self.file_ignore_patterns or {}) do + local file = vim.F.if_nil(entry.filename, type(entry.value) == "string" and entry.value) -- false if none is true + if file then + if string.find(file, v) then + log.trace("SKIPPING", entry.value, "because", v) + self:_decrement "processed" + return + end + end + end + + self.sorter:score(prompt, entry, cb_add, cb_filter) + end +end + +--- Handles updating the picker after all the entries are scored/processed. +---@param results_bufnr number +---@param _ number +---@param prompt string +---@param status_updater function +function Picker:get_result_completor(results_bufnr, _, prompt, status_updater) + return vim.schedule_wrap(function() + if self.closed == true or self:is_done() then + return + end + + self:_do_selection(prompt) + + status_updater { completed = true } + + self:clear_extra_rows(results_bufnr) + self.sorter:_finish(prompt) + + if self.sorting_strategy == "descending" then + local visible_result_rows = vim.api.nvim_win_get_height(self.results_win) + vim.api.nvim_win_set_cursor(self.results_win, { self.max_results - visible_result_rows, 1 }) + vim.api.nvim_win_set_cursor(self.results_win, { self.max_results, 1 }) + else + vim.api.nvim_win_set_cursor(self.results_win, { 1, 0 }) + end + self:_on_complete() + end) +end + +function Picker:_do_selection(prompt) + local selection_strategy = self.selection_strategy or "reset" + -- TODO: Either: always leave one result or make sure we actually clean up the results when nothing matches + if selection_strategy == "row" then + if self._selection_row == nil and self.default_selection_index ~= nil then + self:set_selection(self:get_row(self.default_selection_index)) + else + self:set_selection(self:get_selection_row()) + end + elseif selection_strategy == "follow" then + if self._selection_row == nil and self.default_selection_index ~= nil then + self:set_selection(self:get_row(self.default_selection_index)) + else + local index = self.manager:find_entry(self:get_selection()) + + if index then + local follow_row = self:get_row(index) + self:set_selection(follow_row) + else + self:set_selection(self:get_reset_row()) + end + end + elseif selection_strategy == "reset" then + if self.default_selection_index ~= nil then + self:set_selection(self:get_row(self.default_selection_index)) + else + self:set_selection(self:get_reset_row()) + end + elseif selection_strategy == "closest" then + if prompt == "" and self.default_selection_index ~= nil then + self:set_selection(self:get_row(self.default_selection_index)) + else + self:set_selection(self:get_reset_row()) + end + elseif selection_strategy == "none" then + if self._selection_entry then + local old_entry, old_row = self._selection_entry, self._selection_row + self:reset_selection() -- required to reset selection before updating prefix + if old_row >= 0 then + self:update_prefix(old_entry, old_row) + self.highlighter:hi_multiselect(old_row, self:is_multi_selected(old_entry)) + end + end + return + else + error("Unknown selection strategy: " .. selection_strategy) + end +end + +--- Wrapper function for `Picker:new` that incorporates user provided `opts` +--- with the telescope `defaults` +---@param opts table +---@param defaults table +---@return Picker +pickers.new = function(opts, defaults) + opts = opts or {} + defaults = defaults or {} + local result = {} + + for k, v in pairs(opts) do + assert(type(k) == "string" or type(k) == "number", "Should be string or number, found: " .. type(k)) + result[k] = v + end + + for k, v in pairs(defaults) do + if result[k] == nil then + assert(type(k) == "string", "Should be string, defaults") + result[k] = v + else + -- For attach mappings, we want people to be able to pass in another function + -- and apply their mappings after we've applied our defaults. + if k == "attach_mappings" then + local opt_value = result[k] + result[k] = function(...) + v(...) + return opt_value(...) + end + end + end + end + + if result["previewer"] == false then + result["previewer"] = defaults["previewer"] + result["__hide_previewer"] = true + elseif result["previewer"] == true then + result["previewer"] = defaults["previewer"] + elseif type(opts["preview"]) == "table" and opts["preview"]["hide_on_startup"] then + result["__hide_previewer"] = true + end + + return Picker:new(result) +end + +--- Close the picker which has prompt with buffer number `prompt_bufnr` +---@param prompt_bufnr number +function pickers.on_close_prompt(prompt_bufnr) + local status = state.get_status(prompt_bufnr) + local picker = status.picker + require("telescope.actions.state").get_current_history():reset() + + if type(picker.cache_picker) == "table" then + local cached_pickers = state.get_global_key "cached_pickers" or {} + + if type(picker.cache_picker.index) == "number" then + if not vim.tbl_isempty(cached_pickers) then + table.remove(cached_pickers, picker.cache_picker.index) + end + end + + -- if picker was disabled post-hoc (e.g. `cache_picker = false` conclude after deletion) + if picker.cache_picker.disabled ~= true then + if picker.cache_picker.limit_entries > 0 then + -- edge case: starting in normal mode and not having run a search means having no manager instantiated + if picker.manager then + picker.manager.linked_states:truncate(picker.cache_picker.limit_entries) + else + picker.manager = EntryManager:new(picker.max_results, picker.entry_adder, picker.stats) + end + end + local curr_prompt = picker:_get_prompt() + picker.default_text = curr_prompt + picker.cache_picker.selection_row = picker._selection_row + + -- Only cache if prompt is not empty or ignore_empty_prompt is false + if not picker.cache_picker.ignore_empty_prompt or (curr_prompt and curr_prompt ~= "") then + picker.cache_picker.cached_prompt = curr_prompt + table.insert(cached_pickers, 1, picker) + picker.cache_picker.is_cached = true + end + + -- release pickers + if picker.cache_picker.num_pickers > 0 then + while #cached_pickers > picker.cache_picker.num_pickers do + table.remove(cached_pickers, #cached_pickers) + end + end + state.set_global_key("cached_pickers", cached_pickers) + end + end + + if picker.sorter then + picker.sorter:_destroy() + end + + if picker.all_previewers then + for _, v in ipairs(picker.all_previewers) do + v:teardown() + end + end + + if picker.finder then + picker.finder:close() + end + + -- so we dont call close_windows multiple times we clear that autocmd + vim.api.nvim_clear_autocmds { + group = "PickerInsert", + event = "BufLeave", + buffer = prompt_bufnr, + } + picker.close_windows(status) + + vim.o.mousemoveevent = picker.__original_mousemoveevent +end + +function pickers.on_resize_window(prompt_bufnr) + local status = state.get_status(prompt_bufnr) + local picker = status.picker + + picker:full_layout_update() +end + +--- Get the prompt text without the prompt prefix. +function Picker:_get_prompt() + local cursor_line = vim.api.nvim_win_get_cursor(self.prompt_win)[1] - 1 + return vim.api + .nvim_buf_get_lines(self.prompt_bufnr, cursor_line, cursor_line + 1, false)[1] + :sub(#self.prompt_prefix + 1) +end + +function Picker:_reset_highlights() + self.highlighter:clear_display() + vim.api.nvim_buf_clear_namespace(self.results_bufnr, ns_telescope_matching, 0, -1) +end + +-- Toggles whether finder is attached to prompt buffer input +function Picker:_toggle_finder_attach() + self._finder_attached = not self._finder_attached +end + +function Picker:_detach() + self.finder:close() + + -- TODO: Can we add a "cleanup" / "teardown" function that completely removes these. + -- self.finder = nil + -- self.previewer = nil + -- self.sorter = nil + -- self.manager = nil + + self.closed = true +end + +function Picker:_get_next_filtered_prompt() + local prompt = self:_get_prompt() + local on_input_result = self._on_input_filter_cb(prompt) or {} + + local new_prompt = on_input_result.prompt + if new_prompt then + prompt = new_prompt + end + + local new_finder = on_input_result.updated_finder + if new_finder then + self.finder:close() + self.finder = new_finder + end + + return prompt +end + +function Picker:_resume_picker() + -- resume previous picker + local index = 1 + for entry in self.manager:iter() do + self:entry_adder(index, entry, _, true) + index = index + 1 + end + self.cache_picker.is_cached = false + local on_resume_complete = function() + if vim.api.nvim_buf_is_valid(self.prompt_bufnr) then + vim.api.nvim_buf_call(self.prompt_bufnr, function() + vim.cmd "do User TelescopeResumePost" + end) + end + end + -- if text changed, required to set anew to restart finder; otherwise hl and selection + if self.cache_picker.cached_prompt ~= self.default_text then + self:set_prompt(self.default_text) + on_resume_complete() + else + -- scheduling required to apply highlighting and selection appropriately + await_schedule(function() + if self.cache_picker.selection_row ~= nil then + self:set_selection(self.cache_picker.selection_row) + end + on_resume_complete() + end) + end +end + +pickers._Picker = Picker + +return pickers diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers/entry_display.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers/entry_display.lua new file mode 100644 index 0000000..1353cff --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers/entry_display.lua @@ -0,0 +1,169 @@ +---@tag telescope.pickers.entry_display + +---@brief [[ +--- Entry Display is used to format each entry shown in the result panel. +--- +--- Entry Display create() will give us a function based on the configuration +--- of column widths we pass into it. We then can use this function n times to +--- return a string based on structured input. +--- +--- Note that if you call `create()` inside `make_display` it will be called for +--- every single entry. So it is suggested to do this outside of `make_display` +--- for the best performance. +--- +--- The create function will use the column widths passed to it in +--- configuration.items. Each item in that table is the number of characters in +--- the column. It's also possible for the final column to not have a fixed +--- width, this will be shown in the configuration as 'remaining = true'. +--- +--- An example of this configuration is shown for the buffers picker: +--- +--- local displayer = entry_display.create { +--- separator = " ", +--- items = { +--- { width = opts.bufnr_width }, +--- { width = 4 }, +--- { width = icon_width }, +--- { remaining = true }, +--- }, +--- } +--- +--- +--- This shows 4 columns, the first is defined in the opts as the width we'll +--- use when display_string is the number of the buffer. The second has a fixed +--- width of 4 and the third column's width will be decided by the width of the +--- icons we use. The fourth column will use the remaining space. Finally, we +--- have also defined the separator between each column will be the space " ". +--- +--- An example of how the display reference will be used is shown, again for +--- the buffers picker: +--- +--- return displayer { +--- { entry.bufnr, "TelescopeResultsNumber" }, +--- { entry.indicator, "TelescopeResultsComment" }, +--- { icon, hl_group }, +--- display_bufname .. ":" .. entry.lnum, +--- } +--- +--- +--- There are two types of values each column can have. Either a simple String +--- or a table containing the String as well as the hl_group. +--- +--- The displayer can return values, string and an optional highlights. The string +--- is all the text to be displayed for this entry as a single string. If parts of +--- the string are to be highlighted they will be described in the highlights +--- table. +--- +--- For a better understanding of how create() and displayer are used it's best to look +--- at the code in make_entry.lua. +---@brief ]] + +local strings = require "plenary.strings" +local state = require "telescope.state" +local resolve = require "telescope.config.resolve" + +local entry_display = {} +entry_display.truncate = strings.truncate + +entry_display.create = function(configuration) + local generator = {} + for _, v in ipairs(configuration.items) do + if v.width then + local justify = v.right_justify + local width + table.insert(generator, function(item) + if width == nil then + local status = state.get_status(vim.F.if_nil(configuration.prompt_bufnr, vim.api.nvim_get_current_buf())) + local s = {} + s[1] = vim.api.nvim_win_get_width(status.layout.results.winid) - #status.picker.selection_caret + s[2] = vim.api.nvim_win_get_height(status.layout.results.winid) + width = resolve.resolve_width(v.width)(nil, s[1], s[2]) + end + if type(item) == "table" then + return strings.align_str(entry_display.truncate(item[1], width), width, justify), item[2] + else + return strings.align_str(entry_display.truncate(item, width), width, justify) + end + end) + else + table.insert(generator, function(item) + if type(item) == "table" then + return item[1], item[2] + else + return item + end + end) + end + end + + return function(self, picker) + local results = {} + local highlights = {} + for i = 1, #generator do + if self[i] ~= nil then + local str, hl = generator[i](self[i], picker) + if hl then + local hl_start = 0 + for j = 1, (i - 1) do + hl_start = hl_start + #results[j] + (#configuration.separator or 1) + end + local hl_end = hl_start + #str:gsub("%s*$", "") + + if type(hl) == "function" then + for _, hl_res in ipairs(hl()) do + table.insert(highlights, { { hl_res[1][1] + hl_start, hl_res[1][2] + hl_start }, hl_res[2] }) + end + else + table.insert(highlights, { { hl_start, hl_end }, hl }) + end + end + + table.insert(results, str) + end + end + + if configuration.separator_hl then + local width = #configuration.separator or 1 + + local hl_start, hl_end + for _, v in ipairs(results) do + hl_start = (hl_end or 0) + #tostring(v) + hl_end = hl_start + width + table.insert(highlights, { { hl_start, hl_end }, configuration.separator_hl }) + end + end + + local final_str = table.concat(results, configuration.separator or "│") + if configuration.hl_chars then + for i = 1, #final_str do + local c = final_str:sub(i, i) + local hl = configuration.hl_chars[c] + if hl then + table.insert(highlights, { { i - 1, i }, hl }) + end + end + end + + return final_str, highlights + end +end + +entry_display.resolve = function(self, entry) + local display, display_highlights + if type(entry.display) == "function" then + self:_increment "display_fn" + display, display_highlights = entry:display(self) + + if type(display) == "string" then + return display, display_highlights + end + else + display = entry.display + end + + if type(display) == "string" then + return display, display_highlights + end +end + +return entry_display diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers/highlights.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers/highlights.lua new file mode 100644 index 0000000..be693a7 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers/highlights.lua @@ -0,0 +1,125 @@ +local a = vim.api +local log = require "telescope.log" +local conf = require("telescope.config").values + +local highlights = {} + +local ns_telescope_selection = a.nvim_create_namespace "telescope_selection" +local ns_telescope_multiselection = a.nvim_create_namespace "telescope_multiselection" +local ns_telescope_entry = a.nvim_create_namespace "telescope_entry" + +local Highlighter = {} +Highlighter.__index = Highlighter + +function Highlighter:new(picker) + return setmetatable({ + picker = picker, + }, self) +end + +function Highlighter:hi_display(row, prefix, display_highlights) + -- This is the bug that made my highlight fixes not work. + -- We will leave the solution commented, so the test fails. + if not display_highlights or vim.tbl_isempty(display_highlights) then + return + end + + local results_bufnr = assert(self.picker.results_bufnr, "Must have a results bufnr") + + a.nvim_buf_clear_namespace(results_bufnr, ns_telescope_entry, row, row + 1) + local len_prefix = #prefix + + for _, hl_block in ipairs(display_highlights) do + a.nvim_buf_add_highlight( + results_bufnr, + ns_telescope_entry, + hl_block[2], + row, + len_prefix + hl_block[1][1], + len_prefix + hl_block[1][2] + ) + end +end + +function Highlighter:clear_display() + if + not self + or not self.picker + or not self.picker.results_bufnr + or not vim.api.nvim_buf_is_valid(self.picker.results_bufnr) + then + return + end + + a.nvim_buf_clear_namespace(self.picker.results_bufnr, ns_telescope_entry, 0, -1) +end + +function Highlighter:hi_sorter(row, prompt, display) + local picker = self.picker + if not picker.sorter or not picker.sorter.highlighter then + return + end + + local results_bufnr = assert(self.picker.results_bufnr, "Must have a results bufnr") + picker:highlight_one_row(results_bufnr, prompt, display, row) +end + +function Highlighter:hi_selection(row, caret) + caret = vim.F.if_nil(caret, "") + local results_bufnr = assert(self.picker.results_bufnr, "Must have a results bufnr") + + a.nvim_buf_clear_namespace(results_bufnr, ns_telescope_selection, 0, -1) + a.nvim_buf_add_highlight(results_bufnr, ns_telescope_selection, "TelescopeSelectionCaret", row, 0, #caret) + + a.nvim_buf_set_extmark( + results_bufnr, + ns_telescope_selection, + row, + #caret, + { end_line = row + 1, hl_eol = conf.hl_result_eol, hl_group = "TelescopeSelection" } + ) +end + +function Highlighter:hi_multiselect(row, is_selected) + local results_bufnr = assert(self.picker.results_bufnr, "Must have a results bufnr") + + if is_selected then + vim.api.nvim_buf_add_highlight(results_bufnr, ns_telescope_multiselection, "TelescopeMultiSelection", row, 0, -1) + if self.picker.multi_icon then + local line = vim.api.nvim_buf_get_lines(results_bufnr, row, row + 1, false)[1] + local pos = line:find(self.picker.multi_icon) + if pos and pos <= math.max(#self.picker.selection_caret, #self.picker.entry_prefix) then + vim.api.nvim_buf_add_highlight( + results_bufnr, + ns_telescope_multiselection, + "TelescopeMultiIcon", + row, + pos - 1, + pos - 1 + #self.picker.multi_icon + ) + end + end + else + local existing_marks = vim.api.nvim_buf_get_extmarks( + results_bufnr, + ns_telescope_multiselection, + { row, 0 }, + { row, -1 }, + {} + ) + + -- This is still kind of weird to me, since it seems like I'm erasing stuff + -- when I shouldn't... Perhaps it's about the gravity of the extmark? + if #existing_marks > 0 then + log.trace("Clearing highlight multi select row: ", row) + + vim.api.nvim_buf_clear_namespace(results_bufnr, ns_telescope_multiselection, row, row + 1) + end + end +end + +highlights.new = function(...) + return Highlighter:new(...) +end + +return highlights diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers/layout.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers/layout.lua new file mode 100644 index 0000000..1dde1c4 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers/layout.lua @@ -0,0 +1,193 @@ +---@tag telescope.pickers.layout +---@config { ["module"] = "telescope.pickers.layout" } + +---@brief [[ +--- The telescope pickers layout can be configured using the +--- |telescope.defaults.create_layout| option. +--- +--- Parameters: ~ +--- - picker : A Picker object. +--- +--- Return: ~ +--- - layout : instance of `TelescopeLayout` class. +--- +--- Example: ~ +--- +--- local Layout = require "telescope.pickers.layout" +--- +--- require("telescope").setup { +--- create_layout = function(picker) +--- local function create_window(enter, width, height, row, col, title) +--- local bufnr = vim.api.nvim_create_buf(false, true) +--- local winid = vim.api.nvim_open_win(bufnr, enter, { +--- style = "minimal", +--- relative = "editor", +--- width = width, +--- height = height, +--- row = row, +--- col = col, +--- border = "single", +--- title = title, +--- }) +--- +--- vim.wo[winid].winhighlight = "Normal:Normal" +--- +--- return Layout.Window { +--- bufnr = bufnr, +--- winid = winid, +--- } +--- end +--- +--- local function destory_window(window) +--- if window then +--- if vim.api.nvim_win_is_valid(window.winid) then +--- vim.api.nvim_win_close(window.winid, true) +--- end +--- if vim.api.nvim_buf_is_valid(window.bufnr) then +--- vim.api.nvim_buf_delete(window.bufnr, { force = true }) +--- end +--- end +--- end +--- +--- local layout = Layout { +--- picker = picker, +--- mount = function(self) +--- self.results = create_window(false, 40, 20, 0, 0, "Results") +--- self.preview = create_window(false, 40, 23, 0, 42, "Preview") +--- self.prompt = create_window(true, 40, 1, 22, 0, "Prompt") +--- end, +--- unmount = function(self) +--- destory_window(self.results) +--- destory_window(self.preview) +--- destory_window(self.prompt) +--- end, +--- update = function(self) end, +--- } +--- +--- return layout +--- end, +--- } +--- +---@brief ]] + +local function wrap_instance(class, instance) + local self = instance + if not getmetatable(instance) then + self = setmetatable(instance, { __index = class }) + end + return self +end + +---@class TelescopeWindowBorder.config +---@field bufnr integer +---@field winid integer|nil +---@field change_title nil|function: (self: TelescopeWindowBorder, title: string, pos?: "NW"|"N"|"NE"|"SW"|"S"|"SE"):nil + +---@param class TelescopeWindowBorder +---@param config TelescopeWindowBorder.config +---@return TelescopeWindowBorder +local function init_border(class, config) + config = config or {} + + ---@type TelescopeWindowBorder + local self = wrap_instance(class, config) + if not self.change_title then + self.change_title = class.change_title + end + + return self +end + +---@class TelescopeWindowBorder +---@field bufnr integer|nil +---@field winid integer|nil +local Border = setmetatable({}, { + __call = init_border, + __name = "TelescopeWindowBorder", +}) + +---@param title string +---@param pos "NW"|"N"|"NE"|"SW"|"S"|"SE"|nil +function Border:change_title(title, pos) end + +---@class TelescopeWindow.config +---@field bufnr integer +---@field winid integer|nil +---@field border TelescopeWindowBorder.config|nil + +---@param class TelescopeWindow +---@param config TelescopeWindow.config +---@return TelescopeWindow +local function init_window(class, config) + config = config or {} + + ---@type TelescopeWindow + local self = wrap_instance(class, config) + self.border = Border(config.border) + + return self +end + +---@class TelescopeWindow +---@field border TelescopeWindowBorder +---@field bufnr integer +---@field winid integer +local Window = setmetatable({}, { + __call = init_window, + __name = "TelescopeWindow", +}) + +---@class TelescopeLayout.config +---@field mount function: (self: TelescopeLayout):nil +---@field unmount function: (self: TelescopeLayout):nil +---@field update function: (self: TelescopeLayout):nil +---@field prompt TelescopeWindow|nil +---@field results TelescopeWindow|nil +---@field preview TelescopeWindow|nil + +---@param class TelescopeLayout +---@param config TelescopeLayout.config +---@return TelescopeLayout +local function init_layout(class, config) + config = config or {} + + ---@type TelescopeLayout + local self = wrap_instance(class, config) + + assert(config.mount, "missing layout:mount") + assert(config.unmount, "missing layout:unmount") + assert(config.update, "missing layout:update") + + return self +end + +---@class TelescopeLayout +---@field prompt TelescopeWindow +---@field results TelescopeWindow +---@field preview TelescopeWindow|nil +local Layout = setmetatable({ + Window = Window, +}, { + __call = init_layout, + __name = "TelescopeLayout", +}) + +--- Create the layout. +--- This needs to ensure the required properties are populated. +function Layout:mount() end + +--- Destroy the layout. +--- This is responsible for performing clean-up, for example: +--- - deleting buffers +--- - closing windows +--- - clearing autocmds +function Layout:unmount() end + +--- Refresh the layout. +--- This is called when, for example, vim is resized. +function Layout:update() end + +---@alias TelescopeWindow.constructor fun(config: TelescopeWindow.config): TelescopeWindow +---@alias TelescopeLayout.constructor fun(config: TelescopeLayout.config): TelescopeLayout + +return Layout --[[@as TelescopeLayout.constructor|{ Window: TelescopeWindow.constructor }]] diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers/layout_strategies.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers/layout_strategies.lua new file mode 100644 index 0000000..5be5fb9 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers/layout_strategies.lua @@ -0,0 +1,961 @@ +---@tag telescope.layout +---@config { ["module"] = "telescope.layout" } + +---@brief [[ +--- The layout of telescope pickers can be adjusted using the +--- |telescope.defaults.layout_strategy| and |telescope.defaults.layout_config| options. +--- For example, the following configuration changes the default layout strategy and the +--- default size of the picker: +--- +--- require('telescope').setup{ +--- defaults = { +--- layout_strategy = 'vertical', +--- layout_config = { height = 0.95 }, +--- }, +--- } +--- +--- +--- ──────────────────────────────────────────────────────────────────────────────── +--- +--- Layout strategies are different functions to position telescope. +--- +--- All layout strategies are functions with the following signature: +--- +--- +--- function(picker, columns, lines, layout_config) +--- -- Do some calculations here... +--- return { +--- preview = preview_configuration +--- results = results_configuration, +--- prompt = prompt_configuration, +--- } +--- end +--- +--- +---
+---   Parameters: ~
+---     - picker        : A Picker object. (docs coming soon)
+---     - columns       : (number) Columns in the vim window
+---     - lines         : (number) Lines in the vim window
+---     - layout_config : (table) The configuration values specific to the picker.
+--- 
+--- +--- This means you can create your own layout strategy if you want! Just be aware +--- for now that we may change some APIs or interfaces, so they may break if you create +--- your own. +--- +--- A good method for creating your own would be to copy one of the strategies that most +--- resembles what you want from "./lua/telescope/pickers/layout_strategies.lua" in the +--- telescope repo. +--- +---@brief ]] + +local resolve = require "telescope.config.resolve" +local p_window = require "telescope.pickers.window" + +local get_border_size = function(opts) + if opts.window.border == false then + return 0 + end + + return 1 +end + +local calc_tabline = function(max_lines) + local tbln = (vim.o.showtabline == 2) or (vim.o.showtabline == 1 and #vim.api.nvim_list_tabpages() > 1) + if tbln then + max_lines = max_lines - 1 + end + return max_lines, tbln +end + +-- Helper function for capping over/undersized width/height, and calculating spacing +--@param cur_size number: size to be capped +--@param max_size any: the maximum size, e.g. max_lines or max_columns +--@param bs number: the size of the border +--@param w_num number: the maximum number of windows of the picker in the given direction +--@param b_num number: the number of border rows/column in the given direction (when border enabled) +--@param s_num number: the number of gaps in the given direction (when border disabled) +local calc_size_and_spacing = function(cur_size, max_size, bs, w_num, b_num, s_num) + local spacing = s_num * (1 - bs) + b_num * bs + cur_size = math.min(cur_size, max_size) + cur_size = math.max(cur_size, w_num + spacing) + return cur_size, spacing +end + +local layout_strategies = {} +layout_strategies._configurations = {} + +--@param strategy_config table: table with keys for each option for a strategy +--@return table: table with keys for each option (for this strategy) and with keys for each layout_strategy +local get_valid_configuration_keys = function(strategy_config) + local valid_configuration_keys = { + -- TEMP: There are a few keys we should say are valid to start with. + preview_cutoff = true, + prompt_position = true, + } + + for key in pairs(strategy_config) do + valid_configuration_keys[key] = true + end + + for name in pairs(layout_strategies) do + valid_configuration_keys[name] = true + end + + return valid_configuration_keys +end + +local adjust_pos = function(pos, ...) + for _, opts in ipairs { ... } do + opts.col = opts.col and opts.col + pos[1] + opts.line = opts.line and opts.line + pos[2] + end +end + +--@param strategy_name string: the name of the layout_strategy we are validating for +--@param configuration table: table with keys for each option available +--@param values table: table containing all of the non-default options we want to set +--@param default_layout_config table: table with the default values to configure layouts +--@return table: table containing the combined options (defaults and non-defaults) +local function validate_layout_config(strategy_name, configuration, values, default_layout_config) + assert(strategy_name, "It is required to have a strategy name for validation.") + local valid_configuration_keys = get_valid_configuration_keys(configuration) + + -- If no default_layout_config provided, check Telescope's config values + default_layout_config = vim.F.if_nil(default_layout_config, require("telescope.config").values.layout_config) + + local result = {} + local get_value = function(k) + -- skip "private" items + if string.sub(k, 1, 1) == "_" then + return + end + + local val + -- Prioritise options that are specific to this strategy + if values[strategy_name] ~= nil and values[strategy_name][k] ~= nil then + val = values[strategy_name][k] + end + + -- Handle nested layout config values + if layout_strategies[k] and strategy_name ~= k and type(val) == "table" then + val = vim.tbl_deep_extend("force", default_layout_config[k], val) + end + + if val == nil and values[k] ~= nil then + val = values[k] + end + + if val == nil then + if default_layout_config[strategy_name] ~= nil and default_layout_config[strategy_name][k] ~= nil then + val = default_layout_config[strategy_name][k] + else + val = default_layout_config[k] + end + end + + return val + end + + -- Always set the values passed first. + for k in pairs(values) do + if not valid_configuration_keys[k] then + -- TODO: At some point we'll move to error here, + -- but it's a bit annoying to just straight up crash everyone's stuff. + vim.api.nvim_err_writeln( + string.format( + "Unsupported layout_config key for the %s strategy: %s\n%s", + strategy_name, + k, + vim.inspect(values) + ) + ) + end + + result[k] = get_value(k) + end + + -- And then set other valid keys via "inheritance" style extension + for k in pairs(valid_configuration_keys) do + if result[k] == nil then + result[k] = get_value(k) + end + end + + return result +end + +-- List of options that are shared by more than one layout. +local shared_options = { + width = { "How wide to make Telescope's entire layout", "See |resolver.resolve_width()|" }, + height = { "How tall to make Telescope's entire layout", "See |resolver.resolve_height()|" }, + mirror = "Flip the location of the results/prompt and preview windows", + scroll_speed = "The number of lines to scroll through the previewer", + prompt_position = { "Where to place prompt window.", "Available Values: 'bottom', 'top'" }, + anchor = { "Which edge/corner to pin the picker to", "See |resolver.resolve_anchor_pos()|" }, + anchor_padding = { + "Specifies an amount of additional padding around the anchor", + "Values should be a positive integer", + }, +} + +-- Used for generating vim help documentation. +layout_strategies._format = function(name) + local strategy_config = layout_strategies._configurations[name] + if vim.tbl_isempty(strategy_config) then + return {} + end + + local results = { "
", "`picker.layout_config` shared options:" }
+
+  local strategy_keys = vim.tbl_keys(strategy_config)
+  table.sort(strategy_keys, function(a, b)
+    return a < b
+  end)
+
+  local add_value = function(k, val)
+    if type(val) == "string" then
+      table.insert(results, string.format("  - %s: %s", k, val))
+    elseif type(val) == "table" then
+      table.insert(results, string.format("  - %s:", k))
+      for _, line in ipairs(val) do
+        table.insert(results, string.format("    - %s", line))
+      end
+    else
+      error(string.format("expected string or table but found '%s'", type(val)))
+    end
+  end
+
+  for _, k in ipairs(strategy_keys) do
+    if shared_options[k] then
+      add_value(k, strategy_config[k])
+    end
+  end
+
+  table.insert(results, "")
+  table.insert(results, "`picker.layout_config` unique options:")
+
+  for _, k in ipairs(strategy_keys) do
+    if not shared_options[k] then
+      add_value(k, strategy_config[k])
+    end
+  end
+
+  table.insert(results, "
") + return results +end + +--@param name string: the name to be assigned to the layout +--@param layout_config table: table where keys are the available options for the layout +--@param layout function: function with signature +-- function(self, max_columns, max_lines, layout_config): table +-- the returned table is the sizing and location information for the parts of the picker +--@retun function: wrapped function that inputs a validated layout_config into the `layout` function +local function make_documented_layout(name, layout_config, layout) + -- Save configuration data to be used by documentation + layout_strategies._configurations[name] = layout_config + + -- Return new function that always validates configuration + return function(self, max_columns, max_lines, override_layout) + return layout( + self, + max_columns, + max_lines, + validate_layout_config( + name, + layout_config, + vim.tbl_deep_extend("keep", vim.F.if_nil(override_layout, {}), vim.F.if_nil(self.layout_config, {})) + ) + ) + end +end + +--- Horizontal layout has two columns, one for the preview +--- and one for the prompt and results. +--- +---
+--- ┌──────────────────────────────────────────────────┐
+--- │                                                  │
+--- │    ┌───────────────────┐┌───────────────────┐    │
+--- │    │                   ││                   │    │
+--- │    │                   ││                   │    │
+--- │    │                   ││                   │    │
+--- │    │      Results      ││                   │    │
+--- │    │                   ││      Preview      │    │
+--- │    │                   ││                   │    │
+--- │    │                   ││                   │    │
+--- │    └───────────────────┘│                   │    │
+--- │    ┌───────────────────┐│                   │    │
+--- │    │      Prompt       ││                   │    │
+--- │    └───────────────────┘└───────────────────┘    │
+--- │                                                  │
+--- └──────────────────────────────────────────────────┘
+--- 
+---@eval { ["description"] = require('telescope.pickers.layout_strategies')._format("horizontal") } +--- +layout_strategies.horizontal = make_documented_layout( + "horizontal", + vim.tbl_extend("error", shared_options, { + preview_width = { "Change the width of Telescope's preview window", "See |resolver.resolve_width()|" }, + preview_cutoff = "When columns are less than this value, the preview will be disabled", + }), + function(self, max_columns, max_lines, layout_config) + local initial_options = p_window.get_initial_window_options(self) + local preview = initial_options.preview + local results = initial_options.results + local prompt = initial_options.prompt + + local tbln + max_lines, tbln = calc_tabline(max_lines) + + local width_opt = layout_config.width + local width = resolve.resolve_width(width_opt)(self, max_columns, max_lines) + + local height_opt = layout_config.height + local height = resolve.resolve_height(height_opt)(self, max_columns, max_lines) + + local bs = get_border_size(self) + + local w_space + if self.previewer and max_columns >= layout_config.preview_cutoff then + -- Cap over/undersized width (with previewer) + width, w_space = calc_size_and_spacing(width, max_columns, bs, 2, 4, 1) + + preview.width = resolve.resolve_width(vim.F.if_nil(layout_config.preview_width, function(_, cols) + if cols < 150 then + return math.floor(cols * 0.4) + elseif cols < 200 then + return 80 + else + return 120 + end + end))(self, width, max_lines) + + results.width = width - preview.width - w_space + prompt.width = results.width + else + -- Cap over/undersized width (without previewer) + width, w_space = calc_size_and_spacing(width, max_columns, bs, 1, 2, 0) + + preview.width = 0 + results.width = width - preview.width - w_space + prompt.width = results.width + end + + local h_space + -- Cap over/undersized height + height, h_space = calc_size_and_spacing(height, max_lines, bs, 2, 4, 1) + + prompt.height = 1 + results.height = height - prompt.height - h_space + + if self.previewer then + preview.height = height - 2 * bs + else + preview.height = 0 + end + + local width_padding = math.floor((max_columns - width) / 2) + -- Default value is false, to use the normal horizontal layout + if not layout_config.mirror then + results.col = width_padding + bs + 1 + prompt.col = results.col + preview.col = results.col + results.width + 1 + bs + else + preview.col = width_padding + bs + 1 + prompt.col = preview.col + preview.width + 1 + bs + results.col = preview.col + preview.width + 1 + bs + end + + preview.line = math.floor((max_lines - height) / 2) + bs + 1 + if layout_config.prompt_position == "top" then + prompt.line = preview.line + results.line = prompt.line + prompt.height + 1 + bs + elseif layout_config.prompt_position == "bottom" then + results.line = preview.line + prompt.line = results.line + results.height + 1 + bs + else + error(string.format("Unknown prompt_position: %s\n%s", self.window.prompt_position, vim.inspect(layout_config))) + end + + local anchor = layout_config.anchor or "" + local anchor_padding = layout_config.anchor_padding or 1 + + local anchor_pos = resolve.resolve_anchor_pos(anchor, width, height, max_columns, max_lines, anchor_padding) + adjust_pos(anchor_pos, prompt, results, preview) + + if tbln then + prompt.line = prompt.line + 1 + results.line = results.line + 1 + preview.line = preview.line + 1 + end + + return { + preview = self.previewer and preview.width > 0 and preview, + results = results, + prompt = prompt, + } + end +) + +--- Centered layout with a combined block of the prompt +--- and results aligned to the middle of the screen. +--- The preview window is then placed in the remaining +--- space above or below, according to `anchor` or `mirror`. +--- Particularly useful for creating dropdown menus +--- (see |telescope.themes| and |themes.get_dropdown()|). +--- +--- Note that vertical anchoring, i.e. `anchor` containing +--- `"N"` or `"S"`, will override `mirror` config. For `"N"` +--- anchoring preview will be placed below prompt/result +--- block. For `"S"` anchoring preview will be placed above +--- prompt/result block. For horizontal only anchoring preview +--- will be placed according to `mirror` config, default is +--- above the prompt/result block. +--- +---
+--- ┌──────────────────────────────────────────────────┐
+--- │    ┌────────────────────────────────────────┐    │
+--- │    │                 Preview                │    │
+--- │    │                 Preview                │    │
+--- │    └────────────────────────────────────────┘    │
+--- │    ┌────────────────────────────────────────┐    │
+--- │    │                 Prompt                 │    │
+--- │    ├────────────────────────────────────────┤    │
+--- │    │                 Result                 │    │
+--- │    │                 Result                 │    │
+--- │    └────────────────────────────────────────┘    │
+--- │                                                  │
+--- │                                                  │
+--- │                                                  │
+--- │                                                  │
+--- └──────────────────────────────────────────────────┘
+--- 
+---@eval { ["description"] = require("telescope.pickers.layout_strategies")._format("center") } +--- +layout_strategies.center = make_documented_layout( + "center", + vim.tbl_extend("error", shared_options, { + preview_cutoff = "When lines are less than this value, the preview will be disabled", + }), + function(self, max_columns, max_lines, layout_config) + local initial_options = p_window.get_initial_window_options(self) + local preview = initial_options.preview + local results = initial_options.results + local prompt = initial_options.prompt + + local tbln + max_lines, tbln = calc_tabline(max_lines) + + -- This sets the width for the whole layout + local width_opt = layout_config.width + local width = resolve.resolve_width(width_opt)(self, max_columns, max_lines) + + -- This sets the height for the whole layout + local height_opt = layout_config.height + local height = resolve.resolve_height(height_opt)(self, max_columns, max_lines) + + local bs = get_border_size(self) + + local w_space + -- Cap over/undersized width + width, w_space = calc_size_and_spacing(width, max_columns, bs, 1, 2, 0) + + prompt.width = width - w_space + results.width = width - w_space + preview.width = width - w_space + + local h_space + -- Cap over/undersized height + height, h_space = calc_size_and_spacing(height, max_lines, bs, 2, 3, 0) + + prompt.height = 1 + results.height = height - prompt.height - h_space + + local topline = math.floor((max_lines / 2) - ((results.height + (2 * bs)) / 2) + 1) + -- Align the prompt and results so halfway up the screen is + -- in the middle of this combined block + if layout_config.prompt_position == "top" then + prompt.line = topline + results.line = prompt.line + 1 + bs + elseif layout_config.prompt_position == "bottom" then + results.line = topline + prompt.line = results.line + results.height + bs + if type(prompt.title) == "string" then + prompt.title = { { pos = "S", text = prompt.title } } + end + else + error(string.format("Unknown prompt_position: %s\n%s", self.window.prompt_position, vim.inspect(layout_config))) + end + + local width_padding = math.floor((max_columns - width) / 2) + bs + 1 + results.col, preview.col, prompt.col = width_padding, width_padding, width_padding + + local anchor = layout_config.anchor or "" + local anchor_padding = layout_config.anchor_padding or 1 + + local anchor_pos = resolve.resolve_anchor_pos(anchor, width, height, max_columns, max_lines, anchor_padding) + adjust_pos(anchor_pos, prompt, results, preview) + + -- Vertical anchoring (S or N variations) ignores layout_config.mirror + anchor = anchor:upper() + local mirror + if anchor:find "S" then + mirror = false + elseif anchor:find "N" then + mirror = true + else + mirror = layout_config.mirror + end + + -- Set preview position + local block_line = math.min(results.line, prompt.line) + if not mirror then -- Preview at top + preview.line = 1 + bs + preview.height = block_line - (2 + 2 * bs) + else -- Preview at bottom + preview.line = block_line + results.height + 2 + 2 * bs + preview.height = max_lines - preview.line - bs + 1 + end + + if not (self.previewer and max_lines >= layout_config.preview_cutoff) then + preview.height = 0 + end + + if tbln then + prompt.line = prompt.line + 1 + results.line = results.line + 1 + preview.line = preview.line + 1 + end + + return { + preview = self.previewer and preview.height > 0 and preview, + results = results, + prompt = prompt, + } + end +) + +--- Cursor layout dynamically positioned below the cursor if possible. +--- If there is no place below the cursor it will be placed above. +--- +---
+--- ┌──────────────────────────────────────────────────┐
+--- │                                                  │
+--- │   █                                              │
+--- │   ┌──────────────┐┌─────────────────────┐        │
+--- │   │    Prompt    ││      Preview        │        │
+--- │   ├──────────────┤│      Preview        │        │
+--- │   │    Result    ││      Preview        │        │
+--- │   │    Result    ││      Preview        │        │
+--- │   └──────────────┘└─────────────────────┘        │
+--- │                                         █        │
+--- │                                                  │
+--- │                                                  │
+--- │                                                  │
+--- │                                                  │
+--- │                                                  │
+--- └──────────────────────────────────────────────────┘
+--- 
+---@eval { ["description"] = require("telescope.pickers.layout_strategies")._format("cursor") } +layout_strategies.cursor = make_documented_layout( + "cursor", + vim.tbl_extend("error", { + width = shared_options.width, + height = shared_options.height, + scroll_speed = shared_options.scroll_speed, + }, { + preview_width = { "Change the width of Telescope's preview window", "See |resolver.resolve_width()|" }, + preview_cutoff = "When columns are less than this value, the preview will be disabled", + }), + function(self, max_columns, max_lines, layout_config) + local initial_options = p_window.get_initial_window_options(self) + local preview = initial_options.preview + local results = initial_options.results + local prompt = initial_options.prompt + local winid = self.original_win_id + + local height_opt = layout_config.height + local height = resolve.resolve_height(height_opt)(self, max_columns, max_lines) + + local width_opt = layout_config.width + local width = resolve.resolve_width(width_opt)(self, max_columns, max_lines) + + local bs = get_border_size(self) + + local h_space + -- Cap over/undersized height + height, h_space = calc_size_and_spacing(height, max_lines, bs, 2, 3, 0) + + prompt.height = 1 + results.height = height - prompt.height - h_space + preview.height = height - 2 * bs + + local w_space + if self.previewer and max_columns >= layout_config.preview_cutoff then + -- Cap over/undersized width (with preview) + width, w_space = calc_size_and_spacing(width, max_columns, bs, 2, 4, 0) + + preview.width = resolve.resolve_width(vim.F.if_nil(layout_config.preview_width, 2 / 3))(self, width, max_lines) + prompt.width = width - preview.width - w_space + results.width = prompt.width + else + -- Cap over/undersized width (without preview) + width, w_space = calc_size_and_spacing(width, max_columns, bs, 1, 2, 0) + + preview.width = 0 + prompt.width = width - w_space + results.width = prompt.width + end + + local position = vim.api.nvim_win_get_position(winid) + local winbar = (function() + if vim.fn.exists "&winbar" == 1 then + return vim.wo[winid].winbar == "" and 0 or 1 + end + return 0 + end)() + local top_left = { + line = vim.api.nvim_win_call(winid, vim.fn.winline) + position[1] + bs + winbar, + col = vim.api.nvim_win_call(winid, vim.fn.wincol) + position[2], + } + local bot_right = { + line = top_left.line + height - 1, + col = top_left.col + width - 1, + } + + if bot_right.line > max_lines then + -- position above current line + top_left.line = top_left.line - height - 1 + end + if bot_right.col >= max_columns then + -- cap to the right of the screen + top_left.col = max_columns - width + end + + prompt.line = top_left.line + 1 + results.line = prompt.line + bs + 1 + preview.line = prompt.line + + prompt.col = top_left.col + 1 + results.col = prompt.col + preview.col = results.col + (bs * 2) + results.width + + return { + preview = self.previewer and preview.width > 0 and preview, + results = results, + prompt = prompt, + } + end +) + +--- Vertical layout stacks the items on top of each other. +--- Particularly useful with thinner windows. +--- +---
+--- ┌──────────────────────────────────────────────────┐
+--- │                                                  │
+--- │    ┌────────────────────────────────────────┐    │
+--- │    │                 Preview                │    │
+--- │    │                 Preview                │    │
+--- │    │                 Preview                │    │
+--- │    └────────────────────────────────────────┘    │
+--- │    ┌────────────────────────────────────────┐    │
+--- │    │                 Result                 │    │
+--- │    │                 Result                 │    │
+--- │    └────────────────────────────────────────┘    │
+--- │    ┌────────────────────────────────────────┐    │
+--- │    │                 Prompt                 │    │
+--- │    └────────────────────────────────────────┘    │
+--- │                                                  │
+--- └──────────────────────────────────────────────────┘
+--- 
+---@eval { ["description"] = require("telescope.pickers.layout_strategies")._format("vertical") } +--- +layout_strategies.vertical = make_documented_layout( + "vertical", + vim.tbl_extend("error", shared_options, { + preview_cutoff = "When lines are less than this value, the preview will be disabled", + preview_height = { "Change the height of Telescope's preview window", "See |resolver.resolve_height()|" }, + }), + function(self, max_columns, max_lines, layout_config) + local initial_options = p_window.get_initial_window_options(self) + local preview = initial_options.preview + local results = initial_options.results + local prompt = initial_options.prompt + + local tbln + max_lines, tbln = calc_tabline(max_lines) + + local width_opt = layout_config.width + local width = resolve.resolve_width(width_opt)(self, max_columns, max_lines) + + local height_opt = layout_config.height + local height = resolve.resolve_height(height_opt)(self, max_columns, max_lines) + + local bs = get_border_size(self) + + local w_space + -- Cap over/undersized width + width, w_space = calc_size_and_spacing(width, max_columns, bs, 1, 2, 0) + + prompt.width = width - w_space + results.width = prompt.width + preview.width = prompt.width + + local h_space + if self.previewer and max_lines >= layout_config.preview_cutoff then + -- Cap over/undersized height (with previewer) + height, h_space = calc_size_and_spacing(height, max_lines, bs, 3, 6, 2) + + preview.height = + resolve.resolve_height(vim.F.if_nil(layout_config.preview_height, 0.5))(self, max_columns, height) + else + -- Cap over/undersized height (without previewer) + height, h_space = calc_size_and_spacing(height, max_lines, bs, 2, 4, 1) + + preview.height = 0 + end + prompt.height = 1 + results.height = height - preview.height - prompt.height - h_space + + local width_padding = math.floor((max_columns - width) / 2) + bs + 1 + results.col, preview.col, prompt.col = width_padding, width_padding, width_padding + + local height_padding = math.floor((max_lines - height) / 2) + if not layout_config.mirror then + preview.line = height_padding + (1 + bs) + if layout_config.prompt_position == "top" then + prompt.line = (preview.height == 0) and preview.line or preview.line + preview.height + (1 + bs) + results.line = prompt.line + prompt.height + (1 + bs) + elseif layout_config.prompt_position == "bottom" then + results.line = (preview.height == 0) and preview.line or preview.line + preview.height + (1 + bs) + prompt.line = results.line + results.height + (1 + bs) + else + error(string.format("Unknown prompt_position: %s\n%s", self.window.prompt_position, vim.inspect(layout_config))) + end + else + if layout_config.prompt_position == "top" then + prompt.line = height_padding + (1 + bs) + results.line = prompt.line + prompt.height + (1 + bs) + preview.line = results.line + results.height + (1 + bs) + elseif layout_config.prompt_position == "bottom" then + results.line = height_padding + (1 + bs) + prompt.line = results.line + results.height + (1 + bs) + preview.line = prompt.line + prompt.height + (1 + bs) + else + error(string.format("Unknown prompt_position: %s\n%s", self.window.prompt_position, vim.inspect(layout_config))) + end + end + + local anchor = layout_config.anchor or "" + local anchor_padding = layout_config.anchor_padding or 1 + + local anchor_pos = resolve.resolve_anchor_pos(anchor, width, height, max_columns, max_lines, anchor_padding) + adjust_pos(anchor_pos, prompt, results, preview) + + if tbln then + prompt.line = prompt.line + 1 + results.line = results.line + 1 + preview.line = preview.line + 1 + end + + return { + preview = self.previewer and preview.height > 0 and preview, + results = results, + prompt = prompt, + } + end +) + +--- Flex layout swaps between `horizontal` and `vertical` strategies based on the window width +--- - Supports |layout_strategies.vertical| or |layout_strategies.horizontal| features +--- +---@eval { ["description"] = require("telescope.pickers.layout_strategies")._format("flex") } +--- +layout_strategies.flex = make_documented_layout( + "flex", + vim.tbl_extend("error", shared_options, { + flip_columns = "The number of columns required to move to horizontal mode", + flip_lines = "The number of lines required to move to horizontal mode", + vertical = "Options to pass when switching to vertical layout", + horizontal = "Options to pass when switching to horizontal layout", + }), + function(self, max_columns, max_lines, layout_config) + local flip_columns = vim.F.if_nil(layout_config.flip_columns, layout_config.horizontal.preview_cutoff) + local flip_lines = vim.F.if_nil(layout_config.flip_lines, layout_config.vertical.preview_cutoff) + + if max_columns < flip_columns and max_lines >= flip_lines then + self.__flex_strategy = "vertical" + self.layout_config.flip_columns = nil + self.layout_config.flip_lines = nil + return layout_strategies.vertical(self, max_columns, max_lines, layout_config.vertical) + else + self.__flex_strategy = "horizontal" + self.layout_config.flip_columns = nil + self.layout_config.flip_lines = nil + return layout_strategies.horizontal(self, max_columns, max_lines, layout_config.horizontal) + end + end +) + +layout_strategies.current_buffer = make_documented_layout("current_buffer", { + -- No custom options. + -- height, width ignored +}, function(self, _, _, _) + local initial_options = p_window.get_initial_window_options(self) + + local window_width = vim.api.nvim_win_get_width(0) + local window_height = vim.api.nvim_win_get_height(0) + + local preview = initial_options.preview + local results = initial_options.results + local prompt = initial_options.prompt + + local bs = get_border_size(self) + + -- Width + local width_padding = (1 + bs) -- TODO(l-kershaw): make this configurable + + prompt.width = window_width - 2 * width_padding + results.width = prompt.width + preview.width = prompt.width + + -- Height + local height_padding = (1 + bs) -- TODO(l-kershaw): make this configurable + + prompt.height = 1 + if self.previewer then + results.height = 10 -- TODO(l-kershaw): make this configurable + preview.height = window_height - results.height - prompt.height - 2 * (1 + bs) - 2 * height_padding + else + results.height = window_height - prompt.height - (1 + bs) - 2 * height_padding + preview.height = 0 + end + + local win_position = vim.api.nvim_win_get_position(0) + + local line = win_position[1] + if self.previewer then + preview.line = height_padding + line + 1 + results.line = preview.line + preview.height + (1 + bs) + prompt.line = results.line + results.height + (1 + bs) + else + results.line = height_padding + line + 1 + prompt.line = results.line + results.height + (1 + bs) + end + + local col = win_position[2] + width_padding + 1 + preview.col, results.col, prompt.col = col, col, col + + return { + preview = preview.height > 0 and preview, + results = results, + prompt = prompt, + } +end) + +--- Bottom pane can be used to create layouts similar to "ivy". +--- +--- For an easy ivy configuration, see |themes.get_ivy()| +layout_strategies.bottom_pane = make_documented_layout( + "bottom_pane", + vim.tbl_extend("error", shared_options, { + preview_width = { "Change the width of Telescope's preview window", "See |resolver.resolve_width()|" }, + preview_cutoff = "When columns are less than this value, the preview will be disabled", + }), + function(self, max_columns, max_lines, layout_config) + local initial_options = p_window.get_initial_window_options(self) + local results = initial_options.results + local prompt = initial_options.prompt + local preview = initial_options.preview + + local tbln + max_lines, tbln = calc_tabline(max_lines) + + local height = vim.F.if_nil(resolve.resolve_height(layout_config.height)(self, max_columns, max_lines), 25) + if type(layout_config.height) == "table" and type(layout_config.height.padding) == "number" then + -- Since bottom_pane only has padding at the top, we only need half as much padding in total + -- This doesn't match the vim help for `resolve.resolve_height`, but it matches expectations + height = math.floor((max_lines + height) / 2) + end + + local bs = get_border_size(self) + + -- Cap over/undersized height + height, _ = calc_size_and_spacing(height, max_lines, bs, 2, 3, 0) + + -- Height + prompt.height = 1 + results.height = height - prompt.height - (2 * bs) + preview.height = results.height - bs + + -- Width + prompt.width = max_columns - 2 * bs + if self.previewer and max_columns >= layout_config.preview_cutoff then + -- Cap over/undersized width (with preview) + local width, w_space = calc_size_and_spacing(max_columns, max_columns, bs, 2, 4, 0) + + preview.width = resolve.resolve_width(vim.F.if_nil(layout_config.preview_width, 0.5))(self, width, max_lines) + results.width = width - preview.width - w_space + else + results.width = prompt.width + preview.width = 0 + end + + -- Line + if layout_config.prompt_position == "top" then + prompt.line = max_lines - results.height - (1 + bs) + 1 + results.line = prompt.line + 1 + preview.line = results.line + bs + if results.border == true then + results.border = { 0, 1, 1, 1 } + end + if type(results.title) == "string" then + results.title = { { pos = "S", text = results.title } } + end + if type(preview.title) == "string" then + preview.title = { { pos = "S", text = preview.title } } + end + elseif layout_config.prompt_position == "bottom" then + results.line = max_lines - results.height - (1 + bs) + 1 + preview.line = results.line + prompt.line = max_lines - bs + if type(prompt.title) == "string" then + prompt.title = { { pos = "S", text = prompt.title } } + end + if results.border == true then + results.border = { 1, 1, 0, 1 } + end + else + error(string.format("Unknown prompt_position: %s\n%s", self.window.prompt_position, vim.inspect(layout_config))) + end + + -- Col + prompt.col = 0 -- centered + if layout_config.mirror and preview.width > 0 then + results.col = preview.width + (3 * bs) + 1 + preview.col = bs + 1 + else + results.col = bs + 1 + preview.col = results.width + (3 * bs) + 1 + end + + if tbln then + prompt.line = prompt.line + 1 + results.line = results.line + 1 + preview.line = preview.line + 1 + end + + return { + preview = self.previewer and preview.width > 0 and preview, + prompt = prompt, + results = results, + } + end +) + +layout_strategies._validate_layout_config = validate_layout_config + +return layout_strategies diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers/multi.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers/multi.lua new file mode 100644 index 0000000..50aa051 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers/multi.lua @@ -0,0 +1,50 @@ +local MultiSelect = {} +MultiSelect.__index = MultiSelect + +function MultiSelect:new() + return setmetatable({ + _entries = {}, + }, MultiSelect) +end + +function MultiSelect:get() + local marked_entries = {} + for entry, count in pairs(self._entries) do + table.insert(marked_entries, { count, entry }) + end + + table.sort(marked_entries, function(left, right) + return left[1] < right[1] + end) + + local selections = {} + for _, entry in ipairs(marked_entries) do + table.insert(selections, entry[2]) + end + + return selections +end + +function MultiSelect:is_selected(entry) + return self._entries[entry] +end + +local multi_select_count = 0 +function MultiSelect:add(entry) + multi_select_count = multi_select_count + 1 + self._entries[entry] = multi_select_count +end + +function MultiSelect:drop(entry) + self._entries[entry] = nil +end + +function MultiSelect:toggle(entry) + if self:is_selected(entry) then + self:drop(entry) + else + self:add(entry) + end +end + +return MultiSelect diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers/scroller.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers/scroller.lua new file mode 100644 index 0000000..a658f68 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers/scroller.lua @@ -0,0 +1,124 @@ +local scroller = {} + +local range_calculators = { + ascending = function(max_results, num_results) + return 0, math.min(max_results, num_results) + end, + + descending = function(max_results, num_results) + return math.max(max_results - num_results, 0), max_results + end, +} + +local scroll_calculators = { + cycle = function(range_fn) + return function(max_results, num_results, row) + local start, finish = range_fn(max_results, num_results) + + if row >= finish then + return start + elseif row < start then + return (finish - 1 < 0) and finish or finish - 1 + end + + return row + end + end, + + limit = function(range_fn) + return function(max_results, num_results, row) + local start, finish = range_fn(max_results, num_results) + + if row >= finish and finish > 0 then + return finish - 1 + elseif row < start then + return start + end + + return row + end + end, +} + +scroller.create = function(scroll_strategy, sorting_strategy) + local range_fn = range_calculators[sorting_strategy] + if not range_fn then + error(debug.traceback("Unknown sorting strategy: " .. sorting_strategy)) + end + + local scroll_fn = scroll_calculators[scroll_strategy] + if not scroll_fn then + error(debug.traceback("Unknown scroll strategy: " .. (scroll_strategy or ""))) + end + + local calculator = scroll_fn(range_fn) + return function(max_results, num_results, row) + local result = calculator(max_results, num_results, row) + + if result < 0 then + error( + string.format( + "Must never return a negative row: { result = %s, args = { %s %s %s } }", + result, + max_results, + num_results, + row + ) + ) + end + + if result > max_results then + error( + string.format( + "Must never exceed max results: { result = %s, args = { %s %s %s } }", + result, + max_results, + num_results, + row + ) + ) + end + + return result + end +end + +scroller.top = function(sorting_strategy, max_results, num_results) + if sorting_strategy == "ascending" then + return 0 + end + return (num_results > max_results) and 0 or (max_results - num_results) +end + +scroller.middle = function(sorting_strategy, max_results, num_results) + local mid_pos + + if sorting_strategy == "ascending" then + mid_pos = math.floor(num_results / 2) + else + mid_pos = math.floor(max_results - num_results / 2) + end + + return (num_results < max_results) and mid_pos or math.floor(max_results / 2) +end + +scroller.bottom = function(sorting_strategy, max_results, num_results) + if sorting_strategy == "ascending" then + return math.min(max_results, num_results) - 1 + end + return max_results - 1 +end + +scroller.better = function(sorting_strategy) + if sorting_strategy == "ascending" then + return -1 + else + return 1 + end +end + +scroller.worse = function(sorting_strategy) + return -(scroller.better(sorting_strategy)) +end + +return scroller diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers/window.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers/window.lua new file mode 100644 index 0000000..945a7e3 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/pickers/window.lua @@ -0,0 +1,49 @@ +local resolve = require "telescope.config.resolve" + +local p_window = {} + +function p_window.get_window_options(picker, max_columns, max_lines) + local layout_strategy = picker.layout_strategy + local getter = require("telescope.pickers.layout_strategies")[layout_strategy] + + if not getter then + error(string.format("'%s' is not a valid layout strategy", layout_strategy)) + end + + return getter(picker, max_columns, max_lines) +end + +function p_window.get_initial_window_options(picker) + local popup_border = resolve.win_option(picker.window.border) + local popup_borderchars = resolve.win_option(picker.window.borderchars) + + local preview = { + title = picker.preview_title, + border = popup_border.preview, + borderchars = popup_borderchars.preview, + enter = false, + highlight = false, + } + + local results = { + title = picker.results_title, + border = popup_border.results, + borderchars = popup_borderchars.results, + enter = false, + } + + local prompt = { + title = picker.prompt_title, + border = popup_border.prompt, + borderchars = popup_borderchars.prompt, + enter = true, + } + + return { + preview = preview, + results = results, + prompt = prompt, + } +end + +return p_window diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/previewers/buffer_previewer.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/previewers/buffer_previewer.lua new file mode 100644 index 0000000..2e04865 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/previewers/buffer_previewer.lua @@ -0,0 +1,1239 @@ +local from_entry = require "telescope.from_entry" +local Path = require "plenary.path" +local utils = require "telescope.utils" +local putils = require "telescope.previewers.utils" +local Previewer = require "telescope.previewers.previewer" +local conf = require("telescope.config").values +local global_state = require "telescope.state" + +local pscan = require "plenary.scandir" + +local buf_delete = utils.buf_delete +local git_command = utils.__git_command + +local previewers = {} + +local ns_previewer = vim.api.nvim_create_namespace "telescope.previewers" + +local has_file = 1 == vim.fn.executable "file" + +-- TODO(fdschmidt93) switch to Job once file_maker callbacks get cleaned up with plenary async +-- avoids SIGABRT from utils.get_os_command_output due to vim.time in fs_stat cb +local function capture(cmd, raw) + local f = assert(io.popen(cmd, "r")) + local s = assert(f:read "*a") + f:close() + if raw then + return s + end + s = string.gsub(s, "^%s+", "") + s = string.gsub(s, "%s+$", "") + s = string.gsub(s, "[\n\r]+", " ") + return s +end + +local function defaulter(f, default_opts) + default_opts = default_opts or {} + return { + new = function(opts) + if conf.preview == false and not opts.preview then + return false + end + opts.preview = type(opts.preview) ~= "table" and {} or opts.preview + if type(conf.preview) == "table" then + for k, v in pairs(conf.preview) do + opts.preview[k] = vim.F.if_nil(opts.preview[k], v) + end + end + return f(opts) + end, + __call = function() + local ok, err = pcall(f(default_opts)) + if not ok then + error(debug.traceback(err)) + end + end, + } +end + +-- modified vim.split to incorporate a timer +local function split(s, sep, plain, opts) + opts = opts or {} + local t = {} + for c in vim.gsplit(s, sep, plain) do + local line = opts.file_encoding and vim.iconv(c, opts.file_encoding, "utf8") or c + table.insert(t, line) + if opts.preview.timeout then + local diff_time = (vim.loop.hrtime() - opts.start_time) / 1e6 + if diff_time > opts.preview.timeout then + return + end + end + end + return t +end +local bytes_to_megabytes = math.pow(1024, 2) + +local color_hash = { + ["p"] = "TelescopePreviewPipe", + ["c"] = "TelescopePreviewCharDev", + ["d"] = "TelescopePreviewDirectory", + ["b"] = "TelescopePreviewBlock", + ["l"] = "TelescopePreviewLink", + ["s"] = "TelescopePreviewSocket", + ["."] = "TelescopePreviewNormal", + ["r"] = "TelescopePreviewRead", + ["w"] = "TelescopePreviewWrite", + ["x"] = "TelescopePreviewExecute", + ["-"] = "TelescopePreviewHyphen", + ["T"] = "TelescopePreviewSticky", + ["S"] = "TelescopePreviewSticky", + [2] = "TelescopePreviewSize", + [3] = "TelescopePreviewUser", + [4] = "TelescopePreviewGroup", + [5] = "TelescopePreviewDate", +} +color_hash[6] = function(line) + return color_hash[line:sub(1, 1)] +end + +local colorize_ls_long = function(bufnr, data, sections) + local windows_add = Path.path.sep == "\\" and 2 or 0 + for lnum, line in ipairs(data) do + local section = sections[lnum] + for i = 1, section[1].end_index - 1 do -- Highlight permissions + local c = line:sub(i, i) + vim.api.nvim_buf_add_highlight(bufnr, ns_previewer, color_hash[c], lnum - 1, i - 1, i) + end + for i = 2, #section do -- highlights size, (user, group), date and name + local hl_group = color_hash[i + (i ~= 2 and windows_add or 0)] + vim.api.nvim_buf_add_highlight( + bufnr, + ns_previewer, + type(hl_group) == "function" and hl_group(line) or hl_group, + lnum - 1, + section[i].start_index - 1, + section[i].end_index - 1 + ) + end + end +end + +local handle_directory_preview = function(filepath, bufnr, opts) + opts.preview.ls_short = vim.F.if_nil(opts.preview.ls_short, false) + + local set_colorize_lines + if opts.preview.ls_short then + set_colorize_lines = function(data, sections) + local PATH_SECTION = Path.path.sep == "\\" and 4 or 6 + local paths = {} + for i, line in ipairs(data) do + local section = sections[i][PATH_SECTION] + local path = line:sub(section.start_index, section.end_index) + table.insert(paths, path) + end + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, paths) + for i, path in ipairs(paths) do + local hl = color_hash[6](data[i]) + vim.api.nvim_buf_add_highlight(bufnr, ns_previewer, hl, i - 1, 0, #path) + end + end + else + set_colorize_lines = function(data, sections) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, data) + colorize_ls_long(bufnr, data, sections) + end + end + + pscan.ls_async(filepath, { + hidden = true, + group_directories_first = true, + on_exit = vim.schedule_wrap(function(data, sections) + set_colorize_lines(data, sections) + if opts.callback then + opts.callback(bufnr) + end + end), + }) +end + +local handle_file_preview = function(filepath, bufnr, stat, opts) + vim.schedule(function() + opts.ft = opts.use_ft_detect and putils.filetype_detect(filepath) + local possible_binary = false + if type(opts.preview.filetype_hook) == "function" and opts.ft ~= nil and opts.ft ~= "" then + if not opts.preview.filetype_hook(filepath, bufnr, opts) then + return + end + end + if opts.preview.check_mime_type == true and has_file and (opts.ft == nil or opts.ft == "") then + -- avoid SIGABRT in buffer previewer happening with utils.get_os_command_output + local mime_type = capture(string.format([[file --mime-type -b "%s"]], filepath)) + if putils.binary_mime_type(mime_type) then + if type(opts.preview.mime_hook) == "function" then + opts.preview.mime_hook(filepath, bufnr, opts) + return + else + possible_binary = true + end + end + if mime_type[2] == "json" then + opts.ft = "json" + end + end + + local mb_filesize = stat.size / bytes_to_megabytes + if opts.preview.filesize_limit then + if mb_filesize > opts.preview.filesize_limit then + if type(opts.preview.filesize_hook) == "function" then + opts.preview.filesize_hook(filepath, bufnr, opts) + else + putils.set_preview_message(bufnr, opts.winid, "File exceeds preview size limit", opts.preview.msg_bg_fillchar) + end + return + end + end + + opts.start_time = vim.loop.hrtime() + Path:new(filepath):_read_async(vim.schedule_wrap(function(data) + if not vim.api.nvim_buf_is_valid(bufnr) then + return + end + local processed_data = split(data, "[\r]?\n", nil, opts) + + if processed_data then + local ok = pcall(vim.api.nvim_buf_set_lines, bufnr, 0, -1, false, processed_data) + if not ok then + return + end + -- last resort, if ft is still empty at this point in time, + -- we need to determine the filetype using the buffer contents + if opts.ft == nil or opts.ft == "" then + opts.ft = vim.filetype.match { filename = filepath, buf = bufnr } + end + -- we need to attempt to call filetype hook at this point "again" + -- previously only if we had a valid filetype, now every time + -- also if there will never be a filetype + if type(opts.preview.filetype_hook) == "function" then + if not opts.preview.filetype_hook(filepath, bufnr, opts) then + return + end + end + -- if we still dont have a ft we need to display the binary message + if (opts.ft == nil or opts.ft == "") and possible_binary then + putils.set_preview_message(bufnr, opts.winid, "Binary cannot be previewed", opts.preview.msg_bg_fillchar) + return + end + + if opts.callback then + opts.callback(bufnr) + end + + if not (opts.preview.highlight_limit and mb_filesize > opts.preview.highlight_limit) then + putils.highlighter(bufnr, opts.ft, opts) + end + else + if type(opts.preview.timeout_hook) == "function" then + opts.preview.timeout_hook(filepath, bufnr, opts) + else + putils.set_preview_message(bufnr, opts.winid, "Previewer timed out", opts.preview.msg_bg_fillchar) + end + return + end + end)) + end) +end + +local PREVIEW_TIMEOUT_MS = 250 +local PREVIEW_FILESIZE_MB = 25 +local PREVIEW_HIGHLIGHT_MB = 1 + +previewers.file_maker = function(filepath, bufnr, opts) + opts = vim.F.if_nil(opts, {}) + opts.preview = vim.F.if_nil(opts.preview, {}) + opts.preview.timeout = vim.F.if_nil(opts.preview.timeout, PREVIEW_TIMEOUT_MS) + opts.preview.filesize_limit = vim.F.if_nil(opts.preview.filesize_limit, PREVIEW_FILESIZE_MB) + opts.preview.highlight_limit = vim.F.if_nil(opts.preview.highlight_limit, PREVIEW_HIGHLIGHT_MB) + opts.preview.msg_bg_fillchar = vim.F.if_nil(opts.preview.msg_bg_fillchar, "╱") + opts.preview.treesitter = vim.F.if_nil(opts.preview.treesitter, true) + if opts.use_ft_detect == nil then + opts.use_ft_detect = true + end + if opts.bufname ~= filepath then + if not vim.in_fast_event() then + filepath = utils.path_expand(filepath) + end + vim.loop.fs_stat(filepath, function(_, stat) + if not stat then + return + end + if stat.type == "directory" then + handle_directory_preview(filepath, bufnr, opts) + else + handle_file_preview(filepath, bufnr, stat, opts) + end + end) + else + if opts.callback then + if vim.in_fast_event() then + vim.schedule(function() + opts.callback(bufnr) + end) + else + opts.callback(bufnr) + end + end + end +end + +local search_cb_jump = function(self, bufnr, query) + if not query then + return + end + vim.api.nvim_buf_call(bufnr, function() + pcall(vim.fn.matchdelete, self.state.hl_id, self.state.winid) + vim.cmd "keepjumps norm! gg" + vim.fn.search(query, "W") + vim.cmd "norm! zz" + + self.state.hl_id = vim.fn.matchadd("TelescopePreviewMatch", query) + end) +end + +local search_teardown = function(self) + if self.state and self.state.hl_id then + pcall(vim.fn.matchdelete, self.state.hl_id, self.state.hl_win) + self.state.hl_id = nil + end +end + +local scroll_fn = function(self, direction) + if not self.state then + return + end + + local input = direction > 0 and [[]] or [[]] + local count = math.abs(direction) + + vim.api.nvim_win_call(self.state.winid, function() + vim.cmd([[normal! ]] .. count .. input) + end) +end + +local scroll_horizontal_fn = function(self, direction) + if not self.state then + return + end + + local input = direction > 0 and [[zl]] or [[zh]] + local count = math.abs(direction) + + vim.api.nvim_win_call(self.state.winid, function() + vim.api.nvim_win_set_option(self.state.winid, "virtualedit", "all") + vim.cmd([[normal! ]] .. count .. input) + end) +end + +previewers.new_buffer_previewer = function(opts) + opts = opts or {} + + assert(opts.define_preview, "define_preview is a required function") + assert(not opts.preview_fn, "preview_fn not allowed") + + local opt_setup = opts.setup + local opt_teardown = opts.teardown + + local old_bufs = {} + local bufname_table = {} + local preview_window_id + + local function get_bufnr(self) + if not self.state then + return nil + end + return self.state.bufnr + end + + local function set_bufnr(self, value) + if self.state then + self.state.bufnr = value + table.insert(old_bufs, value) + end + end + + local function get_bufnr_by_bufname(self, value) + if not self.state then + return nil + end + return bufname_table[value] + end + + local function set_bufname(self, value) + if self.state then + self.state.bufname = value + if value then + bufname_table[value] = get_bufnr(self) + end + end + end + + function opts.setup(self) + local state = {} + if opt_setup then + state = vim.tbl_deep_extend("force", state, opt_setup(self)) + end + return state + end + + function opts.teardown(self) + if opt_teardown then + opt_teardown(self) + end + + local last_nr + if opts.keep_last_buf then + last_nr = global_state.get_global_key "last_preview_bufnr" + -- Push in another buffer so the last one will not be cleaned up + if preview_window_id then + local bufnr = vim.api.nvim_create_buf(false, true) + utils.win_set_buf_noautocmd(preview_window_id, bufnr) + end + end + + set_bufnr(self, nil) + set_bufname(self, nil) + + for _, bufnr in ipairs(old_bufs) do + if bufnr ~= last_nr then + buf_delete(bufnr) + end + end + -- enable resuming picker with existing previewer to avoid lookup of deleted bufs + bufname_table = {} + end + + function opts.preview_fn(self, entry, status) + local preview_winid = status.layout.preview and status.layout.preview.winid + if get_bufnr(self) == nil then + set_bufnr(self, vim.api.nvim_win_get_buf(preview_winid)) + preview_window_id = preview_winid + end + + if opts.get_buffer_by_name and get_bufnr_by_bufname(self, opts.get_buffer_by_name(self, entry)) then + self.state.bufname = opts.get_buffer_by_name(self, entry) + self.state.bufnr = get_bufnr_by_bufname(self, self.state.bufname) + utils.win_set_buf_noautocmd(preview_winid, self.state.bufnr) + else + local bufnr = vim.api.nvim_create_buf(false, true) + set_bufnr(self, bufnr) + vim.api.nvim_buf_set_option(bufnr, "modifiable", true) + + vim.schedule(function() + if vim.api.nvim_buf_is_valid(bufnr) then + utils.win_set_buf_noautocmd(preview_winid, bufnr) + end + end) + + vim.api.nvim_win_set_option(preview_winid, "winhl", "Normal:TelescopePreviewNormal") + vim.api.nvim_win_set_option(preview_winid, "signcolumn", "no") + vim.api.nvim_win_set_option(preview_winid, "foldlevel", 100) + vim.api.nvim_win_set_option(preview_winid, "wrap", false) + vim.api.nvim_win_set_option(preview_winid, "scrollbind", false) + + self.state.winid = preview_winid + self.state.bufname = nil + end + + if opts.keep_last_buf then + global_state.set_global_key("last_preview_bufnr", self.state.bufnr) + end + + opts.define_preview(self, entry, status) + + vim.schedule(function() + if not self or not self.state or not self.state.bufnr then + return + end + + if vim.api.nvim_buf_is_valid(self.state.bufnr) then + vim.api.nvim_buf_call(self.state.bufnr, function() + vim.api.nvim_exec_autocmds("User", { + pattern = "TelescopePreviewerLoaded", + data = { + title = entry.preview_title, + bufname = self.state.bufname, + filetype = putils.filetype_detect(self.state.bufname or ""), + }, + }) + end) + end + end) + + if opts.get_buffer_by_name then + set_bufname(self, opts.get_buffer_by_name(self, entry)) + end + end + + if not opts.scroll_fn then + opts.scroll_fn = scroll_fn + end + + if not opts.scroll_horizontal_fn then + opts.scroll_horizontal_fn = scroll_horizontal_fn + end + + return Previewer:new(opts) +end + +previewers.cat = defaulter(function(opts) + opts = opts or {} + local cwd = opts.cwd or vim.loop.cwd() + return previewers.new_buffer_previewer { + title = "File Preview", + dyn_title = function(_, entry) + return Path:new(from_entry.path(entry, false, false)):normalize(cwd) + end, + + get_buffer_by_name = function(_, entry) + return from_entry.path(entry, false, false) + end, + + define_preview = function(self, entry) + local p = from_entry.path(entry, true, false) + if p == nil or p == "" then + return + end + conf.buffer_previewer_maker(p, self.state.bufnr, { + bufname = self.state.bufname, + winid = self.state.winid, + preview = opts.preview, + file_encoding = opts.file_encoding, + }) + end, + } +end, {}) + +previewers.vimgrep = defaulter(function(opts) + opts = opts or {} + local cwd = opts.cwd or vim.loop.cwd() + + local jump_to_line = function(self, bufnr, entry) + pcall(vim.api.nvim_buf_clear_namespace, bufnr, ns_previewer, 0, -1) + + if entry.lnum and entry.lnum > 0 then + local lnum, lnend = entry.lnum - 1, (entry.lnend or entry.lnum) - 1 + + local col, colend = 0, -1 + -- Both col delimiters should be provided for them to take effect. + -- This is to ensure that column range highlighting was opted in, as `col` + -- is already used to determine the buffer jump position elsewhere. + if entry.col and entry.colend then + col, colend = entry.col - 1, entry.colend - 1 + end + + for i = lnum, lnend do + pcall( + vim.api.nvim_buf_add_highlight, + bufnr, + ns_previewer, + "TelescopePreviewLine", + i, + i == lnum and col or 0, + i == lnend and colend or -1 + ) + end + + local middle_ln = math.floor(lnum + (lnend - lnum) / 2) + pcall(vim.api.nvim_win_set_cursor, self.state.winid, { middle_ln + 1, 0 }) + if bufnr ~= nil then + vim.api.nvim_buf_call(bufnr, function() + vim.cmd "norm! zz" + end) + end + end + end + + return previewers.new_buffer_previewer { + title = "Grep Preview", + dyn_title = function(_, entry) + return Path:new(from_entry.path(entry, false, false)):normalize(cwd) + end, + + get_buffer_by_name = function(_, entry) + return from_entry.path(entry, false, false) + end, + + define_preview = function(self, entry) + -- builtin.buffers: bypass path validation for terminal buffers that don't have appropriate path + local has_buftype = entry.bufnr + and vim.api.nvim_buf_is_valid(entry.bufnr) + and vim.api.nvim_buf_get_option(entry.bufnr, "buftype") ~= "" + or false + local p + if not has_buftype then + p = from_entry.path(entry, true, false) + if p == nil or p == "" then + return + end + end + + -- Workaround for unnamed buffer when using builtin.buffer + if entry.bufnr and (p == "[No Name]" or has_buftype) then + local lines = vim.api.nvim_buf_get_lines(entry.bufnr, 0, -1, false) + vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, lines) + -- schedule so that the lines are actually there and can be jumped onto when we call jump_to_line + vim.schedule(function() + jump_to_line(self, self.state.bufnr, entry) + end) + else + conf.buffer_previewer_maker(p, self.state.bufnr, { + bufname = self.state.bufname, + winid = self.state.winid, + preview = opts.preview, + callback = function(bufnr) + jump_to_line(self, bufnr, entry) + end, + file_encoding = opts.file_encoding, + }) + end + end, + } +end, {}) + +previewers.qflist = previewers.vimgrep + +previewers.ctags = defaulter(function(opts) + local determine_jump = function(entry) + if entry.scode then + return function(self) + -- un-escape / then escape required + -- special chars for vim.fn.search() + -- ] ~ * + local scode = entry.scode:gsub([[\/]], "/"):gsub("[%]~*]", function(x) + return "\\" .. x + end) + + pcall(vim.fn.matchdelete, self.state.hl_id, self.state.winid) + vim.cmd "keepjumps norm! gg" + vim.fn.search(scode, "W") + vim.cmd "norm! zz" + + self.state.hl_id = vim.fn.matchadd("TelescopePreviewMatch", scode) + end + else + return function(self, bufnr) + if self.state.last_set_bufnr then + pcall(vim.api.nvim_buf_clear_namespace, self.state.last_set_bufnr, ns_previewer, 0, -1) + end + pcall(vim.api.nvim_buf_add_highlight, bufnr, ns_previewer, "TelescopePreviewMatch", entry.lnum - 1, 0, -1) + pcall(vim.api.nvim_win_set_cursor, self.state.winid, { entry.lnum, 0 }) + self.state.last_set_bufnr = bufnr + end + end + end + + return previewers.new_buffer_previewer { + title = "Tags Preview", + teardown = function(self) + if self.state and self.state.hl_id then + pcall(vim.fn.matchdelete, self.state.hl_id, self.state.hl_win) + self.state.hl_id = nil + elseif self.state and self.state.last_set_bufnr and vim.api.nvim_buf_is_valid(self.state.last_set_bufnr) then + vim.api.nvim_buf_clear_namespace(self.state.last_set_bufnr, ns_previewer, 0, -1) + end + end, + + get_buffer_by_name = function(_, entry) + return entry.filename + end, + + define_preview = function(self, entry) + conf.buffer_previewer_maker(entry.filename, self.state.bufnr, { + bufname = self.state.bufname, + winid = self.state.winid, + preview = opts.preview, + callback = function(bufnr) + pcall(vim.api.nvim_buf_call, bufnr, function() + determine_jump(entry)(self, bufnr) + end) + end, + file_encoding = opts.file_encoding, + }) + end, + } +end, {}) + +previewers.builtin = defaulter(function(opts) + return previewers.new_buffer_previewer { + title = "Grep Preview", + teardown = search_teardown, + + get_buffer_by_name = function(_, entry) + return entry.filename + end, + + define_preview = function(self, entry) + local module_name = vim.fn.fnamemodify(vim.fn.fnamemodify(entry.filename, ":h"), ":t") + local text + if entry.text:sub(1, #module_name) ~= module_name then + text = module_name .. "." .. entry.text + else + text = entry.text:gsub("_", ".", 1) + end + + conf.buffer_previewer_maker(entry.filename, self.state.bufnr, { + bufname = self.state.bufname, + winid = self.state.winid, + preview = opts.preview, + callback = function(bufnr) + search_cb_jump(self, bufnr, text) + end, + file_encoding = opts.file_encoding, + }) + end, + } +end, {}) + +previewers.help = defaulter(function(opts) + return previewers.new_buffer_previewer { + title = "Help Preview", + teardown = search_teardown, + + get_buffer_by_name = function(_, entry) + return entry.filename + end, + + define_preview = function(self, entry) + local query = entry.cmd + query = query:sub(2) + query = [[\V]] .. query + + conf.buffer_previewer_maker(entry.filename, self.state.bufnr, { + bufname = self.state.bufname, + winid = self.state.winid, + preview = opts.preview, + callback = function(bufnr) + putils.highlighter(bufnr, "help", opts) + search_cb_jump(self, bufnr, query) + end, + file_encoding = opts.file_encoding, + }) + end, + } +end, {}) + +previewers.man = defaulter(function(opts) + local pager = utils.get_lazy_default(opts.PAGER, function() + return vim.fn.executable "col" == 1 and { "col", "-bx" } or { "cat" } + end) + return previewers.new_buffer_previewer { + title = "Man Preview", + get_buffer_by_name = function(_, entry) + return entry.value .. "/" .. entry.section + end, + + define_preview = function(self, entry) + local win_width = vim.api.nvim_win_get_width(self.state.winid) + putils.job_maker(vim.deepcopy(pager), self.state.bufnr, { + writer = { "man", entry.section, entry.value }, + env = { ["MANWIDTH"] = win_width, PATH = vim.env.PATH, MANPATH = vim.env.MANPATH }, + value = entry.value .. "/" .. entry.section, + bufname = self.state.bufname, + }) + putils.highlighter(self.state.bufnr, "man", opts) + end, + } +end) + +previewers.git_branch_log = defaulter(function(opts) + local highlight_buffer = function(bufnr, content) + for i = 1, #content do + local line = content[i] + local hstart, hend = line:find "[0-9a-fA-F]+" + if hstart then + if hend < #line then + pcall( + vim.api.nvim_buf_add_highlight, + bufnr, + ns_previewer, + "TelescopeResultsIdentifier", + i - 1, + hstart - 1, + hend + ) + end + end + local _, cstart = line:find "- %(" + if cstart then + local cend = string.find(line, "%) ") + if cend then + pcall( + vim.api.nvim_buf_add_highlight, + bufnr, + ns_previewer, + "TelescopeResultsConstant", + i - 1, + cstart - 1, + cend + ) + end + end + local dstart, _ = line:find " %(%d" + if dstart then + pcall( + vim.api.nvim_buf_add_highlight, + bufnr, + ns_previewer, + "TelescopeResultsSpecialComment", + i - 1, + dstart, + #line + ) + end + end + end + + return previewers.new_buffer_previewer { + title = "Git Branch Preview", + get_buffer_by_name = function(_, entry) + return entry.value + end, + + define_preview = function(self, entry) + local cmd = git_command({ + "--no-pager", + "log", + "--graph", + "--max-count=1000", -- prevent fork bombing with large repos + "--pretty=format:%h -%d %s (%cr)", + "--abbrev-commit", + "--date=relative", + entry.value, + }, opts) + + putils.job_maker(cmd, self.state.bufnr, { + value = entry.value, + bufname = self.state.bufname, + cwd = opts.cwd, + callback = function(bufnr, content) + if not content then + return + end + highlight_buffer(bufnr, content) + end, + }) + end, + } +end, {}) + +previewers.git_stash_diff = defaulter(function(opts) + return previewers.new_buffer_previewer { + title = "Git Stash Preview", + get_buffer_by_name = function(_, entry) + return entry.value + end, + + define_preview = function(self, entry, _) + local cmd = git_command({ "--no-pager", "stash", "show", "-p", entry.value }, opts) + putils.job_maker(cmd, self.state.bufnr, { + value = entry.value, + bufname = self.state.bufname, + cwd = opts.cwd, + callback = function(bufnr) + if vim.api.nvim_buf_is_valid(bufnr) then + putils.highlighter(bufnr, "diff", opts) + end + end, + }) + end, + } +end, {}) + +previewers.git_commit_diff_to_parent = defaulter(function(opts) + return previewers.new_buffer_previewer { + title = "Git Diff to Parent Preview", + teardown = search_teardown, + get_buffer_by_name = function(_, entry) + return entry.value + end, + + define_preview = function(self, entry) + local cmd = git_command({ "--no-pager", "diff", entry.value .. "^!" }, opts) + if opts.current_file then + table.insert(cmd, "--") + table.insert(cmd, opts.current_file) + end + + putils.job_maker(cmd, self.state.bufnr, { + value = entry.value, + bufname = self.state.bufname, + cwd = opts.cwd, + callback = function(bufnr) + if vim.api.nvim_buf_is_valid(bufnr) then + search_cb_jump(self, bufnr, opts.current_line) + putils.highlighter(bufnr, "diff", opts) + end + end, + }) + end, + } +end, {}) + +previewers.git_commit_diff_to_head = defaulter(function(opts) + return previewers.new_buffer_previewer { + title = "Git Diff to Head Preview", + teardown = search_teardown, + + get_buffer_by_name = function(_, entry) + return entry.value + end, + + define_preview = function(self, entry) + local cmd = git_command({ "--no-pager", "diff", "--cached", entry.value }, opts) + if opts.current_file then + table.insert(cmd, "--") + table.insert(cmd, opts.current_file) + end + + putils.job_maker(cmd, self.state.bufnr, { + value = entry.value, + bufname = self.state.bufname, + cwd = opts.cwd, + callback = function(bufnr) + if vim.api.nvim_buf_is_valid(bufnr) then + search_cb_jump(self, bufnr, opts.current_line) + putils.highlighter(bufnr, "diff", opts) + end + end, + }) + end, + } +end, {}) + +previewers.git_commit_diff_as_was = defaulter(function(opts) + return previewers.new_buffer_previewer { + title = "Git Show Preview", + teardown = search_teardown, + + get_buffer_by_name = function(_, entry) + return entry.value + end, + + define_preview = function(self, entry) + local cmd = git_command({ "--no-pager", "show" }, opts) + local cf = opts.current_file and Path:new(opts.current_file):make_relative(opts.cwd) + local value = cf and (entry.value .. ":" .. cf) or entry.value + local ft = cf and putils.filetype_detect(value) or "diff" + table.insert(cmd, value) + + putils.job_maker(cmd, self.state.bufnr, { + value = entry.value, + bufname = self.state.bufname, + cwd = opts.cwd, + callback = function(bufnr) + if vim.api.nvim_buf_is_valid(bufnr) then + search_cb_jump(self, bufnr, opts.current_line) + putils.highlighter(bufnr, ft, opts) + end + end, + }) + end, + } +end, {}) + +previewers.git_commit_message = defaulter(function(opts) + local hl_map = { + "TelescopeResultsIdentifier", + "TelescopePreviewUser", + "TelescopePreviewDate", + } + return previewers.new_buffer_previewer { + title = "Git Message", + get_buffer_by_name = function(_, entry) + return entry.value + end, + + define_preview = function(self, entry) + local cmd = git_command({ "--no-pager", "log", "-n 1", entry.value }, opts) + + putils.job_maker(cmd, self.state.bufnr, { + value = entry.value, + bufname = self.state.bufname, + cwd = opts.cwd, + callback = function(bufnr, content) + if not content then + return + end + for k, v in ipairs(hl_map) do + local _, s = content[k]:find "%s" + if s then + vim.api.nvim_buf_add_highlight(bufnr, ns_previewer, v, k - 1, s, #content[k]) + end + end + end, + }) + end, + } +end, {}) + +previewers.git_file_diff = defaulter(function(opts) + return previewers.new_buffer_previewer { + title = "Git File Diff Preview", + get_buffer_by_name = function(_, entry) + return entry.value + end, + + define_preview = function(self, entry) + if entry.status and (entry.status == "??" or entry.status == "A ") then + local p = from_entry.path(entry, true, false) + if p == nil or p == "" then + return + end + conf.buffer_previewer_maker(p, self.state.bufnr, { + bufname = self.state.bufname, + winid = self.state.winid, + preview = opts.preview, + file_encoding = opts.file_encoding, + }) + else + local cmd = git_command({ "--no-pager", "diff", "HEAD", "--", entry.value }, opts) + putils.job_maker(cmd, self.state.bufnr, { + value = entry.value, + bufname = self.state.bufname, + cwd = opts.cwd, + callback = function(bufnr) + if vim.api.nvim_buf_is_valid(bufnr) then + putils.highlighter(bufnr, "diff", opts) + end + end, + }) + end + end, + } +end, {}) + +previewers.autocommands = defaulter(function(_) + return previewers.new_buffer_previewer { + title = "Autocommands Preview", + teardown = function(self) + if self.state and self.state.last_set_bufnr and vim.api.nvim_buf_is_valid(self.state.last_set_bufnr) then + pcall(vim.api.nvim_buf_clear_namespace, self.state.last_set_bufnr, ns_previewer, 0, -1) + end + end, + + get_buffer_by_name = function(_, entry) + return entry.value.group_name + end, + + define_preview = function(self, entry, status) + local results = vim.tbl_filter(function(x) + return x.value.group_name == entry.value.group_name + end, status.picker.finder.results) + + if self.state.last_set_bufnr then + pcall(vim.api.nvim_buf_clear_namespace, self.state.last_set_bufnr, ns_previewer, 0, -1) + end + + local preview_winid = status.layout.preview and status.layout.preview.winid + + local selected_row = 0 + if self.state.bufname ~= entry.value.group_name then + local display = {} + table.insert(display, string.format(" augroup: %s - [ %d entries ]", entry.value.group_name, #results)) + -- TODO: calculate banner width/string in setup() + -- TODO: get column characters to be the same HL group as border + table.insert(display, string.rep("─", vim.fn.getwininfo(preview_winid)[1].width)) + + for idx, item in ipairs(results) do + if item == entry then + selected_row = idx + end + table.insert( + display, + string.format(" %-14s▏%-08s %s", item.value.event, item.value.pattern, item.value.command) + ) + end + + vim.api.nvim_buf_set_option(self.state.bufnr, "filetype", "vim") + vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, display) + vim.api.nvim_buf_add_highlight(self.state.bufnr, 0, "TelescopeBorder", 1, 0, -1) + else + for idx, item in ipairs(results) do + if item == entry then + selected_row = idx + break + end + end + end + + vim.api.nvim_buf_add_highlight(self.state.bufnr, ns_previewer, "TelescopePreviewLine", selected_row + 1, 0, -1) + -- set the cursor position after self.state.bufnr is connected to the + -- preview window (which is scheduled in new_buffer_previewer) + vim.schedule(function() + pcall(vim.api.nvim_win_set_cursor, preview_winid, { selected_row, 0 }) + end) + + self.state.last_set_bufnr = self.state.bufnr + end, + } +end, {}) + +previewers.highlights = defaulter(function(_) + return previewers.new_buffer_previewer { + title = "Highlights Preview", + teardown = function(self) + if self.state and self.state.last_set_bufnr and vim.api.nvim_buf_is_valid(self.state.last_set_bufnr) then + vim.api.nvim_buf_clear_namespace(self.state.last_set_bufnr, ns_previewer, 0, -1) + end + end, + + get_buffer_by_name = function() + return "highlights" + end, + + define_preview = function(self, entry) + if not self.state.bufname then + local output = utils.split_lines(vim.fn.execute "highlight") + local hl_groups = {} + for _, v in ipairs(output) do + if v ~= "" then + if v:sub(1, 1) == " " then + local part_of_old = v:match "%s+(.*)" + hl_groups[#hl_groups] = hl_groups[#hl_groups] .. part_of_old + else + table.insert(hl_groups, v) + end + end + end + + vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, hl_groups) + for k, v in ipairs(hl_groups) do + local startPos = string.find(v, "xxx", 1, true) - 1 + local endPos = startPos + 3 + local hlgroup = string.match(v, "([^ ]*)%s+.*") + pcall(vim.api.nvim_buf_add_highlight, self.state.bufnr, 0, hlgroup, k - 1, startPos, endPos) + end + end + + vim.schedule(function() + vim.api.nvim_buf_call(self.state.bufnr, function() + vim.cmd "keepjumps norm! gg" + vim.fn.search("^" .. entry.value .. " ") + local lnum = vim.api.nvim_win_get_cursor(self.state.winid)[1] + -- That one is actually a match but its better to use it like that then matchadd + pcall(vim.api.nvim_buf_clear_namespace, self.state.bufnr, ns_previewer, 0, -1) + vim.api.nvim_buf_add_highlight( + self.state.bufnr, + ns_previewer, + "TelescopePreviewMatch", + lnum - 1, + 0, + #entry.value + ) + -- we need to zz after the highlighting otherwise highlighting doesnt work + vim.cmd "norm! zz" + end) + end) + end, + } +end, {}) + +previewers.pickers = defaulter(function(_) + local ns_telescope_multiselection = vim.api.nvim_create_namespace "telescope_mulitselection" + local get_row = function(picker, preview_height, index) + if picker.sorting_strategy == "ascending" then + return index - 1 + else + return preview_height - index + end + end + return previewers.new_buffer_previewer { + + dyn_title = function(_, entry) + if entry.value.default_text and entry.value.default_text ~= "" then + return string.format("%s ─ %s", entry.value.prompt_title, entry.value.default_text) + end + return entry.value.prompt_title + end, + + get_buffer_by_name = function(_, entry) + return tostring(entry.value.prompt_bufnr) + end, + + teardown = function(self) + if self.state and self.state.last_set_bufnr and vim.api.nvim_buf_is_valid(self.state.last_set_bufnr) then + vim.api.nvim_buf_clear_namespace(self.state.last_set_bufnr, ns_telescope_multiselection, 0, -1) + end + end, + + define_preview = function(self, entry) + vim.api.nvim_buf_call(self.state.bufnr, function() + local ns_telescope_entry = vim.api.nvim_create_namespace "telescope_entry" + local preview_height = vim.api.nvim_win_get_height(self.state.winid) + + if self.state.bufname then + return + end + + local picker = entry.value + -- prefill buffer to be able to set lines individually + local placeholder = utils.repeated_table(preview_height, "") + vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, placeholder) + + for index = 1, math.min(preview_height, picker.manager:num_results()) do + local row = get_row(picker, preview_height, index) + local e = picker.manager:get_entry(index) + + local display, display_highlight + -- if-clause as otherwise function return values improperly unpacked + if type(e.display) == "function" then + display, display_highlight = e:display() + else + display = e.display + end + + vim.api.nvim_buf_set_lines(self.state.bufnr, row, row + 1, false, { display }) + + if display_highlight ~= nil then + for _, hl_block in ipairs(display_highlight) do + vim.api.nvim_buf_add_highlight( + self.state.bufnr, + ns_telescope_entry, + hl_block[2], + row, + hl_block[1][1], + hl_block[1][2] + ) + end + end + if picker._multi:is_selected(e) then + vim.api.nvim_buf_add_highlight( + self.state.bufnr, + ns_telescope_multiselection, + "TelescopeMultiSelection", + row, + 0, + -1 + ) + end + end + end) + end, + } +end, {}) + +previewers.display_content = defaulter(function(_) + return previewers.new_buffer_previewer { + define_preview = function(self, entry) + assert( + type(entry.preview_command) == "function", + "entry must provide a preview_command function which will put the content into the buffer" + ) + vim.api.nvim_buf_call(self.state.bufnr, function() + entry.preview_command(entry, self.state.bufnr) + end) + end, + } +end, {}) + +return previewers diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/previewers/init.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/previewers/init.lua new file mode 100644 index 0000000..b066040 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/previewers/init.lua @@ -0,0 +1,322 @@ +---@tag telescope.previewers +---@config { ["module"] = "telescope.previewers" } + +---@brief [[ +--- Provides a Previewer table that has to be implemented by each previewer. +--- To achieve this, this module also provides two wrappers that abstract most +--- of the work and make it really easy to create new previewers. +--- - `previewers.new_termopen_previewer` +--- - `previewers.new_buffer_previewer` +--- +--- Furthermore, there are a collection of previewers already defined which +--- can be used for every picker, as long as the entries of the picker provide +--- the necessary fields. The more important ones are +--- - `previewers.cat` +--- - `previewers.vimgrep` +--- - `previewers.qflist` +--- - `previewers.vim_buffer_cat` +--- - `previewers.vim_buffer_vimgrep` +--- - `previewers.vim_buffer_qflist` +--- +--- Previewers can be disabled for any builtin or custom picker by doing +--- :Telescope find_files previewer=false +---@brief ]] + +local Previewer = require "telescope.previewers.previewer" +local term_previewer = require "telescope.previewers.term_previewer" +local buffer_previewer = require "telescope.previewers.buffer_previewer" + +local previewers = {} + +--- This is the base table all previewers have to implement. It's possible to +--- write a wrapper for this because most previewers need to have the same +--- keys set. +--- Examples of wrappers are: +--- - `new_buffer_previewer` +--- - `new_termopen_previewer` +--- +--- To create a new table do following: +--- - `local new_previewer = Previewer:new(opts)` +--- +--- What `:new` expects is listed below +--- +--- The interface provides the following set of functions. All of them, besides +--- `new`, will be handled by telescope pickers. +--- - `:new(opts)` +--- - `:preview(entry, status)` +--- - `:teardown()` +--- - `:send_input(input)` +--- - `:scroll_fn(direction)` +--- - `:scroll_horizontal_fn(direction)` +--- +--- `Previewer:new()` expects a table as input with following keys: +--- - `setup` function(self): Will be called the first time preview will be +--- called. +--- - `teardown` function(self): Will be called on clean up. +--- - `preview_fn` function(self, entry, status): Will be called each time +--- a new entry was selected. +--- - `title` function(self): Will return the static title of the previewer. +--- - `dynamic_title` function(self, entry): Will return the dynamic title of +--- the previewer. Will only be called +--- when config value dynamic_preview_title +--- is true. +--- - `send_input` function(self, input): This is meant for +--- `termopen_previewer` and it can be +--- used to send input to the terminal +--- application, like less. +--- - `scroll_fn` function(self, direction): Used to make scrolling work. +--- - `scroll_horizontal_fn` function(self, direction): Used to make +--- horizontal scrolling work. +previewers.Previewer = Previewer + +--- A shorthand for creating a new Previewer. +--- The provided table will be forwarded to `Previewer:new(...)` +previewers.new = function(...) + return Previewer:new(...) +end + +--- Is a wrapper around Previewer and helps with creating a new +--- `termopen_previewer`. +--- +--- It requires you to specify one table entry `get_command(entry, status)`. +--- This `get_command` function has to return the terminal command that will be +--- executed for each entry. Example: +--- +--- get_command = function(entry, status) +--- return { 'bat', entry.path } +--- end +--- +--- +--- Additionally you can define: +--- - `title` a static title for example "File Preview" +--- - `dyn_title(self, entry)` a dynamic title function which gets called +--- when config value `dynamic_preview_title = true` +--- - `env` table: define environment variables to forward to the terminal +--- process. Example: +--- - `{ ['PAGER'] = '', ['MANWIDTH'] = 50 }` +--- +--- It's an easy way to get your first previewer going and it integrates well +--- with `bat` and `less`. Providing out of the box scrolling if the command +--- uses less. +--- +--- Furthermore, if `env` is not set, it will forward all `config.set_env` environment variables to +--- that terminal process. +previewers.new_termopen_previewer = term_previewer.new_termopen_previewer + +--- Provides a `termopen_previewer` which has the ability to display files. +--- It will always show the top of the file and has support for +--- `bat`(prioritized) and `cat`. Each entry has to provide either the field +--- `path` or `filename` in order to make this previewer work. +--- +--- The preferred way of using this previewer is like this +--- `require('telescope.config').values.cat_previewer` +--- This will respect user configuration and will use `buffer_previewers` in +--- case it's configured that way. +previewers.cat = term_previewer.cat + +--- Provides a `termopen_previewer` which has the ability to display files at +--- the provided line. It has support for `bat`(prioritized) and `cat`. +--- Each entry has to provide either the field `path` or `filename` and +--- a `lnum` field in order to make this previewer work. +--- +--- The preferred way of using this previewer is like this +--- `require('telescope.config').values.grep_previewer` +--- This will respect user configuration and will use `buffer_previewers` in +--- case it's configured that way. +previewers.vimgrep = term_previewer.vimgrep + +--- Provides a `termopen_previewer` which has the ability to display files at +--- the provided line or range. It has support for `bat`(prioritized) and +--- `cat`. Each entry has to provide either the field `path` or `filename`, +--- `lnum` and a `start` and `finish` range in order to make this previewer +--- work. +--- +--- The preferred way of using this previewer is like this +--- `require('telescope.config').values.qflist_previewer` +--- This will respect user configuration and will use buffer previewers in +--- case it's configured that way. +previewers.qflist = term_previewer.qflist + +--- An interface to instantiate a new `buffer_previewer`. +--- That means that the content actually lives inside a vim buffer which +--- enables us more control over the actual content. For example, we can +--- use `vim.fn.search` to jump to a specific line or reuse buffers/already +--- opened files more easily. +--- This interface is more complex than `termopen_previewer` but offers more +--- flexibility over your content. +--- It was designed to display files but was extended to also display the +--- output of terminal commands. +--- +--- In the following options, state table and general tips are mentioned to +--- make your experience with this previewer more seamless. +--- +--- +--- options: +--- - `define_preview = function(self, entry, status)` (required) +--- Is called for each selected entry, after each selection_move +--- (up or down) and is meant to handle things like reading file, +--- jump to line or attach a highlighter. +--- - `setup = function(self)` (optional) +--- Is called once at the beginning, before the preview for the first +--- entry is displayed. You can return a table of vars that will be +--- available in `self.state` in each `define_preview` call. +--- - `teardown = function(self)` (optional) +--- Will be called at the end, when the picker is being closed and is +--- meant to clean up everything that was allocated by the previewer. +--- The `buffer_previewer` will automatically clean up all created buffers. +--- So you only need to handle things that were introduced by you. +--- - `keep_last_buf = true` (optional) +--- Will not delete the last selected buffer. This would allow you to +--- reuse that buffer in the select action. For example, that buffer can +--- be opened in a new split, rather than recreating that buffer in +--- an action. To access the last buffer number: +--- `require('telescope.state').get_global_key("last_preview_bufnr")` +--- - `get_buffer_by_name = function(self, entry)` +--- Allows you to set a unique name for each buffer. This is used for +--- caching purposes. `self.state.bufname` will be nil if the entry was +--- never loaded or the unique name when it was loaded once. For example, +--- useful if you have one file but multiple entries. This happens for grep +--- and lsp builtins. So to make the cache work only load content if +--- `self.state.bufname ~= entry.your_unique_key` +--- - `title` a static title for example "File Preview" +--- - `dyn_title(self, entry)` a dynamic title function which gets called +--- when config value `dynamic_preview_title = true` +--- +--- `self.state` table: +--- - `self.state.bufnr` +--- Is the current buffer number, in which you have to write the loaded +--- content. +--- Don't create a buffer yourself, otherwise it's not managed by the +--- buffer_previewer interface and you will probably be better off +--- writing your own interface. +--- - self.state.winid +--- Current window id. Useful if you want to set the cursor to a provided +--- line number. +--- - self.state.bufname +--- Will return the current buffer name, if `get_buffer_by_name` is +--- defined. nil will be returned if the entry was never loaded or when +--- `get_buffer_by_name` is not set. +--- +--- Tips: +--- - If you want to display content of a terminal job, use: +--- `require('telescope.previewers.utils').job_maker(cmd, bufnr, opts)` +--- - `cmd` table: for example { 'git', 'diff', entry.value } +--- - `bufnr` number: in which the content will be written +--- - `opts` table: with following keys +--- - `bufname` string: used for cache +--- - `value` string: used for cache +--- - `mode` string: either "insert" or "append". "insert" is default +--- - `env` table: define environment variables. Example: +--- - `{ ['PAGER'] = '', ['MANWIDTH'] = 50 }` +--- - `cwd` string: define current working directory for job +--- - `callback` function(bufnr, content): will be called when job +--- is done. Content will be nil if job is already loaded. +--- So you can do highlighting only the first time the previewer +--- is created for that entry. +--- Use the returned `bufnr` and not `self.state.bufnr` in callback, +--- because state can already be changed at this point in time. +--- - If you want to attach a highlighter use: +--- - `require('telescope.previewers.utils').highlighter(bufnr, ft)` +--- - This will prioritize tree sitter highlighting if available for +--- environment and language. +--- - `require('telescope.previewers.utils').regex_highlighter(bufnr, ft)` +--- - `require('telescope.previewers.utils').ts_highlighter(bufnr, ft)` +--- - If you want to use `vim.fn.search` or similar you need to run it in +--- that specific buffer context. Do +--- +--- vim.api.nvim_buf_call(bufnr, function() +--- -- for example `search` and `matchadd` +--- end) +--- +--- to achieve that. +--- - If you want to read a file into the buffer it's best to use +--- `buffer_previewer_maker`. But access this function with +--- `require('telescope.config').values.buffer_previewer_maker` +--- because it can be redefined by users. +previewers.new_buffer_previewer = buffer_previewer.new_buffer_previewer + +--- A universal way of reading a file into a buffer previewer. +--- It handles async reading, cache, highlighting, displaying directories +--- and provides a callback which can be used, to jump to a line in the buffer. +---@param filepath string: String to the filepath, will be expanded +---@param bufnr number: Where the content will be written +---@param opts table: keys: `use_ft_detect`, `bufname` and `callback` +previewers.buffer_previewer_maker = buffer_previewer.file_maker + +--- A previewer that is used to display a file. It uses the `buffer_previewer` +--- interface and won't jump to the line. To integrate this one into your +--- own picker make sure that the field `path` or `filename` is set for +--- each entry. +--- The preferred way of using this previewer is like this +--- `require('telescope.config').values.file_previewer` +--- This will respect user configuration and will use `termopen_previewer` in +--- case it's configured that way. +previewers.vim_buffer_cat = buffer_previewer.cat + +--- A previewer that is used to display a file and jump to the provided line. +--- It uses the `buffer_previewer` interface. To integrate this one into your +--- own picker make sure that the field `path` or `filename` and `lnum` is set +--- in each entry. If the latter is not present, it will default to the first +--- line. Additionally, `lnend`, `col` and `colend` can be set to highlight a +--- text range instead of a single line. All line/column values are 1-indexed. +--- The preferred way of using this previewer is like this +--- `require('telescope.config').values.grep_previewer` +--- This will respect user configuration and will use `termopen_previewer` in +--- case it's configured that way. +previewers.vim_buffer_vimgrep = buffer_previewer.vimgrep + +--- Is the same as `vim_buffer_vimgrep` and only exists for consistency with +--- `term_previewers`. +--- +--- The preferred way of using this previewer is like this +--- `require('telescope.config').values.qflist_previewer` +--- This will respect user configuration and will use `termopen_previewer` in +--- case it's configured that way. +previewers.vim_buffer_qflist = buffer_previewer.qflist + +--- A previewer that shows a log of a branch as graph +previewers.git_branch_log = buffer_previewer.git_branch_log + +--- A previewer that shows a diff of a stash +previewers.git_stash_diff = buffer_previewer.git_stash_diff + +--- A previewer that shows a diff of a commit to a parent commit.
+--- The run command is `git --no-pager diff SHA^! -- $CURRENT_FILE` +--- +--- The current file part is optional. So is only uses it with bcommits. +previewers.git_commit_diff_to_parent = buffer_previewer.git_commit_diff_to_parent + +--- A previewer that shows a diff of a commit to head.
+--- The run command is `git --no-pager diff --cached $SHA -- $CURRENT_FILE` +--- +--- The current file part is optional. So is only uses it with bcommits. +previewers.git_commit_diff_to_head = buffer_previewer.git_commit_diff_to_head + +--- A previewer that shows a diff of a commit as it was.
+--- The run command is `git --no-pager show $SHA:$CURRENT_FILE` or `git --no-pager show $SHA` +previewers.git_commit_diff_as_was = buffer_previewer.git_commit_diff_as_was + +--- A previewer that shows the commit message of a diff.
+--- The run command is `git --no-pager log -n 1 $SHA` +previewers.git_commit_message = buffer_previewer.git_commit_message + +--- A previewer that shows the current diff of a file. Used in git_status.
+--- The run command is `git --no-pager diff $FILE` +previewers.git_file_diff = buffer_previewer.git_file_diff + +previewers.ctags = buffer_previewer.ctags +previewers.builtin = buffer_previewer.builtin +previewers.help = buffer_previewer.help +previewers.man = buffer_previewer.man +previewers.autocommands = buffer_previewer.autocommands +previewers.highlights = buffer_previewer.highlights +previewers.pickers = buffer_previewer.pickers + +--- A deprecated way of displaying content more easily. Was written at a time, +--- where the buffer_previewer interface wasn't present. Nowadays it's easier +--- to just use this. We will keep it around for backwards compatibility +--- because some extensions use it. +--- It doesn't use cache or some other clever tricks. +previewers.display_content = buffer_previewer.display_content + +return previewers diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/previewers/previewer.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/previewers/previewer.lua new file mode 100644 index 0000000..a02d5bf --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/previewers/previewer.lua @@ -0,0 +1,107 @@ +local utils = require "telescope.utils" + +local Previewer = {} +Previewer.__index = Previewer + +local force_function_wrap = function(value) + if value ~= nil then + if type(value) ~= "function" then + return function() + return tostring(value) + end + else + return value + end + end +end + +function Previewer:new(opts) + opts = opts or {} + + return setmetatable({ + state = nil, + _title_fn = force_function_wrap(opts.title), + _dyn_title_fn = force_function_wrap(opts.dyn_title), + _setup_func = opts.setup, + _teardown_func = opts.teardown, + _send_input = opts.send_input, + _scroll_fn = opts.scroll_fn, + _scroll_horizontal_fn = opts.scroll_horizontal_fn, + preview_fn = opts.preview_fn, + _empty_bufnr = nil, + }, Previewer) +end + +function Previewer:preview(entry, status) + if not entry then + if not self._empty_bufnr then + self._empty_bufnr = vim.api.nvim_create_buf(false, true) + end + + if vim.api.nvim_buf_is_valid(self._empty_bufnr) then + vim.api.nvim_win_set_buf(status.layout.preview.winid, self._empty_bufnr) + end + return + end + + if not self.state then + if self._setup_func then + self.state = self:_setup_func(status) + else + self.state = {} + end + end + + return self:preview_fn(entry, status) +end + +function Previewer:title(entry, dynamic) + if dynamic == true and self._dyn_title_fn ~= nil then + if entry == nil then + if self._title_fn ~= nil then + return self:_title_fn() + else + return "" + end + end + return self:_dyn_title_fn(entry) + end + if self._title_fn ~= nil then + return self:_title_fn() + end +end + +function Previewer:teardown() + if self._empty_bufnr then + utils.buf_delete(self._empty_bufnr) + end + if self._teardown_func then + self:_teardown_func() + end +end + +function Previewer:send_input(input) + if self._send_input then + self:_send_input(input) + else + vim.api.nvim_err_writeln "send_input is not defined for this previewer" + end +end + +function Previewer:scroll_fn(direction) + if self._scroll_fn then + self:_scroll_fn(direction) + else + vim.api.nvim_err_writeln "scroll_fn is not defined for this previewer" + end +end + +function Previewer:scroll_horizontal_fn(direction) + if self._scroll_horizontal_fn then + self:_scroll_horizontal_fn(direction) + else + vim.api.nvim_err_writeln "scroll_horizontal_fn is not defined for this previewer" + end +end + +return Previewer diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/previewers/term_previewer.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/previewers/term_previewer.lua new file mode 100644 index 0000000..bf6f40c --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/previewers/term_previewer.lua @@ -0,0 +1,341 @@ +local conf = require("telescope.config").values +local utils = require "telescope.utils" +local Path = require "plenary.path" +local from_entry = require "telescope.from_entry" +local Previewer = require "telescope.previewers.previewer" +local putil = require "telescope.previewers.utils" + +local defaulter = utils.make_default_callable + +local previewers = {} + +-- TODO: Should play with these some more, ty @clason +local bat_options = { "--style=plain", "--color=always", "--paging=always" } +local has_less = (vim.fn.executable "less" == 1) and conf.use_less + +local get_file_stat = function(filename) + return vim.loop.fs_stat(utils.path_expand(filename)) or {} +end + +local list_dir = (function() + if vim.fn.has "win32" == 1 then + return function(dirname) + return { "cmd.exe", "/c", "dir", utils.path_expand(dirname) } + end + else + return function(dirname) + return { "ls", "-la", utils.path_expand(dirname) } + end + end +end)() + +local bat_maker = function(filename, lnum, start, finish) + if get_file_stat(filename).type == "directory" then + return list_dir(filename) + end + + local command = { "bat" } + + if lnum then + vim.list_extend(command, { "--highlight-line", lnum }) + end + + if has_less then + if start then + vim.list_extend(command, { "--pager", string.format("less -RS +%s", start) }) + else + vim.list_extend(command, { "--pager", "less -RS" }) + end + else + if start and finish then + vim.list_extend(command, { "-r", string.format("%s:%s", start, finish) }) + end + end + + return utils.flatten { + command, + bat_options, + "--", + utils.path_expand(filename), + } +end + +local cat_maker = function(filename, _, start, _) + if get_file_stat(filename).type == "directory" then + return list_dir(filename) + end + + if 1 == vim.fn.executable "file" then + local mime_type = utils.get_os_command_output({ "file", "--mime-type", "-b", filename })[1] + if putil.binary_mime_type(mime_type) then + return { "echo", "Binary file found. These files cannot be displayed!" } + end + end + + if has_less then + if start then + return { "less", "-RS", string.format("+%s", start), utils.path_expand(filename) } + else + return { "less", "-RS", utils.path_expand(filename) } + end + else + return { + "cat", + "--", + utils.path_expand(filename), + } + end +end + +local get_maker = function(opts) + local maker = opts.maker + if not maker and 1 == vim.fn.executable "bat" then + maker = bat_maker + elseif not maker and 1 == vim.fn.executable "cat" then + maker = cat_maker + end + + if not maker then + error "Needs maker" + end + + return maker +end + +previewers.new_termopen_previewer = function(opts) + opts = opts or {} + + assert(opts.get_command, "get_command is a required function") + assert(not opts.preview_fn, "preview_fn not allowed") + + local opt_setup = opts.setup + local opt_teardown = opts.teardown + + local old_bufs = {} + local bufentry_table = {} + local term_ids = {} + + local function get_term_id(self) + if self.state then + return self.state.termopen_id + end + end + + local function get_bufnr(self) + if self.state then + return self.state.termopen_bufnr + end + end + + local function set_term_id(self, value) + if self.state and term_ids[self.state.termopen_bufnr] == nil then + term_ids[self.state.termopen_bufnr] = value + self.state.termopen_id = value + end + end + + local function set_bufnr(self, value) + if get_bufnr(self) then + table.insert(old_bufs, get_bufnr(self)) + end + if self.state then + self.state.termopen_bufnr = value + end + end + + local function get_bufnr_by_bufentry(self, value) + if self.state then + return bufentry_table[value] + end + end + + local function set_bufentry(self, value) + if self.state and value then + bufentry_table[value] = get_bufnr(self) + end + end + + function opts.setup(self) + local state = {} + if opt_setup then + state = vim.tbl_deep_extend("force", state, opt_setup(self)) + end + return state + end + + function opts.teardown(self) + if opt_teardown then + opt_teardown(self) + end + + set_bufnr(self, nil) + set_bufentry(self, nil) + + for _, bufnr in ipairs(old_bufs) do + local term_id = term_ids[bufnr] + if term_id and utils.job_is_running(term_id) then + vim.fn.jobstop(term_id) + end + utils.buf_delete(bufnr) + end + bufentry_table = {} + end + + function opts.preview_fn(self, entry, status) + local preview_winid = status.layout.preview and status.layout.preview.winid + if get_bufnr(self) == nil then + set_bufnr(self, vim.api.nvim_win_get_buf(preview_winid)) + end + + local prev_bufnr = get_bufnr_by_bufentry(self, entry) + if prev_bufnr then + set_bufnr(self, prev_bufnr) + utils.win_set_buf_noautocmd(preview_winid, self.state.termopen_bufnr) + self.state.termopen_id = term_ids[self.state.termopen_bufnr] + else + local bufnr = vim.api.nvim_create_buf(false, true) + set_bufnr(self, bufnr) + utils.win_set_buf_noautocmd(preview_winid, bufnr) + + local term_opts = { + cwd = opts.cwd or vim.loop.cwd(), + env = opts.env or conf.set_env, + } + + local cmd = opts.get_command(entry, status) + if cmd then + vim.api.nvim_buf_call(bufnr, function() + set_term_id(self, vim.fn.termopen(cmd, term_opts)) + end) + end + set_bufentry(self, entry) + end + end + + if not opts.send_input then + function opts.send_input(self, input) + local termcode = vim.api.nvim_replace_termcodes(input, true, false, true) + + local term_id = get_term_id(self) + if term_id then + if not utils.job_is_running(term_id) then + return + end + + vim.fn.chansend(term_id, termcode) + end + end + end + + if not opts.scroll_fn then + function opts.scroll_fn(self, direction) + if not self.state then + return + end + + local input = direction > 0 and "d" or "u" + local count = math.abs(direction) + + self:send_input(count .. input) + end + end + + return Previewer:new(opts) +end + +previewers.cat = defaulter(function(opts) + opts = opts or {} + + local maker = get_maker(opts) + local cwd = opts.cwd or vim.loop.cwd() + + return previewers.new_termopen_previewer { + title = "File Preview", + dyn_title = function(_, entry) + return Path:new(from_entry.path(entry, false, false)):normalize(cwd) + end, + + get_command = function(entry) + local p = from_entry.path(entry, true, false) + if p == nil or p == "" then + return + end + + return maker(p) + end, + } +end, {}) + +previewers.vimgrep = defaulter(function(opts) + opts = opts or {} + + local maker = get_maker(opts) + local cwd = opts.cwd or vim.loop.cwd() + + return previewers.new_termopen_previewer { + title = "Grep Preview", + dyn_title = function(_, entry) + return Path:new(from_entry.path(entry, false, false)):normalize(cwd) + end, + + get_command = function(entry, status) + local win_id = status.layout.preview and status.layout.preview.winid + local height = vim.api.nvim_win_get_height(win_id) + + local p = from_entry.path(entry, true, false) + if p == nil or p == "" then + return + end + if entry.bufnr and (p == "[No Name]" or vim.api.nvim_buf_get_option(entry.bufnr, "buftype") ~= "") then + return + end + + local lnum = entry.lnum or 0 + + local context = math.floor(height / 2) + local start = math.max(0, lnum - context) + local finish = lnum + context + + return maker(p, lnum, start, finish) + end, + } +end, {}) + +previewers.qflist = defaulter(function(opts) + opts = opts or {} + + local maker = get_maker(opts) + local cwd = opts.cwd or vim.loop.cwd() + + return previewers.new_termopen_previewer { + title = "Grep Preview", + dyn_title = function(_, entry) + return Path:new(from_entry.path(entry, false, false)):normalize(cwd) + end, + + get_command = function(entry, status) + local win_id = status.layout.preview and status.layout.preview.winid + local height = vim.api.nvim_win_get_height(win_id) + + local p = from_entry.path(entry, true, false) + if p == nil or p == "" then + return + end + local lnum = entry.lnum + + local start, finish + if entry.start and entry.finish then + start = entry.start + finish = entry.finish + else + local context = math.floor(height / 2) + start = math.max(0, lnum - context) + finish = lnum + context + end + + return maker(p, lnum, start, finish) + end, + } +end, {}) + +return previewers diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/previewers/utils.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/previewers/utils.lua new file mode 100644 index 0000000..b430e5a --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/previewers/utils.lua @@ -0,0 +1,244 @@ +local ts_utils = require "telescope.utils" +local strings = require "plenary.strings" +local conf = require("telescope.config").values + +local Job = require "plenary.job" +local Path = require "plenary.path" + +local telescope_utils = require "telescope.utils" + +local utils = {} + +local detect_from_shebang = function(p) + local s = p:readbyterange(0, 256) + if s then + local lines = telescope_utils.split_lines(s) + return vim.filetype.match { contents = lines } + end +end + +local parse_modeline = function(tail) + if tail:find "vim:" then + return tail:match ".*:ft=([^: ]*):.*$" or "" + end +end + +local detect_from_modeline = function(p) + local s = p:readbyterange(-256, 256) + if s then + local lines = telescope_utils.split_lines(s) + local idx = lines[#lines] ~= "" and #lines or #lines - 1 + if idx >= 1 then + return parse_modeline(lines[idx]) + end + end +end + +utils.filetype_detect = function(filepath) + if type(filepath) ~= string then + filepath = tostring(filepath) + end + + local match = vim.filetype.match { filename = filepath } + if match and match ~= "" then + return match + end + + local p = Path:new(filepath) + if p and p:is_file() then + match = detect_from_shebang(p) + if match and match ~= "" then + return match + end + + match = detect_from_modeline(p) + if match and match ~= "" then + return match + end + end +end + +-- API helper functions for buffer previewer +--- Job maker for buffer previewer +utils.job_maker = function(cmd, bufnr, opts) + opts = opts or {} + opts.mode = opts.mode or "insert" + -- bufname and value are optional + -- if passed, they will be use as the cache key + -- if any of them are missing, cache will be skipped + if opts.bufname ~= opts.value or not opts.bufname or not opts.value then + local command = table.remove(cmd, 1) + local writer = (function() + if opts.writer ~= nil then + local wcommand = table.remove(opts.writer, 1) + return Job:new { + command = wcommand, + args = opts.writer, + env = opts.env, + cwd = opts.cwd, + } + end + end)() + + Job:new({ + command = command, + args = cmd, + env = opts.env, + cwd = opts.cwd, + writer = writer, + on_exit = vim.schedule_wrap(function(j) + if not vim.api.nvim_buf_is_valid(bufnr) then + return + end + if opts.mode == "append" then + vim.api.nvim_buf_set_lines(bufnr, -1, -1, false, j:result()) + elseif opts.mode == "insert" then + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, j:result()) + end + if opts.callback then + opts.callback(bufnr, j:result()) + end + end), + }):start() + else + if opts.callback then + opts.callback(bufnr) + end + end +end + +local function has_filetype(ft) + return ft and ft ~= "" +end + +--- Attach default highlighter which will choose between regex and ts +utils.highlighter = function(bufnr, ft, opts) + opts = vim.F.if_nil(opts, {}) + opts.preview = vim.F.if_nil(opts.preview, {}) + opts.preview.treesitter = (function() + if type(opts.preview) == "table" and opts.preview.treesitter then + return opts.preview.treesitter + end + if type(conf.preview) == "table" and conf.preview.treesitter then + return conf.preview.treesitter + end + if type(conf.preview) == "boolean" then + return conf.preview + end + -- We should never get here + return false + end)() + + if type(opts.preview.treesitter) == "boolean" then + local temp = { enable = opts.preview.treesitter } + opts.preview.treesitter = temp + end + + local ts_highlighting = (function() + if type(opts.preview.treesitter.enable) == "table" then + if vim.tbl_contains(opts.preview.treesitter.enable, ft) then + return true + end + return false + end + + if vim.tbl_contains(vim.F.if_nil(opts.preview.treesitter.disable, {}), ft) then + return false + end + + return opts.preview.treesitter.enable == nil or opts.preview.treesitter.enable == true + end)() + + local ts_success + if ts_highlighting then + ts_success = utils.ts_highlighter(bufnr, ft) + end + if not ts_highlighting or ts_success == false then + utils.regex_highlighter(bufnr, ft) + end +end + +--- Attach regex highlighter +utils.regex_highlighter = function(bufnr, ft) + if has_filetype(ft) then + return pcall(vim.api.nvim_buf_set_option, bufnr, "syntax", ft) + end + return false +end + +-- Attach ts highlighter +utils.ts_highlighter = function(bufnr, ft) + if has_filetype(ft) then + local lang = vim.treesitter.language.get_lang(ft) or ft + if lang and ts_utils.has_ts_parser(lang) then + return vim.treesitter.start(bufnr, lang) + end + end + return false +end + +utils.set_preview_message = function(bufnr, winid, message, fillchar) + fillchar = vim.F.if_nil(fillchar, "╱") + local height = vim.api.nvim_win_get_height(winid) + local width = vim.api.nvim_win_get_width(winid) + vim.api.nvim_buf_set_lines( + bufnr, + 0, + -1, + false, + ts_utils.repeated_table(height, table.concat(ts_utils.repeated_table(width, fillchar), "")) + ) + local anon_ns = vim.api.nvim_create_namespace "" + local padding = table.concat(ts_utils.repeated_table(#message + 4, " "), "") + local formatted_message = " " .. message .. " " + -- Populate lines table based on height + local lines = {} + if height == 1 then + lines[1] = formatted_message + else + for i = 1, math.min(height, 3), 1 do + if i % 2 == 0 then + lines[i] = formatted_message + else + lines[i] = padding + end + end + end + vim.api.nvim_buf_set_extmark( + bufnr, + anon_ns, + 0, + 0, + { end_line = height, hl_group = "TelescopePreviewMessageFillchar" } + ) + local col = math.floor((width - strings.strdisplaywidth(formatted_message)) / 2) + for i, line in ipairs(lines) do + local line_pos = math.floor(height / 2) - 2 + i + vim.api.nvim_buf_set_extmark( + bufnr, + anon_ns, + math.max(line_pos, 0), + 0, + { virt_text = { { line, "TelescopePreviewMessage" } }, virt_text_pos = "overlay", virt_text_win_col = col } + ) + end +end + +--- Check if mime type is binary. +--- NOT an exhaustive check, may get false negatives. Ideally should check +--- filetype with `vim.filetype.match` or `filetype_detect` first for filetype +--- info. +---@param mime_type string +---@return boolean +utils.binary_mime_type = function(mime_type) + local type_, subtype = unpack(vim.split(mime_type, "/")) + if vim.tbl_contains({ "text", "inode" }, type_) then + return false + end + if vim.tbl_contains({ "json", "javascript" }, subtype) then + return false + end + return true +end + +return utils diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/sorters.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/sorters.lua new file mode 100644 index 0000000..c1b4b0f --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/sorters.lua @@ -0,0 +1,624 @@ +local log = require "telescope.log" +local util = require "telescope.utils" + +local sorters = {} + +local ngram_highlighter = function(ngram_len, prompt, display) + local highlights = {} + display = display:lower() + + for disp_index = 1, #display do + local char = display:sub(disp_index, disp_index + ngram_len - 1) + if prompt:find(char, 1, true) then + table.insert(highlights, { + start = disp_index, + finish = disp_index + ngram_len - 1, + }) + end + end + + return highlights +end + +local FILTERED = -1 + +local Sorter = {} +Sorter.__index = Sorter + +---@class Sorter +--- Sorter sorts a list of results by return a single integer for a line, +--- given a prompt +--- +--- Lower number is better (because it's like a closer match) +--- But, any number below 0 means you want that line filtered out. +---@field scoring_function function: Function that has the interface: (sorter, prompt, line): number +---@field tags table: Unique tags collected at filtering for tag completion +---@field filter_function function: Function that can filter results +---@field highlighter function: Highlights results to display them pretty +---@field discard boolean: Whether this is a discardable style sorter or not. +---@field score function: Override the score function if desired. +---@field init function: Function to run when creating sorter +---@field start function: Function to run on every new prompt +---@field finish function: Function to run after every new prompt +---@field destroy function: Functo to run when destroying sorter +function Sorter:new(opts) + opts = opts or {} + + return setmetatable({ + score = opts.score, + state = {}, + tags = opts.tags, + + -- State management + init = opts.init, + start = opts.start, + finish = opts.finish, + destroy = opts.destroy, + _status = nil, + + filter_function = opts.filter_function, + scoring_function = opts.scoring_function, + highlighter = opts.highlighter, + discard = opts.discard, + _discard_state = { + filtered = {}, + prompt = "", + }, + }, Sorter) +end + +function Sorter:_init() + self._status = "init" + if self.init then + self:init() + end +end + +function Sorter:_destroy() + self._status = "destroy" + if self.destroy then + self:destroy() + end +end + +-- TODO: We could make this a bit smarter and cache results "as we go" and where they got filtered. +-- Then when we hit backspace, we don't have to re-caculate everything. +-- Prime did a lot of the hard work already, but I don't want to copy as much memory around +-- as he did in his example. +-- Example can be found in ./scratch/prime_prompt_cache.lua +function Sorter:_start(prompt) + self._status = "start" + if self.start then + self:start(prompt) + end + + if not self.discard then + return + end + + local previous = self._discard_state.prompt + local len_previous = #previous + + if #prompt < len_previous then + log.trace "Reset discard because shorter prompt" + self._discard_state.filtered = {} + elseif string.sub(prompt, 1, len_previous) ~= previous then + log.trace "Reset discard no match" + self._discard_state.filtered = {} + end + + self._discard_state.prompt = prompt +end + +function Sorter:_finish(prompt) + self._status = "finish" + if self.finish then + self:finish(prompt) + end +end + +-- TODO: Consider doing something that makes it so we can skip the filter checks +-- if we're not discarding. Also, that means we don't have to check otherwise as well :) +function Sorter:score(prompt, entry, cb_add, cb_filter) + if not entry or not entry.ordinal then + return + end + + if self._status and self._status ~= "start" then + return + end + + local ordinal = entry.ordinal + if self:_was_discarded(prompt, ordinal) then + return cb_filter(entry) + end + + local filter_score + if self.filter_function ~= nil then + if self.tags then + self.tags:insert(entry) + end + filter_score, prompt = self:filter_function(prompt, entry, cb_add, cb_filter) + end + + if filter_score == FILTERED then + return cb_filter(entry) + end + + local score = self:scoring_function(prompt or "", ordinal, entry, cb_add, cb_filter) + if score == FILTERED then + self:_mark_discarded(prompt, ordinal) + return cb_filter(entry) + end + + if cb_add then + return cb_add(score, entry) + else + return score + end +end + +function Sorter:_was_discarded(prompt, ordinal) + return self.discard and self._discard_state.filtered[ordinal] +end + +function Sorter:_mark_discarded(prompt, ordinal) + if not self.discard then + return + end + + self._discard_state.filtered[ordinal] = true +end + +function sorters.new(...) + return Sorter:new(...) +end + +sorters.Sorter = Sorter + +local make_cached_tail = function() + local os_sep = util.get_separator() + local match_string = "[^" .. os_sep .. "]*$" + return setmetatable({}, { + __index = function(t, k) + local tail = string.match(k, match_string) + + rawset(t, k, tail) + return tail + end, + }) +end + +local make_cached_uppers = function() + return setmetatable({}, { + __index = function(t, k) + local obj = {} + for i = 1, #k do + local s_byte = k:byte(i, i) + if s_byte <= 90 and s_byte >= 65 then + obj[s_byte] = true + end + end + + rawset(t, k, obj) + return obj + end, + }) +end + +-- TODO: Match on upper case words +-- TODO: Match on last match +sorters.get_fuzzy_file = function(opts) + opts = opts or {} + + local ngram_len = opts.ngram_len or 2 + + local cached_ngrams = {} + + local function overlapping_ngrams(s, n) + if cached_ngrams[s] and cached_ngrams[s][n] then + return cached_ngrams[s][n] + end + + local R = {} + for i = 1, s:len() - n + 1 do + R[#R + 1] = s:sub(i, i + n - 1) + end + + if not cached_ngrams[s] then + cached_ngrams[s] = {} + end + + cached_ngrams[s][n] = R + + return R + end + + local cached_tails = make_cached_tail() + local cached_uppers = make_cached_uppers() + + return Sorter:new { + scoring_function = function(_, prompt, line) + local N = #prompt + + if N == 0 or N < ngram_len then + -- TODO: If the character is in the line, + -- then it should get a point or somethin. + return 1 + end + + local prompt_lower = prompt:lower() + local line_lower = line:lower() + + local prompt_lower_ngrams = overlapping_ngrams(prompt_lower, ngram_len) + + -- Contains the original string + local contains_string = line_lower:find(prompt_lower, 1, true) + + local prompt_uppers = cached_uppers[prompt] + local line_uppers = cached_uppers[line] + + local uppers_matching = 0 + for k, _ in pairs(prompt_uppers) do + if line_uppers[k] then + uppers_matching = uppers_matching + 1 + end + end + + -- TODO: Consider case senstivity + local tail = cached_tails[line_lower] + local contains_tail = tail:find(prompt, 1, true) + + local consecutive_matches = 0 + local previous_match_index = 0 + local match_count = 0 + + for i = 1, #prompt_lower_ngrams do + local match_start = line_lower:find(prompt_lower_ngrams[i], 1, true) + if match_start then + match_count = match_count + 1 + if match_start > previous_match_index then + consecutive_matches = consecutive_matches + 1 + end + + previous_match_index = match_start + end + end + + local tail_modifier = 1 + if contains_tail then + tail_modifier = 2 + end + + local denominator = ( + (10 * match_count / #prompt_lower_ngrams) + -- biases for shorter strings + + 3 * match_count * ngram_len / #line + + consecutive_matches + + N / (contains_string or (2 * #line)) + -- + 30/(c1 or 2*N) + -- TODO: It might be possible that this too strongly correlates, + -- but it's unlikely for people to type capital letters without actually + -- wanting to do something with a capital letter in it. + + uppers_matching + ) * tail_modifier + + if denominator == 0 or denominator ~= denominator then + return -1 + end + + if #prompt > 2 and denominator < 0.5 then + return -1 + end + + return 1 / denominator + end, + + highlighter = opts.highlighter or function(_, prompt, display) + return ngram_highlighter(ngram_len, prompt, display) + end, + } +end + +sorters.get_generic_fuzzy_sorter = function(opts) + opts = opts or {} + + local ngram_len = opts.ngram_len or 2 + + local cached_ngrams = {} + local function overlapping_ngrams(s, n) + if cached_ngrams[s] and cached_ngrams[s][n] then + return cached_ngrams[s][n] + end + + local R = {} + for i = 1, s:len() - n + 1 do + R[#R + 1] = s:sub(i, i + n - 1) + end + + if not cached_ngrams[s] then + cached_ngrams[s] = {} + end + + cached_ngrams[s][n] = R + + return R + end + + return Sorter:new { + -- self + -- prompt (which is the text on the line) + -- line (entry.ordinal) + -- entry (the whole entry) + scoring_function = function(_, prompt, line, _) + if prompt == 0 or #prompt < ngram_len then + return 1 + end + + local prompt_lower = prompt:lower() + local line_lower = line:lower() + + local prompt_ngrams = overlapping_ngrams(prompt_lower, ngram_len) + + local N = #prompt + + local contains_string = line_lower:find(prompt_lower, 1, true) + + local consecutive_matches = 0 + local previous_match_index = 0 + local match_count = 0 + + for i = 1, #prompt_ngrams do + local match_start = line_lower:find(prompt_ngrams[i], 1, true) + if match_start then + match_count = match_count + 1 + if match_start > previous_match_index then + consecutive_matches = consecutive_matches + 1 + end + + previous_match_index = match_start + end + end + + -- TODO: Copied from ashkan. + local denominator = ( + (10 * match_count / #prompt_ngrams) + -- biases for shorter strings + -- TODO(ashkan): this can bias towards repeated finds of the same + -- subpattern with overlapping_ngrams + + 3 * match_count * ngram_len / #line + + consecutive_matches + + N / (contains_string or (2 * #line)) -- + 30/(c1 or 2*N) + + ) + + if denominator == 0 or denominator ~= denominator then + return -1 + end + + if #prompt > 2 and denominator < 0.5 then + return -1 + end + + return 1 / denominator + end, + + highlighter = opts.highlighter or function(_, prompt, display) + return ngram_highlighter(ngram_len, prompt, display) + end, + } +end + +sorters.fuzzy_with_index_bias = function(opts) + opts = opts or {} + opts.ngram_len = 2 + + -- TODO: Probably could use a better sorter here. + local fuzzy_sorter = sorters.get_generic_fuzzy_sorter(opts) + + return Sorter:new { + scoring_function = function(_, prompt, line, entry, cb_add, cb_filter) + local base_score = fuzzy_sorter:scoring_function(prompt, line, cb_add, cb_filter) + + if base_score == FILTERED then + return FILTERED + end + + if not base_score or base_score == 0 then + return entry.index + else + return math.min(math.pow(entry.index, 0.25), 2) * base_score + end + end, + highlighter = fuzzy_sorter.highlighter, + } +end + +-- Sorter using the fzy algorithm +sorters.get_fzy_sorter = function(opts) + opts = opts or {} + local fzy = opts.fzy_mod or require "telescope.algos.fzy" + local OFFSET = -fzy.get_score_floor() + + return sorters.Sorter:new { + discard = true, + + scoring_function = function(_, prompt, line) + -- Check for actual matches before running the scoring alogrithm. + if not fzy.has_match(prompt, line) then + return -1 + end + + local fzy_score = fzy.score(prompt, line) + + -- The fzy score is -inf for empty queries and overlong strings. Since + -- this function converts all scores into the range (0, 1), we can + -- convert these to 1 as a suitable "worst score" value. + if fzy_score == fzy.get_score_min() then + return 1 + end + + -- Poor non-empty matches can also have negative values. Offset the score + -- so that all values are positive, then invert to match the + -- telescope.Sorter "smaller is better" convention. Note that for exact + -- matches, fzy returns +inf, which when inverted becomes 0. + return 1 / (fzy_score + OFFSET) + end, + + -- The fzy.positions function, which returns an array of string indices, is + -- compatible with telescope's conventions. It's moderately wasteful to + -- call call fzy.score(x,y) followed by fzy.positions(x,y): both call the + -- fzy.compute function, which does all the work. But, this doesn't affect + -- perceived performance. + highlighter = function(_, prompt, display) + return fzy.positions(prompt, display) + end, + } +end + +-- TODO: Could probably do something nice where we check their conf +-- and choose their default for this. +-- But I think `fzy` is good default for now. +sorters.highlighter_only = function(opts) + opts = opts or {} + local fzy = opts.fzy_mod or require "telescope.algos.fzy" + + return Sorter:new { + scoring_function = function() + return 1 + end, + + highlighter = function(_, prompt, display) + return fzy.positions(prompt, display) + end, + } +end + +sorters.empty = function() + return Sorter:new { + scoring_function = function() + return 1 + end, + } +end + +-- Bad & Dumb Sorter +sorters.get_levenshtein_sorter = function() + return Sorter:new { + scoring_function = function(_, prompt, line) + return require "telescope.algos.string_distance"(prompt, line) + end, + } +end + +local substr_highlighter = function(make_display) + return function(_, prompt, display) + local highlights = {} + display = make_display(prompt, display) + + local search_terms = util.max_split(prompt, "%s") + local hl_start, hl_end + + for _, word in pairs(search_terms) do + hl_start, hl_end = display:find(word, 1, true) + if hl_start then + table.insert(highlights, { start = hl_start, finish = hl_end }) + end + end + + return highlights + end +end + +sorters.get_substr_matcher = function() + local make_display = vim.o.smartcase + and function(prompt, display) + local has_upper_case = not not prompt:match "%u" + return has_upper_case and display or display:lower() + end + or function(_, display) + return display:lower() + end + + return Sorter:new { + highlighter = substr_highlighter(make_display), + scoring_function = function(_, prompt, _, entry) + if #prompt == 0 then + return 1 + end + + local display = make_display(prompt, entry.ordinal) + + local search_terms = util.max_split(prompt, "%s") + for _, word in pairs(search_terms) do + if not display:find(word, 1, true) then + return -1 + end + end + + return entry.index + end, + } +end + +local substr_matcher = function(_, prompt, line, _) + local display = line:lower() + local search_terms = util.max_split(prompt:lower(), "%s") + local matched = 0 + local total_search_terms = 0 + for _, word in pairs(search_terms) do + total_search_terms = total_search_terms + 1 + if display:find(word, 1, true) then + matched = matched + 1 + end + end + + return matched == total_search_terms and 0 or FILTERED +end + +local filter_function = function(opts) + local scoring_function = vim.F.if_nil(opts.filter_function, substr_matcher) + local tag = vim.F.if_nil(opts.tag, "ordinal") + + return function(_, prompt, entry) + local filter = "^(" .. opts.delimiter .. "(%S+)" .. "[" .. opts.delimiter .. "%s]" .. ")" + local matched = prompt:match(filter) + + if matched == nil then + return 0, prompt + end + -- clear prompt of tag + prompt = prompt:sub(#matched + 1, -1) + local query = vim.trim(matched:gsub(opts.delimiter, "")) + return scoring_function(_, query, entry[tag], _), prompt + end +end + +local function create_tag_set(tag) + tag = vim.F.if_nil(tag, "ordinal") + local set = {} + return setmetatable(set, { + __index = { + insert = function(set_, entry) + local value = entry[tag] + if not set_[value] then + set_[value] = true + end + end, + }, + }) +end + +sorters.prefilter = function(opts) + local sorter = opts.sorter + opts.delimiter = vim.F.if_nil(opts.delimiter, ":") + sorter._delimiter = opts.delimiter + sorter.tags = create_tag_set(opts.tag) + sorter.filter_function = filter_function(opts) + sorter._was_discarded = function() + return false + end + return sorter +end + +return sorters diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/state.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/state.lua new file mode 100644 index 0000000..a3e6d85 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/state.lua @@ -0,0 +1,39 @@ +local state = {} + +TelescopeGlobalState = TelescopeGlobalState or {} +TelescopeGlobalState.global = TelescopeGlobalState.global or {} + +--- Set the status for a particular prompt bufnr +function state.set_status(prompt_bufnr, status) + TelescopeGlobalState[prompt_bufnr] = status +end + +function state.set_global_key(key, value) + TelescopeGlobalState.global[key] = value +end + +function state.get_global_key(key) + return TelescopeGlobalState.global[key] +end + +function state.get_status(prompt_bufnr) + return TelescopeGlobalState[prompt_bufnr] or {} +end + +function state.clear_status(prompt_bufnr) + state.set_status(prompt_bufnr, nil) +end + +function state.get_existing_prompt_bufnrs() + local prompt_bufnrs = {} + + for key, _ in pairs(TelescopeGlobalState) do + if type(key) == "number" then + table.insert(prompt_bufnrs, key) + end + end + + return prompt_bufnrs +end + +return state diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/testharness/helpers.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/testharness/helpers.lua new file mode 100644 index 0000000..1f0b0b1 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/testharness/helpers.lua @@ -0,0 +1,56 @@ +local test_helpers = {} + +test_helpers.get_picker = function() + local state = require "telescope.state" + return state.get_status(vim.api.nvim_get_current_buf()).picker +end + +test_helpers.get_results_bufnr = function() + local state = require "telescope.state" + return state.get_status(vim.api.nvim_get_current_buf()).layout.results.bufnr +end + +test_helpers.get_file = function() + return vim.fn.fnamemodify(vim.api.nvim_buf_get_name(0), ":t") +end + +test_helpers.get_prompt = function() + return vim.api.nvim_buf_get_lines(0, 0, -1, false)[1] +end + +test_helpers.get_results = function() + return vim.api.nvim_buf_get_lines(test_helpers.get_results_bufnr(), 0, -1, false) +end + +test_helpers.get_best_result = function() + local results = test_helpers.get_results() + local picker = test_helpers.get_picker() + + if picker.sorting_strategy == "ascending" then + return results[1] + else + return results[#results] + end +end + +test_helpers.get_selection = function() + local state = require "telescope.state" + return state.get_global_key "selected_entry" +end + +test_helpers.get_selection_value = function() + return test_helpers.get_selection().value +end + +test_helpers.make_globals = function() + GetFile = test_helpers.get_file -- luacheck: globals GetFile + GetPrompt = test_helpers.get_prompt -- luacheck: globals GetPrompt + + GetResults = test_helpers.get_results -- luacheck: globals GetResults + GetBestResult = test_helpers.get_best_result -- luacheck: globals GetBestResult + + GetSelection = test_helpers.get_selection -- luacheck: globals GetSelection + GetSelectionValue = test_helpers.get_selection_value -- luacheck: globals GetSelectionValue +end + +return test_helpers diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/testharness/init.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/testharness/init.lua new file mode 100644 index 0000000..9a8e707 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/testharness/init.lua @@ -0,0 +1,112 @@ +local assert = require "luassert" + +local Path = require "plenary.path" + +local tester = {} +tester.debug = false + +local get_results_from_contents = function(content) + local nvim = vim.fn.jobstart( + { "nvim", "--noplugin", "-u", "scripts/minimal_init.vim", "--headless", "--embed" }, + { rpc = true } + ) + + local result = vim.fn.rpcrequest(nvim, "nvim_exec_lua", content, {}) + assert.are.same(true, result[1], vim.inspect(result)) + + local count = 0 + while + vim.fn.rpcrequest(nvim, "nvim_exec_lua", "return require('telescope.testharness.runner').state.done", {}) ~= true + do + count = count + 1 + vim.wait(100) + + -- TODO: Could maybe wait longer, but it's annoying to wait if the test is going to timeout. + if count > 100 then + break + end + end + + local state = vim.fn.rpcrequest(nvim, "nvim_exec_lua", "return require('telescope.testharness.runner').state", {}) + vim.fn.jobstop(nvim) + + assert.are.same(true, state.done, vim.inspect(state)) + + local result_table = {} + for _, v in ipairs(state.results) do + table.insert(result_table, v) + end + + return result_table, state +end + +local check_results = function(results, state) + assert(state, "Must pass state") + + for _, v in ipairs(results) do + local assertion + if not v._type or v._type == "are" or v._type == "_default" then + assertion = assert.are.same + else + assertion = assert.are_not.same + end + + -- TODO: I think it would be nice to be able to see the state, + -- but it clutters up the test output so much here. + -- + -- So we would have to consider how to do that I think. + assertion(v.expected, v.actual, string.format("Test Case: %s // %s", v.location, v.case)) + end +end + +tester.run_string = function(contents) + contents = [[ + return (function() + local tester = require('telescope.testharness') + local runner = require('telescope.testharness.runner') + local helper = require('telescope.testharness.helpers') + helper.make_globals() + local ok, msg = pcall(function() + runner.log("Loading Test") + ]] .. contents .. [[ + end) + return {ok, msg or runner.state} + end)() + ]] + + check_results(get_results_from_contents(contents)) +end + +tester.run_file = function(filename) + local file = "./lua/tests/pickers/" .. filename .. ".lua" + local path = Path:new(file) + + if not path:exists() then + assert.are.same("", file) + end + + local contents = string.format( + [[ + return (function() + local runner = require('telescope.testharness.runner') + local helper = require('telescope.testharness.helpers') + helper.make_globals() + local ok, msg = pcall(function() + runner.log("Loading Test") + return loadfile("%s")() + end) + return {ok, msg or runner.state} + end)() + ]], + path:absolute() + ) + + check_results(get_results_from_contents(contents)) +end + +tester.not_ = function(val) + val._type = "are_not" + return val +end + +return tester diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/testharness/runner.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/testharness/runner.lua new file mode 100644 index 0000000..af1bf30 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/testharness/runner.lua @@ -0,0 +1,156 @@ +local builtin = require "telescope.builtin" + +local DELAY = vim.g.telescope_test_delay or 50 +local runner = {} + +-- State is test variable +runner.state = { + done = false, + results = {}, + msgs = {}, +} + +local writer = function(val) + table.insert(runner.state.results, val) +end + +local invalid_test_case = function(k) + error { case = k, expected = "", actual = k } +end + +local _VALID_KEYS = { + post_typed = true, + post_close = true, +} + +local replace_terms = function(input) + return vim.api.nvim_replace_termcodes(input, true, false, true) +end + +runner.nvim_feed = function(text, feed_opts) + feed_opts = feed_opts or "m" + + vim.api.nvim_feedkeys(text, feed_opts, true) +end + +local end_test_cases = function() + runner.state.done = true +end + +local execute_test_case = function(location, key, spec) + local ok, actual = pcall(spec[2]) + + if not ok then + writer { + location = "Error: " .. location, + case = key, + expected = "To succeed and return: " .. tostring(spec[1]), + actual = actual, + + _type = spec._type, + } + + end_test_cases() + else + writer { + location = location, + case = key, + expected = spec[1], + actual = actual, + + _type = spec._type, + } + end + + return ok +end + +runner.log = function(msg) + table.insert(runner.state.msgs, msg) +end + +runner.picker = function(picker_name, input, test_cases, opts) + opts = opts or {} + + for k, _ in pairs(test_cases) do + if not _VALID_KEYS[k] then + return invalid_test_case(k) + end + end + + opts.on_complete = { + runner.create_on_complete(input, test_cases), + } + + opts._on_error = function(self, msg) + runner.state.done = true + writer { + location = "Error while running on complete", + expected = "To Work", + actual = msg, + } + end + + runner.log "Starting picker" + builtin[picker_name](opts) + runner.log "Called picker" +end + +runner.create_on_complete = function(input, test_cases) + input = replace_terms(input) + + local actions = {} + for i = 1, #input do + local char = input:sub(i, i) + table.insert(actions, { + cb = function() + runner.log("Inserting char: " .. char) + runner.nvim_feed(char, "") + end, + char = char, + }) + end + + return function() + local action + + repeat + action = table.remove(actions, 1) + if action then + action.cb() + end + until not action or string.match(action.char, "%g") + + if #actions > 0 then + return + end + + vim.defer_fn(function() + if test_cases.post_typed then + for k, v in ipairs(test_cases.post_typed) do + if not execute_test_case("post_typed", k, v) then + return + end + end + end + + vim.defer_fn(function() + runner.nvim_feed(replace_terms "", "") + + vim.defer_fn(function() + if test_cases.post_close then + for k, v in ipairs(test_cases.post_close) do + if not execute_test_case("post_close", k, v) then + return + end + end + end + + vim.defer_fn(end_test_cases, DELAY) + end, DELAY) + end, DELAY) + end, DELAY) + end +end + +return runner diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/themes.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/themes.lua new file mode 100644 index 0000000..69d12e8 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/themes.lua @@ -0,0 +1,139 @@ +-- Prototype Theme System (WIP) +-- Currently certain designs need a number of parameters. +-- +-- local opts = themes.get_dropdown { winblend = 3 } + +---@tag telescope.themes +---@config { ["module"] = "telescope.themes" } + +---@brief [[ +--- Themes are ways to combine several elements of styling together. +--- +--- They are helpful for managing the several different UI aspects for telescope and provide +--- a simple interface for users to get a particular "style" of picker. +---@brief ]] + +local themes = {} + +--- Dropdown style theme. +--- +--- Usage: +--- +--- local opts = {...} -- picker options +--- local builtin = require('telescope.builtin') +--- local themes = require('telescope.themes') +--- builtin.find_files(themes.get_dropdown(opts)) +--- +function themes.get_dropdown(opts) + opts = opts or {} + + local theme_opts = { + theme = "dropdown", + + results_title = false, + + sorting_strategy = "ascending", + layout_strategy = "center", + layout_config = { + preview_cutoff = 1, -- Preview should always show (unless previewer = false) + + width = function(_, max_columns, _) + return math.min(max_columns, 80) + end, + + height = function(_, _, max_lines) + return math.min(max_lines, 15) + end, + }, + + border = true, + borderchars = { + prompt = { "─", "│", " ", "│", "╭", "╮", "│", "│" }, + results = { "─", "│", "─", "│", "├", "┤", "╯", "╰" }, + preview = { "─", "│", "─", "│", "╭", "╮", "╯", "╰" }, + }, + } + if opts.layout_config and opts.layout_config.prompt_position == "bottom" then + theme_opts.borderchars = { + prompt = { "─", "│", "─", "│", "╭", "╮", "╯", "╰" }, + results = { "─", "│", "─", "│", "╭", "╮", "┤", "├" }, + preview = { "─", "│", "─", "│", "╭", "╮", "╯", "╰" }, + } + end + + return vim.tbl_deep_extend("force", theme_opts, opts) +end + +--- Cursor style theme. +--- +--- Usage: +--- +--- local opts = {...} -- picker options +--- local builtin = require('telescope.builtin') +--- local themes = require('telescope.themes') +--- builtin.find_files(themes.get_cursor(opts)) +--- +function themes.get_cursor(opts) + opts = opts or {} + + local theme_opts = { + theme = "cursor", + + sorting_strategy = "ascending", + results_title = false, + layout_strategy = "cursor", + layout_config = { + width = 80, + height = 9, + }, + borderchars = { + prompt = { "─", "│", " ", "│", "╭", "╮", "│", "│" }, + results = { "─", "│", "─", "│", "├", "┤", "╯", "╰" }, + preview = { "─", "│", "─", "│", "╭", "╮", "╯", "╰" }, + }, + } + + return vim.tbl_deep_extend("force", theme_opts, opts) +end + +--- Ivy style theme. +--- +--- Usage: +--- +--- local opts = {...} -- picker options +--- local builtin = require('telescope.builtin') +--- local themes = require('telescope.themes') +--- builtin.find_files(themes.get_ivy(opts)) +--- +function themes.get_ivy(opts) + opts = opts or {} + + local theme_opts = { + theme = "ivy", + + sorting_strategy = "ascending", + + layout_strategy = "bottom_pane", + layout_config = { + height = 25, + }, + + border = true, + borderchars = { + prompt = { "─", " ", " ", " ", "─", "─", " ", " " }, + results = { " " }, + preview = { "─", "│", "─", "│", "╭", "╮", "╯", "╰" }, + }, + } + if opts.layout_config and opts.layout_config.prompt_position == "bottom" then + theme_opts.borderchars = { + prompt = { " ", " ", "─", " ", " ", " ", "─", "─" }, + results = { "─", " ", " ", " ", "─", "─", " ", " " }, + preview = { "─", " ", "─", "│", "┬", "─", "─", "╰" }, + } + end + + return vim.tbl_deep_extend("force", theme_opts, opts) +end + +return themes diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/utils.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/utils.lua new file mode 100644 index 0000000..5f85ecb --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/telescope/utils.lua @@ -0,0 +1,808 @@ +---@tag telescope.utils +---@config { ["module"] = "telescope.utils" } + +---@brief [[ +--- Utilities for writing telescope pickers +---@brief ]] + +local Path = require "plenary.path" +local Job = require "plenary.job" + +local log = require "telescope.log" + +local truncate = require("plenary.strings").truncate +local get_status = require("telescope.state").get_status + +local utils = {} + +utils.iswin = vim.loop.os_uname().sysname == "Windows_NT" + +---@param s string +---@param i number +---@param encoding "utf-8" | "utf-16" | "utf-32" +---@return integer +utils.str_byteindex = function(s, i, encoding) + if vim.fn.has "nvim-0.11" == 1 then + return vim.str_byteindex(s, encoding, i, false) + else + return vim.lsp.util._str_byteindex_enc(s, i, encoding) + end +end + +--TODO(clason): Remove when dropping support for Nvim 0.9 +utils.islist = vim.fn.has "nvim-0.10" == 1 and vim.islist or vim.tbl_islist +local flatten = function(t) + return vim.iter(t):flatten():totable() +end +utils.flatten = vim.fn.has "nvim-0.11" == 1 and flatten or vim.tbl_flatten + +--- Hybrid of `vim.fn.expand()` and custom `vim.fs.normalize()` +--- +--- Paths starting with '%', '#' or '<' are expanded with `vim.fn.expand()`. +--- Otherwise avoids using `vim.fn.expand()` due to its overly aggressive +--- expansion behavior which can sometimes lead to errors or the creation of +--- non-existent paths when dealing with valid absolute paths. +--- +--- Other paths will have '~' and environment variables expanded. +--- Unlike `vim.fs.normalize()`, backslashes are preserved. This has better +--- compatibility with `plenary.path` and also avoids mangling valid Unix paths +--- with literal backslashes. +--- +--- Trailing slashes are trimmed. With the exception of root paths. +--- eg. `/` on Unix or `C:\` on Windows +--- +---@param path string +---@return string +utils.path_expand = function(path) + vim.validate { + path = { path, { "string" } }, + } + + if utils.is_uri(path) then + return path + end + + if path:match "^[%%#<]" then + path = vim.fn.expand(path) + end + + if path:sub(1, 1) == "~" then + local home = vim.loop.os_homedir() or "~" + if home:sub(-1) == "\\" or home:sub(-1) == "/" then + home = home:sub(1, -2) + end + path = home .. path:sub(2) + end + + path = path:gsub("%$([%w_]+)", vim.loop.os_getenv) + path = path:gsub("/+", "/") + if utils.iswin then + path = path:gsub("\\+", "\\") + if path:match "^%w:\\$" then + return path + else + return (path:gsub("(.)\\$", "%1")) + end + end + return (path:gsub("(.)/$", "%1")) +end + +utils.get_separator = function() + return Path.path.sep +end + +utils.cycle = function(i, n) + return i % n == 0 and n or i % n +end + +utils.get_lazy_default = function(x, defaulter, ...) + if x == nil then + return defaulter(...) + else + return x + end +end + +utils.repeated_table = function(n, val) + local empty_lines = {} + for _ = 1, n do + table.insert(empty_lines, val) + end + return empty_lines +end + +utils.filter_symbols = function(results, opts, post_filter) + local has_ignore = opts.ignore_symbols ~= nil + local has_symbols = opts.symbols ~= nil + local filtered_symbols + + if has_symbols and has_ignore then + utils.notify("filter_symbols", { + msg = "Either opts.symbols or opts.ignore_symbols, can't process opposing options at the same time!", + level = "ERROR", + }) + return {} + elseif not (has_ignore or has_symbols) then + return results + elseif has_ignore then + if type(opts.ignore_symbols) == "string" then + opts.ignore_symbols = { opts.ignore_symbols } + end + if type(opts.ignore_symbols) ~= "table" then + utils.notify("filter_symbols", { + msg = "Please pass ignore_symbols as either a string or a list of strings", + level = "ERROR", + }) + return {} + end + + opts.ignore_symbols = vim.tbl_map(string.lower, opts.ignore_symbols) + filtered_symbols = vim.tbl_filter(function(item) + return not vim.tbl_contains(opts.ignore_symbols, string.lower(item.kind)) + end, results) + elseif has_symbols then + if type(opts.symbols) == "string" then + opts.symbols = { opts.symbols } + end + if type(opts.symbols) ~= "table" then + utils.notify("filter_symbols", { + msg = "Please pass filtering symbols as either a string or a list of strings", + level = "ERROR", + }) + return {} + end + + opts.symbols = vim.tbl_map(string.lower, opts.symbols) + filtered_symbols = vim.tbl_filter(function(item) + return vim.tbl_contains(opts.symbols, string.lower(item.kind)) + end, results) + end + + if type(post_filter) == "function" then + filtered_symbols = post_filter(filtered_symbols) + end + + if not vim.tbl_isempty(filtered_symbols) then + return filtered_symbols + end + + -- print message that filtered_symbols is now empty + if has_symbols then + local symbols = table.concat(opts.symbols, ", ") + utils.notify("filter_symbols", { + msg = string.format("%s symbol(s) were not part of the query results", symbols), + level = "WARN", + }) + elseif has_ignore then + local symbols = table.concat(opts.ignore_symbols, ", ") + utils.notify("filter_symbols", { + msg = string.format("%s ignore_symbol(s) have removed everything from the query result", symbols), + level = "WARN", + }) + end + return {} +end + +local path_filename_first = function(path, reverse_directories) + local dirs = vim.split(path, utils.get_separator()) + local filename + + if reverse_directories then + dirs = utils.reverse_table(dirs) + filename = table.remove(dirs, 1) + else + filename = table.remove(dirs, #dirs) + end + + local tail = table.concat(dirs, utils.get_separator()) + -- Trim prevents a top-level filename to have a trailing white space + local transformed_path = vim.trim(filename .. " " .. tail) + local path_style = { { { #filename, #transformed_path }, "TelescopeResultsComment" } } + + return transformed_path, path_style +end + +local calc_result_length = function(truncate_len) + local status = get_status(vim.api.nvim_get_current_buf()) + local len = vim.api.nvim_win_get_width(status.layout.results.winid) - status.picker.selection_caret:len() - 2 + return type(truncate_len) == "number" and len - truncate_len or len +end + +local path_truncate = function(path, truncate_len, opts) + if opts.__length == nil then + opts.__length = calc_result_length(truncate_len) + end + if opts.__prefix == nil then + opts.__prefix = 0 + end + return truncate(path, opts.__length - opts.__prefix, nil, -1) +end + +local path_shorten = function(path, length, exclude) + if exclude ~= nil then + return Path:new(path):shorten(length, exclude) + else + return Path:new(path):shorten(length) + end +end + +local path_abs = function(path, opts) + local cwd + if opts.cwd then + cwd = opts.cwd + if not vim.in_fast_event() then + cwd = utils.path_expand(opts.cwd) + end + else + cwd = vim.loop.cwd() + end + return Path:new(path):make_relative(cwd) +end + +-- IMPORTANT: This function should have been a local function as it's only used +-- in this file, but the code was already exported a long time ago. By making it +-- local we would potential break consumers of this method. +utils.path_smart = (function() + local paths = {} + local os_sep = utils.get_separator() + return function(filepath) + local final = filepath + if #paths ~= 0 then + local dirs = vim.split(filepath, os_sep) + local max = 1 + for _, p in pairs(paths) do + if #p > 0 and p ~= filepath then + local _dirs = vim.split(p, os_sep) + for i = 1, math.min(#dirs, #_dirs) do + if (dirs[i] ~= _dirs[i]) and i > max then + max = i + break + end + end + end + end + if #dirs ~= 0 then + if max == 1 and #dirs >= 2 then + max = #dirs - 2 + end + final = "" + for k, v in pairs(dirs) do + if k >= max - 1 then + final = final .. (#final > 0 and os_sep or "") .. v + end + end + end + end + if not paths[filepath] then + paths[filepath] = "" + table.insert(paths, filepath) + end + if final and final ~= filepath then + return ".." .. os_sep .. final + else + return filepath + end + end +end)() + +utils.path_tail = (function() + local os_sep = utils.get_separator() + + if os_sep == "/" then + return function(path) + for i = #path, 1, -1 do + if path:sub(i, i) == os_sep then + return path:sub(i + 1, -1) + end + end + return path + end + else + return function(path) + for i = #path, 1, -1 do + local c = path:sub(i, i) + if c == os_sep or c == "/" then + return path:sub(i + 1, -1) + end + end + return path + end + end +end)() + +utils.is_path_hidden = function(opts, path_display) + path_display = path_display or vim.F.if_nil(opts.path_display, require("telescope.config").values.path_display) + + return path_display == nil + or path_display == "hidden" + or type(path_display) == "table" and (vim.tbl_contains(path_display, "hidden") or path_display.hidden) +end + +utils.is_uri = function(filename) + local char = string.byte(filename, 1) or 0 + + -- is alpha? + if char < 65 or (char > 90 and char < 97) or char > 122 then + return false + end + + for i = 2, #filename do + char = string.byte(filename, i) + if char == 58 then -- `:` + return i < #filename and string.byte(filename, i + 1) ~= 92 -- `\` + elseif + not ( + (char >= 48 and char <= 57) -- 0-9 + or (char >= 65 and char <= 90) -- A-Z + or (char >= 97 and char <= 122) -- a-z + or char == 43 -- `+` + or char == 46 -- `.` + or char == 45 -- `-` + ) + then + return false + end + end + return false +end + +--- Transform path is a util function that formats a path based on path_display +--- found in `opts` or the default value from config. +--- It is meant to be used in make_entry to have a uniform interface for +--- builtins as well as extensions utilizing the same user configuration +--- Note: It is only supported inside `make_entry`/`make_display` the use of +--- this function outside of telescope might yield to undefined behavior and will +--- not be addressed by us +---@param opts table: The opts the users passed into the picker. Might contains a path_display key +---@param path string|nil: The path that should be formatted +---@return string: path to be displayed +---@return table: The transformed path ready to be displayed with the styling +utils.transform_path = function(opts, path) + if path == nil then + return "", {} + end + if utils.is_uri(path) then + return path, {} + end + + ---@type fun(opts:table, path: string): string, table? + local path_display = vim.F.if_nil(opts.path_display, require("telescope.config").values.path_display) + + local transformed_path = path + local path_style = {} + + if type(path_display) == "function" then + local custom_transformed_path, custom_path_style = path_display(opts, transformed_path) + return custom_transformed_path, custom_path_style or path_style + elseif utils.is_path_hidden(nil, path_display) then + return "", path_style + elseif type(path_display) == "table" then + if vim.tbl_contains(path_display, "tail") or path_display.tail then + return utils.path_tail(transformed_path), path_style + end + + if not vim.tbl_contains(path_display, "absolute") and not path_display.absolute then + transformed_path = path_abs(transformed_path, opts) + end + + if vim.tbl_contains(path_display, "smart") or path_display.smart then + transformed_path = utils.path_smart(transformed_path) + end + + if vim.tbl_contains(path_display, "shorten") or path_display["shorten"] ~= nil then + local length + local exclude = nil + + if type(path_display["shorten"]) == "table" then + local shorten = path_display["shorten"] + length = shorten.len + exclude = shorten.exclude + else + length = type(path_display["shorten"]) == "number" and path_display["shorten"] + end + + transformed_path = path_shorten(transformed_path, length, exclude) + end + + if vim.tbl_contains(path_display, "truncate") or path_display.truncate then + transformed_path = path_truncate(transformed_path, path_display.truncate, opts) + end + + if vim.tbl_contains(path_display, "filename_first") or path_display["filename_first"] ~= nil then + local reverse_directories = false + + if type(path_display["filename_first"]) == "table" then + local filename_first_opts = path_display["filename_first"] + + if filename_first_opts.reverse_directories == nil or filename_first_opts.reverse_directories == false then + reverse_directories = false + else + reverse_directories = filename_first_opts.reverse_directories + end + end + + transformed_path, path_style = path_filename_first(transformed_path, reverse_directories) + end + + return transformed_path, path_style + else + log.warn("`path_display` must be either a function or a table.", "See `:help telescope.defaults.path_display.") + return transformed_path, path_style + end +end + +-- local x = utils.make_default_callable(function(opts) +-- return function() +-- print(opts.example, opts.another) +-- end +-- end, { example = 7, another = 5 }) + +-- x() +-- x.new { example = 3 }() +function utils.make_default_callable(f, default_opts) + default_opts = default_opts or {} + + return setmetatable({ + new = function(opts) + opts = vim.tbl_extend("keep", opts, default_opts) + return f(opts) + end, + }, { + __call = function() + local ok, err = pcall(f(default_opts)) + if not ok then + error(debug.traceback(err)) + end + end, + }) +end + +function utils.job_is_running(job_id) + if job_id == nil then + return false + end + return vim.fn.jobwait({ job_id }, 0)[1] == -1 +end + +function utils.buf_delete(bufnr) + if bufnr == nil then + return + end + + -- Suppress the buffer deleted message for those with &report<2 + local start_report = vim.o.report + if start_report < 2 then + vim.o.report = 2 + end + + if vim.api.nvim_buf_is_valid(bufnr) and vim.api.nvim_buf_is_loaded(bufnr) then + vim.api.nvim_buf_delete(bufnr, { force = true }) + end + + if start_report < 2 then + vim.o.report = start_report + end +end + +function utils.win_delete(name, win_id, force, bdelete) + if win_id == nil or not vim.api.nvim_win_is_valid(win_id) then + return + end + + local bufnr = vim.api.nvim_win_get_buf(win_id) + if bdelete then + utils.buf_delete(bufnr) + end + + if not vim.api.nvim_win_is_valid(win_id) then + return + end + + if not pcall(vim.api.nvim_win_close, win_id, force) then + log.trace("Unable to close window: ", name, "/", win_id) + end +end + +function utils.max_split(s, pattern, maxsplit) + pattern = pattern or " " + maxsplit = maxsplit or -1 + + local t = {} + + local curpos = 0 + while maxsplit ~= 0 and curpos < #s do + local found, final = string.find(s, pattern, curpos, false) + if found ~= nil then + local val = string.sub(s, curpos, found - 1) + + if #val > 0 then + maxsplit = maxsplit - 1 + table.insert(t, val) + end + + curpos = final + 1 + else + table.insert(t, string.sub(s, curpos)) + break + -- curpos = curpos + 1 + end + + if maxsplit == 0 then + table.insert(t, string.sub(s, curpos)) + end + end + + return t +end + +-- IMPORTANT: This function should have been a local function as it's only used +-- in this file, but the code was already exported a long time ago. By making it +-- local we would potential break consumers of this method. +function utils.data_directory() + local sourced_file = require("plenary.debug_utils").sourced_filepath() + local base_directory = vim.fn.fnamemodify(sourced_file, ":h:h:h") + + return Path:new({ base_directory, "data" }):absolute() .. Path.path.sep +end + +function utils.buffer_dir() + return vim.fn.expand "%:p:h" +end + +function utils.display_termcodes(str) + return str:gsub(string.char(9), ""):gsub("", ""):gsub(" ", "") +end + +function utils.get_os_command_output(cmd, cwd) + if type(cmd) ~= "table" then + utils.notify("get_os_command_output", { + msg = "cmd has to be a table", + level = "ERROR", + }) + return {} + end + local command = table.remove(cmd, 1) + local stderr = {} + local stdout, ret = Job:new({ + command = command, + args = cmd, + cwd = cwd, + on_stderr = function(_, data) + table.insert(stderr, data) + end, + }):sync() + return stdout, ret, stderr +end + +function utils.win_set_buf_noautocmd(win, buf) + local save_ei = vim.o.eventignore + vim.o.eventignore = "all" + vim.api.nvim_win_set_buf(win, buf) + vim.o.eventignore = save_ei +end + +local load_once = function(f) + local resolved = nil + return function(...) + if resolved == nil then + resolved = f() + end + + return resolved(...) + end +end + +-- IMPORTANT: This function should have been a local function as it's only used +-- in this file, but the code was already exported a long time ago. By making it +-- local we would potential break consumers of this method. +utils.file_extension = function(filename) + local parts = vim.split(filename, "%.") + -- this check enables us to get multi-part extensions, like *.test.js for example + if #parts > 2 then + return table.concat(vim.list_slice(parts, #parts - 1), ".") + else + return table.concat(vim.list_slice(parts, #parts), ".") + end +end + +utils.transform_devicons = load_once(function() + local has_devicons, devicons = pcall(require, "nvim-web-devicons") + + if has_devicons then + if not devicons.has_loaded() then + devicons.setup() + end + + return function(filename, display, disable_devicons) + local conf = require("telescope.config").values + if disable_devicons or not filename then + return display + end + + local basename = utils.path_tail(filename) + local icon, icon_highlight = devicons.get_icon(basename, utils.file_extension(basename), { default = false }) + if not icon then + icon, icon_highlight = devicons.get_icon(basename, nil, { default = true }) + icon = icon or " " + end + local icon_display = icon .. " " .. (display or "") + + if conf.color_devicons then + return icon_display, icon_highlight, icon + else + return icon_display, nil, icon + end + end + else + return function(_, display, _) + return display + end + end +end) + +utils.get_devicons = load_once(function() + local has_devicons, devicons = pcall(require, "nvim-web-devicons") + + if has_devicons then + if not devicons.has_loaded() then + devicons.setup() + end + + return function(filename, disable_devicons) + local conf = require("telescope.config").values + if disable_devicons or not filename then + return "" + end + + local basename = utils.path_tail(filename) + local icon, icon_highlight = devicons.get_icon(basename, utils.file_extension(basename), { default = false }) + if not icon then + icon, icon_highlight = devicons.get_icon(basename, nil, { default = true }) + end + if conf.color_devicons then + return icon, icon_highlight + else + return icon, nil + end + end + else + return function(_, _) + return "" + end + end +end) + +--- Checks if treesitter parser for language is installed +---@param lang string +utils.has_ts_parser = function(lang) + if vim.fn.has "nvim-0.11" == 1 then + return vim.treesitter.language.add(lang) + else + return pcall(vim.treesitter.language.add, lang) + end +end + +--- Telescope Wrapper around vim.notify +---@param funname string: name of the function that will be +---@param opts table: opts.level string, opts.msg string, opts.once bool +utils.notify = function(funname, opts) + opts.once = vim.F.if_nil(opts.once, false) + local level = vim.log.levels[opts.level] + if not level then + error("Invalid error level", 2) + end + local notify_fn = opts.once and vim.notify_once or vim.notify + notify_fn(string.format("[telescope.%s]: %s", funname, opts.msg), level, { + title = "telescope.nvim", + }) +end + +utils.__warn_no_selection = function(name) + utils.notify(name, { + msg = "Nothing currently selected", + level = "WARN", + }) +end + +--- Generate git command optionally with git env variables +---@param args string[] +---@param opts? table +---@return string[] +utils.__git_command = function(args, opts) + opts = opts or {} + + local _args = { "git" } + if opts.gitdir then + vim.list_extend(_args, { "--git-dir", opts.gitdir }) + end + if opts.toplevel then + vim.list_extend(_args, { "--work-tree", opts.toplevel }) + end + + return vim.list_extend(_args, args) +end + +utils.list_find = function(func, list) + for i, v in ipairs(list) do + if func(v, i, list) then + return i, v + end + end +end + +--- Takes the path and parses optional cursor location `$file:$line:$column` +--- If line or column not present `0` returned. +---@param path string +---@return string path +---@return integer? lnum +---@return integer? col +utils.__separate_file_path_location = function(path) + local location_numbers = {} + for i = #path, 1, -1 do + if path:sub(i, i) == ":" then + if i == #path then + path = path:sub(1, i - 1) + else + local location_value = tonumber(path:sub(i + 1)) + if location_value then + table.insert(location_numbers, location_value) + path = path:sub(1, i - 1) + + if #location_numbers == 2 then + -- There couldn't be more than 2 : separated number + break + end + end + end + end + end + + if #location_numbers == 2 then + -- because of the reverse the line number will be second + return path, location_numbers[2], location_numbers[1] + end + + if #location_numbers == 1 then + return path, location_numbers[1], 0 + end + + return path, nil, nil +end + +local function add_offset(offset, obj) + return { obj[1] + offset, obj[2] + offset } +end + +utils.merge_styles = function(style1, style2, offset) + for _, item in ipairs(style2) do + item[1] = add_offset(offset, item[1]) + table.insert(style1, item) + end + + return style1 +end + +-- IMPORTANT: This function should have been a local function as it's only used +-- in this file, but the code was already exported a long time ago. By making it +-- local we would potential break consumers of this method. +utils.reverse_table = function(input_table) + local temp_table = {} + for index = 0, #input_table do + temp_table[#input_table - index] = input_table[index + 1] -- Reverses the order + end + return temp_table +end + +utils.split_lines = (function() + if utils.iswin then + return function(s, opts) + return vim.split(s, "\r?\n", opts) + end + else + return function(s, opts) + return vim.split(s, "\n", opts) + end + end +end)() + +return utils diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/action_spec.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/action_spec.lua new file mode 100644 index 0000000..3db014f --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/action_spec.lua @@ -0,0 +1,510 @@ +local actions = require "telescope.actions" +local action_set = require "telescope.actions.set" + +local transform_mod = require("telescope.actions.mt").transform_mod + +local eq = assert.are.same + +describe("actions", function() + it("should allow creating custom actions", function() + local a = transform_mod { + x = function() + return 5 + end, + } + + eq(5, a.x()) + end) + + it("allows adding actions", function() + local a = transform_mod { + x = function() + return "x" + end, + y = function() + return "y" + end, + } + + local x_plus_y = a.x + a.y + + eq({ "x", "y" }, { x_plus_y() }) + end) + + it("ignores nils from added actions", function() + local a = transform_mod { + x = function() + return "x" + end, + y = function() + return "y" + end, + nil_maker = function() + return nil + end, + } + + local x_plus_y = a.x + a.nil_maker + a.y + + eq({ "x", "y" }, { x_plus_y() }) + end) + + it("allows overriding an action", function() + local a = transform_mod { + x = function() + return "x" + end, + y = function() + return "y" + end, + } + + -- actions.file_goto_selection_edit:replace(...) + a.x:replace(function() + return "foo" + end) + eq("foo", a.x()) + + a._clear() + eq("x", a.x()) + end) + + it("allows overriding an action only in specific cases with if", function() + local a = transform_mod { + x = function(e) + return e * 10 + end, + y = function() + return "y" + end, + } + + -- actions.file_goto_selection_edit:replace(...) + a.x:replace_if(function(e) + return e > 0 + end, function(e) + return (e / 10) + end) + eq(-100, a.x(-10)) + eq(10, a.x(100)) + eq(1, a.x(10)) + + a._clear() + eq(100, a.x(10)) + end) + + it("allows overriding an action only in specific cases with mod", function() + local a = transform_mod { + x = function(e) + return e * 10 + end, + y = function() + return "y" + end, + } + + -- actions.file_goto_selection_edit:replace(...) + a.x:replace_map { + [function(e) + return e > 0 + end] = function(e) + return (e / 10) + end, + [function(e) + return e == 0 + end] = function(e) + return (e + 10) + end, + } + + eq(-100, a.x(-10)) + eq(10, a.x(100)) + eq(1, a.x(10)) + eq(10, a.x(0)) + + a._clear() + eq(100, a.x(10)) + end) + + it("continuous replacement", function() + local a = transform_mod { + x = function() + return "cleared" + end, + y = function() + return "y" + end, + } + + -- Replace original, which becomes new fallback + a.x:replace(function() + return "negative" + end) + + -- actions.file_goto_selection_edit:replace(...) + a.x:replace_map { + [function(e) + return e > 0 + end] = function(e) + return "positive" + end, + [function(e) + return e == 0 + end] = function(e) + return "zero" + end, + } + + eq("positive", a.x(10)) + eq("zero", a.x(0)) + eq("negative", a.x(-10)) + + a._clear() + eq("cleared", a.x(10)) + end) + + it("enhance.pre", function() + local a = transform_mod { + x = function() + return "x" + end, + y = function() + return "y" + end, + } + + local called_pre = false + + a.y:enhance { + pre = function() + called_pre = true + end, + } + eq("y", a.y()) + eq(true, called_pre) + end) + + it("enhance.post", function() + local a = transform_mod { + x = function() + return "x" + end, + y = function() + return "y" + end, + } + + local called_post = false + + a.y:enhance { + post = function() + called_post = true + end, + } + eq("y", a.y()) + eq(true, called_post) + end) + + it("static_pre static_post", function() + local called_pre = false + local called_post = false + local static_post = 0 + local a = transform_mod { + x = { + pre = function() + called_pre = true + end, + action = function() + return "x" + end, + post = function() + called_post = true + end, + }, + } + + eq("x", a.x()) + eq(true, called_pre) + eq(true, called_post) + end) + + it("can call both", function() + local a = transform_mod { + x = function() + return "x" + end, + y = function() + return "y" + end, + } + + local called_count = 0 + local count_inc = function() + called_count = called_count + 1 + end + + a.y:enhance { + pre = count_inc, + post = count_inc, + } + + eq("y", a.y()) + eq(2, called_count) + end) + + it("can call both even when combined", function() + local a = transform_mod { + x = function() + return "x" + end, + y = function() + return "y" + end, + } + + local called_count = 0 + local count_inc = function() + called_count = called_count + 1 + end + + a.y:enhance { + pre = count_inc, + post = count_inc, + } + + a.x:enhance { + post = count_inc, + } + + local x_plus_y = a.x + a.y + x_plus_y() + + eq(3, called_count) + end) + + it( + "can call replace fn even when combined before replace registered the fn (because that happens with mappings)", + function() + local a = transform_mod { + x = function() + return "x" + end, + y = function() + return "y" + end, + } + + local called_count = 0 + local count_inc = function() + called_count = called_count + 1 + end + + local x_plus_y = a.x + a.y + a.x:replace(function() + count_inc() + end) + a.y:replace(function() + count_inc() + end) + + x_plus_y() + + eq(2, called_count) + end + ) + + it( + "can call enhance fn even when combined before enhance registed fns (because that happens with mappings)", + function() + local a = transform_mod { + x = function() + return "x" + end, + y = function() + return "y" + end, + } + + local called_count = 0 + local count_inc = function() + called_count = called_count + 1 + end + + local x_plus_y = a.x + a.y + a.y:enhance { + pre = count_inc, + post = count_inc, + } + + a.x:enhance { + post = count_inc, + } + + x_plus_y() + + eq(3, called_count) + end + ) + + it("clears enhance", function() + local a = transform_mod { + x = function() + return "x" + end, + y = function() + return "y" + end, + } + + local called_post = false + + a.y:enhance { + post = function() + called_post = true + end, + } + + a._clear() + + eq("y", a.y()) + eq(false, called_post) + end) + + it("handles passing arguments", function() + local a = transform_mod { + x = function(bufnr) + return string.format "bufnr: %s" + end, + } + + a.x:replace(function(bufnr) + return string.format("modified: %s", bufnr) + end) + eq("modified: 5", a.x(5)) + end) + + it("handles add with two different tables", function() + local count_a = 0 + local count_b = 0 + local a = transform_mod { + x = function() + count_a = count_a + 1 + end, + } + local b = transform_mod { + y = function() + count_b = count_b + 1 + end, + } + + local called_count = 0 + local count_inc = function() + called_count = called_count + 1 + end + + a.x:enhance { + post = count_inc, + } + b.y:enhance { + post = count_inc, + } + + local x_plus_y = a.x + b.y + x_plus_y() + + eq(2, called_count) + eq(1, count_a) + eq(1, count_b) + end) + + it("handles tripple concat with static pre post", function() + local count_a = 0 + local count_b = 0 + local count_c = 0 + local static_pre = 0 + local static_post = 0 + local a = transform_mod { + x = { + pre = function() + static_pre = static_pre + 1 + end, + action = function() + count_a = count_a + 1 + end, + post = function() + static_post = static_post + 1 + end, + }, + } + local b = transform_mod { + y = { + pre = function() + static_pre = static_pre + 1 + end, + action = function() + count_b = count_b + 1 + end, + post = function() + static_post = static_post + 1 + end, + }, + } + local c = transform_mod { + z = { + pre = function() + static_pre = static_pre + 1 + end, + action = function() + count_c = count_c + 1 + end, + post = function() + static_post = static_post + 1 + end, + }, + } + + local replace_count = 0 + a.x:replace(function() + replace_count = replace_count + 1 + end) + + local x_plus_y_plus_z = a.x + b.y + c.z + x_plus_y_plus_z() + + eq(0, count_a) + eq(1, count_b) + eq(1, count_c) + eq(1, replace_count) + eq(3, static_pre) + eq(3, static_post) + end) + + describe("action_set", function() + it("can replace `action_set.edit`", function() + action_set.edit:replace(function(_, arg) + return "replaced:" .. arg + end) + eq("replaced:edit", actions.file_edit()) + eq("replaced:vnew", actions.file_vsplit()) + end) + + pending("handles backwards compat with select and edit files", function() + -- Reproduce steps: + -- In config, we have { [""] = actions.select, ... } + -- In caller, we have actions._goto:replace(...) + -- Person calls `select`, does not see update + action_set.edit:replace(function(_, arg) + return "default_to_edit:" .. arg + end) + eq("default_to_edit:edit", actions.select_default()) + + action_set.select:replace(function(_, arg) + return "override_with_select:" .. arg + end) + eq("override_with_select:default", actions.select_default()) + + -- Sometimes you might want to change the default selection... + -- but you don't want to prohibit the ability to edit the code... + end) + end) +end) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/command_spec.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/command_spec.lua new file mode 100644 index 0000000..9212815 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/command_spec.lua @@ -0,0 +1,102 @@ +local command = require "telescope.command" + +local eq = assert.are.same + +describe("command_parser", function() + local test_parse = function(should, input, output) + it(should, function() + command.convert_user_opts(input) + eq(output, input) + end) + end + + -- Strings + test_parse("should handle cwd", { cwd = "string" }, { cwd = "string" }) + + -- Find commands + test_parse( + "should handle find_command 1", + { find_command = "rg,--ignore,--hidden,files" }, + { find_command = { "rg", "--ignore", "--hidden", "files" } } + ) + test_parse( + "should handle find_command 2", + { find_command = "fd,-t,f,-H" }, + { find_command = { "fd", "-t", "f", "-H" } } + ) + test_parse( + "should handle find_command 3", + { find_command = "fdfind,--type,f,--no-ignore" }, + { find_command = { "fdfind", "--type", "f", "--no-ignore" } } + ) + + -- Dictionaries/tables + test_parse( + "should handle layout_config viml 1", + { layout_config = "{'prompt_position':'top'}" }, + { layout_config = { prompt_position = "top" } } + ) + test_parse( + "should handle layout_config viml 2", + { layout_config = "#{prompt_position:'bottom'}" }, + { layout_config = { prompt_position = "bottom" } } + ) + test_parse( + "should handle layout_config viml 3", + { layout_config = "{'mirror':v:true}" }, + { layout_config = { mirror = true } } + ) + test_parse( + "should handle layout_config viml 4", + { layout_config = "#{mirror:v:true}" }, + { layout_config = { mirror = true } } + ) + test_parse( + "should handle layout_config lua 1", + { layout_config = "{prompt_position='bottom'}" }, + { layout_config = { prompt_position = "bottom" } } + ) + test_parse( + "should handle layout_config lua 2", + { layout_config = "{mirror=true}" }, + { layout_config = { mirror = true } } + ) + + -- Lists/tables + test_parse( + "should handle symbols commas list", + { symbols = "alpha,beta,gamma" }, + { symbols = { "alpha", "beta", "gamma" } } + ) + test_parse( + "should handle symbols viml list", + { symbols = "['alpha','beta','gamma']" }, + { symbols = { "alpha", "beta", "gamma" } } + ) + test_parse( + "should handle symbols lua list", + { symbols = "{'alpha','beta','gamma'}" }, + { symbols = { "alpha", "beta", "gamma" } } + ) + + -- Booleans + test_parse("should handle booleans 1", { hidden = "true" }, { hidden = true }) + test_parse("should handle booleans 2", { no_ignore = "false" }, { no_ignore = false }) + + -- Numbers + test_parse("should handle numbers 1", { depth = "2" }, { depth = 2 }) + test_parse("should handle numbers 2", { bufnr_width = "4" }, { bufnr_width = 4 }) + test_parse("should handle numbers 3", { severity = "27" }, { severity = 27 }) + + -- Multiple options + test_parse( + "should handle multiple options 1", + { layout_config = '{prompt_position="top"}', cwd = "/foobar", severity = "27" }, + { layout_config = { prompt_position = "top" }, cwd = "/foobar", severity = 27 } + ) + test_parse( + "should handle multiple options 2", + { symbols = "['alef','bet','gimel']", depth = "2", find_command = "rg,--ignore,files" }, + { symbols = { "alef", "bet", "gimel" }, depth = 2, find_command = { "rg", "--ignore", "files" } } + ) +end) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/entry_display_spec.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/entry_display_spec.lua new file mode 100644 index 0000000..a09ccae --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/entry_display_spec.lua @@ -0,0 +1,34 @@ +local entry_display = require "telescope.pickers.entry_display" + +describe("truncate", function() + for _, ambiwidth in ipairs { "single", "double" } do + for _, case in ipairs { + { args = { "abcde", 6 }, expected = { single = "abcde", double = "abcde" } }, + { args = { "abcde", 5 }, expected = { single = "abcde", double = "abcde" } }, + { args = { "abcde", 4 }, expected = { single = "abc…", double = "ab…" } }, + { args = { "アイウエオ", 11 }, expected = { single = "アイウエオ", double = "アイウエオ" } }, + { args = { "アイウエオ", 10 }, expected = { single = "アイウエオ", double = "アイウエオ" } }, + { args = { "アイウエオ", 9 }, expected = { single = "アイウエ…", double = "アイウ…" } }, + { args = { "アイウエオ", 8 }, expected = { single = "アイウ…", double = "アイウ…" } }, + { args = { "├─┤", 7 }, expected = { single = "├─┤", double = "├─┤" } }, + { args = { "├─┤", 6 }, expected = { single = "├─┤", double = "├─┤" } }, + { args = { "├─┤", 5 }, expected = { single = "├─┤", double = "├…" } }, + { args = { "├─┤", 4 }, expected = { single = "├─┤", double = "├…" } }, + { args = { "├─┤", 3 }, expected = { single = "├─┤", double = "…" } }, + { args = { "├─┤", 2 }, expected = { single = "├…", double = "…" } }, + } do + local msg = ("can truncate: ambiwidth = %s, [%s, %d] -> %s"):format( + ambiwidth, + case.args[1], + case.args[2], + case.expected[ambiwidth] + ) + it(msg, function() + local original = vim.o.ambiwidth + vim.o.ambiwidth = ambiwidth + assert.are.same(case.expected[ambiwidth], entry_display.truncate(case.args[1], case.args[2])) + vim.o.ambiwidth = original + end) + end + end +end) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/entry_manager_spec.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/entry_manager_spec.lua new file mode 100644 index 0000000..6d2b5d3 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/entry_manager_spec.lua @@ -0,0 +1,189 @@ +local EntryManager = require "telescope.entry_manager" + +local eq = assert.are.same + +describe("process_result", function() + it("works with one entry", function() + local manager = EntryManager:new(5, nil) + + manager:add_entry(nil, 1, "hello", "") + + eq(1, manager:get_score(1)) + end) + + it("works with two entries", function() + local manager = EntryManager:new(5, nil) + + manager:add_entry(nil, 1, "hello", "") + manager:add_entry(nil, 2, "later", "") + + eq(2, manager.linked_states.size) + + eq("hello", manager:get_entry(1)) + eq("later", manager:get_entry(2)) + end) + + it("calls functions when inserting", function() + local called_count = 0 + local manager = EntryManager:new(5, function() + called_count = called_count + 1 + end) + + assert(called_count == 0) + manager:add_entry(nil, 1, "hello", "") + assert(called_count == 1) + end) + + it("calls functions when inserting twice", function() + local called_count = 0 + local manager = EntryManager:new(5, function() + called_count = called_count + 1 + end) + + assert(called_count == 0) + manager:add_entry(nil, 1, "hello", "") + manager:add_entry(nil, 2, "world", "") + assert(called_count == 2) + end) + + it("correctly sorts lower scores", function() + local called_count = 0 + local manager = EntryManager:new(5, function() + called_count = called_count + 1 + end) + manager:add_entry(nil, 5, "worse result", "") + manager:add_entry(nil, 2, "better result", "") + + eq("better result", manager:get_entry(1)) + eq("worse result", manager:get_entry(2)) + + eq(2, called_count) + end) + + it("respects max results", function() + local called_count = 0 + local manager = EntryManager:new(1, function() + called_count = called_count + 1 + end) + manager:add_entry(nil, 2, "better result", "") + manager:add_entry(nil, 5, "worse result", "") + + eq("better result", manager:get_entry(1)) + eq(1, called_count) + end) + + it("should allow simple entries", function() + local manager = EntryManager:new(5) + + local counts_executed = 0 + manager:add_entry( + nil, + 1, + setmetatable({}, { + __index = function(t, k) + local val = nil + if k == "ordinal" then + counts_executed = counts_executed + 1 + + -- This could be expensive, only call later + val = "wow" + end + + rawset(t, k, val) + return val + end, + }), + "" + ) + + eq("wow", manager:get_ordinal(1)) + eq("wow", manager:get_ordinal(1)) + eq("wow", manager:get_ordinal(1)) + + eq(1, counts_executed) + end) + + it("should not loop a bunch", function() + local info = {} + local manager = EntryManager:new(5, nil, info) + manager:add_entry(nil, 4, "better result", "") + manager:add_entry(nil, 3, "better result", "") + manager:add_entry(nil, 2, "better result", "") + + -- Loops once to find 3 < 4 + -- Loops again to find 2 < 3 + eq(2, info.looped) + end) + + it("should not loop a bunch, part 2", function() + local info = {} + local manager = EntryManager:new(5, nil, info) + manager:add_entry(nil, 4, "better result", "") + manager:add_entry(nil, 2, "better result", "") + manager:add_entry(nil, 3, "better result", "") + + -- Loops again to find 2 < 4 + -- Loops once to find 3 > 2 + -- but less than 4 + eq(3, info.looped) + end) + + it("should update worst score in all append case", function() + local manager = EntryManager:new(2, nil) + manager:add_entry(nil, 2, "result 2", "") + manager:add_entry(nil, 3, "result 3", "") + manager:add_entry(nil, 4, "result 4", "") + + eq(3, manager.worst_acceptable_score) + end) + + it("should update worst score in all prepend case", function() + local called_count = 0 + local manager = EntryManager:new(2, function() + called_count = called_count + 1 + end) + manager:add_entry(nil, 5, "worse result", "") + manager:add_entry(nil, 4, "less worse result", "") + manager:add_entry(nil, 2, "better result", "") + + -- Once for insert 5 + -- Once for prepend 4 + -- Once for prepend 2 + eq(3, called_count) + + eq("better result", manager:get_entry(1)) + eq(4, manager.worst_acceptable_score) + end) + + it("should call tiebreaker if score is the same, sort length", function() + local manager = EntryManager:new(5, nil) + local picker = { + tiebreak = function(curr, prev, prompt) + eq("asdf", prompt) + return #curr < #prev + end, + } + + manager:add_entry(picker, 0.5, "same same", "asdf") + manager:add_entry(picker, 0.5, "same", "asdf") + + eq("same", manager:get_entry(1)) + eq("same same", manager:get_entry(2)) + end) + + it("should call tiebreaker if score is the same, keep initial", function() + local manager = EntryManager:new(5, nil) + local picker = { + tiebreak = function(_, _, prompt) + eq("asdf", prompt) + return false + end, + } + + manager:add_entry(picker, 0.5, "same same", "asdf") + manager:add_entry(picker, 0.5, "same", "asdf") + + eq("same", manager:get_entry(2)) + eq("same same", manager:get_entry(1)) + end) +end) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/layout_strategies_spec.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/layout_strategies_spec.lua new file mode 100644 index 0000000..3315f63 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/layout_strategies_spec.lua @@ -0,0 +1,161 @@ +local config = require "telescope.config" +local resolve = require "telescope.config.resolve" +local layout_strats = require "telescope.pickers.layout_strategies" + +local validate_layout_config = layout_strats._validate_layout_config + +local eq = assert.are.same + +describe("layout_strategies", function() + it("should have validator", function() + assert(validate_layout_config, "Has validator") + end) + + local test_height = function(should, output, input, opts) + opts = opts or {} + + local max_columns, max_lines = opts.max_columns or 100, opts.max_lines or 100 + it(should, function() + local layout_config = validate_layout_config("horizontal", { height = true }, { height = input }) + + eq(output, resolve.resolve_height(layout_config.height)({}, max_columns, max_lines)) + end) + end + + test_height("should handle numbers", 10, 10) + + test_height("should handle percentage: 100", 10, 0.1, { max_lines = 100 }) + test_height("should handle percentage: 110", 11, 0.1, { max_lines = 110 }) + + test_height("should call functions: simple", 5, function() + return 5 + end) + test_height("should call functions: percentage", 15, function(_, _, lines) + return 0.1 * lines + end, { + max_lines = 150, + }) + + local test_defaults_key = function(should, key, strat, output, ours, theirs, override) + ours = ours or {} + theirs = theirs or {} + override = override or {} + + it(should, function() + config.clear_defaults() + config.set_defaults({ layout_config = theirs }, { layout_config = { ours, "description" } }) + local layout_config = validate_layout_config(strat, layout_strats._configurations[strat], override) + eq(output, layout_config[key]) + end) + end + + test_defaults_key( + "should use ours if theirs and override don't give the key", + "height", + "horizontal", + 50, + { height = 50 }, + { width = 100 }, + { width = 120 } + ) + + test_defaults_key( + "should use ours if theirs and override don't give the key for this strategy", + "height", + "horizontal", + 50, + { height = 50 }, + { vertical = { height = 100 } }, + { vertical = { height = 120 } } + ) + + test_defaults_key( + "should use theirs if override doesn't give the key", + "height", + "horizontal", + 100, + { height = 50 }, + { height = 100 }, + { width = 120 } + ) + + test_defaults_key( + "should use override if key given", + "height", + "horizontal", + 120, + { height = 50 }, + { height = 100 }, + { height = 120 } + ) + + test_defaults_key( + "should use override if key given for this strategy", + "height", + "horizontal", + 120, + { height = 50 }, + { height = 100 }, + { horizontal = { height = 120 } } + ) + + test_defaults_key( + "should use theirs if override doesn't give key (even if ours has strategy specific)", + "height", + "horizontal", + 100, + { horizontal = { height = 50 } }, + { height = 100 }, + { width = 120 } + ) + + test_defaults_key( + "should use override (even if ours has strategy specific)", + "height", + "horizontal", + 120, + { horizontal = { height = 50 } }, + { height = 100 }, + { height = 120 } + ) + + test_defaults_key( + "should use override (even if theirs has strategy specific)", + "height", + "horizontal", + 120, + { height = 50 }, + { horizontal = { height = 100 } }, + { height = 120 } + ) + + test_defaults_key( + "should use override (even if ours and theirs have strategy specific)", + "height", + "horizontal", + 120, + { horizontal = { height = 50 } }, + { horizontal = { height = 100 } }, + { height = 120 } + ) + + test_defaults_key( + "should handle user config overriding a table with a number", + "height", + "horizontal", + 120, + { height = { padding = 5 } }, + { height = 120 }, + {} + ) + + test_defaults_key( + "should handle user oneshot overriding a table with a number", + "height", + "horizontal", + 120, + {}, + { height = { padding = 5 } }, + { height = 120 } + ) +end) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/linked_list_spec.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/linked_list_spec.lua new file mode 100644 index 0000000..bc17ba1 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/linked_list_spec.lua @@ -0,0 +1,133 @@ +local LinkedList = require "telescope.algos.linked_list" + +describe("LinkedList", function() + it("can create a list", function() + local l = LinkedList:new() + + assert.are.same(0, l.size) + end) + + it("can add a single entry to the list", function() + local l = LinkedList:new() + l:append "hello" + + assert.are.same(1, l.size) + end) + + it("can iterate over one item", function() + local l = LinkedList:new() + l:append "hello" + + for val in l:iter() do + assert.are.same("hello", val) + end + end) + + it("iterates in order", function() + local l = LinkedList:new() + l:append "hello" + l:append "world" + + local x = {} + for val in l:iter() do + table.insert(x, val) + end + + assert.are.same({ "hello", "world" }, x) + end) + + it("iterates in order, for prepend", function() + local l = LinkedList:new() + l:prepend "world" + l:prepend "hello" + + local x = {} + for val in l:iter() do + table.insert(x, val) + end + + assert.are.same({ "hello", "world" }, x) + end) + + it("iterates in order, for combo", function() + local l = LinkedList:new() + l:prepend "world" + l:prepend "hello" + l:append "last" + l:prepend "first" + + local x = {} + for val in l:iter() do + table.insert(x, val) + end + + assert.are.same({ "first", "hello", "world", "last" }, x) + assert.are.same(#x, l.size) + end) + + it("has ipairs", function() + local l = LinkedList:new() + l:prepend "world" + l:prepend "hello" + l:append "last" + l:prepend "first" + + local x = {} + for v in l:iter() do + table.insert(x, v) + end + assert.are.same({ "first", "hello", "world", "last" }, x) + + local expected = {} + for i, v in ipairs(x) do + table.insert(expected, { i, v }) + end + + local actual = {} + for i, v in l:ipairs() do + table.insert(actual, { i, v }) + end + + assert.are.same(expected, actual) + end) + + describe("track_at", function() + it("should update tracked when only appending", function() + local l = LinkedList:new { track_at = 2 } + l:append "first" + l:append "second" + l:append "third" + + assert.are.same("second", l.tracked) + end) + + it("should update tracked when first some prepend and then append", function() + local l = LinkedList:new { track_at = 2 } + l:prepend "first" + l:append "second" + l:append "third" + + assert.are.same("second", l.tracked) + end) + + it("should update when only prepending", function() + local l = LinkedList:new { track_at = 2 } + l:prepend "third" + l:prepend "second" + l:prepend "first" + + assert.are.same("second", l.tracked) + end) + + it("should update when lots of prepend and append", function() + local l = LinkedList:new { track_at = 2 } + l:prepend "third" + l:prepend "second" + l:prepend "first" + l:append "fourth" + l:prepend "zeroth" + + assert.are.same("first", l.tracked) + end) + end) +end) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/pickers/find_files_spec.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/pickers/find_files_spec.lua new file mode 100644 index 0000000..5a1c460 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/pickers/find_files_spec.lua @@ -0,0 +1,143 @@ +-- Just skip on mac, it has flaky CI for some reason +if vim.fn.has "mac" == 1 or require("telescope.utils").iswin then + return +end + +local tester = require "telescope.testharness" + +local disp = function(val) + return vim.inspect(val, { newline = " ", indent = "" }) +end + +describe("builtin.find_files", function() + it("should find the readme", function() + tester.run_file "find_files__readme" + end) + + it("should handle cycling for full list", function() + tester.run_file "find_files__scrolling_descending_cycle" + end) + + for _, configuration in ipairs { + { sorting_strategy = "descending" }, + { sorting_strategy = "ascending" }, + } do + it("should not display devicons when disabled: " .. disp(configuration), function() + tester.run_string(string.format( + [[ + local max_results = 5 + + runner.picker('find_files', 'README.md', { + post_typed = { + { "> README.md", GetPrompt }, + { "> README.md", GetBestResult }, + }, + post_close = { + { 'README.md', GetFile }, + { 'README.md', GetFile }, + } + }, vim.tbl_extend("force", { + disable_devicons = true, + sorter = require('telescope.sorters').get_fzy_sorter(), + layout_strategy = 'center', + layout_config = { + height = max_results + 1, + width = 0.9, + }, + }, vim.json.decode([==[%s]==]))) + ]], + vim.json.encode(configuration) + )) + end) + + pending("use devicons, if it has it when enabled", function() + if not pcall(require, "nvim-web-devicons") then + return + end + + local md = require("nvim-web-devicons").get_icon "md" + tester.run_string(string.format( + [[ + runner.picker('find_files', 'README.md', { + post_typed = { + { "> README.md", GetPrompt }, + { "> %s README.md", GetBestResult } + }, + post_close = { + { 'README.md', GetFile }, + { 'README.md', GetFile }, + } + }, vim.tbl_extend("force", { + disable_devicons = false, + sorter = require('telescope.sorters').get_fzy_sorter(), + }, vim.json.decode([==[%s]==]))) + ]], + md, + vim.json.encode(configuration) + )) + end) + end + + it("should find the readme, using lowercase", function() + tester.run_string [[ + runner.picker('find_files', 'readme.md', { + post_close = { + { 'README.md', GetFile }, + } + }) + ]] + end) + + it("should find the pickers.lua, using lowercase", function() + tester.run_string [[ + runner.picker('find_files', 'pickers.lua', { + post_close = { + { 'pickers.lua', GetFile }, + } + }) + ]] + end) + + it("should find the pickers.lua", function() + tester.run_string [[ + runner.picker('find_files', 'pickers.lua', { + post_close = { + { 'pickers.lua', GetFile }, + { 'pickers.lua', GetFile }, + } + }) + ]] + end) + + it("should be able to c-n the items", function() + tester.run_string [[ + runner.picker('find_files', 'fixtures/find_files/file', { + post_typed = { + { + { + " lua/tests/fixtures/find_files/file_a.txt", + "> lua/tests/fixtures/find_files/file_abc.txt", + }, GetResults + }, + }, + post_close = { + { 'file_abc.txt', GetFile }, + }, + }, { + sorter = require('telescope.sorters').get_fzy_sorter(), + sorting_strategy = "ascending", + disable_devicons = true, + }) + ]] + end) + + it("should be able to get the current selection", function() + tester.run_string [[ + runner.picker('find_files', 'fixtures/find_files/file_abc', { + post_typed = { + { 'lua/tests/fixtures/find_files/file_abc.txt', GetSelectionValue }, + } + }) + ]] + end) +end) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/pickers/live_grep_spec.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/pickers/live_grep_spec.lua new file mode 100644 index 0000000..3471c90 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/pickers/live_grep_spec.lua @@ -0,0 +1,46 @@ +if vim.fn.has "mac" == 1 or require("telescope.utils").iswin then + return +end + +local tester = require "telescope.testharness" + +local disp = function(val) + return vim.inspect(val, { newline = " ", indent = "" }) +end + +describe("builtin.live_grep", function() + for _, configuration in ipairs { + { sorting_strategy = "descending" }, + { sorting_strategy = "ascending" }, + } do + it("clears results correctly when " .. disp(configuration), function() + tester.run_string(string.format( + [[ + runner.picker( + "live_grep", + "abcdG", + { + post_typed = { + { + 5, + function() + return #vim.tbl_filter(function(line) + return line ~= "" + end, GetResults()) + end, + }, + }, + }, + vim.tbl_extend("force", { + sorter = require("telescope.sorters").get_fzy_sorter(), + layout_strategy = "center", + cwd = "./lua/tests/fixtures/live_grep", + temp__scrolling_limit = 5, + }, vim.json.decode [==[%s]==]) + ) + ]], + vim.json.encode(configuration) + )) + end) + end +end) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/resolver_spec.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/resolver_spec.lua new file mode 100644 index 0000000..3103901 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/resolver_spec.lua @@ -0,0 +1,208 @@ +local eq = function(a, b) + assert.are.same(a, b) +end + +local resolve = require "telescope.config.resolve" + +describe("telescope.config.resolve", function() + describe("win_option", function() + it("should resolve for percentages", function() + local height_config = 0.8 + local opt = resolve.win_option(height_config) + + eq(height_config, opt.preview) + eq(height_config, opt.prompt) + eq(height_config, opt.results) + end) + + it("should resolve for percentages with default", function() + local height_config = 0.8 + local opt = resolve.win_option(nil, height_config) + + eq(height_config, opt.preview) + eq(height_config, opt.prompt) + eq(height_config, opt.results) + end) + + it("should resolve table values", function() + local table_val = { "a" } + local opt = resolve.win_option(nil, table_val) + + eq(table_val, opt.preview) + eq(table_val, opt.prompt) + eq(table_val, opt.results) + end) + + it("should allow overrides for different wins", function() + local prompt_override = { "a", prompt = "b" } + local opt = resolve.win_option(prompt_override) + eq("a", opt.preview) + eq("a", opt.results) + eq("b", opt.prompt) + end) + + it("should allow overrides for all wins", function() + local all_specified = { preview = "a", prompt = "b", results = "c" } + local opt = resolve.win_option(all_specified) + eq("a", opt.preview) + eq("b", opt.prompt) + eq("c", opt.results) + end) + + it("should allow some specified with a simple default", function() + local some_specified = { prompt = "b", results = "c" } + local opt = resolve.win_option(some_specified, "a") + eq("a", opt.preview) + eq("b", opt.prompt) + eq("c", opt.results) + end) + end) + + describe("resolve_height/width", function() + local test_sizes = { + { 24, 100 }, + { 35, 125 }, + { 60, 59 }, + { 100, 40 }, + } + it("should handle percentages", function() + local percentages = { 0.1, 0.33333, 0.5, 0.99 } + for _, s in ipairs(test_sizes) do + for _, p in ipairs(percentages) do + eq(math.floor(s[1] * p), resolve.resolve_width(p)(nil, unpack(s))) + eq(math.floor(s[2] * p), resolve.resolve_height(p)(nil, unpack(s))) + end + end + end) + + it("should handle percentages with min/max boundary", function() + eq(20, resolve.resolve_width { 0.1, min = 20 }(nil, 40, 120)) + eq(30, resolve.resolve_height { 0.1, min = 20 }(nil, 40, 300)) + + eq(24, resolve.resolve_width { 0.4, max = 80 }(nil, 60, 60)) + eq(80, resolve.resolve_height { 0.4, max = 80 }(nil, 60, 300)) + end) + + it("should handle fixed size", function() + local fixed = { 5, 8, 13, 21, 34 } + for _, s in ipairs(test_sizes) do + for _, f in ipairs(fixed) do + eq(math.min(f, s[1]), resolve.resolve_width(f)(nil, unpack(s))) + eq(math.min(f, s[2]), resolve.resolve_height(f)(nil, unpack(s))) + end + end + end) + + it("should handle functions", function() + local func = function(_, max_columns, max_lines) + if max_columns < 45 then + return math.min(max_columns, max_lines) + elseif max_columns < max_lines then + return max_columns * 0.8 + else + return math.min(max_columns, max_lines) * 0.5 + end + end + for _, s in ipairs(test_sizes) do + eq(func(nil, unpack(s)), resolve.resolve_height(func)(nil, unpack(s))) + end + end) + + it("should handle padding", function() + local func = function(_, max_columns, max_lines) + return math.floor(math.min(max_columns * 0.6, max_lines * 0.8)) + end + local pads = { 0.1, 5, func } + for _, s in ipairs(test_sizes) do + for _, p in ipairs(pads) do + eq(s[1] - 2 * resolve.resolve_width(p)(nil, unpack(s)), resolve.resolve_width { padding = p }(nil, unpack(s))) + eq( + s[2] - 2 * resolve.resolve_height(p)(nil, unpack(s)), + resolve.resolve_height { padding = p }(nil, unpack(s)) + ) + end + end + end) + end) + + describe("resolve_anchor_pos", function() + local test_sizes = { + { 6, 7, 8, 9, 1 }, + { 10, 20, 30, 40, 1 }, + { 15, 15, 16, 16, 1 }, + { 17, 19, 23, 31, 1 }, + { 21, 18, 26, 24, 1 }, + { 50, 100, 150, 200, 1 }, + } + + it([[should not adjust when "CENTER" or "" is the anchor]], function() + for _, s in ipairs(test_sizes) do + eq({ 0, 0 }, resolve.resolve_anchor_pos("", unpack(s))) + eq({ 0, 0 }, resolve.resolve_anchor_pos("center", unpack(s))) + eq({ 0, 0 }, resolve.resolve_anchor_pos("CENTER", unpack(s))) + end + end) + + it([[should end up at top when "N" in the anchor]], function() + local top_test = function(anchor, p_width, p_height, max_columns, max_lines) + local pos = resolve.resolve_anchor_pos(anchor, p_width, p_height, max_columns, max_lines, 1) + eq(1, pos[2] + math.floor((max_lines - p_height) / 2)) + end + for _, s in ipairs(test_sizes) do + top_test("NW", unpack(s)) + top_test("N", unpack(s)) + top_test("NE", unpack(s)) + end + end) + + it([[should end up at left when "W" in the anchor]], function() + local left_test = function(anchor, p_width, p_height, max_columns, max_lines) + local pos = resolve.resolve_anchor_pos(anchor, p_width, p_height, max_columns, max_lines, 1) + eq(1, pos[1] + math.floor((max_columns - p_width) / 2)) + end + for _, s in ipairs(test_sizes) do + left_test("NW", unpack(s)) + left_test("W", unpack(s)) + left_test("SW", unpack(s)) + end + end) + + it([[should end up at bottom when "S" in the anchor]], function() + local bot_test = function(anchor, p_width, p_height, max_columns, max_lines) + local pos = resolve.resolve_anchor_pos(anchor, p_width, p_height, max_columns, max_lines, 1) + eq(max_lines - 1, pos[2] + p_height + math.floor((max_lines - p_height) / 2)) + end + for _, s in ipairs(test_sizes) do + bot_test("SW", unpack(s)) + bot_test("S", unpack(s)) + bot_test("SE", unpack(s)) + end + end) + + it([[should end up at right when "E" in the anchor]], function() + local right_test = function(anchor, p_width, p_height, max_columns, max_lines) + local pos = resolve.resolve_anchor_pos(anchor, p_width, p_height, max_columns, max_lines, 1) + eq(max_columns - 1, pos[1] + p_width + math.floor((max_columns - p_width) / 2)) + end + for _, s in ipairs(test_sizes) do + right_test("NE", unpack(s)) + right_test("E", unpack(s)) + right_test("SE", unpack(s)) + end + end) + + it([[should ignore casing of the anchor]], function() + local case_test = function(a1, a2, p_width, p_height, max_columns, max_lines) + local pos1 = resolve.resolve_anchor_pos(a1, p_width, p_height, max_columns, max_lines, 1) + local pos2 = resolve.resolve_anchor_pos(a2, p_width, p_height, max_columns, max_lines, 1) + eq(pos1, pos2) + end + for _, s in ipairs(test_sizes) do + case_test("ne", "NE", unpack(s)) + case_test("w", "W", unpack(s)) + case_test("sW", "sw", unpack(s)) + case_test("cEnTeR", "CeNtEr", unpack(s)) + end + end) + end) +end) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/scroller_spec.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/scroller_spec.lua new file mode 100644 index 0000000..96d64af --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/scroller_spec.lua @@ -0,0 +1,143 @@ +local p_scroller = require "telescope.pickers.scroller" + +local log = require "telescope.log" +log.use_console = false + +local eq = assert.are.same + +describe("scroller", function() + local max_results = 10 + + describe("ascending cycle", function() + local cycle_scroller = p_scroller.create("cycle", "ascending") + + it("should return values within the max results", function() + eq(5, cycle_scroller(max_results, max_results, 5)) + end) + + it("should return 0 at 0", function() + eq(0, cycle_scroller(max_results, max_results, 0)) + end) + + it("should cycle you to the top when you go below 0", function() + eq(max_results - 1, cycle_scroller(max_results, max_results, -1)) + end) + + it("should cycle you to 0 when you go past the results", function() + eq(0, cycle_scroller(max_results, max_results, max_results + 1)) + end) + + it("should cycle when current results is less than max_results", function() + eq(0, cycle_scroller(max_results, 5, 7)) + end) + end) + + describe("ascending limit", function() + local limit_scroller = p_scroller.create("limit", "ascending") + + it("should return values within the max results", function() + eq(5, limit_scroller(max_results, max_results, 5)) + end) + + it("should return 0 at 0", function() + eq(0, limit_scroller(max_results, max_results, 0)) + end) + + it("should not cycle", function() + eq(0, limit_scroller(max_results, max_results, -1)) + end) + + it("should not cycle you to 0 when you go past the results", function() + eq(max_results - 1, limit_scroller(max_results, max_results, max_results + 1)) + end) + + it("should stay at current results when current results is less than max_results", function() + local current = 5 + eq(current - 1, limit_scroller(max_results, current, 7)) + end) + end) + + describe("descending cycle", function() + local cycle_scroller = p_scroller.create("cycle", "descending") + + it("should return values within the max results", function() + eq(5, cycle_scroller(max_results, max_results, 5)) + end) + + it("should return max_results - 1 at 0", function() + eq(0, cycle_scroller(max_results, max_results, 0)) + end) + + it("should cycle you to the bot when you go below 0", function() + eq(max_results - 1, cycle_scroller(max_results, max_results, -1)) + end) + + it("should cycle you to 0 when you go past the results", function() + eq(0, cycle_scroller(max_results, max_results, max_results + 1)) + end) + + it("should cycle when current results is less than max_results", function() + eq(9, cycle_scroller(max_results, 5, 4)) + end) + end) + + describe("descending limit", function() + local limit_scroller = p_scroller.create("limit", "descending") + + it("should return values within the max results", function() + eq(5, limit_scroller(max_results, max_results, 5)) + end) + + it("should return 0 at 0", function() + eq(0, limit_scroller(max_results, max_results, 0)) + end) + + it("should not cycle", function() + eq(0, limit_scroller(max_results, max_results, -1)) + end) + + it("should not cycle you to 0 when you go past the results", function() + eq(max_results - 1, limit_scroller(max_results, max_results, max_results + 1)) + end) + + it("should stay at current results when current results is less than max_results", function() + local current = 5 + eq(max_results - current, limit_scroller(max_results, current, 4)) + end) + end) + + describe("https://github.com/nvim-telescope/telescope.nvim/pull/293#issuecomment-751463224", function() + it("should handle having many more results than necessary", function() + local scroller = p_scroller.create("cycle", "descending") + + -- 23 112 23 + eq(0, scroller(23, 112, 23)) + end) + end) + + describe("should give top, middle and bottom index", function() + it("should handle ascending", function() + eq(0, p_scroller.top("ascending", 20, 1000)) + eq(19, p_scroller.bottom("ascending", 20, 1000)) + + eq(0, p_scroller.top("ascending", 20, 10)) + eq(9, p_scroller.bottom("ascending", 20, 10)) + + eq(5, p_scroller.middle("ascending", 11, 100)) + eq(10, p_scroller.middle("ascending", 20, 100)) + eq(12, p_scroller.middle("ascending", 25, 100)) + end) + + it("should handle descending", function() + eq(0, p_scroller.top("descending", 20, 1000)) + eq(19, p_scroller.bottom("descending", 20, 1000)) + + eq(10, p_scroller.top("descending", 20, 10)) + eq(19, p_scroller.bottom("descending", 20, 10)) + + eq(25, p_scroller.middle("descending", 30, 10)) + eq(50, p_scroller.middle("descending", 60, 20)) + eq(105, p_scroller.middle("descending", 120, 30)) + end) + end) +end) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/sorters_spec.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/sorters_spec.lua new file mode 100644 index 0000000..f3453a2 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/sorters_spec.lua @@ -0,0 +1,84 @@ +local sorters = require "telescope.sorters" + +describe("get_substr_matcher", function() + local function with_smartcase(smartcase, case) + local original = vim.o.smartcase + vim.o.smartcase = smartcase + + describe("scoring_function", function() + it(case.msg, function() + local matcher = sorters.get_substr_matcher() + assert.are.same(case.expected_score, matcher.scoring_function(_, case.prompt, _, case.entry)) + end) + end) + + describe("highlighter", function() + it("returns valid highlights", function() + local matcher = sorters.get_substr_matcher() + local highlights = matcher.highlighter(_, case.prompt, case.entry.ordinal) + table.sort(highlights, function(a, b) + return a.start < b.start + end) + assert.are.same(case.expected_highlights, highlights) + end) + end) + + vim.o.smartcase = original + end + + describe("when smartcase=OFF", function() + for _, case in ipairs { + { + msg = "doesn't match", + prompt = "abc def", + entry = { index = 3, ordinal = "abc d" }, + expected_score = -1, + expected_highlights = { { start = 1, finish = 3 } }, + }, + { + msg = "matches with lower case letters only", + prompt = "abc def", + entry = { index = 3, ordinal = "abc def ghi" }, + expected_score = 3, + expected_highlights = { { start = 1, finish = 3 }, { start = 5, finish = 7 } }, + }, + { + msg = "doesn't match with upper case letters", + prompt = "ABC def", + entry = { index = 3, ordinal = "ABC def ghi" }, + expected_score = -1, + expected_highlights = { { start = 5, finish = 7 } }, + }, + } do + with_smartcase(false, case) + end + end) + + describe("when smartcase=OFF", function() + for _, case in ipairs { + { + msg = "doesn't match", + prompt = "abc def", + entry = { index = 3, ordinal = "abc d" }, + expected_score = -1, + expected_highlights = { { start = 1, finish = 3 } }, + }, + { + msg = "matches with lower case letters only", + prompt = "abc def", + entry = { index = 3, ordinal = "abc def ghi" }, + expected_score = 3, + expected_highlights = { { start = 1, finish = 3 }, { start = 5, finish = 7 } }, + }, + { + msg = "matches with upper case letters", + prompt = "ABC def", + entry = { index = 3, ordinal = "ABC def ghi" }, + expected_score = 3, + expected_highlights = { { start = 1, finish = 3 }, { start = 5, finish = 7 } }, + }, + } do + with_smartcase(true, case) + end + end) +end) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/telescope_spec.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/telescope_spec.lua new file mode 100644 index 0000000..44594dd --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/telescope_spec.lua @@ -0,0 +1,218 @@ +local picker = require "telescope.pickers" +local Path = require "plenary.path" + +local eq = assert.are.same + +local function new_path(unix_path) + return Path:new(unpack(vim.split(unix_path, "/"))).filename +end + +describe("telescope", function() + describe("Picker", function() + describe("window_dimensions", function() + it("", function() + assert(true) + end) + end) + + describe("attach_mappings", function() + local new_picker = function(a, b) + a.finder = true + return picker.new(a, b) + end + + it("should allow for passing in a function", function() + local p = new_picker({}, { + attach_mappings = function() + return 1 + end, + }) + eq(1, p.attach_mappings()) + end) + + it("should override an attach mappings passed in by opts", function() + local called_order = {} + local p = new_picker({ + attach_mappings = function() + table.insert(called_order, "opts") + end, + }, { + attach_mappings = function() + table.insert(called_order, "default") + end, + }) + + p.attach_mappings() + + eq({ "default", "opts" }, called_order) + end) + end) + end) + + describe("Sorters", function() + describe("generic_fuzzy_sorter", function() + it("sort matches well", function() + local sorter = require("telescope.sorters").get_generic_fuzzy_sorter() + + local exact_match = sorter:score("hello", { ordinal = "hello" }) + local no_match = sorter:score("abcdef", { ordinal = "ghijkl" }) + local ok_match = sorter:score("abcdef", { ordinal = "ab" }) + + assert(exact_match < no_match, "exact match better than no match") + assert(exact_match < ok_match, "exact match better than ok match") + assert(ok_match < no_match, "ok match better than no match") + end) + + it("sorts multiple finds better", function() + local sorter = require("telescope.sorters").get_generic_fuzzy_sorter() + + local multi_match = sorter:score("generics", "exercises/generics/generics2.rs") + local one_match = sorter:score("abcdef", "exercises/generics/README.md") + + -- assert(multi_match < one_match) + end) + end) + + describe("fuzzy_file", function() + it("sort matches well", function() + local sorter = require("telescope.sorters").get_fuzzy_file() + + local exact_match = sorter:score("abcdef", { ordinal = "abcdef" }) + local no_match = sorter:score("abcdef", { ordinal = "ghijkl" }) + local ok_match = sorter:score("abcdef", { ordinal = "ab" }) + + assert(exact_match < no_match, string.format("Exact match better than no match: %s %s", exact_match, no_match)) + assert(exact_match < ok_match, string.format("Exact match better than OK match: %s %s", exact_match, ok_match)) + assert(ok_match < no_match, "OK match better than no match") + end) + + it("sorts matches after last os sep better", function() + local sorter = require("telescope.sorters").get_fuzzy_file() + + local better_match = sorter:score("aaa", { ordinal = new_path "bbb/aaa" }) + local worse_match = sorter:score("aaa", { ordinal = new_path "aaa/bbb" }) + + assert(better_match < worse_match, "Final match should be stronger") + end) + + pending("sorts multiple finds better", function() + local sorter = require("telescope.sorters").get_fuzzy_file() + + local multi_match = sorter:score("generics", { ordinal = "exercises/generics/generics2.rs" }) + local one_match = sorter:score("abcdef", { ordinal = "exercises/generics/README.md" }) + + assert(multi_match < one_match) + end) + end) + + describe("fzy", function() + local sorter = require("telescope.sorters").get_fzy_sorter() + local function score(prompt, line) + line = new_path(line) + return sorter:score(prompt, { ordinal = line }, function(val) + return val + end, function() + return -1 + end) + end + + describe("matches", function() + it("exact matches", function() + assert.True(score("a", "a") >= 0) + assert.True(score("a.bb", "a.bb") >= 0) + end) + it("ignore case", function() + assert.True(score("AbB", "abb") >= 0) + assert.True(score("abb", "ABB") >= 0) + end) + it("partial matches", function() + assert.True(score("a", "ab") >= 0) + assert.True(score("a", "ba") >= 0) + assert.True(score("aba", "baabbaab") >= 0) + end) + it("with delimiters between", function() + assert.True(score("abc", "a|b|c") >= 0) + end) + it("with empty query", function() + assert.True(score("", "") >= 0) + assert.True(score("", "a") >= 0) + end) + it("rejects non-matches", function() + assert.True(score("a", "") < 0) + assert.True(score("a", "b") < 0) + assert.True(score("aa", "a") < 0) + assert.True(score("ba", "a") < 0) + assert.True(score("ab", "a") < 0) + end) + end) + + describe("scoring", function() + it("prefers beginnings of words", function() + assert.True(score("amor", "app/models/order") < score("amor", "app/models/zrder")) + end) + it("prefers consecutive letters", function() + assert.True(score("amo", "app/models/foo") < score("amo", "app/m/foo")) + assert.True(score("erf", "perfect") < score("erf", "terrific")) + end) + it("prefers contiguous over letter following period", function() + assert.True(score("gemfil", "Gemfile") < score("gemfil", "Gemfile.lock")) + end) + it("prefers shorter matches", function() + assert.True(score("abce", "abcdef") < score("abce", "abc de")) + assert.True(score("abc", " a b c ") < score("abc", " a b c ")) + assert.True(score("abc", " a b c ") < score("abc", " a b c ")) + end) + it("prefers shorter candidates", function() + assert.True(score("test", "tests") < score("test", "testing")) + end) + it("prefers matches at the beginning", function() + assert.True(score("ab", "abbb") < score("ab", "babb")) + assert.True(score("test", "testing") < score("test", "/testing")) + end) + it("prefers matches at some locations", function() + assert.True(score("a", "/a") < score("a", "ba")) + assert.True(score("a", "bA") < score("a", "ba")) + assert.True(score("a", ".a") < score("a", "ba")) + end) + end) + + local function positions(prompt, line) + return sorter:highlighter(prompt, new_path(line)) + end + + describe("positioning", function() + it("favors consecutive positions", function() + assert.same({ 1, 5, 6 }, positions("amo", "app/models/foo")) + end) + it("favors word beginnings", function() + assert.same({ 1, 5, 12, 13 }, positions("amor", "app/models/order")) + end) + it("works when there are no bonuses", function() + assert.same({ 2, 4 }, positions("as", "tags")) + assert.same({ 3, 8 }, positions("as", "examples.txt")) + end) + it("favors smaller groupings of positions", function() + assert.same({ 3, 5, 7 }, positions("abc", "a/a/b/c/c")) + assert.same({ 3, 5 }, positions("ab", "caacbbc")) + end) + it("handles exact matches", function() + assert.same({ 1, 2, 3 }, positions("foo", "foo")) + end) + it("ignores empty requests", function() + assert.same({}, positions("", "")) + assert.same({}, positions("", "foo")) + assert.same({}, positions("foo", "")) + end) + end) + end) + + describe("layout_strategies", function() + describe("center", function() + it("should handle large terminals", function() + -- TODO: This could call layout_strategies.center w/ some weird edge case. + -- and then assert stuff about the dimensions. + end) + end) + end) + end) +end) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/utils_spec.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/utils_spec.lua new file mode 100644 index 0000000..b99a665 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/automated/utils_spec.lua @@ -0,0 +1,393 @@ +local Path = require "plenary.path" +local utils = require "telescope.utils" + +local eq = assert.are.equal + +describe("path_expand()", function() + it("removes trailing os_sep", function() + if utils.iswin then + eq([[C:\Users\a\b]], utils.path_expand [[C:\Users\a\b\]]) + else + eq("/home/user", utils.path_expand "/home/user/") + end + end) + + it("works with root dir", function() + if utils.iswin then + eq([[C:\]], utils.path_expand [[C:\]]) + else + eq("/", utils.path_expand "/") + end + end) + + it("works with ~", function() + eq(vim.loop.os_homedir() .. "/src/foo", utils.path_expand "~/src/foo") + end) + + it("handles duplicate os_sep", function() + if utils.iswin then + eq([[C:\Users\a]], utils.path_expand [[C:\\\Users\\a]]) + else + eq("/home/user", utils.path_expand "/home///user") + end + end) + + it("preserves fake whitespace characters and whitespace", function() + local path_space = "/home/user/hello world" + eq(path_space, utils.path_expand(path_space)) + local path_newline = [[/home/user/hello\nworld]] + eq(path_newline, utils.path_expand(path_newline)) + end) + describe("early return for uri", function() + local uris = { + [[https://www.example.com/index.html]], + [[ftp://ftp.example.com/files/document.pdf]], + [[mailto:user@example.com]], + [[tel:+1234567890]], + [[file:///home/user/documents/report.docx]], + [[news:comp.lang.python]], + [[ldap://ldap.example.com:389/dc=example,dc=com]], + [[git://github.com/user/repo.git]], + [[steam://run/123456]], + [[magnet:?xt=urn:btih:6B4C3343E1C63A1BC36AEB8A3D1F52C4EDEEB096]], + } + + for _, uri in ipairs(uris) do + it(uri, function() + eq(uri, utils.path_expand(uri)) + end) + end + end) +end) + +describe("is_uri", function() + describe("detects valid uris", function() + local uris = { + [[https://www.example.com/index.html]], + [[ftp://ftp.example.com/files/document.pdf]], + [[mailto:user@example.com]], + [[tel:+1234567890]], + [[file:///home/user/documents/report.docx]], + [[news:comp.lang.python]], + [[ldap://ldap.example.com:389/dc=example,dc=com]], + [[git://github.com/user/repo.git]], + [[steam://run/123456]], + [[magnet:?xt=urn:btih:6B4C3343E1C63A1BC36AEB8A3D1F52C4EDEEB096]], + } + + for _, uri in ipairs(uris) do + it(uri, function() + assert.True(utils.is_uri(uri)) + end) + end + end) + + describe("detects invalid uris/paths", function() + local inputs = { + "hello", + "hello:", + "123", + "", + } + for _, input in ipairs(inputs) do + it(input, function() + assert.False(utils.is_uri(input)) + end) + end + end) + + describe("handles windows paths", function() + local paths = { + [[C:\Users\Usuario\Documents\archivo.txt]], + [[D:\Projects\project_folder\source_code.py]], + [[E:\Music\song.mp3]], + } + + for _, uri in ipairs(paths) do + it(uri, function() + assert.False(utils.is_uri(uri)) + end) + end + end) + + describe("handles linux paths", function() + local paths = { + [[/home/usuario/documents/archivo.txt]], + [[/var/www/html/index.html]], + [[/mnt/backup/backup_file.tar.gz]], + } + + for _, path in ipairs(paths) do + it(path, function() + assert.False(utils.is_uri(path)) + end) + end + end) + + describe("handles macos paths", function() + local paths = { + [[/Users/Usuario/Documents/archivo.txt]], + [[/Applications/App.app/Contents/MacOS/app_executable]], + [[/Volumes/ExternalDrive/Data/file.xlsx]], + } + + for _, path in ipairs(paths) do + it(path, function() + assert.False(utils.is_uri(path)) + end) + end + end) +end) + +describe("__separates_file_path_location", function() + local suites = { + { + input = "file.txt:12:4", + file = "file.txt", + row = 12, + col = 4, + }, + { + input = "file.txt:12", + file = "file.txt", + row = 12, + col = 0, + }, + { + input = "file:12:4", + file = "file", + row = 12, + col = 4, + }, + { + input = "file:12:", + file = "file", + row = 12, + col = 0, + }, + { + input = "file:", + file = "file", + }, + } + + for _, suite in ipairs(suites) do + it("separtates file path for " .. suite.input, function() + local file, row, col = utils.__separate_file_path_location(suite.input) + + eq(file, suite.file) + eq(row, suite.row) + eq(col, suite.col) + end) + end +end) + +describe("transform_path", function() + local cwd = (function() + if utils.iswin then + return [[C:\Users\user\projects\telescope.nvim]] + else + return "/home/user/projects/telescope.nvim" + end + end)() + + local function new_relpath(unix_path) + return Path:new(unpack(vim.split(unix_path, "/"))).filename + end + + local function assert_path(path_display, path, expect) + local opts = { cwd = cwd, __length = 15 } + if type(path_display) == "string" then + opts.path_display = { path_display } + eq(expect, utils.transform_path(opts, path)) + opts.path_display = { [path_display] = true } + eq(expect, utils.transform_path(opts, path)) + elseif type(path_display) == "table" then + opts.path_display = path_display + eq(expect, utils.transform_path(opts, path)) + elseif type(path_display) == "function" then + opts.path_display = path_display + eq(expect, utils.transform_path(opts, path)) + elseif path_display == nil then + eq(expect, utils.transform_path(opts, path)) + end + end + + it("handles nil path", function() + assert_path(nil, nil, "") + end) + + it("returns back uri", function() + local uri = [[https://www.example.com/index.html]] + assert_path(nil, uri, uri) + end) + + it("handles 'hidden' path_display", function() + eq("", utils.transform_path({ cwd = cwd, path_display = "hidden" }, "foobar")) + assert_path("hidden", "foobar", "") + end) + + it("returns relative path for default opts", function() + local relative = Path:new { "lua", "telescope", "init.lua" } + local absolute = Path:new { cwd, relative } + assert_path(nil, absolute.filename, relative.filename) + assert_path(nil, relative.filename, relative.filename) + end) + + it("handles 'tail' path_display", function() + local path = new_relpath "lua/telescope/init.lua" + assert_path("tail", path, "init.lua") + end) + + it("handles 'smart' path_display", function() + local path1 = new_relpath "lua/telescope/init.lua" + local path2 = new_relpath "lua/telescope/finders.lua" + local path3 = new_relpath "lua/telescope/finders/async_job_finder.lua" + local path4 = new_relpath "plugin/telescope.lua" + + assert_path("smart", path1, path1) + assert_path("smart", path2, new_relpath "../telescope/finders.lua") + assert_path("smart", path3, new_relpath "../telescope/finders/async_job_finder.lua") + assert_path("smart", path4, path4) + end) + + it("handles 'absolute' path_display", function() + local relative = Path:new { "lua", "telescope", "init.lua" } + local absolute = Path:new { cwd, relative } + + -- TODO: feels like 'absolute' should turn relative paths to absolute + -- assert_path("absolute", relative.filename, absolute.filename) + assert_path("absolute", absolute.filename, absolute.filename) + end) + + it("handles default 'shorten' path_display", function() + assert_path("shorten", new_relpath "lua/telescope/init.lua", new_relpath "l/t/init.lua") + end) + + it("handles 'shorten' with number", function() + assert_path({ shorten = 2 }, new_relpath "lua/telescope/init.lua", new_relpath "lu/te/init.lua") + end) + + it("handles 'shorten' with option table", function() + assert_path({ shorten = { len = 2 } }, new_relpath "lua/telescope/init.lua", new_relpath "lu/te/init.lua") + assert_path( + { shorten = { len = 2, exclude = { 1, 3, -1 } } }, + new_relpath "lua/telescope/builtin/init.lua", + new_relpath "lua/te/builtin/init.lua" + ) + end) + + it("handles default 'truncate' path_display", function() + assert_path({ "truncate" }, new_relpath "lua/telescope/init.lua", new_relpath "…scope/init.lua") + end) + + it("handles 'filename_first' path_display", function() + assert_path("filename_first", new_relpath "init.lua", new_relpath "init.lua") + assert_path("filename_first", new_relpath "lua/telescope/init.lua", new_relpath "init.lua lua/telescope") + end) + + it("handles 'filename_first' path_display with the option to reverse directories", function() + assert_path({ filename_first = { reverse_directories = true } }, new_relpath "init.lua", new_relpath "init.lua") + assert_path( + { filename_first = { reverse_directories = true } }, + new_relpath "lua/telescope/init.lua", + new_relpath "init.lua telescope/lua" + ) + assert_path({ filename_first = { reverse_directories = false } }, new_relpath "init.lua", new_relpath "init.lua") + assert_path( + { filename_first = { reverse_directories = false } }, + new_relpath "lua/telescope/init.lua", + new_relpath "init.lua lua/telescope" + ) + end) + + it("handles function passed to path_display", function() + assert_path(function(_, path) + return string.gsub(path, "^doc", "d") + end, new_relpath "doc/mydoc.md", new_relpath "d/mydoc.md") + end) +end) + +describe("path_tail", function() + local function assert_tails(paths) + for _, path in ipairs(paths) do + it("gets the tail of " .. path, function() + local tail = vim.fn.fnamemodify(path, ":p:t") + eq(tail, utils.path_tail(path)) + end) + end + end + + if jit and jit.os:lower() == "windows" then + describe("handles windows paths", function() + local paths = { + [[C:\Users\username\AppData\Local\nvim-data\log]], + [[D:\Projects\project_folder\source_code.py]], + [[E:\Music\song.mp3]], + [[/home/usuario/documents/archivo.txt]], + [[/var/www/html/index.html]], + [[/mnt/backup/backup_file.tar.gz]], + } + + assert_tails(paths) + end) + elseif jit and jit.os:lower() == "linux" then + describe("handles linux paths", function() + local paths = { + [[/home/usuario/documents/archivo.txt]], + [[/var/www/html/index.html]], + [[/mnt/backup/backup_file.tar.gz]], + } + + assert_tails(paths) + end) + elseif jit and jit.os:lower() == "osx" then + describe("handles macos paths", function() + local paths = { + [[/Users/Usuario/Documents/archivo.txt]], + [[/Applications/App.app/Contents/MacOS/app_executable]], + [[/Volumes/ExternalDrive/Data/file.xlsx]], + } + + assert_tails(paths) + end) + end +end) + +describe("split_lines", function() + local expect = { + "", + "", + "line3 of the file", + "", + "line5 of the file", + "", + "", + "line8 of the file, last line of file", + "", + } + + local function get_fake_file(line_ending) + return table.concat(expect, line_ending) + end + + local newline_file = get_fake_file "\n" + local carriage_newline_file = get_fake_file "\r\n" + + if utils.iswin then + describe("handles files on Windows", function() + it("reads file with newline only", function() + assert.are.same(expect, utils.split_lines(newline_file)) + end) + it("reads file with carriage return and newline", function() + assert.are.same(expect, utils.split_lines(carriage_newline_file)) + end) + end) + else + describe("handles files on non Windows environment", function() + it("reads file with newline only", function() + assert.are.same(expect, utils.split_lines(newline_file)) + end) + end) + end +end) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/fixtures/find_files/file_a.txt b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/fixtures/find_files/file_a.txt new file mode 100644 index 0000000..e69de29 diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/fixtures/find_files/file_abc.txt b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/fixtures/find_files/file_abc.txt new file mode 100644 index 0000000..e69de29 diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/fixtures/live_grep/a.txt b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/fixtures/live_grep/a.txt new file mode 100644 index 0000000..abb3445 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/fixtures/live_grep/a.txt @@ -0,0 +1,14 @@ +abc +abc +abc +abc +abc + + +abcd +abcd +abcd +abcd +abcd + +abcde diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/helpers.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/helpers.lua new file mode 100644 index 0000000..73bd0c8 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/helpers.lua @@ -0,0 +1,87 @@ +local finders = require "telescope.finders" +local make_entry = require "telescope.make_entry" +local previewers = require "telescope.previewers" +local pickers = require "telescope.pickers" +local sorters = require "telescope.sorters" +local utils = require "telescope.utils" + +local helpers = {} + +-- TODO: We should do something with builtins to get those easily. +helpers.auto_find_files = function(opts) + opts = opts or {} + opts.prompt_prefix = "" + + local find_command = opts.find_command + + if not find_command then + if 1 == vim.fn.executable "fd" then + find_command = { "fd", "--type", "f" } + elseif 1 == vim.fn.executable "fdfind" then + find_command = { "fdfind", "--type", "f" } + elseif 1 == vim.fn.executable "rg" then + find_command = { "rg", "--files" } + end + end + + if opts.cwd then + opts.cwd = utils.path_expand(opts.cwd) + end + + opts.entry_maker = opts.entry_maker or make_entry.gen_from_file(opts) + + local p = pickers.new(opts, { + prompt = "Find Files", + finder = finders.new_oneshot_job(find_command, opts), + previewer = previewers.cat.new(opts), + sorter = sorters.get_fuzzy_file(), + + track = true, + }) + + local count = 0 + p:register_completion_callback(function(s) + print( + count, + vim.inspect(s.stats, { + process = function(item) + if type(item) == "string" and item:sub(1, 1) == "_" then + return nil + end + + return item + end, + }) + ) + + count = count + 1 + end) + + local feed = function(text, feed_opts) + feed_opts = feed_opts or "n" + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(text, true, false, true), feed_opts, true) + end + + p:register_completion_callback(coroutine.wrap(function() + local input = opts.input + + for i = 1, #input do + feed(input:sub(i, i)) + coroutine.yield() + end + + vim.wait(300, function() end) + feed("", "") + + vim.defer_fn(function() + PASSED = opts.condition() + COMPLETED = true + end, 500) + + coroutine.yield() + end)) + + p:find() +end + +return helpers diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/pickers/find_files__readme.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/pickers/find_files__readme.lua new file mode 100644 index 0000000..1b76ad6 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/pickers/find_files__readme.lua @@ -0,0 +1,8 @@ +local helper = require "telescope.testharness.helpers" +local runner = require "telescope.testharness.runner" + +runner.picker("find_files", "README.md", { + post_close = { + { "README.md", helper.get_file }, + }, +}) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/pickers/find_files__scrolling_descending_cycle.lua b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/pickers/find_files__scrolling_descending_cycle.lua new file mode 100644 index 0000000..6b3c023 --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/lua/tests/pickers/find_files__scrolling_descending_cycle.lua @@ -0,0 +1,12 @@ +local tester = require "telescope.testharness" +local helper = require "telescope.testharness.helpers" +local runner = require "telescope.testharness.runner" + +runner.picker("find_files", "telescope", { + post_close = { + tester.not_ { "plugin/telescope.vim", helper.get_file }, + }, +}, { + sorting_strategy = "descending", + scroll_strategy = "cycle", +}) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/plugin/telescope.lua b/.config/nvim/pack/vendor/start/telescope.nvim/plugin/telescope.lua new file mode 100644 index 0000000..338036c --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/plugin/telescope.lua @@ -0,0 +1,156 @@ +if 1 ~= vim.fn.has "nvim-0.9.0" then + vim.api.nvim_err_writeln "Telescope.nvim requires at least nvim-0.9.0. See `:h telescope.changelog-2499`" + return +end + +if vim.g.loaded_telescope == 1 then + return +end +vim.g.loaded_telescope = 1 + +local highlights = { + -- Sets the highlight for selected items within the picker. + TelescopeSelection = { default = true, link = "Visual" }, + TelescopeSelectionCaret = { default = true, link = "TelescopeSelection" }, + TelescopeMultiSelection = { default = true, link = "Type" }, + TelescopeMultiIcon = { default = true, link = "Identifier" }, + + -- "Normal" in the floating windows created by telescope. + TelescopeNormal = { default = true, link = "Normal" }, + TelescopePreviewNormal = { default = true, link = "TelescopeNormal" }, + TelescopePromptNormal = { default = true, link = "TelescopeNormal" }, + TelescopeResultsNormal = { default = true, link = "TelescopeNormal" }, + + -- Border highlight groups. + -- Use TelescopeBorder to override the default. + -- Otherwise set them specifically + TelescopeBorder = { default = true, link = "TelescopeNormal" }, + TelescopePromptBorder = { default = true, link = "TelescopeBorder" }, + TelescopeResultsBorder = { default = true, link = "TelescopeBorder" }, + TelescopePreviewBorder = { default = true, link = "TelescopeBorder" }, + + -- Title highlight groups. + -- Use TelescopeTitle to override the default. + -- Otherwise set them specifically + TelescopeTitle = { default = true, link = "TelescopeBorder" }, + TelescopePromptTitle = { default = true, link = "TelescopeTitle" }, + TelescopeResultsTitle = { default = true, link = "TelescopeTitle" }, + TelescopePreviewTitle = { default = true, link = "TelescopeTitle" }, + + TelescopePromptCounter = { default = true, link = "NonText" }, + + -- Used for highlighting characters that you match. + TelescopeMatching = { default = true, link = "Special" }, + + -- Used for the prompt prefix + TelescopePromptPrefix = { default = true, link = "Identifier" }, + + -- Used for highlighting the matched line inside Previewer. Works only for (vim_buffer_ previewer) + TelescopePreviewLine = { default = true, link = "Visual" }, + TelescopePreviewMatch = { default = true, link = "Search" }, + + TelescopePreviewPipe = { default = true, link = "Constant" }, + TelescopePreviewCharDev = { default = true, link = "Constant" }, + TelescopePreviewDirectory = { default = true, link = "Directory" }, + TelescopePreviewBlock = { default = true, link = "Constant" }, + TelescopePreviewLink = { default = true, link = "Special" }, + TelescopePreviewSocket = { default = true, link = "Statement" }, + TelescopePreviewRead = { default = true, link = "Constant" }, + TelescopePreviewWrite = { default = true, link = "Statement" }, + TelescopePreviewExecute = { default = true, link = "String" }, + TelescopePreviewHyphen = { default = true, link = "NonText" }, + TelescopePreviewSticky = { default = true, link = "Keyword" }, + TelescopePreviewSize = { default = true, link = "String" }, + TelescopePreviewUser = { default = true, link = "Constant" }, + TelescopePreviewGroup = { default = true, link = "Constant" }, + TelescopePreviewDate = { default = true, link = "Directory" }, + TelescopePreviewMessage = { default = true, link = "TelescopePreviewNormal" }, + TelescopePreviewMessageFillchar = { default = true, link = "TelescopePreviewMessage" }, + + -- Used for Picker specific Results highlighting + TelescopeResultsClass = { default = true, link = "Function" }, + TelescopeResultsConstant = { default = true, link = "Constant" }, + TelescopeResultsField = { default = true, link = "Function" }, + TelescopeResultsFunction = { default = true, link = "Function" }, + TelescopeResultsMethod = { default = true, link = "Method" }, + TelescopeResultsOperator = { default = true, link = "Operator" }, + TelescopeResultsStruct = { default = true, link = "Struct" }, + TelescopeResultsVariable = { default = true, link = "SpecialChar" }, + + TelescopeResultsLineNr = { default = true, link = "LineNr" }, + TelescopeResultsIdentifier = { default = true, link = "Identifier" }, + TelescopeResultsNumber = { default = true, link = "Number" }, + TelescopeResultsComment = { default = true, link = "Comment" }, + TelescopeResultsSpecialComment = { default = true, link = "SpecialComment" }, + + -- Used for git status Results highlighting + TelescopeResultsDiffChange = { default = true, link = "DiffChange" }, + TelescopeResultsDiffAdd = { default = true, link = "DiffAdd" }, + TelescopeResultsDiffDelete = { default = true, link = "DiffDelete" }, + TelescopeResultsDiffUntracked = { default = true, link = "NonText" }, +} + +for k, v in pairs(highlights) do + vim.api.nvim_set_hl(0, k, v) +end + +-- This is like "" in your terminal. +-- To use it, do `cmap (TelescopeFuzzyCommandSearch) +vim.keymap.set( + "c", + "(TelescopeFuzzyCommandSearch)", + "e \"lua require('telescope.builtin').command_history " + .. '{ default_text = [=[" . escape(getcmdline(), \'"\') . "]=] }"', + { silent = true, noremap = true } +) + +vim.api.nvim_create_user_command("Telescope", function(opts) + require("telescope.command").load_command(unpack(opts.fargs)) +end, { + nargs = "*", + complete = function(_, line) + local builtin_list = vim.tbl_keys(require "telescope.builtin") + local extensions_list = vim.tbl_keys(require("telescope._extensions").manager) + + local l = vim.split(line, "%s+") + local n = #l - 2 + + if n == 0 then + local commands = { builtin_list, extensions_list } + -- TODO(clason): remove when dropping support for Nvim 0.9 + if vim.fn.has "nvim-0.11" == 1 then + commands = vim.iter(commands):flatten():totable() + else + commands = vim.tbl_flatten(commands) + end + table.sort(commands) + + return vim.tbl_filter(function(val) + return vim.startswith(val, l[2]) + end, commands) + end + + if n == 1 then + local is_extension = vim.tbl_filter(function(val) + return val == l[2] + end, extensions_list) + + if #is_extension > 0 then + local extensions_subcommand_dict = require("telescope.command").get_extensions_subcommand() + local commands = extensions_subcommand_dict[l[2]] + table.sort(commands) + + return vim.tbl_filter(function(val) + return vim.startswith(val, l[3]) + end, commands) + end + end + + local options_list = vim.tbl_keys(require("telescope.config").values) + table.sort(options_list) + + return vim.tbl_filter(function(val) + return vim.startswith(val, l[#l]) + end, options_list) + end, +}) diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/scripts/gendocs.lua b/.config/nvim/pack/vendor/start/telescope.nvim/scripts/gendocs.lua new file mode 100644 index 0000000..ec0047c --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/scripts/gendocs.lua @@ -0,0 +1,49 @@ +-- Setup telescope with defaults +if RELOAD then + RELOAD "telescope" +end +require("telescope").setup() + +local docgen = require "docgen" + +local docs = {} + +docs.test = function() + -- TODO: Fix the other files so that we can add them here. + local input_files = { + "./lua/telescope/init.lua", + "./lua/telescope/command.lua", + "./lua/telescope/builtin/init.lua", + "./lua/telescope/themes.lua", + "./lua/telescope/mappings.lua", + "./lua/telescope/pickers/layout.lua", + "./lua/telescope/pickers/layout_strategies.lua", + "./lua/telescope/config/resolve.lua", + "./lua/telescope/make_entry.lua", + "./lua/telescope/pickers/entry_display.lua", + "./lua/telescope/utils.lua", + "./lua/telescope/actions/init.lua", + "./lua/telescope/actions/state.lua", + "./lua/telescope/actions/set.lua", + "./lua/telescope/actions/layout.lua", + "./lua/telescope/actions/utils.lua", + "./lua/telescope/actions/generate.lua", + "./lua/telescope/previewers/init.lua", + "./lua/telescope/actions/history.lua", + } + + local output_file = "./doc/telescope.txt" + local output_file_handle = io.open(output_file, "w") + + for _, input_file in ipairs(input_files) do + docgen.write(input_file, output_file_handle) + end + + output_file_handle:write " vim:tw=78:ts=8:ft=help:norl:\n" + output_file_handle:close() + vim.cmd [[checktime]] +end + +docs.test() + +return docs diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/scripts/minimal_init.vim b/.config/nvim/pack/vendor/start/telescope.nvim/scripts/minimal_init.vim new file mode 100644 index 0000000..6f6731f --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/scripts/minimal_init.vim @@ -0,0 +1,8 @@ +set rtp+=. +set rtp+=../plenary.nvim/ +set rtp+=../tree-sitter-lua/ + +runtime! plugin/plenary.vim +runtime! plugin/telescope.lua + +let g:telescope_test_delay = 100 diff --git a/.config/nvim/pack/vendor/start/telescope.nvim/telescope.nvim-scm-1.rockspec b/.config/nvim/pack/vendor/start/telescope.nvim/telescope.nvim-scm-1.rockspec new file mode 100644 index 0000000..3dc660d --- /dev/null +++ b/.config/nvim/pack/vendor/start/telescope.nvim/telescope.nvim-scm-1.rockspec @@ -0,0 +1,37 @@ +local MODREV, SPECREV = 'scm', '-1' +rockspec_format = '3.0' +package = 'telescope.nvim' +version = MODREV .. SPECREV + +description = { + summary = 'Find, Filter, Preview, Pick. All lua, all the time.', + detailed = [[ + A highly extendable fuzzy finder over lists. + Built on the latest awesome features from neovim core. + Telescope is centered around modularity, allowing for easy customization. + ]], + labels = { 'neovim', 'plugin', }, + homepage = 'https://github.com/nvim-telescope/telescope.nvim', + license = 'MIT', +} + +dependencies = { + 'lua == 5.1', + 'plenary.nvim', +} + +source = { + url = 'git://github.com/nvim-telescope/telescope.nvim', +} + +build = { + type = 'builtin', + copy_directories = { + 'doc', + 'ftplugin', + 'plugin', + 'scripts', + 'autoload', + 'data', + } +} diff --git a/.config/zed/.DS_Store b/.config/zed/.DS_Store new file mode 100644 index 0000000..4d4f329 Binary files /dev/null and b/.config/zed/.DS_Store differ diff --git a/.config/zed/keymap.json b/.config/zed/keymap.json new file mode 100644 index 0000000..d267779 --- /dev/null +++ b/.config/zed/keymap.json @@ -0,0 +1,20 @@ +// Zed keymap +// +// For information on binding keys, see the Zed +// documentation: https://zed.dev/docs/key-bindings +// +// To see the default key bindings run `zed: open default keymap` +// from the command palette. +[ + { + "bindings": { + "f3": "editor::RestartLanguageServer", + "f6": "editor::OrganizeImports", + "f7": "editor::Format", + "f10": "debugger::StepOver", + "cmd-shift-a": "editor::SelectAllMatches", + "cmd-\\": "workspace::CloseAllDocks", + "cmd-|": "workspace::CloseInactiveTabsAndPanes" + } + } +] diff --git a/.config/zed/settings.json b/.config/zed/settings.json new file mode 100644 index 0000000..e6d209b --- /dev/null +++ b/.config/zed/settings.json @@ -0,0 +1,116 @@ +// Zed settings +// +// For information on how to configure Zed, see the Zed +// documentation: https://zed.dev/docs/configuring-zed +// +// To see all of Zed's default settings without changing your +// custom settings, run `zed: open default settings` from the +// command palette (cmd-shift-p / ctrl-shift-p) +{ + // general + "agent": { + "default_profile": "write", + "default_model": { + "provider": "zed.dev", + "model": "claude-sonnet-4" + } + }, + "features": { + "edit_prediction_provider": "zed" + }, + "vim_mode": false, + "confirm_quit": true, + "auto_update": false, + "telemetry": { + "diagnostics": false, + "metrics": false + }, + + "when_closing_with_no_tabs": "keep_window_open", + + // scrollbar + "scrollbar": { + "diagnostics": "none" + }, + + // editor + "show_completions_on_input": false, + "diagnostics_max_severity": "off", + "use_autoclose": false, + "use_auto_surround": false, + "show_edit_predictions": false, + "auto_indent_on_paste": true, + "extend_comment_on_newline": false, + + // appearance + "current_line_highlight": "none", + "selection_highlight": true, + + "theme": { + "mode": "system", + "light": "One Dark", + "dark": "New Darcula" + }, + + "indent_guides": { + "enabled": false + }, + + "cursor_shape": "bar", + "buffer_font_family": "Zed Plex Mono", + "format_on_save": "off", + "buffer_line_height": "standard", + "buffer_font_features": { + "calt": false + }, + + "ui_font_size": 14, + "buffer_font_size": 14, + + "toolbar": { + "quick_actions": false, + "breadcrumbs": false + }, + + // turn off ui crap + "project_panel": { + "show_diagnostics": "off" + }, + + "diagnostics": { + "include_warnings": false, + "inline": { + "enabled": false + } + }, + + "tab_bar": { + "show_nav_history_buttons": false + }, + + // git settings + "git": { + "git_gutter": "hide", + "inline_blame": { + "enabled": false + } + }, + + "gutter": { + "code_actions": false, + "folds": false, + "line_numbers": false, + "runnables": false + }, + + "languages": { + "Go": { + "show_edit_predictions": false, + "enable_language_server": true + }, + "JavaScript": { + "tab_size": 4, + "hard_tabs": true + } + } +} diff --git a/.config/zed/themes/all-hallows-eve.json b/.config/zed/themes/all-hallows-eve.json new file mode 100644 index 0000000..cd25072 --- /dev/null +++ b/.config/zed/themes/all-hallows-eve.json @@ -0,0 +1,290 @@ +{ + "$schema": "https://zed.dev/schema/themes/v0.2.0.json", + "name": "All Hallows' Eve", + "author": "David Heinemeier Hansson (original), adapted for Zed", + "themes": [ + { + "name": "All Hallows' Eve", + "appearance": "dark", + "style": { + "background": "#2a2a2a", + "foreground": "#ffffff", + "accent": "#DE761B", + "border": "#555555", + "border.variant": "#444444", + "border.focused": "#DE761B", + "border.selected": "#DE761B", + "border.transparent": "#00000000", + "border.disabled": "#404040", + "elevated_surface.background": "#333333", + "surface.background": "#2a2a2a", + "element.background": "#333333", + "element.hover": "#3a3a3a", + "element.active": "#4a90e2", + "element.selected": "#4a90e2", + "element.disabled": "#404040", + "drop_target.background": "#DE761B80", + "ghost_element.background": "#00000000", + "ghost_element.hover": "#3a3a3a80", + "ghost_element.active": "#4a90e280", + "ghost_element.selected": "#4a90e280", + "ghost_element.disabled": "#40404080", + "text": "#ffffff", + "text.muted": "#cccccc", + "text.placeholder": "#aaaaaa", + "text.disabled": "#666666", + "text.accent": "#DE761B", + "icon": "#ffffff", + "icon.muted": "#cccccc", + "icon.disabled": "#666666", + "icon.placeholder": "#aaaaaa", + "icon.accent": "#DE761B", + "status_bar.background": "#2a2a2a", + "title_bar.background": "#2a2a2a", + "title_bar.inactive_background": "#242424", + "toolbar.background": "#2a2a2a", + "tab_bar.background": "#2a2a2a", + "tab.inactive_background": "#242424", + "tab.active_background": "#333333", + "search.match_background": "#DE761B80", + "editor.selection.background": "#4a90e260", + "panel.background": "#2a2a2a", + "panel.focused_border": "#DE761B", + "pane.focused_border": "#DE761B", + "scrollbar.thumb.background": "#555555", + "scrollbar.thumb.hover_background": "#666666", + "scrollbar.thumb.border": "#444444", + "scrollbar.track.background": "#2a2a2a", + "scrollbar.track.border": "#444444", + "editor.background": "#000000", + "editor.gutter.background": "#2a2a2a", + "editor.subheader.background": "#181818", + "editor.active_line.background": "#222222", + "editor.highlighted_line.background": "#222222", + "editor.line_number": "#666666", + "editor.active_line_number": "#DE761B", + "editor.invisible": "#404040", + "editor.wrap_guide": "#333333", + "editor.active_wrap_guide": "#666666", + "editor.document_highlight.read_background": "#4a90e240", + "editor.document_highlight.write_background": "#DE761B40", + "terminal.background": "#000000", + "terminal.foreground": "#ffffff", + "terminal.bright_foreground": "#ffffff", + "terminal.dim_foreground": "#cccccc", + "terminal.ansi.black": "#000000", + "terminal.ansi.bright_black": "#666666", + "terminal.ansi.dim_black": "#333333", + "terminal.ansi.red": "#DE761B", + "terminal.ansi.bright_red": "#ff9966", + "terminal.ansi.dim_red": "#994422", + "terminal.ansi.green": "#66cc33", + "terminal.ansi.bright_green": "#99ff66", + "terminal.ansi.dim_green": "#449922", + "terminal.ansi.yellow": "#cccc33", + "terminal.ansi.bright_yellow": "#ffff66", + "terminal.ansi.dim_yellow": "#999922", + "terminal.ansi.blue": "#3387cc", + "terminal.ansi.bright_blue": "#66aaff", + "terminal.ansi.dim_blue": "#225599", + "terminal.ansi.magenta": "#9933cc", + "terminal.ansi.bright_magenta": "#cc66ff", + "terminal.ansi.dim_magenta": "#662299", + "terminal.ansi.cyan": "#66cccc", + "terminal.ansi.bright_cyan": "#99ffff", + "terminal.ansi.dim_cyan": "#449999", + "terminal.ansi.white": "#ffffff", + "terminal.ansi.bright_white": "#ffffff", + "terminal.ansi.dim_white": "#cccccc", + "link_text.hover": "#66aaff", + "conflict": "#DE761B", + "conflict.background": "#DE761B20", + "conflict.border": "#DE761B", + "created": "#66cc33", + "created.background": "#66cc3320", + "created.border": "#66cc33", + "deleted": "#ff6666", + "deleted.background": "#ff666620", + "deleted.border": "#ff6666", + "error": "#ff6666", + "error.background": "#ff666620", + "error.border": "#ff6666", + "hidden": "#666666", + "hidden.background": "#66666620", + "hidden.border": "#666666", + "hint": "#cccc33", + "hint.background": "#cccc3320", + "hint.border": "#cccc33", + "ignored": "#666666", + "ignored.background": "#66666620", + "ignored.border": "#666666", + "info": "#3387cc", + "info.background": "#3387cc20", + "info.border": "#3387cc", + "modified": "#cccc33", + "modified.background": "#cccc3320", + "modified.border": "#cccc33", + "predictive": "#9933cc", + "predictive.background": "#9933cc20", + "predictive.border": "#9933cc", + "renamed": "#3387cc", + "renamed.background": "#3387cc20", + "renamed.border": "#3387cc", + "success": "#66cc33", + "success.background": "#66cc3320", + "success.border": "#66cc33", + "unreachable": "#666666", + "unreachable.background": "#66666620", + "unreachable.border": "#666666", + "warning": "#cccc33", + "warning.background": "#cccc3320", + "warning.border": "#cccc33", + "separator": "#66cc33", + "separator.background": "#66cc3320", + "list.separator": "#66cc33", + "list.separator.background": "#66cc3320", + "players": [ + { + "cursor": "#FF00FF", + "background": "#4a90e240", + "selection": "#4a90e240" + }, + { + "cursor": "#ffffff", + "background": "#4a90e240", + "selection": "#4a90e240" + }, + { + "cursor": "#ffffff", + "background": "#4a90e240", + "selection": "#4a90e240" + }, + { + "cursor": "#ffffff", + "background": "#4a90e240", + "selection": "#4a90e240" + } + ], + "syntax": { + "attribute": { + "color": "#DE761B" + }, + "boolean": { + "color": "#3387cc" + }, + "comment": { + "color": "#FFFF00" + }, + "comment.doc": { + "color": "#FFFF00" + }, + "constant": { + "color": "#3387cc" + }, + "constructor": { + "color": "#DE761B" + }, + "embedded": { + "color": "#FBD598" + }, + "emphasis": { + "color": "#FBD598" + }, + "emphasis.strong": { + "color": "#FBD598" + }, + "enum": { + "color": "#DE761B" + }, + "function": { + "color": "#FBD598" + }, + "hint": { + "color": "#cccc33" + }, + "keyword": { + "color": "#DE761B" + }, + "label": { + "color": "#DE761B" + }, + "link_text": { + "color": "#66cc33" + }, + "link_uri": { + "color": "#3387cc" + }, + "number": { + "color": "#3387cc" + }, + "operator": { + "color": "#FBD598" + }, + "predictive": { + "color": "#9933cc" + }, + "preproc": { + "color": "#d0d0ff" + }, + "primary": { + "color": "#FBD598" + }, + "property": { + "color": "#DE761B" + }, + "punctuation": { + "color": "#FBD598" + }, + "punctuation.bracket": { + "color": "#FBD598" + }, + "punctuation.delimiter": { + "color": "#FBD598" + }, + "punctuation.list_marker": { + "color": "#DE761B" + }, + "punctuation.special": { + "color": "#DE761B" + }, + "string": { + "color": "#1FBA17" + }, + "string.escape": { + "color": "#aaaaaa" + }, + "string.regex": { + "color": "#cccc33" + }, + "string.special": { + "color": "#cccc33" + }, + "string.special.symbol": { + "color": "#3387cc" + }, + "tag": { + "color": "#DE761B" + }, + "text.literal": { + "color": "#66cc33" + }, + "title": { + "color": "#DE761B" + }, + "type": { + "color": "#DE761B" + }, + "variable": { + "color": "#FBD598" + }, + "variable.special": { + "color": "#DE761B" + }, + "variant": { + "color": "#DE761B" + } + } + } + } + ] +} diff --git a/.emacs.d/.DS_Store b/.emacs.d/.DS_Store new file mode 100644 index 0000000..9616c0e Binary files /dev/null and b/.emacs.d/.DS_Store differ diff --git a/.emacs.d/custom.el b/.emacs.d/custom.el new file mode 100755 index 0000000..50e28f8 --- /dev/null +++ b/.emacs.d/custom.el @@ -0,0 +1,13 @@ +(custom-set-variables + ;; custom-set-variables was added by Custom. + ;; If you edit it by hand, you could mess it up, so be careful. + ;; Your init file should contain only one such instance. + ;; If there is more than one, they won't work right. + '(custom-safe-themes + '("5e38acfff91084e4c8174e272d2f8b84808884b4f49c62145b48810ec1766b5e" "eb085d94b1f7bfa54c7003bf7edb40eab1133d90cfebcf1d11be5959635a526f" default))) +(custom-set-faces + ;; custom-set-faces was added by Custom. + ;; If you edit it by hand, you could mess it up, so be careful. + ;; Your init file should contain only one such instance. + ;; If there is more than one, they won't work right. + ) diff --git a/.emacs.d/init.el b/.emacs.d/init.el new file mode 100755 index 0000000..ae02b91 --- /dev/null +++ b/.emacs.d/init.el @@ -0,0 +1,251 @@ +;; Max's init.el -*- lexical-binding: t; -*- + +;; General Configuration + +;; set load paths for lisp evaluation +(add-to-list 'load-path "~/.emacs.d/lisp/") + +(let ((default-directory "~/.emacs.d/lisp/")) + (normal-top-level-add-subdirs-to-load-path)) + + (setq indent-tabs-mode t) + (stupid-indent-mode) + (setq-local stupid-indent-level 4) +)) + +;; general settings +(setq-default inhibit-startup-screen t) +(show-paren-mode 1) +(delete-selection-mode 1) +(setq cua-auto-tabify-rectangles nil) ;; Don't tabify after rectangle commands +(transient-mark-mode 1) ;; No region when it is not highlighted +(setq cua-keep-region-after-copy nil) ;; don't reselect after copy +(menu-bar-mode -1) +(scroll-bar-mode -1) +(tool-bar-mode -1) +(global-auto-revert-mode t) +(electric-pair-mode -1) +(setq ns-pop-up-frames nil) +(setq initial-major-mode 'text-mode) +(setq initial-scratch-message "Just give him some food, water, and a funny hat." ) +(setq ediff-split-window-function 'split-window-horizontally) +(setq-default truncate-lines 1) +(setq column-number-mode t) +(setq dired-dnd-protocol-alist nil) +(global-so-long-mode 1) +(column-number-mode 1) +(blink-cursor-mode 0) +(setq use-dialog-box nil) +(electric-indent-mode 0) +(tooltip-mode -1) +(setq-default select-enable-clipboard nil) +(setq-default x-select-enable-clipboard nil) +(setq-default backward-delete-char-untabify-method nil) +(setq ring-bell-function 'ignore) +(setq custom-file "~/.emacs.d/custom.el") ;; place custom in a separate file +(setq-default require-final-newline t) +(cua-mode t) + +;; backup and autosave settings +(setq backup-by-copying t ; don't clobber symlinks + backup-directory-alist '(("." . "~/.emacs.d/saves/")) ; don't litter my fs tree + delete-old-versions t + kept-new-versions 6 + kept-old-versions 2 + version-control t) ; use versioned backups +(setq auto-save-file-name-transforms + `((".*" "~/.emacs.d/saves/" t))) +(setq create-lockfiles nil) + +;; Keybindings / Keybinds +;; global +(global-set-key (kbd "S-") #'my-mouse-start-rectangle) +(global-set-key (kbd "") 'revert-buffer-quick) +(global-set-key (kbd "") 'my-file-manager-command) +(global-set-key (kbd "") 'my-terminal-emulator-command) +(global-set-key [f8] 'goto-line) +(global-set-key (kbd "C-\\") 'my-transpose-windows) +(global-unset-key (kbd "C-x C-SPC")) +(global-set-key (kbd "C-x C-SPC") 'rectangle-mark-mode) +(global-set-key [C-return] 'save-buffer) +(global-set-key [?\C-z] 'undo) +(global-set-key (kbd "C-*") 'search-current-word) +(global-set-key (kbd "C-;") 'comment-line) +(global-set-key (kbd "M-") 'save-buffers-kill-terminal) ;; windows thing +(global-set-key (kbd "C-y") 'clipboard-yank) ;; fix killring messing with system clipboard +(global-set-key (kbd "C-w") 'clipboard-kill-region) +(global-set-key (kbd "M-w") 'clipboard-kill-ring-save) +(global-set-key (kbd "M-j") 'my-duplicate-line) +(global-set-key (kbd "") 'bookmark-jump) +(global-set-key (kbd "") 'bookmark-set) +(global-set-key (kbd "") 'bookmark-delete) + +;; custom bind minor mode +;; this allows binding keys that override all other modes +(defvar my-keys-minor-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "M-p") 'backward-paragraph) + (define-key map (kbd "M-n") 'forward-paragraph) + (define-key map (kbd "C-o") 'next-multiframe-window) + (define-key map [?\C-0] 'delete-window) + (define-key map [?\C-1] 'delete-other-windows) + (define-key map [?\C-2] 'split-window-vertically) + (define-key map [?\C-3] 'split-window-horizontally) + (define-key map [?\C-4] 'find-file-at-point) + (define-key map [?\C-5] 'switch-to-buffer) + (define-key map (kbd "C-j") 'dabbrev-expand) + map) + "my-keys-minor-mode keymap.") + +(define-minor-mode my-keys-minor-mode + "A minor mode so that my key settings override annoying major modes." + :init-value t + :lighter "") + +;; don't enable override keymap in minibuf +(defun my-minibuffer-setup-hook () + (my-keys-minor-mode 0)) +(add-hook 'minibuffer-setup-hook 'my-minibuffer-setup-hook) + +(my-keys-minor-mode 1) + +;; Custom Functions + +;; test function +(defun my-test () + (interactive) + (message "Hello world!")) + +;; duplicate line (cleanly) +;; https://stackoverflow.com/questions/88399/how-do-i-duplicate-a-whole-line-in-emacs +(defun my-duplicate-line (arg) + "Duplicate current line, leaving point in lower line." + (interactive "*p") + (setq buffer-undo-list (cons (point) buffer-undo-list)) + (let ((bol (save-excursion (beginning-of-line) (point))) + eol) + (save-excursion + (end-of-line) + (setq eol (point)) + (let ((line (buffer-substring bol eol)) + (buffer-undo-list t) + (count arg)) + (while (> count 0) + (newline) + (insert line) + (setq count (1- count))) + ) + (setq buffer-undo-list (cons (cons eol (point)) buffer-undo-list))) + ) + (next-line arg)) + +;; select rectangle with shift+mouse +(defun my-mouse-start-rectangle (start-event) + (interactive "e") + (deactivate-mark) + (mouse-set-point start-event) + (rectangle-mark-mode +1) + (let ((drag-event)) + (track-mouse + (while (progn + (setq drag-event (read-event)) + (mouse-movement-p drag-event)) + (mouse-set-point drag-event))))) + +;; open file manager +(defun my-file-manager-command () + (interactive) + (cond ((eq system-type 'windows-nt) + (shell-command "explorer.exe .")) + ((eq system-type 'gnu/linux) + (shell-command "setsid -f nautilus . >/dev/null 2>&1")))) + +;; open terminal or cmd prompt +(defun my-terminal-emulator-command () + (interactive) + (cond ((eq system-type 'windows-nt) + (let ((proc (start-process "cmd" nil "cmd.exe" "/C" "start" "cmd.exe"))) + (set-process-query-on-exit-flag proc nil))) + ((eq system-type 'gnu/linux) + (shell-command "setsid -f gnome-terminal . >/dev/null 2>&1")))) + +;; transpose (move) windows +(defun my-transpose-windows (arg) + "Transpose the buffers shown in two windows." + (interactive "p") + (let ((selector (if (>= arg 0) 'next-window 'previous-window))) + (while (/= arg 0) + (let ((this-win (window-buffer)) + (next-win (window-buffer (funcall selector)))) + (set-window-buffer (selected-window) next-win) + (set-window-buffer (funcall selector) this-win) + (select-window (funcall selector))) + (setq arg (if (plusp arg) (1- arg) (1+ arg)))))) + +;; search current word +(defun search-current-word () + (interactive) + (let ($p1 $p2) + (if (region-active-p) + (setq $p1 (region-beginning) $p2 (region-end)) + (save-excursion + + (skip-chars-backward "-_A-Za-z0-9") + (setq $p1 (point)) + (right-char) + (skip-chars-forward "-_A-Za-z0-9") + (setq $p2 (point)))) + (setq mark-active nil) + (when (< $p1 (point)) + (goto-char $p1)) + (isearch-mode t) + (isearch-yank-string (buffer-substring-no-properties $p1 $p2)))) + +;; Tweaks / Fixes +;; Emacs sucks by default, here are some fixes. + +;; fix isearch +(defadvice isearch-search (after isearch-no-fail activate) + (unless isearch-success + (ad-disable-advice 'isearch-search 'after 'isearch-no-fail) + (ad-activate 'isearch-search) + (isearch-repeat (if isearch-forward 'forward)) + (ad-enable-advice 'isearch-search 'after 'isearch-no-fail) + (ad-activate 'isearch-search))) + +(add-hook 'isearch-mode-end-hook + #'endless/goto-match-beginning) + +(defun endless/goto-match-beginning () + "Go to the start of current isearch match. +Use in `isearch-mode-end-hook'." + (when (and isearch-forward + (number-or-marker-p isearch-other-end) + (not mark-active) + (not isearch-mode-end-hook-quit)) + (goto-char isearch-other-end))) + +;; appearance +;; (set-face-attribute 'default nil :font "Consolas-15") + +;; the name of my emacs color scheme +(custom-set-faces + '(default ((t (:foreground "#d3b58d" :background "#181E2C")))) + '(custom-group-tag-face ((t (:underline t :foreground "lightblue"))) t) + '(custom-variable-tag-face ((t (:underline t :foreground "lightblue"))) t) + '(font-lock-builtin-face ((t nil))) + '(font-lock-comment-face ((t (:foreground "#bf9319")))) + '(font-lock-function-name-face ((((class color) (background dark)) (:foreground "white")))) + '(font-lock-keyword-face ((t (:foreground "white" )))) + '(font-lock-string-face ((t (:foreground "#8fcddb")))) + '(font-lock-variable-name-face ((((class color) (background dark)) (:foreground "#c8d4ec")))) + '(font-lock-warning-face ((t (:foreground "#504038")))) + '(highlight ((t (:foreground "navyblue" :background "darkseagreen2")))) + '(mode-line ((t (:inverse-video t)))) + '(region ((t (:background "blue")))) + '(widget-field-face ((t (:foreground "white"))) t) + '(widget-single-line-field-face ((t (:background "darkgray"))) t)) + +(set-background-color "#181E2C") +(global-font-lock-mode 1) +(set-cursor-color "lightgreen") diff --git a/.emacs.d/lisp/go-mode.el b/.emacs.d/lisp/go-mode.el new file mode 100755 index 0000000..6003f71 --- /dev/null +++ b/.emacs.d/lisp/go-mode.el @@ -0,0 +1,3056 @@ +;;; go-mode.el --- Major mode for the Go programming language + +;;; Commentary: + +;; Copyright 2013 The go-mode Authors. All rights reserved. +;; Use of this source code is governed by a BSD-style +;; license that can be found in the LICENSE file. + +;; Author: The go-mode Authors +;; Version: 1.6.0 +;; Keywords: languages go +;; Package-Requires: ((emacs "26.1")) +;; URL: https://github.com/dominikh/go-mode.el +;; +;; This file is not part of GNU Emacs. + +;;; Code: + +(require 'cl-lib) +(require 'compile) +(require 'etags) +(require 'ffap) +(require 'find-file) +(require 'ring) +(require 'url) +(require 'xref) + + +(eval-when-compile + (defmacro go--forward-word (&optional arg) + (if (fboundp 'forward-word-strictly) + `(forward-word-strictly ,arg) + `(forward-word ,arg)))) + +(defun go--delete-whole-line (&optional arg) + "Delete the current line without putting it in the `kill-ring'. +Derived from function `kill-whole-line'. ARG is defined as for that +function." + (setq arg (or arg 1)) + (if (and (> arg 0) + (eobp) + (save-excursion (forward-visible-line 0) (eobp))) + (signal 'end-of-buffer nil)) + (if (and (< arg 0) + (bobp) + (save-excursion (end-of-visible-line) (bobp))) + (signal 'beginning-of-buffer nil)) + (cond ((zerop arg) + (delete-region (progn (forward-visible-line 0) (point)) + (progn (end-of-visible-line) (point)))) + ((< arg 0) + (delete-region (progn (end-of-visible-line) (point)) + (progn (forward-visible-line (1+ arg)) + (unless (bobp) + (backward-char)) + (point)))) + (t + (delete-region (progn (forward-visible-line 0) (point)) + (progn (forward-visible-line arg) (point)))))) + +(defun go-goto-opening-parenthesis (&optional _legacy-unused) + "Move up one level of parentheses. + +Return non-nil if there was a paren to move up to." + ;; The old implementation of go-goto-opening-parenthesis had an + ;; optional argument to speed up the function. It didn't change the + ;; function's outcome. + + ;; Silently fail if there's no matching opening parenthesis. + (let ((open-char (nth 1 (syntax-ppss)))) + (when open-char + (goto-char open-char)))) + + +(defconst go-dangling-operators-regexp "[^-]-\\|[^+]\\+\\|[/*&><.=|^]") +(defconst go--max-dangling-operator-length 2 + "The maximum length of dangling operators. +This must be at least the length of the longest string matched by +‘go-dangling-operators-regexp’ and must be updated whenever that +constant is changed.") + +(defconst go-identifier-regexp "[[:word:][:multibyte:]]+") +(defconst go-type-name-no-prefix-regexp "\\(?:[[:word:][:multibyte:]]+\\.\\)?[[:word:][:multibyte:]]+") +(defconst go-qualified-identifier-regexp (concat go-identifier-regexp "\\." go-identifier-regexp)) +(defconst go-label-regexp go-identifier-regexp) +(defconst go-type-regexp "[[:word:][:multibyte:]*]+") +(defconst go-func-regexp (concat "\\_\\s *\\(" go-identifier-regexp "\\)")) +(defconst go-func-meth-regexp (concat + "\\_\\s *\\(?:(\\s *" + "\\(" go-identifier-regexp "\\s +\\)?" go-type-regexp + "\\s *)\\s *\\)?\\(" + go-identifier-regexp + "\\)(")) + +(defconst go--comment-start-regexp "[[:space:]]*\\(?:/[/*]\\)") +(defconst go--case-regexp "\\([[:space:]]*case\\([[:space:]]\\|$\\)\\)") +(defconst go--case-or-default-regexp (concat "\\(" go--case-regexp "\\|" "[[:space:]]*default:\\)")) + +(defconst go-builtins + '("append" "cap" "clear" "close" "complex" + "copy" "delete" "imag" "len" "make" + "max" "min" "new" "panic" "print" + "println" "real" "recover") + "All built-in functions in the Go language. Used for font locking.") + +(defconst go-mode-keywords + '("break" "default" "func" "interface" "select" + "case" "defer" "go" "map" "struct" + "chan" "else" "goto" "package" "switch" + "const" "fallthrough" "if" "range" "type" + "continue" "for" "import" "return" "var") + "All keywords in the Go language. Used for font locking.") + +(defconst go-constants '("nil" "true" "false" "iota")) +(defconst go-type-name-regexp (concat "\\**\\(\\(?:" go-identifier-regexp "\\.\\)?" go-identifier-regexp "\\)")) + +(defvar go-dangling-cache) +(defvar go-godoc-history nil) +(defvar go--coverage-current-file-name) + +(defgroup go nil + "Major mode for editing Go code." + :link '(url-link "https://github.com/dominikh/go-mode.el") + :group 'languages) + +(defgroup go-cover nil + "Options specific to `cover`." + :group 'go) + +(defgroup godoc nil + "Options specific to `godoc'." + :group 'go) + +(defcustom go-fontify-function-calls t + "Fontify function and method calls if this is non-nil." + :type 'boolean + :group 'go) + +(defcustom go-fontify-variables t + "Fontify variable declarations if this is non-nil." + :type 'boolean + :group 'go) + +(defcustom go-mode-hook nil + "Hook called by `go-mode'." + :type 'hook + :group 'go) + +(defcustom go-command "go" + "The 'go' command. +Some users have multiple Go development trees and invoke the 'go' +tool via a wrapper that sets GOROOT and GOPATH based on the +current directory. Such users should customize this variable to +point to the wrapper script." + :type 'string + :group 'go) + +(defcustom gofmt-command "gofmt" + "The 'gofmt' command. +Some users may replace this with 'goimports' +from https://golang.org/x/tools/cmd/goimports." + :type 'string + :group 'go) + +(defcustom gofmt-args nil + "Additional arguments to pass to gofmt." + :type '(repeat string) + :group 'go) + +(defcustom gofmt-show-errors 'buffer + "Where to display gofmt error output. +It can either be displayed in its own buffer, in the echo area, or not at all. + +Please note that Emacs outputs to the echo area when writing +files and will overwrite gofmt's echo output if used from inside +a `before-save-hook'." + :type '(choice + (const :tag "Own buffer" buffer) + (const :tag "Echo area" echo) + (const :tag "None" nil)) + :group 'go) + +(defcustom godef-command "godef" + "The 'godef' command." + :type 'string + :group 'go) + +(defcustom go-other-file-alist + '(("_test\\.go\\'" (".go")) + ("\\.go\\'" ("_test.go"))) + "See the documentation of `ff-other-file-alist' for details." + :type '(repeat (list regexp (choice (repeat string) function))) + :group 'go) + +(defcustom go-packages-function 'go-packages-go-list + "Function called by `go-packages' to determine the list of available packages. +This is used in e.g. tab completion in `go-import-add'. + +This package provides two functions: `go-packages-go-list' uses +'go list all' to determine all Go packages. `go-packages-native' uses +elisp to find all .a files in all /pkg/ directories. +`go-packages-native' is obsolete as it doesn't behave correctly with +the Go build cache or Go modules." + :type 'function + :package-version '(go-mode . 1.4.0) + :group 'go) + +(defcustom go-guess-gopath-functions (list #'go-plain-gopath) + "Functions to call in sequence to detect a project's GOPATH. + +The functions in this list will be called one after another, +until a function returns non-nil. The order of the functions in +this list is important, as some project layouts may superficially +look like others." + :type '(repeat function) + :group 'go) + +(make-obsolete-variable 'go-guess-gopath-functions "GOPATH has been deprecated in favour of Go modules." "1.7.0") + +(defcustom go-confirm-playground-uploads t + "Ask before uploading code to the public Go Playground. + +Set this to nil to upload without prompting." + :type 'boolean + :group 'go) + +(defcustom godoc-command "go doc" + "Which executable to use for `godoc'. +This can be either an absolute path or an executable in PATH." + :type 'string + :group 'go) + +(defcustom godoc-and-godef-command "go doc" + "Which executable to use for `godoc-and-godef'. +This can be either an absolute path or an executable in PATH." + :type 'string + :group 'go) + +(defcustom godoc-use-completing-read nil + "Provide auto-completion for godoc. +Only really desirable when using `godoc' instead of `go doc'." + :type 'boolean + :group 'godoc) + +(defcustom godoc-reuse-buffer nil + "Reuse a single *godoc* buffer to display godoc-at-point calls. +The default behavior is to open a separate buffer for each call." + :type 'boolean + :group 'godoc) + +(defcustom godoc-at-point-function #'godoc-and-godef + "Function to call to display the documentation for an +identifier at a given position. + +This package provides two functions: `godoc-and-godef' uses a +combination of godef and godoc to find the documentation. This +approach has several caveats. See its documentation for more +information. The second function, `godoc-gogetdoc' uses an +additional tool that correctly determines the documentation for +any identifier. It provides better results than +`godoc-and-godef'." + :type 'function + :group 'godoc) + +(defun godoc-and-godef (point) + "Use a combination of godef and godoc to guess the documentation at POINT. + +Due to a limitation in godoc, it is not possible to differentiate +between functions and methods, which may cause `godoc-at-point' +to display more documentation than desired. Furthermore, it +doesn't work on package names or variables. + +Consider using ‘godoc-gogetdoc’ instead for more accurate results." + (condition-case nil + (let* ((output (godef--call point)) + (file (car output)) + (name-parts (split-string (cadr output) " ")) + (first (car name-parts))) + (if (not (godef--successful-p file)) + (message "%s" (godef--error file)) + (go--godoc (format "%s %s" + (file-name-directory file) + (if (or (string= first "type") (string= first "const")) + (cadr name-parts) + (car name-parts))) + godoc-and-godef-command))) + (file-error (message "Could not run godef binary")))) + +(defun godoc-gogetdoc (point) + "Use the gogetdoc tool to find the documentation for an identifier at POINT. + +You can install gogetdoc with 'go get -u github.com/zmb3/gogetdoc'." + (if (not (buffer-file-name (go--coverage-origin-buffer))) + ;; TODO: gogetdoc supports unsaved files, but not introducing + ;; new artificial files, so this limitation will stay for now. + (error "Cannot use gogetdoc on a buffer without a file name")) + (let ((posn (format "%s:#%d" (file-truename buffer-file-name) (1- (position-bytes point)))) + (out (godoc--get-buffer ""))) + (with-temp-buffer + (go--insert-modified-files) + (call-process-region (point-min) (point-max) "gogetdoc" nil out nil + "-modified" + (format "-pos=%s" posn))) + (with-current-buffer out + (goto-char (point-min)) + (godoc-mode) + (display-buffer (current-buffer) t)))) + +(defun go--kill-new-message (url) + "Make URL the latest kill and print a message." + (kill-new url) + (message "%s" url)) + +(defcustom go-play-browse-function 'go--kill-new-message + "Function to call with the Playground URL. +See `go-play-region' for more details." + :type '(choice + (const :tag "Nothing" nil) + (const :tag "Kill + Message" go--kill-new-message) + (const :tag "Browse URL" browse-url) + (function :tag "Call function")) + :group 'go) + +(defcustom go-coverage-display-buffer-func 'display-buffer-reuse-window + "How `go-coverage' should display the coverage buffer. +See `display-buffer' for a list of possible functions." + :type 'function + :group 'go-cover) + +(defface go-coverage-untracked + '((t (:foreground "#505050"))) + "Coverage color of untracked code." + :group 'go-cover) + +(defface go-coverage-0 + '((t (:foreground "#c00000"))) + "Coverage color for uncovered code." + :group 'go-cover) +(defface go-coverage-1 + '((t (:foreground "#808080"))) + "Coverage color for covered code with weight 1." + :group 'go-cover) +(defface go-coverage-2 + '((t (:foreground "#748c83"))) + "Coverage color for covered code with weight 2." + :group 'go-cover) +(defface go-coverage-3 + '((t (:foreground "#689886"))) + "Coverage color for covered code with weight 3." + :group 'go-cover) +(defface go-coverage-4 + '((t (:foreground "#5ca489"))) + "Coverage color for covered code with weight 4." + :group 'go-cover) +(defface go-coverage-5 + '((t (:foreground "#50b08c"))) + "Coverage color for covered code with weight 5." + :group 'go-cover) +(defface go-coverage-6 + '((t (:foreground "#44bc8f"))) + "Coverage color for covered code with weight 6." + :group 'go-cover) +(defface go-coverage-7 + '((t (:foreground "#38c892"))) + "Coverage color for covered code with weight 7." + :group 'go-cover) +(defface go-coverage-8 + '((t (:foreground "#2cd495"))) + "Coverage color for covered code with weight 8. +For mode=set, all covered lines will have this weight." + :group 'go-cover) +(defface go-coverage-9 + '((t (:foreground "#20e098"))) + "Coverage color for covered code with weight 9." + :group 'go-cover) +(defface go-coverage-10 + '((t (:foreground "#14ec9b"))) + "Coverage color for covered code with weight 10." + :group 'go-cover) +(defface go-coverage-covered + '((t (:foreground "#2cd495"))) + "Coverage color of covered code." + :group 'go-cover) + +(defvar go-mode-syntax-table + (let ((st (make-syntax-table))) + (modify-syntax-entry ?+ "." st) + (modify-syntax-entry ?- "." st) + (modify-syntax-entry ?% "." st) + (modify-syntax-entry ?& "." st) + (modify-syntax-entry ?| "." st) + (modify-syntax-entry ?^ "." st) + (modify-syntax-entry ?! "." st) + (modify-syntax-entry ?= "." st) + (modify-syntax-entry ?< "." st) + (modify-syntax-entry ?> "." st) + (modify-syntax-entry ?/ ". 124b" st) + (modify-syntax-entry ?* ". 23" st) + (modify-syntax-entry ?\n "> b" st) + (modify-syntax-entry ?\" "\"" st) + (modify-syntax-entry ?\' "\"" st) + (modify-syntax-entry ?` "\"" st) + (modify-syntax-entry ?\\ "\\" st) + ;; TODO make _ a symbol constituent now that xemacs is gone + (modify-syntax-entry ?_ "w" st) + + st) + "Syntax table for Go mode.") + +(defun go--fontify-type-switch-case-pre () + "Move point to line following the end of case statement. + +This is used as an anchored font lock keyword PRE-MATCH-FORM. We +expand the font lock region to include multiline type switch case +statements." + (save-excursion + (beginning-of-line) + (while (or (looking-at "[[:space:]]*\\($\\|//\\)") (go--line-suffix-p ",")) + (forward-line)) + (when (go--line-suffix-p ":") + (forward-line)) + (point))) + +(defun go--build-font-lock-keywords () + ;; we cannot use 'symbols in regexp-opt because GNU Emacs <24 + ;; doesn't understand that + (append + `( + ;; Match param lists in func signatures. This uses the + ;; MATCH-ANCHORED format (see `font-lock-keywords' docs). + ;; + ;; Parent/anchor match. It matches the param list opening "(". + (go--match-param-start + ;; Sub-matcher that matches individual params in the param list. + (go--fontify-param + ;; Pre-match form that runs before the first sub-match. + (go--fontify-param-pre) + ;; Post-match form that runs after last sub-match. + (go--fontify-param-post) + ;; Subexp 1 is the param variable name, if any. + (1 font-lock-variable-name-face nil t) + ;; Subexp 2 is the param type name, if any. We set the LAXMATCH + ;; flag to allow optional regex groups. + (2 font-lock-type-face nil t))) + + ;; Special case to match non-parenthesized function results. For + ;; example, "func(i int) string". + (go--match-single-func-result 1 font-lock-type-face) + + ;; Match name+type pairs, such as "foo bar" in "var foo bar". + (go--match-ident-type-pair 2 font-lock-type-face) + + ;; An anchored matcher for type switch case clauses. + (go--match-type-switch-case + (go--fontify-type-switch-case + (go--fontify-type-switch-case-pre) + nil + (1 font-lock-type-face))) + + ;; Match variable names in var decls, constant names in const + ;; decls, and type names in type decls. + (go--match-decl + (1 font-lock-variable-name-face nil t) + (2 font-lock-constant-face nil t) + (3 font-lock-type-face nil t)) + + (,(concat "\\_<" (regexp-opt go-mode-keywords t) "\\_>") . font-lock-keyword-face) + (,(concat "\\(\\_<" (regexp-opt go-builtins t) "\\_>\\)[[:space:]]*(") 1 font-lock-builtin-face) + (,(concat "\\_<" (regexp-opt go-constants t) "\\_>") . font-lock-constant-face) + + ;; Function (not method) name + (,go-func-regexp 1 font-lock-function-name-face)) + + (if go-fontify-function-calls + ;; Function call/method name + `((,(concat "\\(" go-identifier-regexp "\\)[[:space:]]*(") 1 font-lock-function-name-face) + ;; Bracketed function call + (,(concat "[^[:word:][:multibyte:]](\\(" go-identifier-regexp "\\))[[:space:]]*(") 1 font-lock-function-name-face)) + ;; Method name + `((,go-func-meth-regexp 2 font-lock-function-name-face))) + + `( + ;; Raw string literal, needed for font-lock-syntactic-keywords + ("\\(`[^`]*`\\)" 1 font-lock-multiline) + + ;; RHS of type alias. + (go--match-type-alias 2 font-lock-type-face) + + ;; Arrays/slices: [] | [123] | [some.Const] | [someConst] | [...] + (,(concat "\\(?:^\\|[^[:word:][:multibyte:]]\\)\\[\\(?:[[:digit:]]+\\|" go-qualified-identifier-regexp "\\|" go-identifier-regexp "\\|\\.\\.\\.\\)?\\]" go-type-name-regexp) 1 font-lock-type-face) + + ;; Unary "!" + ("\\(!\\)[^=]" 1 font-lock-negation-char-face) + + ;; Composite literal type + (,(concat go-type-name-regexp "{") 1 font-lock-type-face) + + ;; Map value type + (go--match-map-value 1 font-lock-type-face) + + ;; Map key type + (,(concat "\\_\\[" go-type-name-regexp) 1 font-lock-type-face) + + ;; Channel type + (,(concat "\\_[[:space:]]*\\(?:<-[[:space:]]*\\)?" go-type-name-regexp) 1 font-lock-type-face) + + ;; "new()"/"make()" type + (,(concat "\\_<\\(?:new\\|make\\)\\_>\\(?:[[:space:]]\\|)\\)*(" go-type-name-regexp) 1 font-lock-type-face) + + ;; Type assertion + (,(concat "\\.\\s *(" go-type-name-regexp) 1 font-lock-type-face) + + ;; Composite literal field names and label definitions. + (go--match-ident-colon 1 font-lock-constant-face) + + ;; Labels in goto/break/continue + (,(concat "\\_<\\(?:goto\\|break\\|continue\\)\\_>[[:space:]]*\\(" go-label-regexp "\\)") 1 font-lock-constant-face)))) + +(let ((m (define-prefix-command 'go-goto-map))) + (define-key m "a" #'go-goto-arguments) + (define-key m "d" #'go-goto-docstring) + (define-key m "f" #'go-goto-function) + (define-key m "i" #'go-goto-imports) + (define-key m "m" #'go-goto-method-receiver) + (define-key m "n" #'go-goto-function-name) + (define-key m "r" #'go-goto-return-values)) + +(defvar go-mode-map + (let ((m (make-sparse-keymap))) + (unless (boundp 'electric-indent-chars) + (define-key m "}" #'go-mode-insert-and-indent) + (define-key m ")" #'go-mode-insert-and-indent)) + (define-key m (kbd "C-c C-a") #'go-import-add) + (define-key m (kbd "C-c C-j") #'godef-jump) + (define-key m (kbd "C-x 4 C-c C-j") #'godef-jump-other-window) + (define-key m (kbd "C-c C-d") #'godef-describe) + (define-key m (kbd "C-c C-f") 'go-goto-map) + m) + "Keymap used by ‘go-mode’.") + +(easy-menu-define go-mode-menu go-mode-map + "Menu for Go mode." + '("Go" + ["Describe Expression" godef-describe t] + ["Jump to Definition" godef-jump t] + "---" + ["Add Import" go-import-add t] + ["Go to Imports" go-goto-imports t] + "---" + ("Playground" + ["Send Buffer" go-play-buffer t] + ["Send Region" go-play-region t] + ["Download" go-download-play t]) + "---" + ["Coverage" go-coverage t] + ["Gofmt" gofmt t] + ["Godoc" godoc t] + "---" + ["Customize Mode" (customize-group 'go) t])) + +(defun go-mode-insert-and-indent (key) + "Invoke the global binding of KEY, then reindent the line." + + (interactive (list (this-command-keys))) + (call-interactively (lookup-key (current-global-map) key)) + (indent-according-to-mode)) + +(defmacro go-paren-level () + `(car (syntax-ppss))) + +(defmacro go-in-string-or-comment-p () + `(nth 8 (syntax-ppss))) + +(defmacro go-in-string-p () + `(nth 3 (syntax-ppss))) + +(defmacro go-in-comment-p () + `(nth 4 (syntax-ppss))) + +(defmacro go-goto-beginning-of-string-or-comment () + `(goto-char (nth 8 (syntax-ppss)))) + +(defun go--backward-irrelevant (&optional stop-at-string) + "Skip backwards over any characters that are irrelevant for +indentation and related tasks. + +It skips over whitespace, comments, cases and labels and, if +STOP-AT-STRING is not true, over strings." + + (let (pos (start-pos (point))) + (skip-chars-backward "\n\s\t") + (if (and (save-excursion (beginning-of-line) (go-in-string-p)) + (= (char-before) ?`) + (not stop-at-string)) + (backward-char)) + (if (and (go-in-string-p) + (not stop-at-string)) + (go-goto-beginning-of-string-or-comment)) + (if (looking-back "\\*/" (line-beginning-position)) + (backward-char)) + (if (go-in-comment-p) + (go-goto-beginning-of-string-or-comment)) + (setq pos (point)) + (beginning-of-line) + (if (or (looking-at (concat "^" go-label-regexp ":")) + (looking-at "^[[:space:]]*\\(case .+\\|default\\):")) + (end-of-line 0) + (goto-char pos)) + (if (/= start-pos (point)) + (go--backward-irrelevant stop-at-string)) + (/= start-pos (point)))) + +(defun go--buffer-narrowed-p () + "Return non-nil if the current buffer is narrowed." + (/= (buffer-size) + (- (point-max) + (point-min)))) + +(defun go-previous-line-has-dangling-op-p () + "Return non-nil if the current line is a continuation line. +The return value is cached based on the current `line-beginning-position'." + (let* ((line-begin (line-beginning-position)) + (val (gethash line-begin go-dangling-cache 'nope))) + (when (or (go--buffer-narrowed-p) (equal val 'nope)) + (save-excursion + (go--forward-line -1) + (if (go--current-line-has-dangling-op-p) + (setq val (line-end-position)) + (setq val nil)) + + (if (not (go--buffer-narrowed-p)) + (puthash line-begin val go-dangling-cache)))) + val)) + +(defun go--current-line-has-dangling-op-p () + "Return non-nil if current line ends in a dangling operator. +The return value is not cached." + (or + (and + (go--line-suffix-p go-dangling-operators-regexp) + + ;; "=" does not behave like a dangling operator in decl statements. + (not (go--line-suffix-p "\\(?:var\\|type\\|const\\)[[:space:]].*=")) + + ;; Don't mistake "1234." for a dangling operator. + (not (go--line-suffix-p "[[:space:]]-?[[:digit:]][_0-9]*\\."))) + + ;; treat comma as dangling operator in certain cases + (and + (go--line-suffix-p ",") + (save-excursion (end-of-line) (go--commas-indent-p))))) + + +(defun go--commas-indent-p () + "Return non-nil if in a context where dangling commas indent next line." + (not (or + (go--open-paren-position) + (go--in-composite-literal-p) + (go--in-case-clause-list-p) + (go--in-struct-definition-p)))) + +(defun go--in-case-clause-list-p () + "Return non-nil if inside a multi-line case cause list. + +This function is only concerned with list items on lines after the +case keyword. It returns nil for the case line itself." + (save-excursion + (beginning-of-line) + (when (not (looking-at go--case-or-default-regexp)) + (let (saw-colon) + ;; optionally skip line with the colon + (when (go--line-suffix-p ":") + (setq saw-colon t) + (forward-line -1)) + + ;; go backwards while at a comment or a line ending in comma + (while (and + (or + (go--boring-line-p) + (go--line-suffix-p ",")) + (not (looking-at go--case-regexp)) + (go--forward-line -1))) + + (and + (looking-at-p go--case-regexp) + ;; we weren't in case list if first line ended in colon + ;; and the "case" line ended in colon + (not (and saw-colon (looking-at ".*:[[:space:]]*$")))))))) + +(defun go--in-composite-literal-p () + "Return non-nil if point is in a composite literal." + (save-excursion + (save-match-data + (and + (go-goto-opening-parenthesis) + + ;; Opening paren-like character is a curly. + (eq (char-after) ?{) + + (or + ;; Curly is preceded by non space (e.g. "Foo{"), definitely + ;; composite literal. + (zerop (skip-syntax-backward " ")) + + ;; Curly preceded by comma or semicolon. This is a composite + ;; literal with implicit type name. + (looking-back "[,:]" (1- (point))) + + ;; If we made it to the beginning of line we are either a naked + ;; block or a composite literal with implicit type name. If we + ;; are the latter, we must be contained in another composite + ;; literal. + (and (bolp) (go--in-composite-literal-p))))))) + +(defun go--in-paren-with-prefix-p (paren prefix) + (save-excursion + (and + (go-goto-opening-parenthesis) + (eq (char-after) paren) + (skip-syntax-backward " ") + (> (point) (length prefix)) + (string= prefix (buffer-substring (- (point) (length prefix)) (point)))))) + +(defun go--in-struct-definition-p () + "Return non-nil if point is inside a struct definition." + (go--in-paren-with-prefix-p ?{ "struct")) + +(defun go--in-interface-p () + "Return non-nil if point is inside an interface definition." + (go--in-paren-with-prefix-p ?{ "interface")) + + +(defun go--in-type-switch-p () + "Return non-nil if point is inside a type switch statement." + (go--in-paren-with-prefix-p ?{ ".(type)")) + +(defun go--open-paren-position () + "Return non-nil if point is between '(' and ')'. + +The return value is the position of the opening paren." + (save-excursion + (let ((start-paren-level (go-paren-level))) + (and + (go-goto-opening-parenthesis) + + ;; opening paren-like character is actually a paren + (eq (char-after) ?\() + + ;; point is before the closing paren + (< (go-paren-level) start-paren-level) + + (point))))) + +(defun go-indentation-at-point () + "Return the appropriate indentation for the current line." + (save-excursion + (beginning-of-line) + + (if (go-in-comment-p) + (go--multiline-comment-indent) + (go--indentation-at-point)))) + +;; It's unfortunate that the user cannot reindent the current line to +;; align with the previous line; however, if they could, then people +;; who use reindent-then-newline-and-indent wouldn't be able to +;; explicitly indent lines inside comments. +(defun go--multiline-comment-indent () + "Return the appropriate indent inside multiline comment. + +Assumes point is at beginning of line within comment. This +function has basic logic to indent as you add new lines to a +multiline comment, and to line up all the `*' if each line starts +with `*'. The gofmt behavior for multiline comments is +surprisingly complex and strange/buggy, so we just aim to do +something simple rather than encode all the subtle behavior." + (let* (;; Indent of current line. + (indent (current-indentation)) + ;; Indent of opening "/*". + start-indent + ;; Default indent to use based on preceding context. + natural-indent + ;; Non-nil means keep existing indent and give up calculating indent. + give-up + ;; Whether all comment lines (except first) begin with "*". + (all-star t)) + + (save-excursion + (go-goto-beginning-of-string-or-comment) + + (setq start-indent (current-indentation)) + + ;; If other stuff precedes start of multiline comment, give up. + (setq give-up (/= (current-column) start-indent)) + + ;; Skip "/*". + (forward-char 2) + + (skip-syntax-forward " ") + + (if (not (eolp)) + ;; If we aren't at EOL, we have content on the first line. + ;; Base our natural indent on that. + (setq natural-indent (current-column)) + ;; Otherwise default to 1 space beyond "/*". + (setq natural-indent (+ start-indent 3))) + + (let (done) + (while (not done) + (setq done (or (looking-at ".*\\*/") (not (zerop (forward-line))))) + (setq all-star (and all-star (looking-at "[[:space:]]*\\*")))))) + + ;; If previous line has comment content, use its indent as our + ;; natural indent. + (save-excursion + (when (zerop (forward-line -1)) + (beginning-of-line) + (when (and (go-in-comment-p) (> (current-indentation) 0)) + (setq natural-indent (current-indentation))))) + + (cond + (give-up indent) + + (all-star (1+ start-indent)) + + ;; Closing "*/" with no preceding content always lines up with "/*". + ((looking-at "[[:space:]]*\\*/") start-indent) + + ;; If the line is already indented, leave it. + (t (if (zerop indent) natural-indent indent))))) + +(defun go--indentation-at-point () + "Return the appropriate indentation for the current non-comment line. + +This function works by walking a line's characters backwards. When it +encounters a closing paren or brace it bounces to the corresponding +opener. If it arrives at the beginning of the line you are indenting, +it moves to the end of the previous line if the current line is a +continuation line, else it moves to the containing opening paren or +brace. If it arrives at the beginning of a line other than the line +you are indenting, it will continue to the previous dangling line if +the line you are indenting was not a continuation line, otherwise it +is done." + (save-excursion + (beginning-of-line) + + (let ( + ;; Beginning of our starting line. + (start-line (point)) + + ;; Whether this is our first iteration of the outer while loop. + (first t) + + ;; Whether we start in a block (i.e. our first line is not a + ;; continuation line and is in an "if", "for", etc. block). + (in-block) + + ;; Our desired indent relative to our ending line's indent. + (indent 0)) + + ;; Skip leading whitespace. + (skip-syntax-forward " ") + + ;; Decrement indent if the first character on the line is a closer. + (when (or (eq (char-after) ?\)) (eq (char-after) ?})) + (cl-decf indent tab-width)) + + (while (or + ;; Always run the first iteration so we process empty lines. + first + + ;; Otherwise stop if we are at the start of a line. + (not (bolp))) + (setq first nil) + + (cl-case (char-before) + + ;; We have found a closer (paren or brace). + ((?\) ?}) + (backward-char) + (let ((bol (line-beginning-position))) + + ;; Jump back to corresponding opener. + (go-goto-opening-parenthesis) + + ;; Here we decrement the indent if we are closing an indented + ;; expression. In other words, the closer's line was indented + ;; relative to the opener's line, and that indent should not + ;; be inherited by our starting line. + (when (and + ;; We care about dangling expressions, not child blocks. + (not in-block) + + ;; Opener and closer aren't on same line. + (< (point) bol) + + (go-previous-line-has-dangling-op-p) + + ;; Opener is at same paren level as start of line (ignore sub-expressions). + (eq (go-paren-level) (save-excursion (beginning-of-line) (go-paren-level))) + + ;; This dangling line opened indent relative to previous dangling line. + (go--continuation-line-indents-p)) + (cl-decf indent tab-width)))) + + ;; Brackets don't affect indentation, so just skip them. + ((?\]) + (backward-char))) + + ;; Skip non-closers since we are only interested in closing parens/braces. + (skip-syntax-backward "^)" (line-beginning-position)) + + (when (go-in-string-or-comment-p) + (go-goto-beginning-of-string-or-comment)) + + ;; At the beginning of the starting line. + (when (= start-line (point)) + + ;; We are a continuation line. + (if (go-previous-line-has-dangling-op-p) + (progn + ;; Presume a continuation line always gets an extra indent. + ;; We reduce the indent after the loop, if necessary. + (cl-incf indent tab-width) + + ;; Go to the end of the dangling line. + (goto-char (go-previous-line-has-dangling-op-p))) + + ;; If we aren't a continuation line and we have an enclosing paren + ;; or brace, jump to opener and increment our indent. + (when (go-goto-opening-parenthesis) + (setq in-block (go--flow-block-p)) + (cl-incf indent tab-width)))) + + ;; If we started in a child block we must follow dangling lines + ;; until they don't dangle anymore. This is to handle cases like: + ;; + ;; if foo || + ;; foo && + ;; foo { + ;; X + ;; + ;; There can be an arbitrary number of indents, so we must go back to + ;; the "if" to determine the indent of "X". + (when (and in-block (bolp) (go-previous-line-has-dangling-op-p)) + (goto-char (go-previous-line-has-dangling-op-p)))) + + ;; If our ending line is a continuation line but doesn't open + ;; an extra indent, reduce indent. We tentatively gave indents to all + ;; dangling lines and all lines inside open parens, so here we take that + ;; indent back. + ;; + ;; 1 + 1 + + ;; ending line 1 + foo( 1 + foo( + ;; starting line 1, becomes 1, + ;; ) ) + ;; + ;; + ;; 1 + 1 + + ;; ending line 1 + becomes 1 + + ;; starting line 1 1 + (when (and + (go-previous-line-has-dangling-op-p) + (not (go--continuation-line-indents-p))) + (cl-decf indent tab-width)) + + ;; Apply our computed indent relative to the indent of the + ;; ending line, or 0 if we are at the top level. + (if (and + (= 0 (go-paren-level)) + (not (go-previous-line-has-dangling-op-p))) + indent + (+ indent (current-indentation)))))) + +(defconst go--operator-chars "*/%<>&\\^+\\-|=!,." + "Individual characters that appear in operators. +Comma and period are included because they can be dangling operators, so +they need to be considered by `go--continuation-line-indents-p'") + +(defun go--operator-precedence (op) + "Go operator precedence (higher binds tighter)." + (cl-case (intern op) + (\. 7) ; "." in "foo.bar", binds tightest + (! 6) + ((* / % << >> & &^) 5) + ((+ - | ^) 4) + ((== != < <= > >=) 3) + (&& 2) + (|| 1) + (t 0))) + +(defun go--flow-block-p () + "Return whether looking at a { that opens a control flow block. + +We check for a { that is preceded by a space and is not a func +literal opening brace." + (save-excursion + (when (and + (eq (char-after) ?{) + (not (zerop (skip-syntax-backward " ")))) + + (let ((eol (line-end-position)) + (level (go-paren-level)) + (found-func-literal)) + + (beginning-of-line) + + ;; See if we find any "func" keywords on this line at the same paren + ;; level as the curly. + (while (and + (not found-func-literal) + (re-search-forward "\\_" eol t)) + (setq found-func-literal (and + (= level (go-paren-level)) + (not (go-in-string-or-comment-p))))) + (not found-func-literal))))) + +(defun go--continuation-line-indents-p () + "Return non-nil if the current continuation line opens an additional indent. + +This function works by looking at the Go operators used on the current +line. If all the operators bind tighter than the previous line's +dangling operator and the current line ends in a dangling operator or +open paren, the next line will have an additional indent. + +For example: +foo || + foo && // this continuation line opens another indent + foo" + (save-excursion + (let (prev-op (all-tighter t)) + + ;; Record the dangling operator from previous line. + (save-excursion + (goto-char (go-previous-line-has-dangling-op-p)) + (go--end-of-line) + (skip-syntax-backward " ") + (let ((end (point))) + (skip-chars-backward go--operator-chars) + (setq prev-op (buffer-substring-no-properties (point) end)))) + + (beginning-of-line) + + (when (or + ;; We can only open indent if we have a dangling operator, or + (go--current-line-has-dangling-op-p) + + (save-excursion + (go--end-of-line) + (backward-char) + (or + ;; Line ends in a "(" or ",", or + (eq (char-after) ?\() + (eq (char-after) ?,) + + ;; Line ends in a "{" that isn't a control block. + (and + (eq (char-after) ?{) + (not (go--flow-block-p)))))) + + (let ((prev-precedence (go--operator-precedence prev-op)) + (start-depth (go-paren-level)) + (line-start (line-beginning-position))) + + (end-of-line) + + ;; While we haven't found a looser operator and are on the starting line... + (while (and all-tighter (> (point) line-start)) + + ;; Skip over non-operator characters. + (skip-chars-backward (concat "^" go--operator-chars) line-start) + + (let ((end (point))) + (cond + ;; Ignore sub-expressions at different paren levels. + ((/= (go-paren-level) start-depth) + (skip-syntax-backward "^()")) + + ((go-in-string-or-comment-p) + (go-goto-beginning-of-string-or-comment)) + + ;; We found an operator. Check if it has lower precedence. + ((/= (skip-chars-backward go--operator-chars) 0) + (when (>= + prev-precedence + (go--operator-precedence (buffer-substring (point) end))) + (setq all-tighter nil))))))) + all-tighter)))) + +(defun go--end-of-line () + "Move to the end of the code on the current line. +Point will be left before any trailing comments. Point will be left +after the opening backtick of multiline strings." + (end-of-line) + (let ((keep-going t)) + (while keep-going + (skip-syntax-backward " ") + (when (looking-back "\\*/" (- (point) 2)) + ;; back up so we are in the /* comment */ + (backward-char)) + (if (go-in-comment-p) + (go-goto-beginning-of-string-or-comment) + (setq keep-going nil)))) + (when (go-in-string-p) + (go-goto-beginning-of-string-or-comment) + ;; forward one so point is after the opening "`" + (forward-char))) + +(defun go--line-suffix-p (re) + "Return non-nil if RE matches the end of the line starting from `point'. + +Trailing whitespace, trailing comments and trailing multiline strings are +ignored." + (let ((start (point)) + (end (save-excursion (go--end-of-line) (point)))) + (when (< start end) + (string-match-p + (concat "\\(?:" re "\\)$") + (buffer-substring-no-properties start end))))) + +(defun go--boring-line-p () + "Return non-nil if the current line probably doesn't impact indentation. + +A boring line is one that starts with a comment, is empty, is part of a +multiline comment, or starts and ends in a multiline string." + (or + (looking-at (concat go--comment-start-regexp "\\|[[:space:]]*$")) + (go-in-comment-p) + (and (go-in-string-p) (save-excursion (end-of-line) (go-in-string-p))))) + +(defun go--forward-line (&optional count) + "Like `forward-line' but skip comments and empty lines. + +Return non-nil if point changed lines." + (let (moved) + (while (and + (zerop (forward-line count)) + (setq moved t) + (go--boring-line-p)) + (setq count (if (and count (< count 0 )) -1 1))) + moved)) + +(defun go--case-comment-p (indent) + "Return non-nil if looking at a comment attached to a case statement. + +INDENT is the normal indent of this line, i.e. that of the case body." + (when (and + (> (current-indentation) 0) + (looking-at go--comment-start-regexp)) + + (let (switch-before + case-after + has-case-aligned-preceding-comment) + + (save-excursion + ;; Search for previous case-aligned comment. + (while (and + (zerop (forward-line -1)) + (cond + ((looking-at "^[[:space:]]*$")) + + ((looking-at go--comment-start-regexp) + (when (= (current-indentation) (- indent tab-width)) + (setq has-case-aligned-preceding-comment t)) + t) + + ((go-in-comment-p))))) + + ;; Record if a switch (or select) precedes us. + (setq switch-before (looking-at "^[[:space:]]*\\(switch\\|select\\)[[:space:]]"))) + + ;; Record if first proceeding non-comment line is a case statement. + (save-excursion + (while (and + (zerop (forward-line 1)) + (or + (looking-at go--comment-start-regexp) + (looking-at "^[[:space:]]*$") + (go-in-comment-p)))) + + (setq case-after (looking-at go--case-or-default-regexp))) + + (and + ;; a "case" statement comes after our comment + case-after + + (or + ;; "switch" statement precedes us, always align with "case" + switch-before + + ;; a preceding comment is aligned with "case", we should too + has-case-aligned-preceding-comment + + ;; other cases are ambiguous, so if comment is currently + ;; aligned with "case", leave it that way + (= (current-indentation) (- indent tab-width))))))) + +(defun go-mode-indent-line () + (interactive) + (let (indent + ;; case sensitively match "case", "default", etc. + (case-fold-search nil) + (pos (- (point-max) (point))) + (point (point)) + (beg (line-beginning-position)) + (non-tab-indents 0)) + (back-to-indentation) + (if (go-in-string-p) + (goto-char point) + (setq indent (go-indentation-at-point)) + (when (or + (and + (looking-at (concat go-label-regexp ":\\([[:space:]]*/.+\\)?$\\|" go--case-or-default-regexp)) + ;; don't think last part of multiline case statement is a label + (not (go-previous-line-has-dangling-op-p)) + (not (go--in-case-clause-list-p)) + (not (go--in-composite-literal-p))) + + ;; comment attached above a "case" statement + (go--case-comment-p indent)) + (cl-decf indent tab-width)) + + ;; Don't do anything if current indent is correct. + (when (/= indent (current-column)) + ;; Don't use tabs for indenting beyond "/*" in multiline + ;; comments. They don't play well with gofmt. + (when (go-in-comment-p) + (save-excursion + (go-goto-beginning-of-string-or-comment) + (when (> indent (current-indentation)) + (setq non-tab-indents (- indent (current-indentation))) + (setq indent (current-indentation))))) + + (delete-region beg (point)) + (indent-to indent) + (insert-char ? non-tab-indents)) + + ;; If initial point was within line's indentation, + ;; position after the indentation. Else stay at same point in text. + (if (> (- (point-max) pos) (point)) + (goto-char (- (point-max) pos)))))) + +(defun go-beginning-of-defun (&optional count) + (when (and (not (go-in-string-or-comment-p)) + (not (bolp)) + (save-excursion + (beginning-of-line) + (looking-at go-func-meth-regexp))) + ;; Point is already somewhere on the function definition. Move to the end of line so that searching backwards finds + ;; it. We don't go to the end of line unconditionally because that confuses evil-mode + ;; (https://github.com/dominikh/go-mode.el/issues/186) + ;; + ;; If point is already at the beginning of line and looking at a function, then we want go-beginning-of-defun to + ;; jump to the previous function instead. + (end-of-line)) + (setq count (or count 1)) + (let (first failure) + (dotimes (i (abs count)) + (setq first t) + (while (and (not failure) + (or first (go-in-string-or-comment-p))) + (if (>= count 0) + (progn + (go--backward-irrelevant) + (if (not (re-search-backward go-func-meth-regexp nil t)) + (setq failure t))) + (if (looking-at go-func-meth-regexp) + (forward-char)) + (if (not (re-search-forward go-func-meth-regexp nil t)) + (setq failure t))) + (setq first nil))) + (if (< count 0) + (beginning-of-line)) + (not failure))) + +(defun go-end-of-defun () + (let (orig-level) + ;; It can happen that we're not placed before a function by emacs + (if (not (looking-at "func")) + (go-beginning-of-defun -1)) + ;; Find the { that starts the function, i.e., the next { that isn't + ;; preceded by struct or interface, or a comment or struct tag. BUG: + ;; breaks if there's a comment between the struct/interface keyword and + ;; bracket, like this: + ;; + ;; struct /* why? */ { + (while (progn + (skip-chars-forward "^{") + (forward-char) + (or (go-in-string-or-comment-p) + (looking-back "\\(struct\\|interface\\)\\s-*{" + (line-beginning-position))))) + (setq orig-level (go-paren-level)) + (while (>= (go-paren-level) orig-level) + (skip-chars-forward "^}") + (forward-char)))) + + +(defvar go--fontify-param-has-name nil + "Whether the current params list has names. + +This is used during fontification of function signatures.") + +(defvar go--fontify-param-beg nil + "Position of \"(\" starting param list. + +This is used during fontification of function signatures.") + +(defun go--fontify-param-pre () + "Set `go--fontify-param-has-name' and `go--fontify-param-beg' appropriately. + +This is used as an anchored font lock keyword PRE-MATCH-FORM. We +must set `go--fontify-param-has-name' ahead of time because you +can't know if the param list is types only or names and types +until you see the end. For example: + +// types only +func foo(int, string) {} + +// names and types (don't know so until you see the \"int\"). +func foo(i, j int) {}" + (setq go--fontify-param-has-name (eq + (go--parameter-list-type (point-max)) + 'present)) + + ;; Remember where our match started so we can continue our search + ;; from here. + (setq go--fontify-param-beg (point)) + + ;; Return position of closing paren so we process the entire + ;; multiline param list. + (save-excursion + (let ((depth (go-paren-level))) + ;; First check that our paren is closed by the end of the file. This + ;; avoids expanding the fontification region to the entire file when you + ;; have an unclosed paren at file scope. + (when (save-excursion + (goto-char (1+ (buffer-size))) + (< (go-paren-level) depth)) + (while (and + (re-search-forward ")" nil t) + (>= (go-paren-level) depth))))) + (point))) + +(defun go--fontify-param-post () + "Move point back to opening paren. + +This is used as an anchored font lock keyword POST-MATCH-FORM. We +move point back to the opening \"(\" so we find nested param +lists." + (goto-char go--fontify-param-beg)) + +(defun go--match-param-start (end) + "Search for the starting of param lists. + +Search for the opening `(' of function signature param lists. +This covers the func receiver, params, and results. Interface +declarations are also included." + (let (found-match) + (while (and + (not found-match) + (re-search-forward (concat "\\(\\_<" go-identifier-regexp "\\)?(") end t)) + (when (not (go-in-string-or-comment-p)) + (save-excursion + (goto-char (match-beginning 0)) + + (let ((name (match-string 1))) + (when name + ;; We are in a param list if "func" preceded the "(" (i.e. + ;; func literal), or if we are in an interface + ;; declaration, e.g. "interface { foo(i int) }". + (setq found-match (or (string= name "func") (go--in-interface-p)))) + + ;; Otherwise we are in a param list if our "(" is preceded + ;; by ") " or "func ". + (when (and (not found-match) (not (zerop (skip-syntax-backward " ")))) + (setq found-match (or + (eq (char-before) ?\)) + (looking-back "\\_ (go-paren-level) orig-level))) + (forward-char)) + (when (and (looking-at-p ",") + (< (point) (1- end))) + (forward-char) + t))) + +(defun go--looking-at-keyword () + (and (looking-at (concat "\\(" go-identifier-regexp "\\)")) + (member (match-string 1) go-mode-keywords))) + +(defun go--match-type-switch-case (end) + "Match a \"case\" clause within a type switch." + (let (found-match) + (while (and + (not found-match) + + ;; Search for "case" statements. + (re-search-forward "^[[:space:]]*case " end t)) + + ;; Make sure we are in a type switch statement. + (setq found-match (go--in-type-switch-p))) + found-match)) + +(defun go--fontify-type-switch-case (end) + "Match a single type within a type switch case." + (let (found-match done) + ;; Loop until we find a match because we must skip types we don't + ;; handle, such as "interface { foo() }". + (while (and (not found-match) (not done)) + (when (looking-at (concat "\\(?:[[:space:]]*\\|//.*\\|\n\\)*" go-type-name-regexp "[[:space:]]*[,:]")) + (goto-char (match-end 1)) + (unless (member (match-string 1) go-constants) + (setq found-match t))) + (setq done (not (go--search-next-comma end)))) + found-match)) + +(defun go--containing-decl () + "Return containing decl kind var|const|type, if any." + (save-match-data + (or + (save-excursion + (and + (go-goto-opening-parenthesis) + (eq (char-after) ?\() + (skip-syntax-backward " ") + (skip-syntax-backward "w") + (looking-at "\\(var\\|const\\|type\\)[[:space:]]") + (match-string-no-properties 1))) + + (save-excursion + (let ((depth (go-paren-level))) + (beginning-of-line) + (and + (= (go-paren-level) depth) + (looking-at "[[:space:]]*\\(var\\|const\\|type\\)[[:space:]]") + (match-string-no-properties 1))))))) + +(defconst go--decl-ident-re (concat "\\(?:^\\|[[:space:]]\\)\\(\\(\\(" go-identifier-regexp "\\)\\)\\)\\_>")) + +(defun go--match-decl (end) + "Match identifiers in \"var\", \"type\" and \"const\" decls, as +well as \":=\" assignments. + +In order to only scan once, the regex has three subexpressions +that match the same identifier. Depending on the kind of +containing decl we zero out the subexpressions so the right one +gets highlighted by the font lock keyword." + (let (found-match decl) + (while (and + (not found-match) + (re-search-forward go--decl-ident-re end t)) + + (save-excursion + ;; Skip keywords. + (cond + ((member (match-string 1) go-mode-keywords)) + + ((and + ;; We are in a decl of some kind. + (setq decl (go--containing-decl)) + + ;; We aren't on right side of equals sign. + (not (go--looking-back-p "="))) + + (setq found-match t) + + ;; Unset match data subexpressions that don't apply based on + ;; the decl kind. + (let ((md (match-data))) + (cond + ((string= decl "var") + (setf (nth 4 md) nil (nth 5 md) nil (nth 6 md) nil (nth 7 md) nil) + (when (not go-fontify-variables) + (setf (nth 2 md) nil (nth 3 md) nil))) + ((string= decl "const") + (setf (nth 2 md) nil (nth 3 md) nil (nth 6 md) nil (nth 7 md) nil)) + ((string= decl "type") + (setf (nth 2 md) nil (nth 3 md) nil (nth 4 md) nil (nth 5 md) nil))) + (set-match-data md))) + + (go-fontify-variables + (save-match-data + ;; Left side of ":=" assignment. + (when (looking-at ".*:=") + (let ((depth (go-paren-level))) + (goto-char (match-end 0)) + ;; Make sure the ":=" isn't in a comment or a sub-block. + (setq found-match (and + (not (go-in-string-or-comment-p)) + (= depth (go-paren-level))))))))))) + found-match)) + +(defun go--looking-back-p (re) + "Return non-nil if RE matches beginning of line to point. + +RE is not anchored automatically." + (string-match-p + re + (buffer-substring-no-properties (point) (line-beginning-position)))) + + +(defconst go--ident-type-pair-re (concat "\\_<\\(" go-identifier-regexp "\\)[[:space:]]+" go-type-name-regexp)) + +(defun go--match-ident-type-pair (end) + "Search for identifier + type-name pairs. + +For example, this looks for the \"foo bar\" in \"var foo bar\", +yielding match-data for \"bar\" since that is a type name to be +fontified. This approach matches type names in var and const +decls, and in struct definitions. Return non-nil if search +succeeds." + (let (found-match) + (while (and + (not found-match) + (re-search-forward go--ident-type-pair-re end t)) + + ;; Make sure the neither match is a keyword. + (if (member (match-string 2) go-mode-keywords) + (goto-char (match-end 2)) + (if (member (match-string 1) go-mode-keywords) + (goto-char (match-end 1)) + (setq found-match t)))) + + found-match)) + +(defconst go--single-func-result-re (concat ")[[:space:]]+" go-type-name-regexp "\\(?:$\\|[[:space:]),]\\)")) + +(defun go--match-single-func-result (end) + "Match single result types. + +Parenthetical result lists are handled by the param list keyword, +so we need a separate keyword to handle singular result types +such as \"string\" in: + +func foo(i int) string" + (let (found-match) + (while (and + (not found-match) + (re-search-forward go--single-func-result-re end t)) + (when (not (member (match-string 1) go-mode-keywords)) + (setq found-match t) + (goto-char (match-end 1)))) + found-match)) + +(defconst go--type-alias-re + (concat "^[[:space:]]*\\(type[[:space:]]+\\)?" go-identifier-regexp "[[:space:]]*=[[:space:]]*" go-type-name-regexp)) + +(defun go--match-type-alias (end) + "Search for type aliases. + +We are looking for the right-hand-side of the type alias" + (let (found-match) + (while (and + (not found-match) + (re-search-forward go--type-alias-re end t)) + ;; Either line started with "type", or we are in a "type" block. + (setq found-match (or + (match-string 1) + (go--in-paren-with-prefix-p ?\( "type")))) + found-match)) + + +(defconst go--map-value-re + (concat "\\_\\[\\(?:\\[[^]]*\\]\\)*[^]]*\\]" go-type-name-regexp)) + +(defun go--match-map-value (end) + "Search for map value types." + (when (re-search-forward go--map-value-re end t) + ;; Move point to beginning of map value in case value itself is + ;; also a map (we will match it next iteration). + (goto-char (match-beginning 1)) + t)) + +(defconst go--label-re (concat "\\(" go-label-regexp "\\):")) + +(defun go--match-ident-colon (end) + "Search for composite literal field names and label definitions." + (let (found-match) + (while (and + (not found-match) + (re-search-forward go--label-re end t)) + + (save-excursion + (goto-char (match-beginning 1)) + (skip-syntax-backward " ") + + (setq found-match (or + ;; We are a label/field name if we are at the + ;; beginning of the line. + (bolp) + + ;; Composite literal field names, e.g. "Foo{Bar:". Note + ;; that this gives false positives for literal maps, + ;; arrays, and slices. + (and + (or (eq (char-before) ?,) (eq (char-before) ?{)) + (go--in-composite-literal-p)))))) + + found-match)) + +(defun go--parameter-list-type (end) + "Return `present' if the parameter list has names, or `absent' if not. +Assumes point is at the beginning of a parameter list, just +after '('." + (save-excursion + (skip-chars-forward "[:space:]\n" end) + (cond ((> (point) end) + nil) + ((looking-at (concat go-identifier-regexp "[[:space:]\n]*,")) + (goto-char (match-end 0)) + (go--parameter-list-type end)) + ((or (looking-at go-qualified-identifier-regexp) + (looking-at (concat go-type-name-no-prefix-regexp "[[:space:]\n]*\\(?:)\\|\\'\\)")) + (go--looking-at-keyword) + (looking-at "[*\\[]\\|\\.\\.\\.\\|\\'")) + 'absent) + (t 'present)))) + +(defun go--reset-dangling-cache-before-change (&optional _beg _end) + "Reset `go-dangling-cache'. + +This is intended to be called from `before-change-functions'." + (setq go-dangling-cache (make-hash-table :test 'eql))) + +(defun go--electric-indent-function (inserted-char) + (let ((prev (char-before (1- (point))))) + (cond + ;; Indent after starting/ending a comment. This is handy for + ;; comments above "case" statements and closing multiline + ;; comments. + ((or + (and (eq inserted-char ?/) (eq prev ?/)) + (and (eq inserted-char ?/) (eq prev ?*)) + (and (eq inserted-char ?*) (eq prev ?/))) + 'do-indent) + + ((eq inserted-char ? ) + (and + (eq prev ?e) + (eq (char-before (- (point) 2)) ?s) + (eq (char-before (- (point) 3)) ?a) + (eq (char-before (- (point) 4)) ?c))) + + ;; Trick electric-indent-mode into indenting inside multiline + ;; comments. + ((and (eq inserted-char ?\n) (go-in-comment-p)) + 'do-indent)))) + +(defun go--comment-region (beg end &optional arg) + "Switch to block comment when commenting a partial line." + (save-excursion + (goto-char beg) + (let ((beg-bol (line-beginning-position))) + (goto-char end) + (if (and + ;; beg and end are on the same line + (eq (line-beginning-position) beg-bol) + ;; end is not at end of line + (not (eq end (line-end-position)))) + (let ((comment-start "/* ") + (comment-end " */") + (comment-padding "")) + (comment-region-default beg end arg)) + (comment-region-default beg end arg))))) + +;;;###autoload +(define-derived-mode go-mode prog-mode "Go" + "Major mode for editing Go source text. + +This mode provides (not just) basic editing capabilities for +working with Go code. It offers almost complete syntax +highlighting, indentation that is almost identical to gofmt and +proper parsing of the buffer content to allow features such as +navigation by function, manipulation of comments or detection of +strings. + +In addition to these core features, it offers various features to +help with writing Go code. You can directly run buffer content +through gofmt, read godoc documentation from within Emacs, modify +and clean up the list of package imports or interact with the +Playground (uploading and downloading pastes). + +The following extra functions are defined: + +- `gofmt' +- `godoc' and `godoc-at-point' +- `go-import-add' +- `go-goto-arguments' +- `go-goto-docstring' +- `go-goto-function' +- `go-goto-function-name' +- `go-goto-imports' +- `go-goto-return-values' +- `go-goto-method-receiver' +- `go-play-buffer' and `go-play-region' +- `go-download-play' +- `godef-describe' and `godef-jump' +- `go-coverage' + +If you want to automatically run `gofmt' before saving a file, +add the following hook to your Emacs configuration: + +\(add-hook 'before-save-hook #'gofmt-before-save) + +If you want to use `godef-jump' instead of etags (or similar), +consider binding godef-jump to `M-.', which is the default key +for `find-tag': + +\(add-hook 'go-mode-hook (lambda () + (local-set-key (kbd \"M-.\") #'godef-jump))) + +Please note that godef is an external dependency. You can install +it with + +go get github.com/rogpeppe/godef + + +If you're looking for even more integration with Go, namely +on-the-fly syntax checking, auto-completion and snippets, it is +recommended that you look at flycheck +\(see URL `https://github.com/flycheck/flycheck') or flymake in combination +with goflymake (see URL `https://github.com/dougm/goflymake'), gocode +\(see URL `https://github.com/nsf/gocode'), go-eldoc +\(see URL `github.com/syohex/emacs-go-eldoc') and yasnippet-go +\(see URL `https://github.com/dominikh/yasnippet-go')" + + ;; Font lock + (setq font-lock-defaults '(go--build-font-lock-keywords)) + (setq font-lock-multiline t) + + ;; Indentation + (set (make-local-variable 'indent-line-function) #'go-mode-indent-line) + + ;; Comments + (set (make-local-variable 'comment-start) "// ") + (set (make-local-variable 'comment-end) "") + (set (make-local-variable 'comment-use-syntax) t) + (set (make-local-variable 'comment-start-skip) "\\(//+\\|/\\*+\\)\\s *") + (set (make-local-variable 'comment-region-function) #'go--comment-region) + ;; Set comment-multi-line to t so that comment-indent-new-line + ;; doesn't use one /* */ per line. Thanks to comment-use-syntax, + ;; Emacs is smart enough to still insert new // for single-line + ;; comments. + (set (make-local-variable 'comment-multi-line) t) + + (set (make-local-variable 'beginning-of-defun-function) #'go-beginning-of-defun) + (set (make-local-variable 'end-of-defun-function) #'go-end-of-defun) + (setq-local paragraph-start + (concat "[[:space:]]*\\(?:" + comment-start-skip + "\\|\\*/?[[:space:]]*\\|\\)$")) + (setq-local paragraph-separate paragraph-start) + (setq-local fill-paragraph-function #'go-fill-paragraph) + (setq-local fill-forward-paragraph-function #'go--fill-forward-paragraph) + (setq-local adaptive-fill-function #'go--find-fill-prefix) + (setq-local adaptive-fill-first-line-regexp "") + (setq-local comment-line-break-function #'go--comment-indent-new-line) + + (set (make-local-variable 'parse-sexp-lookup-properties) t) + (set (make-local-variable 'syntax-propertize-function) #'go-propertize-syntax) + + (when (boundp 'electric-indent-chars) + (set (make-local-variable 'electric-indent-chars) '(?\n ?} ?\) ?:)) + (add-hook 'electric-indent-functions #'go--electric-indent-function nil t)) + + (set (make-local-variable 'compilation-error-screen-columns) nil) + + (set (make-local-variable 'go-dangling-cache) (make-hash-table :test 'eql)) + (add-hook 'before-change-functions #'go--reset-dangling-cache-before-change t t) + + ;; ff-find-other-file + (setq ff-other-file-alist 'go-other-file-alist) + + (setq imenu-generic-expression + '(("type" "^type *\\([^ \t\n\r\f]*\\)" 1) + ("func" "^func *\\(.*\\) {" 1))) + (imenu-add-to-menubar "Index") + + ;; Go style + (setq indent-tabs-mode t) + + ;; Handle unit test failure output in compilation-mode + ;; + ;; Note that we add our entry to the beginning of + ;; compilation-error-regexp-alist. In older versions of Emacs, the + ;; list was processed from the end, and we would've wanted to add + ;; ours last. But at some point this changed, and now the list is + ;; processed from the beginning. It's important that our entry comes + ;; before gnu, because gnu matches go test output, but includes the + ;; leading whitespace in the file name. + ;; + ;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2001-12/msg00674.html + ;; documents the old, reversed order. + (when (and (boundp 'compilation-error-regexp-alist) + (boundp 'compilation-error-regexp-alist-alist)) + (add-to-list 'compilation-error-regexp-alist 'go-test) + (add-to-list 'compilation-error-regexp-alist-alist + '(go-test . ("^\\s-+\\([^()\t\n]+\\):\\([0-9]+\\):? .*$" 1 2)) t))) + +;;;###autoload +(add-to-list 'auto-mode-alist (cons "\\.go\\'" 'go-mode)) + +(defun go--apply-rcs-patch (patch-buffer) + "Apply an RCS-formatted diff from PATCH-BUFFER to the current buffer." + (let ((target-buffer (current-buffer)) + ;; Relative offset between buffer line numbers and line numbers + ;; in patch. + ;; + ;; Line numbers in the patch are based on the source file, so + ;; we have to keep an offset when making changes to the + ;; buffer. + ;; + ;; Appending lines decrements the offset (possibly making it + ;; negative), deleting lines increments it. This order + ;; simplifies the forward-line invocations. + (line-offset 0) + (column (current-column))) + (save-excursion + (with-current-buffer patch-buffer + (goto-char (point-min)) + (while (not (eobp)) + (unless (looking-at "^\\([ad]\\)\\([0-9]+\\) \\([0-9]+\\)") + (error "Invalid rcs patch or internal error in go--apply-rcs-patch")) + (forward-line) + (let ((action (match-string 1)) + (from (string-to-number (match-string 2))) + (len (string-to-number (match-string 3)))) + (cond + ((equal action "a") + (let ((start (point))) + (forward-line len) + (let ((text (buffer-substring start (point)))) + (with-current-buffer target-buffer + (cl-decf line-offset len) + (goto-char (point-min)) + (forward-line (- from len line-offset)) + (insert text))))) + ((equal action "d") + (with-current-buffer target-buffer + (go--goto-line (- from line-offset)) + (cl-incf line-offset len) + (go--delete-whole-line len))) + (t + (error "Invalid rcs patch or internal error in go--apply-rcs-patch"))))))) + (move-to-column column))) + +(defun gofmt--is-goimports-p () + (string-equal (file-name-base gofmt-command) "goimports")) + +(defun gofmt () + "Format the current buffer according to the formatting tool. + +The tool used can be set via ‘gofmt-command’ (default: gofmt) and additional +arguments can be set as a list via ‘gofmt-args’." + (interactive) + (let ((tmpfile (make-nearby-temp-file "gofmt" nil ".go")) + (patchbuf (get-buffer-create "*Gofmt patch*")) + (errbuf (if gofmt-show-errors (get-buffer-create "*Gofmt Errors*"))) + (coding-system-for-read 'utf-8) + (coding-system-for-write 'utf-8) + our-gofmt-args) + + (unwind-protect + (save-restriction + (widen) + (if errbuf + (with-current-buffer errbuf + (setq buffer-read-only nil) + (erase-buffer))) + (with-current-buffer patchbuf + (erase-buffer)) + + (write-region nil nil tmpfile) + + (when (and (gofmt--is-goimports-p) buffer-file-name) + (setq our-gofmt-args + (append our-gofmt-args + ;; srcdir, despite its name, supports + ;; accepting a full path, and some features + ;; of goimports rely on knowing the full + ;; name. + (list "-srcdir" (file-local-name + (file-truename buffer-file-name)))))) + (setq our-gofmt-args + (append our-gofmt-args gofmt-args + (list "-w" (file-local-name tmpfile)))) + (message "Calling gofmt: %s %s" gofmt-command our-gofmt-args) + ;; We're using errbuf for the mixed stdout and stderr output. This + ;; is not an issue because gofmt -w does not produce any stdout + ;; output in case of success. + (if (zerop (apply #'process-file gofmt-command nil errbuf nil our-gofmt-args)) + (progn + ;; There is no remote variant of ‘call-process-region’, but we + ;; can invoke diff locally, and the results should be the same. + (if (zerop (let ((local-copy (file-local-copy tmpfile))) + (unwind-protect + (call-process-region + (point-min) (point-max) "diff" nil patchbuf + nil "-n" "-" (or local-copy tmpfile)) + (when local-copy (delete-file local-copy))))) + (message "Buffer is already gofmted") + (go--apply-rcs-patch patchbuf) + (message "Applied gofmt")) + (if errbuf (gofmt--kill-error-buffer errbuf))) + (message "Could not apply gofmt") + (if errbuf (gofmt--process-errors (buffer-file-name) tmpfile errbuf)))) + + (kill-buffer patchbuf) + (delete-file tmpfile)))) + + +(defun gofmt--process-errors (filename tmpfile errbuf) + (with-current-buffer errbuf + (if (eq gofmt-show-errors 'echo) + (progn + (message "%s" (buffer-string)) + (gofmt--kill-error-buffer errbuf)) + ;; Convert the gofmt stderr to something understood by the compilation mode. + (goto-char (point-min)) + (if (save-excursion + (save-match-data + (search-forward "flag provided but not defined: -srcdir" nil t))) + (insert "Your version of goimports is too old and doesn't support vendoring. Please update goimports!\n\n")) + (insert "gofmt errors:\n") + (let ((truefile + (if (gofmt--is-goimports-p) + (concat (file-name-directory filename) (file-name-nondirectory tmpfile)) + tmpfile))) + (while (search-forward-regexp + (concat "^\\(" (regexp-quote (file-local-name truefile)) + "\\):") + nil t) + (replace-match (file-name-nondirectory filename) t t nil 1))) + (compilation-mode) + (display-buffer errbuf)))) + +(defun gofmt--kill-error-buffer (errbuf) + (let ((win (get-buffer-window errbuf))) + (if win + (quit-window t win) + (kill-buffer errbuf)))) + +;;;###autoload +(defun gofmt-before-save () + "Add this to .emacs to run gofmt on the current buffer when saving: +\(add-hook 'before-save-hook 'gofmt-before-save). + +Note that this will cause ‘go-mode’ to get loaded the first time +you save any file, kind of defeating the point of autoloading." + + (interactive) + (when (eq major-mode 'go-mode) (gofmt))) + +(defun godoc--read-query () + "Read a godoc query from the minibuffer." + (if godoc-use-completing-read + (completing-read "godoc; " + (go-packages) nil nil nil 'go-godoc-history) + (read-from-minibuffer "godoc: " nil nil nil 'go-godoc-history))) + +(defun godoc--buffer-name (query) + "Determine the name to use for the output buffer of a given godoc QUERY." + (if godoc-reuse-buffer + "*godoc*" + (concat "*godoc " query "*"))) + +(defun godoc--get-buffer (query) + "Get an empty buffer for a godoc QUERY." + (let* ((buffer-name (godoc--buffer-name query)) + (buffer (get-buffer buffer-name))) + ;; Kill the existing buffer if it already exists. + (when buffer (kill-buffer buffer)) + (get-buffer-create buffer-name))) + +(defun godoc--buffer-sentinel (proc event) + "Sentinel function run when godoc command completes." + (with-current-buffer (process-buffer proc) + (cond ((string= event "finished\n") ;; Successful exit. + (goto-char (point-min)) + (godoc-mode) + (display-buffer (current-buffer) t)) + ((/= (process-exit-status proc) 0) ;; Error exit. + (let ((output (buffer-string))) + (kill-buffer (current-buffer)) + (message (concat "godoc: " output))))))) + +(define-derived-mode godoc-mode special-mode "Godoc" + "Major mode for showing Go documentation." + (view-mode-enter)) + +;;;###autoload +(defun godoc (query) + "Show Go documentation for QUERY, much like \\\\[man]." + (interactive (list (godoc--read-query))) + (go--godoc query godoc-command)) + +(defun go--godoc (query command) + (unless (string= query "") + (set-process-sentinel + (start-process-shell-command "godoc" (godoc--get-buffer query) + (concat command " " query)) + 'godoc--buffer-sentinel) + nil)) + +(defun godoc-at-point (point) + "Show Go documentation for the identifier at POINT. + +It uses `godoc-at-point-function' to look up the documentation." + (interactive "d") + (funcall godoc-at-point-function point)) + +(defun go-goto-imports () + "Move point to the block of imports. + +If using + + import ( + \"foo\" + \"bar\" + ) + +it will move point directly behind the last import. + +If using + + import \"foo\" + import \"bar\" + +it will move point to the next line after the last import. + +If no imports can be found, point will be moved after the package +declaration." + (interactive) + ;; FIXME if there's a block-commented import before the real + ;; imports, we'll jump to that one. + + ;; Generally, this function isn't very forgiving. it'll bark on + ;; extra whitespace. It works well for clean code. + (let ((old-point (point))) + (goto-char (point-min)) + (cond + ((re-search-forward "^import ()" nil t) + (backward-char 1) + 'block-empty) + ((re-search-forward "^import ([^)]+)" nil t) + (backward-char 2) + 'block) + ((re-search-forward "\\(^import \\([^\"]+ \\)?\"[^\"]+\"\n?\\)+" nil t) + 'single) + ((re-search-forward "^[[:space:]\n]*package .+?\n" nil t) + (message "No imports found, moving point after package declaration") + 'none) + (t + (goto-char old-point) + (message "No imports or package declaration found. Is this really a Go file?") + 'fail)))) + +(defun go-play-buffer () + "Like `go-play-region', but acts on the entire buffer." + (interactive) + (go-play-region (point-min) (point-max))) + +(defun go-play-region (start end) + "Send the region between START and END to the Playground. +If non-nil `go-play-browse-function' is called with the +Playground URL. + +By default this function will prompt to confirm you want to upload +code to the Playground. You can disable the confirmation by setting +`go-confirm-playground-uploads' to nil." + (interactive "r") + (if (and go-confirm-playground-uploads + (not (yes-or-no-p "Upload to public Go Playground? "))) + (message "Upload aborted") + (let* ((url-request-method "POST") + (url-request-extra-headers + '(("Content-Type" . "text/plain; charset=UTF-8"))) + (url-request-data + (encode-coding-string + (buffer-substring-no-properties start end) + 'utf-8)) + + (content-buf (url-retrieve + "https://play.golang.org/share" + (lambda (arg) + (cond + ((equal :error (car arg)) + (signal 'go-play-error (cdr arg))) + (t + (re-search-forward "\n\n") + (let ((url (format "https://play.golang.org/p/%s" + (buffer-substring (point) (point-max))))) + (when go-play-browse-function + (funcall go-play-browse-function url)))))))))))) + +;;;###autoload +(defun go-download-play (url) + "Download a paste from the playground and insert it in a Go buffer. +Tries to look for a URL at point." + (interactive (list (read-from-minibuffer "Playground URL: " (ffap-url-p (ffap-string-at-point 'url))))) + (with-current-buffer + (let ((url-request-method "GET") url-request-data url-request-extra-headers) + (url-retrieve-synchronously (concat url ".go"))) + (let ((buffer (generate-new-buffer (concat (car (last (split-string url "/"))) ".go")))) + (goto-char (point-min)) + (re-search-forward "\n\n") + (copy-to-buffer buffer (point) (point-max)) + (kill-buffer) + (with-current-buffer buffer + (go-mode) + (switch-to-buffer buffer))))) + +(defun go-propertize-syntax (start end) + (save-excursion + (goto-char start) + (while (search-forward "\\" end t) + (put-text-property (1- (point)) (point) 'syntax-table (if (= (char-after) ?`) '(1) '(9)))))) + +(defun go-import-add (arg import) + "Add a new IMPORT to the list of imports. + +When called with a prefix ARG asks for an alternative name to +import the package as. + +If no list exists yet, one will be created if possible. + +If an identical import has been commented, it will be +uncommented, otherwise a new import will be added." + + ;; - If there's a matching `// import "foo"`, uncomment it + ;; - If we're in an import() block and there's a matching `"foo"`, uncomment it + ;; - Otherwise add a new import, with the appropriate syntax + (interactive + (list + current-prefix-arg + (replace-regexp-in-string "^[\"']\\|[\"']$" "" (completing-read "Package: " (go-packages))))) + (save-excursion + (let (as line import-start) + (if arg + (setq as (read-from-minibuffer "Import as: "))) + (if as + (setq line (format "%s \"%s\"" as import)) + (setq line (format "\"%s\"" import))) + + (goto-char (point-min)) + (if (re-search-forward (concat "^[[:space:]]*//[[:space:]]*import " line "$") nil t) + (uncomment-region (line-beginning-position) (line-end-position)) + (cl-case (go-goto-imports) + (fail (message "Could not find a place to add import.")) + (block-empty + (insert "\n\t" line "\n")) + (block + (save-excursion + (re-search-backward "^import (") + (setq import-start (point))) + (if (re-search-backward (concat "^[[:space:]]*//[[:space:]]*" line "$") import-start t) + (uncomment-region (line-beginning-position) (line-end-position)) + (insert "\n\t" line))) + (single (insert "import " line "\n")) + (none (insert "\nimport (\n\t" line "\n)\n"))))))) + +(defun go-root-and-paths () + (let* ((output (process-lines go-command "env" "GOROOT" "GOPATH")) + (root (car output)) + (paths (split-string (cadr output) path-separator))) + (cons root paths))) + +(defun go--string-prefix-p (s1 s2 &optional ignore-case) + "Return non-nil if S1 is a prefix of S2. +If IGNORE-CASE is non-nil, the comparison is case-insensitive." + (eq t (compare-strings s1 nil nil + s2 0 (length s1) ignore-case))) + +(defun go--directory-dirs (dir) + "Recursively return all subdirectories in DIR." + (if (file-directory-p dir) + (let ((dir (directory-file-name dir)) + (dirs '()) + (files (directory-files dir nil nil t))) + (dolist (file files) + (unless (member file '("." "..")) + (let ((file (concat dir "/" file))) + (if (and (file-directory-p file) + (not (file-symlink-p file))) + (setq dirs (append (cons file + (go--directory-dirs file)) + dirs)))))) + dirs) + '())) + + +(defun go-packages () + (funcall go-packages-function)) + +(defun go-packages-native () + "Return a list of all installed Go packages." + (declare (obsolete "this function does not work well with modern versions of Go. You should use `go-packages-go-list' instead." "1.7.0")) + (sort + (delete-dups + (cl-mapcan + (lambda (topdir) + (let ((pkgdir (concat topdir "/pkg/"))) + (cl-mapcan (lambda (dir) + (mapcar (lambda (file) + (let ((sub (substring file (length pkgdir) -2))) + (unless (or (go--string-prefix-p "obj/" sub) (go--string-prefix-p "tool/" sub)) + (mapconcat #'identity (cdr (split-string sub "/")) "/")))) + (if (file-directory-p dir) + (directory-files dir t "\\.a$")))) + (if (file-directory-p pkgdir) + (go--directory-dirs pkgdir))))) + (go-root-and-paths))) + #'string<)) + +(defun go-packages-go-list () + "Return a list of all Go packages, using `go list'." + (process-lines go-command "list" "-e" "all")) + +(defun go-unused-imports-lines () + (reverse (remove nil + (mapcar + (lambda (line) + (when (string-match "^\\(.+\\):\\([[:digit:]]+\\):\\([[:digit:]]+\\): imported and not used: \".+\".*$" line) + (let ((error-file-name (match-string 1 line)) + (error-line-num (match-string 2 line))) + (if (string= (file-truename error-file-name) (file-truename buffer-file-name)) + (string-to-number error-line-num))))) + (split-string (shell-command-to-string + (concat go-command + (if (string-match "_test\\.go$" buffer-file-truename) + " test -c" + (concat " build -o " null-device)) + " -gcflags=-e" + " " + (shell-quote-argument (file-truename buffer-file-name)))) "\n"))))) + +(defun go-remove-unused-imports (arg) + "Remove all unused imports. +If ARG is non-nil, unused imports will be commented, otherwise +they will be removed completely." + (declare (obsolete "set `gofmt-command' to goimports instead, or use LSP and gopls's \"Organize Imports\" code action." "1.7.0")) + (interactive "P") + (save-excursion + (let ((cur-buffer (current-buffer)) flymake-state lines) + (when (boundp 'flymake-mode) + (setq flymake-state flymake-mode) + (flymake-mode -1)) + (save-some-buffers nil (lambda () (equal cur-buffer (current-buffer)))) + (if (buffer-modified-p) + (message "Cannot operate on unsaved buffer") + (setq lines (go-unused-imports-lines)) + (dolist (import lines) + (go--goto-line import) + (beginning-of-line) + (if arg + (comment-region (line-beginning-position) (line-end-position)) + (go--delete-whole-line))) + (message "Removed %d imports" (length lines))) + (if flymake-state (flymake-mode 1))))) + +(defun godef--find-file-line-column (specifier other-window) + "Given a file name in the format of `filename:line:column', +visit FILENAME and go to line LINE and column COLUMN." + (if (not (string-match "\\(.+\\):\\([0-9]+\\):\\([0-9]+\\)" specifier)) + ;; We've only been given a directory name + (funcall (if other-window #'find-file-other-window #'find-file) specifier) + (let ((filename (match-string 1 specifier)) + (line (string-to-number (match-string 2 specifier))) + (column (string-to-number (match-string 3 specifier)))) + (funcall (if other-window #'find-file-other-window #'find-file) filename) + (go--goto-line line) + (beginning-of-line) + (forward-char (1- column)) + (if (buffer-modified-p) + (message "Buffer is modified, file position might not have been correct"))))) + +(defun godef--call (point) + "Call godef, acquiring definition position and expression +description at POINT." + (if (not (buffer-file-name (go--coverage-origin-buffer))) + (error "Cannot use godef on a buffer without a file name") + (let ((outbuf (generate-new-buffer "*godef*")) + (coding-system-for-read 'utf-8) + (coding-system-for-write 'utf-8)) + (prog2 + (call-process-region (point-min) + (point-max) + godef-command + nil + outbuf + nil + "-i" + "-t" + "-f" + (file-truename (buffer-file-name (go--coverage-origin-buffer))) + "-o" + ;; Emacs point and byte positions are 1-indexed. + (number-to-string (1- (position-bytes point)))) + (with-current-buffer outbuf + (split-string (buffer-substring-no-properties (point-min) (point-max)) "\n")) + (kill-buffer outbuf))))) + +(defun godef--successful-p (output) + (not (or (string= "-" output) + (string= "godef: no identifier found" output) + (string= "godef: no object" output) + (go--string-prefix-p "godef: no declaration found for " output) + (go--string-prefix-p "error finding import path for " output)))) + +(defun godef--error (output) + (cond + ((godef--successful-p output) + nil) + ((string= "-" output) + "godef: expression is not defined anywhere") + (t + output))) + +(defun godef-describe (point) + "Describe the expression at POINT." + (interactive "d") + (condition-case nil + (let ((description (cdr (butlast (godef--call point) 1)))) + (if (not description) + (message "No description found for expression at point") + (message "%s" (mapconcat #'identity description "\n")))) + (file-error (message "Could not run godef binary")))) + +(defun godef-jump (point &optional other-window) + "Jump to the definition of the expression at POINT." + (interactive "d") + (condition-case nil + (let ((file (car (godef--call point)))) + (if (not (godef--successful-p file)) + (message "%s" (godef--error file)) + (push-mark) + ;; TODO: Integrate this facility with XRef. + (xref-push-marker-stack) + (godef--find-file-line-column file other-window))) + (file-error (message "Could not run godef binary")))) + +(defun godef-jump-other-window (point) + (interactive "d") + (godef-jump point t)) + +(defun go--goto-line (line) + (goto-char (point-min)) + (forward-line (1- line))) + +(defun go--line-column-to-point (line column) + (save-excursion + (go--goto-line line) + (forward-char (1- column)) + (point))) + +(cl-defstruct go--covered + start-line start-column end-line end-column covered count) + +(defun go--coverage-file () + "Return the coverage file to use, either by reading it from the +current coverage buffer or by prompting for it." + (if (boundp 'go--coverage-current-file-name) + go--coverage-current-file-name + (read-file-name "Coverage file: " nil nil t))) + +(defun go--coverage-origin-buffer () + "Return the buffer to base the coverage on." + (or (buffer-base-buffer) (current-buffer))) + +(defun go--coverage-face (count divisor) + "Return the intensity face for COUNT when using DIVISOR +to scale it to a range [0,10]. + +DIVISOR scales the absolute cover count to values from 0 to 10. +For DIVISOR = 0 the count will always translate to 8." + (let* ((norm (cond + ((= count 0) + -0.1) ;; Uncovered code, set to -0.1 so n becomes 0. + ((= divisor 0) + 0.8) ;; covermode=set, set to 0.8 so n becomes 8. + (t + (/ (log count) divisor)))) + (n (1+ (floor (* norm 9))))) ;; Convert normalized count [0,1] to intensity [0,10] + (concat "go-coverage-" (number-to-string n)))) + +(defun go--coverage-make-overlay (range divisor) + "Create a coverage overlay for a RANGE of covered/uncovered code. +Use DIVISOR to scale absolute counts to a [0,10] scale." + (let* ((count (go--covered-count range)) + (face (go--coverage-face count divisor)) + (ov (make-overlay (go--line-column-to-point (go--covered-start-line range) + (go--covered-start-column range)) + (go--line-column-to-point (go--covered-end-line range) + (go--covered-end-column range))))) + + (overlay-put ov 'face face) + (overlay-put ov 'help-echo (format "Count: %d" count)))) + +(defun go--coverage-clear-overlays () + "Remove existing overlays and put a single untracked overlay +over the entire buffer." + (remove-overlays) + (overlay-put (make-overlay (point-min) (point-max)) + 'face + 'go-coverage-untracked)) + +(defun go--coverage-parse-file (coverage-file file-name) + "Parse COVERAGE-FILE and extract coverage information and +divisor for FILE-NAME." + (let (ranges + (max-count 0)) + (with-temp-buffer + (insert-file-contents coverage-file) + (go--goto-line 2) ;; Skip over mode + (while (not (eobp)) + (let* ((parts (split-string (buffer-substring (line-beginning-position) (line-end-position)) ":")) + (file (car parts)) + (rest (split-string (nth 1 parts) "[., ]"))) + + (cl-destructuring-bind + (start-line start-column end-line end-column num count) + (mapcar #'string-to-number rest) + + (when (string= (file-name-nondirectory file) file-name) + (if (> count max-count) + (setq max-count count)) + (push (make-go--covered :start-line start-line + :start-column start-column + :end-line end-line + :end-column end-column + :covered (/= count 0) + :count count) + ranges))) + + (forward-line))) + + (list ranges (if (> max-count 0) (log max-count) 0))))) + +(defun go-coverage (&optional coverage-file) + "Open a clone of the current buffer and overlay it with +coverage information gathered via go test -coverprofile=COVERAGE-FILE. + +If COVERAGE-FILE is nil, it will either be inferred from the +current buffer if it's already a coverage buffer, or be prompted +for." + (interactive) + (let* ((cur-buffer (current-buffer)) + (origin-buffer (go--coverage-origin-buffer)) + (gocov-buffer-name (concat (buffer-name origin-buffer) "")) + (coverage-file (or coverage-file (go--coverage-file))) + (ranges-and-divisor (go--coverage-parse-file + coverage-file + (file-name-nondirectory (buffer-file-name origin-buffer)))) + (cov-mtime (nth 5 (file-attributes coverage-file))) + (cur-mtime (nth 5 (file-attributes (buffer-file-name origin-buffer))))) + + (if (< (float-time cov-mtime) (float-time cur-mtime)) + (message "Coverage file is older than the source file.")) + + (with-current-buffer (or (get-buffer gocov-buffer-name) + (make-indirect-buffer origin-buffer gocov-buffer-name t)) + (set (make-local-variable 'go--coverage-current-file-name) coverage-file) + + (save-excursion + (go--coverage-clear-overlays) + (dolist (range (car ranges-and-divisor)) + (go--coverage-make-overlay range (cadr ranges-and-divisor)))) + + (if (not (eq cur-buffer (current-buffer))) + (display-buffer (current-buffer) `(,go-coverage-display-buffer-func)))))) + +(defun go-goto-function (&optional arg) + "Go to the function definition (named or anonymous) surrounding point. + +If we are on a docstring, follow the docstring down. +If no function is found, assume that we are at the top of a file +and search forward instead. + +If point is looking at the func keyword of an anonymous function, +go to the surrounding function. + +If ARG is non-nil, anonymous functions are ignored." + (interactive "P") + (let ((p (point))) + (cond + ((save-excursion + (beginning-of-line) + (looking-at "^//")) + ;; In case we are looking at the docstring, move on forward until we are + ;; not anymore + (beginning-of-line) + (while (looking-at "^//") + (forward-line 1)) + ;; If we are still not looking at a function, retry by calling self again. + (when (not (looking-at "\\")) + (go-goto-function arg))) + + ;; If we're already looking at an anonymous func, look for the + ;; surrounding function. + ((and (looking-at "\\") + (not (looking-at "^func\\>"))) + (re-search-backward "\\" nil t)) + + ((not (looking-at "\\")) + ;; If point is on the "func" keyword, step back a word and retry + (if (string= (symbol-name (symbol-at-point)) "func") + (backward-word) + ;; If we are not looking at the beginning of a function line, do a regexp + ;; search backwards + (re-search-backward "\\" nil t)) + + ;; If nothing is found, assume that we are at the top of the file and + ;; should search forward instead. + (when (not (looking-at "\\")) + (re-search-forward "\\" nil t) + (go--forward-word -1)) + + ;; If we have landed at an anonymous function, it is possible that we + ;; were not inside it but below it. If we were not inside it, we should + ;; go to the containing function. + (while (and (not (go--in-function-p p)) + (not (looking-at "^func\\>"))) + (go-goto-function arg))))) + + (cond + ((go-in-comment-p) + ;; If we are still in a comment, redo the call so that we get out of it. + (go-goto-function arg)) + + ((and (looking-at "\\")) + (go-goto-function)) + (let ((start (point))) + (go--goto-opening-curly-brace) + + (unless (looking-at "{") + (error "Expected to be looking at opening curly brace")) + (forward-list 1) + (and (>= compare-point start) + (<= compare-point (point)))))) + +(defun go-goto-function-name (&optional arg) + "Go to the name of the current function. + +If the function is a test, place point after 'Test'. +If the function is anonymous, place point on the 'func' keyword. + +If ARG is non-nil, anonymous functions are skipped." + (interactive "P") + (when (not (looking-at "\\")) + (go-goto-function arg)) + ;; If we are looking at func( we are on an anonymous function and + ;; nothing else should be done. + (when (not (looking-at "\\ b" st) + st) + "Syntax table for `go-dot-mod-mode'.") + +(defconst go-dot-mod-mode-keywords + '("module" "go" "toolchain" "require" "exclude" "replace" "retract") + "All keywords for go.mod files. Used for font locking.") + +(defgroup go-dot-mod nil + "Options specific to `go-dot-mod-mode`." + :group 'go) + +(defface go-dot-mod-module-name '((t :inherit default)) + "Face for module name in \"require\" list." + :group 'go-dot-mod) + +(defface go-dot-mod-module-version '((t :inherit default)) + "Face for module version in \"require\" list." + :group 'go-dot-mod) + +(defface go-dot-mod-module-semver '((t :inherit go-dot-mod-module-version)) + "Face for module semver in \"require\" list." + :group 'go-dot-mod) + + +(defvar go-dot-mod-font-lock-keywords + `( + (,(concat "^\\s-*\\(" (regexp-opt go-dot-mod-mode-keywords t) "\\)\\s-") 1 font-lock-keyword-face) + ("\\(?:^\\|=>\\)\\s-*\\([^[:space:]\n()]+\\)\\(?:\\s-+\\(v[0-9]+\\.[0-9]+\\.[0-9]+\\)\\([^[:space:]\n]*\\)\\)?" (1 'go-dot-mod-module-name) (2 'go-dot-mod-module-semver nil t) (3 'go-dot-mod-module-version nil t))) + "Keyword highlighting specification for `go-dot-mod-mode'.") + +;;;###autoload +(define-derived-mode go-dot-mod-mode fundamental-mode "Go Mod" + "A major mode for editing go.mod files." + :syntax-table go-dot-mod-mode-syntax-table + (set (make-local-variable 'comment-start) "// ") + (set (make-local-variable 'comment-end) "") + (set (make-local-variable 'comment-use-syntax) t) + (set (make-local-variable 'comment-start-skip) "\\(//+\\)\\s *") + + (set (make-local-variable 'font-lock-defaults) + '(go-dot-mod-font-lock-keywords)) + (set (make-local-variable 'indent-line-function) 'go-mode-indent-line) + + ;; Go style + (setq indent-tabs-mode t) + + ;; we borrow the go-mode-indent function so we need this buffer cache + (set (make-local-variable 'go-dangling-cache) (make-hash-table :test 'eql)) + (add-hook 'before-change-functions #'go--reset-dangling-cache-before-change t t)) + +;;;###autoload +(add-to-list 'auto-mode-alist '("go\\.mod\\'" . go-dot-mod-mode)) + +(defconst go-dot-work-mode-keywords + '("go" "toolchain" "use" "replace") + "All keywords for go.work files. Used for font locking.") + +;;;###autoload +(define-derived-mode go-dot-work-mode fundamental-mode "Go Work" + "A major mode for editor go.work files." + :syntax-table go-dot-mod-mode-syntax-table + (set (make-local-variable 'comment-start) "// ") + (set (make-local-variable 'comment-end) "") + (set (make-local-variable 'comment-use-syntax) t) + (set (make-local-variable 'comment-start-skip) "\\(//+\\)\\s *") + + (set (make-local-variable 'font-lock-defaults) + '(go-dot-work-mode-keywords)) + (set (make-local-variable 'indent-line-function) 'go-mode-indent-line) + + ;; Go style + (setq indent-tabs-mode t) + + ;; we borrow the go-mode-indent function so we need this buffer cache + (set (make-local-variable 'go-dangling-cache) (make-hash-table :test 'eql)) + (add-hook 'before-change-functions #'go--reset-dangling-cache-before-change t t)) + +;;;###autoload +(add-to-list 'auto-mode-alist '("go\\.work\\'" . go-dot-work-mode)) + +;; The following functions were copied (and modified) from rust-mode.el. +;; +;; Copyright (c) 2015 The Rust Project Developers +;; +;; Permission is hereby granted, free of charge, to any +;; person obtaining a copy of this software and associated +;; documentation files (the "Software"), to deal in the +;; Software without restriction, including without +;; limitation the rights to use, copy, modify, merge, +;; publish, distribute, sublicense, and/or sell copies of +;; the Software, and to permit persons to whom the Software +;; is furnished to do so, subject to the following +;; conditions: +;; +;; The above copyright notice and this permission notice +;; shall be included in all copies or substantial portions +;; of the Software. + +(defun go--fill-prefix-for-comment-start (line-start) + "Determine what to use for `fill-prefix' based on the text at LINE-START." + (let ((result + ;; Replace /* with same number of spaces + (replace-regexp-in-string + "\\(?:/\\*+?\\)[!*]?" + (lambda (s) + (let ((offset (if (eq t + (compare-strings "/*" nil nil + s + (- (length s) 2) + (length s))) + 1 2))) + (make-string (1+ (- (length s) offset)) ?\x20))) + line-start))) + ;; Make sure we've got at least one space at the end + (if (not (= (aref result (- (length result) 1)) ?\x20)) + (setq result (concat result " "))) + result)) + +(defun go--in-comment-paragraph (body) + ;; We might move the point to fill the next comment, but we don't want it + ;; seeming to jump around on the user + (save-excursion + ;; If we're outside of a comment, with only whitespace and then a comment + ;; in front, jump to the comment and prepare to fill it. + (when (not (go-in-comment-p)) + (beginning-of-line) + (when (looking-at (concat "[[:space:]\n]*" comment-start-skip)) + (goto-char (match-end 0)))) + + ;; If we're at the beginning of a comment paragraph with nothing but + ;; whitespace til the next line, jump to the next line so that we use the + ;; existing prefix to figure out what the new prefix should be, rather than + ;; inferring it from the comment start. + (while (save-excursion + (end-of-line) + (and (go-in-comment-p) + (save-excursion + (beginning-of-line) + (looking-at paragraph-start)) + (looking-at "[[:space:]]*$") + (nth 4 (syntax-ppss (line-beginning-position 2))))) + (goto-char (line-beginning-position 2))) + + ;; If we're on the last line of a multiline-style comment that started + ;; above, back up one line so we don't mistake the * of the */ that ends + ;; the comment for a prefix. + (when (save-excursion + (and (nth 4 (syntax-ppss (line-beginning-position 1))) + (looking-at "[[:space:]]*\\*/"))) + (goto-char (line-end-position 0))) + (funcall body))) + +(defun go--with-comment-fill-prefix (body) + (let* + ((line-string (buffer-substring-no-properties + (line-beginning-position) (line-end-position))) + (line-comment-start + (when (go-in-comment-p) + (cond + ;; If we're inside the comment and see a * prefix, use it + ((string-match "^\\([[:space:]]*\\*+[[:space:]]*\\)" + line-string) + (match-string 1 line-string)) + ;; If we're at the start of a comment, figure out what prefix + ;; to use for the subsequent lines after it + ((string-match (concat "[[:space:]]*" comment-start-skip) line-string) + (go--fill-prefix-for-comment-start + (match-string 0 line-string)))))) + (fill-prefix + (or line-comment-start + fill-prefix))) + (funcall body))) + +(defun go--find-fill-prefix () + (go--in-comment-paragraph + (lambda () + (go--with-comment-fill-prefix + (lambda () + fill-prefix))))) + +(defun go-fill-paragraph (&rest args) + "Special wrapping for `fill-paragraph'. +This handles multi-line comments with a * prefix on each line." + (go--in-comment-paragraph + (lambda () + (go--with-comment-fill-prefix + (lambda () + (let + ((fill-paragraph-function + (if (not (eq fill-paragraph-function 'go-fill-paragraph)) + fill-paragraph-function)) + (fill-paragraph-handle-comment t)) + (apply 'fill-paragraph args) + t)))))) + +(defun go--do-auto-fill (&rest args) + "Special wrapping for `do-auto-fill'. +This handles multi-line comments with a * prefix on each line." + (go--with-comment-fill-prefix + (lambda () + (apply 'do-auto-fill args) + t))) + +(defun go--fill-forward-paragraph (arg) + ;; This is to work around some funny behavior when a paragraph separator is + ;; at the very top of the file and there is a fill prefix. + (let ((fill-prefix nil)) (forward-paragraph arg))) + +(defun go--comment-indent-new-line (&optional arg) + (go--with-comment-fill-prefix + (lambda () (comment-indent-new-line arg)))) + + + +(provide 'go-mode) + +;;; go-mode.el ends here diff --git a/.emacs.d/lisp/jai-mode.el b/.emacs.d/lisp/jai-mode.el new file mode 100755 index 0000000..8bba83f --- /dev/null +++ b/.emacs.d/lisp/jai-mode.el @@ -0,0 +1,233 @@ +;;; jai-mode.el --- Major mode for JAI -*- lexical-binding: t; -*- + +;; Copyright (C) 2015-2023 Kristoffer Grönlund + +;; Author: Kristoffer Grönlund +;; Maintainer: Kristoffer Grönlund +;; URL: https://github.com/krig/jai-mode +;; Version: 0.0.1 +;; Package-Requires: ((emacs "26.1")) +;; Keywords: languages + +;; This file is not part of GNU Emacs. + +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: +;; +;; Major mdoe for JAI +;; + +;;; Code: + +(require 'rx) +(require 'js) +(require 'compile) + +(defconst jai-mode-syntax-table + (let ((table (make-syntax-table))) + (modify-syntax-entry ?\" "\"" table) + (modify-syntax-entry ?\\ "\\" table) + + ;; additional symbols + (modify-syntax-entry ?_ "w" table) + + (modify-syntax-entry ?' "." table) + (modify-syntax-entry ?: "." table) + (modify-syntax-entry ?+ "." table) + (modify-syntax-entry ?- "." table) + (modify-syntax-entry ?% "." table) + (modify-syntax-entry ?& "." table) + (modify-syntax-entry ?| "." table) + (modify-syntax-entry ?^ "." table) + (modify-syntax-entry ?! "." table) + (modify-syntax-entry ?= "." table) + (modify-syntax-entry ?< "." table) + (modify-syntax-entry ?> "." table) + (modify-syntax-entry ?? "." table) + + ;; Modify some syntax entries to allow nested block comments + (modify-syntax-entry ?/ ". 124b" table) + (modify-syntax-entry ?* ". 23n" table) + (modify-syntax-entry ?\n "> b" table) + (modify-syntax-entry ?\^m "> b" table) + + table)) + +(defconst jai-builtins + '("it" "it_index")) + +(defconst jai-keywords + '("if" "ifx" "else" "then" "while" "for" "switch" "case" "struct" "enum" + "return" "remove" "continue" "break" "defer" "inline" "no_inline" + "using" "code_of" "initializer_of" "size_of" "type_of" "cast" "type_info" + "null" "true" "false" "xx" "context" "operator" "push_context" "is_constant" + "enum_flags" "union" "interface")) + +(defconst jai-typenames + '("int" "u64" "u32" "u16" "u8" + "s64" "s32" "s16" "s8" "float" + "float32" "float64" "string" + "bool")) + +(defun jai-wrap-word-rx (s) + (concat "\\<" s "\\>")) + +(defun jai-keywords-rx (keywords) + "build keyword regexp" + (jai-wrap-word-rx (regexp-opt keywords t))) + +(defconst jai-hat-type-rx (rx (group (and "^" (1+ word))))) +(defconst jai-dollar-type-rx (rx (group "$" (or (1+ word) (opt "$"))))) +(defconst jai-number-rx + (rx (and + symbol-start + (or (and (+ digit) (opt (and (any "eE") (opt (any "-+")) (+ digit)))) + (and "0" (any "xX") (+ hex-digit))) + (opt (and (any "_" "A-Z" "a-z") (* (any "_" "A-Z" "a-z" "0-9")))) + symbol-end))) + +(defconst jai-font-lock-defaults + `(;; Keywords + (,(jai-keywords-rx jai-keywords) 1 font-lock-keyword-face) + + ;; single quote characters + ("\\('[[:word:]]\\)\\>" 1 font-lock-constant-face) + + ;; Variables + (,(jai-keywords-rx jai-builtins) 1 font-lock-variable-name-face) + + ;; Hash directives + ("#\\w+" . font-lock-preprocessor-face) + + ;; At notes + ("@\\w+" . font-lock-preprocessor-face) + + ;; Strings + ("\\\".*\\\"" . font-lock-string-face) + + ;; Numbers + (,(jai-wrap-word-rx jai-number-rx) . font-lock-constant-face) + + ;; Types + (,(jai-keywords-rx jai-typenames) 1 font-lock-type-face) + (,jai-hat-type-rx 1 font-lock-type-face) + (,jai-dollar-type-rx 1 font-lock-type-face) + + ("---" . font-lock-constant-face))) + +;; add setq-local for older emacs versions +(unless (fboundp 'setq-local) + (defmacro setq-local (var val) + `(set (make-local-variable ',var) ,val))) + +(defconst jai--defun-rx "\(.*\).*\{") + +(defmacro jai-paren-level () + `(car (syntax-ppss))) + +(defun jai-line-is-defun () + "return t if current line begins a procedure" + (interactive) + (save-excursion + (beginning-of-line) + (let (found) + (while (and (not (eolp)) (not found)) + (if (looking-at jai--defun-rx) + (setq found t) + (forward-char 1))) + found))) + +(defun jai-beginning-of-defun () + "Go to line on which current function starts." + (interactive) + (let ((orig-level (jai-paren-level))) + (while (and + (not (jai-line-is-defun)) + (not (bobp)) + (> orig-level 0)) + (setq orig-level (jai-paren-level)) + (while (>= (jai-paren-level) orig-level) + (skip-chars-backward "^{") + (backward-char)))) + (when (jai-line-is-defun) + (beginning-of-line))) + +(defun jai-end-of-defun () + "Go to line on which current function ends." + (interactive) + (let ((orig-level (jai-paren-level))) + (when (> orig-level 0) + (jai-beginning-of-defun) + (end-of-line) + (setq orig-level (jai-paren-level)) + (skip-chars-forward "^}") + (while (>= (jai-paren-level) orig-level) + (skip-chars-forward "^}") + (forward-char))))) + +(defalias 'jai-parent-mode + (if (fboundp 'prog-mode) 'prog-mode 'fundamental-mode)) + +;; imenu hookup +(add-hook 'jai-mode-hook + (lambda () + (setq imenu-generic-expression + '(("type" "^\\(.*:*.*\\) : " 1) + ("function" "^\\(.*\\) :: " 1) + ("struct" "^\\(.*\\) *:: *\\(struct\\)\\(.*\\){" 1))))) + +;; NOTE: taken from the scala-indent package and modified for Jai. +;; Still uses the js-indent-line as a base, which will have to be +;; replaced when the language is more mature. +(defun jai--indent-on-parentheses () + (when (and (= (char-syntax (char-before)) ?\)) + (= (save-excursion (back-to-indentation) (point)) (1- (point)))) + (js-indent-line))) + +(defun jai--add-self-insert-hooks () + (add-hook 'post-self-insert-hook + 'jai--indent-on-parentheses)) + +;;;###autoload +(define-derived-mode jai-mode jai-parent-mode "Jai" + :syntax-table jai-mode-syntax-table + :group 'jai + (setq bidi-paragraph-direction 'left-to-right) + (setq-local require-final-newline mode-require-final-newline) + (setq-local parse-sexp-ignore-comments t) + (setq-local comment-start-skip "\\(//+\\|/\\*+\\)\\s *") + (setq-local comment-start "//") + (setq-local block-comment-start "/*") + (setq-local block-comment-end "*/") + (setq-local indent-line-function 'js-indent-line) + (setq-local font-lock-defaults '(jai-font-lock-defaults)) + (setq-local beginning-of-defun-function 'jai-beginning-of-defun) + (setq-local end-of-defun-function 'jai-end-of-defun) + + ;; add indent functionality to some characters + (jai--add-self-insert-hooks) + + (font-lock-ensure)) + +;;;###autoload +(add-to-list 'auto-mode-alist '("\\.jai\\'" . jai-mode)) + +(defconst jai--error-regexp + "^\\([^ \n:]+.*\.jai\\):\\([0-9]+\\),\\([0-9]+\\):") +(push `(jai ,jai--error-regexp 1 2 3 2) compilation-error-regexp-alist-alist) +(push 'jai compilation-error-regexp-alist) + +(provide 'jai-mode) +;;; jai-mode.el ends here diff --git a/.emacs.d/lisp/stupid-indent-mode.el b/.emacs.d/lisp/stupid-indent-mode.el new file mode 100755 index 0000000..ea3b23f --- /dev/null +++ b/.emacs.d/lisp/stupid-indent-mode.el @@ -0,0 +1,145 @@ +;;; stupid-indent-mode.el --- Plain stupid indentation minor mode + +;; Copyright (C) 2013 Mihai Bazon + +;; Author: Mihai Bazon +;; Keywords: + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; Dumb indentation mode is appropriate for editing buffers that Emacs +;; does not fully understand syntactically, such as HTML/PHP +;; (typically involving multiple languages with different indentation +;; rules in the same buffer). The default indentation level is 2 +;; (customize `stupid-indent-level'). +;; +;; Key bindings: +;; +;; TAB -- indent current line by the value of `stupid-indent-level' +;; S-TAB -- outdent current line +;; C-c TAB -- indent region +;; C-c S-TAB -- outdent region +;; RET -- newline and indent +;; C-c C-TAB -- indent according to mode + +;;; Code: + +(defcustom stupid-indent-level 2 + "Indentation level for stupid-indent-mode") + +(defun %stupid-force-indent-line () + (let (col) + (save-excursion + (back-to-indentation) + (setq col (+ (current-column) stupid-indent-level)) + (indent-line-to col)) + (when (< (current-column) col) + (back-to-indentation)))) + +(defun stupid-indent-line () + (interactive) + (let ((bt (save-excursion + (back-to-indentation) + (current-column)))) + (cond + ((< (current-column) bt) + (back-to-indentation)) + ((looking-at "\\s-*\n") + (let ((col (save-excursion + (previous-line) + (back-to-indentation) + (current-column)))) + (if (< (current-column) col) + (indent-line-to col) + (%stupid-force-indent-line)))) + (t + (%stupid-force-indent-line))))) + +(defun stupid-outdent-line () + (interactive) + (let (col) + (save-excursion + (back-to-indentation) + (setq col (- (current-column) stupid-indent-level)) + (when (>= col 0) + (indent-line-to col))))) + +(defun stupid-indent-region (start stop) + (interactive "r") + (setq stop (copy-marker stop)) + (goto-char start) + (while (< (point) stop) + (unless (and (bolp) (eolp)) + (%stupid-force-indent-line)) + (forward-line 1))) + +(defun stupid-outdent-region (start stop) + (interactive "r") + (setq stop (copy-marker stop)) + (goto-char start) + (while (< (point) stop) + (unless (and (bolp) (eolp)) + (stupid-outdent-line)) + (forward-line 1))) + +(defun stupid-indent () + (interactive) + (if (use-region-p) + (save-excursion + (stupid-indent-region (region-beginning) (region-end)) + (setq deactivate-mark nil)) + (stupid-indent-line))) + +(defun stupid-outdent () + (interactive) + (if (use-region-p) + (save-excursion + (stupid-outdent-region (region-beginning) (region-end)) + (setq deactivate-mark nil)) + (stupid-outdent-line))) + +(defun stupid-indent-newline () + (interactive) + (when (< (point) + (save-excursion + (back-to-indentation) + (point))) + (back-to-indentation)) + (let ((col (save-excursion + (back-to-indentation) + (current-column)))) + (newline) + (indent-to-column col))) + +(define-minor-mode stupid-indent-mode + "Stupid indent mode is just plain stupid." + :init-value nil + :lighter "/SI" + :global nil + :keymap `( + (,(kbd "") . stupid-indent) + (,(kbd "") . stupid-outdent) + (,(kbd "C-c ") . stupid-indent-region) + (,(kbd "C-c ") . stupid-outdent-region) + (,(kbd "") . stupid-indent-newline) + (,(kbd "C-c C-") . indent-according-to-mode) + ) + (when stupid-indent-mode + (add-hook 'write-contents-functions + 'delete-trailing-whitespace))) + +(provide 'stupid-indent-mode) +;;; stupid-indent-mode.el ends here diff --git a/.emacs.d/lisp/web-mode.el b/.emacs.d/lisp/web-mode.el new file mode 100755 index 0000000..5f3df02 --- /dev/null +++ b/.emacs.d/lisp/web-mode.el @@ -0,0 +1,15046 @@ +;;; web-mode.el --- major mode for editing web templates -*- coding: utf-8; lexical-binding: t; -*- + +;; Copyright 2011-2023 François-Xavier Bois + +;; Version: 17.3.15 +;; Author: François-Xavier Bois +;; Maintainer: François-Xavier Bois +;; Package-Requires: ((emacs "23.1")) +;; URL: https://web-mode.org +;; Repository: http://github.com/fxbois/web-mode +;; Created: July 2011 +;; Keywords: languages +;; License: GNU General Public License >= 3 +;; Distribution: This file is not part of Emacs + +;;; Commentary: + +;;============================================================================== +;; WEB-MODE is sponsored by ** Kernix ** Best Digital Agency & Data Lab (Paris) +;;============================================================================== + +;;; Code: + +;;---- CONSTS ------------------------------------------------------------------ + +(defconst web-mode-version "17.3.15" + "Web Mode version.") + +;;---- GROUPS ------------------------------------------------------------------ + +(defgroup web-mode nil + "Major mode for editing web templates" + :group 'languages + :prefix "web-" + :link '(url-link :tag "Site" "https://web-mode.org") + :link '(url-link :tag "Repository" "https://github.com/fxbois/web-mode")) + +(defgroup web-mode-faces nil + "Faces for syntax highlighting." + :group 'web-mode + :group 'faces) + +;;---- CUSTOMS ----------------------------------------------------------------- + +(defcustom web-mode-block-padding 0 + "Multi-line block (php, ruby, java, python, asp, etc.) left padding. + -1 to have to code aligned on the column 0." + :type '(choice (integer :tags "Number of spaces") + (const :tags "No indent" nil)) + :group 'web-mode) + +(defcustom web-mode-part-padding 1 + "Part elements (script, style) left padding." + :type '(choice (integer :tags "Number of spaces") + (const :tags "No indent" nil)) + :group 'web-mode) + +(defcustom web-mode-script-padding web-mode-part-padding + "Script element left padding." + :type '(choice (integer :tags "Number of spaces") + (const :tags "No indent" nil)) + :group 'web-mode) + +(defcustom web-mode-style-padding web-mode-part-padding + "Style element left padding." + :type '(choice (integer :tags "Number of spaces") + (const :tags "No indent" nil)) + :group 'web-mode) + +(defcustom web-mode-attr-indent-offset nil + "Html attribute indentation level." + :type '(choice (integer :tags "Number of spaces") + (const :tags "Default" nil)) + :safe #'(lambda (v) (or (integerp v) (booleanp v))) + :group 'web-mode) + +(defcustom web-mode-attr-value-indent-offset nil + "Html attribute value indentation level." + :type '(choice (integer :tags "Number of spaces") + (const :tags "Default" nil)) + :safe #'(lambda (v) (or (integerp v) (booleanp v))) + :group 'web-mode) + +(defcustom web-mode-markup-indent-offset + (if (and (boundp 'standard-indent) standard-indent) standard-indent 2) + "Html indentation level." + :type 'integer + :safe #'integerp + :group 'web-mode) + +(defcustom web-mode-markup-comment-indent-offset + 5 + "Html comment indentation level." + :type 'integer + :safe #'integerp + :group 'web-mode) + +(defcustom web-mode-css-indent-offset + (if (and (boundp 'standard-indent) standard-indent) standard-indent 2) + "CSS indentation level." + :type 'integer + :safe #'integerp + :group 'web-mode) + +(defcustom web-mode-code-indent-offset + (if (and (boundp 'standard-indent) standard-indent) standard-indent 2) + "Code (javascript, php, etc.) indentation level." + :type 'integer + :safe #'integerp + :group 'web-mode) + +(defcustom web-mode-sql-indent-offset 4 + "Sql (inside strings) indentation level." + :type 'integer + :safe #'integerp + :group 'web-mode) + +(defcustom web-mode-enable-css-colorization (display-graphic-p) + "In a CSS part, set background according to the color: #xxx, rgb(x,x,x)." + :type 'boolean + :group 'web-mode) + +(defcustom web-mode-enable-comment-interpolation nil + "Enable highlight of keywords like FIXME, TODO, etc. in comments." + :type 'boolean + :group 'web-mode) + +(defcustom web-mode-enable-comment-annotation nil + "Enable annotation in comments (jsdoc, phpdoc, etc.)." + :type 'boolean + :group 'web-mode) + +(defcustom web-mode-enable-auto-indentation (display-graphic-p) + "Auto-indentation." + :type 'boolean + :group 'web-mode) + +(defcustom web-mode-enable-auto-closing (display-graphic-p) + "Auto-closing." + :type 'boolean + :group 'web-mode) + +(defcustom web-mode-enable-auto-pairing (display-graphic-p) + "Auto-pairing." + :type 'boolean + :group 'web-mode) + +(defcustom web-mode-enable-auto-opening (display-graphic-p) + "Html element auto-opening." + :type 'boolean + :group 'web-mode) + +(defcustom web-mode-enable-auto-quoting (display-graphic-p) + "Add double quotes after the character = in a tag." + :type 'boolean + :group 'web-mode) + +(defcustom web-mode-enable-auto-expanding nil + "e.g. s/ expands to |." + :type 'boolean + :group 'web-mode) + +(defcustom web-mode-enable-curly-brace-indentation nil + "Indent lines beginning with {." + :type 'boolean + :group 'web-mode) + +(defcustom web-mode-enable-control-block-indentation t + "Control blocks increase indentation." + :type 'boolean + :group 'web-mode) + +(defcustom web-mode-enable-current-element-highlight nil + "Enable current element highlight." + :type 'boolean + :group 'web-mode) + +(defcustom web-mode-enable-current-column-highlight nil + "Show column for current element." + :type 'boolean + :group 'web-mode) + +(defcustom web-mode-enable-whitespace-fontification nil + "Enable whitespaces." + :type 'boolean + :group 'web-mode) + +(defcustom web-mode-enable-html-entities-fontification nil + "Enable html entities fontification." + :type 'boolean + :group 'web-mode) + +(defcustom web-mode-enable-block-face nil + "Enable block face (useful for setting a background for example). +See web-mode-block-face." + :type 'boolean + :group 'web-mode) + +(defcustom web-mode-enable-part-face nil + "Enable part face (useful for setting background of ") + (cond + ((string-match-p " lang[ ]*=[ ]*[\"']stylus" style) + (setq element-content-type "stylus")) + ((string-match-p " lang[ ]*=[ ]*[\"']sass" style) + (setq element-content-type "sass")) + (t + (setq element-content-type "css")) + ) ;cond + ) ;let + ) ;style + ((string= tname "script") + (let (script) + (setq script (buffer-substring-no-properties tbeg tend) + part-close-tag "") + (cond + ((string-match-p " type[ ]*=[ ]*[\"']text/\\(jsx\\|babel\\)" script) + (setq element-content-type "jsx")) + ((string-match-p " type[ ]*=[ ]*[\"']text/\\(markdown\\|template\\)" script) + (setq element-content-type "markdown")) + ((string-match-p " type[ ]*=[ ]*[\"']text/ruby" script) + (setq element-content-type "ruby")) + ((seq-some (lambda (x) + (string-match-p (concat "type[ ]*=[ ]*[\"']" x) script)) + web-mode-script-template-types) + (setq element-content-type "html" + part-close-tag nil)) + ((string-match-p " type[ ]*=[ ]*[\"']application/\\(ld\\+json\\|json\\)" script) + (setq element-content-type "json")) + ((string-match-p " lang[ ]*=[ ]*[\"']\\(typescript\\|ts\\)" script) + (setq element-content-type "typescript")) + (t + (setq element-content-type "javascript")) + ) ;cond + ) ;let + ) ;script + ((string= tname "i18n") + (setq element-content-type "javascript" + part-close-tag "")) + ((and (string= tname "template") (string-match-p " lang" (buffer-substring-no-properties tbeg tend))) + (let (template) + (setq template (buffer-substring-no-properties tbeg tend) + part-close-tag "") + (cond + ((string-match-p " lang[ ]*=[ ]*[\"']pug" template) + (setq element-content-type "pug")) + (t + (setq element-content-type "html")) + ) ;cond + ) ;let + ) ;style + ((and (string= web-mode-engine "archibus") + (string= tname "sql")) + (setq element-content-type "sql" + part-close-tag "")) + ) + + (add-text-properties tbeg tend props) + (put-text-property tbeg (1+ tbeg) 'tag-beg flags) + (put-text-property (1- tend) tend 'tag-end t) + + (when (and part-close-tag + (web-mode-dom-sf part-close-tag reg-end t) + (setq part-beg tend) + (setq part-end (match-beginning 0)) + (> part-end part-beg)) + (put-text-property part-beg part-end 'part-side + (intern element-content-type web-mode-obarray)) + (setq tend part-end) + ) ;when + + (goto-char tend) + + ) ;while + + ))) + +;; FLAGS: tag +;; (1)attrs (2)custom (4)slash-beg (8)slash-end (16)bracket-end (32)namespaced + +;; FLAGS: attr +;; (1)custom-attr (2)engine-attr (4)spread-attr[jsx] (8)code-value +;; https://www.w3.org/TR/2012/WD-html-markup-20120329/syntax.html#attr-value-unquoted + +;; STATES: attr +;; (0)nil (1)space (2)name (3)space-before (4)equal (5)space-after +;; (6)value-uq (7)value-sq (8)value-dq (9)value-bq : jsx attr={} +;; (10)value-block + +(defun web-mode-attr-skip (limit) + + (let ((tag-flags 0) (attr-flags 0) (continue t) (attrs 0) (brace-depth 0) + (state 0) (equal-offset 0) (go-back nil) + (is-jsx (or (string= web-mode-content-type "jsx") (eq (get-text-property (point) 'part-type) 'jsx))) + attr name-beg name-end val-beg char pos mem step escaped spaced quoted) + + (while continue + + (setq pos (point) + char (char-after) + mem state + ;;spaced (eq char ?\s) + spaced (member char '(?\s ?\n)) + step nil) + + (ignore mem step) ;; Only used in debug print + (when quoted (setq quoted (1+ quoted))) + + (cond + + ((>= pos limit) + (setq continue nil) + (setq go-back t) + (setq attrs (+ attrs (web-mode-attr-scan pos state char name-beg name-end val-beg attr-flags equal-offset tag-flags))) + ) + + ((and (or (= state 0) (= state 1)) (get-text-property pos 'block-side)) + ) + + ((or (and (= state 8) (not (member char '(?\" ?\\)))) + (and (= state 7) (not (member char '(?\' ?\\)))) + (and (= state 9) (not (member char '(?} ?\\)))) + ) + (when (and (= state 9) (eq char ?\{)) + (setq brace-depth (1+ brace-depth))) + ) + + ((and (= state 9) (eq char ?\}) (> brace-depth 1)) + (setq brace-depth (1- brace-depth))) + + ;; #1233 + ;;((get-text-property pos 'block-side) + ;; (when (= state 2) + ;; (setq name-end pos)) + ;; ) + + ((and (= state 2) is-jsx (eq char ?\}) (eq attr-flags 4)) + (setq name-end pos) + (setq attrs (+ attrs (web-mode-attr-scan pos state char name-beg name-end val-beg attr-flags equal-offset tag-flags))) + (setq state 0 + attr-flags 0 + equal-offset 0 + name-beg nil + name-end nil + val-beg nil) + ) + + ((or (and (= state 8) (eq ?\" char) (not escaped)) + (and (= state 7) (eq ?\' char) (not escaped)) + (and (= state 9) (eq ?\} char) (= brace-depth 1)) + (and (= state 10) (get-text-property pos 'block-end)) + ) + (setq attrs (+ attrs (web-mode-attr-scan pos state char name-beg name-end val-beg attr-flags equal-offset tag-flags))) + (setq state 0 + attr-flags 0 + equal-offset 0 + name-beg nil + name-end nil + val-beg nil) + ) + + ((and (member state '(4 5)) (get-text-property pos 'block-beg)) + (setq val-beg pos) + (setq state 10)) + + ((and (member state '(4 5)) (member char '(?\' ?\" ?\{))) + (setq val-beg pos) + (setq quoted 1) + (setq state (cond ((eq ?\' char) 7) + ((eq ?\" char) 8) + (t 9))) + (setq step 100) + (when (= state 9) (setq brace-depth 1)) + ) + + ((and (eq ?\= char) (member state '(2 3))) + (setq equal-offset (- pos name-beg) + name-end (1- pos)) + (setq state 4) + (setq attr (buffer-substring-no-properties name-beg (1+ name-end))) + (when (and web-mode-indentless-attributes (member (downcase attr) web-mode-indentless-attributes)) + (setq attr-flags (logior attr-flags 8))) + ) + + ((and spaced (= state 0)) + (setq state 1) + ) + + ((and (eq char ?\<) (not (member state '(7 8 9)))) + (setq continue nil) + (setq go-back t) + (setq attrs (+ attrs (web-mode-attr-scan pos state char name-beg name-end val-beg attr-flags equal-offset tag-flags))) + ) + + ((and (eq char ?\>) (not (member state '(7 8 9)))) + (setq tag-flags (logior tag-flags 16)) + (when (eq (char-before) ?\/) + (setq tag-flags (logior tag-flags 8)) + ) + (setq continue nil) + (when name-beg + (setq attrs (+ attrs (web-mode-attr-scan pos state char name-beg name-end val-beg attr-flags equal-offset tag-flags)))) + ) + + ((and spaced (member state '(1 3 5))) + ) + + ((and spaced (= state 2)) + (setq state 3) + ) + + ((and (eq char ?\/) (member state '(4 5))) + (setq attrs (+ attrs (web-mode-attr-scan pos state char name-beg name-end val-beg attr-flags equal-offset tag-flags))) + (setq state 1 + attr-flags 0 + equal-offset 0 + name-beg nil + name-end nil + val-beg nil) + ) + + ((and (eq char ?\/) (member state '(0 1))) + ) + + ((and spaced (= state 4)) + (setq state 5) + ) + + ((and (= state 3) + (or (and (>= char 97) (<= char 122)) ;a - z + (and (>= char 65) (<= char 90)) ;A - Z + (eq char ?\-))) + (setq attrs (+ attrs (web-mode-attr-scan pos state char name-beg name-end val-beg attr-flags equal-offset tag-flags))) + (setq state 2 + attr-flags 0 + equal-offset 0 + name-beg pos + name-end pos + val-beg nil) + ) + + ((and (eq char ?\n) (not (member state '(7 8 9)))) + (setq attrs (+ attrs (web-mode-attr-scan pos state char name-beg name-end val-beg attr-flags equal-offset tag-flags))) + (setq state 1 + attr-flags 0 + equal-offset 0 + name-beg nil + name-end nil + val-beg nil) + ) + + ((and (= state 6) (member char '(?\s ?\n))) ;#1150 + (setq attrs (+ attrs (web-mode-attr-scan pos state char name-beg name-end val-beg attr-flags equal-offset tag-flags))) + (setq state 1 + attr-flags 0 + equal-offset 0 + name-beg nil + name-end nil + val-beg nil) + ) + + ((and quoted (= quoted 2) (member char '(?\s ?\n ?\>))) + (when (eq char ?\>) + (setq tag-flags (logior tag-flags 16)) + (setq continue nil)) + (setq state 6) + (setq attrs (+ attrs (web-mode-attr-scan pos state char name-beg name-end val-beg attr-flags equal-offset tag-flags))) + (setq state 1 + attr-flags 0 + equal-offset 0 + name-beg nil + name-end nil + val-beg nil) + ) + + ((and (not spaced) (= state 1)) + (when (and is-jsx (eq char ?\{)) + (setq attr-flags 4)) + (setq state 2) + (setq name-beg pos + name-end pos) + ) + + ((member state '(4 5)) + (setq val-beg pos) + (setq state 6) + ) + + ((= state 1) + (setq state 2) + ) + + ((= state 2) + (setq name-end pos) + (when (and nil (= attr-flags 0) (member char '(?\- ?\:))) + (let (attr) + (setq attr (buffer-substring-no-properties name-beg (1+ name-end))) + (cond + ((member attr '("http-equiv")) + (setq attr-flags (1- attr-flags)) + ) + ((and (eq char ?\-) (not (string= attr "http-"))) + (setq attr-flags (logior attr-flags 1))) + ) ;cond + ) ;let + ) ;when attr-flags = 1 + ) ;state=2 + + ) ;cond + + ;;(message "point(%S) state(%S) c(%S) name-beg(%S) name-end(%S) val-beg(%S) attr-flags(%S) equal-offset(%S)" pos state char name-beg name-end val-beg attr-flags equal-offset tag-flags) + + (when (and quoted (>= quoted 2)) + (setq quoted nil)) + + (setq escaped (eq ?\\ char)) + (when (null go-back) + (forward-char)) + + ;;(when (not (= mem state)) (message "pos=%S before=%S after=%S step=%S" pos mem state step)) + + ) ;while + + (when (> attrs 0) (setq tag-flags (logior tag-flags 1))) + + tag-flags)) + +(defun web-mode-attr-scan (pos state char name-beg name-end val-beg attr-flags equal-offset tag-flags) + ;;(message "point(%S) state(%S) c(%c) name-beg(%S) name-end(%S) val-beg(%S) attr-flags(%S) equal-offset(%S) tag-flags(%S)" pos state char name-beg name-end val-beg attr-flags equal-offset tag-flags) + (when (null attr-flags) (setq attr-flags 0)) + (when (and name-beg name-end web-mode-engine-attr-regexp) + (let (name) + (setq name (buffer-substring-no-properties name-beg (1+ name-end))) + (cond + ((string-match-p "^data[-]" name) + (setq attr-flags (logior attr-flags 1)) + ) + ((string-match-p web-mode-engine-attr-regexp name) + (setq attr-flags (logior attr-flags 2)) + ) + ) + ) ;name + ) + ;;(message "%S" name) + (cond + ((null name-beg) + 0) + ((or (and (= state 8) (not (eq ?\" char))) + (and (= state 7) (not (eq ?\' char)))) + (put-text-property name-beg (1+ name-beg) 'tag-attr-beg attr-flags) + (put-text-property name-beg val-beg 'tag-attr t) + (put-text-property (1- val-beg) val-beg 'tag-attr-end equal-offset) + 1) + ((and (member state '(4 5)) (null val-beg)) + (put-text-property name-beg (1+ name-beg) 'tag-attr-beg attr-flags) + (put-text-property name-beg (+ name-beg equal-offset 1) 'tag-attr t) + (put-text-property (+ name-beg equal-offset) (+ name-beg equal-offset 1) 'tag-attr-end equal-offset) + 1) + (t + (let (val-end) + (if (null val-beg) + (setq val-end name-end) + (setq val-end pos) + (cond + ((null char) + (setq val-end (1- val-end))) + ((member char '(?\s ?\n ?\/)) + (setq val-end (1- val-end))) + ((eq char ?\>) + (if (= (logand tag-flags 8) 8) + (progn + ;;(message "tag-flags=%S %S" tag-flags (logand tag-flags 8)) + (setq val-end (- val-end 2))) + (setq val-end (- val-end 1))) + ;; (message "val-end=%S" val-end) + ) + ) + ) + (put-text-property name-beg (1+ name-beg) 'tag-attr-beg attr-flags) + (put-text-property name-beg (1+ val-end) 'tag-attr t) + (put-text-property val-end (1+ val-end) 'tag-attr-end equal-offset) + ) ;let + 1) ;t + ) ;cond + ) + +(defun web-mode-part-foreach (reg-beg reg-end func) + (let ((i 0) (continue t) (part-beg reg-beg) (part-end nil)) + (while continue + (setq part-end nil) + (unless (get-text-property part-beg 'part-side) + (setq part-beg (web-mode-part-next-position part-beg))) + (when (and part-beg (< part-beg reg-end)) + (setq part-end (web-mode-part-end-position part-beg))) + (cond + ((> (setq i (1+ i)) 100) + (message "process-parts ** warning (%S) **" (point)) + (setq continue nil)) + ((or (null part-end) (> part-end reg-end)) + (setq continue nil)) + (t + (setq part-end (1+ part-end)) + (funcall func part-beg part-end) + (setq part-beg part-end)) + ) ;cond + ) ;while + )) + +(defun web-mode-part-scan (reg-beg reg-end &optional content-type depth) + (save-excursion + (let (token-re ch-before ch-at ch-next token-type beg continue) + ;;(message "%S %S" reg-beg reg-end) + (cond + (content-type + ) + ((member web-mode-content-type web-mode-part-content-types) + (setq content-type web-mode-content-type)) + (t + (setq content-type (symbol-name (get-text-property reg-beg 'part-side)))) + ) ;cond + + (goto-char reg-beg) + + (cond + ((member content-type '("javascript" "json")) + (setq token-re "/\\|\"\\|'\\|`")) + ((member content-type '("typescript")) + (setq token-re "/\\|\"\\|'\\|`\\|//\\|/\\*")) + ((member content-type '("jsx")) + (setq token-re "/\\|\"\\|'\\|`\\|]")) + ((string= web-mode-content-type "css") + (setq token-re "\"\\|'\\|/\\*\\|//")) + ((string= content-type "css") + (setq token-re "\"\\|'\\|/\\*")) + (t + (setq token-re "/\\*\\|\"\\|'")) + ) + + (while (and token-re (< (point) reg-end) (web-mode-dom-rsf token-re reg-end t)) + + (setq beg (match-beginning 0) + token-type nil + continue t + ch-at (char-after beg) + ch-next (or (char-after (1+ beg)) ?\d) + ch-before (or (char-before beg) ?\d)) + + ;;(message "[%S>%S|%S] %S %c %c %c" reg-beg reg-end depth beg ch-before ch-at ch-next) + + (cond + + ((eq ?\' ch-at) + (while (and continue (search-forward "'" reg-end t)) + (cond + ((get-text-property (1- (point)) 'block-side) + (setq continue t)) + (t + (setq continue (web-mode-string-continue-p reg-beg))) + ) + ) ;while + (setq token-type 'string)) + + ((eq ?\` ch-at) + (while (and continue (search-forward "`" reg-end t)) + (cond + ((get-text-property (1- (point)) 'block-side) + (setq continue t)) + (t + (setq continue (web-mode-string-continue-p reg-beg))) + ) + ) ;while + (setq token-type 'string)) + + ((eq ?\" ch-at) + (while (and continue (search-forward "\"" reg-end t)) + (cond + ((get-text-property (1- (point)) 'block-side) + (setq continue t)) + (t + (setq continue (web-mode-string-continue-p reg-beg))) + ) ;cond + ) ;while + (cond + ((string= content-type "json") + (if (looking-at-p "[ ]*:") + (cond + ((eq ?\@ (char-after (1+ beg))) + (setq token-type 'context)) + (t + (setq token-type 'key)) + ) + (setq token-type 'string)) + ) ;json + (t + (setq token-type 'string)) + ) ;cond + ) + + ((and (eq ?\< ch-at) + (not (or (and (>= ch-before 97) (<= ch-before 122)) + (and (>= ch-before 65) (<= ch-before 90))))) + ;;(message "before [%S>%S|%S] pt=%S" reg-beg reg-end depth (point)) + (search-backward "<") + (if (web-mode-jsx-skip reg-end) + (web-mode-jsx-scan-element beg (point) depth) + (forward-char)) + ;;(message "after [%S>%S|%S] pt=%S" reg-beg reg-end depth (point)) + ) + + ((and (eq ?\/ ch-at) (member content-type '("javascript" "jsx" "typescript"))) + (cond + ((eq ?\\ ch-before) + ) + ((eq ?\* ch-next) + ;;(message "--> %S %S" (point) reg-end) + (when (search-forward "*/" reg-end t) + (setq token-type 'comment)) + ) + ((eq ?\/ ch-next) + (setq token-type 'comment) + (goto-char (if (< reg-end (line-end-position)) reg-end (line-end-position))) + ) + ((and (looking-at-p ".*/") + (looking-back "\\(^\\|case\\|[[(,=:!&|?{};]\\)[ ]*/" (point-min))) + ;;(re-search-forward "/[gimyu]*" reg-end t)) + (let ((eol (line-end-position))) + (while (and continue (search-forward "/" eol t)) + (cond + ((get-text-property (1- (point)) 'block-side) + (setq continue t)) + ((looking-back "\\\\+/" reg-beg t) + (setq continue (= (mod (- (point) (match-beginning 0)) 2) 0))) + (t + (re-search-forward "[gimyu]*" eol t) + (setq token-type 'string) + (setq continue nil)) + ) + ) ;while + ) ;let + ) + ) ;cond + ) + + ((eq ?\/ ch-next) + ;;(message "%S" (point)) + (cond + ((and (string= content-type "css") + (eq ?/ ch-at) + (eq ?: ch-before)) + ) + (t + (unless (eq ?\\ ch-before) + (setq token-type 'comment) + (goto-char (if (< reg-end (line-end-position)) reg-end (line-end-position))) + ) + ) + ) + + ) + + ((eq ?\* ch-next) + (cond + ((search-forward "*/" reg-end t) + (setq token-type 'comment)) + ((not (eobp)) + (forward-char)) + ) ;cond + ) + + ) ;cond + + (when (and beg (>= reg-end (point)) token-type) + (put-text-property beg (point) 'part-token token-type) + (cond + ((eq token-type 'comment) + (put-text-property beg (1+ beg) 'syntax-table (string-to-syntax "<")) + (when (< (point) (point-max)) + (if (< (point) (line-end-position)) + (put-text-property (1- (point)) (point) 'syntax-table (string-to-syntax ">")) ;#445 + (put-text-property (point) (1+ (point)) 'syntax-table (string-to-syntax ">")) ;#377 + ) + ) ;when + ) ;comment + ((eq token-type 'string) + (put-text-property beg (1+ beg) 'syntax-table (string-to-syntax "|")) + (when (< (point) (point-max)) + (if (< (point) (line-end-position)) + (put-text-property (1- (point)) (point) 'syntax-table (string-to-syntax "|")) + (put-text-property (point) (1+ (point)) 'syntax-table (string-to-syntax "|")) + ) + ) ;when + ) ;string + ) ;cond + ) ;when + + (when (> (point) reg-end) + (message "reg-beg(%S) reg-end(%S) token-type(%S) point(%S)" reg-beg reg-end token-type (point))) + + ;;(message "#[%S>%S|%S] %S %c %c %c | (%S)" reg-beg reg-end depth beg ch-before ch-at ch-next (point)) + + ) ;while + + ))) + +(defun web-mode-string-continue-p (reg-beg) + "Is `point' preceeded by an odd number of backslashes?" + (let ((p (1- (point)))) + (while (and (< reg-beg p) (eq ?\\ (char-before p))) + (setq p (1- p))) + (= (mod (- (point) p) 2) 0))) + +;; css rule = selector(s) + declaration (properties) +(defun web-mode-css-rule-next (limit) + (let (at-rule var-rule sel-beg sel-end dec-beg dec-end chunk) + (skip-chars-forward "\n\t ") + (setq sel-beg (point)) + (when (and (< (point) limit) + (web-mode-part-rsf "[{;]" limit)) + (setq sel-end (1- (point))) + (cond + ((eq (char-before) ?\{) + (setq dec-beg (point)) + (setq dec-end (web-mode-closing-paren-position (1- dec-beg) limit)) + (if dec-end + (progn + (goto-char dec-end) + (forward-char)) + (setq dec-end limit) + (goto-char limit)) + ) + (t + ) + ) ;cond + (setq chunk (buffer-substring-no-properties sel-beg sel-end)) + (cond + ((string-match "@\\([[:alpha:]-]+\\)" chunk) + (setq at-rule (match-string-no-properties 1 chunk))) + ((string-match "\\$\\([[:alpha:]-]+\\)" chunk) + (setq var-rule (match-string-no-properties 1 chunk))) + ) ;cond + ) ;when + (if (not sel-end) + (progn (goto-char limit) nil) + (list :at-rule at-rule + :var-rule var-rule + :sel-beg sel-beg + :sel-end sel-end + :dec-beg dec-beg + :dec-end dec-end) + ) ;if + )) + +(defun web-mode-css-rule-current (&optional pos part-beg part-end) + "Current CSS rule boundaries." + (unless pos (setq pos (point))) + (unless part-beg (setq part-beg (web-mode-part-beginning-position pos))) + (unless part-end (setq part-end (web-mode-part-end-position pos))) + (save-excursion + (let (beg end) + (goto-char pos) + (if (not (web-mode-part-sb "{" part-beg)) + (progn + (setq beg part-beg) + (if (web-mode-part-sf ";" part-end) + (setq end (1+ (point))) + (setq end part-end)) + ) ;progn + (setq beg (point)) + (setq end (web-mode-closing-paren-position beg part-end)) + (if end + (setq end (1+ end)) + (setq end (line-end-position))) + ;; (message "%S >>beg%S >>end%S" pos beg end) + (if (> pos end) + + ;;selectors + (progn + (goto-char pos) + (if (web-mode-part-rsb "[};]" part-beg) + (setq beg (1+ (point))) + (setq beg part-beg) + ) ;if + (goto-char pos) + (if (web-mode-part-rsf "[{;]" part-end) + (cond + ((eq (char-before) ?\;) + (setq end (point)) + ) + (t + (setq end (web-mode-closing-paren-position (1- (point)) part-end)) + (if end + (setq end (1+ end)) + (setq end part-end)) + ) + ) ;cond + (setq end part-end) + ) + ) ;progn selectors + + ;; declaration + (goto-char beg) + (if (web-mode-part-rsb "[}{;]" part-beg) + (setq beg (1+ (point))) + (setq beg part-beg) + ) ;if + ) ;if > pos end + ) + ;; (message "beg(%S) end(%S)" beg end) + (when (eq (char-after beg) ?\n) + (setq beg (1+ beg))) + (cons beg end) + ))) + +(defun web-mode-jsx-skip (reg-end) + (let ((continue t) (pos nil) (i 0)) + (looking-at "<\\([[:alpha:]][[:alnum:]:-]*\\)") + ;; (let ((tag (match-string-no-properties 1))) + ;; (message "point=%S tag=%S" (point) tag)) + (save-excursion + (while continue + (cond + ((> (setq i (1+ i)) 1000) + (message "jsx-skip ** warning **") + (setq continue nil)) + ((looking-at "<[[:alpha:]][[:alnum:]:-]*[ ]*/>") + (goto-char (match-end 0)) + (setq pos (point)) + (setq continue nil)) + ((not (web-mode-dom-rsf ">\\([ \t\n]*[\];,)':}|&]\\)\\|{" reg-end)) + (setq continue nil) + ) + ((eq (char-before) ?\{) + (backward-char) + (web-mode-closing-paren reg-end) + (forward-char) + ) + (t + (setq continue nil) + (setq pos (match-beginning 1)) + ) ;t + ) ;cond + ) ;while + ) ;save-excursion + (when pos (goto-char pos)) + ;;(message "jsx-skip: %S" pos) + pos)) + +;; (defun web-mode-jsx-skip2 (reg-end) +;; (let ((continue t) (pos nil) (i 0) (tag nil) (regexp nil) (counter 1)) +;; (looking-at "<\\([[:alpha:]][[:alnum:]:-]*\\)") +;; (setq tag (match-string-no-properties 1)) +;; (setq regexp (concat " (setq i (1+ i)) 100) +;; (message "jsx-skip ** warning **") +;; (setq continue nil)) +;; ((looking-at "<[[:alpha:]][[:alnum:]:-]*[ ]*/>") +;; (goto-char (match-end 0)) +;; (setq pos (point)) +;; (setq continue nil)) +;; ((not (web-mode-dom-rsf ">\\([ \t\n]*[\];,)':}]\\)\\|{" reg-end)) +;; (setq continue nil) +;; ) +;; ((eq (char-before) ?\{) +;; (backward-char) +;; (web-mode-closing-paren reg-end) +;; (forward-char) +;; ) +;; (t +;; (setq continue nil) +;; (setq pos (match-beginning 1)) +;; ) ;t +;; ) ;cond +;; ) ;while +;; ) ;save-excursion +;; (when pos (goto-char pos)) +;; ;;(message "jsx-skip: %S" pos) +;; pos)) + +;; http://facebook.github.io/jsx/ +;; https://github.com/facebook/jsx/blob/master/AST.md +(defun web-mode-jsx-scan-element (reg-beg reg-end depth) + (unless depth (setq depth 1)) + (save-excursion + (goto-char reg-beg) + (put-text-property reg-beg (1+ reg-beg) 'jsx-beg depth) + (put-text-property (1- reg-end) reg-end 'jsx-end depth) + (put-text-property reg-beg reg-end 'jsx-depth depth) + (goto-char reg-beg) + (web-mode-scan-elements reg-beg reg-end) + (web-mode-jsx-scan-expression reg-beg reg-end (1+ depth)) + )) + +(defun web-mode-jsx-scan-expression (reg-beg reg-end depth) + (let ((continue t) beg end) + (save-excursion + (goto-char reg-beg) + ;;(message "reg-beg=%S reg-end=%S" reg-beg reg-end) + (while (and continue (search-forward "{" reg-end t)) + (backward-char) + (setq beg (point) + end (web-mode-closing-paren reg-end)) + (cond + ((eq (get-text-property beg 'part-token) 'comment) + (forward-char)) + ((not end) + (setq continue nil)) + (t + (setq end (1+ end)) + (put-text-property beg end 'jsx-depth depth) + (put-text-property beg (1+ beg) 'jsx-beg depth) + (put-text-property (1- end) end 'jsx-end depth) + (web-mode-part-scan beg end "jsx" (1+ depth)) + ) ;t + ) ;cond + ) ;while + ) ;save-excursion + )) + +(defun web-mode-jsx-is-html (&optional pos) + (interactive) + (unless pos (setq pos (point))) + (let ((depth (get-text-property pos 'jsx-depth))) + (cond + ((or (null depth) (<= pos 2)) + (setq pos nil)) + ((and (= depth 1) (get-text-property pos 'jsx-beg)) + (setq pos nil)) + ((get-text-property pos 'tag-end) + (setq pos nil)) + ((get-text-property pos 'tag-attr-beg) + (setq pos nil)) + ((get-text-property pos 'jsx-beg) + (setq pos (null (get-text-property pos 'tag-beg)))) + ((setq pos (web-mode-jsx-depth-beginning-position pos)) + (setq pos (not (null (get-text-property pos 'tag-beg))))) + (t + (setq pos nil)) + ) ;cond + ;;(message "is-html: %S (depth=%S)" pos depth) + pos)) + +(defun web-mode-jsx-is-expr (&optional pos) + (cond + ((and (get-text-property pos 'jsx-beg) + (not (get-text-property pos 'tag-beg))) + nil) + (t + (setq pos (web-mode-jsx-depth-beginning-position pos)) + (null (get-text-property pos 'tag-beg))) + ) ;cond + ) + +(defun web-mode-jsx-depth-beginning-position (&optional pos target-depth) + (interactive) + (unless pos (setq pos (point))) + (unless target-depth (setq target-depth (get-text-property pos 'jsx-depth))) + (cond + ((or (null target-depth) (bobp)) + (setq pos nil)) + ((and (get-text-property pos 'jsx-beg) (= target-depth (get-text-property pos 'jsx-depth))) + ) + (t + (let ((continue t) depth) + (while continue + (setq pos (previous-single-property-change pos 'jsx-depth)) + (cond + ((or (null pos) + (null (setq depth (get-text-property pos 'jsx-depth)))) + (setq continue nil + pos nil)) + ((and (get-text-property pos 'jsx-beg) (= target-depth depth)) + (setq continue nil)) + ) ;cond + ) ;while + ) ;let + ) ;t + ) ;cond + ;;(message "beg: %S" pos) + pos) + +(defun web-mode-jsx-element-next (reg-end) + (let (continue beg end) + (setq beg (point)) + (unless (get-text-property beg 'jsx-depth) + (setq beg (next-single-property-change beg 'jsx-beg))) + (setq continue (and beg (< beg reg-end)) + end beg) + (while continue + (setq end (next-single-property-change end 'jsx-end)) + (cond + ((or (null end) (> end reg-end)) + (setq continue nil + end nil)) + ((eq (get-text-property end 'jsx-depth) 1) + (setq continue nil)) + (t + (setq end (1+ end))) + ) ;cond + ) ;while + ;;(message "beg=%S end=%S" beg end) + (if (and beg end (< beg end)) (cons beg end) nil))) + +(defun web-mode-jsx-expression-next (reg-end) + (let (beg end depth continue pos) + (setq beg (point)) + ;;(message "pt=%S" beg) + (unless (and (get-text-property beg 'jsx-beg) (null (get-text-property beg 'tag-beg))) + ;;(setq beg (next-single-property-change beg 'jsx-beg)) + (setq continue t + pos (1+ beg)) + (while continue + (setq pos (next-single-property-change pos 'jsx-beg)) + (cond + ((null pos) + (setq continue nil + beg nil)) + ((> pos reg-end) + (setq continue nil + beg nil)) + ((null (get-text-property pos 'jsx-beg)) + ) + ((null (get-text-property pos 'tag-beg)) + (setq continue nil + beg pos)) + ;;(t + ;; (setq pos (1+ pos))) + ) ;cond + ) ;while + ) ;unless + ;;(message "beg=%S" beg) + (when (and beg (< beg reg-end)) + (setq depth (get-text-property beg 'jsx-beg) + continue (not (null depth)) + pos beg) + ;;(message "beg=%S" beg) + (while continue + (setq pos (next-single-property-change pos 'jsx-end)) + ;;(message "pos=%S" pos) + (cond + ((null pos) + (setq continue nil)) + ((> pos reg-end) + (setq continue nil)) + ((eq depth (get-text-property pos 'jsx-end)) + (setq continue nil + end pos)) + (t + ;;(setq pos (1+ pos)) + ) + ) ;cond + ) ;while + ) ;when + ;;(message "%S > %S" beg end) + (if (and beg end) (cons beg end) nil))) + +(defun web-mode-jsx-depth-next (reg-end) + (let (beg end depth continue pos) + (setq beg (point)) + ;;(message "pt=%S" beg) + (unless (get-text-property beg 'jsx-beg) + ;;(setq beg (next-single-property-change beg 'jsx-beg)) + ;;(setq pos (1+ beg)) + (setq pos (next-single-property-change (1+ beg) 'jsx-beg)) + (cond + ((null pos) + (setq beg nil)) + ((>= pos reg-end) + (setq beg nil)) + (t + (setq beg pos)) + ) ;cond + ) ;unless + ;;(message "beg=%S" beg) + (when beg + (setq depth (get-text-property beg 'jsx-beg) + continue (not (null depth)) + pos beg) + ;;(message "beg=%S" beg) + (while continue + (setq pos (next-single-property-change pos 'jsx-end)) + ;;(message "pos=%S" pos) + (cond + ((null pos) + (setq continue nil)) + ((> pos reg-end) + (setq continue nil)) + ((eq depth (get-text-property pos 'jsx-end)) + (setq continue nil + end pos)) + (t + ;;(setq pos (1+ pos)) + ) + ) ;cond + ) ;while + ) ;when + ;;(message "%S > %S" beg end) + (if (and beg end) (cons beg end) nil))) + +(defun web-mode-jsx-beginning () + (interactive) + (let (depth (continue t) (reg-beg (point-min)) (pos (point))) + (setq depth (get-text-property pos 'jsx-depth)) + (cond + ((not depth) + ) + ((get-text-property (1- pos) 'jsx-beg) + (goto-char (1- pos))) + (t + (while continue + (setq pos (previous-single-property-change pos 'jsx-beg)) + ;;(message "pos=%S" pos) + (cond + ((null pos) + (setq continue nil)) + ((<= pos reg-beg) + (setq continue nil)) + ((eq depth (get-text-property pos 'jsx-beg)) + (setq continue nil)) + ) ;cond + ) ;while + (web-mode-go pos) + ) ;t + ) ;cond + )) + +(defun web-mode-jsx-end () + (interactive) + (let (depth (continue t) (reg-end (point-max)) (pos (point))) + (setq depth (get-text-property pos 'jsx-depth)) + (cond + ((not depth) + ) + ((get-text-property pos 'jsx-end) + (goto-char (+ pos 1))) + (t + (while continue + (setq pos (next-single-property-change pos 'jsx-end)) + ;;(message "pos=%S" pos) + (cond + ((null pos) + (setq continue nil)) + ((> pos reg-end) + (setq continue nil)) + ((eq depth (get-text-property pos 'jsx-end)) + (setq continue nil)) + ) ;cond + ) ;while + (web-mode-go pos 1) + ) ;t + ) ;cond + )) + +;;---- FONTIFICATION ----------------------------------------------------------- + +(defun web-mode-fontify (limit) + (when web-mode-trace + (message "fontify: point(%S) limit(%S)" (point) limit)) + (cond + ;;(web-mode-skip-fontification + ;; nil) + (t + (web-mode-with-silent-modifications + (save-excursion + (save-restriction + (save-match-data + (let ((beg (point)) + (buffer-undo-list t) + (end limit) + (inhibit-point-motion-hooks t) + (inhibit-quit t)) + (remove-list-of-text-properties beg end '(font-lock-face face)) + (cond + ((and (get-text-property beg 'block-side) + (not (get-text-property beg 'block-beg))) + (web-mode-fontify-block beg end)) + ((or (member web-mode-content-type web-mode-part-content-types) + (get-text-property beg 'part-side)) + (web-mode-fontify-part beg end) + (web-mode-block-foreach beg end 'web-mode-fontify-block)) + ((string= web-mode-engine "none") + (web-mode-fontify-tags beg end) + (web-mode-part-foreach beg end 'web-mode-fontify-part)) + (t + (web-mode-fontify-tags beg end) + (web-mode-part-foreach beg end 'web-mode-fontify-part) + (web-mode-block-foreach beg end 'web-mode-fontify-block)) + ) ;cond + (when web-mode-enable-element-content-fontification + (web-mode-fontify-elements beg end)) + (when web-mode-enable-whitespace-fontification + (web-mode-fontify-whitespaces beg end)) + ) ;let + )))) + nil) ;t + )) + +(defun web-mode-buffer-fontify () + (interactive) + (cond + ((and (fboundp 'font-lock-flush) global-font-lock-mode) + (font-lock-flush) + (font-lock-ensure)) + (t ;emacs 24 + ;;(font-lock-fontify-buffer) + (and global-font-lock-mode + (font-lock-fontify-region (point-min) (point-max)))) + )) + +(defun web-mode-unfontify-region (beg end) + (ignore beg end) + ;;(message "unfontify: %S %S" beg end) + ) + +(defun web-mode-fontify-region (beg end keywords) + ;; (message "beg=%S end=%S keywords=%S" beg end (symbol-name keywords)) + (save-excursion + (let ((font-lock-keywords keywords) + (font-lock-multiline nil) + (font-lock-keywords-case-fold-search + (member web-mode-engine '("archibus" "asp" "template-toolkit"))) + (font-lock-keywords-only t) + (font-lock-extend-region-functions nil)) + (when (and (listp font-lock-keywords) global-font-lock-mode) + (font-lock-fontify-region beg end) + ) + ))) + +(defun web-mode-fontify-tags (reg-beg reg-end &optional depth) + (let ((continue t)) + (goto-char reg-beg) + (when (and (not (get-text-property (point) 'tag-beg)) + (not (web-mode-tag-next))) + (setq continue nil)) + (when (and continue (>= (point) reg-end)) + (setq continue nil)) + (while continue + (cond + (depth + (when (eq depth (get-text-property (point) 'jsx-depth)) + (web-mode-fontify-tag)) + ) + (t + (web-mode-fontify-tag)) + ) ;cond + (when (or (not (web-mode-tag-next)) + (>= (point) reg-end)) + (setq continue nil)) + ) ;while + (when web-mode-enable-inlays + (when (null web-mode-inlay-regexp) + (setq web-mode-inlay-regexp (regexp-opt '("\\[" "\\(" "\\begin{align}")))) + (let (beg end expr) + (goto-char reg-beg) + (while (web-mode-dom-rsf web-mode-inlay-regexp reg-end) + (setq beg (match-beginning 0) + end nil + expr (substring (match-string-no-properties 0) 0 2)) + (setq expr (cond + ((string= expr "\\[") "\\]") + ((string= expr "\\(") "\\)") + (t "\\end{align}"))) + (when (and (web-mode-dom-sf expr reg-end) + (setq end (match-end 0)) + (not (text-property-any beg end 'tag-end t))) + (font-lock-append-text-property beg end 'font-lock-face 'web-mode-inlay-face) + ) ;when + ) ;while + ) ;let + ) ;when + (when web-mode-enable-html-entities-fontification + (let (beg end) + (goto-char reg-beg) + (while (web-mode-dom-rsf "&\\([#]?[[:alnum:]]\\{2,8\\}\\);" reg-end) + (setq beg (match-beginning 0) + end (match-end 0)) + (when (not (text-property-any beg end 'tag-end t)) + (font-lock-append-text-property beg end 'font-lock-face 'web-mode-html-entity-face) + ) ;when + ) ;while + ) ;let + ) ;when + )) + +(defun web-mode-fontify-tag (&optional beg end) + (unless beg (setq beg (point))) + (unless end (setq end (1+ (web-mode-tag-end-position beg)))) + (let (name type face flags slash-beg slash-end bracket-end) + (setq flags (get-text-property beg 'tag-beg) + type (get-text-property beg 'tag-type) + name (get-text-property beg 'tag-name)) + (setq bracket-end (> (logand flags 16) 0)) + (cond + ((eq type 'comment) + (put-text-property beg end 'font-lock-face 'web-mode-comment-face) + (when (and web-mode-enable-comment-interpolation (> (- end beg) 5)) + (web-mode-interpolate-comment beg end nil))) + ((eq type 'cdata) + (put-text-property beg end 'font-lock-face 'web-mode-doctype-face)) + ((eq type 'doctype) + (put-text-property beg end 'font-lock-face 'web-mode-doctype-face)) + ((eq type 'declaration) + (put-text-property beg end 'font-lock-face 'web-mode-doctype-face)) + (name + (setq slash-beg (> (logand flags 4) 0) + slash-end (> (logand flags 8) 0) + bracket-end (> (logand flags 16) 0)) + (setq face (cond + ((not bracket-end) 'web-mode-html-tag-unclosed-face) + ((and web-mode-enable-element-tag-fontification + (setq face (cdr (assoc name web-mode-element-tag-faces)))) + face) + ((> (logand flags 32) 0) 'web-mode-html-tag-namespaced-face) + ((> (logand flags 2) 0) 'web-mode-html-tag-custom-face) + (t 'web-mode-html-tag-face))) + (put-text-property beg (+ beg (if slash-beg 2 1)) + 'font-lock-face 'web-mode-html-tag-bracket-face) + (unless (string= name "_fragment_") + (put-text-property (+ beg (if slash-beg 2 1)) + (+ beg (if slash-beg 2 1) (length name)) + 'font-lock-face face)) + (when (or slash-end bracket-end) + (put-text-property (- end (if slash-end 2 1)) end 'font-lock-face 'web-mode-html-tag-bracket-face) + ) ;when + (when (> (logand flags 1) 0) + ;;(message "%S>%S" beg end) + (web-mode-fontify-attrs beg end)) + ) ;case name + ) ;cond + )) + +(defun web-mode-fontify-attrs (reg-beg reg-end) + (let ((continue t) (pos reg-beg) beg end flags offset face) + ;;(message "fontify-attrs %S>%S" reg-beg reg-end) + (while continue + (setq beg (web-mode-attribute-next-position pos reg-end)) + (cond + ((or (null beg) (>= beg reg-end)) + (setq continue nil)) + (t + (setq flags (or (get-text-property beg 'tag-attr-beg) 0)) + (setq face (cond + ((= (logand flags 1) 1) 'web-mode-html-attr-custom-face) + ((= (logand flags 2) 2) 'web-mode-html-attr-engine-face) + ((= (logand flags 4) 4) nil) + (t 'web-mode-html-attr-name-face))) + ;;(setq end (if (get-text-property beg 'tag-attr-end) beg (web-mode-attribute-end-position beg))) + (setq end (web-mode-attribute-end-position beg)) + ;;(message "beg=%S end=%S" beg end) + (cond + ((or (null end) (>= end reg-end)) + (setq continue nil)) + (t + (setq offset (get-text-property end 'tag-attr-end)) + (if (= offset 0) + (put-text-property beg (1+ end) 'font-lock-face face) + (put-text-property beg (+ beg offset) 'font-lock-face face) + (put-text-property (+ beg offset) (+ beg offset 1) + 'font-lock-face + 'web-mode-html-attr-equal-face) + (when (not (get-text-property (+ beg offset 1) 'jsx-beg)) + (put-text-property (+ beg offset 1) (1+ end) + 'font-lock-face + 'web-mode-html-attr-value-face) + ) + ) ;if offset + (setq pos (1+ end)) + ) ;t + ) ;cond + ) ;t + );cond + ) ;while + )) + +(defun web-mode-fontify-block (reg-beg reg-end) + (when web-mode-trace + (message "fontify-block: reg-beg(%S) reg-end(%S) engine(%S) keywords(%S)" + reg-beg reg-end web-mode-engine (not (null web-mode-engine-font-lock-keywords)))) + + (let (sub1 sub2 sub3 continue char keywords token-type face beg end (buffer (current-buffer))) + + ;; NOTE: required for blocks inside tag attrs + ;; NOTE: ajout de face dans la liste pour sucharger la couleur définie par + ;; un prealable web-mode-fontity-part (2022-12-25 #1230) + (remove-list-of-text-properties reg-beg reg-end '(font-lock-face face)) + ;;(message "reg-beg=%S reg-end=%S" reg-beg reg-end) + + (goto-char reg-beg) + + (when (null web-mode-engine-font-lock-keywords) + (setq sub1 (buffer-substring-no-properties + reg-beg (+ reg-beg 1)) + sub2 (buffer-substring-no-properties + reg-beg (+ reg-beg 2)) + sub3 (buffer-substring-no-properties + reg-beg (+ reg-beg (if (>= (point-max) (+ reg-beg 3)) 3 2)))) + ) + + (cond + + ((and (get-text-property reg-beg 'block-beg) + (eq (get-text-property reg-beg 'block-token) 'comment)) + (put-text-property reg-beg reg-end 'font-lock-face 'web-mode-comment-face) + ) ;comment block + + (web-mode-engine-font-lock-keywords + (setq keywords web-mode-engine-font-lock-keywords)) + + ((string= web-mode-engine "django") + (cond + ((string= sub2 "{{") + (setq keywords web-mode-django-expr-font-lock-keywords)) + ((string= sub2 "{%") + (setq keywords web-mode-django-code-font-lock-keywords)) + ((string= sub1 "#") + (setq keywords web-mode-django-code-font-lock-keywords)) + )) ;django + + ((string= web-mode-engine "mako") + (cond + ((member sub3 '("<% " "<%\n" "<%!")) + (setq keywords web-mode-mako-block-font-lock-keywords)) + ((eq (aref sub2 0) ?\%) + (setq keywords web-mode-mako-block-font-lock-keywords)) + ((member sub2 '("<%" " %S face(%S)" beg end face) + (remove-list-of-text-properties beg end '(face)) + (put-text-property beg end 'font-lock-face face) + ) + (setq continue nil + end nil) + ) ;if end + ) ;progn beg + (setq continue nil + end nil) + ) ;if beg + (when (and beg end) + (save-match-data + (when (and web-mode-enable-heredoc-fontification + (eq char ?\<) + (> (- end beg) 8) + (string-match-p "JS\\|JAVASCRIPT\\|HTM\\|CSS" (buffer-substring-no-properties beg end))) + (setq keywords + (cond + ((string-match-p "H" (buffer-substring-no-properties beg (+ beg 8))) + web-mode-html-font-lock-keywords) + (t + web-mode-javascript-font-lock-keywords) + )) + (web-mode-fontify-region beg end keywords) + ) + ) ;save-match-data + (when (and web-mode-enable-string-interpolation + (member char '(?\" ?\<)) + (member web-mode-engine '("php" "erb")) + (> (- end beg) 4)) + (web-mode-interpolate-block-string beg end) + ) ;when + (when (and web-mode-enable-comment-interpolation + (eq token-type 'comment) + (> (- end beg) 3)) + (web-mode-interpolate-comment beg end t) + ) ;when + (when (and web-mode-enable-comment-annotation + (eq token-type 'comment) + (> (- end beg) 3)) + (web-mode-annotate-comment beg end) + ) ;when + (when (and web-mode-enable-sql-detection + (eq token-type 'string) + (> (- end beg) 6) + (web-mode-looking-at-p (concat "\\(.\\|<<<[[:alnum:]]+\\)[ \n]*" web-mode-sql-queries) beg) + ) + (web-mode-interpolate-sql-string beg end) + ) ;when + ) ;when beg end + ) ;while continue + ) ;when keywords + + (when (and (member web-mode-engine '("mako")) + (> (- reg-end reg-beg) 12) + (eq ?\< (char-after reg-beg))) + (web-mode-interpolate-block-tag reg-beg reg-end)) + + (when web-mode-enable-block-face + (font-lock-append-text-property reg-beg reg-end 'face 'web-mode-block-face)) + + )) + +(defun web-mode-fontify-part (reg-beg reg-end &optional depth) + (save-excursion + (let (continue token-type face pos beg end string-face comment-face content-type) + ;;(message "fontify-part: reg-beg(%S) reg-end(%S)" reg-beg reg-end) + (if (member web-mode-content-type web-mode-part-content-types) + (setq content-type web-mode-content-type) + (setq content-type (symbol-name (get-text-property reg-beg 'part-side)))) + ;;(message "content-type=%S" content-type) + (unless depth + (when (string= content-type "jsx") (setq depth 0)) + ) + (setq string-face 'web-mode-part-string-face + comment-face 'web-mode-part-comment-face) + (cond + ((member content-type '("javascript" "jsx")) + (setq string-face 'web-mode-javascript-string-face + comment-face 'web-mode-javascript-comment-face) + (web-mode-fontify-region reg-beg reg-end web-mode-javascript-font-lock-keywords)) + ((string= content-type "json") + (setq string-face 'web-mode-json-string-face + comment-face 'web-mode-json-comment-face) + (web-mode-fontify-region reg-beg reg-end web-mode-javascript-font-lock-keywords)) + ((string= content-type "css") + (setq string-face 'web-mode-css-string-face + comment-face 'web-mode-css-comment-face) + (web-mode-fontify-css-rules reg-beg reg-end)) + ((string= content-type "sql") + (web-mode-fontify-region reg-beg reg-end web-mode-sql-font-lock-keywords)) + ((string= content-type "stylus") + (web-mode-fontify-region reg-beg reg-end web-mode-stylus-font-lock-keywords)) + ((string= content-type "sass") + (web-mode-fontify-region reg-beg reg-end web-mode-sass-font-lock-keywords)) + ((string= content-type "pug") + (web-mode-fontify-region reg-beg reg-end web-mode-pug-font-lock-keywords)) + ((string= content-type "markdown") + (web-mode-fontify-region reg-beg reg-end web-mode-markdown-font-lock-keywords)) + ((string= content-type "ruby") + (web-mode-fontify-region reg-beg reg-end web-mode-erb-font-lock-keywords)) + ((string= content-type "typescript") + (web-mode-fontify-region reg-beg reg-end web-mode-javascript-font-lock-keywords)) + ) ;cond + + (goto-char reg-beg) + + ;;(when (string= content-type "jsx") (web-mode-fontify-tags reg-beg reg-end)) + ;;(setq continue (and pos (< pos reg-end))) + (setq continue t + pos reg-beg) + (while continue + (if (get-text-property pos 'part-token) + (setq beg pos) + (setq beg (next-single-property-change pos 'part-token))) + (cond + ((or (null beg) (>= beg reg-end)) + (setq continue nil + end nil)) + ((and (eq depth 0) (get-text-property beg 'jsx-depth)) + (setq pos (or (next-single-property-change beg 'jsx-depth) (point-max)))) + (t + ;;(message "%c" (char-after beg)) + (setq token-type (get-text-property beg 'part-token)) + (setq face (cond + ((eq token-type 'string) string-face) + ((eq token-type 'comment) comment-face) + ((eq token-type 'context) 'web-mode-json-context-face) + ((eq token-type 'key) 'web-mode-json-key-face) + (t nil))) + (setq end (or (next-single-property-change beg 'part-token) (point-max)) + pos end) + (cond + ((or (null end) (> end reg-end)) + (setq continue nil + end nil)) + (t + (when face + (remove-list-of-text-properties beg end '(face)) + (put-text-property beg end 'font-lock-face face)) + (cond + ((< (- end beg) 6) + ) + ((eq token-type 'string) + (cond + ((and (eq (char-after beg) ?\`) + web-mode-enable-literal-interpolation + (member content-type '("javascript" "jsx" "typescript"))) + (web-mode-interpolate-javascript-literal beg end) + ) + ((and (eq (char-after beg) ?\") + web-mode-enable-string-interpolation + (member content-type '("javascript" "jsx" "typescript"))) + (web-mode-interpolate-javascript-string beg end)) + ) ;cond + ) ;case string + ((eq token-type 'comment) + (when web-mode-enable-comment-interpolation + (web-mode-interpolate-comment beg end t)) + (when web-mode-enable-comment-annotation + (web-mode-annotate-comment beg end)) + ) + ) ;cond + ) ;t + ) ;cond + ) ;t + ) ;cond + ) ;while + + (when (and (string= web-mode-content-type "html") web-mode-enable-part-face) + (font-lock-append-text-property reg-beg reg-end 'face + (cond + ((string= content-type "javascript") + 'web-mode-script-face) + ((string= content-type "css") + 'web-mode-style-face) + (t + 'web-mode-part-face))) + ) + + (when (and web-mode-enable-css-colorization (string= content-type "stylus")) + (goto-char reg-beg) + (while (and (re-search-forward "#[0-9a-fA-F]\\{6\\}\\|#[0-9a-fA-F]\\{3\\}\\|rgba?([ ]*\\([[:digit:]]\\{1,3\\}\\)[ ]*,[ ]*\\([[:digit:]]\\{1,3\\}\\)[ ]*,[ ]*\\([[:digit:]]\\{1,3\\}\\)\\(.*?\\))" end t) + (<= (point) reg-end)) + (web-mode-colorize (match-beginning 0) (match-end 0)) + ) + ) + + (when (and (eq depth 0) (string= content-type "jsx")) + (let (pair elt-beg elt-end exp-beg exp-end exp-depth) + (goto-char reg-beg) + (while (setq pair (web-mode-jsx-element-next reg-end)) + ;;(message "elt-pair=%S" pair) + (setq elt-beg (car pair) + elt-end (cdr pair)) + (remove-list-of-text-properties elt-beg (1+ elt-end) '(face)) + (web-mode-fontify-tags elt-beg elt-end 1) + (goto-char elt-beg) + (while (setq pair (web-mode-jsx-expression-next elt-end)) + ;;(message "exp-pair=%S elt-end=%S" pair elt-end) + (setq exp-beg (car pair) + exp-end (cdr pair)) + (when (eq (char-after exp-beg) ?\{) + ;;(message "%S : %c %c" exp-beg (char-after (+ exp-beg 1)) (char-after (+ exp-beg 2))) + (cond + ;;((and (eq (char-after (+ exp-beg 1)) ?\/) (eq (char-after (+ exp-beg 2)) ?\*)) + ;; (put-text-property exp-beg (1+ exp-end) 'font-lock-face 'web-mode-part-comment-face) + ;; ) + (t + (setq exp-depth (get-text-property exp-beg 'jsx-depth)) + (remove-list-of-text-properties exp-beg exp-end '(font-lock-face)) + (put-text-property exp-beg (1+ exp-beg) 'font-lock-face 'web-mode-block-delimiter-face) + (when (and (eq (get-text-property exp-beg 'tag-attr-beg) 4) (web-mode-looking-at-p "\.\.\." (1+ exp-beg))) + (put-text-property exp-beg (+ exp-beg 4) 'font-lock-face 'web-mode-block-delimiter-face)) + (put-text-property exp-end (1+ exp-end) 'font-lock-face 'web-mode-block-delimiter-face) + (web-mode-fontify-tags (1+ exp-beg) exp-end (1+ exp-depth)) + (web-mode-fontify-part (1+ exp-beg) exp-end exp-depth) + (web-mode-fontify-region (1+ exp-beg) exp-end web-mode-javascript-font-lock-keywords) + ) ;t + ) ;cond + ) ;when + (goto-char (1+ exp-beg)) + ) ;while exp + + (when (and elt-beg web-mode-jsx-depth-faces) + (let (depth-beg depth-end jsx-face) + (goto-char elt-beg) + (while (setq pair (web-mode-jsx-depth-next reg-end)) + ;;(message "depth-pair=%S" pair) + (setq depth-beg (car pair) + depth-end (cdr pair) + depth (get-text-property depth-beg 'jsx-depth) + jsx-face (elt web-mode-jsx-depth-faces (1- depth))) + ;;(message "%S" jsx-face) + (font-lock-prepend-text-property depth-beg (1+ depth-end) 'face jsx-face) + (goto-char (+ depth-beg 2)) + ) + ) ;let + ) + + (goto-char (1+ elt-end)) + ) ;while elt + ) ;let + ) ;when + + ) ;let + ) ;save-excursion + ) + +(defun web-mode-fontify-css-rules (part-beg part-end) + (save-excursion + (goto-char part-beg) + (let (rule (continue t) (i 0) (at-rule nil)) + (while continue + (setq rule (web-mode-css-rule-next part-end)) + ;;(message "rule=%S" rule) + (cond + ((> (setq i (1+ i)) 1000) + (message "fontify-css-rules ** too much rules **") + (setq continue nil)) + ((null rule) + (setq continue nil)) + ((and (setq at-rule (plist-get rule :at-rule)) + (not (member at-rule '("charset" "font-face" "import" "viewport"))) + (plist-get rule :dec-end)) + (web-mode-fontify-css-rule (plist-get rule :sel-beg) + (plist-get rule :sel-end) + nil nil) + (web-mode-fontify-css-rules (plist-get rule :dec-beg) + (plist-get rule :dec-end))) + (t + (web-mode-fontify-css-rule (plist-get rule :sel-beg) + (plist-get rule :sel-end) + (plist-get rule :dec-beg) + (plist-get rule :dec-end))) + ) ;cond + ) ;while + ) ;let + )) + +(defun web-mode-fontify-css-rule (sel-beg sel-end dec-beg dec-end) + (save-excursion + ;;(let ((end sel-end)) + ;;(message "sel-beg=%S sel-end=%S dec-beg=%S dec-end=%S" sel-beg sel-end dec-beg dec-end) + (web-mode-fontify-region sel-beg sel-end web-mode-selector-font-lock-keywords) + (when (and dec-beg dec-end) + ;;(setq end dec-end) + (web-mode-fontify-region dec-beg dec-end web-mode-declaration-font-lock-keywords) + ) ;when + (when (and dec-beg dec-end) + (goto-char dec-beg) + (while (and web-mode-enable-css-colorization + (re-search-forward "\\(?1:#[0-9a-fA-F]\\{6\\}\\)\\|\\(?1:#[0-9a-fA-F]\\{3\\}\\)\\|\\(?1:rgba?([ ]*\\(?2:[[:digit:]]\\{1,3\\}\\)[ ]*,[ ]*\\(?3:[[:digit:]]\\{1,3\\}\\)[ ]*,[ ]*\\(?4:[[:digit:]]\\{1,3\\}\\)\\(.*?\\))\\)\\|[: ]\\(?1:black\\|silver\\|gray\\|white\\|maroon\\|red\\|purple\\|fuchsia\\|green\\|lime\\|olive\\|yellow\\|navy\\|blue\\|teal\\|aqua\\|orange\\|aliceblue\\|antiquewhite\\|aquamarine\\|azure\\|beige\\|bisque\\|blanchedalmond\\|blueviolet\\|brown\\|burlywood\\|cadetblue\\|chartreuse\\|chocolate\\|coral\\|cornflowerblue\\|cornsilk\\|crimson\\|cyan\\|darkblue\\|darkcyan\\|darkgoldenrod\\|darkgray\\|darkgreen\\|darkgrey\\|darkkhaki\\|darkmagenta\\|darkolivegreen\\|darkorange\\|darkorchid\\|darkred\\|darksalmon\\|darkseagreen\\|darkslateblue\\|darkslategray\\|darkslategrey\\|darkturquoise\\|darkviolet\\|deeppink\\|deepskyblue\\|dimgray\\|dimgrey\\|dodgerblue\\|firebrick\\|floralwhite\\|forestgreen\\|gainsboro\\|ghostwhite\\|gold\\|goldenrod\\|greenyellow\\|grey\\|honeydew\\|hotpink\\|indianred\\|indigo\\|ivory\\|khaki\\|lavender\\|lavenderblush\\|lawngreen\\|lemonchiffon\\|lightblue\\|lightcoral\\|lightcyan\\|lightgoldenrodyellow\\|lightgray\\|lightgreen\\|lightgrey\\|lightpink\\|lightsalmon\\|lightseagreen\\|lightskyblue\\|lightslategray\\|lightslategrey\\|lightsteelblue\\|lightyellow\\|limegreen\\|linen\\|magenta\\|mediumaquamarine\\|mediumblue\\|mediumorchid\\|mediumpurple\\|mediumseagreen\\|mediumslateblue\\|mediumspringgreen\\|mediumturquoise\\|mediumvioletred\\|midnightblue\\|mintcream\\|mistyrose\\|moccasin\\|navajowhite\\|oldlace\\|olivedrab\\|orangered\\|orchid\\|palegoldenrod\\|palegreen\\|paleturquoise\\|palevioletred\\|papayawhip\\|peachpuff\\|peru\\|pink\\|plum\\|powderblue\\|rosybrown\\|royalblue\\|saddlebrown\\|salmon\\|sandybrown\\|seagreen\\|seashell\\|sienna\\|skyblue\\|slateblue\\|slategray\\|slategrey\\|snow\\|springgreen\\|steelblue\\|tan\\|thistle\\|tomato\\|turquoise\\|violet\\|wheat\\|whitesmoke\\|yellowgreen\\)[ ;]" dec-end t) + ;;(progn (message "%S %S" end (point)) t) + (<= (point) dec-end)) + ;;(message "web-mode-colorize beg=%S end=%S match=%S" (match-beginning 0) (match-end 0) (buffer-substring-no-properties (match-beginning 0) (match-end 0))) + (web-mode-colorize (match-beginning 1) (match-end 1)) + ) ;while + ) ;when + ;;) ;let + )) + +(defun web-mode-colorize-foreground (color) + (let* ((values (x-color-values color)) + (r (car values)) + (g (cadr values)) + (b (car (cdr (cdr values))))) + (if (> 128.0 (floor (+ (* .3 r) (* .59 g) (* .11 b)) 256)) + "white" "black"))) + +(defun web-mode-colorize (beg end) + (let (str plist) + (setq str (buffer-substring-no-properties beg end)) + ;;(setq str1 (match-string-no-properties 1)) + ;;(message "str=%S" str str1) + (cond + ;;(t + ;; (message "%S %S %S %S %S" (match-string-no-properties 0) (match-string-no-properties 1) (match-string-no-properties 2) (match-string-no-properties 3) (match-string-no-properties 4)) + ;; ) + ((string= (substring str 0 1) "#") + (setq plist (list :background str + :foreground (web-mode-colorize-foreground str)))) + ((and (>= (length str) 3) (string= (substring str 0 3) "rgb")) + (setq str (format "#%02X%02X%02X" + (string-to-number (match-string-no-properties 2)) + (string-to-number (match-string-no-properties 3)) + (string-to-number (match-string-no-properties 4)))) + (setq plist (list :background str + :foreground (web-mode-colorize-foreground str)))) + ((string= str "black") (setq plist (list :background "#000000" :foreground (web-mode-colorize-foreground "#000000")))) + ((string= str "silver") (setq plist (list :background "#c0c0c0" :foreground (web-mode-colorize-foreground "#c0c0c0")))) + ((string= str "gray") (setq plist (list :background "#808080" :foreground (web-mode-colorize-foreground "#808080")))) + ((string= str "white") (setq plist (list :background "#ffffff" :foreground (web-mode-colorize-foreground "#ffffff")))) + ((string= str "maroon") (setq plist (list :background "#800000" :foreground (web-mode-colorize-foreground "#800000")))) + ((string= str "red") (setq plist (list :background "#ff0000" :foreground (web-mode-colorize-foreground "#ff0000")))) + ((string= str "purple") (setq plist (list :background "#800080" :foreground (web-mode-colorize-foreground "#800080")))) + ((string= str "fuchsia") (setq plist (list :background "#ff00ff" :foreground (web-mode-colorize-foreground "#ff00ff")))) + ((string= str "green") (setq plist (list :background "#008000" :foreground (web-mode-colorize-foreground "#008000")))) + ((string= str "lime") (setq plist (list :background "#00ff00" :foreground (web-mode-colorize-foreground "#00ff00")))) + ((string= str "olive") (setq plist (list :background "#808000" :foreground (web-mode-colorize-foreground "#808000")))) + ((string= str "yellow") (setq plist (list :background "#ffff00" :foreground (web-mode-colorize-foreground "#ffff00")))) + ((string= str "navy") (setq plist (list :background "#000080" :foreground (web-mode-colorize-foreground "#000080")))) + ((string= str "blue") (setq plist (list :background "#0000ff" :foreground (web-mode-colorize-foreground "#0000ff")))) + ((string= str "teal") (setq plist (list :background "#008080" :foreground (web-mode-colorize-foreground "#008080")))) + ((string= str "aqua") (setq plist (list :background "#00ffff" :foreground (web-mode-colorize-foreground "#00ffff")))) + ((string= str "orange") (setq plist (list :background "#ffa500" :foreground (web-mode-colorize-foreground "#ffa500")))) + ((string= str "aliceblue") (setq plist (list :background "#f0f8ff" :foreground (web-mode-colorize-foreground "#f0f8ff")))) + ((string= str "antiquewhite") (setq plist (list :background "#faebd7" :foreground (web-mode-colorize-foreground "#faebd7")))) + ((string= str "aquamarine") (setq plist (list :background "#7fffd4" :foreground (web-mode-colorize-foreground "#7fffd4")))) + ((string= str "azure") (setq plist (list :background "#f0ffff" :foreground (web-mode-colorize-foreground "#f0ffff")))) + ((string= str "beige") (setq plist (list :background "#f5f5dc" :foreground (web-mode-colorize-foreground "#f5f5dc")))) + ((string= str "bisque") (setq plist (list :background "#ffe4c4" :foreground (web-mode-colorize-foreground "#ffe4c4")))) + ((string= str "blanchedalmond") (setq plist (list :background "#ffebcd" :foreground (web-mode-colorize-foreground "#ffebcd")))) + ((string= str "blueviolet") (setq plist (list :background "#8a2be2" :foreground (web-mode-colorize-foreground "#8a2be2")))) + ((string= str "brown") (setq plist (list :background "#a52a2a" :foreground (web-mode-colorize-foreground "#a52a2a")))) + ((string= str "burlywood") (setq plist (list :background "#deb887" :foreground (web-mode-colorize-foreground "#deb887")))) + ((string= str "cadetblue") (setq plist (list :background "#5f9ea0" :foreground (web-mode-colorize-foreground "#5f9ea0")))) + ((string= str "chartreuse") (setq plist (list :background "#7fff00" :foreground (web-mode-colorize-foreground "#7fff00")))) + ((string= str "chocolate") (setq plist (list :background "#d2691e" :foreground (web-mode-colorize-foreground "#d2691e")))) + ((string= str "coral") (setq plist (list :background "#ff7f50" :foreground (web-mode-colorize-foreground "#ff7f50")))) + ((string= str "cornflowerblue") (setq plist (list :background "#6495ed" :foreground (web-mode-colorize-foreground "#6495ed")))) + ((string= str "cornsilk") (setq plist (list :background "#fff8dc" :foreground (web-mode-colorize-foreground "#fff8dc")))) + ((string= str "crimson") (setq plist (list :background "#dc143c" :foreground (web-mode-colorize-foreground "#dc143c")))) + ((string= str "cyan") (setq plist (list :background "#00ffff" :foreground (web-mode-colorize-foreground "#00ffff")))) + ((string= str "darkblue") (setq plist (list :background "#00008b" :foreground (web-mode-colorize-foreground "#00008b")))) + ((string= str "darkcyan") (setq plist (list :background "#008b8b" :foreground (web-mode-colorize-foreground "#008b8b")))) + ((string= str "darkgoldenrod") (setq plist (list :background "#b8860b" :foreground (web-mode-colorize-foreground "#b8860b")))) + ((string= str "darkgray") (setq plist (list :background "#a9a9a9" :foreground (web-mode-colorize-foreground "#a9a9a9")))) + ((string= str "darkgreen") (setq plist (list :background "#006400" :foreground (web-mode-colorize-foreground "#006400")))) + ((string= str "darkgrey") (setq plist (list :background "#a9a9a9" :foreground (web-mode-colorize-foreground "#a9a9a9")))) + ((string= str "darkkhaki") (setq plist (list :background "#bdb76b" :foreground (web-mode-colorize-foreground "#bdb76b")))) + ((string= str "darkmagenta") (setq plist (list :background "#8b008b" :foreground (web-mode-colorize-foreground "#8b008b")))) + ((string= str "darkolivegreen") (setq plist (list :background "#556b2f" :foreground (web-mode-colorize-foreground "#556b2f")))) + ((string= str "darkorange") (setq plist (list :background "#ff8c00" :foreground (web-mode-colorize-foreground "#ff8c00")))) + ((string= str "darkorchid") (setq plist (list :background "#9932cc" :foreground (web-mode-colorize-foreground "#9932cc")))) + ((string= str "darkred") (setq plist (list :background "#8b0000" :foreground (web-mode-colorize-foreground "#8b0000")))) + ((string= str "darksalmon") (setq plist (list :background "#e9967a" :foreground (web-mode-colorize-foreground "#e9967a")))) + ((string= str "darkseagreen") (setq plist (list :background "#8fbc8f" :foreground (web-mode-colorize-foreground "#8fbc8f")))) + ((string= str "darkslateblue") (setq plist (list :background "#483d8b" :foreground (web-mode-colorize-foreground "#483d8b")))) + ((string= str "darkslategray") (setq plist (list :background "#2f4f4f" :foreground (web-mode-colorize-foreground "#2f4f4f")))) + ((string= str "darkslategrey") (setq plist (list :background "#2f4f4f" :foreground (web-mode-colorize-foreground "#2f4f4f")))) + ((string= str "darkturquoise") (setq plist (list :background "#00ced1" :foreground (web-mode-colorize-foreground "#00ced1")))) + ((string= str "darkviolet") (setq plist (list :background "#9400d3" :foreground (web-mode-colorize-foreground "#9400d3")))) + ((string= str "deeppink") (setq plist (list :background "#ff1493" :foreground (web-mode-colorize-foreground "#ff1493")))) + ((string= str "deepskyblue") (setq plist (list :background "#00bfff" :foreground (web-mode-colorize-foreground "#00bfff")))) + ((string= str "dimgray") (setq plist (list :background "#696969" :foreground (web-mode-colorize-foreground "#696969")))) + ((string= str "dimgrey") (setq plist (list :background "#696969" :foreground (web-mode-colorize-foreground "#696969")))) + ((string= str "dodgerblue") (setq plist (list :background "#1e90ff" :foreground (web-mode-colorize-foreground "#1e90ff")))) + ((string= str "firebrick") (setq plist (list :background "#b22222" :foreground (web-mode-colorize-foreground "#b22222")))) + ((string= str "floralwhite") (setq plist (list :background "#fffaf0" :foreground (web-mode-colorize-foreground "#fffaf0")))) + ((string= str "forestgreen") (setq plist (list :background "#228b22" :foreground (web-mode-colorize-foreground "#228b22")))) + ((string= str "gainsboro") (setq plist (list :background "#dcdcdc" :foreground (web-mode-colorize-foreground "#dcdcdc")))) + ((string= str "ghostwhite") (setq plist (list :background "#f8f8ff" :foreground (web-mode-colorize-foreground "#f8f8ff")))) + ((string= str "gold") (setq plist (list :background "#ffd700" :foreground (web-mode-colorize-foreground "#ffd700")))) + ((string= str "goldenrod") (setq plist (list :background "#daa520" :foreground (web-mode-colorize-foreground "#daa520")))) + ((string= str "greenyellow") (setq plist (list :background "#adff2f" :foreground (web-mode-colorize-foreground "#adff2f")))) + ((string= str "grey") (setq plist (list :background "#808080" :foreground (web-mode-colorize-foreground "#808080")))) + ((string= str "honeydew") (setq plist (list :background "#f0fff0" :foreground (web-mode-colorize-foreground "#f0fff0")))) + ((string= str "hotpink") (setq plist (list :background "#ff69b4" :foreground (web-mode-colorize-foreground "#ff69b4")))) + ((string= str "indianred") (setq plist (list :background "#cd5c5c" :foreground (web-mode-colorize-foreground "#cd5c5c")))) + ((string= str "indigo") (setq plist (list :background "#4b0082" :foreground (web-mode-colorize-foreground "#4b0082")))) + ((string= str "ivory") (setq plist (list :background "#fffff0" :foreground (web-mode-colorize-foreground "#fffff0")))) + ((string= str "khaki") (setq plist (list :background "#f0e68c" :foreground (web-mode-colorize-foreground "#f0e68c")))) + ((string= str "lavender") (setq plist (list :background "#e6e6fa" :foreground (web-mode-colorize-foreground "#e6e6fa")))) + ((string= str "lavenderblush") (setq plist (list :background "#fff0f5" :foreground (web-mode-colorize-foreground "#fff0f5")))) + ((string= str "lawngreen") (setq plist (list :background "#7cfc00" :foreground (web-mode-colorize-foreground "#7cfc00")))) + ((string= str "lemonchiffon") (setq plist (list :background "#fffacd" :foreground (web-mode-colorize-foreground "#fffacd")))) + ((string= str "lightblue") (setq plist (list :background "#add8e6" :foreground (web-mode-colorize-foreground "#add8e6")))) + ((string= str "lightcoral") (setq plist (list :background "#f08080" :foreground (web-mode-colorize-foreground "#f08080")))) + ((string= str "lightcyan") (setq plist (list :background "#e0ffff" :foreground (web-mode-colorize-foreground "#e0ffff")))) + ((string= str "lightgoldenrodyellow") (setq plist (list :background "#fafad2" :foreground (web-mode-colorize-foreground "#fafad2")))) + ((string= str "lightgray") (setq plist (list :background "#d3d3d3" :foreground (web-mode-colorize-foreground "#d3d3d3")))) + ((string= str "lightgreen") (setq plist (list :background "#90ee90" :foreground (web-mode-colorize-foreground "#90ee90")))) + ((string= str "lightgrey") (setq plist (list :background "#d3d3d3" :foreground (web-mode-colorize-foreground "#d3d3d3")))) + ((string= str "lightpink") (setq plist (list :background "#ffb6c1" :foreground (web-mode-colorize-foreground "#ffb6c1")))) + ((string= str "lightsalmon") (setq plist (list :background "#ffa07a" :foreground (web-mode-colorize-foreground "#ffa07a")))) + ((string= str "lightseagreen") (setq plist (list :background "#20b2aa" :foreground (web-mode-colorize-foreground "#20b2aa")))) + ((string= str "lightskyblue") (setq plist (list :background "#87cefa" :foreground (web-mode-colorize-foreground "#87cefa")))) + ((string= str "lightslategray") (setq plist (list :background "#778899" :foreground (web-mode-colorize-foreground "#778899")))) + ((string= str "lightslategrey") (setq plist (list :background "#778899" :foreground (web-mode-colorize-foreground "#778899")))) + ((string= str "lightsteelblue") (setq plist (list :background "#b0c4de" :foreground (web-mode-colorize-foreground "#b0c4de")))) + ((string= str "lightyellow") (setq plist (list :background "#ffffe0" :foreground (web-mode-colorize-foreground "#ffffe0")))) + ((string= str "limegreen") (setq plist (list :background "#32cd32" :foreground (web-mode-colorize-foreground "#32cd32")))) + ((string= str "linen") (setq plist (list :background "#faf0e6" :foreground (web-mode-colorize-foreground "#faf0e6")))) + ((string= str "magenta") (setq plist (list :background "#ff00ff" :foreground (web-mode-colorize-foreground "#ff00ff")))) + ((string= str "mediumaquamarine") (setq plist (list :background "#66cdaa" :foreground (web-mode-colorize-foreground "#66cdaa")))) + ((string= str "mediumblue") (setq plist (list :background "#0000cd" :foreground (web-mode-colorize-foreground "#0000cd")))) + ((string= str "mediumorchid") (setq plist (list :background "#ba55d3" :foreground (web-mode-colorize-foreground "#ba55d3")))) + ((string= str "mediumpurple") (setq plist (list :background "#9370db" :foreground (web-mode-colorize-foreground "#9370db")))) + ((string= str "mediumseagreen") (setq plist (list :background "#3cb371" :foreground (web-mode-colorize-foreground "#3cb371")))) + ((string= str "mediumslateblue") (setq plist (list :background "#7b68ee" :foreground (web-mode-colorize-foreground "#7b68ee")))) + ((string= str "mediumspringgreen") (setq plist (list :background "#00fa9a" :foreground (web-mode-colorize-foreground "#00fa9a")))) + ((string= str "mediumturquoise") (setq plist (list :background "#48d1cc" :foreground (web-mode-colorize-foreground "#48d1cc")))) + ((string= str "mediumvioletred") (setq plist (list :background "#c71585" :foreground (web-mode-colorize-foreground "#c71585")))) + ((string= str "midnightblue") (setq plist (list :background "#191970" :foreground (web-mode-colorize-foreground "#191970")))) + ((string= str "mintcream") (setq plist (list :background "#f5fffa" :foreground (web-mode-colorize-foreground "#f5fffa")))) + ((string= str "mistyrose") (setq plist (list :background "#ffe4e1" :foreground (web-mode-colorize-foreground "#ffe4e1")))) + ((string= str "moccasin") (setq plist (list :background "#ffe4b5" :foreground (web-mode-colorize-foreground "#ffe4b5")))) + ((string= str "navajowhite") (setq plist (list :background "#ffdead" :foreground (web-mode-colorize-foreground "#ffdead")))) + ((string= str "oldlace") (setq plist (list :background "#fdf5e6" :foreground (web-mode-colorize-foreground "#fdf5e6")))) + ((string= str "olivedrab") (setq plist (list :background "#6b8e23" :foreground (web-mode-colorize-foreground "#6b8e23")))) + ((string= str "orangered") (setq plist (list :background "#ff4500" :foreground (web-mode-colorize-foreground "#ff4500")))) + ((string= str "orchid") (setq plist (list :background "#da70d6" :foreground (web-mode-colorize-foreground "#da70d6")))) + ((string= str "palegoldenrod") (setq plist (list :background "#eee8aa" :foreground (web-mode-colorize-foreground "#eee8aa")))) + ((string= str "palegreen") (setq plist (list :background "#98fb98" :foreground (web-mode-colorize-foreground "#98fb98")))) + ((string= str "paleturquoise") (setq plist (list :background "#afeeee" :foreground (web-mode-colorize-foreground "#afeeee")))) + ((string= str "palevioletred") (setq plist (list :background "#db7093" :foreground (web-mode-colorize-foreground "#db7093")))) + ((string= str "papayawhip") (setq plist (list :background "#ffefd5" :foreground (web-mode-colorize-foreground "#ffefd5")))) + ((string= str "peachpuff") (setq plist (list :background "#ffdab9" :foreground (web-mode-colorize-foreground "#ffdab9")))) + ((string= str "peru") (setq plist (list :background "#cd853f" :foreground (web-mode-colorize-foreground "#cd853f")))) + ((string= str "pink") (setq plist (list :background "#ffc0cb" :foreground (web-mode-colorize-foreground "#ffc0cb")))) + ((string= str "plum") (setq plist (list :background "#dda0dd" :foreground (web-mode-colorize-foreground "#dda0dd")))) + ((string= str "powderblue") (setq plist (list :background "#b0e0e6" :foreground (web-mode-colorize-foreground "#b0e0e6")))) + ((string= str "rosybrown") (setq plist (list :background "#bc8f8f" :foreground (web-mode-colorize-foreground "#bc8f8f")))) + ((string= str "royalblue") (setq plist (list :background "#4169e1" :foreground (web-mode-colorize-foreground "#4169e1")))) + ((string= str "saddlebrown") (setq plist (list :background "#8b4513" :foreground (web-mode-colorize-foreground "#8b4513")))) + ((string= str "salmon") (setq plist (list :background "#fa8072" :foreground (web-mode-colorize-foreground "#fa8072")))) + ((string= str "sandybrown") (setq plist (list :background "#f4a460" :foreground (web-mode-colorize-foreground "#f4a460")))) + ((string= str "seagreen") (setq plist (list :background "#2e8b57" :foreground (web-mode-colorize-foreground "#2e8b57")))) + ((string= str "seashell") (setq plist (list :background "#fff5ee" :foreground (web-mode-colorize-foreground "#fff5ee")))) + ((string= str "sienna") (setq plist (list :background "#a0522d" :foreground (web-mode-colorize-foreground "#a0522d")))) + ((string= str "skyblue") (setq plist (list :background "#87ceeb" :foreground (web-mode-colorize-foreground "#87ceeb")))) + ((string= str "slateblue") (setq plist (list :background "#6a5acd" :foreground (web-mode-colorize-foreground "#6a5acd")))) + ((string= str "slategray") (setq plist (list :background "#708090" :foreground (web-mode-colorize-foreground "#708090")))) + ((string= str "slategrey") (setq plist (list :background "#708090" :foreground (web-mode-colorize-foreground "#708090")))) + ((string= str "snow") (setq plist (list :background "#fffafa" :foreground (web-mode-colorize-foreground "#fffafa")))) + ((string= str "springgreen") (setq plist (list :background "#00ff7f" :foreground (web-mode-colorize-foreground "#00ff7f")))) + ((string= str "steelblue") (setq plist (list :background "#4682b4" :foreground (web-mode-colorize-foreground "#4682b4")))) + ((string= str "tan") (setq plist (list :background "#d2b48c" :foreground (web-mode-colorize-foreground "#d2b48c")))) + ((string= str "thistle") (setq plist (list :background "#d8bfd8" :foreground (web-mode-colorize-foreground "#d8bfd8")))) + ((string= str "tomato") (setq plist (list :background "#ff6347" :foreground (web-mode-colorize-foreground "#ff6347")))) + ((string= str "turquoise") (setq plist (list :background "#40e0d0" :foreground (web-mode-colorize-foreground "#40e0d0")))) + ((string= str "violet") (setq plist (list :background "#ee82ee" :foreground (web-mode-colorize-foreground "#ee82ee")))) + ((string= str "wheat") (setq plist (list :background "#f5deb3" :foreground (web-mode-colorize-foreground "#f5deb3")))) + ((string= str "whitesmoke") (setq plist (list :background "#f5f5f5" :foreground (web-mode-colorize-foreground "#f5f5f5")))) + ((string= str "yellowgreen") (setq plist (list :background "#9acd32" :foreground (web-mode-colorize-foreground "#9acd32")))) + ) ;cond + (put-text-property beg end 'face plist) + )) + +(defun web-mode-interpolate-block-tag (beg end) + (save-excursion + (goto-char (+ 4 beg)) + (setq end (1- end)) + (while (re-search-forward "${.*?}" end t) + (remove-list-of-text-properties (match-beginning 0) (match-end 0) '(face)) + (web-mode-fontify-region (match-beginning 0) (match-end 0) + web-mode-uel-font-lock-keywords)) + )) + +(defun web-mode-interpolate-javascript-string (beg end) + (save-excursion + (goto-char (1+ beg)) + (setq end (1- end)) + (while (re-search-forward "${.*?}" end t) + (put-text-property (match-beginning 0) (match-end 0) + 'font-lock-face + 'web-mode-variable-name-face) + ) + )) + +(defun web-mode-interpolate-javascript-literal (beg end) + (save-excursion + (setq end (1- end)) + (goto-char (1+ beg)) + (cond + ((web-mode-looking-back "\\(css\\|styled[[:alnum:].]+\\|css = \\)" beg) + (goto-char (1+ beg)) + (while (re-search-forward ".*?:" end t) + (put-text-property (match-beginning 0) (match-end 0) + 'font-lock-face + 'web-mode-interpolate-color1-face) + ) + ) ;case css + ((web-mode-looking-back "\\(template\\|html\\|html = \\)" beg) + (goto-char (1+ beg)) + (while (re-search-forward web-mode-tag-regexp end t) + (put-text-property (match-beginning 1) (match-end 1) + 'font-lock-face + 'web-mode-interpolate-color1-face) + ) + (goto-char (1+ beg)) + (while (re-search-forward "\\| [.@?]?[[:alnum:]]+=" end t) + (cond + ((member (char-after (match-beginning 0)) '(?\< ?\/ ?\>)) + (put-text-property (match-beginning 0) (match-end 0) + 'font-lock-face + 'web-mode-interpolate-color2-face) + ) + (t + (put-text-property (1+ (match-beginning 0)) (1- (match-end 0)) + 'font-lock-face + 'web-mode-interpolate-color3-face) + ) ;t + ) ;cond + ) ;while + (goto-char (1+ beg)) + (while (re-search-forward "<\\(script\\|style\\)>\\(.*\\)" end t) + (put-text-property (match-beginning 2) (match-end 2) + 'font-lock-face + 'web-mode-interpolate-color4-face) + ) + ) ;case html + ) ;cond type of literal + (goto-char (1+ beg)) + (while (re-search-forward "${.*?}" end t) + (put-text-property (match-beginning 0) (match-end 0) + 'font-lock-face + 'web-mode-variable-name-face) + ) ;while + )) + +;; todo : parsing plus compliqué: {$obj->values[3]->name} +(defun web-mode-interpolate-block-string (beg end) + (save-excursion + (goto-char (1+ beg)) + (setq end (1- end)) + (cond + ((string= web-mode-engine "php") + (while (re-search-forward "$[[:alnum:]_]+\\(->[[:alnum:]_]+\\)*\\|{[ ]*$.+?}" end t) + ;; (message "%S > %S" (match-beginning 0) (match-end 0)) + (remove-list-of-text-properties (match-beginning 0) (match-end 0) '(font-lock-face)) + (web-mode-fontify-region (match-beginning 0) (match-end 0) + web-mode-php-var-interpolation-font-lock-keywords) + )) + ((string= web-mode-engine "erb") + (while (re-search-forward "#{.*?}" end t) + (remove-list-of-text-properties (match-beginning 0) (match-end 0) '(font-lock-face)) + (put-text-property (match-beginning 0) (match-end 0) + 'font-lock-face 'web-mode-variable-name-face) + )) + ) ;cond + )) + +(defun web-mode-interpolate-comment (beg end _block-side) + (save-excursion + (let ((regexp (concat "\\_<\\(" web-mode-comment-keywords "\\)\\_>"))) + (goto-char beg) + (while (re-search-forward regexp end t) + (font-lock-prepend-text-property (match-beginning 1) (match-end 1) + 'font-lock-face + 'web-mode-comment-keyword-face) + ) ;while + ))) + +(defun web-mode-annotate-comment (beg end) + (save-excursion + ;;(message "beg=%S end=%S" beg end) + (goto-char beg) + (when (looking-at-p "/\\*\\*") + (while (re-search-forward "\\(.+\\)" end t) + (font-lock-prepend-text-property (match-beginning 1) (match-end 1) + 'font-lock-face + 'web-mode-annotation-face)) + (goto-char beg) + (while (re-search-forward "[ ]+\\({[^}]+}\\)" end t) + (font-lock-prepend-text-property (match-beginning 1) (match-end 1) + 'font-lock-face + 'web-mode-annotation-type-face)) + (goto-char beg) + (while (re-search-forward "\\(@[[:alnum:]]+\\)" end t) + (font-lock-prepend-text-property (match-beginning 1) (match-end 1) + 'font-lock-face + 'web-mode-annotation-tag-face)) + (goto-char beg) + (while (re-search-forward "}[[:blank:]]+\\([[:graph:]]+\\)" end t) + (font-lock-prepend-text-property (match-beginning 1) (match-end 1) + 'font-lock-face + 'web-mode-annotation-value-face)) + (goto-char beg) + (while (re-search-forward "@see[[:blank:]]+\\([[:graph:]]+\\)" end t) + (font-lock-prepend-text-property (match-beginning 1) (match-end 1) + 'font-lock-face + 'web-mode-annotation-value-face)) + (goto-char beg) + (while (re-search-forward "{\\(@\\(?:link\\|code\\)\\)\\s-+\\([^}\n]+\\)\\(#.+\\)?}" end t) + (font-lock-prepend-text-property (match-beginning 2) (match-end 2) + 'font-lock-face + 'web-mode-annotation-value-face)) + (goto-char beg) + (while (re-search-forward "\\(\\)" end t) + (font-lock-prepend-text-property (match-beginning 1) (match-end 1) + 'font-lock-face + 'web-mode-annotation-html-face) + (font-lock-prepend-text-property (match-beginning 2) (match-end 2) + 'font-lock-face + 'web-mode-annotation-html-face) + (font-lock-prepend-text-property (match-beginning 3) (match-end 3) + 'font-lock-face + 'web-mode-annotation-html-face)) + ) ;when + )) + +(defun web-mode-interpolate-sql-string (beg end) + (save-excursion + (let ((case-fold-search t) + (regexp (concat "\\_<\\(" web-mode-sql-keywords "\\)\\_>"))) + (goto-char beg) + (while (re-search-forward regexp end t) + (font-lock-prepend-text-property (match-beginning 1) (match-end 1) + 'font-lock-face + 'web-mode-sql-keyword-face) + ) ;while + ))) + +;;---- EFFECTS ----------------------------------------------------------------- + +(defun web-mode-fill-paragraph (&optional _justify) + (save-excursion + (let ((pos (point)) + prop pair beg end delim-beg delim-end chunk fill-coll) + (ignore delim-beg delim-end fill-coll) + (cond + ((or (eq (get-text-property pos 'part-token) 'comment) + (eq (get-text-property pos 'block-token) 'comment)) + (setq prop + (if (get-text-property pos 'part-token) 'part-token 'block-token)) + (setq pair (web-mode-property-boundaries prop pos)) + (when (and pair (> (- (cdr pair) (car pair)) 6)) + (setq fill-coll (if (< fill-column 10) 70 fill-column)) + (setq beg (car pair) + end (cdr pair)) + (goto-char beg) + (setq chunk (buffer-substring-no-properties beg (+ beg 2))) + (cond + ((string= chunk "//") + (setq delim-beg "//" + delim-end "EOL")) + ((string= chunk "/*") + (setq delim-beg "/*" + delim-end "*/")) + ((string= chunk "{#") + (setq delim-beg "{#" + delim-end "#}")) + ((string= chunk "")) + ) + ) + ) ;comment - case + ((web-mode-is-content) + (setq pair (web-mode-content-boundaries pos)) + (setq beg (car pair) + end (cdr pair)) + ) + ) ;cond + ;;(message "beg(%S) end(%S)" beg end) + (when (and beg end) + (fill-region beg end)) + t))) + +(defun web-mode-engine-syntax-check () + (interactive) + (let ((proc nil) (errors nil) + (file (concat temporary-file-directory "emacs-web-mode-tmp"))) + (write-region (point-min) (point-max) file) + (cond + ;; ((null (buffer-file-name)) + ;; ) + ((string= web-mode-engine "php") + (setq proc (start-process "php-proc" nil "php" "-l" file)) + (set-process-filter + proc + (lambda (_proc output) + (cond + ((string-match-p "No syntax errors" output) + (message "No syntax errors") + ) + (t + ;; (setq output (replace-regexp-in-string temporary-file-directory "" output)) + ;; (message output) + (message "Syntax error") + (setq errors t)) + ) ;cond + ;; (delete-file file) + ) ;lambda + ) + ) ;php + (t + (message "no syntax checker found") + ) ;t + ) ;cond + errors)) + +(defun web-mode-jshint () + "Run JSHint on all the JavaScript parts." + (interactive) + (let (proc) + (when (buffer-file-name) + (setq proc (start-process + "jshint-proc" + nil + (or (executable-find "jshint") "/usr/local/bin/jshint") + "--extract=auto" + (buffer-file-name))) + (setq web-mode-jshint-errors 0) + (set-process-filter proc + (lambda (_proc output) + (let ((offset 0) overlay pos (old 0) msg) + (remove-overlays (point-min) (point-max) 'font-lock-face 'web-mode-error-face) + (while (string-match + "line \\([[:digit:]]+\\), col \\([[:digit:]]+\\), \\(.+\\)\\.$" + output offset) + (setq web-mode-jshint-errors (1+ web-mode-jshint-errors)) + (setq offset (match-end 0)) + (setq pos (web-mode-coord-position + (match-string-no-properties 1 output) + (match-string-no-properties 2 output))) + (when (get-text-property pos 'tag-beg) + (setq pos (1- pos))) + (when (not (= pos old)) + (setq old pos) + (setq overlay (make-overlay pos (1+ pos))) + (overlay-put overlay 'font-lock-face 'web-mode-error-face) + ) + (setq msg (or (overlay-get overlay 'help-echo) + (concat "line=" + (match-string-no-properties 1 output) + " column=" + (match-string-no-properties 2 output) + ))) + (overlay-put overlay 'help-echo + (concat msg " ## " (match-string-no-properties 3 output))) + ) ;while + )) + ) + ) ;when + )) + +(defun web-mode-dom-errors-show () + "Show unclosed tags." + (interactive) + (let (beg end tag pos l tags i cont cell overlay overlays first + (ori (point)) + (errors 0) + (continue t) + ) + (setq overlays (overlays-in (point-min) (point-max))) + (when overlays + (dolist (overlay overlays) + (when (eq (overlay-get overlay 'face) 'web-mode-warning-face) + (delete-overlay overlay) + ) + ) + ) + (goto-char (point-min)) + (when (not (or (get-text-property (point) 'tag-beg) + (web-mode-tag-next))) + (setq continue nil)) + (while continue + (setq pos (point)) + (setq tag (get-text-property pos 'tag-name)) + (cond + ((eq (get-text-property (point) 'tag-type) 'start) + (setq tags (push (list tag pos) tags)) + ;; (message "(%S) opening %S" pos tag) + ) + ((eq (get-text-property (point) 'tag-type) 'end) + (setq i 0 + l (length tags) + cont t) + (while (and (< i l) cont) + (setq cell (nth i tags)) + ;; (message "cell=%S" cell) + (setq i (1+ i)) + (cond + ((string= tag (nth 0 cell)) + (setq cont nil) + ) + (t + (setq errors (1+ errors)) + (setq beg (nth 1 cell)) + (setq end (web-mode-tag-end-position beg)) + (unless first + (setq first beg)) + (setq overlay (make-overlay beg (1+ end))) + (overlay-put overlay 'font-lock-face 'web-mode-warning-face) + ;; (message "invalid <%S> at %S" (nth 0 cell) (nth 1 cell)) + ) + ) ;cond + ) ;while + + (dotimes (_i i) + (setq tags (cdr tags))) + + ) + ) ;cond + (when (not (web-mode-tag-next)) + (setq continue nil)) + ) ;while + (message "%S error(s) detected" errors) + (if (< errors 1) + (goto-char ori) + (goto-char first) + (recenter)) + ;; (message "%S" tags) + )) + +(defun web-mode-fontify-elements (beg end) + (save-excursion + (goto-char beg) + (let ((continue (or (get-text-property (point) 'tag-beg) (web-mode-tag-next))) + (i 0) (ctx nil) (face nil)) + (while continue + (cond + ((> (setq i (1+ i)) 1000) + (message "fontify-elements ** too much tags **") + (setq continue nil)) + ((> (point) end) + (setq continue nil)) + ((not (get-text-property (point) 'tag-beg)) + (setq continue nil)) + ((eq (get-text-property (point) 'tag-type) 'start) + (when (and (setq ctx (web-mode-element-boundaries (point))) + (<= (car (cdr ctx)) end) + (setq face (cdr (assoc (get-text-property (point) 'tag-name) web-mode-element-content-faces)))) + (font-lock-prepend-text-property (1+ (cdr (car ctx))) (car (cdr ctx)) + 'font-lock-face face)) + ) + ) ;cond + (when (not (web-mode-tag-next)) + (setq continue nil)) + ) ;while + ))) + +(defun web-mode-enable (feature) + "Enable one feature." + (interactive + (list (completing-read + "Feature: " + (let (features) + (dolist (elt web-mode-features) + (setq features (append features (list (car elt))))) + features)))) + (when (and (or (not feature) (< (length feature) 1)) web-mode-last-enabled-feature) + (setq feature web-mode-last-enabled-feature)) + (when feature + (setq web-mode-last-enabled-feature feature) + (setq feature (cdr (assoc feature web-mode-features))) + (cond + ((eq feature 'web-mode-enable-current-column-highlight) + (web-mode-column-show)) + ((eq feature 'web-mode-enable-current-element-highlight) + (when (not web-mode-enable-current-element-highlight) + (web-mode-toggle-current-element-highlight)) + ) + ((eq feature 'web-mode-enable-whitespace-fontification) + (web-mode-whitespaces-on)) + (t + (set feature t) + (web-mode-buffer-fontify)) + ) + ) ;when + ) + +(defun web-mode-disable (feature) + "Disable one feature." + (interactive + (list (completing-read + "Feature: " + (let (features) + (dolist (elt web-mode-features) + (setq features (append features (list (car elt))))) + features)))) + (when (and (or (not feature) (< (length feature) 1)) web-mode-last-enabled-feature) + (setq feature web-mode-last-enabled-feature)) + (when feature + (setq feature (cdr (assoc feature web-mode-features))) + (cond + ((eq feature 'web-mode-enable-current-column-highlight) + (web-mode-column-hide)) + ((eq feature 'web-mode-enable-current-element-highlight) + (when web-mode-enable-current-element-highlight + (web-mode-toggle-current-element-highlight)) + ) + ((eq feature 'web-mode-enable-whitespace-fontification) + (web-mode-whitespaces-off)) + (t + (set feature nil) + (web-mode-buffer-fontify)) + ) + ) ;when + ) + +(defun web-mode-toggle-current-element-highlight () + "Toggle highlighting of the current html element." + (interactive) + (if web-mode-enable-current-element-highlight + (progn + (web-mode-delete-tag-overlays) + (setq web-mode-enable-current-element-highlight nil)) + (setq web-mode-enable-current-element-highlight t) + )) + +(defun web-mode-make-tag-overlays () + (unless web-mode-overlay-tag-start + (setq web-mode-overlay-tag-start (make-overlay 1 1) + web-mode-overlay-tag-end (make-overlay 1 1)) + (overlay-put web-mode-overlay-tag-start + 'font-lock-face + 'web-mode-current-element-highlight-face) + (overlay-put web-mode-overlay-tag-end + 'font-lock-face + 'web-mode-current-element-highlight-face))) + +(defun web-mode-delete-tag-overlays () + (when web-mode-overlay-tag-start + (delete-overlay web-mode-overlay-tag-start) + (delete-overlay web-mode-overlay-tag-end))) + +(defun web-mode-column-overlay-factory (index) + (let (overlay) + (when (null web-mode-column-overlays) + (dotimes (_i 100) + (setq overlay (make-overlay 1 1)) + (overlay-put overlay 'font-lock-face 'web-mode-current-column-highlight-face) + (setq web-mode-column-overlays (append web-mode-column-overlays (list overlay))) + ) + ) ;when + (setq overlay (nth index web-mode-column-overlays)) + (when (null overlay) + (setq overlay (make-overlay 1 1)) + (overlay-put overlay 'font-lock-face 'web-mode-current-column-highlight-face) + (setq web-mode-column-overlays (append web-mode-column-overlays (list overlay))) + ) ;when + overlay)) + +(defun web-mode-column-hide () + (setq web-mode-enable-current-column-highlight nil) + (remove-overlays (point-min) (point-max) + 'font-lock-face + 'web-mode-current-column-highlight-face)) + +(defun web-mode-count-invisible-character-ranges (min max) + (interactive "r") + (let ((count 0) (current-pos min)) + (save-excursion + (while (<= current-pos max) + (goto-char current-pos) + (if (get-text-property current-pos 'invisible) + (progn + (setq count (1+ count)) + (setq current-pos (1+ current-pos)) + (while (and (<= current-pos max) + (get-text-property current-pos 'invisible)) + (setq current-pos (1+ current-pos)))) + (setq current-pos (1+ current-pos))))) + count)) + +(defun web-mode-column-show () + (let ((index 0) overlay diff column line-to line-from line-delta regions (overlay-skip nil) last-line-no) + (web-mode-column-hide) + (setq web-mode-enable-current-column-highlight t) + (save-mark-and-excursion + (back-to-indentation) + (setq column (current-column) + line-to (web-mode-line-number)) + (when (and (get-text-property (point) 'tag-beg) + (member (get-text-property (point) 'tag-type) '(start end)) + (web-mode-tag-match) + (setq line-from (web-mode-line-number)) + (not (= line-from line-to))) + (when (> line-from line-to) + (let (tmp) + (setq tmp line-from) + (setq line-from line-to) + (setq line-to tmp)) + ) ;when + ;;(message "column(%S) line-from(%S) line-to(%S)" column line-from line-to) + (goto-char (point-min)) + (when (> line-from 1) + (forward-line (1- line-from))) + ;; Added by JMA + (save-mark-and-excursion + (let (start-point end-point) + (goto-line line-from) + (move-to-column column) + (setq start-point (point)) + (goto-line line-to) + (move-to-column column) + (setq end-point (point)) + (setq line-delta (count-lines start-point end-point t)) + (setq line-delta (+ line-delta (web-mode-count-invisible-character-ranges start-point end-point)))) + (setq line-to (+ line-from (1- line-delta)))) + ;(message (format "Currently at line: %d" (line-number-at-pos))) + (setq last-line-no (line-number-at-pos)) + ;; end JMA add + (while (<= line-from line-to) + (setq overlay (web-mode-column-overlay-factory index)) + (setq diff (- (line-end-position) (point))) + (cond + ((or (and (= column 0) (= diff 0)) + (> column diff)) + (end-of-line) + (move-overlay overlay (point) (point)) + (overlay-put overlay + 'after-string + (concat + (if (> column diff) (make-string (- column diff) ?\s) "") + (propertize " " + 'font-lock-face + 'web-mode-current-column-highlight-face) + ) ;concat + ) + ) + (t + (move-to-column column) + (overlay-put overlay 'after-string nil) + (move-overlay overlay (point) (1+ (point))) + ) + ) ;cond + (setq line-from (1+ line-from)) + (forward-line) + ;; JMA ADD + ;(message (format "Currently at line: %d" (line-number-at-pos))) + (if (not (= (1+ last-line-no) (line-number-at-pos))) + (delete-overlay overlay)) + (setq last-line-no (line-number-at-pos)) + ;; END JMA ADD + (setq index (1+ index)) + ) ;while + ) ;when + ) ;save-mark-and-excursion + ) ;let + ) + +(defun web-mode-column-show2 () + (let ((index 0) overlay diff column line-to line-from + line-delta regions (overlay-skip nil) last-line-no) + (web-mode-column-hide) + (setq web-mode-enable-current-column-highlight t) + (save-excursion + (back-to-indentation) + (setq column (current-column) + line-to (web-mode-line-number)) + (when (and (get-text-property (point) 'tag-beg) + (member (get-text-property (point) 'tag-type) '(start end)) + (web-mode-tag-match) + (setq line-from (web-mode-line-number)) + (not (= line-from line-to))) + (when (> line-from line-to) + (let (tmp) + (setq tmp line-from) + (setq line-from line-to) + (setq line-to tmp)) + ) ;when + ;;(message "column(%S) line-from(%S) line-to(%S)" column line-from line-to) + (goto-char (point-min)) + (when (> line-from 1) + (forward-line (1- line-from))) + (while (<= line-from line-to) + (setq overlay (web-mode-column-overlay-factory index)) + (setq diff (- (line-end-position) (point))) + (cond + ((or (and (= column 0) (= diff 0)) + (> column diff)) + (end-of-line) + (move-overlay overlay (point) (point)) + (overlay-put overlay + 'after-string + (concat + (if (> column diff) (make-string (- column diff) ?\s) "") + (propertize " " + 'font-lock-face + 'web-mode-current-column-highlight-face) + ) ;concat + ) + ) + (t + (move-to-column column) + (overlay-put overlay 'after-string nil) + (move-overlay overlay (point) (1+ (point))) + ) + ) ;cond + (setq line-from (1+ line-from)) + (forward-line) + (setq index (1+ index)) + ) ;while + ) ;when + ) ;save-excursion + ) ;let + ) + +(defun web-mode-highlight-current-element () + (let ((ctx (web-mode-element-boundaries)) len) + (cond + ((null ctx) + (web-mode-delete-tag-overlays)) + ((eq (get-text-property (caar ctx) 'tag-type) 'void) ;; #1046 + (web-mode-make-tag-overlays) + (setq len (length (get-text-property (caar ctx) 'tag-name))) + (move-overlay web-mode-overlay-tag-start (+ (caar ctx) 1) (+ (caar ctx) 1 len)) + (move-overlay web-mode-overlay-tag-end (+ (cadr ctx) 1) (+ (cadr ctx) 1 len)) ;; #1257 + ) + (t + (web-mode-make-tag-overlays) + (setq len (length (get-text-property (caar ctx) 'tag-name))) + (move-overlay web-mode-overlay-tag-start (+ (caar ctx) 1) (+ (caar ctx) 1 len)) + (move-overlay web-mode-overlay-tag-end (+ (cadr ctx) 2) (+ (cadr ctx) 2 len)) + ) ;t + ) ;cond + )) + +(defun web-mode-fontify-whitespaces (beg end) + (save-excursion + (goto-char beg) + (while (re-search-forward web-mode-whitespaces-regexp end t) + (add-text-properties (match-beginning 0) (match-end 0) + '(face web-mode-whitespace-face)) + ) ;while + )) + +(defun web-mode-whitespaces-show () + "Toggle whitespaces." + (interactive) + (if web-mode-enable-whitespace-fontification + (web-mode-whitespaces-off) + (web-mode-whitespaces-on))) + +(defun web-mode-whitespaces-on () + "Show whitespaces." + (interactive) + (when web-mode-display-table + (setq buffer-display-table web-mode-display-table)) + (setq web-mode-enable-whitespace-fontification t)) + +(defun web-mode-whitespaces-off () + (setq buffer-display-table nil) + (setq web-mode-enable-whitespace-fontification nil)) + +(defun web-mode-use-tabs () + "Tweaks vars to be compatible with TAB indentation." + (let (offset) + (setq web-mode-block-padding 0) + (setq web-mode-script-padding 0) + (setq web-mode-style-padding 0) + (setq offset + (cond + ((and (boundp 'tab-width) tab-width) tab-width) + ((and (boundp 'standard-indent) standard-indent) standard-indent) + (t 4))) + ;; (message "offset(%S)" offset) + (setq web-mode-attr-indent-offset offset) + (setq web-mode-code-indent-offset offset) + (setq web-mode-css-indent-offset offset) + (setq web-mode-markup-indent-offset offset) + (setq web-mode-sql-indent-offset offset) + (add-to-list 'web-mode-indentation-params '("lineup-args" . nil)) + (add-to-list 'web-mode-indentation-params '("lineup-calls" . nil)) + (add-to-list 'web-mode-indentation-params '("lineup-concats" . nil)) + (add-to-list 'web-mode-indentation-params '("lineup-ternary" . nil)) + )) + +(defun web-mode-element-children-fold-or-unfold (&optional pos) + "Fold/Unfold all the children of the current html element." + (interactive) + (unless pos (setq pos (point))) + (save-excursion + (dolist (child (reverse (web-mode-element-children pos))) + (goto-char child) + (web-mode-fold-or-unfold)) + )) + +(defun web-mode-fold-or-unfold (&optional pos) + "Toggle folding on an html element or a control block." + (interactive) + (web-mode-scan) + (web-mode-with-silent-modifications + (save-excursion + (if pos (goto-char pos)) + (let (beg-inside beg-outside end-inside end-outside overlay overlays) + (when (looking-back "^[\t ]*" (point-min)) + (back-to-indentation)) + (setq overlays (overlays-at (point))) + (dolist (elt overlays) + (when (and (not overlay) + (eq (overlay-get elt 'font-lock-face) 'web-mode-folded-face)) + (setq overlay elt))) + (cond + ;; *** unfolding + (overlay + (setq beg-inside (overlay-start overlay) + end-inside (overlay-end overlay)) + (remove-overlays beg-inside end-inside) + (put-text-property beg-inside end-inside 'invisible nil) + ) + ;; *** block folding + ((and (get-text-property (point) 'block-side) + (cdr (web-mode-block-is-control (point)))) + (setq beg-outside (web-mode-block-beginning-position (point))) + (setq beg-inside (1+ (web-mode-block-end-position (point)))) + (when (web-mode-block-match) + (setq end-inside (point)) + (setq end-outside (1+ (web-mode-block-end-position (point))))) + ) + ;; *** html comment folding + ((eq (get-text-property (point) 'tag-type) 'comment) + (setq beg-outside (web-mode-tag-beginning-position)) + (setq beg-inside (+ beg-outside 4)) + (setq end-outside (web-mode-tag-end-position)) + (setq end-inside (- end-outside 3)) + ) + ;; *** tag folding + ((or (member (get-text-property (point) 'tag-type) '(start end)) + (web-mode-element-parent)) + (when (not (web-mode-element-is-collapsed (point))) + (web-mode-tag-beginning) + (when (eq (get-text-property (point) 'tag-type) 'end) + (web-mode-tag-match)) + (setq beg-outside (point)) + (web-mode-tag-end) + (setq beg-inside (point)) + (goto-char beg-outside) + (when (web-mode-tag-match) + (setq end-inside (point)) + (web-mode-tag-end) + (setq end-outside (point))) + ) + ) + ) ;cond + (when (and beg-inside beg-outside end-inside end-outside) + (setq overlay (make-overlay beg-outside end-outside)) + (overlay-put overlay 'font-lock-face 'web-mode-folded-face) + (put-text-property beg-inside end-inside 'invisible t)) + )))) + +;;---- TRANSFORMATION ---------------------------------------------------------- + +(defun web-mode-buffer-change-tag-case (&optional type) + "Change html tag case." + (interactive) + (save-excursion + (goto-char (point-min)) + (let ((continue t) f) + (setq f (if (member type '("upper" "uppercase" "upper-case")) 'uppercase 'downcase)) + (when (and (not (get-text-property (point) 'tag-beg)) + (not (web-mode-tag-next))) + (setq continue nil)) + (while continue + (skip-chars-forward " and < in html content." + (interactive) + (save-excursion + (let ((min (point-min)) (max (point-max))) + (when mark-active + (setq min (region-beginning) + max (region-end)) + (deactivate-mark)) + (goto-char min) + (while (web-mode-content-rsf "[&<>]" max) + (replace-match (cdr (assq (char-before) web-mode-xml-chars)) t t)) + ))) + +(defun web-mode-dom-quotes-replace () + "Replace dumb quotes." + (interactive) + (save-excursion + (let (expr (min (point-min)) (max (point-max))) + (when mark-active + (setq min (region-beginning) + max (region-end)) + (deactivate-mark)) + (goto-char min) + (setq expr (concat (car web-mode-smart-quotes) "\\2" (cdr web-mode-smart-quotes))) + (while (web-mode-content-rsf "\\(\"\\)\\(.\\{1,200\\}\\)\\(\"\\)" max) + (replace-match expr) + ) ;while + ))) + +;;---- INDENTATION ------------------------------------------------------------- + +;; todo : passer de règle en règle et mettre un \n à la fin +(defun web-mode-css-indent () + (save-excursion + (goto-char (point-min)) + (let ((continue t) part-end) + (while continue + (cond + ((not (web-mode-part-next)) + (setq continue nil)) + ((eq (get-text-property (point) 'part-side) 'css) + (setq part-end (web-mode-part-end-position)) + (while (web-mode-css-rule-next part-end) + (when (not (looking-at-p "[[:space:]]*\\($\\|<\\)")) + (newline) + (indent-according-to-mode) + (setq part-end (web-mode-part-end-position))) + ) + ) + ) ;cond + ) + ))) + +(defun web-mode-buffer-indent () + "Indent all buffer." + (interactive) + (let ((debug t) (ts (current-time)) (sub nil)) + (indent-region (point-min) (point-max)) + (when debug + (setq sub (time-subtract (current-time) ts)) + (message "buffer-indent: time elapsed = %Ss %9Sµs" (nth 1 sub) (nth 2 sub))) + (delete-trailing-whitespace))) + +(defun web-mode-point-context (pos) + "POS should be at the beginning of the indentation." + (save-excursion + (let (curr-char curr-indentation curr-line + language + options + reg-beg reg-col + prev-char prev-indentation prev-line prev-pos + token + part-language + depth) + + (setq reg-beg (point-min) + reg-col 0 + token "live" + options "" + language "" + prev-line "" + prev-char 0 + prev-pos nil) + + (when (get-text-property pos 'part-side) + (setq part-language (symbol-name (get-text-property pos 'part-side)))) + + ;;(message "part-language=%S" part-language) + + (cond + + ((and (bobp) (member web-mode-content-type '("html" "xml"))) + (setq language web-mode-content-type) + ) + + ((string= web-mode-content-type "css") + (setq language "css" + curr-indentation web-mode-css-indent-offset)) + + ((member web-mode-content-type '("javascript" "json" "typescript")) + (setq language web-mode-content-type + curr-indentation web-mode-code-indent-offset)) + + ((or (string= web-mode-content-type "jsx") + (and part-language (string= part-language "jsx"))) + (setq language "jsx" + curr-indentation web-mode-code-indent-offset) + (cond + ((web-mode-jsx-is-html pos) + (setq curr-indentation web-mode-markup-indent-offset + options "is-html")) + ((and (setq depth (get-text-property pos 'jsx-depth)) (> depth 1)) + (when (get-text-property pos 'jsx-beg) + (setq depth (1- depth))) + (setq reg-beg (web-mode-jsx-depth-beginning-position pos depth)) + (setq reg-beg (1+ reg-beg)) + ;;(message "%S" (point)) + (save-excursion + (goto-char reg-beg) + ;;(message "pt=%S" reg-beg) + (cond + ((and (not (looking-at-p "[ ]*$")) + (looking-back "^[[:space:]]*{" (point-min))) + (setq reg-col (+ (current-indentation) ;; #1027 + (cond + ((looking-at "[ ]+") (1+ (length (match-string-no-properties 0)))) + (t 0)) + )) + ) + ((looking-at-p "[ ]*\\[[ ]*$") ;; #0659 + (setq reg-col (current-indentation)) + ) + ((and (looking-back "=[ ]*{" (point-min)) ;; #0739 #1022 + (not (looking-at-p "[[:space:]]*<"))) + (setq reg-col (current-indentation)) + ) + ;;((and (looking-back "=[ ]*{" (point-min)) ;; #0739 + ;; (looking-at-p "{[ ]*")) + ;; (setq reg-col (current-indentation)) + ;; ) + ((get-text-property (1- (point)) 'tag-beg) + ;;(message "point=%S" (point)) + (setq reg-col (current-indentation)) + ) + (t + (message "%S : %S %S" (point) (current-indentation) web-mode-code-indent-offset) + ;;(setq reg-col (+ (current-indentation) web-mode-code-indent-offset web-mode-jsx-expression-padding))) + (setq reg-col (+ (current-indentation) web-mode-code-indent-offset))) + ) + + ;;(message "%S %S %S" (point) (current-indentation) reg-col) + ) ;save-excursion + ) + ((string= web-mode-content-type "jsx") + (setq reg-beg (point-min))) + (t + (setq reg-beg (or (web-mode-part-beginning-position pos) (point-min))) + (save-excursion + (goto-char reg-beg) + (search-backward "<" nil t) + (setq reg-col (current-column)) + ) ;save-excursion + ) + ) ;cond + ;;(message "jsx reg-beg=%S" reg-beg) + ) ;jsx + + ((string= web-mode-content-type "php") + (setq language "php" + curr-indentation web-mode-code-indent-offset)) + + ((or (string= web-mode-content-type "xml")) + (setq language "xml" + curr-indentation web-mode-markup-indent-offset)) + + ;; TODO: est ce util ? + ((and (get-text-property pos 'tag-beg) + (get-text-property pos 'tag-name) + ;;(not (get-text-property pos 'part-side)) + ) + (setq language "html" + curr-indentation web-mode-markup-indent-offset)) + + ((and (get-text-property pos 'block-side) + (not (get-text-property pos 'block-beg))) + + (setq reg-beg (or (web-mode-block-beginning-position pos) (point-min))) + (goto-char reg-beg) + (setq reg-col (current-column)) + ;;(message "%S %S" reg-beg reg-col) + (setq language web-mode-engine) + (setq curr-indentation web-mode-code-indent-offset) + + (cond + ((string= web-mode-engine "blade") + (save-excursion + (when (web-mode-rsf "{[{!]+[ ]*") + (setq reg-col (current-column)))) + (setq reg-beg (+ reg-beg 2)) + ) + ((string= web-mode-engine "razor") + ;;(setq reg-beg (+ reg-beg 2)) + ;;(setq reg-col (current-column)) + ) + ;; tests/demo.chtml + ((string= web-mode-engine "ctemplate") + (save-excursion + (when (web-mode-rsf "{{#?") + (setq reg-col (current-column)))) + ) + ((string= web-mode-engine "dust") + (save-excursion + (when (web-mode-rsf "{@") + (setq reg-col (current-column)))) + ) + ((string= web-mode-engine "svelte") + (save-excursion + (when (web-mode-rsf "{@") + (setq reg-col (current-column)))) + ) + ((string= web-mode-engine "template-toolkit") + (setq reg-beg (+ reg-beg 3) + reg-col (+ reg-col 3)) + ) + ((and (string= web-mode-engine "jsp") + (web-mode-looking-at "<%@" reg-beg)) + (save-excursion + (goto-char reg-beg) + (looking-at "<%@[ ]*[[:alpha:]]+[ ]+\\| pos (point-min)) + (eq (get-text-property pos 'part-token) 'comment) + (eq (get-text-property (1- pos) 'part-token) 'comment) + (progn + (setq reg-beg (previous-single-property-change pos 'part-token)) + t)) + (and (> pos (point-min)) + (eq (get-text-property pos 'block-token) 'comment) + (eq (get-text-property (1- pos) 'block-token) 'comment) + (progn + (setq reg-beg (previous-single-property-change pos 'block-token)) + t)) + (and (> pos (point-min)) + (eq (get-text-property pos 'tag-type) 'comment) + (not (get-text-property pos 'tag-beg)) + (progn + (setq reg-beg (web-mode-tag-beginning-position pos)) + t)) + ) + (setq token "comment")) + ((or (and (> pos (point-min)) + (member (get-text-property pos 'part-token) + '(string context key)) + (member (get-text-property (1- pos) 'part-token) + '(string context key))) + (and (eq (get-text-property pos 'block-token) 'string) + (eq (get-text-property (1- pos) 'block-token) 'string))) + (setq token "string")) + ) + + (goto-char pos) + (setq curr-line (web-mode-trim + (buffer-substring-no-properties + (line-beginning-position) + (line-end-position)))) + (setq curr-char (if (string= curr-line "") 0 (aref curr-line 0))) + + (when (or (member language '("php" "blade" "javascript" "typescript" "jsx" "razor" "css")) + (and (member language '("html" "xml")) + (not (eq ?\< curr-char)))) + (let (prev) + (cond + ((member language '("html" "xml" "javascript" "typescript" "jsx" "css")) + (when (setq prev (web-mode-part-previous-live-line reg-beg)) + (setq prev-line (nth 0 prev) + prev-indentation (nth 1 prev) + prev-pos (nth 2 prev)) + ) + ) + ((setq prev (web-mode-block-previous-live-line)) + (setq prev-line (car prev) + prev-indentation (cdr prev)) + (setq prev-line (web-mode-clean-block-line prev-line))) + ) ;cond + ) ;let + (when (>= (length prev-line) 1) + (setq prev-char (aref prev-line (1- (length prev-line)))) + (setq prev-line (substring-no-properties prev-line)) + ) + ) + + (cond + ((not (member web-mode-content-type '("html" "xml"))) + ) + ((member language '("javascript" "typescript" "jsx" "ruby")) + (setq reg-col (if web-mode-script-padding (+ reg-col web-mode-script-padding) 0))) + ((member language '("css" "sql" "markdown" "pug" "sass" "stylus")) + (setq reg-col (if web-mode-style-padding (+ reg-col web-mode-style-padding) 0))) + ((not (member language '("html" "xml"))) + (setq reg-col + (cond + ((not web-mode-block-padding) reg-col) + ((eq web-mode-block-padding -1) 0) + (t (+ reg-col web-mode-block-padding)) + ) ;cond + ) ;setq + ) + ) + + (list :curr-char curr-char + :curr-indentation curr-indentation + :curr-line curr-line + :language language + :options options + :prev-char prev-char + :prev-indentation prev-indentation + :prev-line prev-line + :prev-pos prev-pos + :reg-beg reg-beg + :reg-col reg-col + :token token) + ))) + +(defun web-mode-indent-line () + + (web-mode-scan) + + (let ((offset nil) + (char nil) + (debug nil) + (inhibit-modification-hooks nil) + (adjust t)) + + (save-excursion + (back-to-indentation) + (setq char (char-after)) + (let* ((pos (point)) + (ctx (web-mode-point-context pos)) + (curr-char (plist-get ctx :curr-char)) + (curr-indentation (plist-get ctx :curr-indentation)) + (curr-line (plist-get ctx :curr-line)) + (language (plist-get ctx :language)) + (prev-char (plist-get ctx :prev-char)) + (prev-indentation (plist-get ctx :prev-indentation)) + (prev-line (plist-get ctx :prev-line)) + (prev-pos (plist-get ctx :prev-pos)) + (reg-beg (plist-get ctx :reg-beg)) + (reg-col (plist-get ctx :reg-col)) + (token (plist-get ctx :token)) + (options (plist-get ctx :options)) + (chars (list curr-char prev-char)) + (tmp nil) + (is-js (member language '("javascript" "jsx" "ejs" "typescript")))) + + (when (member language '("json" "typescript")) + (setq language "javascript")) + + ;;(message "%S %S" (plist-get ctx :language) language) + ;;(message "curr-char=[%c] prev-char=[%c]\n%S" curr-char prev-char ctx) + ;;(message "options=%S" ctx) + + (cond + + ((or (bobp) (= (line-number-at-pos pos) 1)) + (when debug (message "I100(%S) first line" pos)) + (setq offset 0)) + + ;; #123 #1145 + ((and web-mode-enable-front-matter-block + (eq (char-after (point-min)) ?\-) + (or (looking-at-p "---") + (search-forward "---" (point-max) t))) + (when debug (message "I108(%S) front-matter-block" pos)) + (setq offset nil)) + + ;; #1073 + ((get-text-property pos 'invisible) + (when debug (message "I110(%S) invible" pos)) + (setq offset nil)) + + ((string= token "string") + (when debug (message "I120(%S) string" pos)) + (cond + ((web-mode-is-token-end pos) + (if (get-text-property pos 'block-side) + (web-mode-block-token-beginning) + (web-mode-part-token-beginning)) + (setq offset (current-indentation)) + ) + ((and web-mode-enable-sql-detection + (web-mode-block-token-starts-with (concat "[ \n]*" web-mode-sql-queries))) + (save-excursion + (let (col) + (web-mode-block-string-beginning) + (skip-chars-forward "[ \"'\n]") + (setq col (current-column)) + (goto-char pos) + (if (looking-at-p "\\(SELECT\\|INSERT\\|DELETE\\|UPDATE\\|FROM\\|LEFT\\|JOIN\\|WHERE\\|GROUP BY\\|LIMIT\\|HAVING\\|\)\\)") + (setq offset col) + (setq offset (+ col web-mode-sql-indent-offset))) + ) + ) ;save-excursion + ) + ((and is-js + (web-mode-is-ql-string pos "Relay\.QL")) + (setq offset (web-mode-relayql-indentation pos)) + ) + ((and is-js + (web-mode-is-ql-string pos "gql")) + (setq offset (web-mode-relayql-indentation pos "gql")) + ) + ((and is-js + (web-mode-is-ql-string pos "graphql")) + (setq offset (web-mode-relayql-indentation pos "graphql")) + ) + ((and is-js + (web-mode-is-css-string pos)) + (when debug (message "I127(%S) css string" pos)) + (setq offset (web-mode-token-css-indentation pos)) + ) + ((and is-js + (web-mode-is-html-string pos)) + (when debug (message "I128(%S) html string" pos)) + (setq offset (web-mode-token-html-indentation pos)) + ) + (t + (setq offset nil)) + ) ;cond + ) ;case string + + ((string= token "comment") + (when debug (message "I130(%S) comment" pos)) + (if (eq (get-text-property pos 'tag-type) 'comment) + (web-mode-tag-beginning) + (goto-char (car + (web-mode-property-boundaries + (if (eq (get-text-property pos 'part-token) 'comment) + 'part-token + 'block-token) + pos)))) + (setq offset (current-column)) + (cond + ((string= web-mode-engine "freemarker") + (setq offset (+ (current-indentation) 2))) + ((member (buffer-substring-no-properties (point) (+ (point) 2)) '("/*" "{*" "@*")) + (cond + ((eq ?\* curr-char) + (setq offset (+ offset 1))) + (t + (setq offset (+ offset 3))) + ) ;cond + ) + ((string= (buffer-substring-no-properties (point) (+ (point) 4)) "" curr-line) + (setq offset offset)) + ((string-match-p "^-" curr-line) + (setq offset (+ offset 3))) + (t + (setq offset (+ offset web-mode-markup-comment-indent-offset))) + ) ;cond + ) + ((and (string= web-mode-engine "django") (looking-back "{% comment %}" (point-min))) + (setq offset (- offset 12))) + ((and (string= web-mode-engine "mako") (looking-back "<%doc%>" (point-min))) + (setq offset (- offset 6))) + ((and (string= web-mode-engine "mason") (looking-back "<%doc%>" (point-min))) + (setq offset (- offset 6))) + ) ;cond + ) ;case comment + + ((and (string= web-mode-engine "mason") + (string-match-p "^%" curr-line)) + (when debug (message "I140(%S) mason" pos)) + (setq offset 0)) + + ((and (string= web-mode-engine "razor") + (string-match-p "^\\([{}]\\|else\\)" curr-line)) + (when debug (message "I142(%S) razor" pos)) + (save-excursion + (web-mode-block-previous) + (setq offset (current-indentation)) + )) + + ((and (string= web-mode-engine "django") + (string-match-p "^#" curr-line)) + (when debug (message "I144(%S) django line statements" pos)) + (setq offset 0)) + + ((and (get-text-property pos 'block-beg) + (or (web-mode-block-is-close pos) + (web-mode-block-is-inside pos))) + (when debug (message "I150(%S) block-match" pos)) + (cond + ((not (web-mode-block-match)) + ) + ((and (string= web-mode-engine "closure") + (string-match-p "{\\(case\\|default\\)" curr-line)) + (setq offset (+ (current-indentation) web-mode-markup-indent-offset))) + (t + (setq offset (current-indentation)) + (if (and (string= web-mode-engine "blade") + (string-match-p "@break" curr-line)) + (setq offset (+ (current-indentation) offset))) + ) + ) ;cond + ) + + ((eq (get-text-property pos 'block-token) 'delimiter-end) + (when debug (message "I160(%S) block-beginning" pos)) + (when (web-mode-block-beginning) + (setq reg-col (current-indentation)) + (setq offset (current-column)))) + + ((or (and (get-text-property pos 'tag-beg) + (eq (get-text-property pos 'tag-type) 'end)) + (and (eq (get-text-property pos 'tag-type) 'comment) + (string-match-p "" (point)) + (web-mode-insert-text-at-pos "" (point)) + (web-mode-insert-text-at-pos "") + (search-backward " -->") + ) ;case html + ) ;cond + )) + +(defun web-mode-comment (pos) + (let (ctx language col sel beg end block-side single-line-block pos-after content) + + (setq pos-after pos) + + (setq block-side (get-text-property pos 'block-side)) + (setq single-line-block (web-mode-is-single-line-block pos)) + + (cond + + ((and block-side (string= web-mode-engine "erb")) + (web-mode-comment-erb-block pos) + ) + + ((and block-side (string= web-mode-engine "artanis")) + (web-mode-comment-artanis-block pos) + ) + + ((and single-line-block block-side + (intern-soft (concat "web-mode-comment-" web-mode-engine "-block"))) + (funcall (intern (concat "web-mode-comment-" web-mode-engine "-block")) pos) + ) + + (t + (setq ctx (web-mode-point-context + (if mark-active (region-beginning) (line-beginning-position)))) + ;;(message "%S" ctx) + (setq language (plist-get ctx :language)) + (setq col (current-column)) + (cond + (mark-active + ;;(message "%S %S" (point) col) + ) + ((and (member language '("html" "xml")) + (get-text-property (progn (back-to-indentation) (point)) 'tag-beg)) + (web-mode-element-select)) + (t + (end-of-line) + (set-mark (line-beginning-position))) + ) ;cond + + (setq beg (region-beginning) + end (region-end)) + + (when (> (point) (mark)) + (exchange-point-and-mark)) + + (if (and (eq (char-before end) ?\n) + (not (eq (char-after end) ?\n))) + (setq end (1- end))) + + (setq sel (buffer-substring-no-properties beg end)) + + (cond + + ((member language '("html" "xml")) + (cond + ((and (= web-mode-comment-style 2) (string= web-mode-engine "django")) + (setq content (concat "{# " sel " #}"))) + ((and (= web-mode-comment-style 2) (member web-mode-engine '("ejs" "erb"))) + (setq content (concat "<%# " sel " %>"))) + ((and (= web-mode-comment-style 2) (string= web-mode-engine "artanis")) + (setq content (concat "<%; " sel " %>"))) + ((and (= web-mode-comment-style 2) (string= web-mode-engine "aspx")) + (setq content (concat "<%-- " sel " --%>"))) + ((and (= web-mode-comment-style 2) (string= web-mode-engine "smarty")) + (setq content (concat "{* " sel " *}"))) + ((and (= web-mode-comment-style 2) (string= web-mode-engine "expressionengine")) + (setq content (concat "{!-- " sel " --}"))) + ((and (= web-mode-comment-style 2) (string= web-mode-engine "xoops")) + (setq content (concat "<{* " sel " *}>"))) + ((and (= web-mode-comment-style 2) (string= web-mode-engine "hero")) + (setq content (concat "<%# " sel " %>"))) + ((and (= web-mode-comment-style 2) (string= web-mode-engine "blade")) + (setq content (concat "{{-- " sel " --}}"))) + ((and (= web-mode-comment-style 2) (string= web-mode-engine "ctemplate")) + (setq content (concat "{{!-- " sel " --}}"))) + ((and (= web-mode-comment-style 2) (string= web-mode-engine "antlers")) + (setq content (concat "{{# " sel " #}}"))) + ((and (= web-mode-comment-style 2) (string= web-mode-engine "razor")) + (setq content (concat "@* " sel " *@"))) + (t + (setq content (concat "")) + (when (< (length sel) 1) + (search-backward " -->") + (setq pos-after nil)) + )) + ) ;case html + + ((member language '("php" "javascript" "typescript" "java" "jsx")) + (let (alt) + (setq alt (cdr (assoc language web-mode-comment-formats))) + ;;(message "language=%S alt=%S sel=%S col=%S" language alt sel col) + (cond + ((and alt (string= alt "//")) + (setq content (replace-regexp-in-string (concat "\n[ ]\\{" (number-to-string col) "\\}") "\n" sel)) + (setq content (replace-regexp-in-string (concat "\n") "\n// " content)) + (setq content (concat "// " content))) + ((get-text-property pos 'jsx-depth) + (setq content (concat "{/* " sel " */}"))) + (web-mode-comment-prefixing + (setq content (replace-regexp-in-string (concat "\n[ ]\\{" (number-to-string col) "\\}") "\n* " sel)) + (setq content (concat "/* " content " */"))) + (t + (setq content (concat "/* " sel " */"))) + ) ;cond + ) ;let + ) + + ((member language '("erb")) + (setq content (replace-regexp-in-string "^[ ]*" "#" sel))) + + ((member language '("asp")) + (setq content (replace-regexp-in-string "^[ ]*" "'" sel))) + + (t + (setq content (concat "/* " sel " */"))) + + ) ;cond + + (when content + (delete-region beg end) + (deactivate-mark) + (let (beg end) + (setq beg (line-beginning-position)) + (insert content) + (setq end (line-end-position)) + (indent-region beg end) + ) + ) ;when + + ) ;t + ) ;cond + + (when pos-after (goto-char pos-after)) + + )) + +(defun web-mode-comment-ejs-block (pos) + (let (beg end) + (setq beg (web-mode-block-beginning-position pos) + end (web-mode-block-end-position pos)) + (web-mode-insert-text-at-pos "//" (+ beg 2)))) + +(defun web-mode-comment-erb-block (pos) + (let (beg end) + (setq beg (web-mode-block-beginning-position pos) + end (web-mode-block-end-position pos)) + (web-mode-insert-text-at-pos "#" (+ beg 2)))) + +(defun web-mode-comment-artanis-block (pos) + (let (beg end) + (setq beg (web-mode-block-beginning-position pos) + end (web-mode-block-end-position pos)) + (web-mode-insert-text-at-pos ";" (+ beg 2)))) + +(defun web-mode-comment-django-block (pos) + (let (beg end) + (setq beg (web-mode-block-beginning-position pos) + end (web-mode-block-end-position pos)) + (web-mode-insert-text-at-pos "#" end) + (web-mode-insert-text-at-pos "#" (1+ beg)))) + +(defun web-mode-comment-dust-block (pos) + (let (beg end) + (setq beg (web-mode-block-beginning-position pos) + end (web-mode-block-end-position pos)) + (web-mode-insert-text-at-pos "!" end) + (web-mode-insert-text-at-pos "!" (1+ beg)))) + +(defun web-mode-comment-aspx-block (pos) + (let (beg end) + (setq beg (web-mode-block-beginning-position pos) + end (web-mode-block-end-position pos)) + (web-mode-insert-text-at-pos "#" end) + (web-mode-insert-text-at-pos "#" (1+ beg)))) + +(defun web-mode-comment-jsp-block (pos) + (let (beg end) + (setq beg (web-mode-block-beginning-position pos) + end (web-mode-block-end-position pos)) + (web-mode-insert-text-at-pos "--" (+ beg 2)))) + +(defun web-mode-comment-go-block (pos) + (let (beg end) + (setq beg (web-mode-block-beginning-position pos) + end (web-mode-block-end-position pos)) + (web-mode-insert-text-at-pos "*/" (1- end)) + (web-mode-insert-text-at-pos "/*" (+ beg (if (web-mode-looking-at "{{" beg) 2 0))))) + +(defun web-mode-comment-php-block (pos) + (let (beg end) + (setq beg (web-mode-block-beginning-position pos) + end (web-mode-block-end-position pos)) + (web-mode-insert-text-at-pos "*/" (- end 2)) + (web-mode-insert-text-at-pos "/*" (+ beg 1 (if (web-mode-looking-at "<\\?php" beg) 5 3))))) + +(defun web-mode-comment-svelte-block (pos) + (let (beg end) + (setq beg (web-mode-block-beginning-position pos) + end (web-mode-block-end-position pos)) + (web-mode-insert-text-at-pos "!" end) + (web-mode-insert-text-at-pos "!" (1+ beg)))) + +(defun web-mode-comment-boundaries (&optional pos) + (interactive) + (unless pos (setq pos (point))) + (let ((beg pos) (end pos) prop) + (save-excursion + (goto-char pos) + (setq prop + (cond + ((eq (get-text-property pos 'block-token) 'comment) 'block-token) + ((eq (get-text-property pos 'tag-type) 'comment) 'tag-type) + ((eq (get-text-property pos 'part-token) 'comment) 'part-token) + (t nil) + )) + (if (null prop) + (setq beg nil + end nil) + (when (and (not (bobp)) + (eq (get-text-property pos prop) (get-text-property (1- pos) prop))) + (setq beg (or (previous-single-property-change pos prop) (point-min)))) + (when (and (not (eobp)) + (eq (get-text-property pos prop) (get-text-property (1+ pos) prop))) + (setq end (or (next-single-property-change pos prop) (point-max))))) + (message "beg(%S) end(%S) point-max(%S)" beg end (point-max)) + (when (and beg (string= (buffer-substring-no-properties beg (+ beg 2)) "//")) + (goto-char end) + (while (and (looking-at-p "\n[ ]*//") + (not (eobp))) + (search-forward "//") + (backward-char 2) + ;;(message "%S" (point)) + (setq end (next-single-property-change (point) prop)) + (goto-char end) + ;;(message "%S" (point)) + ) ;while + ) ;when + ;;(when end (setq end (1- end))) ;; #1021 + ) ;save-excursion + ;;(message "beg=%S end=%S" beg end) + (if (and beg end) (cons beg end) nil) + )) + +(defun web-mode-uncomment (pos) + (let ((beg pos) (end pos) (sub2 "") comment boundaries) + (save-excursion + (cond + ((and (get-text-property pos 'block-side) + (intern-soft (concat "web-mode-uncomment-" web-mode-engine "-block"))) + (funcall (intern (concat "web-mode-uncomment-" web-mode-engine "-block")) pos)) + ((and (setq boundaries (web-mode-comment-boundaries pos)) + (setq beg (car boundaries)) + (setq end (1+ (cdr boundaries))) + (> (- end beg) 4)) + (when (and (eq (get-text-property beg 'part-token) 'comment) + (> beg 1) ;#1158 + (get-text-property (1- beg) 'jsx-beg)) + (setq beg (1- beg) + end (1+ end))) + (setq comment (buffer-substring-no-properties beg end)) + (setq sub2 (substring comment 0 2)) + (cond + ((member sub2 '("$\\)" "" comment))) + ((string= sub2 "{#") + (setq comment (replace-regexp-in-string "\\(^{#[ ]?\\|[ ]?#}$\\)" "" comment))) + ((string= sub2 "{/") ;jsx comments + (setq comment (replace-regexp-in-string "\\(^{/\\*[ ]?\\|[ ]?\\*/}$\\)" "" comment))) + ((string= sub2 "/*") + ;;(message "%S" comment) + ;;(setq comment (replace-regexp-in-string "\\(\\*/\\|^/\\*[ ]?\\|^[ \t]*\\*\\)" "" comment)) + (setq comment (replace-regexp-in-string "\\([ ]?\\*/$\\|^/\\*[ ]?\\)" "" comment)) + (setq comment (replace-regexp-in-string "\\(^[ \t]*\\*\\)" "" comment)) + ;;(message "%S" comment) + ) + ((string= sub2 "''") + (setq comment (replace-regexp-in-string "''" "" comment))) + ((string= sub2 "//") + (setq comment (replace-regexp-in-string "^ *//" "" comment))) + ) ;cond + (delete-region beg end) + (web-mode-insert-and-indent comment) + (goto-char beg) + ) + ) ;cond + (indent-according-to-mode) + ))) + +(defun web-mode-uncomment-erb-block (pos) + (let (beg end) + (setq beg (web-mode-block-beginning-position pos) + end (web-mode-block-end-position pos)) + (cond + ((string= (buffer-substring-no-properties beg (+ beg 4)) "<%#=") + (web-mode-remove-text-at-pos 1 (+ beg 2))) + ((string-match-p "<[%[:alpha:]]" (buffer-substring-no-properties (+ beg 2) (- end 2))) + (web-mode-remove-text-at-pos 2 (1- end)) + (web-mode-remove-text-at-pos 3 beg)) + (t + (web-mode-remove-text-at-pos 1 (+ beg 2))) + ) ;cond + ) + ) + +(defun web-mode-uncomment-artanis-block (pos) + (let (beg end) + (setq beg (web-mode-block-beginning-position pos) + end (web-mode-block-end-position pos)) + (cond + ((string= (buffer-substring-no-properties beg (+ beg 4)) "<%;=") + (web-mode-remove-text-at-pos 1 (+ beg 2))) + ((string-match-p "<[%[:alpha:]]" (buffer-substring-no-properties (+ beg 2) (- end 2))) + (web-mode-remove-text-at-pos 2 (1- end)) + (web-mode-remove-text-at-pos 3 beg)) + (t + (web-mode-remove-text-at-pos 1 (+ beg 2))) + ) ;cond + ) + ) + +(defun web-mode-uncomment-ejs-block (pos) + (let (beg end) + (setq beg (web-mode-block-beginning-position pos) + end (web-mode-block-end-position pos)) + (web-mode-remove-text-at-pos 1 (+ beg 2)))) + +(defun web-mode-uncomment-django-block (pos) + (let (beg end) + (setq beg (web-mode-block-beginning-position pos) + end (web-mode-block-end-position pos)) + (cond + ((web-mode-looking-at-p "{#[{%]" beg) + (web-mode-remove-text-at-pos 1 (1- end)) + (web-mode-remove-text-at-pos 1 (1+ beg)) + ) + (t + (web-mode-remove-text-at-pos 2 (1- end)) + (web-mode-remove-text-at-pos 2 beg)) + ) ;cond + )) + +(defun web-mode-uncomment-ctemplate-block (pos) + (let (beg end) + (setq beg (web-mode-block-beginning-position pos) + end (web-mode-block-end-position pos)) + (web-mode-remove-text-at-pos 5 (- end 4)) + (web-mode-remove-text-at-pos 5 beg))) + +(defun web-mode-uncomment-antlers-block (pos) + (let (beg end) + (setq beg (web-mode-block-beginning-position pos) + end (web-mode-block-end-position pos)) + (web-mode-remove-text-at-pos 3 (- end 2)) + (web-mode-remove-text-at-pos 3 beg))) + +(defun web-mode-uncomment-dust-block (pos) + (let (beg end) + (setq beg (web-mode-block-beginning-position pos) + end (web-mode-block-end-position pos)) + (web-mode-remove-text-at-pos 1 (1- end)) + (web-mode-remove-text-at-pos 1 (1+ beg)))) + +(defun web-mode-uncomment-aspx-block (pos) + (let (beg end) + (setq beg (web-mode-block-beginning-position pos) + end (web-mode-block-end-position pos)) + (web-mode-remove-text-at-pos 1 (1- end)) + (web-mode-remove-text-at-pos 1 (1+ beg)))) + +(defun web-mode-uncomment-jsp-block (pos) + (let (beg end) + (setq beg (web-mode-block-beginning-position pos) + end (web-mode-block-end-position pos)) + (web-mode-remove-text-at-pos 2 (+ beg 2)))) + +(defun web-mode-uncomment-go-block (pos) + (let (beg end) + (setq beg (web-mode-block-beginning-position pos) + end (web-mode-block-end-position pos)) + (web-mode-remove-text-at-pos 2 (+ beg 2)) + (web-mode-remove-text-at-pos 2 (- end 5)))) + +(defun web-mode-uncomment-svelte-block (pos) + (let (beg end) + (setq beg (web-mode-block-beginning-position pos) + end (web-mode-block-end-position pos)) + (web-mode-remove-text-at-pos 1 (1- end)) + (web-mode-remove-text-at-pos 1 (1+ beg)))) + +(defun web-mode-snippet-names () + (mapcar #'car web-mode-snippets)) + +(defun web-mode-snippet-insert (code) + "Insert a snippet." + (interactive + (list (completing-read "Snippet: " (web-mode-snippet-names)))) + (let (beg + (continue t) + (counter 0) + end + sel + snippet + (l (length web-mode-snippets)) + pos) + (when mark-active + (setq sel (web-mode-trim (buffer-substring-no-properties + (region-beginning) (region-end)))) + (delete-region (region-beginning) (region-end))) + (while (and continue (< counter l)) + (setq snippet (nth counter web-mode-snippets)) + (when (string= (car snippet) code) + (setq continue nil)) + (setq counter (1+ counter))) + (when snippet + (setq snippet (cdr snippet)) + (setq beg (line-beginning-position)) + (insert snippet) + (setq pos (point) + end (point)) + (cond + ((string-match-p "¦" snippet) + (search-backward "¦") + (delete-char 1) + (setq pos (point) + end (1- end))) + ((string-match-p "|" snippet) + (search-backward "|") + (delete-char 1) + (setq pos (point) + end (1- end))) + ) ;cond + (when sel + (insert sel) + (setq pos (point) + end (+ end (length sel)))) + (goto-char end) + (setq end (line-end-position)) + (unless sel (goto-char pos)) + (indent-region beg end)) + )) + +(defun web-mode-looking-at (regexp pos) + (save-excursion + (goto-char pos) + (looking-at regexp))) + +(defun web-mode-looking-at-p (regexp pos) + (save-excursion + (goto-char pos) + (looking-at-p regexp))) + +(defun web-mode-looking-back (regexp pos &optional limit greedy) + (save-excursion + (goto-char pos) + (if limit + (looking-back regexp limit greedy) + (looking-back regexp (point-min))))) + +(defun web-mode-insert-text-at-pos (text pos) + (let ((mem web-mode-enable-auto-pairing)) + (setq web-mode-enable-auto-pairing nil) + (save-excursion + (goto-char pos) + (insert text) + (setq web-mode-enable-auto-pairing mem) + ))) + +(defun web-mode-remove-text-at-pos (n &optional pos) + (unless pos (setq pos (point))) + (delete-region pos (+ pos n))) + +(defun web-mode-insert-and-indent (text) + (let (beg end) + (setq beg (line-beginning-position)) + (insert text) + (setq end (line-end-position)) + (indent-region beg end) + )) + +(defun web-mode-column-at-pos (pos) + (save-excursion + (goto-char pos) + (current-column))) + +(defun web-mode-indentation-at-pos (pos) + (save-excursion + (goto-char pos) + (current-indentation))) + +(defun web-mode-navigate (&optional pos) + "Move point to the matching opening/closing tag/block." + (interactive) + (unless pos (setq pos (point))) + (let (init) + (goto-char pos) + (setq init (point)) + (when (> (current-indentation) (current-column)) + (back-to-indentation)) + (setq pos (point)) + (cond + ((and (get-text-property pos 'block-side) + (web-mode-block-beginning) + (web-mode-block-controls-get (point))) + (web-mode-block-match)) + ((member (get-text-property pos 'tag-type) '(start end)) + (web-mode-tag-beginning) + (web-mode-tag-match)) + (t + (goto-char init)) + ) + )) + +(defun web-mode-block-match (&optional pos) + (unless pos (setq pos (point))) + (let (pos-ori controls control (counter 1) type (continue t) pair) + (setq pos-ori pos) + (goto-char pos) + (setq controls (web-mode-block-controls-get pos)) + ;;(message "controls=%S" controls) + (cond + (controls + (setq pair (car controls)) + (setq control (cdr pair)) + (setq type (car pair)) + (when (eq type 'inside) (setq type 'close)) + (while continue + (cond + ((and (> pos-ori 1) (bobp)) + (setq continue nil)) + ((or (and (eq type 'open) (not (web-mode-block-next))) + (and (eq type 'close) (not (web-mode-block-previous)))) + (setq continue nil) + ) + ((null (setq controls (web-mode-block-controls-get (point)))) + ) + (t + ;;TODO : est il nécessaire de faire un reverse sur controls si on doit matcher backward + (dolist (pair controls) + (cond + ((not (string= (cdr pair) control)) + ) + ((eq (car pair) 'inside) + ) + ((eq (car pair) type) + (setq counter (1+ counter))) + (t + (setq counter (1- counter))) + ) + ) ;dolist + (when (= counter 0) + (setq continue nil)) + ) ;t + ) ;cond + ) ;while + (if (= counter 0) (point) nil) + ) ;controls + (t + (goto-char pos-ori) + nil + ) ;controls = nul + ) ;conf + )) + +(defun web-mode-tag-match (&optional pos) + "Move point to the matching opening/closing tag." + (interactive) + (unless pos (setq pos (point))) + (let (regexp name) + (cond + ((eq (get-text-property pos 'tag-type) 'void) + (web-mode-tag-beginning)) + ((and (eq (get-text-property pos 'tag-type) 'comment) + (web-mode-looking-at-p " %S %S" pos (get-text-property pos 'jsx-depth)) + ) + ((and blockside + (member (get-text-property pos 'block-token) '(string comment)) + (eq (get-text-property pos 'block-token) (get-text-property (1- pos) 'block-token))) + (setq pos (web-mode-block-token-beginning-position pos))) + ((and (not blockside) + (member (get-text-property pos 'part-token) '(string comment)) + (eq (get-text-property pos 'part-token) (get-text-property (1- pos) 'part-token))) + (setq pos (web-mode-part-token-beginning-position pos))) + ((and (not blockside) + (get-text-property pos 'block-side)) + (when (setq pos (web-mode-block-beginning-position pos)) + (setq pos (1- pos)))) + ((member char '(?\) ?\] ?\})) + (setq pos (web-mode-part-opening-paren-position pos reg-beg)) + (setq pos (1- pos))) + ((and (eq char ?\=) + (web-mode-looking-back "[<>!=]+" pos reg-beg t)) + (setq pos (- pos 1 (length (match-string-no-properties 0))))) + ((member char '(?\( ?\{ ?\[ ?\= ?\< ?\>)) + (web-mode-looking-at ".[ \t\n]*" pos) + (setq continue nil + pos (+ pos (length (match-string-no-properties 0))))) + + ((web-mode-looking-at "\\(return\\)[ \n]" pos) + (setq continue nil + pos (+ pos (length (match-string-no-properties 0))))) + ((and (eq char ?\:) + (web-mode-looking-back "[{,][ \t\n]*[[:alnum:]_]+[ ]*" pos)) + (web-mode-looking-at ".[ \t\n]*" pos) + (setq continue nil + pos (+ pos (length (match-string-no-properties 0))))) + (t + (setq pos (web-mode-rsb-position pos regexp reg-beg)) + (when (not pos) + (cond + (is-jsx + (when (web-mode-looking-at "[ \n]*" reg-beg) + (setq pos (+ reg-beg (length (match-string-no-properties 0))))) + (setq continue nil)) + (t + (message "javascript-statement-beginning-position ** search failure **") + (setq continue nil + pos reg-beg)) + ) ;cond + ) + ) ;t + ) ;cond + ) ;while + ;;(message "%S -------" pos) + pos)) + +(defun web-mode-javascript-args-beginning-position (pos &optional reg-beg) + (unless pos (setq pos (point))) + (setq pos (1- pos)) + (let ((char nil) + (blockside (get-text-property pos 'block-side)) + (i 0) + (continue (not (null pos)))) + (unless reg-beg + (if blockside + (setq reg-beg (web-mode-block-beginning-position pos)) + (setq reg-beg (web-mode-part-beginning-position pos))) + ) + (while continue + (setq char (char-after pos)) + ;;(message "pos(%S) char(%c)" pos char) + (cond + ((> (setq i (1+ i)) 20000) + (message "javascript-args-beginning-position ** warning (%S) **" pos) + (setq continue nil + pos nil)) + ((null pos) + (message "javascript-args-beginning-position ** invalid pos **") + (setq continue nil)) + ((< pos reg-beg) + (message "javascript-args-beginning-position ** failure(position) **") + (setq continue nil + pos reg-beg)) + ((and blockside + (member (get-text-property pos 'block-token) '(string comment)) + (eq (get-text-property pos 'block-token) (get-text-property (1- pos) 'block-token))) + (setq pos (web-mode-block-token-beginning-position pos))) + ((and (not blockside) + (member (get-text-property pos 'part-token) '(string comment)) + (eq (get-text-property pos 'part-token) (get-text-property (1- pos) 'part-token))) + (setq pos (web-mode-part-token-beginning-position pos))) + ((and (not blockside) + (get-text-property pos 'block-side)) + (when (setq pos (web-mode-block-beginning-position pos)) + (setq pos (1- pos))) + ) + ((member char '(?\) ?\] ?\})) + (when (setq pos (web-mode-part-opening-paren-position pos reg-beg)) + (setq pos (1- pos)))) + ((member char '(?\( ?\[ ?\{)) + (web-mode-looking-at ".[ ]*" pos) + (setq pos (+ pos (length (match-string-no-properties 0))) + continue nil) + ) + ((web-mode-looking-at "\\(var\\|let\\|return\\|const\\)[ \n]" pos) + (setq pos (+ pos (length (match-string-no-properties 0))) + continue nil)) + (t + (setq pos (web-mode-rsb-position pos "[\]\[}{)(]\\|\\(var\\|let\\|return\\|const\\)" reg-beg)) + (when (not pos) + (message "javascript-args-beginning-position ** search failure **") + (setq continue nil + pos reg-beg))) + ) ;cond + ) ;while + ;;(message "=%S" pos) + pos)) + +(defun web-mode-javascript-calls-beginning-position (pos &optional reg-beg) + (unless pos (setq pos (point))) + ;;(message "pos=%S" pos) + (let ((char nil) + (dot-pos nil) + (blockside (get-text-property pos 'block-side)) + (i 0) + (continue (not (null pos)))) + (unless reg-beg + (setq reg-beg (if blockside + (web-mode-block-beginning-position pos) + (web-mode-part-beginning-position pos)))) + (while continue + (setq char (char-after pos)) + ;;(message "%S| %S=%c" reg-beg pos char) + (cond + ((> (setq i (1+ i)) 20000) + (message "javascript-calls-beginning-position ** warning (%S) **" pos) + (setq continue nil + pos nil)) + ((null pos) + (message "javascript-calls-beginning-position ** invalid pos **") + (setq continue nil)) + ((< pos reg-beg) + (setq continue nil + pos reg-beg)) + ((and blockside + (member (get-text-property pos 'block-token) '(string comment)) + (eq (get-text-property pos 'block-token) (get-text-property (1- pos) 'block-token))) + (setq pos (web-mode-block-token-beginning-position pos))) + ((and (not blockside) + (member (get-text-property pos 'part-token) '(string comment)) + (eq (get-text-property pos 'part-token) (get-text-property (1- pos) 'part-token))) + (setq pos (web-mode-part-token-beginning-position pos))) + ((and (not blockside) + (get-text-property pos 'block-side)) + (when (setq pos (web-mode-block-beginning-position pos)) + (setq pos (1- pos)))) + ((and (member char '(?\.)) (> i 1)) + (setq dot-pos pos + pos (1- pos))) + ((member char '(?\) ?\])) + (when (setq pos (web-mode-part-opening-paren-position pos reg-beg)) + (setq pos (1- pos))) + ) + ((member char '(?\( ?\{ ?\} ?\[ ?\= ?\? ?\: ?\; ?\, ?\& ?\| ?\>)) + (web-mode-looking-at ".[ \t\n]*" pos) + (setq pos (+ pos (length (match-string-no-properties 0))) + continue nil)) + ((web-mode-looking-at "\\(return\\|else\\|const\\)[ \n]" pos) + (setq pos (+ pos (length (match-string-no-properties 0))) + continue nil)) + (t + (setq pos (web-mode-rsb-position pos "[\]\[}{)(=?:;,&|>.]\\|\\(return\\|else\\|const\\)" reg-beg)) + (when (not pos) + (message "javascript-calls-beginning-position ** search failure **") + (setq pos reg-beg + continue nil)) + ) ;t + ) ;cond + ) ;while + ;;(message "pos=%S dot-pos=%S" pos dot-pos) + (if (null pos) pos (cons pos dot-pos)) + )) + +(defun web-mode-part-token-beginning-position (&optional pos) + (unless pos (setq pos (point))) + (cond + ((not (get-text-property pos 'part-token)) + nil) + ((or (= pos (point-min)) + (and (> pos (point-min)) + (not (get-text-property (1- pos) 'part-token)))) + pos) + (t + (setq pos (previous-single-property-change pos 'part-token)) + (if (and pos (> pos (point-min))) pos (point-min))) + )) + +(defun web-mode-part-token-end-position (&optional pos) + (unless pos (setq pos (point))) + (cond + ((not (get-text-property pos 'part-token)) + nil) + ((or (= pos (point-max)) + (not (get-text-property (1+ pos) 'part-token))) + pos) + (t + (1- (next-single-property-change pos 'part-token))) + )) + +(defun web-mode-block-token-beginning-position (&optional pos) + (unless pos (setq pos (point))) + (cond + ((not (get-text-property pos 'block-token)) + nil) + ((or (= pos (point-min)) + (and (> pos (point-min)) + (not (get-text-property (1- pos) 'block-token)))) + pos) + (t + (setq pos (previous-single-property-change pos 'block-token)) + (if (and pos (> pos (point-min))) pos (point-min))) + )) + +(defun web-mode-block-token-end-position (&optional pos) + (unless pos (setq pos (point))) + (cond + ((not (get-text-property pos 'block-token)) + nil) + ((or (= pos (point-max)) + (not (get-text-property (1+ pos) 'block-token))) + pos) + (t + (1- (next-single-property-change pos 'block-token))) + )) + +(defun web-mode-block-code-end-position (&optional pos) + (unless pos (setq pos (point))) + (setq pos (web-mode-block-end-position pos)) + (cond + ((not pos) + nil) + ((and (eq (get-text-property pos 'block-token) 'delimiter-end) + (eq (get-text-property (1- pos) 'block-token) 'delimiter-end)) + (previous-single-property-change pos 'block-token)) + ((= pos (1- (point-max))) ;; TODO: comparer plutot avec line-end-position + (point-max)) + (t + pos) + )) + +(defun web-mode-block-end-position (&optional pos) + (unless pos (setq pos (point))) + (cond + ((get-text-property pos 'block-end) + pos) + ((get-text-property pos 'block-side) + (or (next-single-property-change pos 'block-end) + (point-max))) + (t + nil) + )) + +(defun web-mode-block-previous-position (&optional pos) + (unless pos (setq pos (point))) + (cond + ((= pos (point-min)) + (setq pos nil)) + ((get-text-property pos 'block-side) + (setq pos (web-mode-block-beginning-position pos)) + (cond + ((or (null pos) (= pos (point-min))) + (setq pos nil) + ) + ((and (setq pos (previous-single-property-change pos 'block-beg)) + (> pos (point-min))) + (setq pos (1- pos)) + ) + ) + ) ;block-side + ((get-text-property (1- pos) 'block-side) + (setq pos (web-mode-block-beginning-position (1- pos))) + ) + (t + (setq pos (previous-single-property-change pos 'block-side)) + (cond + ((and (null pos) (get-text-property (point-min) 'block-beg)) + (setq pos (point-min))) + ((and pos (> pos (point-min))) + (setq pos (web-mode-block-beginning-position (1- pos)))) + ) + ) + ) ;conf + pos) + +(defun web-mode-block-next-position (&optional pos limit) + (unless pos (setq pos (point))) + (unless limit (setq limit (point-max))) + (cond + ((and (get-text-property pos 'block-side) + (setq pos (web-mode-block-end-position pos)) + (< pos (point-max)) + (setq pos (1+ pos))) + (unless (get-text-property pos 'block-beg) + (setq pos (next-single-property-change pos 'block-side))) + ) + (t + (setq pos (next-single-property-change pos 'block-side))) + ) ;cond + (if (and pos (<= pos limit)) pos nil)) + +(defun web-mode-is-css-string (pos) + (let (beg) + (cond + ((and (setq beg (web-mode-part-token-beginning-position pos)) + (web-mode-looking-at-p "`" beg) + (web-mode-looking-back "\\(styled[[:alnum:].]+\\|css\\)" beg)) + beg) + (t + nil) + ) ;cond + )) + +;; Relay.QL , gql, graphql +(defun web-mode-is-ql-string (pos prefix-regexp) + (let (beg) + (cond + ((and (setq beg (web-mode-part-token-beginning-position pos)) + (web-mode-looking-back prefix-regexp beg)) + beg) + (t + nil) + ) ;cond + )) + +(defun web-mode-is-html-string (pos) + (let (beg) + (cond + ((and (setq beg (web-mode-part-token-beginning-position pos)) + (web-mode-looking-at-p "`[ \t\n]*<[a-zA-Z]" beg) + (web-mode-looking-back "\\(template\\|html\\)\\([ ]*[=:][ ]*\\)?" beg)) + beg) + (t + nil) + ) ;cond + )) + +;;---- EXCURSION --------------------------------------------------------------- + +(defun web-mode-backward-sexp (n) + (interactive "p") + (if (< n 0) (web-mode-forward-sexp (- n)) + (let (pos) + (dotimes (_ n) + (skip-chars-backward "[:space:]") + (setq pos (point)) + (cond + ((bobp) nil) + ((get-text-property (1- pos) 'block-end) + (backward-char 1) + (web-mode-block-beginning)) + ((get-text-property (1- pos) 'block-token) + (backward-char 1) + (web-mode-block-token-beginning)) + ((get-text-property (1- pos) 'part-token) + (backward-char 1) + (web-mode-part-token-beginning)) + ((get-text-property (1- pos) 'tag-end) + (backward-char 1) + (web-mode-element-beginning)) + ((get-text-property (1- pos) 'tag-attr) + (backward-char 1) + (web-mode-attribute-beginning)) + ((get-text-property (1- pos) 'tag-type) + (backward-char 1) + (web-mode-tag-beginning)) + ((get-text-property (1- pos) 'jsx-end) + (backward-char 1) + (web-mode-jsx-beginning)) + (t + (let ((forward-sexp-function nil)) + (backward-sexp)) + ) ;case t + ) ;cond + ) ;dotimes + ))) ;let if defun + +(defun web-mode-forward-sexp (n) + (interactive "p") + (if (< n 0) (web-mode-backward-sexp (- n)) + (let (pos) + (dotimes (_ n) + (skip-chars-forward "[:space:]") + (setq pos (point)) + (cond + ((eobp) nil) + ((get-text-property pos 'block-beg) + (web-mode-block-end)) + ((get-text-property pos 'block-token) + (web-mode-block-token-end)) + ((get-text-property pos 'part-token) + (web-mode-part-token-end)) + ((get-text-property pos 'tag-beg) + (web-mode-element-end)) + ((get-text-property pos 'tag-attr) + (web-mode-attribute-end)) + ((get-text-property pos 'tag-type) + (web-mode-tag-end)) + ((get-text-property pos 'jsx-beg) + (web-mode-jsx-end)) + (t + (let ((forward-sexp-function nil)) + (forward-sexp)) + ) ;case t + ) ;cond + ) ;dotimes + ))) ;let if defun + +(defun web-mode-comment-beginning () + "Fetch current comment beg." + (interactive) + (web-mode-go (web-mode-comment-beginning-position (point)))) + +(defun web-mode-comment-end () + "Fetch current comment end." + (interactive) + (web-mode-go (web-mode-comment-end-position (point)) 1)) + +(defun web-mode-tag-beginning () + "Fetch current html tag beg." + (interactive) + (web-mode-go (web-mode-tag-beginning-position (point)))) + +(defun web-mode-tag-end () + "Fetch current html tag end." + (interactive) + (web-mode-go (web-mode-tag-end-position (point)) 1)) + +(defun web-mode-tag-previous () + "Fetch previous tag." + (interactive) + (web-mode-go (web-mode-tag-previous-position (point)))) + +(defun web-mode-tag-next () + "Fetch next tag. Might be html comment or server tag (e.g. jsp)." + (interactive) + (web-mode-go (web-mode-tag-next-position (point)))) + +(defun web-mode-attribute-beginning () + "Fetch html attribute beginning." + (interactive) + (web-mode-go (web-mode-attribute-beginning-position (point)))) + +(defun web-mode-attribute-end () + "Fetch html attribute end." + (interactive) + (web-mode-go (web-mode-attribute-end-position (point)) 1)) + +(defun web-mode-attribute-next (&optional arg) + "Fetch next attribute." + (interactive "p") + (unless arg (setq arg 1)) + (cond + ((= arg 1) (web-mode-go (web-mode-attribute-next-position (point)))) + ((< arg 1) (web-mode-element-previous (* arg -1))) + (t + (while (>= arg 1) + (setq arg (1- arg)) + (web-mode-go (web-mode-attribute-next-position (point))) + ) + ) + ) + ) + +(defun web-mode-attribute-previous (&optional arg) + "Fetch previous attribute." + (interactive "p") + (unless arg (setq arg 1)) + (unless arg (setq arg 1)) + (cond + ((= arg 1) (web-mode-go (web-mode-attribute-previous-position (point)))) + ((< arg 1) (web-mode-element-next (* arg -1))) + (t + (while (>= arg 1) + (setq arg (1- arg)) + (web-mode-go (web-mode-attribute-previous-position (point))) + ) + ) + ) + ) + +(defun web-mode-element-previous (&optional arg) + "Fetch previous element." + (interactive "p") + (unless arg (setq arg 1)) + (cond + ((= arg 1) (web-mode-go (web-mode-element-previous-position (point)))) + ((< arg 1) (web-mode-element-next (* arg -1))) + (t + (while (>= arg 1) + (setq arg (1- arg)) + (web-mode-go (web-mode-element-previous-position (point))) + ) ;while + ) ;t + ) ;cond + ) + +(defun web-mode-element-next (&optional arg) + "Fetch next element." + (interactive "p") + (unless arg (setq arg 1)) + (cond + ((= arg 1) (web-mode-go (web-mode-element-next-position (point)))) + ((< arg 1) (web-mode-element-previous (* arg -1))) + (t + (while (>= arg 1) + (setq arg (1- arg)) + (web-mode-go (web-mode-element-next-position (point))) + ) ;while + ) ;t + ) ;cond + ) + +(defun web-mode-element-sibling-next () + "Fetch next sibling element." + (interactive) + (let ((pos (point))) + (save-excursion + (cond + ((not (get-text-property pos 'tag-type)) + (if (and (web-mode-element-parent) + (web-mode-tag-match) + (web-mode-tag-next) + (member (get-text-property (point) 'tag-type) '(start void comment))) + (setq pos (point)) + (setq pos nil)) + ) + ((member (get-text-property pos 'tag-type) '(start void)) + (if (and (web-mode-tag-match) + (web-mode-tag-next) + (member (get-text-property (point) 'tag-type) '(start void comment))) + (setq pos (point)) + (setq pos nil)) + ) + ((and (web-mode-tag-next) + (member (get-text-property (point) 'tag-type) '(start void comment))) + (setq pos (point))) + (t + (setq pos nil)) + ) ;cond + ) ;save-excursion + (web-mode-go pos))) + +(defun web-mode-element-sibling-previous () + "Fetch previous sibling element." + (interactive) + (let ((pos (point))) + (save-excursion + (cond + ((not (get-text-property pos 'tag-type)) + (if (and (web-mode-element-parent) + (web-mode-tag-previous) + (web-mode-element-beginning)) + (setq pos (point)) + (setq pos nil)) + ) + ((eq (get-text-property pos 'tag-type) 'start) + (if (and (web-mode-tag-beginning) + (web-mode-tag-previous) + (web-mode-element-beginning)) + (setq pos (point)) + (setq pos nil)) + ) + ((and (web-mode-element-beginning) + (web-mode-tag-previous) + (web-mode-element-beginning)) + (setq pos (point))) + (t + (setq pos nil)) + ) ;cond + ) ;save-excursion + (web-mode-go pos))) + +(defun web-mode-element-beginning () + "Move to beginning of element." + (interactive) + (web-mode-go (web-mode-element-beginning-position (point)))) + +(defun web-mode-element-end () + "Move to end of element." + (interactive) + (web-mode-go (web-mode-element-end-position (point)) 1)) + +(defun web-mode-element-parent () + "Fetch parent element." + (interactive) + (web-mode-go (web-mode-element-parent-position (point)))) + +(defun web-mode-element-child () + "Fetch child element." + (interactive) + (web-mode-go (web-mode-element-child-position (point)))) + +(defun web-mode-dom-traverse () + "Traverse html dom tree." + (interactive) + (cond + ((web-mode-element-child) + ) + ((web-mode-element-sibling-next) + ) + ((and (web-mode-element-parent) + (not (web-mode-element-sibling-next))) + (goto-char (point-min))) + (t + (goto-char (point-min))) + ) ;cond + ) + +(defun web-mode-closing-paren (limit) + (let ((pos (web-mode-closing-paren-position (point) limit))) + (if (or (null pos) (> pos limit)) + nil + (goto-char pos) + pos) + )) + +(defun web-mode-part-next () + "Move point to the beginning of the next part." + (interactive) + (web-mode-go (web-mode-part-next-position (point)))) + +(defun web-mode-part-beginning () + "Move point to the beginning of the current part." + (interactive) + (web-mode-go (web-mode-part-beginning-position (point)))) + +(defun web-mode-part-end () + "Move point to the end of the current part." + (interactive) + (web-mode-go (web-mode-part-end-position (point)) 1)) + +(defun web-mode-block-previous () + "Move point to the beginning of the previous block." + (interactive) + (web-mode-go (web-mode-block-previous-position (point)))) + +(defun web-mode-block-next () + "Move point to the beginning of the next block." + (interactive) + (web-mode-go (web-mode-block-next-position (point)))) + +(defun web-mode-block-beginning () + "Move point to the beginning of the current block." + (interactive) + (web-mode-go (web-mode-block-beginning-position (point)))) + +(defun web-mode-block-end () + "Move point to the end of the current block." + (interactive) + (web-mode-go (web-mode-block-end-position (point)) 1)) + +(defun web-mode-block-token-beginning () + (web-mode-go (web-mode-block-token-beginning-position (point)))) + +(defun web-mode-block-token-end () + (web-mode-go (web-mode-block-token-end-position (point)) 1)) + +(defun web-mode-part-token-beginning () + (web-mode-go (web-mode-part-token-beginning-position (point)))) + +(defun web-mode-part-token-end () + (web-mode-go (web-mode-part-token-end-position (point)) 1)) + +(defun web-mode-block-opening-paren (limit) + (web-mode-go (web-mode-block-opening-paren-position (point) limit))) + +(defun web-mode-block-string-beginning (&optional pos block-beg) + (unless pos (setq pos (point))) + (unless block-beg (setq block-beg (web-mode-block-beginning-position pos))) + (web-mode-go (web-mode-block-string-beginning-position pos block-beg))) + +(defun web-mode-block-statement-beginning (pos block-beg is-ternary) + (unless pos (setq pos (point))) + (unless block-beg (setq block-beg (web-mode-block-beginning-position pos))) + (web-mode-go (web-mode-block-statement-beginning-position pos block-beg is-ternary))) + +(defun web-mode-block-args-beginning (&optional pos block-beg) + (unless pos (setq pos (point))) + (unless block-beg (setq block-beg (web-mode-block-beginning-position pos))) + (web-mode-go (web-mode-block-args-beginning-position pos block-beg))) + +(defun web-mode-block-calls-beginning (&optional pos block-beg) + (unless pos (setq pos (point))) + (unless block-beg (setq block-beg (web-mode-block-beginning-position pos))) + (web-mode-go (web-mode-block-calls-beginning-position pos block-beg))) + +(defun web-mode-javascript-string-beginning (&optional pos reg-beg) + (unless pos (setq pos (point))) + (unless reg-beg + (if (get-text-property pos 'block-side) + (setq reg-beg (web-mode-block-beginning-position pos)) + (setq reg-beg (web-mode-part-beginning-position pos)))) + (web-mode-go (web-mode-javascript-string-beginning-position pos reg-beg))) + +(defun web-mode-javascript-statement-beginning (pos reg-beg is-ternary) + (unless pos (setq pos (point))) + (unless reg-beg + (if (get-text-property pos 'block-side) + (setq reg-beg (web-mode-block-beginning-position pos)) + (setq reg-beg (web-mode-part-beginning-position pos)))) + (web-mode-go (web-mode-javascript-statement-beginning-position pos reg-beg is-ternary))) + +(defun web-mode-javascript-args-beginning (&optional pos reg-beg) + (unless pos (setq pos (point))) + (unless reg-beg + (setq reg-beg (if (get-text-property pos 'block-side) + (web-mode-block-beginning-position pos) + (web-mode-part-beginning-position pos)))) + ;;(message "reg-beg%S" reg-beg) + (web-mode-go (web-mode-javascript-args-beginning-position pos reg-beg))) + +(defun web-mode-javascript-calls-beginning (&optional pos reg-beg) + (unless pos (setq pos (point))) + (unless reg-beg + (if (get-text-property pos 'block-side) + (setq reg-beg (web-mode-block-beginning-position pos)) + (setq reg-beg (web-mode-part-beginning-position pos)))) + (let (pair) + (setq pair (web-mode-javascript-calls-beginning-position pos reg-beg)) + (when pair (web-mode-go (car pair))) + )) + +(defun web-mode-go (pos &optional offset) + (unless offset (setq offset 0)) + (when pos + (cond + ((and (> offset 0) (<= (+ pos offset) (point-max))) + (setq pos (+ pos offset))) + ((and (< offset 0) (>= (+ pos offset) (point-min))) + (setq pos (+ pos offset))) + ) ;cond + (goto-char pos)) + pos) + +;;---- SEARCH ------------------------------------------------------------------ + +(defun web-mode-rsf-balanced (regexp-open regexp-close &optional limit noerror) + (unless noerror (setq noerror t)) + (let ((continue t) + (level 1) + (pos (point)) + ret + (regexp (concat regexp-open "\\|" regexp-close))) + (while continue + (setq ret (re-search-forward regexp limit noerror)) + (cond + ((null ret) + (setq continue nil) + ) + (t + (if (string-match-p regexp-open (match-string-no-properties 0)) + (setq level (1+ level)) + (setq level (1- level))) + (when (< level 1) + (setq continue nil) + ) + ) ;t + ) ;cond + ) ;while + (when (not (= level 0)) (goto-char pos)) + ret)) + +(defun web-mode-block-sb (expr &optional limit noerror) + (unless limit (setq limit (web-mode-block-beginning-position (point)))) + (unless noerror (setq noerror t)) + (let ((continue t) ret) + (while continue + (setq ret (search-backward expr limit noerror)) + (when (or (null ret) + (not (get-text-property (point) 'block-token))) + (setq continue nil) + ) ;when + ) ;while + ret)) + +(defun web-mode-block-sf (expr &optional limit noerror) + (unless limit (setq limit (web-mode-block-end-position (point)))) + (unless noerror (setq noerror t)) + (let ((continue t) ret) + (while continue + (setq ret (search-forward expr limit noerror)) + (when (or (null ret) + (not (get-text-property (point) 'block-token))) + (setq continue nil) + ) ;when + ) ;while + ret)) + +(defun web-mode-block-rsb (regexp &optional limit noerror) + (unless limit (setq limit (web-mode-block-beginning-position (point)))) + (unless noerror (setq noerror t)) + (let ((continue t) ret) + (while continue + (setq ret (re-search-backward regexp limit noerror)) + (when (or (null ret) + (not (get-text-property (point) 'block-token))) + (setq continue nil) + ) ;when + ) ;while + ret)) + +(defun web-mode-block-rsf (regexp &optional limit noerror) + (unless limit (setq limit (web-mode-block-end-position (point)))) + (unless noerror (setq noerror t)) + (let ((continue t) ret) + (while continue + (setq ret (re-search-forward regexp limit noerror)) + (when (or (null ret) + (not (get-text-property (point) 'block-token))) + (setq continue nil) + ) ;when + ) ;while + ret)) + +(defun web-mode-part-sb (expr &optional limit noerror) + (unless limit (setq limit (web-mode-part-beginning-position (point)))) + (unless noerror (setq noerror t)) + (let ((continue t) ret) + (while continue + (setq ret (search-backward expr limit noerror)) + (when (or (null ret) + (and (not (get-text-property (point) 'part-token)) + (not (get-text-property (point) 'block-side))) + ) + (setq continue nil) + ) ;when + ) ;while + ret)) + +(defun web-mode-part-sf (expr &optional limit noerror) + (unless limit (setq limit (web-mode-part-end-position (point)))) + (unless noerror (setq noerror t)) + (let ((continue t) ret) + (while continue + (setq ret (search-forward expr limit noerror)) + (when (or (null ret) + (and (not (get-text-property (point) 'part-token)) + (not (get-text-property (point) 'block-side))) + ) + (setq continue nil) + ) ;when + ) ;while + ret)) + +(defun web-mode-part-rsb (regexp &optional limit noerror) + (unless limit (setq limit (web-mode-part-beginning-position (point)))) + (unless noerror (setq noerror t)) + (let ((continue t) ret) + (while continue + (setq ret (re-search-backward regexp limit noerror)) + (when (or (null ret) + (and (not (get-text-property (point) 'part-token)) + (not (get-text-property (point) 'block-side))) + ) + (setq continue nil) + ) ;when + ) ;while + ret)) + +(defun web-mode-part-rsf (regexp &optional limit noerror) + (unless limit (setq limit (web-mode-part-end-position (point)))) + (unless noerror (setq noerror t)) + (let ((continue t) ret) + (while continue + (setq ret (re-search-forward regexp limit t)) + (when (or (null ret) + (and (not (get-text-property (point) 'part-token)) + (not (get-text-property (point) 'block-side))) + ) + (setq continue nil) + ) ;when + ) ;while + ret)) + +(defun web-mode-javascript-rsb (regexp &optional limit noerror) + (unless limit (setq limit (web-mode-part-beginning-position (point)))) + (unless noerror (setq noerror t)) + (let ((continue t) ret) + (while continue + (setq ret (re-search-backward regexp limit noerror)) + (when (or (null ret) + (and (not (get-text-property (point) 'part-token)) + (not (get-text-property (point) 'block-side)) + (not (get-text-property (point) 'jsx-depth))) + ) + (setq continue nil) + ) ;when + ) ;while + ret)) + +(defun web-mode-javascript-rsf (regexp &optional limit noerror) + (unless limit (setq limit (web-mode-part-end-position (point)))) + (unless noerror (setq noerror t)) + (let ((continue t) ret) + (while continue + (setq ret (re-search-forward regexp limit t)) + (when (or (null ret) + (and (not (get-text-property (point) 'part-token)) + (not (get-text-property (point) 'block-side)) + (not (get-text-property (point) 'jsx-depth))) + ) + (setq continue nil) + ) ;when + ) ;while + ret)) + +(defun web-mode-dom-sf (expr &optional limit noerror) + (unless noerror (setq noerror t)) + (let ((continue t) ret) + (while continue + (setq ret (search-forward expr limit noerror)) + (if (or (null ret) + (not (get-text-property (- (point) (length expr)) 'block-side))) + (setq continue nil)) + ) + ret)) + +(defun web-mode-dom-rsf (regexp &optional limit noerror) + (unless noerror (setq noerror t)) + (let ((continue t) (ret nil)) + (while continue + (setq ret (re-search-forward regexp limit noerror)) + ;; (message "ret=%S point=%S limit=%S i=%S" ret (point) limit 0) + (cond + ((null ret) + (setq continue nil)) + ((or (get-text-property (match-beginning 0) 'block-side) + (get-text-property (match-beginning 0) 'part-token)) + ) + (t + (setq continue nil)) + ) ;cond + ) ;while + ret)) + +(defun web-mode-rsb-position (pos regexp &optional limit noerror) + (unless noerror (setq noerror t)) + (save-excursion + (goto-char pos) + (if (re-search-backward regexp limit noerror) (point) nil) + )) + +(defun web-mode-rsb (regexp &optional limit noerror) + (unless noerror (setq noerror t)) + (let ((continue t) ret) + (while continue + (setq ret (re-search-backward regexp limit noerror)) + (if (or (null ret) + (not (web-mode-is-comment-or-string))) + (setq continue nil))) + ret)) + +(defun web-mode-rsf (regexp &optional limit noerror) + (unless noerror (setq noerror t)) + (let ((continue t) ret) + (while continue + (setq ret (re-search-forward regexp limit noerror)) + (if (or (null ret) + (not (web-mode-is-comment-or-string))) + (setq continue nil)) + ) + ret)) + +(defun web-mode-sb (expr &optional limit noerror) + (unless noerror (setq noerror t)) + (let ((continue t) ret) + (while continue + (setq ret (search-backward expr limit noerror)) + (if (or (null ret) + (not (web-mode-is-comment-or-string))) + (setq continue nil))) + ret)) + +(defun web-mode-sf (expr &optional limit noerror) + (unless noerror (setq noerror t)) + (let ((continue t) ret) + (while continue + (setq ret (search-forward expr limit noerror)) + (if (or (null ret) + (not (web-mode-is-comment-or-string))) + (setq continue nil))) + ret)) + +(defun web-mode-content-rsf (regexp &optional limit noerror) + (unless noerror (setq noerror t)) + (let ((continue t) ret beg end) + (while continue + (setq ret (re-search-forward regexp limit noerror) + beg (if (null ret) (point) (match-beginning 0)) + end (if (null ret) (point) (1- (match-end 0)))) + (if (or (null ret) + (and (web-mode-is-content beg) + (web-mode-is-content end))) + (setq continue nil))) + ret)) + +;;---- ADVICES ----------------------------------------------------------------- + +(defadvice ac-start (before web-mode-set-up-ac-sources activate) + "Set `ac-sources' based on current language before running auto-complete." + (when (equal major-mode 'web-mode) + ;; set ignore each time to nil. User has to implement a hook to change it + ;; for each completion + (setq web-mode-ignore-ac-start-advice nil) + (run-hooks 'web-mode-before-auto-complete-hooks) + (unless web-mode-ignore-ac-start-advice + (when web-mode-ac-sources-alist + (let ((new-web-mode-ac-sources + (assoc (web-mode-language-at-pos) + web-mode-ac-sources-alist))) + (setq ac-sources (cdr new-web-mode-ac-sources))))))) + +;;---- MINOR MODE ADDONS ------------------------------------------------------- + +(defun web-mode-yasnippet-exit-hook () + "Yasnippet exit hook" + (when (and (boundp 'yas-snippet-beg) (boundp 'yas-snippet-end)) + (indent-region yas-snippet-beg yas-snippet-end))) + +(defun web-mode-imenu-index () + "Returns imenu items." + (interactive) + (let (toc-index + line) + (save-excursion + (goto-char (point-min)) + (while (not (eobp)) + (setq line (buffer-substring-no-properties + (line-beginning-position) + (line-end-position))) + (let (found + (i 0) + item + regexp + type + type-idx + content + content-idx + content-regexp + close-tag-regexp + concat-str + jumpto + str) + (while (and (not found ) (< i (length web-mode-imenu-regexp-list))) + (setq item (nth i web-mode-imenu-regexp-list)) + (setq regexp (nth 0 item)) + (setq type-idx (nth 1 item)) + (setq content-idx (nth 2 item)) + (setq concat-str (nth 3 item)) + (when (not (numberp content-idx)) + (setq content-regexp (nth 2 item) + close-tag-regexp (nth 4 item) + content-idx nil)) + + (when (string-match regexp line) + + (cond + (content-idx + (setq type (match-string type-idx line)) + (setq content (match-string content-idx line)) + (setq str (concat type concat-str content)) + (setq jumpto (line-beginning-position))) + (t + (let (limit) + (setq type (match-string type-idx line)) + (goto-char (line-beginning-position)) + (save-excursion + (setq limit (re-search-forward close-tag-regexp (point-max) t))) + + (when limit + (when (re-search-forward content-regexp limit t) + (setq content (match-string 1)) + (setq str (concat type concat-str content)) + (setq jumpto (line-beginning-position)) + ) + ))) + ) + (when str (setq toc-index + (cons (cons str jumpto) + toc-index) + ) + (setq found t)) + ) + (setq i (1+ i)))) + (forward-line) + (goto-char (line-end-position)) ;; make sure we are at eobp + )) + (nreverse toc-index))) + +;;---- UNIT TESTING ------------------------------------------------------------ + +(defun web-mode-test () + "Executes web-mode unit tests. See `web-mode-tests-directory'." + (interactive) + (let (files regexp) + (setq regexp "^[[:alnum:]][[:alnum:]._]+\\'") + (setq files (directory-files web-mode-tests-directory t regexp)) + (dolist (file files) + (cond + ((eq (string-to-char (file-name-nondirectory file)) ?\_) + (delete-file file)) + (t + (web-mode-test-process file)) + ) ;cond + ) ;dolist + )) + +(defun web-mode-test-process (file) + (with-temp-buffer + (let (out sig1 sig2 success err) + (setq-default indent-tabs-mode nil) + (if (string-match-p "sql" file) + (setq web-mode-enable-sql-detection t) + (setq web-mode-enable-sql-detection nil)) + (insert-file-contents file) + (set-visited-file-name file) + (web-mode) + (setq sig1 (md5 (current-buffer))) + (delete-horizontal-space) + (while (not (eobp)) + (forward-line) + (delete-horizontal-space) + (end-of-line)) + (web-mode-buffer-indent) + (setq sig2 (md5 (current-buffer))) + (setq success (string= sig1 sig2)) + (setq out (concat (if success "ok" "ko") " : " (file-name-nondirectory file) "\n")) + (princ out) + (setq err (concat (file-name-directory file) "_err." (file-name-nondirectory file))) + (if success + (when (file-readable-p err) + (delete-file err)) + (write-file err) + (message "[%s]" (buffer-string)) + ) ;if + out))) + +;;---- MISC -------------------------------------------------------------------- + +(defun web-mode-set-engine (engine) + "Set the engine for the current buffer." + (interactive + (list (completing-read + "Engine: " + (let (engines) + (dolist (elt web-mode-engines) + (setq engines (append engines (list (car elt))))) + engines)))) + (setq web-mode-content-type "html" + web-mode-engine (web-mode-engine-canonical-name engine) + web-mode-minor-engine engine) + (web-mode-on-engine-setted) + (web-mode-buffer-fontify)) + +(defun web-mode-set-content-type (content-type) + "Set the content-type for the current buffer" + (interactive (list (completing-read "Content-type: " web-mode-part-content-types))) + (setq web-mode-content-type content-type) + (when (called-interactively-p 'any) + ) + (web-mode-buffer-fontify)) + +(defun web-mode-on-engine-setted () + (let (elt elts) + + (when (string= web-mode-engine "razor") (setq web-mode-enable-block-face t)) + ;;(setq web-mode-engine-attr-regexp (cdr (assoc web-mode-engine web-mode-engine-attr-regexps))) + (setq web-mode-engine-token-regexp (cdr (assoc web-mode-engine web-mode-engine-token-regexps))) + + ;;(message "%S %S %S" web-mode-engine web-mode-engine-attr-regexp web-mode-engine-token-regexp) + + (when (null web-mode-minor-engine) + (setq web-mode-minor-engine "none")) + + (setq elt (assoc web-mode-engine web-mode-engine-open-delimiter-regexps)) + (cond + (elt + (setq web-mode-block-regexp (cdr elt))) + ((string= web-mode-engine "archibus") + (setq web-mode-block-regexp nil)) + (t + (setq web-mode-engine "none")) + ) + + (unless (boundp 'web-mode-extra-auto-pairs) + (setq web-mode-extra-auto-pairs nil)) + + (setq web-mode-auto-pairs + (append + (cdr (assoc web-mode-engine web-mode-engines-auto-pairs)) + (cdr (assoc nil web-mode-engines-auto-pairs)) + (cdr (assoc web-mode-engine web-mode-extra-auto-pairs)) + (cdr (assoc nil web-mode-extra-auto-pairs)))) + + (unless (boundp 'web-mode-extra-snippets) + (setq web-mode-extra-snippets nil)) + + (setq elts + (append + (cdr (assoc web-mode-engine web-mode-extra-snippets)) + (cdr (assoc nil web-mode-extra-snippets)) + (cdr (assoc web-mode-engine web-mode-engines-snippets)) + (cdr (assoc nil web-mode-engines-snippets)))) + + ;;(message "%S" elts) + + (dolist (elt elts) + (unless (assoc (car elt) web-mode-snippets) + (setq web-mode-snippets (cons elt web-mode-snippets))) + ) + + (setq web-mode-engine-font-lock-keywords + (symbol-value (cdr (assoc web-mode-engine web-mode-engines-font-lock-keywords)))) + + (when (and (string= web-mode-minor-engine "jinja") + (not (member "endtrans" web-mode-django-control-blocks))) + (add-to-list 'web-mode-django-control-blocks "endtrans") + (setq web-mode-django-control-blocks-regexp + (regexp-opt web-mode-django-control-blocks t)) + ) + + (when (string= web-mode-engine "spip") + (modify-syntax-entry ?# "w" (syntax-table))) + + ;;(message "%S" (symbol-value (cdr (assoc web-mode-engine web-mode-engines-font-lock-keywords)))) + + )) + +(defun web-mode-detect-engine () + (save-excursion + (goto-char (point-min)) + (when (re-search-forward "-\\*- engine:[ ]*\\([[:alnum:]-]+\\)[ ]*-\\*-" web-mode-chunk-length t) + (setq web-mode-minor-engine (match-string-no-properties 1)) + (setq web-mode-engine (web-mode-engine-canonical-name web-mode-minor-engine))) + web-mode-minor-engine)) + +(defun web-mode-guess-engine-and-content-type () + (let (buff-name found) + + (setq buff-name (buffer-file-name)) + (unless buff-name (setq buff-name (buffer-name))) + (setq web-mode-is-scratch (string= buff-name "*scratch*")) + (setq web-mode-content-type nil) + + (when (boundp 'web-mode-content-types-alist) + (setq found nil) + (dolist (elt web-mode-content-types-alist) + (when (and (not found) (string-match-p (cdr elt) buff-name)) + (setq web-mode-content-type (car elt) + found t)) + ) ;dolist + ) ;when + + (unless web-mode-content-type + (setq found nil) + (dolist (elt web-mode-content-types) + (when (and (not found) (string-match-p (cdr elt) buff-name)) + (setq web-mode-content-type (car elt) + found t) + ;;(message "%S" web-mode-content-type) + ) ;when + ) ;dolist + ) ;unless + + (when (boundp 'web-mode-engines-alist) + (setq found nil) + (dolist (elt web-mode-engines-alist) + (cond + ((stringp (cdr elt)) + (when (string-match-p (cdr elt) buff-name) + (setq web-mode-engine (car elt)))) + ((functionp (cdr elt)) + (when (funcall (cdr elt)) + (setq web-mode-engine (car elt)))) + ) ;cond + ) ;dolist + ) ;when + + (unless web-mode-engine + (setq found nil) + (dolist (elt web-mode-engine-file-regexps) + ;;(message "%S %S %S" (cdr elt) (car elt) buff-name) + (when (and (not found) (string-match-p (cdr elt) buff-name)) + ;;(message "%S %S %S" (cdr elt) (car elt) buff-name) + (setq web-mode-engine (car elt) + found t) + ;;(when (and web-mode-engine (string= web-mode-engine "astro")) + ;; (setq web-mode-enable-front-matter-block t) + ;;) ;when + ) ;when + ) + ) + + (when (and (or (null web-mode-engine) (string= web-mode-engine "none")) + (string-match-p "php" (buffer-substring-no-properties + (line-beginning-position) + (line-end-position)))) + (setq web-mode-engine "php")) + + (when (and (string= web-mode-content-type "javascript") + (string-match-p "@jsx" + (buffer-substring-no-properties + (point-min) + (if (< (point-max) web-mode-chunk-length) + (point-max) + web-mode-chunk-length) + ))) + (setq web-mode-content-type "jsx")) + + (when web-mode-engine + (setq web-mode-minor-engine web-mode-engine + web-mode-engine (web-mode-engine-canonical-name web-mode-engine)) + ) + + ;;(message "%S %S" web-mode-engine web-mode-enable-engine-detection) + + (when (and (or (null web-mode-engine) + (string= web-mode-engine "none")) + web-mode-enable-engine-detection) + (web-mode-detect-engine)) + + (web-mode-on-engine-setted) + + )) + +(defun web-mode-engine-canonical-name (name) + (let (engine) + (cond + ((null name) + nil) + ((assoc name web-mode-engines) + name) + (t + (dolist (elt web-mode-engines) + (when (and (null engine) (member name (cdr elt))) + (setq engine (car elt))) + ) ;dolist + engine) + ))) + +(defun web-mode-on-after-save () + (when web-mode-is-scratch + (web-mode-guess-engine-and-content-type) + (web-mode-buffer-fontify)) + nil) + +(defun web-mode-on-exit () + (web-mode-with-silent-modifications + (put-text-property (point-min) (point-max) 'invisible nil) + (remove-overlays) + (remove-hook 'change-major-mode-hook 'web-mode-on-exit t) + )) + +(defun web-mode-file-link (file) + "Insert a link to a file in html document. This function can be +extended to support more filetypes by customizing +`web-mode-links'." + (interactive + (list (file-relative-name (read-file-name "Link file: ")))) + (let ((matched nil) + (point-line (line-number-at-pos)) + (point-column (current-column))) + (dolist (type web-mode-links) + (when (string-match (car type) file) + (setq matched t) + (when (nth 2 type) + (goto-char (point-min)) + (search-forward "") + (backward-char 7) + (open-line 1)) + (insert (format (cadr type) file)) + (indent-for-tab-command) + (when (nth 2 type) + ;; return point where it was and fix indentation + (forward-line) + (indent-for-tab-command) + (if (> point-line (- (line-number-at-pos) 2)) + (forward-line (+ (- point-line (line-number-at-pos)) 1)) + (forward-line (- point-line (line-number-at-pos)))) + (move-to-column point-column)) + ;; move point back if needed + (backward-char (nth 3 type)))) + (when (not matched) + (user-error "Unknown file type")))) + +(defun web-mode-reload () + "Reload web-mode." + (interactive) + (web-mode-with-silent-modifications + (put-text-property (point-min) (point-max) 'invisible nil) + (remove-overlays) + (setq font-lock-unfontify-region-function 'font-lock-default-unfontify-region) + (load "web-mode.el") + (setq web-mode-change-beg nil + web-mode-change-end nil) + (web-mode) + )) + +(defun web-mode-measure (msg) + (let (sub) + (when (null web-mode-time) (setq web-mode-time (current-time))) + (setq sub (time-subtract (current-time) web-mode-time)) + (when nil + (save-excursion + (let ((n 0)) + (goto-char (point-min)) + (while (web-mode-tag-next) + (setq n (1+ n)) + ) + (message "%S tags found" n) + ))) + (message "%18s: time elapsed = %Ss %9Sµs" msg (nth 1 sub) (nth 2 sub)) + )) + +(defun web-mode-reveal () + "Display text properties at point." + (interactive) + (let (symbols out) + (setq out (format + "[point=%S engine=%S minor=%S content-type=%S language-at-pos=%S]\n" + (point) + web-mode-engine + web-mode-minor-engine + web-mode-content-type + (web-mode-language-at-pos (point)))) + (setq symbols (append web-mode-scan-properties '(font-lock-face face))) + (dolist (symbol symbols) + (when symbol + (setq out (concat out (format "%s(%S) " (symbol-name symbol) (get-text-property (point) symbol))))) + ) + (message "%s\n" out) + ;;(message "syntax-class=%S" (syntax-class (syntax-after (point)))) + (message nil))) + +(defun web-mode-toggle-tracing () + "Toggle tracing." + (interactive) + (if web-mode-trace + (setq web-mode-trace nil) + (message "** tracing on ** point(%S) web-mode-change-beg(%S) web-mode-change-end(%S) web-mode-skip-fontification(%S)" + (point) web-mode-change-beg web-mode-change-end web-mode-skip-fontification) + (setq web-mode-trace t))) + +(defun web-mode-debug () + "Display informations useful for debugging." + (interactive) + (let ((modes nil) + (customs '(web-mode-enable-current-column-highlight web-mode-enable-current-element-highlight indent-tabs-mode)) + (ignore '(abbrev-mode auto-composition-mode auto-compression-mode auto-encryption-mode auto-insert-mode blink-cursor-mode column-number-mode delete-selection-mode display-time-mode electric-indent-mode file-name-shadow-mode font-lock-mode global-font-lock-mode global-hl-line-mode line-number-mode menu-bar-mode mouse-wheel-mode recentf-mode show-point-mode tool-bar-mode tooltip-mode transient-mark-mode))) + (message "\n") + (message "--- WEB-MODE DEBUG BEG ---") + (message "versions: emacs(%S.%S) web-mode(%S)" + emacs-major-version emacs-minor-version web-mode-version) + (message "vars: engine(%S) minor(%S) content-type(%S) file(%S)" + web-mode-engine + web-mode-minor-engine + web-mode-content-type + (or (buffer-file-name) (buffer-name))) + (message "system: window(%S) config(%S)" window-system system-configuration) + (message "colors: fg(%S) bg(%S) " + (cdr (assoc 'foreground-color default-frame-alist)) + (cdr (assoc 'background-color default-frame-alist))) + (mapc (lambda (mode) + (condition-case nil + (if (and (symbolp mode) (symbol-value mode) (not (member mode ignore))) + (push mode modes)) + (error nil)) + ) ;lambda + minor-mode-list) + (message "minor modes: %S" modes) + (message "vars:") + (dolist (custom customs) + (message (format "%s=%S " (symbol-name custom) (symbol-value custom)))) + (message "--- WEB-MODE DEBUG END ---") + (switch-to-buffer "*Messages*") + (goto-char (point-max)) + (recenter) + )) + +(provide 'web-mode) + +;;; web-mode.el ends here + +;; Local Variables: +;; coding: utf-8 +;; indent-tabs-mode: nil +;; sentence-end-double-space: nil +;; End: diff --git a/.emacs.d/lisp/xah-find.el b/.emacs.d/lisp/xah-find.el new file mode 100755 index 0000000..b9be4cb --- /dev/null +++ b/.emacs.d/lisp/xah-find.el @@ -0,0 +1,731 @@ +;;; xah-find.el --- find replace in pure emacs lisp. Purpose similar to grep/sed. -*- coding: utf-8; lexical-binding: t; -*- + +;; Copyright © 2012-2021 by Xah Lee + +;; Author: Xah Lee ( http://xahlee.info/ ) +;; Version: 5.4.20211014135145 +;; Created: 02 April 2012 +;; Package-Requires: ((emacs "24.1")) +;; Keywords: convenience, extensions, files, tools, unix +;; License: GPL v3 +;; Homepage: http://ergoemacs.org/emacs/elisp-xah-find-text.html + +;; This file is not part of GNU Emacs. + +;;; Commentary: + +;; Provides emacs commands for find/replace text of files in a directory, written entirely in emacs lisp. + +;; This package provides these commands: + +;; xah-find-text +;; xah-find-text-regex +;; xah-find-count +;; xah-find-replace-text +;; xah-find-replace-text-regex + +;; • Pure emacs lisp. No dependencies on unix/linux grep/sed/find. Especially useful on Windows. + +;; • Output is highlighted and clickable for jumping to occurrence. + +;; • Using emacs regex, not bash/perl etc regex. + +;; These commands treats find/replace string as sequence of chars, not as lines as in grep/sed, so it's easier to find or replace a text containing lots newlines, especially programming language source code. + +;; • Reliably Find/Replace string that contains newline chars. + +;; • Reliably Find/Replace string that contains lots Unicode chars. See http://xahlee.info/comp/unix_uniq_unicode_bug.html and http://ergoemacs.org/emacs/emacs_grep_problem.html + +;; • Reliably Find/Replace string that contains lots escape slashes or backslashes. For example, regex in source code, Microsoft Windows' path. + +;; The result output is also not based on lines. Instead, visual separators are used for easy reading. + +;; For each occurrence or replacement, n chars will be printed before and after. The number of chars to show is defined by `xah-find-context-char-count-before' and `xah-find-context-char-count-after' + +;; Each “block of text” in output is one occurrence. +;; For example, if a line in a file has 2 occurrences, then the same line will be reported twice, as 2 “blocks”. +;; so, the number of blocks corresponds exactly to the number of occurrences. + +;; Keys +;; ----------------------- +;; TAB xah-find-next-match +;; xah-find-previous-match + +;; RET xah-find--jump-to-place +;; xah-find--mouse-jump-to-place + +;; xah-find-previous-match +;; xah-find-next-match + +;; xah-find-next-file +;; xah-find-previous-file + +;; M-n xah-find-next-file +;; M-p xah-find-previous-file + +;; IGNORE DIRECTORIES + +;; By default, .git dir is ignored. You can add to it by adding the following in your init: + +;; (setq +;; xah-find-dir-ignore-regex-list +;; [ +;; "\\.git/" +;; ; more regex here. regex is matched against file full path +;; ]) + +;; USE CASE + +;; To give a idea what file size, number of files, are practical, here's my typical use pattern: +;; • 5 thousand HTML files match file name regex. +;; • Each HTML file size are usually less than 200k bytes. +;; • search string length have been up to 13 lines of text. + +;; Homepage: http://ergoemacs.org/emacs/elisp-xah-find-text.html + +;; Like it? +;; Buy Xah Emacs Tutorial +;; http://ergoemacs.org/emacs/buy_xah_emacs_tutorial.html +;; Thank you. + +;;; INSTALL + +;; To install manually, place this file in the directory [~/.emacs.d/lisp/] + +;; Then, place the following code in your emacs init file + +;; (add-to-list 'load-path "~/.emacs.d/lisp/") +;; (autoload 'xah-find-text "xah-find" "find replace" t) +;; (autoload 'xah-find-text-regex "xah-find" "find replace" t) +;; (autoload 'xah-find-replace-text "xah-find" "find replace" t) +;; (autoload 'xah-find-replace-text-regex "xah-find" "find replace" t) +;; (autoload 'xah-find-count "xah-find" "find replace" t) + +;;; HISTORY + +;; version 2.1.0, 2015-05-30 Complete rewrite. +;; version 1.0, 2012-04-02 First version. + +;;; CONTRIBUTOR +;; 2015-12-09 Peter Buckley (dx-pbuckley). defcustom for result highlight color. + +;; HHH___________________________________________________________________ +;;; Code: + +(require 'ido) +(require 'seq) + +(ido-common-initialization) + ;; 2015-07-26 else, when ido-read-directory-name is called, Return key insert line return instead of submit. For some reason i dunno. + +(defvar xah-find-context-char-count-before 100 "Number of characters to print before search string." ) + +(defvar xah-find-context-char-count-after 50 "Number of characters to print after search string." ) + +(defvar xah-find-dir-ignore-regex-list + [ + "\\.git/" + ] + "A list or vector of regex patterns, if match, that directory will be ignored. +The regex match is Case Insensitive." + ) + +(defface xah-find-file-path-highlight + '((t :foreground "black" + :background "pink" + )) + "Face of file path where a text match is found." + :group 'xah-find + ) + +(defface xah-find-match-highlight + '((t :foreground "black" + :background "yellow" + )) + "Face for matched text." + :group 'xah-find + ) + +(defface xah-find-replace-highlight + '((t :foreground "black" + :background "green" + )) + "Face for replaced text." + :group 'xah-find + ) + +(defvar xah-find-file-separator + "ff━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n" + "A string as visual separator." + ) + +(defvar xah-find-occur-separator + "oo────────────────────────────────────────────────────────────\n\n" + "A string as visual separator." + ) + +(defvar xah-find-occur-prefix "〖" "A left-bracket string that marks matched text and navigate previous/next. This string should basically never occure in your files. If it does, jumping to the location may not work." ) + +(defvar xah-find-occur-postfix "〗" "A right-bracket string that marks matched text and navigate previous/next. See also `xah-find-occur-prefix'." ) + +(defvar xah-find-replace-prefix "『" "A left-bracket string that marks matched text and navigate previous/next. See also `xah-find-occur-prefix'." ) + +(defvar xah-find-replace-postfix "』" "A right-bracket string that marks matched text and navigate previous/next. See also `xah-find-occur-prefix'." ) + +;; more brackets at http://xahlee.info/comp/unicode_matching_brackets.html + +(defvar xah-find-filepath-prefix "〘" "A left-bracket string used to mark file path and navigate previous/next. See also `xah-find-occur-prefix'." ) + +(defvar xah-find-filepath-postfix "〙" "A right-bracket string used to mark file path and navigate previous/next. See also `xah-find-occur-prefix'." ) + +(defvar xah-find-pos-prefix "⁅" "A string of left bracket that marks line column position of occurrence. See also `xah-find-occur-prefix'." ) + +(defvar xah-find-pos-postfix "⁆" "A string of right bracket that marks line column position of occurrence. See also `xah-find-occur-prefix'." ) + +;; HHH___________________________________________________________________ + +(defvar xah-find-file-path-regex-history '() "File path regex history list, used by `xah-find-text' and others.") + +(defun xah-find--ignore-dir-p (Path) + "Return true if one of `xah-find-dir-ignore-regex-list' matches PATH. Else, nil. +version 2016-11-16 2021-10-11" + (let ((case-fold-search t)) + (catch 'exit25001 + (mapc + (lambda ($regex) + (when (string-match $regex Path) (throw 'exit25001 $regex))) + xah-find-dir-ignore-regex-list) + nil + ))) + +;; HHH___________________________________________________________________ +(defvar xah-find-output-mode-map nil "Keybinding for `xah-find.el output'") +(progn + (setq xah-find-output-mode-map (make-sparse-keymap)) + + (define-key xah-find-output-mode-map (kbd "") 'xah-find-previous-match) + (define-key xah-find-output-mode-map (kbd "") 'xah-find-next-match) + (define-key xah-find-output-mode-map (kbd "") 'xah-find-next-file) + (define-key xah-find-output-mode-map (kbd "") 'xah-find-previous-file) + + (define-key xah-find-output-mode-map (kbd "TAB") 'xah-find-next-match) + (define-key xah-find-output-mode-map (kbd "") 'xah-find-previous-match) + (define-key xah-find-output-mode-map (kbd "") 'xah-find--mouse-jump-to-place) + (define-key xah-find-output-mode-map (kbd "M-n") 'xah-find-next-file) + (define-key xah-find-output-mode-map (kbd "M-p") 'xah-find-previous-file) + (define-key xah-find-output-mode-map (kbd "RET") 'xah-find--jump-to-place) + ) + +(defvar xah-find-output-syntax-table nil "Syntax table for `xah-find-output-mode'.") + +(setq xah-find-output-syntax-table + (let ( (synTable (make-syntax-table))) + (modify-syntax-entry ?\" "." synTable) + ;; (modify-syntax-entry ?〖 "(〗" synTable) + ;; (modify-syntax-entry ?〗 "(〖" synTable) + synTable)) + +(setq xah-find-font-lock-keywords + (let ( + (xMatch (format "%s\\([^%s]+\\)%s" xah-find-occur-prefix xah-find-occur-postfix xah-find-occur-postfix)) + + (xRep (format "%s\\([^%s]+\\)%s" xah-find-replace-prefix xah-find-replace-postfix xah-find-replace-postfix)) + (xfPath (format "%s\\([^%s]+\\)%s" xah-find-filepath-prefix xah-find-filepath-postfix xah-find-filepath-postfix))) + + `( + (,xMatch . (1 'xah-find-match-highlight)) + (,xRep . (1 'xah-find-replace-highlight)) + (,xfPath . (1 'xah-find-file-path-highlight))))) + +(define-derived-mode xah-find-output-mode fundamental-mode "∑xah-find" + "Major mode for reading output for xah-find commands. +home page: +URL `http://ergoemacs.org/emacs/elisp-xah-find-text.html' + +\\{xah-find-output-mode-map} +Version 2021-06-23" + (setq font-lock-defaults '((xah-find-font-lock-keywords))) + (set-syntax-table xah-find-output-syntax-table)) + +(defun xah-find-next-match () + "Put cursor to next occurrence." + (interactive) + (search-forward xah-find-occur-prefix nil t )) + +(defun xah-find-previous-match () + "Put cursor to previous occurrence." + (interactive) + (search-backward xah-find-occur-postfix nil t )) + +(defun xah-find-next-file () + "Put cursor to next file." + (interactive) + (search-forward xah-find-filepath-prefix nil t )) + +(defun xah-find-previous-file () + "Put cursor to previous file." + (interactive) + (search-backward xah-find-filepath-postfix nil t )) + +(defun xah-find--mouse-jump-to-place (Event) + "Open file and put cursor at location of the occurrence. +Version 2016-12-18" + (interactive "e") + (let* ( + ($pos (posn-point (event-end Event))) + ($fpath (get-text-property $pos 'xah-find-fpath)) + ($posJumpTo (get-text-property $pos 'xah-find-pos))) + (when $fpath + (progn + (find-file-other-window $fpath) + (when $posJumpTo (goto-char $posJumpTo)))))) + +;; (defun xah-find--jump-to-place () +;; "Open file and put cursor at location of the occurrence. +;; Version 2017-04-07" +;; (interactive) +;; (let (($fpath (get-text-property (point) 'xah-find-fpath)) +;; ($posJumpTo (get-text-property (point) 'xah-find-pos))) +;; (if $fpath +;; (if (file-exists-p $fpath) +;; (progn +;; (find-file-other-window $fpath) +;; (when $posJumpTo (goto-char $posJumpTo))) +;; (error "File at 「%s」 does not exist." $fpath)) +;; (insert "\n")))) + +(defun xah-find--jump-to-place () + "Open file and put cursor at location of the occurrence. +Version 2019-03-14" + (interactive) + (let (($fpath (get-text-property (point) 'xah-find-fpath)) + ($posJumpTo (get-text-property (point) 'xah-find-pos)) + ($p0 (point)) + $p1 $p2 + ) + (if $fpath + (if (file-exists-p $fpath) + (progn + (find-file-other-window $fpath) + (when $posJumpTo (goto-char $posJumpTo))) + (error "File at 「%s」 does not exist." $fpath)) + (progn + (save-excursion + (goto-char $p0) + + ;; (if (eq (char-after (line-beginning-position)) (string-to-char xah-find-filepath-prefix )) + ;; (progn ) + ;; (progn )) + + (search-forward xah-find-file-separator) + (search-backward xah-find-filepath-prefix ) + (setq $p1 (1+ (point))) + (search-forward xah-find-filepath-postfix) + (setq $p2 (1- (point))) + (setq $fpath (buffer-substring-no-properties $p1 $p2)) + + (progn + (goto-char $p0) + (if (search-backward xah-find-pos-prefix nil t) + (progn + (setq $p1 (1+ (point))) + (search-forward xah-find-pos-postfix ) + (setq $p2 (1- (point))) + (setq $posJumpTo (string-to-number (buffer-substring-no-properties $p1 $p2)))) + (setq $posJumpTo nil)))) + (if (file-exists-p $fpath) + (progn + (find-file-other-window $fpath) + (when $posJumpTo (goto-char $posJumpTo))) + (error "File at 「%s」 does not exist." $fpath)))))) + +;; HHH___________________________________________________________________ +(defun xah-find--backup-suffix (S) + "Return a string of the form 「~‹S›~‹date time stamp›~」" + (concat "~" S (format-time-string "%Y%m%dT%H%M%S") "~")) + +(defun xah-find--current-date-time-string () + "Return current date-time string in this format 「2012-04-05T21:08:24-07:00」" + (concat + (format-time-string "%Y-%m-%dT%T") + (funcall (lambda (x) (format "%s:%s" (substring x 0 3) (substring x 3 5))) (format-time-string "%z")))) + +(defun xah-find--print-header (BufferObj Cmd InputDir PathRegex SearchStr &optional ReplaceStr Write-file-p BackupQ) + "Print things" + (princ + (concat + "-*- coding: utf-8; mode: xah-find-output -*-" "\n" + "Datetime: " (xah-find--current-date-time-string) "\n" + "Result of: " Cmd "\n" + (format "Directory: %s\n" InputDir ) + (format "Path regex: %s\n" PathRegex ) + (format "Write to file: %s\n" Write-file-p ) + (format "Backup: %s\n" BackupQ ) + (format "Search string: %s\n" SearchStr ) + (when ReplaceStr (format "Replace string [[%s]]\n" ReplaceStr)) + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n" + ) + BufferObj)) + +(defun xah-find--occur-output (P1 P2 Fpath Buff &optional NoContextString-p AltColor) + "Print result to a output buffer, with text properties (e.g. highlight and link). +P1 P2 are region boundary. Region of current buffer are grabbed. The region typically is the searched text. +Fpath is file path to be used as property value for clickable link. +Buff is the buffer to insert P1 P2 region. +NoContextString-p if true, don't add text before and after the region of interest. Else, `xah-find-context-char-count-before' number of chars are inserted before, and similar for `xah-find-context-char-count-after'. +AltColor if true, use a different highlight color face `xah-find-replace-highlight'. Else, use `xah-find-match-highlight'. + Version 2017-04-07 2021-08-05" + (let* ( + ($begin (max 1 (- P1 xah-find-context-char-count-before ))) + ($end (min (point-max) (+ P2 xah-find-context-char-count-after ))) + ($textBefore (if NoContextString-p "" (buffer-substring-no-properties $begin P1 ))) + $textMiddle + ($textAfter (if NoContextString-p "" (buffer-substring-no-properties P2 $end))) + ($face (if AltColor 'xah-find-replace-highlight 'xah-find-match-highlight)) + $bracketL $bracketR + ) + (put-text-property P1 P2 'face $face) + (put-text-property P1 P2 'xah-find-fpath Fpath) + (put-text-property P1 P2 'xah-find-pos P1) + (add-text-properties P1 P2 '(mouse-face highlight)) + (setq $textMiddle (buffer-substring P1 P2 )) + (if AltColor + (setq $bracketL xah-find-replace-prefix $bracketR xah-find-replace-postfix ) + (setq $bracketL xah-find-occur-prefix $bracketR xah-find-occur-postfix )) + (with-current-buffer Buff + (insert + (format "%s%s%s\n" xah-find-pos-prefix P1 xah-find-pos-postfix) + $textBefore + $bracketL + $textMiddle + $bracketR + $textAfter + "\n" + xah-find-occur-separator )))) + +;; (defun xah-find--print-replace-block (P1 P2 Buff) +;; "print " +;; (princ (concat "❬" (buffer-substring-no-properties P1 P2 ) "❭" "\n" xah-find-occur-separator) Buff)) + +(defun xah-find--print-file-count (Filepath4287 Count8086 BuffObj32) + "Print file path and count" + (princ (format "%d %s%s%s\n%s" + Count8086 + xah-find-filepath-prefix + Filepath4287 + xah-find-filepath-postfix + xah-find-file-separator) + BuffObj32)) + +(defun xah-find--switch-to-output (Buffer) + "switch to Buffer and highlight stuff" + (let ($p3 $p4) + (switch-to-buffer Buffer) + (progn + (goto-char (point-min)) + (while (search-forward xah-find-filepath-prefix nil t) + (setq $p3 (point)) + (search-forward xah-find-filepath-postfix nil nil) + (setq $p4 (match-beginning 0)) + (put-text-property $p3 $p4 'xah-find-fpath (buffer-substring-no-properties $p3 $p4)) + (add-text-properties $p3 $p4 '(mouse-face highlight)) + (put-text-property (line-beginning-position) (line-end-position) 'face 'xah-find-file-path-highlight))) + + (goto-char (point-min)) + (search-forward "━" nil t) ; todo, need fix + (search-forward xah-find-occur-prefix nil t) + (xah-find-output-mode) + )) + +;; HHH___________________________________________________________________ + +(defun xah-find--get-fpath-regex (&optional DefaultExt) + "Returns a string, that is a regex to match a file extension. +The result is based on current buffer's file extension. +If current file doesn't have extension or current buffer isn't a file, then extension DefaultExt is used. +DefaultExt should be a string, without dot, such as 「\"html\"」. +If DefaultExt is nil, 「\"html\"」 is used. +Example return value: 「ββ.htmlββ'」, where β is a backslash. +" + (let ( + ($buff-is-file-p (buffer-file-name)) + $fname-ext + $default-ext + ) + (setq $default-ext (if (null DefaultExt) + (progn "html") + (progn DefaultExt))) + (if $buff-is-file-p + (progn + (setq $fname-ext (file-name-extension (buffer-file-name))) + (if (or (null $fname-ext) (equal $fname-ext "")) + (progn (concat "\\." $default-ext "$")) + (progn (concat "\\." $fname-ext "$")))) + (progn (concat "\\." $default-ext "$"))))) + +;;;###autoload +(defun xah-find-count (SearchStr CountExpr CountNumber InputDir PathRegex) + "Report how many occurrences of a string, of a given dir. +Similar to `rgrep', but written in pure elisp. +Result is shown in buffer *xah-find output*. +Case sensitivity is determined by `case-fold-search'. Call `toggle-case-fold-search' to change. +`xah-find-dir-ignore-regex-list' is respected. +\\{xah-find-output-mode-map} + +Version 2021-10-11" + (interactive + (let ( $operator) + (list + (read-string (format "Search string (default %s): " (current-word)) nil 'query-replace-history (current-word)) + (setq $operator (ido-completing-read "Report on: " '("greater than" "greater or equal to" "equal" "not equal" "less than" "less or equal to" ))) + (read-string (format "Count %s: " $operator) "0") + (ido-read-directory-name "Directory: " default-directory default-directory "MUSTMATCH") + (read-from-minibuffer "File path regex: " (xah-find--get-fpath-regex "el") nil nil 'dired-regexp-history)))) + (let* (($outBufName "*xah-find output*") + $outBuffer + ($countOperator + (cond + ((string-equal "less than" CountExpr ) '<) + ((string-equal "less or equal to" CountExpr ) '<=) + ((string-equal "greater than" CountExpr ) '>) + ((string-equal "greater or equal to" CountExpr ) '>=) + ((string-equal "equal" CountExpr ) '=) + ((string-equal "not equal" CountExpr ) '/=) + (t (error "count expression 「%s」 is wrong!" CountExpr )))) + ($countNumber (string-to-number CountNumber))) + (when (get-buffer $outBufName) (kill-buffer $outBufName)) + (setq $outBuffer (generate-new-buffer $outBufName)) + (xah-find--print-header $outBuffer "xah-find-count" InputDir PathRegex SearchStr ) + (mapc + (lambda ($f) + (let (($count 0)) + (with-temp-buffer + (insert-file-contents $f) + (goto-char (point-min)) + (while (search-forward SearchStr nil t) (setq $count (1+ $count))) + (when (funcall $countOperator $count $countNumber) + (xah-find--print-file-count $f $count $outBuffer))))) + (seq-filter (lambda (x) (not (xah-find--ignore-dir-p x))) + (directory-files-recursively InputDir PathRegex))) + (princ "Done." $outBuffer) + (xah-find--switch-to-output $outBuffer))) + +;;;###autoload +(defun xah-find-text (SearchStr InputDir PathRegex FixedCaseSearchQ PrintContext-p) + "Report files that contain string. +By default, not case sensitive, and print surrounding text. +If `universal-argument' is called first, prompt to ask. +`xah-find-dir-ignore-regex-list' is respected. +Result is shown in buffer *xah-find output*. +\\{xah-find-output-mode-map} + +version 2021-10-11" + (interactive + (let (($defaultInput (if (region-active-p) (buffer-substring-no-properties (region-beginning) (region-end)) (current-word)))) + (list + (read-string (format "Search string (default %s): " $defaultInput) nil 'query-replace-history $defaultInput) + (ido-read-directory-name "Directory: " default-directory default-directory "MUSTMATCH") + (read-from-minibuffer "File path regex: " (xah-find--get-fpath-regex "html") nil nil 'dired-regexp-history) + (if current-prefix-arg (y-or-n-p "Fixed case in search?") nil ) + (if current-prefix-arg (y-or-n-p "Print surrounding Text?") t )))) + (let* ((case-fold-search (not FixedCaseSearchQ)) + ($count 0) + ($outBufName "*xah-find output*") + $outBuffer + ) + (setq InputDir (file-name-as-directory InputDir)) ; normalize dir path + (when (get-buffer $outBufName) (kill-buffer $outBufName)) + (setq $outBuffer (generate-new-buffer $outBufName)) + (xah-find--print-header $outBuffer "xah-find-text" InputDir PathRegex SearchStr ) + (mapc + (lambda ($path) + (setq $count 0) + (with-temp-buffer + (insert-file-contents $path) + (while (search-forward SearchStr nil t) + (setq $count (1+ $count)) + (when PrintContext-p (xah-find--occur-output (match-beginning 0) (match-end 0) $path $outBuffer))) + (when (> $count 0) (xah-find--print-file-count $path $count $outBuffer)))) + (seq-filter (lambda (x) (not (xah-find--ignore-dir-p x))) + (directory-files-recursively InputDir PathRegex))) + (princ "Done." $outBuffer) + (xah-find--switch-to-output $outBuffer))) + +(defun xah-find-count-slash (Path) + "Count the number of slash in path. +Useful for finding the level of a nested dir. +Note: you should probably call `expand-file-name' on Path first to canonize path, to make sure dir name always ends in slash. +Version 2021-10-11" + (interactive) + (seq-count (lambda (x) (char-equal x ?/)) Path)) + +;;;###autoload +(defun xah-find-replace-text (SearchStr ReplaceStr InputDir PathRegex DepthMin DepthMax WriteToFileQ FixedCaseSearchQ FixedCaseReplaceQ BackupQ) + "Find/Replace string in all files of a directory. +Search string can span multiple lines. +Search string is not regex. +`xah-find-dir-ignore-regex-list' is respected. + +Backup, if requested, backup filenames has suffix with timestamp, like this: ~xf20150531T233826~ + +Result is shown in buffer *xah-find output*. +\\{xah-find-output-mode-map} + +version 2021-10-11" + (interactive + (let (($searchStr (read-string (format "Search string (default %s): " (current-word)) nil 'query-replace-history (current-word))) + ($replaceStr (read-string "Replace string: " nil 'query-replace-history)) + ($inputDir (ido-read-directory-name "Directory: " default-directory default-directory "MUSTMATCH")) + ($pathRegex (read-from-minibuffer "File path regex: " (xah-find--get-fpath-regex "el") nil nil 'dired-regexp-history)) + ;; ($recurseQ (yes-or-no-p "Recurse to subdirs?")) + ($depthMin (read-number "Min dir depth. Start dir has depth 0:" 0)) + ($depthMax (read-number "Max dir depth. (max+1 depth subdir files are excluded):" 9)) + ($writeToFileQ (y-or-n-p "Write changes to file?")) + ($fixedCaseSearchQ (y-or-n-p "Fixed case in search?")) + ($fixedCaseReplaceQ (y-or-n-p "Fixed case in replacement?")) + ($backupQ (y-or-n-p "Make backup?"))) + (list $searchStr $replaceStr $inputDir $pathRegex + $depthMin $depthMax + $writeToFileQ $fixedCaseSearchQ $fixedCaseReplaceQ $backupQ))) + (let (($outBufName "*xah-find output*") + $outBuffer + ($backupSuffix (xah-find--backup-suffix "xf")) + ($rootDepth (xah-find-count-slash (expand-file-name InputDir)))) + (when (get-buffer $outBufName) (kill-buffer $outBufName)) + (setq $outBuffer (generate-new-buffer $outBufName)) + (xah-find--print-header $outBuffer "xah-find-replace-text" InputDir PathRegex SearchStr ReplaceStr WriteToFileQ BackupQ) + (mapc + (lambda ($f) + (let ((case-fold-search (not FixedCaseSearchQ)) + ($count 0)) + (with-temp-buffer + (insert-file-contents $f) + (while (search-forward SearchStr nil t) + (setq $count (1+ $count)) + (replace-match ReplaceStr FixedCaseReplaceQ "literalreplace") + (xah-find--occur-output (match-beginning 0) (point) $f $outBuffer)) + (when (> $count 0) + (when WriteToFileQ + (when BackupQ (copy-file $f (concat $f $backupSuffix) t)) + (write-region (point-min) (point-max) $f nil 3)) + (xah-find--print-file-count $f $count $outBuffer))))) + (seq-filter + (lambda (x) + (let (($df (- (xah-find-count-slash x) $rootDepth))) + (and (>= $df DepthMin) (<= $df DepthMax)))) + (directory-files-recursively InputDir PathRegex))) + (princ "Done." $outBuffer) + (xah-find--switch-to-output $outBuffer))) + +;;;###autoload +(defun xah-find-text-regex (SearchRegex InputDir PathRegex RecurseQ FixedCaseSearchQ PrintContextLevel) + "Report files that contain a string pattern, similar to `rgrep'. +Result is shown in buffer *xah-find output*. +`xah-find-dir-ignore-regex-list' is respected. + +\\{xah-find-output-mode-map} +Version 2016-12-21 2021-10-11" + (interactive + (list + (read-string (format "Search regex (default %s): " (current-word)) nil 'query-replace-history (current-word)) + (ido-read-directory-name "Directory: " default-directory default-directory "MUSTMATCH") + (read-from-minibuffer "File path regex: " (xah-find--get-fpath-regex "el") nil nil 'dired-regexp-history) + (yes-or-no-p "Recurse to subdirs?") + (y-or-n-p "Fixed case search?") + (ido-completing-read "Print context level: " '("with context string" "just matched pattern" "none")))) + (let (($count 0) + ($outBufName "*xah-find output*") + $outBuffer + ) + (setq InputDir (file-name-as-directory InputDir)) ; add ending slash + (when (get-buffer $outBufName) (kill-buffer $outBufName)) + (setq $outBuffer (generate-new-buffer $outBufName)) + (xah-find--print-header $outBuffer "xah-find-text-regex" InputDir PathRegex SearchRegex) + (mapc + (lambda ($fp) + (setq $count 0) + (with-temp-buffer + (insert-file-contents $fp) + (setq case-fold-search (not FixedCaseSearchQ)) + (while (re-search-forward SearchRegex nil t) + (setq $count (1+ $count)) + (cond + ((equal PrintContextLevel "none") nil) + ((equal PrintContextLevel "just matched pattern") + (xah-find--occur-output (match-beginning 0) (match-end 0) $fp $outBuffer t)) + ((equal PrintContextLevel "with context string") + (xah-find--occur-output (match-beginning 0) (match-end 0) $fp $outBuffer)))) + (when (> $count 0) (xah-find--print-file-count $fp $count $outBuffer)))) + (seq-filter (lambda (x) (not (xah-find--ignore-dir-p x))) + (if RecurseQ + (directory-files-recursively InputDir PathRegex) + (directory-files InputDir t PathRegex)))) + (princ "Done." $outBuffer) + (xah-find--switch-to-output $outBuffer))) + +;;;###autoload +(defun xah-find-replace-text-regex (Regex ReplaceStr InputDir PathRegex WriteToFileQ FixedCaseSearchQ FixedCaseReplaceQ ShowcontexQ BackupQ) + "Find/Replace by regex in all files of a directory. + +`xah-find-dir-ignore-regex-list' is respected. + +Backup, if requested, backup filenames has suffix with timestamp, like this: ~xf20150531T233826~ + +When called in lisp code: +Regex is a regex pattern. +ReplaceStr is replacement string. +InputDir is input directory to search (includes all nested subdirectories). +PathRegex is a regex to filter file paths. +WriteToFileQ, when true, write to file, else, print a report of changes only. +FixedCaseSearchQ sets `case-fold-search' for this operation. +FixedCaseReplaceQ if true, then the letter-case in replacement is literal. (this is relevant only if FixedCaseSearchQ is true.) +ShowcontexQ print characters before and after match. +BackupQ if ture does backup. + +Result is shown in buffer *xah-find output*. +\\{xah-find-output-mode-map} + +Version 2018-08-20 2021-10-11" + (interactive + (list + (read-regexp "Find regex: " ) + (read-string (format "Replace string: ") nil 'query-replace-history) + (ido-read-directory-name "Directory: " default-directory default-directory "MUSTMATCH") + (read-from-minibuffer "File path regex: " (xah-find--get-fpath-regex "el") nil nil 'dired-regexp-history) + (y-or-n-p "Write changes to file?") + (y-or-n-p "Fixed case in search?") + (y-or-n-p "Fixed case in replacement?") + (y-or-n-p "Show context before after in output?") + (y-or-n-p "Make backup?"))) + (let (($outBufName "*xah-find output*") + $outBuffer + ($backupSuffix (xah-find--backup-suffix "xfr"))) + (when (get-buffer $outBufName) (kill-buffer $outBufName)) + (setq $outBuffer (generate-new-buffer $outBufName)) + (xah-find--print-header $outBuffer "xah-find-replace-text-regex" InputDir PathRegex Regex ReplaceStr WriteToFileQ BackupQ ) + (mapc + (lambda ($fp) + (let (($count 0)) + (with-temp-buffer + (insert-file-contents $fp) + (setq case-fold-search (not FixedCaseSearchQ)) + (while (re-search-forward Regex nil t) + (setq $count (1+ $count)) + ;; (xah-find--print-occur-block (match-beginning 0) (match-end 0) $outBuffer) + (xah-find--occur-output (match-beginning 0) (match-end 0) $fp $outBuffer t) + (replace-match ReplaceStr FixedCaseReplaceQ) + (xah-find--occur-output (match-beginning 0) (point) $fp $outBuffer (not ShowcontexQ) t)) + (when (> $count 0) + (xah-find--print-file-count $fp $count $outBuffer) + (when WriteToFileQ + (when BackupQ + (copy-file $fp (concat $fp $backupSuffix) t)) + (write-region (point-min) (point-max) $fp nil 3)))))) + (seq-filter (lambda (x) (not (xah-find--ignore-dir-p x))) + (directory-files-recursively InputDir PathRegex))) + (princ "Done." $outBuffer) + (xah-find--switch-to-output $outBuffer))) + +(provide 'xah-find) + +;;; xah-find.el ends here diff --git a/.emacs.d/xahemacs_2021-10-17.zip b/.emacs.d/xahemacs_2021-10-17.zip new file mode 100755 index 0000000..f5b70ab Binary files /dev/null and b/.emacs.d/xahemacs_2021-10-17.zip differ diff --git a/global.focus-config b/global.focus-config new file mode 100644 index 0000000..671c058 --- /dev/null +++ b/global.focus-config @@ -0,0 +1,519 @@ +[22] # Version number. Do not delete. + +[[workspace]] +# These directories and files will be scanned when a workspace is opened so that search etc. works. +# Example: +# /Users/my_name/projects/my_project # <- the first directory in the list becomes the working directory +# /Users/my_name/jai +# src # <- this would be relative to the config file location + +[ignore] +# Files and directories matching the following wildcards will not be loaded or descended into +# Example: +# *.js - will ignore all files with a '.js' extension +# tmp* - will ignore any files or directories which start with 'tmp' +# /Users/my_name/project/dirname/** - will ignore everything under `dirname` +# /Users/my_name/project/dirname/* - will ignore all files under `dirname`, but not recursively +.svn +.git + +[allow] +# Files and directories matching the wildcards in this section will be loaded, even if they are ignored in the previous section. +# NOTE: known binary file extensions are ignored by default (*.so, *.pdb etc.). If this is not what you want, you can explicitly allow them here. + +[file associations] +# Optional file associations in the format ` ... : ` +# Example: +# *.hpp *.hh *.h : cpp +# todo.txt : todo + + +[[settings]] + +maximize_on_start: false +open_on_the_biggest_monitor: false +cursor_as_block: true +cursor_blink_time_in_seconds: 5 +highlight_selection_occurrences: true +highlight_line_with_cursor: false +highlight_matching_brackets: true +show_paste_effect: true +strip_trailing_whitespace_on_save: except_lines_with_cursor # options: all, except_lines_with_cursor, disabled +smooth_scrolling: true +scroll_beyond_last_line: true +double_shift_to_search_in_workspace: false +can_cancel_go_to_line: true +copy_whole_line_without_selection: true +line_height_scale_percent: 100 +max_editor_width: -1 +line_wrap_is_on_by_default: false +show_line_numbers: false +show_ruler_at_column: 0 +colored_titlebar: false +dark_titlebar: false +hide_mouse_when_typing: false +draw_indent_guides: false +auto_surround_with_brackets_and_quotes: false +auto_close_brackets: false +persist_local_search_results: false # if true, search results will stay highlighted and you have to dismiss them using the `escape` action +load_most_recent_project_on_start: true +projects_sorting_order: most_recent_first + +build_panel_stays_in_one_place: false # if true, the build panel will flip to the inactive pane in two pane layouts +build_panel_line_wrap_always_on: true +build_panel_width_percent: 100 +build_panel_height_percent: 30 + +color_preview_popup: enabled # options: enabled, minimized, disabled +search_is_case_sensitive_when_uppercase_present: true + +detect_indentation: true +indent_using: spaces +tab_size: 4 + +status_bar_position: bottom # options: top, bottom +status_bar_show_cursors_off_screen: true +status_bar_show_line_col: true +status_bar_show_indentation: true +status_bar_show_selected_text_length: false + +show_scrollbar_marks: true +scrollbar_width_scale: 1.0 +scrollbar_min_opacity: 0.0 # if you want the scrollbar to be always visible, set this to 1.0 +scrollbar_max_opacity: 1.0 +scrollbar_fade_in_sensitivity: 10.0 # controls when the scrollbar appears as the mouse pointer gets close +scrollbar_fade_out_delay_seconds: 1.0 # how long the scrollbar stays visible after scrolling + +active_pane_border_width: 1.0 # set the width to 0.0 to disable +inactive_pane_dim_overlay_opacity: 0.1 # controls how dim the inactive panes should be + +# Added after migration to version [22] +comment_highlight_words1: @TODO, TODO, TODO:, NOTE, NOTE: # a comma-separated list of words to highlight within comments using the color `code_comment_highlight1` +comment_highlight_words2: @FIXME # words to highlight using `code_comment_highlight2` +comment_highlight_words3: @ROBUSTNESS # words to highlight using `code_comment_highlight3` +comment_highlight_words4: @INCOMPLETE # words to highlight using `code_comment_highlight4` + +# NOTE: some settings can be specified for a subset of files, based on their language or a wildcard, +# for example: +# [file: , , ...] +# [lang: golang, cpp, c, ...] + +[file: *.md, *.txt] +line_wrap_is_on_by_default: false +draw_indent_guides: false + + +# Below is an example configuration for build commands +# (uncomment and modify to use) + +# [[build commands]] +# build_working_dir: # <- paths relative to the workspace working dir (the first one in the list) are allowed +# open_panel_on_build: true # <- any settings specified here will apply to all commands unless overridden +# close_panel_on_success: false +# clear_build_output_before_running: false +# error_regex: # see examples below +# auto_jump_to_error: false + +# [Debug Build And Run] # <- command name. Can be arbitrary +# build_command: jai main.jai # should be an executable or a script +# build_working_dir: +# timeout_in_seconds: 5 # if you don't want a timeout, don't specify it +# run_command: test.exe # will be run if build succeeds +# run_working_dir: /Users/user # working dir for the run command +# key_binding: F5 + +# [Run] # <- You could have commands that don't build anything and just run something +# run_command: test +# run_working_dir: /Users/user/test +# key_binding: Ctrl-F5 + +# [Release] +# build_command: jai first.jai - release +# key_binding: F9 + + +# For jai: ^(?P.*):(?P\d+),(?P\d+): (?PError|Warning|Info|...):* (?P.*)|^(?P.*error LNK.*) +# For golang: ^(?P.*):(?P\d+):(?P\d+): (?P.*)$ +# ... let us know what regex works for you and we'll add it here + +# NOTE: +# You can use the following variables in build commands: +# %FILE% - full path to currenly active file +# %FILE_DIR% - the directory of the currently active file +# %FILE_NAME% - current file name, with extension +# %FILE_NAME_NO_EXTENSION% - current file name, without extension +# %BUILD_WORKING_DIR% - working dir of the build command +# %RUN_WORKING_DIR% - working dir of the run command +# %PROJECT_CONFIG_DIR% - the dir containing the active project config file + + +[[keymap]] + +# - The first matching combination will be used, so order matters + +[editors] # <- this means that the following key combos will apply only when editing text + +# Key combination Action +Cmd-E copy_current_line_info +Cmd-D select_word_or_create_another_cursor +Cmd-R revert_select_word_or_create_another_cursor +Cmd-Y move_selection_to_next_word +Cmd-Shift-A select_all_occurrences + +Cmd-Shift-D duplicate_lines + +# These shortcuts can be annoying to accidentally use, so they are commented out by default +# Shift-Backspace delete_line_and_go_up +# Shift-Delete delete_line + +Opt-ArrowUp move_selected_lines_up +Opt-ArrowDown move_selected_lines_down + +Cmd-J join_lines +Cmd-Shift-J join_lines_no_spaces_in_between + +Cmd-U change_case_cycle + +Tab indent_or_go_to_next_tabstop +Shift-Tab unindent + +Cmd-] indent +Cmd-[ unindent + +Cmd-S save +Cmd-Shift-S save_as + +Cmd-Opt-Minus cursor_go_back +Cmd-Opt-Plus cursor_go_forward + +Cmd-Shift-Minus go_to_previous_buffer +Cmd-Shift-Plus go_to_next_buffer + +Cmd-/ toggle_comment +Cmd-L select_line + +Ctrl-Cmd-ArrowUp scroll_viewport_up +Ctrl-Cmd-ArrowDown scroll_viewport_down +Ctrl-Cmd-ArrowLeft scroll_viewport_left +Ctrl-Cmd-ArrowRight scroll_viewport_right + +Cmd-Shift-C move_cursor_to_viewport_center + +{Shift}-Ctrl-Opt-Cmd-ArrowUp move_up_to_empty_line +{Shift}-Ctrl-Opt-Cmd-ArrowDown move_down_to_empty_line + +Opt-Shift-I add_cursors_to_line_ends +Opt-Shift-Ctrl-I add_cursors_to_line_starts + +Enter break_line +Cmd-Enter new_line_below_without_breaking +Cmd-Shift-Enter new_line_above_without_breaking + +NumpadEnter break_line +Cmd-NumpadEnter new_line_below_without_breaking +Cmd-Shift-NumpadEnter new_line_above_without_breaking + +Cmd-Opt-S ArrowLeft move_editor_left +Cmd-Opt-S ArrowRight move_editor_right +Cmd-Opt-S ArrowUp move_editor_top +Cmd-Opt-S ArrowDown move_editor_bottom + +Cmd-\ split_right +Cmd-Shift-| split_bottom + +Cmd-Opt-X ArrowLeft split_left +Cmd-Opt-X ArrowRight split_right +Cmd-Opt-X ArrowUp split_top +Cmd-Opt-X ArrowDown split_bottom + +Cmd-Opt-D ArrowLeft duplicate_editor_left +Cmd-Opt-D ArrowRight duplicate_editor_right +Cmd-Opt-D ArrowUp duplicate_editor_top +Cmd-Opt-D ArrowDown duplicate_editor_bottom + +Cmd-Shift-, duplicate_editor + +Cmd-Shift-T reopen_closed_editor + +Cmd-N create_new_file +Cmd-Shift-N create_new_file_in_side_pane + +Opt-Shift-ArrowUp create_cursor_above +Opt-Shift-ArrowDown create_cursor_below + +Opt-Cmd-A align_cursors + +Opt-Cmd-Z toggle_line_wrap +Opt-Cmd-L toggle_line_numbers + +Opt-Cmd-Shift-N open_another_editor_instance + + +[open file dialog] + +Shift-Enter open_entry_in_explorer +Tab open_directory +Backspace pop_directory + + +[search dialog] +Shift-Enter move_up # an alternative way to move + +Cmd-Opt-C toggle_case_sensitive +Cmd-Opt-W toggle_whole_word +Cmd-Opt-R toggle_regex_search + +[common] + +# Common key combos may be used as a fallback if they are not defined in more specific sections. +# For example, if "move_up" is not defined in [editors], the one in the [common] section will be matched. + +Cmd-Shift-P show_commands + +Cmd-F search_in_buffer_dropdown_mode +Cmd-Opt-F search_in_buffer +Cmd-Shift-F search_in_project + +F12 switch_to_project + +Cmd-P open_file_by_name +Cmd-O navigate_to_file +Cmd-Shift-O navigate_to_file_from_root +Ctrl-Tab switch_between_open_files + +Cmd-G go_to_line + +Cmd-C copy +Cmd-X cut +Cmd-V paste + +Cmd-Z undo +Cmd-Shift-Z redo + +Cmd-D select_word +Cmd-A select_all + +Cmd-K cut_to_end_of_line +Cmd-Delete delete_to_end_of_line +Cmd-Backspace delete_to_start_of_line + +Cmd-W close_editor + +Cmd-Shift-W close_pane +Cmd-Opt-W ArrowLeft close_left_pane +Cmd-Opt-W ArrowRight close_right_pane +Cmd-Opt-W ArrowUp close_top_pane +Cmd-Opt-W ArrowDown close_bottom_pane +Cmd-Shift-Opt-W close_other_panes + +Escape escape # combines close_dialog and remove_additional_cursors + +Cmd-Shift-L toggle_expand + +Cmd-B build_panel_toggle + +Enter open_entry +Cmd-Enter open_entry_in_side_pane + +Ctrl-Cmd-Shift-ArrowLeft switch_to_left_pane +Ctrl-Cmd-Shift-ArrowRight switch_to_right_pane +Ctrl-Cmd-Shift-ArrowUp switch_to_top_pane +Ctrl-Cmd-Shift-ArrowDown switch_to_bottom_pane +Cmd-, switch_to_side_pane + +Cmd-1 switch_to_pane_1 +Cmd-2 switch_to_pane_2 +Cmd-3 switch_to_pane_3 +Cmd-4 switch_to_pane_4 +Cmd-5 switch_to_pane_5 +Cmd-6 switch_to_pane_6 +Cmd-7 switch_to_pane_7 +Cmd-8 switch_to_pane_8 +Cmd-9 switch_to_pane_9 + +Tab focus_next_ui_element +Shift-Tab focus_previous_ui_element + +# Ctrl-Cmd-F will always toggle fullscreen on macOS (like most other apps), but you can uncomment this to use something else. +# F11 toggle_fullscreen + +# {Shift}- means shift is optional, the key combination will still be matched. +# NOTE: in this editor the Shift key is hard-coded to extend selection when held +{Shift}-ArrowUp move_up +{Shift}-Opt-Cmd-ArrowUp move_up_fast +{Shift}-PageUp move_up_one_page + +{Shift}-ArrowDown move_down +{Shift}-Opt-Cmd-ArrowDown move_down_fast +{Shift}-PageDown move_down_one_page + +{Shift}-ArrowLeft move_left +{Shift}-Opt-ArrowLeft move_left_by_character_type +{Shift}-Opt-Cmd-ArrowLeft move_left_through_word_throttled +{Shift}-Ctrl-ArrowLeft move_left_through_word +{Shift}-Cmd-ArrowLeft jump_to_line_start + +{Shift}-ArrowRight move_right +{Shift}-Opt-ArrowRight move_right_by_character_type +{Shift}-Opt-Cmd-ArrowRight move_right_through_word_throttled +{Shift}-Ctrl-ArrowRight move_right_through_word +{Shift}-Cmd-ArrowRight jump_to_line_end + +{Shift}-Cmd-ArrowUp jump_to_file_start +{Shift}-Cmd-ArrowDown jump_to_file_end + +{Shift}-Home jump_to_line_start +{Shift}-End jump_to_line_end +{Shift}-Cmd-Home jump_to_file_start +{Shift}-Cmd-End jump_to_file_end +{Shift}-Cmd-M jump_to_matching_bracket + +{Shift}-Backspace delete_left_char +{Shift}-Delete delete_right_char +{Shift}-Opt-Backspace delete_left_by_character_type +{Shift}-Opt-Delete delete_right_by_character_type +{Shift}-Opt-Cmd-Backspace delete_left_by_character_type_fast +{Shift}-Opt-Cmd-Delete delete_right_by_character_type_fast +{Shift}-Ctrl-Backspace delete_left_through_word +{Shift}-Ctrl-Delete delete_right_through_word + +Cmd-= increase_font_size +Cmd-Minus decrease_font_size +Cmd-0 reset_font_size_to_default +Cmd-MouseMiddle reset_font_size_to_default + +F8 go_to_next_build_error +Cmd-F8 go_to_next_build_error_in_side_pane +Shift-F8 go_to_previous_build_error +Shift-Cmd-F8 go_to_previous_build_error_in_side_pane + + +[[style]] + +theme: default + +[fonts] +font: default +font_ui: default +font_ui_bold: default +font_size: 15 +font_ui_size: 12 +anti_aliasing: lcd # options: lcd, normal +hinting: true + +[colors] +background0: 15212AFF +background1: 10191FFF +background2: 18262FFF +background3: 1A2831FF +background4: 21333FFF + +# NOTE: region_scope colors only work for Jai at the moment +region_scope_export: 15212AFF +region_scope_file: 131C22FF +region_scope_module: 1A2831FF + +region_header: 1A5152FF +region_success: 226022FF +region_warning: 986032FF +region_error: 772222FF +region_heredoc: 090e12FF + +selection_active: 1C4449FF +selection_inactive: 1C44497F +selection_highlight: FCEDFC26 +search_result_active: 8E772EFF +search_result_inactive: FCEDFC26 +scrollbar: 33CCCC19 +scrollbar_hover: 33CCCC4C +scrollbar_background: 10191F4C +cursor: 26B2B2FF +cursor_inactive: 196666FF +paste_animation: 1C4449FF +splitter: 10191FFF +splitter_hover: 1C4449FF +ruler: FCEDFC26 +indent_guide: FCEDFC26 +letter_highlight: 599999FF +list_cursor_lite: 33CCCC19 +list_cursor: 33CCCC4C +shadow_dark: 0E161C7F +shadow_transparent: 0E161C00 +text_input_label: 3B4450FF +char_under_cursor: FFFFFFFF +bracket_highlight: E8FCFE30 +active_pane_border: 19666688 +inactive_pane_dim_overlay: 10191FFF + +ui_default: BFC9DBFF +ui_dim: 87919DFF +ui_neutral: 4C4C4CFF +ui_warning: F8AD34FF +ui_warning_dim: 986032FF +ui_error: 772222FF +ui_error_bright: FF0000FF +ui_success: 227722FF + +build_panel_background: 1A2831FF +build_panel_scrollbar: 33CCCC19 +build_panel_scrollbar_hover: 33CCCC4C +build_panel_scrollbar_background: 10191F4C +build_panel_title_bar: 1C303AFF + +code_default: BFC9DBFF +code_invalid: FF0000FF + +code_string_literal: D4BC7DFF +code_multiline_string: D4BC7DFF +code_raw_string: D4BC7DFF +code_char_literal: D4BC7DFF + +code_identifier: BFC9DBFF +code_note: E0AD82FF +code_number: D699B5FF + +code_error: FF0000FF +code_warning: E4D97DFF +code_highlight: E4D97DFF + +code_comment: 87919DFF +code_multiline_comment: 87919DFF + +code_operation: E0AD82FF +code_punctuation: BFC9DBFF + +code_keyword: E67D74FF +code_type: 82AAA3FF +code_value: D699B5FF +code_modifier: E67D74FF +code_attribute: E67D74FF +code_enum_variant: BFC9DBFF +code_macro: E0AD82FF +code_function: D0C5A9FF + +code_builtin_variable: D699B5FF +code_builtin_function: E0AD82FF +code_builtin_exception: E0AD82FF + +code_directive: E67D74FF +code_directive_modifier: E67D74FF + +code_header1: E67D74FF +code_header2: E0AD82FF +code_header3: E0AD82FF +code_header4: E0AD82FF +code_header5: E0AD82FF +code_header6: E0AD82FF + +# Added after migration to version [22] +code_comment_highlight1: E0AD82FF +code_comment_highlight2: FF0000FF +code_comment_highlight3: E67D74FF +code_comment_highlight4: D699B5FF +cursor_line_highlight: 21333FFF +color_preview_title_bar: 21333FFF +code_addition: 33B333FF +code_deletion: E64D4DFF +region_addition: 2260224C +region_deletion: 7722224C +