mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-04-14 15:19:12 +08:00
feat(layer): add telescope layer
This commit is contained in:
parent
48c57041f9
commit
7237a74889
315
autoload/SpaceVim/layers/telescope.vim
Normal file
315
autoload/SpaceVim/layers/telescope.vim
Normal file
@ -0,0 +1,315 @@
|
||||
"=============================================================================
|
||||
" telescope.vim --- telescope support for spacevim
|
||||
" Copyright (c) 2016-2019 Wang Shidong & Contributors
|
||||
" Author: Wang Shidong < wsdjeg@outlook.com >
|
||||
" URL: https://spacevim.org
|
||||
" License: GPLv3
|
||||
"=============================================================================
|
||||
|
||||
""
|
||||
" @section telescope, layers-telescope
|
||||
" @parentsection layers
|
||||
" This layer provides fuzzy finder feature which is based on |telescope|, and this
|
||||
" This layer is not loaded by default. To use this layer:
|
||||
" >
|
||||
" [[layers]]
|
||||
" name = 'telescope'
|
||||
" <
|
||||
" @subsection Key bindings
|
||||
"
|
||||
" The following key bindings will be enabled when this layer is loaded:
|
||||
" >
|
||||
" Key bindings Description
|
||||
" SPC p f / Ctrl-p search files in current directory
|
||||
" <Leader> f SPC Fuzzy find menu:CustomKeyMaps
|
||||
" <Leader> f e Fuzzy find register
|
||||
" <Leader> f h Fuzzy find history/yank
|
||||
" <Leader> f j Fuzzy find jump, change
|
||||
" <Leader> f l Fuzzy find location list
|
||||
" <Leader> f m Fuzzy find output messages
|
||||
" <Leader> f o Fuzzy find functions
|
||||
" <Leader> f t Fuzzy find tags
|
||||
" <Leader> f q Fuzzy find quick fix
|
||||
" <Leader> f r Resumes Unite window
|
||||
" <
|
||||
|
||||
function! SpaceVim#layers#telescope#plugins() abort
|
||||
let plugins = []
|
||||
call add(plugins, [g:_spacevim_root_dir . 'bundle/telescope.nvim', {'merged' : 0, 'loadconf' : 1}])
|
||||
call add(plugins, [g:_spacevim_root_dir . 'bundle/plenary.nvim', {'merged' : 0}])
|
||||
call add(plugins, [g:_spacevim_root_dir . 'bundle/telescope-menu', {'merged' : 0}])
|
||||
call add(plugins, [g:_spacevim_root_dir . 'bundle/telescope-ctags-outline.nvim', {'merged' : 0}])
|
||||
return plugins
|
||||
endfunction
|
||||
|
||||
let s:filename = expand('<sfile>:~')
|
||||
let s:lnum = expand('<slnum>') + 2
|
||||
function! SpaceVim#layers#telescope#config() abort
|
||||
|
||||
let lnum = expand('<slnum>') + s:lnum - 1
|
||||
call SpaceVim#mapping#space#def('nnoremap', ['?'], 'call call('
|
||||
\ . string(s:_function('s:get_menu')) . ', ["CustomKeyMaps", "[SPC]"])',
|
||||
\ ['show-mappings',
|
||||
\ [
|
||||
\ 'SPC ? is to show mappings',
|
||||
\ '',
|
||||
\ 'Definition: ' . s:filename . ':' . lnum,
|
||||
\ ]
|
||||
\ ],
|
||||
\ 1)
|
||||
|
||||
let lnum = expand('<slnum>') + s:lnum - 1
|
||||
call SpaceVim#mapping#space#def('nnoremap', ['h', '[SPC]'], 'call call('
|
||||
\ . string(s:_function('s:get_help')) . ', ["SpaceVim"])',
|
||||
\ ['find-SpaceVim-help',
|
||||
\ [
|
||||
\ 'SPC h SPC is to find SpaceVim help',
|
||||
\ '',
|
||||
\ 'Definition: ' . s:filename . ':' . lnum,
|
||||
\ ]
|
||||
\ ],
|
||||
\ 1)
|
||||
" @fixme SPC h SPC make vim flick
|
||||
exe printf('nmap %sh%s [SPC]h[SPC]', g:spacevim_default_custom_leader, g:spacevim_default_custom_leader)
|
||||
|
||||
let lnum = expand('<slnum>') + s:lnum - 1
|
||||
call SpaceVim#mapping#space#def('nnoremap', ['b', 'b'], 'Telescope buffers',
|
||||
\ ['list-buffer',
|
||||
\ [
|
||||
\ 'SPC b b is to open buffer list',
|
||||
\ '',
|
||||
\ 'Definition: ' . s:filename . ':' . lnum,
|
||||
\ ]
|
||||
\ ],
|
||||
\ 1)
|
||||
|
||||
let lnum = expand('<slnum>') + s:lnum - 1
|
||||
call SpaceVim#mapping#space#def('nnoremap', ['f', 'r'], 'Telescope oldfiles',
|
||||
\ ['open-recent-file',
|
||||
\ [
|
||||
\ 'SPC f r is to open recent file list',
|
||||
\ '',
|
||||
\ 'Definition: ' . s:filename . ':' . lnum,
|
||||
\ ]
|
||||
\ ],
|
||||
\ 1)
|
||||
|
||||
let lnum = expand('<slnum>') + s:lnum - 1
|
||||
call SpaceVim#mapping#space#def('nnoremap', ['j', 'i'], 'Telescope ctags_outline outline',
|
||||
\ ['jump-to-definition-in-buffer',
|
||||
\ [
|
||||
\ 'SPC j i is to jump to a definition in buffer',
|
||||
\ '',
|
||||
\ 'Definition: ' . s:filename . ':' . lnum,
|
||||
\ ]
|
||||
\ ],
|
||||
\ 1)
|
||||
|
||||
let lnum = expand('<slnum>') + s:lnum - 1
|
||||
call SpaceVim#mapping#space#def('nnoremap', ['T', 's'], 'Telescope colorscheme',
|
||||
\ ['fuzzy-find-colorschemes',
|
||||
\ [
|
||||
\ 'SPC T s is to fuzzy find colorschemes',
|
||||
\ '',
|
||||
\ 'Definition: ' . s:filename . ':' . lnum,
|
||||
\ ]
|
||||
\ ],
|
||||
\ 1)
|
||||
|
||||
let lnum = expand('<slnum>') + s:lnum - 1
|
||||
call SpaceVim#mapping#space#def('nnoremap', ['f', 'f'],
|
||||
\ "exe 'CtrlP ' . fnamemodify(bufname('%'), ':h')",
|
||||
\ ['find-files-in-buffer-directory',
|
||||
\ [
|
||||
\ '[SPC f f] is to find files in the directory of the current buffer',
|
||||
\ '',
|
||||
\ 'Definition: ' . s:filename . ':' . lnum,
|
||||
\ ]
|
||||
\ ]
|
||||
\ , 1)
|
||||
|
||||
let lnum = expand('<slnum>') + s:lnum - 1
|
||||
call SpaceVim#mapping#space#def('nnoremap', ['p', 'f'],
|
||||
\ 'Telescope find_files',
|
||||
\ ['find-files-in-project',
|
||||
\ [
|
||||
\ '[SPC p f] is to find files in the root of the current project',
|
||||
\ '',
|
||||
\ 'Definition: ' . s:filename . ':' . lnum,
|
||||
\ ]
|
||||
\ ]
|
||||
\ , 1)
|
||||
|
||||
nnoremap <silent> <C-p> :<C-u>Telescope find_files<cr>
|
||||
|
||||
let lnum = expand('<slnum>') + s:lnum - 1
|
||||
call SpaceVim#mapping#space#def('nnoremap', ['h', 'i'], 'call call('
|
||||
\ . string(s:_function('s:get_help_with_cursor_symbol')) . ', [])',
|
||||
\ ['get-help-for-cursor-symbol',
|
||||
\ [
|
||||
\ '[SPC h i] is to get help with the symbol at point',
|
||||
\ '',
|
||||
\ 'Definition: ' . s:filename . ':' . lnum,
|
||||
\ ]
|
||||
\ ],
|
||||
\ 1)
|
||||
|
||||
let g:_spacevim_mappings.f = {'name' : '+Fuzzy Finder'}
|
||||
call s:defind_fuzzy_finder()
|
||||
|
||||
augroup spacevim_telescope_layer
|
||||
autocmd!
|
||||
" https://github.com/nvim-telescope/telescope.nvim/issues/161
|
||||
autocmd FileType TelescopePrompt call deoplete#custom#buffer_option('auto_complete', v:false)
|
||||
augroup END
|
||||
endfunction
|
||||
|
||||
function! s:get_help_with_cursor_symbol() abort
|
||||
call v:lua.require('telescope.builtin').help_tags({ 'default_text' : expand('<cword>')})
|
||||
endfunction
|
||||
|
||||
function! s:get_help(word) abort
|
||||
call v:lua.require('telescope.builtin').help_tags({ 'default_text' : a:word})
|
||||
endfunction
|
||||
|
||||
function! s:get_menu(menu, input) abort
|
||||
let save_ctrlp_default_input = get(g:, 'ctrlp_default_input', '')
|
||||
let g:ctrlp_default_input = a:input
|
||||
exe 'CtrlPMenu ' . a:menu
|
||||
let g:ctrlp_default_input = save_ctrlp_default_input
|
||||
endfunction
|
||||
|
||||
let s:file = expand('<sfile>:~')
|
||||
let s:unite_lnum = expand('<slnum>') + 3
|
||||
function! s:defind_fuzzy_finder() abort
|
||||
|
||||
nnoremap <silent> <Leader>fe
|
||||
\ :<C-u>Telescope registers<CR>
|
||||
let lnum = expand('<slnum>') + s:unite_lnum - 4
|
||||
let g:_spacevim_mappings.f.e = ['Telescope registers',
|
||||
\ 'fuzzy find registers',
|
||||
\ [
|
||||
\ '[Leader f e ] is to fuzzy find registers',
|
||||
\ '',
|
||||
\ 'Definition: ' . s:file . ':' . lnum,
|
||||
\ ]
|
||||
\ ]
|
||||
nnoremap <silent> <Leader>fr
|
||||
\ :<C-u>Telescope resume<CR>
|
||||
let lnum = expand('<slnum>') + s:unite_lnum - 4
|
||||
let g:_spacevim_mappings.f.r = ['Telescope resume',
|
||||
\ 'resume telescope window',
|
||||
\ [
|
||||
\ '[Leader f r ] is to resume telescope window',
|
||||
\ '',
|
||||
\ 'Definition: ' . s:file . ':' . lnum,
|
||||
\ ]
|
||||
\ ]
|
||||
nnoremap <silent> <Leader>fh
|
||||
\ :<C-u>CtrlPNeoyank<CR>
|
||||
let lnum = expand('<slnum>') + s:unite_lnum - 4
|
||||
let g:_spacevim_mappings.f.h = ['CtrlPNeoyank',
|
||||
\ 'fuzzy find yank history',
|
||||
\ [
|
||||
\ '[Leader f h] is to fuzzy find history and yank content',
|
||||
\ '',
|
||||
\ 'Definition: ' . s:file . ':' . lnum,
|
||||
\ ]
|
||||
\ ]
|
||||
nnoremap <silent> <Leader>fj
|
||||
\ :<C-u>Telescope jumplist<CR>
|
||||
let lnum = expand('<slnum>') + s:unite_lnum - 4
|
||||
let g:_spacevim_mappings.f.j = ['Telescope jumplist',
|
||||
\ 'fuzzy find jump list',
|
||||
\ [
|
||||
\ '[Leader f j] is to fuzzy find jump list',
|
||||
\ '',
|
||||
\ 'Definition: ' . s:file . ':' . lnum,
|
||||
\ ]
|
||||
\ ]
|
||||
|
||||
nnoremap <silent> <Leader>fl
|
||||
\ :<C-u>Telescope loclist<CR>
|
||||
let lnum = expand('<slnum>') + s:unite_lnum - 4
|
||||
let g:_spacevim_mappings.f.l = ['Telescope loclist',
|
||||
\ 'fuzzy find local list',
|
||||
\ [
|
||||
\ '[Leader f q] is to fuzzy find local list',
|
||||
\ '',
|
||||
\ 'Definition: ' . s:file . ':' . lnum,
|
||||
\ ]
|
||||
\ ]
|
||||
|
||||
nnoremap <silent> <Leader>fm
|
||||
\ :<C-u>Telescope messages<CR>
|
||||
let lnum = expand('<slnum>') + s:unite_lnum - 4
|
||||
let g:_spacevim_mappings.f.m = ['Telescope messages',
|
||||
\ 'fuzzy find and yank message history',
|
||||
\ [
|
||||
\ '[Leader f m] is to fuzzy find and yank message history',
|
||||
\ '',
|
||||
\ 'Definition: ' . s:file . ':' . lnum,
|
||||
\ ]
|
||||
\ ]
|
||||
|
||||
nnoremap <silent> <Leader>fq
|
||||
\ :<C-u>Telescope quickfix<CR>
|
||||
let lnum = expand('<slnum>') + s:unite_lnum - 4
|
||||
let g:_spacevim_mappings.f.q = ['Telescope quickfix',
|
||||
\ 'fuzzy find quickfix list',
|
||||
\ [
|
||||
\ '[Leader f q] is to fuzzy find quickfix list',
|
||||
\ '',
|
||||
\ 'Definition: ' . s:file . ':' . lnum,
|
||||
\ ]
|
||||
\ ]
|
||||
|
||||
nnoremap <silent> <Leader>fo :<C-u>Telescope ctags_outline outline<CR>
|
||||
let lnum = expand('<slnum>') + s:unite_lnum - 4
|
||||
let g:_spacevim_mappings.f.o = ['Telescope ctags_outline outline',
|
||||
\ 'fuzzy find outline',
|
||||
\ [
|
||||
\ '[Leader f o] is to fuzzy find outline',
|
||||
\ '',
|
||||
\ 'Definition: ' . s:file . ':' . lnum,
|
||||
\ ]
|
||||
\ ]
|
||||
|
||||
nnoremap <silent> <Leader>f<Space> :CtrlPMenu CustomKeyMaps<CR>
|
||||
let lnum = expand('<slnum>') + s:unite_lnum - 4
|
||||
let g:_spacevim_mappings.f['[SPC]'] = ['CtrlPMenu CustomKeyMaps',
|
||||
\ 'fuzzy find custom key bindings',
|
||||
\ [
|
||||
\ '[Leader f SPC] is to fuzzy find custom key bindings',
|
||||
\ '',
|
||||
\ 'Definition: ' . s:file . ':' . lnum,
|
||||
\ ]
|
||||
\ ]
|
||||
|
||||
nnoremap <silent> <Leader>fp :<C-u>CtrlPMenu AddedPlugins<CR>
|
||||
let lnum = expand('<slnum>') + s:unite_lnum - 4
|
||||
let g:_spacevim_mappings.f.p = ['CtrlPMenu AddedPlugins',
|
||||
\ 'fuzzy find vim packages',
|
||||
\ [
|
||||
\ '[Leader f p] is to fuzzy find vim packages installed in SpaceVim',
|
||||
\ '',
|
||||
\ 'Definition: ' . s:file . ':' . lnum,
|
||||
\ ]
|
||||
\ ]
|
||||
|
||||
endfunction
|
||||
|
||||
" function() wrapper
|
||||
if v:version > 703 || v:version == 703 && has('patch1170')
|
||||
function! s:_function(fstr) abort
|
||||
return function(a:fstr)
|
||||
endfunction
|
||||
else
|
||||
function! s:_SID() abort
|
||||
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
|
||||
endfunction
|
||||
let s:_s = '<SNR>' . s:_SID() . '_'
|
||||
function! s:_function(fstr) abort
|
||||
return function(substitute(a:fstr, 's:', s:_s, 'g'))
|
||||
endfunction
|
||||
endif
|
@ -25,3 +25,5 @@ In `bundle/` directory, there are two kinds of plugins: forked plugins without c
|
||||
- [deoplete](https://github.com/Shougo/deoplete.nvim/tree/1c40f648d2b00e70beb4c473b7c0e32b633bd9ae)
|
||||
- [vim-scala@7657218](https://github.com/derekwyatt/vim-scala/tree/7657218f14837395a4e6759f15289bad6febd1b4)
|
||||
- [neosnippet.vim@5973e80](https://github.com/Shougo/neosnippet.vim/tree/5973e801e7ad38a01e888cb794d74e076a35ea9b)
|
||||
- [telescope.nvim@39b12d8](https://github.com/nvim-telescope/telescope.nvim/tree/39b12d84e86f5054e2ed98829b367598ae53ab41)
|
||||
- [telescope-ctags-outline.nvim](https://github.com/fcying/telescope-ctags-outline.nvim)
|
||||
|
1
bundle/plenary.nvim/.github/FUNDING.yml
vendored
Normal file
1
bundle/plenary.nvim/.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1 @@
|
||||
github: tjdevries
|
59
bundle/plenary.nvim/.github/workflows/default.yml
vendored
Normal file
59
bundle/plenary.nvim/.github/workflows/default.yml
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
name: default
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
x64-ubuntu:
|
||||
name: X64-ubuntu
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: date +%F > todays-date
|
||||
- name: Restore cache for today's nightly.
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: _neovim
|
||||
key: ${{ runner.os }}-x64-${{ hashFiles('todays-date') }}
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
curl -OL https://raw.githubusercontent.com/norcalli/bot-ci/master/scripts/github-actions-setup.sh
|
||||
source github-actions-setup.sh nightly-x64
|
||||
make test
|
||||
|
||||
appimage-ubuntu:
|
||||
name: Appimage-ubuntu
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: date +%F > todays-date
|
||||
- name: Restore cache for today's nightly.
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: build
|
||||
key: ${{ runner.os }}-appimage-${{ hashFiles('todays-date') }}
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
test -d build || {
|
||||
mkdir -p build
|
||||
wget https://github.com/neovim/neovim/releases/download/nightly/nvim.appimage
|
||||
chmod +x nvim.appimage
|
||||
mv nvim.appimage ./build/nvim
|
||||
}
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
export PATH="${PWD}/build/:${PATH}"
|
||||
make test
|
||||
|
||||
stylua:
|
||||
name: stylua
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: JohnnyMorganz/stylua-action@1.0.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# CLI arguments
|
||||
args: --color always --check .
|
43
bundle/plenary.nvim/.gitignore
vendored
Normal file
43
bundle/plenary.nvim/.gitignore
vendored
Normal file
@ -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/
|
6
bundle/plenary.nvim/.stylua.toml
Normal file
6
bundle/plenary.nvim/.stylua.toml
Normal file
@ -0,0 +1,6 @@
|
||||
column_width = 120
|
||||
line_endings = "Unix"
|
||||
indent_type = "Spaces"
|
||||
indent_width = 2
|
||||
quote_style = "AutoPreferDouble"
|
||||
call_parentheses = "None"
|
10
bundle/plenary.nvim/.styluaignore
Normal file
10
bundle/plenary.nvim/.styluaignore
Normal file
@ -0,0 +1,10 @@
|
||||
build/
|
||||
data/
|
||||
lua/luassert
|
||||
lua/plenary/profile.lua
|
||||
lua/plenary/profile/
|
||||
lua/plenary/bit.lua
|
||||
lua/say.lua
|
||||
scratch/
|
||||
scripts/
|
||||
|
7
bundle/plenary.nvim/CHANGELOG.md
Normal file
7
bundle/plenary.nvim/CHANGELOG.md
Normal file
@ -0,0 +1,7 @@
|
||||
## Pre-Alpha
|
||||
- Change the keys in tables returned by functions in the `plenary.window.float` module:
|
||||
- `buf` -> `bufnr`
|
||||
- `minor_buf` -> `minor_bufnr`
|
||||
- `border_buf` -> `border_bufnr`
|
||||
- `win` -> `win_id`
|
||||
- `minor_win` -> `minor_win_id`
|
21
bundle/plenary.nvim/LICENSE
Normal file
21
bundle/plenary.nvim/LICENSE
Normal file
@ -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.
|
5
bundle/plenary.nvim/Makefile
Normal file
5
bundle/plenary.nvim/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
test:
|
||||
nvim --headless --noplugin -u scripts/minimal.vim -c "PlenaryBustedDirectory tests/plenary/ {minimal_init = 'tests/minimal_init.vim'}"
|
||||
|
||||
generate_filetypes:
|
||||
nvim --headless -c 'luafile scripts/update_filetypes_from_github.lua' -c 'qa!'
|
89
bundle/plenary.nvim/POPUP.md
Normal file
89
bundle/plenary.nvim/POPUP.md
Normal file
@ -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
|
335
bundle/plenary.nvim/README.md
Normal file
335
bundle/plenary.nvim/README.md
Normal file
@ -0,0 +1,335 @@
|
||||
# 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
|
||||
|
||||
```vim
|
||||
Plug 'nvim-lua/plenary.nvim'
|
||||
```
|
||||
|
||||
## Modules
|
||||
|
||||
- `plenary.async`
|
||||
- `plenary.async_lib`
|
||||
- `plenary.job`
|
||||
- `plenary.path`
|
||||
- `plenary.scandir`
|
||||
- `plenary.context_manager`
|
||||
- `plenary.test_harness`
|
||||
- `plenary.filetype`
|
||||
- `plenary.strings`
|
||||
|
||||
### 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)
|
||||
- [gitsigns.nvim](https://github.com/lewis6991/gitsigns.nvim)
|
||||
- [vgit.nvim](https://github.com/tanvirtin/vgit.nvim)
|
||||
- [neogit](https://github.com/TimUntersberger/neogit)
|
||||
|
||||
### plenary.async_lib
|
||||
|
||||
Please use `plenary.async` instead. This was version 1 and is just here for compatibility reasons.
|
||||
|
||||
### 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
|
||||
|
||||
`plenery.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
|
||||
|
||||
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 `<Plug>PlenaryTestFile`. For example:
|
||||
|
||||
```
|
||||
nmap <leader>t <Plug>PlenaryTestFile
|
||||
```
|
||||
|
||||
To run a whole directory from the command line, you could do something like:
|
||||
|
||||
```
|
||||
nvim --headless -c "PlenaryBustedDirectory tests/plenary/ {minimal_init = 'tests/minimal_init.vim'}"
|
||||
```
|
||||
|
||||
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:
|
||||
- `minimal_init`: specify an init.vim to use for this instance, uses `--noplugin`
|
||||
- `minimal`: uses `--noplugin` without an init script (overrides `minimal_init`)
|
||||
- `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 debuggin 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. You can see the
|
||||
current limit with `ulimit -n` and set it with `ulimit -n 4096`. (macos might
|
||||
work different)
|
118
bundle/plenary.nvim/TESTS_README.md
Normal file
118
bundle/plenary.nvim/TESTS_README.md
Normal file
@ -0,0 +1,118 @@
|
||||
# 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).
|
||||
|
||||
# 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
|
||||
```
|
885
bundle/plenary.nvim/data/plenary/filetypes/base.lua
Normal file
885
bundle/plenary.nvim/data/plenary/filetypes/base.lua
Normal file
@ -0,0 +1,885 @@
|
||||
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]],
|
||||
['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]],
|
||||
},
|
||||
}
|
55
bundle/plenary.nvim/data/plenary/filetypes/builtin.lua
Normal file
55
bundle/plenary.nvim/data/plenary/filetypes/builtin.lua
Normal file
@ -0,0 +1,55 @@
|
||||
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',
|
||||
['coffee'] = 'coffee',
|
||||
['cljd'] = 'clojure',
|
||||
['dart'] = 'dart',
|
||||
['ex'] = 'elixir',
|
||||
['exs'] = 'elixir',
|
||||
['erb'] = 'eruby',
|
||||
['fnl'] = 'fennel',
|
||||
['gql'] = 'graphql',
|
||||
['graphql'] = 'graphql',
|
||||
['gradle'] = 'groovy',
|
||||
['hbs'] = 'handlebars',
|
||||
['hdbs'] = 'handlebars',
|
||||
['janet'] = 'janet',
|
||||
['jsx'] = 'javascriptreact',
|
||||
['jl'] = 'julia',
|
||||
['kt'] = 'kotlin',
|
||||
['nix'] = 'nix',
|
||||
['purs'] = 'purescript',
|
||||
['rkt'] = 'racket',
|
||||
['res'] = 'rescript',
|
||||
['resi'] = 'rescript',
|
||||
['tsx'] = 'typescriptreact',
|
||||
['plist'] = 'xml',
|
||||
},
|
||||
file_name = {
|
||||
['cakefile'] = 'coffee',
|
||||
['.babelrc'] = 'json',
|
||||
['.eslintrc'] = 'json',
|
||||
['.firebaserc'] = 'json',
|
||||
['.prettierrc'] = 'json',
|
||||
},
|
||||
shebang = shebang
|
||||
}
|
70
bundle/plenary.nvim/lua/luassert/array.lua
Normal file
70
bundle/plenary.nvim/lua/luassert/array.lua
Normal file
@ -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)
|
182
bundle/plenary.nvim/lua/luassert/assert.lua
Normal file
182
bundle/plenary.nvim/lua/luassert/assert.lua
Normal file
@ -0,0 +1,182 @@
|
||||
local s = require 'say'
|
||||
local astate = require 'luassert.state'
|
||||
local util = require 'luassert.util'
|
||||
local unpack = require 'luassert.compatibility'.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 = {...}
|
||||
arguments.n = select('#', ...) -- add argument count for trailing nils
|
||||
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 = {...}
|
||||
arguments.n = select('#', ...)
|
||||
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)
|
328
bundle/plenary.nvim/lua/luassert/assertions.lua
Normal file
328
bundle/plenary.nvim/lua/luassert/assertions.lua
Normal file
@ -0,0 +1,328 @@
|
||||
-- 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)
|
||||
set_failure_message(state, arguments[2])
|
||||
return arguments[1] ~= false and arguments[1] ~= nil
|
||||
end
|
||||
|
||||
local function falsy(state, arguments, 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) 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")
|
3
bundle/plenary.nvim/lua/luassert/compatibility.lua
Normal file
3
bundle/plenary.nvim/lua/luassert/compatibility.lua
Normal file
@ -0,0 +1,3 @@
|
||||
return {
|
||||
unpack = table.unpack or unpack,
|
||||
}
|
28
bundle/plenary.nvim/lua/luassert/formatters/binarystring.lua
Normal file
28
bundle/plenary.nvim/lua/luassert/formatters/binarystring.lua
Normal file
@ -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
|
||||
|
216
bundle/plenary.nvim/lua/luassert/formatters/init.lua
Normal file
216
bundle/plenary.nvim/lua/luassert/formatters/init.lua
Normal file
@ -0,0 +1,216 @@
|
||||
-- module will not return anything, only register formatters with the main assert engine
|
||||
local assert = require('luassert.assert')
|
||||
|
||||
local colors = setmetatable({
|
||||
none = function(c) return c end
|
||||
},{ __index = function(self, key)
|
||||
local ok, term = pcall(require, 'term')
|
||||
local isatty = io.type(io.stdout) == 'file' and ok and term.isatty(io.stdout)
|
||||
if not ok or not isatty or not term.colors then
|
||||
return function(c) return c end
|
||||
end
|
||||
return function(c)
|
||||
for token in key:gmatch("[^%.]+") do
|
||||
c = term.colors[token](c)
|
||||
end
|
||||
return c
|
||||
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") or "none"
|
||||
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
|
||||
|
||||
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)
|
||||
-- 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", "none")
|
17
bundle/plenary.nvim/lua/luassert/init.lua
Normal file
17
bundle/plenary.nvim/lua/luassert/init.lua
Normal file
@ -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
|
48
bundle/plenary.nvim/lua/luassert/languages/en.lua
Normal file
48
bundle/plenary.nvim/lua/luassert/languages/en.lua
Normal file
@ -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 not called with the arguments")
|
||||
s:set("assertion.called_with.negative", "Function was called with the arguments")
|
||||
|
||||
s:set("assertion.returned_with.positive", "Function was not returned with the arguments")
|
||||
s:set("assertion.returned_with.negative", "Function was returned with the arguments")
|
||||
|
||||
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)")
|
80
bundle/plenary.nvim/lua/luassert/match.lua
Normal file
80
bundle/plenary.nvim/lua/luassert/match.lua
Normal file
@ -0,0 +1,80 @@
|
||||
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 = {...}
|
||||
arguments.n = select('#', ...) -- add argument count for trailing nils
|
||||
local matches = matcher.callback(self, arguments, util.errorlevel())
|
||||
return setmetatable({
|
||||
name = matcher.name,
|
||||
mod = self.mod,
|
||||
callback = matches,
|
||||
}, matcher_mt)
|
||||
else
|
||||
local arguments = {...}
|
||||
arguments.n = select('#', ...) -- add argument count for trailing nils
|
||||
|
||||
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)
|
61
bundle/plenary.nvim/lua/luassert/matchers/composite.lua
Normal file
61
bundle/plenary.nvim/lua/luassert/matchers/composite.lua
Normal file
@ -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)
|
174
bundle/plenary.nvim/lua/luassert/matchers/core.lua
Normal file
174
bundle/plenary.nvim/lua/luassert/matchers/core.lua
Normal file
@ -0,0 +1,174 @@
|
||||
-- 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]
|
||||
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(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)
|
3
bundle/plenary.nvim/lua/luassert/matchers/init.lua
Normal file
3
bundle/plenary.nvim/lua/luassert/matchers/init.lua
Normal file
@ -0,0 +1,3 @@
|
||||
-- load basic machers
|
||||
require('luassert.matchers.core')
|
||||
require('luassert.matchers.composite')
|
61
bundle/plenary.nvim/lua/luassert/mock.lua
Normal file
61
bundle/plenary.nvim/lua/luassert/mock.lua
Normal file
@ -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
|
||||
})
|
19
bundle/plenary.nvim/lua/luassert/modifiers.lua
Normal file
19
bundle/plenary.nvim/lua/luassert/modifiers.lua
Normal file
@ -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)
|
2
bundle/plenary.nvim/lua/luassert/namespaces.lua
Normal file
2
bundle/plenary.nvim/lua/luassert/namespaces.lua
Normal file
@ -0,0 +1,2 @@
|
||||
-- stores the list of namespaces
|
||||
return {}
|
171
bundle/plenary.nvim/lua/luassert/spy.lua
Normal file
171
bundle/plenary.nvim/lua/luassert/spy.lua
Normal file
@ -0,0 +1,171 @@
|
||||
-- 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 = {...}
|
||||
arguments.n = select('#',...) -- add argument count for trailing nils
|
||||
table.insert(self.calls, util.copyargs(arguments))
|
||||
local function get_returns(...)
|
||||
local returnvals = {...}
|
||||
returnvals.n = select('#',...) -- add argument count for trailing nils
|
||||
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)
|
||||
return util.matchargs(self.calls, args) ~= nil
|
||||
end,
|
||||
|
||||
returned_with = function(self, args)
|
||||
return util.matchargs(self.returnvals, args) ~= nil
|
||||
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
|
||||
return state.payload:returned_with(arguments)
|
||||
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
|
||||
return state.payload:called_with(arguments)
|
||||
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
|
||||
})
|
128
bundle/plenary.nvim/lua/luassert/state.lua
Normal file
128
bundle/plenary.nvim/lua/luassert/state.lua
Normal file
@ -0,0 +1,128 @@
|
||||
-- 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 s = current
|
||||
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
|
111
bundle/plenary.nvim/lua/luassert/stub.lua
Normal file
111
bundle/plenary.nvim/lua/luassert/stub.lua
Normal file
@ -0,0 +1,111 @@
|
||||
-- module will return a stub module table
|
||||
local assert = require 'luassert.assert'
|
||||
local spy = require 'luassert.spy'
|
||||
local util = require 'luassert.util'
|
||||
local unpack = require 'luassert.compatibility'.unpack
|
||||
|
||||
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_count = select("#", ...)
|
||||
local return_values = {...}
|
||||
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_count == 1 and util.callable(return_values[1]) and return_values[1])
|
||||
local defaultfunc = fn or function()
|
||||
return unpack(return_values, 1, return_values_count)
|
||||
end
|
||||
local oncalls = {}
|
||||
local callbacks = {}
|
||||
local stubfunc = function(...)
|
||||
local args = {...}
|
||||
args.n = select('#', ...)
|
||||
local match = util.matchargs(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 = {...}
|
||||
local n = select('#', ...)
|
||||
defaultfunc = function()
|
||||
return unpack(return_args, 1, n)
|
||||
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 = {...}
|
||||
match_args.n = select('#', ...)
|
||||
match_args = util.copyargs(match_args)
|
||||
return {
|
||||
returns = function(...)
|
||||
local return_args = {...}
|
||||
local n = select('#', ...)
|
||||
table.insert(oncalls, match_args)
|
||||
callbacks[match_args] = function()
|
||||
return unpack(return_args, 1, n)
|
||||
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
|
||||
})
|
286
bundle/plenary.nvim/lua/luassert/util.lua
Normal file
286
bundle/plenary.nvim/lua/luassert/util.lua
Normal file
@ -0,0 +1,286 @@
|
||||
local util = {}
|
||||
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 = {}
|
||||
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, nil, 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 = {}
|
||||
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
|
||||
|
||||
-----------------------------------------------
|
||||
-- Finds matching arguments in a saved list of arguments
|
||||
-- @param argslist list of arguments from which to search
|
||||
-- @param args the arguments of which to find a match
|
||||
-- @return the matching arguments if a match is found, otherwise nil
|
||||
function util.matchargs(argslist, args)
|
||||
local function matches(t1, t2, t1refs)
|
||||
local match = require 'luassert.match'
|
||||
for k1,v1 in pairs(t1) do
|
||||
local v2 = t2[k1]
|
||||
if match.is_matcher(v1) then
|
||||
if not v1(v2) then return false end
|
||||
elseif match.is_matcher(v2) then
|
||||
if match.is_ref_matcher(v2) then v1 = t1refs[k1] end
|
||||
if not v2(v1) then return false end
|
||||
elseif (v2 == nil or not util.deepcompare(v1,v2)) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
for k2,v2 in pairs(t2) do
|
||||
-- only check wether each element has a t1 counterpart, actual comparison
|
||||
-- has been done in first loop above
|
||||
local v1 = t1[k2]
|
||||
if v1 == nil then
|
||||
-- no t1 counterpart, so try to compare using matcher
|
||||
if match.is_matcher(v2) then
|
||||
if not v2(v1) then return false end
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
for k,v in ipairs(argslist) do
|
||||
if matches(v.vals, args, v.refs) then
|
||||
return v
|
||||
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
|
||||
|
||||
return util
|
14
bundle/plenary.nvim/lua/plenary/async/api.lua
Normal file
14
bundle/plenary.nvim/lua/plenary/async/api.lua
Normal file
@ -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
|
||||
|
||||
vim.api[k](...)
|
||||
end
|
||||
end,
|
||||
})
|
117
bundle/plenary.nvim/lua/plenary/async/async.lua
Normal file
117
bundle/plenary.nvim/lua/plenary/async/async.lua
Normal file
@ -0,0 +1,117 @@
|
||||
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 = {}
|
||||
|
||||
---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(type(returned_function) == "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(type(async_function) == "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 type(func) ~= "function" 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
|
218
bundle/plenary.nvim/lua/plenary/async/control.lua
Normal file
218
bundle/plenary.nvim/lua/plenary/async/control.lua
Normal file
@ -0,0 +1,218 @@
|
||||
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()
|
||||
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
|
||||
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
|
||||
await(condvar:wait())
|
||||
end
|
||||
counter = counter - 1
|
||||
end
|
||||
|
||||
Receiver.last = 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 = 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:popright()
|
||||
deque:clear()
|
||||
return unpack(val or {})
|
||||
end
|
||||
|
||||
return Sender, Receiver
|
||||
end
|
||||
|
||||
return M
|
61
bundle/plenary.nvim/lua/plenary/async/init.lua
Normal file
61
bundle/plenary.nvim/lua/plenary/async/init.lua
Normal file
@ -0,0 +1,61 @@
|
||||
---@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
|
||||
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
|
||||
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
|
||||
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
|
||||
a.before_each = exports.tests.before_each
|
||||
a.after_each = exports.tests.after_each
|
||||
|
||||
setfenv(2, env)
|
||||
end
|
||||
|
||||
return exports
|
12
bundle/plenary.nvim/lua/plenary/async/lsp.lua
Normal file
12
bundle/plenary.nvim/lua/plenary/async/lsp.lua
Normal file
@ -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
|
116
bundle/plenary.nvim/lua/plenary/async/structs.lua
Normal file
116
bundle/plenary.nvim/lua/plenary/async/structs.lua
Normal file
@ -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
|
21
bundle/plenary.nvim/lua/plenary/async/tests.lua
Normal file
21
bundle/plenary.nvim/lua/plenary/async/tests.lua
Normal file
@ -0,0 +1,21 @@
|
||||
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))
|
||||
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
|
125
bundle/plenary.nvim/lua/plenary/async/util.lua
Normal file
125
bundle/plenary.nvim/lua/plenary/async/util.lua
Normal file
@ -0,0 +1,125 @@
|
||||
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, 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
|
||||
|
||||
M.will_block = function(async_func)
|
||||
return function()
|
||||
M.block_on(async_func)
|
||||
end
|
||||
end
|
||||
|
||||
M.join = function(async_fns)
|
||||
local len = #async_fns
|
||||
local results = {}
|
||||
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 future that when run will select the first async_function that finishes
|
||||
---@param async_funs table: The async_function that you want to select
|
||||
---@return ...
|
||||
M.run_first = a.wrap(function(async_funs, step)
|
||||
local ran = false
|
||||
|
||||
for _, future in ipairs(async_funs) do
|
||||
assert(type(future) == "function", "type error :: future must be function")
|
||||
|
||||
local callback = function(...)
|
||||
if not ran then
|
||||
ran = true
|
||||
step(...)
|
||||
end
|
||||
end
|
||||
|
||||
future(callback)
|
||||
end
|
||||
end, 2)
|
||||
|
||||
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
|
82
bundle/plenary.nvim/lua/plenary/async/uv_async.lua
Normal file
82
bundle/plenary.nvim/lua/plenary/async/uv_async.lua
Normal file
@ -0,0 +1,82 @@
|
||||
local a = require "plenary.async.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
|
15
bundle/plenary.nvim/lua/plenary/async_lib/api.lua
Normal file
15
bundle/plenary.nvim/lua/plenary/async_lib/api.lua
Normal file
@ -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,
|
||||
})
|
213
bundle/plenary.nvim/lua/plenary/async_lib/async.lua
Normal file
213
bundle/plenary.nvim/lua/plenary/async_lib/async.lua
Normal file
@ -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
|
41
bundle/plenary.nvim/lua/plenary/async_lib/init.lua
Normal file
41
bundle/plenary.nvim/lua/plenary/async_lib/init.lua
Normal file
@ -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
|
15
bundle/plenary.nvim/lua/plenary/async_lib/lsp.lua
Normal file
15
bundle/plenary.nvim/lua/plenary/async_lib/lsp.lua
Normal file
@ -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
|
116
bundle/plenary.nvim/lua/plenary/async_lib/structs.lua
Normal file
116
bundle/plenary.nvim/lua/plenary/async_lib/structs.lua
Normal file
@ -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
|
14
bundle/plenary.nvim/lua/plenary/async_lib/tests.lua
Normal file
14
bundle/plenary.nvim/lua/plenary/async_lib/tests.lua
Normal file
@ -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
|
339
bundle/plenary.nvim/lua/plenary/async_lib/util.lua
Normal file
339
bundle/plenary.nvim/lua/plenary/async_lib/util.lua
Normal file
@ -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
|
82
bundle/plenary.nvim/lua/plenary/async_lib/uv_async.lua
Normal file
82
bundle/plenary.nvim/lua/plenary/async_lib/uv_async.lua
Normal file
@ -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
|
126
bundle/plenary.nvim/lua/plenary/benchmark/init.lua
Normal file
126
bundle/plenary.nvim/lua/plenary/benchmark/init.lua
Normal file
@ -0,0 +1,126 @@
|
||||
local B = {}
|
||||
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<array<string, function>> @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
|
86
bundle/plenary.nvim/lua/plenary/benchmark/stat.lua
Normal file
86
bundle/plenary.nvim/lua/plenary/benchmark/stat.lua
Normal file
@ -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
|
341
bundle/plenary.nvim/lua/plenary/bit.lua
Normal file
341
bundle/plenary.nvim/lua/plenary/bit.lua
Normal file
@ -0,0 +1,341 @@
|
||||
-- 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
|
||||
local extract = M.extract
|
||||
|
||||
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
|
||||
local replace = M.replace
|
||||
|
||||
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
|
264
bundle/plenary.nvim/lua/plenary/busted.lua
Normal file
264
bundle/plenary.nvim/lua/plenary/busted.lua
Normal file
@ -0,0 +1,264 @@
|
||||
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_description]] = {}
|
||||
current_after_each[current_description[#current_description]] = {}
|
||||
end
|
||||
|
||||
local clear_last_each = function()
|
||||
current_before_each[current_description[#current_description]] = nil
|
||||
current_after_each[current_description[#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[#current_description]], fn)
|
||||
end
|
||||
|
||||
mod.after_each = function(fn)
|
||||
table.insert(current_after_each[current_description[#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 pairs(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, printed
|
||||
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
|
||||
assert = require "luassert"
|
||||
|
||||
mod.run = function(file)
|
||||
print("\n" .. HEADER)
|
||||
print("Testing: ", file)
|
||||
|
||||
local ok, msg = pcall(dofile, file)
|
||||
|
||||
if not ok 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
|
||||
|
||||
-- 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
|
||||
|
||||
return mod
|
80
bundle/plenary.nvim/lua/plenary/class.lua
Normal file
80
bundle/plenary.nvim/lua/plenary/class.lua
Normal file
@ -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
|
393
bundle/plenary.nvim/lua/plenary/collections/py_list.lua
Normal file
393
bundle/plenary.nvim/lua/plenary/collections/py_list.lua
Normal file
@ -0,0 +1,393 @@
|
||||
---@brief [[
|
||||
--- This module implements python-like lists. It can be used like so:
|
||||
--- <pre>
|
||||
--- local List = require 'plenary.collections.py_list'
|
||||
--- local l = List{3, 20, 44}
|
||||
--- print(l) -- [3, 20, 44]
|
||||
--- </pre>
|
||||
---@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
|
||||
--- <pre>
|
||||
--- local list = List{1, 2, 3, 4}
|
||||
--- local slice = list:slice(2, 3)
|
||||
--- print(slice) -- [2, 3]
|
||||
--- </pre>
|
||||
--- @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
|
||||
--- <pre>
|
||||
--- local list = List{1, 2, 3, 4}
|
||||
--- local reversed = list:copy():reverse()
|
||||
--- </pre>
|
||||
--- @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
|
||||
--- <pre>
|
||||
--- local list = List{1, 2, 3, 4}
|
||||
--- print(list:join('-')) -- 1-2-3-4
|
||||
--- </pre>
|
||||
--- @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
|
||||
--- <pre>
|
||||
--- other[to], other[to+1]... other[to+len] = self[from], self[from+1]... self[from+len]
|
||||
--- </pre>
|
||||
--- @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:
|
||||
---
|
||||
--- <pre>
|
||||
--- 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)
|
||||
--- </pre>
|
||||
---
|
||||
--- Would print
|
||||
---
|
||||
--- <pre>
|
||||
--- [0, 2, 4, 6, 8] [1, 3, 5, 7, 9]
|
||||
--- </pre>
|
||||
---@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:
|
||||
--- <pre>
|
||||
--- local list = List{8, 4, 7, 9}
|
||||
--- for i, v in list:iter() do
|
||||
--- print(i, v)
|
||||
--- end
|
||||
--- </pre>
|
||||
--- Would print:
|
||||
--- <pre>
|
||||
--- 1 8
|
||||
--- 2 4
|
||||
--- 3 7
|
||||
--- 4 9
|
||||
--- </pre>
|
||||
--- @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:
|
||||
--- <pre>
|
||||
--- local list = List{8, 4, 7, 9}
|
||||
--- for i, v in list:riter() do
|
||||
--- print(i, v)
|
||||
--- end
|
||||
--- </pre>
|
||||
--- Would print:
|
||||
--- <pre>
|
||||
--- 4 9
|
||||
--- 3 7
|
||||
--- 2 4
|
||||
--- 1 8
|
||||
--- </pre>
|
||||
--- @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,
|
||||
})
|
54
bundle/plenary.nvim/lua/plenary/context_manager.lua
Normal file
54
bundle/plenary.nvim/lua/plenary/context_manager.lua
Normal file
@ -0,0 +1,54 @@
|
||||
--- 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 result = 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")
|
||||
|
||||
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 result = callable(context)
|
||||
obj:exit()
|
||||
|
||||
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
|
313
bundle/plenary.nvim/lua/plenary/curl.lua
Normal file
313
bundle/plenary.nvim/lua/plenary/curl.lua
Normal file
@ -0,0 +1,313 @@
|
||||
--[[
|
||||
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)
|
||||
|
||||
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, request = {}, {}, nil
|
||||
|
||||
-- Helpers --------------------------------------------------
|
||||
-------------------------------------------------------------
|
||||
local F = require "plenary.functional"
|
||||
local J = require "plenary.job"
|
||||
local P = require "plenary.path"
|
||||
|
||||
-- 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 vim.tbl_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
|
||||
path = "/tmp/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 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.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(opts.raw)
|
||||
if opts.output then
|
||||
table.insert(result, { "-o", opts.output })
|
||||
end
|
||||
table.insert(result, parse.url(opts.url, opts.query))
|
||||
return vim.tbl_flatten(result), opts
|
||||
end
|
||||
|
||||
-- Parse response ------------------------------------------
|
||||
------------------------------------------------------------
|
||||
parse.response = function(lines, dump_path, code)
|
||||
local headers = P.readlines(dump_path)
|
||||
local status = tonumber(string.match(headers[1], "([%w+]%d+)"))
|
||||
local body = F.join(lines, "\n")
|
||||
|
||||
vim.loop.fs_unlink(dump_path)
|
||||
table.remove(headers, 1)
|
||||
|
||||
return {
|
||||
status = status,
|
||||
headers = headers,
|
||||
body = body,
|
||||
exit = code,
|
||||
}
|
||||
end
|
||||
|
||||
request = function(specs)
|
||||
local response = {}
|
||||
local args, opts = parse.request(vim.tbl_extend("force", {
|
||||
compressed = true,
|
||||
dry_run = false,
|
||||
dump = util.gen_dump_path(),
|
||||
}, specs))
|
||||
|
||||
if opts.dry_run then
|
||||
return args
|
||||
end
|
||||
|
||||
local job = J:new {
|
||||
command = "curl",
|
||||
args = args,
|
||||
on_exit = function(j, code)
|
||||
if code ~= 0 then
|
||||
error(
|
||||
string.format(
|
||||
"%s %s - curl error exit_code=%s stderr=%s",
|
||||
opts.method,
|
||||
opts.url,
|
||||
code,
|
||||
vim.inspect(j:stderr_result())
|
||||
)
|
||||
)
|
||||
end
|
||||
local output = parse.response(j:result(), opts.dump[2], code)
|
||||
if opts.callback then
|
||||
return opts.callback(output)
|
||||
else
|
||||
response = output
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
if opts.callback then
|
||||
return job:start()
|
||||
else
|
||||
job:sync(10000)
|
||||
return response
|
||||
end
|
||||
end
|
||||
|
||||
-- Main ----------------------------------------------------
|
||||
------------------------------------------------------------
|
||||
return (function()
|
||||
local spec = {}
|
||||
local partial = function(method)
|
||||
return function(url, opts)
|
||||
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)()
|
13
bundle/plenary.nvim/lua/plenary/debug_utils.lua
Normal file
13
bundle/plenary.nvim/lua/plenary/debug_utils.lua
Normal file
@ -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
|
162
bundle/plenary.nvim/lua/plenary/enum.lua
Normal file
162
bundle/plenary.nvim/lua/plenary/enum.lua
Normal file
@ -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:
|
||||
---
|
||||
--- <pre>
|
||||
--- 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
|
||||
--- }
|
||||
--- </pre>
|
||||
---
|
||||
--- 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:
|
||||
--- <pre>
|
||||
--- local enum = Enum.make_enum{
|
||||
--- 'Foo',
|
||||
--- 'Bar',
|
||||
--- {'Qux', 10}
|
||||
--- }
|
||||
--- </pre>
|
||||
--- @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,
|
||||
})
|
15
bundle/plenary.nvim/lua/plenary/errors.lua
Normal file
15
bundle/plenary.nvim/lua/plenary/errors.lua
Normal file
@ -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
|
190
bundle/plenary.nvim/lua/plenary/filetype.lua
Normal file
190
bundle/plenary.nvim/lua/plenary/filetype.lua
Normal file
@ -0,0 +1,190 @@
|
||||
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)
|
||||
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
|
||||
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
|
||||
|
||||
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
|
37
bundle/plenary.nvim/lua/plenary/fun.lua
Normal file
37
bundle/plenary.nvim/lua/plenary/fun.lua
Normal file
@ -0,0 +1,37 @@
|
||||
local tbl = require "plenary.tbl"
|
||||
|
||||
local M = {}
|
||||
|
||||
function M.bind(fn, ...)
|
||||
if select("#", ...) == 1 then
|
||||
local arg = ...
|
||||
return function(...)
|
||||
fn(arg, ...)
|
||||
end
|
||||
end
|
||||
|
||||
local args = tbl.pack(...)
|
||||
return function(...)
|
||||
fn(tbl.unpack(args), ...)
|
||||
end
|
||||
end
|
||||
|
||||
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
|
71
bundle/plenary.nvim/lua/plenary/functional.lua
Normal file
71
bundle/plenary.nvim/lua/plenary/functional.lua
Normal file
@ -0,0 +1,71 @@
|
||||
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
|
||||
|
||||
function f.partial(fun, ...)
|
||||
local args = { ... }
|
||||
return function(...)
|
||||
return fun(unpack(args), ...)
|
||||
end
|
||||
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
|
20
bundle/plenary.nvim/lua/plenary/init.lua
Normal file
20
bundle/plenary.nvim/lua/plenary/init.lua
Normal file
@ -0,0 +1,20 @@
|
||||
PLENARY_DEBUG = PLENARY_DEBUG == nil and true or PLENARY_DEBUG
|
||||
|
||||
if PLENARY_DEBUG then
|
||||
require("plenary.reload").reload_module "plenary"
|
||||
end
|
||||
|
||||
-- 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
|
670
bundle/plenary.nvim/lua/plenary/iterators.lua
Normal file
670
bundle/plenary.nvim/lua/plenary/iterators.lua
Normal file
@ -0,0 +1,670 @@
|
||||
---@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"
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- 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 "<iterator>"
|
||||
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 ipairs_gen = ipairs {}
|
||||
|
||||
local pairs_gen = pairs {}
|
||||
|
||||
local map_gen = function(map, key)
|
||||
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 vim.tbl_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
|
||||
---Se 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_gen = 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_gen, { 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
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- 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
|
||||
|
||||
---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 i, 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 i, 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
|
680
bundle/plenary.nvim/lua/plenary/job.lua
Normal file
680
bundle/plenary.nvim/lua/plenary/job.lua
Normal file
@ -0,0 +1,680 @@
|
||||
local vim = vim
|
||||
local uv = vim.loop
|
||||
|
||||
local F = require "plenary.functional"
|
||||
|
||||
---@class Job
|
||||
---@field command string : Command to run
|
||||
---@field args Array : List of arguments to pass
|
||||
---@field cwd string : Working directory for job
|
||||
---@field env Map|Array : Environment looking like: { ['VAR'] = 'VALUE } or { 'VAR=VALUE' }
|
||||
---@field skip_validation boolean : Skip validating the arguments
|
||||
---@field enable_handlers boolean : If set to false, disables all callbacks associated with output
|
||||
---@field on_start function : Run when starting job
|
||||
---@field on_stdout function : (error: string, data: string, self? Job)
|
||||
---@field on_stderr function : (error: string, data: string, self? Job)
|
||||
---@field on_exit function : (self, 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.
|
||||
---@field detached boolean : Spawn the child in a detached state making it a process group leader
|
||||
---@field enabled_recording boolean
|
||||
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(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 not uv.is_active(self._shutdown_check) then
|
||||
vim.wait(1000, function()
|
||||
return self:_pipes_are_closed(self) and self.is_shutdown
|
||||
end, 1, true)
|
||||
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 data == nil or 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 vim.tbl_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, "'enabled_recording' is not enabled for this job.")
|
||||
return self._stdout_results
|
||||
end
|
||||
|
||||
function Job:stderr_result()
|
||||
assert(self.enable_recording, "'enabled_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
|
102
bundle/plenary.nvim/lua/plenary/json.lua
Normal file
102
bundle/plenary.nvim/lua/plenary/json.lua
Normal file
@ -0,0 +1,102 @@
|
||||
-- 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
|
||||
function M.json_strip_comments(jsonString, options)
|
||||
options = options or {}
|
||||
local strip = options.whitespace == false and stripWithoutWhitespace or stripWithWhitespace
|
||||
|
||||
local insideString = false
|
||||
local insideComment = false
|
||||
local offset = 1
|
||||
local result = ""
|
||||
local skip = false
|
||||
|
||||
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
|
||||
i = i + 1
|
||||
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
|
||||
i = i + 1
|
||||
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
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return result .. (insideComment and strip(slice(jsonString, offset)) or slice(jsonString, offset))
|
||||
end
|
||||
|
||||
return M
|
201
bundle/plenary.nvim/lua/plenary/log.lua
Normal file
201
bundle/plenary.nvim/lua/plenary/log.lua
Normal file
@ -0,0 +1,201 @@
|
||||
-- log.lua
|
||||
--
|
||||
-- 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 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
|
||||
use_file = true,
|
||||
|
||||
-- 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,
|
||||
}
|
||||
|
||||
-- {{{ 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 = string.format("%s/%s.log", vim.api.nvim_call_function("stdpath", { "cache" }), config.plugin)
|
||||
|
||||
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)
|
||||
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 nameupper = level_config.name:upper()
|
||||
|
||||
local msg = message_maker(...)
|
||||
local info = debug.getinfo(config.info_level or 2, "Sl")
|
||||
local lineinfo = info.short_src .. ":" .. info.currentline
|
||||
|
||||
-- Output to console
|
||||
if config.use_console then
|
||||
local log_to_console = function()
|
||||
local console_string = string.format("[%-6s%s] %s: %s", nameupper, os.date "%H:%M:%S", lineinfo, 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 fp = assert(io.open(outfile, "a"))
|
||||
local str = string.format("[%-6s%s] %s: %s\n", nameupper, os.date(), lineinfo, msg)
|
||||
fp:write(str)
|
||||
fp:close()
|
||||
end
|
||||
|
||||
-- Output to quickfix
|
||||
if config.use_quickfix then
|
||||
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
|
30
bundle/plenary.nvim/lua/plenary/lsp/override.lua
Normal file
30
bundle/plenary.nvim/lua/plenary/lsp/override.lua
Normal file
@ -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
|
1
bundle/plenary.nvim/lua/plenary/neorocks/init.lua
Normal file
1
bundle/plenary.nvim/lua/plenary/neorocks/init.lua
Normal file
@ -0,0 +1 @@
|
||||
error "neorocks is no longer supported. Please use packer.nvim or other project for neorocks usage."
|
18
bundle/plenary.nvim/lua/plenary/nvim_meta.lua
Normal file
18
bundle/plenary.nvim/lua/plenary/nvim_meta.lua
Normal file
@ -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(),
|
||||
}
|
100
bundle/plenary.nvim/lua/plenary/operators.lua
Normal file
100
bundle/plenary.nvim/lua/plenary/operators.lua
Normal file
@ -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,
|
||||
}
|
899
bundle/plenary.nvim/lua/plenary/path.lua
Normal file
899
bundle/plenary.nvim/lua/plenary/path.lua
Normal file
@ -0,0 +1,899 @@
|
||||
--- 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 == "linux" or os == "osx" or os == "bsd" 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, "^%w+://") ~= nil
|
||||
end
|
||||
|
||||
local is_absolute = function(filename, sep)
|
||||
if sep == "\\" then
|
||||
return string.match(filename, "^[A-Z]:\\.*$")
|
||||
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 parts = _split_by_separator(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_absolute(filename, path.sep) or #_split_by_separator(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 = Path
|
||||
|
||||
-- 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 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
|
||||
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,
|
||||
|
||||
-- Cached values
|
||||
_absolute = uv.fs_realpath(path_string),
|
||||
_cwd = uv.fs_realpath ".",
|
||||
}
|
||||
|
||||
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()
|
||||
if jit and path.sep ~= "\\" then
|
||||
local ffi = require "ffi"
|
||||
ffi.cdef [[
|
||||
typedef unsigned char char_u;
|
||||
void shorten_dir(char_u *str);
|
||||
]]
|
||||
return 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
|
||||
end
|
||||
return function(filename)
|
||||
return shorten_len(filename, 1)
|
||||
end
|
||||
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 = not (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)
|
||||
return abs_path:match(formatted)
|
||||
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()
|
||||
local stat = uv.fs_stat(self:expand())
|
||||
if stat then
|
||||
return stat.type == "file" and true or nil
|
||||
end
|
||||
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:expand(), 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:expand(), "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:expand(), "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:expand(), "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:expand(), "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
|
||||
|
||||
return Path
|
484
bundle/plenary.nvim/lua/plenary/popup/init.lua
Normal file
484
bundle/plenary.nvim/lua/plenary/popup/init.lua
Normal file
@ -0,0 +1,484 @@
|
||||
--- 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")
|
||||
|
||||
-- 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 <buffer=%s> ++once ++nested :lua require('plenary.window').try_close(%s, true)",
|
||||
(silent and "<silent>") 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",
|
||||
"<CR>",
|
||||
'<cmd>lua require"popup".execute_callback(' .. bufnr .. ")<CR>",
|
||||
{ noremap = true }
|
||||
)
|
||||
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
|
33
bundle/plenary.nvim/lua/plenary/popup/utils.lua
Normal file
33
bundle/plenary.nvim/lua/plenary/popup/utils.lua
Normal file
@ -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
|
31
bundle/plenary.nvim/lua/plenary/profile.lua
Normal file
31
bundle/plenary.nvim/lua/plenary/profile.lua
Normal file
@ -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 {}
|
||||
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
|
252
bundle/plenary.nvim/lua/plenary/profile/lua_profiler.lua
Normal file
252
bundle/plenary.nvim/lua/plenary/profile/lua_profiler.lua
Normal file
@ -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 = "<C>"
|
||||
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
|
1072
bundle/plenary.nvim/lua/plenary/profile/memory_profiler.lua
Normal file
1072
bundle/plenary.nvim/lua/plenary/profile/memory_profiler.lua
Normal file
File diff suppressed because it is too large
Load Diff
312
bundle/plenary.nvim/lua/plenary/profile/p.lua
Normal file
312
bundle/plenary.nvim/lua/plenary/profile/p.lua
Normal file
@ -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.
|
||||
-- <number> stack dump depth (callee < caller). Default: 1.
|
||||
-- -<number> 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<number> Minimum sample percentage to be shown. Default: 3.
|
||||
-- i<number> Sampling interval in milliseconds. Default: 10.
|
||||
--
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
-- Cache some library functions and objects.
|
||||
local jit = require("jit")
|
||||
assert(jit.version_num == 20100, "LuaJIT core/library version mismatch")
|
||||
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
|
||||
}
|
||||
|
36
bundle/plenary.nvim/lua/plenary/reload.lua
Normal file
36
bundle/plenary.nvim/lua/plenary/reload.lua
Normal file
@ -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
|
28
bundle/plenary.nvim/lua/plenary/run.lua
Normal file
28
bundle/plenary.nvim/lua/plenary/run.lua
Normal file
@ -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
|
602
bundle/plenary.nvim/lua/plenary/scandir.lua
Normal file
602
bundle/plenary.nvim/lua/plenary/scandir.lua
Normal file
@ -0,0 +1,602 @@
|
||||
local Path = require "plenary.path"
|
||||
local os_sep = Path.path.sep
|
||||
local F = require "plenary.functional"
|
||||
|
||||
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 (uses each gitignore found in path table)
|
||||
-- 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 callback (should return 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 = vim.tbl_flatten { path }
|
||||
local next_dir = vim.tbl_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 = table.getn(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 table.getn(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 table.getn(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 callback (should return 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 = vim.tbl_flatten { path }
|
||||
local next_dir = vim.tbl_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 = table.getn(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 table.getn(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 table.getn(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()
|
||||
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);
|
||||
]]
|
||||
|
||||
return 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
|
||||
else
|
||||
return 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
|
||||
end
|
||||
end)()
|
||||
|
||||
local get_groupname = (function()
|
||||
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);
|
||||
]]
|
||||
|
||||
return 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
|
||||
else
|
||||
return 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
|
||||
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 table.getn(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
|
188
bundle/plenary.nvim/lua/plenary/strings.lua
Normal file
188
bundle/plenary.nvim/lua/plenary/strings.lua
Normal file
@ -0,0 +1,188 @@
|
||||
local path = require("plenary.path").path
|
||||
|
||||
local M = {}
|
||||
|
||||
M.strdisplaywidth = (function()
|
||||
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);
|
||||
]]
|
||||
|
||||
return 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
|
||||
else
|
||||
return 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
|
||||
end
|
||||
end)()
|
||||
|
||||
M.strcharpart = (function()
|
||||
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
|
||||
|
||||
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 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
|
||||
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
|
40
bundle/plenary.nvim/lua/plenary/tbl.lua
Normal file
40
bundle/plenary.nvim/lua/plenary/tbl.lua
Normal file
@ -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
|
202
bundle/plenary.nvim/lua/plenary/test_harness.lua
Normal file
202
bundle/plenary.nvim/lua/plenary/test_harness.lua
Normal file
@ -0,0 +1,202 @@
|
||||
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 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 = table.remove(split_string, 1)
|
||||
|
||||
local opts = assert(loadstring("return " .. table.concat(split_string, " ")))()
|
||||
|
||||
return harness.test_directory(directory, opts)
|
||||
end
|
||||
|
||||
function harness.test_directory(directory, opts)
|
||||
print "Starting..."
|
||||
opts = vim.tbl_deep_extend("force", {
|
||||
winopts = { winblend = 3 },
|
||||
sequential = false,
|
||||
keep_going = true,
|
||||
timeout = 50000,
|
||||
}, opts or {})
|
||||
|
||||
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<CR>", {})
|
||||
|
||||
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 paths = harness._find_files_to_run(directory)
|
||||
|
||||
local path_len = #paths
|
||||
|
||||
local failure = false
|
||||
|
||||
local jobs = vim.tbl_map(function(p)
|
||||
local args = {
|
||||
"--headless",
|
||||
"-c",
|
||||
string.format('lua require("plenary.busted").run("%s")', p:absolute()),
|
||||
}
|
||||
|
||||
if opts.minimal ~= nil then
|
||||
table.insert(args, "--noplugin")
|
||||
elseif opts.minimal_init ~= nil then
|
||||
table.insert(args, "--noplugin")
|
||||
|
||||
table.insert(args, "-u")
|
||||
table.insert(args, opts.minimal_init)
|
||||
end
|
||||
|
||||
local job = Job:new {
|
||||
command = vim.v.progpath,
|
||||
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)
|
||||
Job.join(j, opts.timeout)
|
||||
log.debug("... Completed job number", i)
|
||||
if j.code ~= 0 then
|
||||
failure = true
|
||||
if not opts.keep_going then
|
||||
break
|
||||
end
|
||||
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._find_files_to_run(directory)
|
||||
local finder = Job:new {
|
||||
command = "find",
|
||||
args = { directory, "-type", "f", "-name", "*_spec.lua" },
|
||||
}
|
||||
|
||||
return vim.tbl_map(Path.new, finder:sync())
|
||||
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
|
3
bundle/plenary.nvim/lua/plenary/vararg/init.lua
Normal file
3
bundle/plenary.nvim/lua/plenary/vararg/init.lua
Normal file
@ -0,0 +1,3 @@
|
||||
return {
|
||||
rotate = require "plenary.vararg.rotate",
|
||||
}
|
83
bundle/plenary.nvim/lua/plenary/vararg/rotate.lua
Normal file
83
bundle/plenary.nvim/lua/plenary/vararg/rotate.lua
Normal file
@ -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[0] = function()
|
||||
return A0
|
||||
end
|
||||
|
||||
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 args = tbl.pack(...)
|
||||
args[#args + 1] = first
|
||||
return tbl.unpack(args)
|
||||
end
|
||||
|
||||
local function rotate(nargs, ...)
|
||||
return (rotate_lookup[nargs] or rotate_n)(...)
|
||||
end
|
||||
|
||||
return rotate
|
297
bundle/plenary.nvim/lua/plenary/window/border.lua
Normal file
297
bundle/plenary.nvim/lua/plenary/window/border.lua
Normal file
@ -0,0 +1,297 @@
|
||||
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_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 <buffer=%s> ++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 <buffer=%s> ++nested ++once :lua require('plenary.window').try_close(%s, true)",
|
||||
content_bufnr,
|
||||
obj.win_id
|
||||
)
|
||||
)
|
||||
|
||||
setmetatable(obj, Border)
|
||||
|
||||
return obj
|
||||
end
|
||||
|
||||
return Border
|
212
bundle/plenary.nvim/lua/plenary/window/float.lua
Normal file
212
bundle/plenary.nvim/lua/plenary/window/float.lua
Normal file
@ -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 <buffer=%s> ++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 <buffer> 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 <buffer=%s> ++once ++nested silent! execute 'bdelete! %s'",
|
||||
-- primary_buf,
|
||||
-- minor_buf
|
||||
-- )
|
||||
-- )
|
||||
|
||||
-- vim.cmd(
|
||||
-- string.format(
|
||||
-- "autocmd WinLeave,BufDelete,BufLeave <buffer> ++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
|
16
bundle/plenary.nvim/lua/plenary/window/init.lua
Normal file
16
bundle/plenary.nvim/lua/plenary/window/init.lua
Normal file
@ -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
|
61
bundle/plenary.nvim/lua/say.lua
Normal file
61
bundle/plenary.nvim/lua/say.lua
Normal file
@ -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)
|
103
bundle/plenary.nvim/plenary.nvim-scm-1.rockspec
Normal file
103
bundle/plenary.nvim/plenary.nvim-scm-1.rockspec
Normal file
@ -0,0 +1,103 @@
|
||||
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 = 'http://github.com/nvim-lua/plenary.nvim/archive/v' .. _MODREV .. '.zip',
|
||||
dir = 'plenary.nvim-' .. _MODREV,
|
||||
}
|
||||
|
||||
if _MODREV == 'scm' then
|
||||
source = {
|
||||
url = 'git://github.com/nvim-lua/plenary.nvim',
|
||||
}
|
||||
end
|
||||
|
||||
build = {
|
||||
type = 'builtin',
|
||||
modules = {
|
||||
-- paths are relative to source.dir
|
||||
["plenary.busted"] = "lua/plenary/busted.lua",
|
||||
["plenary.class"] = "lua/plenary/class.lua",
|
||||
["plenary.context_manager"] = "lua/plenary/context_manager.lua",
|
||||
["plenary.debug_utils"] = "lua/plenary/debug_utils.lua",
|
||||
["plenary.enum"] = "lua/plenary/enum.lua",
|
||||
["plenary.errors"] = "lua/plenary/errors.lua",
|
||||
["plenary.filetype"] = "lua/plenary/filetype.lua",
|
||||
["plenary.fun"] = "lua/plenary/fun.lua",
|
||||
["plenary.functional"] = "lua/plenary/functional.lua",
|
||||
["plenary.init"] = "lua/plenary/init.lua",
|
||||
["plenary.iterators"] = "lua/plenary/iterators.lua",
|
||||
["plenary.job"] = "lua/plenary/job.lua",
|
||||
["plenary.log"] = "lua/plenary/log.lua",
|
||||
["plenary.nvim_meta"] = "lua/plenary/nvim_meta.lua",
|
||||
["plenary.operators"] = "lua/plenary/operators.lua",
|
||||
["plenary.path"] = "lua/plenary/path.lua",
|
||||
["plenary.profile"] = "lua/plenary/profile.lua",
|
||||
["plenary.reload"] = "lua/plenary/reload.lua",
|
||||
["plenary.run"] = "lua/plenary/run.lua",
|
||||
["plenary.scandir"] = "lua/plenary/scandir.lua",
|
||||
["plenary.strings"] = "lua/plenary/strings.lua",
|
||||
["plenary.tbl"] = "lua/plenary/tbl.lua",
|
||||
["plenary.test_harness"] = "lua/plenary/test_harness.lua",
|
||||
|
||||
["plenary.async.api"] = "lua/plenary/async/api.lua",
|
||||
["plenary.async.async"] = "lua/plenary/async/async.lua",
|
||||
["plenary.async.control"] = "lua/plenary/async/control.lua",
|
||||
["plenary.async.init"] = "lua/plenary/async/init.lua",
|
||||
["plenary.async.lsp"] = "lua/plenary/async/lsp.lua",
|
||||
["plenary.async.structs"] = "lua/plenary/async/structs.lua",
|
||||
["plenary.async.tests"] = "lua/plenary/async/tests.lua",
|
||||
["plenary.async.util"] = "lua/plenary/async/util.lua",
|
||||
["plenary.async.uv_async"] = "lua/plenary/async/uv_async.lua",
|
||||
|
||||
["plenary.async_lib.api"] = "lua/plenary/async_lib/api.lua",
|
||||
["plenary.async_lib.async"] = "lua/plenary/async_lib/async.lua",
|
||||
["plenary.async_lib.init"] = "lua/plenary/async_lib/init.lua",
|
||||
["plenary.async_lib.lsp"] = "lua/plenary/async_lib/lsp.lua",
|
||||
["plenary.async_lib.structs"] = "lua/plenary/async_lib/structs.lua",
|
||||
["plenary.async_lib.tests"] = "lua/plenary/async_lib/tests.lua",
|
||||
["plenary.async_lib.util"] = "lua/plenary/async_lib/util.lua",
|
||||
["plenary.async_lib.uv_async"] = "lua/plenary/async_lib/uv_async.lua",
|
||||
|
||||
["plenary.collections.py_list"] = "lua/plenary/collections/py_list.lua",
|
||||
|
||||
["plenary.lsp.override"] = "lua/plenary/lsp/override.lua",
|
||||
|
||||
["plenary.neorocks.init"] = "lua/plenary/neorocks/init.lua",
|
||||
|
||||
["plenary.popup.init"] = "lua/plenary/popup/init.lua",
|
||||
["plenary.popup.utils"] = "lua/plenary/popup/utils.lua",
|
||||
|
||||
["plenary.profile.lua_profiler"] = "lua/plenary/profile/lua_profiler.lua",
|
||||
["plenary.profile.memory_profiler"] = "lua/plenary/profile/memory_profiler.lua",
|
||||
["plenary.profile.p"] = "lua/plenary/profile/p.lua",
|
||||
|
||||
["plenary.vararg.init"] = "lua/plenary/vararg/init.lua",
|
||||
["plenary.vararg.rotate"] = "lua/plenary/vararg/rotate.lua",
|
||||
|
||||
["plenary.window.border"] = "lua/plenary/window/border.lua",
|
||||
["plenary.window.float"] = "lua/plenary/window/float.lua",
|
||||
["plenary.window.init"] = "lua/plenary/window/init.lua",
|
||||
},
|
||||
copy_directories = {
|
||||
'plugin'
|
||||
}
|
||||
}
|
||||
|
9
bundle/plenary.nvim/plugin/plenary.vim
Normal file
9
bundle/plenary.nvim/plugin/plenary.vim
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
" Create command for running busted
|
||||
command! -nargs=1 -complete=file PlenaryBustedFile
|
||||
\ lua require('plenary.busted').run(vim.fn.expand("<args>"))
|
||||
|
||||
command! -nargs=+ -complete=file PlenaryBustedDirectory
|
||||
\ lua require('plenary.test_harness').test_directory_command(vim.fn.expand("<args>"))
|
||||
|
||||
nnoremap <Plug>PlenaryTestFile :lua require('plenary.test_harness').test_directory(vim.fn.expand("%:p"))<CR>
|
231
bundle/plenary.nvim/scratch/_reference_count.lua
Normal file
231
bundle/plenary.nvim/scratch/_reference_count.lua
Normal file
@ -0,0 +1,231 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- This module implements a function that traverses all live objects.
|
||||
-- You can implement your own function to pass as a parameter of traverse
|
||||
-- and give you the information you want. As an example we have implemented
|
||||
-- countreferences and findallpaths
|
||||
--
|
||||
-- Alexandra Barros - 2006.03.15
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
module("gc", package.seeall)
|
||||
|
||||
local List = {}
|
||||
|
||||
function List.new ()
|
||||
return {first = 0, last = -1}
|
||||
end
|
||||
|
||||
function List.push (list, value)
|
||||
local last = list.last + 1
|
||||
list.last = last
|
||||
list[last] = value
|
||||
end
|
||||
|
||||
function List.pop (list)
|
||||
local first = list.first
|
||||
if first > list.last then error("list is empty") end
|
||||
local value = list[first]
|
||||
list[first] = nil
|
||||
list.first = first + 1
|
||||
return value
|
||||
end
|
||||
|
||||
function List.isempty (list)
|
||||
return list.first > list.last
|
||||
end
|
||||
|
||||
-- Counts all references for a given object
|
||||
function countreferences(value)
|
||||
local count = -1
|
||||
local f = function(from, to, how, v)
|
||||
if to == value then
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
traverse({edge=f}, {count, f})
|
||||
return count
|
||||
end
|
||||
|
||||
-- Prints all paths to an object
|
||||
function findallpaths(obj)
|
||||
|
||||
local comefrom = {}
|
||||
local f = function(from, to, how, value)
|
||||
if not comefrom[to] then comefrom[to] = {} end
|
||||
table.insert(comefrom[to], 1, {f = from, h = how, v=value})
|
||||
end
|
||||
|
||||
traverse({edge=f}, {comefrom, f})
|
||||
|
||||
|
||||
local function printpath(to)
|
||||
if not to or comefrom[to].visited or to == _G then
|
||||
print("-----")
|
||||
return
|
||||
end
|
||||
comefrom[to].visited = true
|
||||
for i=1, #comefrom[to] do
|
||||
local tfrom = comefrom[to][i].f
|
||||
print("from: ", vim.inspect(tfrom, { newline = '|' }), "\nhow:", comefrom[to][i].h,
|
||||
"\nvalue:", comefrom[to][i].v)
|
||||
printpath(tfrom)
|
||||
end
|
||||
end
|
||||
|
||||
printpath(obj)
|
||||
|
||||
end
|
||||
|
||||
-- Main function
|
||||
-- 'funcs' is a table that contains a funcation for every lua type and also the
|
||||
-- function edge edge (traverseedge).
|
||||
function traverse(funcs, ignoreobjs)
|
||||
|
||||
-- The keys of the marked table are the objetcts (for example, table: 00442330).
|
||||
-- The value of each key is true if the object has been found and false
|
||||
-- otherwise.
|
||||
local env = {marked = {}, list=List.new(), funcs=funcs}
|
||||
|
||||
if ignoreobjs then
|
||||
for i=1, #ignoreobjs do
|
||||
env.marked[ignoreobjs[i]] = true
|
||||
end
|
||||
end
|
||||
|
||||
env.marked["gc"] = true
|
||||
env.marked[gc] = true
|
||||
|
||||
-- marks and inserts on the list
|
||||
edge(env, nil, "_G", "isname", nil)
|
||||
edge(env, nil, _G, "key", "_G")
|
||||
|
||||
-- traverses the active thread
|
||||
-- inserts the local variables
|
||||
-- interates over the function on the stack, starting from the one that
|
||||
-- called traverse
|
||||
for i=2, math.huge do
|
||||
local info = debug.getinfo(i, "f")
|
||||
if not info then break end
|
||||
for j=1, math.huge do
|
||||
local n, v = debug.getlocal(i, j)
|
||||
if not n then break end
|
||||
|
||||
edge(env, nil, n, "isname", nil)
|
||||
edge(env, nil, v, "local", n)
|
||||
end
|
||||
end
|
||||
|
||||
while not List.isempty(env.list) do
|
||||
|
||||
local obj = List.pop(env.list)
|
||||
local t = type(obj)
|
||||
if not gc["traverse" .. t] then
|
||||
error("Could not find traverse " .. t)
|
||||
end
|
||||
|
||||
gc["traverse" .. t](env, obj)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function traversetable(env, obj)
|
||||
|
||||
local f = env.funcs.table
|
||||
if f then f(obj) end
|
||||
|
||||
for key, value in pairs(obj) do
|
||||
edge(env, obj, key, "iskey", nil)
|
||||
edge(env, obj, value, "key", key)
|
||||
end
|
||||
|
||||
local mtable = debug.getmetatable(obj)
|
||||
if mtable then edge(env, obj, mtable, "ismetatable", nil) end
|
||||
|
||||
end
|
||||
|
||||
function traversestring(env, obj)
|
||||
local f = env.funcs.string
|
||||
if f then f(obj) end
|
||||
|
||||
end
|
||||
|
||||
function traverseuserdata(env, obj)
|
||||
local f = env.funcs.userdata
|
||||
if f then f(obj) end
|
||||
|
||||
local mtable = debug.getmetatable(obj)
|
||||
if mtable then edge(env, obj, mtable, "ismetatable", nil) end
|
||||
|
||||
local fenv = debug.getfenv(obj)
|
||||
if fenv then edge(env, obj, fenv, "environment", nil) end
|
||||
|
||||
end
|
||||
|
||||
function traversefunction(env, obj)
|
||||
local f = env.funcs.func
|
||||
if f then f(obj) end
|
||||
|
||||
-- gets the upvalues
|
||||
local i = 1
|
||||
while true do
|
||||
local n, v = debug.getupvalue(obj, i)
|
||||
if not n then break end -- when there is no upvalues
|
||||
edge(env, obj, n, "isname", nil)
|
||||
edge(env, obj, v, "upvalue", n)
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
local fenv = debug.getfenv(obj)
|
||||
edge(env, obj, fenv, "enviroment", nil)
|
||||
|
||||
end
|
||||
|
||||
function traversecdata(env, t)
|
||||
-- print(env, t)
|
||||
end
|
||||
|
||||
function traversethread(env, t)
|
||||
local f = env.funcs.thread
|
||||
if f then f(t) end
|
||||
|
||||
for i=1, math.huge do
|
||||
local info = debug.getinfo(t, i, "f")
|
||||
if not info then break end
|
||||
for j=1, math.huge do
|
||||
local n, v = debug.getlocal(t, i , j)
|
||||
if not n then break end
|
||||
print(n, v)
|
||||
|
||||
edge(env, nil, n, "isname", nil)
|
||||
edge(env, nil, v, "local", n)
|
||||
end
|
||||
end
|
||||
|
||||
local fenv = debug.getfenv(t)
|
||||
edge(env, t, fenv, "enviroment", nil)
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- 'how' is a string that identifies the content of 'to' and 'value':
|
||||
-- if 'how' is "iskey", then 'to' é is a key and 'value' is nil.
|
||||
-- if 'how' is "key", then 'to' is an object and 'value' is the name of the
|
||||
-- key.
|
||||
function edge(env, from, to, how, value)
|
||||
|
||||
local t = type(to)
|
||||
|
||||
if to and (t~="boolean") and (t~="number") and (t~="new") then
|
||||
-- If the destination object has not been found yet
|
||||
if not env.marked[to] then
|
||||
env.marked[to] = true
|
||||
List.push(env.list, to) -- puts on the list to be traversed
|
||||
end
|
||||
|
||||
local f = env.funcs.edge
|
||||
if f then f(from, to, how, value) end
|
||||
|
||||
end
|
||||
|
||||
end
|
24
bundle/plenary.nvim/scratch/busted_tester.lua
Normal file
24
bundle/plenary.nvim/scratch/busted_tester.lua
Normal file
@ -0,0 +1,24 @@
|
||||
local level = 2
|
||||
local info = debug.getinfo(1, 'Sf')
|
||||
local source = info.source
|
||||
|
||||
local b = require('busted.core')()
|
||||
|
||||
b.register('file', 'file', {})
|
||||
|
||||
local testFileLoader = require 'busted.modules.standalone_loader'(b)
|
||||
testFileLoader(info, { verbose = nil })
|
||||
|
||||
local execute = require('busted.execute')(b)
|
||||
|
||||
print(vim.inspect(execute))
|
||||
print(vim.inspect(b))
|
||||
print("===")
|
||||
print(execute(1, {}))
|
||||
print("===")
|
||||
-- execute(1, {})
|
||||
|
||||
-- print(vim.inspect(b.execute))
|
||||
-- print(vim.inspect(b.execute('file', './tests/plenary/bu/simple_busted_spec.lua')))
|
||||
-- require('busted.init')(b)
|
||||
-- print(vim.inspect(b))
|
14
bundle/plenary.nvim/scratch/coroutine_examples.lua
Normal file
14
bundle/plenary.nvim/scratch/coroutine_examples.lua
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
local f = function(xyz)
|
||||
print('calling 1', xyz)
|
||||
local foo = coroutine.yield()
|
||||
print('calling 2', foo)
|
||||
return 5
|
||||
end
|
||||
|
||||
local x = coroutine.wrap(f)
|
||||
|
||||
x('bar')
|
||||
x('zap')
|
||||
-- x()
|
||||
-- x()
|
4
bundle/plenary.nvim/scratch/iterators.lua
Normal file
4
bundle/plenary.nvim/scratch/iterators.lua
Normal file
@ -0,0 +1,4 @@
|
||||
local i = require('plenary.iterators')
|
||||
|
||||
dump(i.split(" hello person dude ", " "):tolist())
|
||||
-- dump(("hello person"):find("person"))
|
55
bundle/plenary.nvim/scratch/job_graveyard.lua
Normal file
55
bundle/plenary.nvim/scratch/job_graveyard.lua
Normal file
@ -0,0 +1,55 @@
|
||||
|
||||
function Job.accumulate_results(results)
|
||||
return function(err, data)
|
||||
if data == nil then
|
||||
if results[#results] == '' then
|
||||
table.remove(results, #results)
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if results[1] == nil then
|
||||
results[1] = ''
|
||||
end
|
||||
|
||||
-- Get rid of pesky \r
|
||||
data = data:gsub("\r", "")
|
||||
|
||||
local line, start, found_newline
|
||||
while true do
|
||||
start = string.find(data, "\n") or #data
|
||||
found_newline = string.find(data, "\n")
|
||||
|
||||
line = string.sub(data, 1, start)
|
||||
data = string.sub(data, start + 1, -1)
|
||||
|
||||
line = line:gsub("\r", "")
|
||||
line = line:gsub("\n", "")
|
||||
|
||||
results[#results] = (results[#results] or '') .. line
|
||||
|
||||
if found_newline then
|
||||
table.insert(results, '')
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- if found_newline and results[#results] == '' then
|
||||
-- table.remove(results, #results)
|
||||
-- end
|
||||
|
||||
-- if string.find(data, "\n") then
|
||||
-- for _, line in ipairs(vim.fn.split(data, "\n")) do
|
||||
-- line = line:gsub("\n", "")
|
||||
-- line = line:gsub("\r", "")
|
||||
|
||||
-- table.insert(results, line)
|
||||
-- end
|
||||
-- else
|
||||
-- results[#results] = results[#results] .. data
|
||||
-- end
|
||||
end
|
||||
end
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user