Init
This commit is contained in:
commit
86508c397c
|
@ -0,0 +1,8 @@
|
|||
Hello! I'm Zane ~
|
||||
|
||||
|
||||
Resume
|
||||
|
||||
Campaign Specialist at Cox Automotive, 2021-2022
|
||||
Front End Developer at Zoe Bios Creative, 2020-2021
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
mail@zane.town
|
||||
|
||||
zane @ irc.tilde.chat
|
||||
|
||||
zane#9090 @ discord
|
Binary file not shown.
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,15 @@
|
|||
[user]
|
||||
name = Zane Schaffer
|
||||
email = z@zane.town
|
||||
|
||||
[core]
|
||||
editor = vim
|
||||
excludesFile = '~/.gitignore'
|
||||
|
||||
[fetch]
|
||||
prune = true
|
||||
[init]
|
||||
defaultBranch = main
|
||||
[status]
|
||||
short = true
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
set -g default-terminal "xterm-256color"
|
||||
set-option -ga terminal-overrides ",xterm-256color:Tc"
|
||||
unbind C-b
|
||||
set -g prefix C-a
|
||||
bind C-a send-prefix
|
||||
bind q kill-pane
|
||||
unbind-key '"'
|
||||
unbind-key %
|
||||
unbind-key v
|
||||
bind v split-window -v
|
||||
|
||||
set-window-option -g mode-keys vi
|
||||
set -g renumber-windows on
|
||||
|
||||
set -g status-left ''
|
||||
set -g status-right '#{now_playing} %I:%M%p '
|
||||
set -g status-style 'fg=colour15 bg=colour0'
|
||||
|
||||
set -g window-status-current-style 'fg=colour7 bold'
|
||||
set -g window-status-activity-style 'fg=colour2'
|
||||
set -g window-status-style 'fg=colour15 bg=colour0'
|
||||
|
||||
set -g pane-border-style 'fg=colour15'
|
||||
set -g pane-active-border-style 'fg=colour15'
|
||||
|
||||
set -g message-style 'fg=colour15 bg=colour0'
|
||||
set -g clock-mode-colour 'colour2'
|
||||
|
||||
set -g @plugin 'tmux-plugins/tpm'
|
||||
set -g @plugin 'tmux-plugins/tmux-sensible'
|
||||
set -g @plugin 'christoomey/vim-tmux-navigator'
|
||||
set -g @plugin 'spywhere/tmux-now-playing'
|
||||
|
||||
run '~/.tmux/plugins/tpm/tpm'
|
|
@ -0,0 +1,49 @@
|
|||
# Exports
|
||||
export EDITOR=vi
|
||||
export VISUAL=vi
|
||||
export PF_INFO="ascii title os host pkgs memory palette"
|
||||
export PF_COL3=4
|
||||
export LS_COLORS="$LS_COLORS:ow=1;7;34:st=30;44:su=30;41"
|
||||
export MANPAGER='nvim +Man!'
|
||||
export PATH="$PATH:$HOME/nand2tetris/tools"
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
export PATH="/opt/local/libexec/gnubin:$PATH"
|
||||
export KEYTIMEOUT=1
|
||||
export VI_MODE_SET_CURSOR=true
|
||||
|
||||
# Aliases
|
||||
alias ls="ls -F --color=always --group-directories-first -h"
|
||||
alias la="ls -F --color=always -Ahs"
|
||||
alias l="ls -A"
|
||||
alias ll="la -l"
|
||||
alias vim=$EDITOR
|
||||
alias t='python $HOME/repos/t/t.py --task-dir $HOME/tasks --list tasks'
|
||||
alias wr='curl -fGsS --compressed "wttr.in/98122?1FQnT"'
|
||||
alias w='curl -fGsS --compressed "wttr.in/98122?format=%C+%f+%p\n"'
|
||||
setopt interactive_comments
|
||||
bindkey -v
|
||||
|
||||
# Prompt
|
||||
PS1='%(?.;.%F{red}%B;%b%f) '
|
||||
|
||||
# Completion
|
||||
fpath=(/opt/local/share/zsh/site-functions $fpath)
|
||||
autoload -Uz compinit
|
||||
compinit
|
||||
|
||||
eval "$(fnm env)"
|
||||
|
||||
#[ -f "/Users/zane/.ghcup/env" ] && source "/Users/zane/.ghcup/env" # ghcup-env
|
||||
source ~/.config/up/up.sh
|
||||
|
||||
[ -f "/Users/zane/.ghcup/env" ] && source "/Users/zane/.ghcup/env" # ghcup-env
|
||||
|
||||
if [ -z "$TMUX" ]
|
||||
then
|
||||
if tmux has-session 2>/dev/null; then
|
||||
tmux attach-session
|
||||
else
|
||||
tmux new-session -s main sandman catgirl tilde
|
||||
fi
|
||||
else
|
||||
fi
|
|
@ -0,0 +1,60 @@
|
|||
require('impatient')
|
||||
-- Don't load stock plugins
|
||||
vim.g.loaded_gzip = 1
|
||||
vim.g.loaded_tar = 1
|
||||
vim.g.loaded_tarPlugin = 1
|
||||
vim.g.loaded_zip = 1
|
||||
vim.g.loaded_zipPlugin = 1
|
||||
vim.g.loaded_getscript = 1
|
||||
vim.g.loaded_getscriptPlugin = 1
|
||||
vim.g.loaded_vimball = 1
|
||||
vim.g.loaded_vimballPlugin = 1
|
||||
vim.g.loaded_2html_plugin = 1
|
||||
vim.g.loaded_logiPat = 1
|
||||
vim.g.loaded_rrhelper = 1
|
||||
vim.g.loaded_netrwPlugin = 1
|
||||
vim.g.did_load_filetypes = 1
|
||||
|
||||
-- Settings
|
||||
HOME = os.getenv("HOME")
|
||||
vim.opt.backspace = "indent,eol,start"
|
||||
vim.opt.history = 1000
|
||||
vim.opt.timeout = false
|
||||
vim.opt.ttimeout = true
|
||||
vim.opt.ttimeoutlen = 50
|
||||
vim.opt.scrolloff = 5
|
||||
vim.opt.clipboard = "unnamed"
|
||||
vim.opt.backup = false
|
||||
vim.opt.swapfile = false
|
||||
vim.opt.laststatus = 3
|
||||
vim.opt.splitbelow = true
|
||||
vim.opt.splitright = true
|
||||
vim.opt.shiftwidth = 2
|
||||
vim.opt.tabstop = 2
|
||||
vim.opt.expandtab = true
|
||||
vim.opt.list = true
|
||||
vim.opt.listchars= { tab = '› ', nbsp='·',trail ='·' }
|
||||
vim.opt.incsearch = true
|
||||
vim.opt.smartcase = true
|
||||
vim.opt.ignorecase = true
|
||||
vim.opt.synmaxcol = 200
|
||||
vim.opt.showmode = false
|
||||
vim.opt.grepprg="rg --vimgrep --smart-case --hidden"
|
||||
vim.opt.grepformat="%f:%l:%c:%m"
|
||||
vim.opt.background = "dark"
|
||||
vim.g.seoulbones_compat = 1
|
||||
vim.g.zenwritten_compat = 1
|
||||
vim.g.tokyobones_compat = 1
|
||||
vim.opt.termguicolors = true
|
||||
vim.opt.background = "light"
|
||||
vim.cmd[[colorscheme seoulbones]]
|
||||
|
||||
-- Mappings
|
||||
vim.api.nvim_set_keymap("t", "jk", "<C-\\><C-n>", {noremap = true})
|
||||
vim.api.nvim_set_keymap("i", "jk", "<esc>", {noremap = true})
|
||||
vim.api.nvim_set_keymap("n", "j", "gj", {noremap = false})
|
||||
vim.api.nvim_set_keymap("n", "k", "gk", {noremap = false})
|
||||
vim.api.nvim_set_keymap("n", "[q", "<Plug>qf_qf_previous", {noremap = false})
|
||||
vim.api.nvim_set_keymap("n", "]q", "<Plug>qf_qf_next", {noremap = false})
|
||||
require("mini.comment").setup()
|
||||
require("tmux").setup()
|
|
@ -0,0 +1,36 @@
|
|||
name: ci
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
linting:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: lunarmodules/luacheck@v0
|
||||
|
||||
testing:
|
||||
strategy:
|
||||
matrix:
|
||||
nvim-version: ["v0.6.0", "v0.6.1", "v0.7.0", "nightly"]
|
||||
# If one versions fails, still run all the other versions
|
||||
fail-fast: false
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install Neovim ${{ matrix.nvim-version }}
|
||||
run: |
|
||||
mkdir ./neovim
|
||||
curl -sL https://github.com/neovim/neovim/releases/download/${{ matrix.nvim-version }}/nvim-linux64.tar.gz \
|
||||
| tar xzf - --strip-components=1 -C ./neovim
|
||||
./neovim/bin/nvim --version
|
||||
|
||||
- name: Install Just
|
||||
uses: extractions/setup-just@v1
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
export PATH="./neovim/bin:$PATH"
|
||||
export VIM="./neovim/share/nvim/runtime"
|
||||
just test
|
|
@ -0,0 +1,3 @@
|
|||
std = luajit
|
||||
globals = { "vim", "_" }
|
||||
exclude_files = { "plenary.nvim" }
|
|
@ -0,0 +1,7 @@
|
|||
all: lint test
|
||||
|
||||
test:
|
||||
nvim --headless --clean -u tests/test_init.vim +Test
|
||||
|
||||
lint:
|
||||
luacheck .
|
|
@ -0,0 +1,661 @@
|
|||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,97 @@
|
|||
# dirbuf.nvim
|
||||
|
||||
A directory buffer for Neovim that lets you edit your filesystem like you edit
|
||||
text. Inspired by [vim-dirvish] and [vidir].
|
||||
|
||||
## Features
|
||||
|
||||
* *Intuitive:* Create, copy, delete, and rename files, directories, and more by
|
||||
editing their lines in the directory buffer. Buffer names are automatically
|
||||
updated to reflect changes.
|
||||
* *Minimal:* Works out of the box with no configuration. Default mappings
|
||||
easily changed.
|
||||
* *Unobtrusive:* Preserves alternate buffers and navigation history. Switch
|
||||
between files with `Ctrl-^` (`Ctrl-6`) and jump around your navigation history
|
||||
with custom `<Plug>` mappings.
|
||||
* *Safe:* Does not modify the filesystem until you save the buffer. Optionally
|
||||
request confirmation and dry-run saving.
|
||||
* *Reliable:* Resolves inter-dependencies in batch renames, including cycles.
|
||||
* *Polite:* Plays nicely with tree-based file viewers like [nvim-tree.lua],
|
||||
[fern.vim], and [carbon.nvim].
|
||||
|
||||
https://user-images.githubusercontent.com/42009212/162110083-9fd3701f-8ffb-4cf7-9333-d57020a9242e.mp4
|
||||
|
||||
## Installation
|
||||
|
||||
Requires [Neovim 0.6](https://github.com/neovim/neovim/releases/tag/v0.6.0) or
|
||||
higher.
|
||||
|
||||
* [vim-plug]: `Plug "elihunter173/dirbuf.nvim"`
|
||||
* [packer.nvim]: `use "elihunter173/dirbuf.nvim"`
|
||||
|
||||
### Notes
|
||||
|
||||
If you use [`nvim-tree.lua`](https://github.com/kyazdani42/nvim-tree.lua), you
|
||||
must disable the `:help nvim-tree.update_to_buf_dir` option. Otherwise, Dirbuf
|
||||
will fail to open directory buffers.
|
||||
|
||||
```lua
|
||||
require("nvim-tree").setup {
|
||||
update_to_buf_dir = { enable = false }
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Run the command `:Dirbuf` to open a directory buffer. Press `-` in any buffer
|
||||
to open a directory buffer for its parent. Editing a directory will also open
|
||||
up a directory buffer, overriding Netrw.
|
||||
|
||||
Inside a directory buffer, there are the following keybindings:
|
||||
* `<CR>`: Open the file or directory at the cursor.
|
||||
* `gh`: Toggle showing hidden files (i.e. dot files).
|
||||
* `-`: Open parent directory.
|
||||
|
||||
See `:help dirbuf.txt` for more info.
|
||||
|
||||
## Configuration
|
||||
|
||||
Configuration is not necessary for Dirbuf to work. But for those that want to
|
||||
override the default config, the following options are available with their
|
||||
default values listed.
|
||||
|
||||
```lua
|
||||
require("dirbuf").setup {
|
||||
hash_padding = 2,
|
||||
show_hidden = true,
|
||||
sort_order = "default",
|
||||
write_cmd = "DirbufSync",
|
||||
}
|
||||
```
|
||||
|
||||
Read the [documentation](/doc/dirbuf.txt) for more information (`:help
|
||||
dirbuf-options`).
|
||||
|
||||
## Development
|
||||
|
||||
A [Justfile][just] is provided to test and lint the project.
|
||||
|
||||
```sh
|
||||
# Run unit tests
|
||||
$ just test
|
||||
# Run luacheck
|
||||
$ just lint
|
||||
```
|
||||
|
||||
`just test` will automatically download [plenary.nvim]'s test harness and run
|
||||
the `*_spec.lua` tests in `tests/`.
|
||||
|
||||
[carbon.nvim]: https://github.com/SidOfc/carbon.nvim
|
||||
[fern.vim]: https://github.com/lambdalisue/fern.vim
|
||||
[just]: https://github.com/casey/just
|
||||
[nvim-tree.lua]: https://github.com/kyazdani42/nvim-tree.lua
|
||||
[packer.nvim]: https://github.com/wbthomason/packer.nvim
|
||||
[plenary.nvim]: https://github.com/nvim-lua/plenary.nvim
|
||||
[vidir]: https://github.com/trapd00r/vidir
|
||||
[vim-dirvish]: https://github.com/justinmk/vim-dirvish
|
||||
[vim-plug]: https://github.com/junegunn/vim-plug
|
|
@ -0,0 +1,197 @@
|
|||
*dirbuf.txt* directory buffer
|
||||
|
||||
==============================================================================
|
||||
OVERVIEW *dirbuf*
|
||||
|
||||
Dirbuf provides Neovim with an editable directory buffer. This buffer is a
|
||||
regular text buffer with some metadata behind the scenes allowing you to
|
||||
leverage all of Neovim's built-in text editing capabilities to efficiently
|
||||
manipulate and edit file directories.
|
||||
|
||||
To create a new file, add a new line containing the name of the file. To
|
||||
create an empty directory, add a "/" at the end.
|
||||
|
||||
To delete a file or directory, delete its line.
|
||||
|
||||
To copy a file or directory, copy its line and give it a new name.
|
||||
|
||||
To rename a file or directory, change its name in the directory buffer.
|
||||
|
||||
When you save the buffer, Dirbuf applies the necessary filesystem operations
|
||||
to get the directory into the desired state. It does this by comparing the
|
||||
snapshot it took of the directory when the buffer was created to the state of
|
||||
the buffer upon saving. Using the hashes at the end of every line, Dirbuf can
|
||||
tell what objects are new (i.e. they do not have a hash) and what objects have
|
||||
changed (i.e. their hash does not match their name).
|
||||
|
||||
Because each Dirbuf buffer name is the literal directory path, you can run any
|
||||
|:!| commands you want and prefix the filenames with |%|. For example, >
|
||||
:!sed 's/hi/ahoy/g' %/pirate_script.txt -i
|
||||
|
||||
Dirbuf is designed to work with built-in Vim concepts as much as possible. Tim
|
||||
Pope's plugins demonstrate this theme; more plugins should too. Re-use of
|
||||
concepts multiplies the utility of those concepts; conversely if a plugin does
|
||||
not reuse a concept, both that concept and the new one are made mutually less
|
||||
valuable--the sum is less than the parts--because the user must learn or
|
||||
choose from two slightly different things instead of one augmented system.
|
||||
|
||||
==============================================================================
|
||||
MAPPINGS *dirbuf-mappings*
|
||||
|
||||
All mappings are listed with their <Plug> mapping and their default mapping.
|
||||
If a mapping to the <Plug> version already exists, then the default mapping is
|
||||
not made.
|
||||
|
||||
Global ~
|
||||
<Plug>(dirbuf_up)
|
||||
- Opens the current file's directory or the [count]th parent
|
||||
directory.
|
||||
|
||||
Buffer-local (filetype=dirbuf) ~
|
||||
<Plug>(dirbuf_up)
|
||||
- Opens the current file's directory or the [count]th parent
|
||||
directory.
|
||||
<Plug>(dirbuf_enter)
|
||||
<CR> Opens file or directory at cursor.
|
||||
<Plug>(dirbuf_toggle_hide)
|
||||
gh Toggles whether hidden files (i.e. dot files) are
|
||||
displayed.
|
||||
<Plug>(dirbuf_history_forward)
|
||||
Moves forward [count] times in the directory buffer
|
||||
history.
|
||||
<Plug>(dirbuf_history_backward)
|
||||
Moves backward [count] times in the directory buffer
|
||||
history.
|
||||
|
||||
==============================================================================
|
||||
COMMANDS *dirbuf-commands*
|
||||
|
||||
:Dirbuf [path] *dirbuf-:Dirbuf*
|
||||
Opens the directory at [path], or its parent if [path] is a file, or the
|
||||
parent of the current file if [path] is not given.
|
||||
|
||||
:DirbufQuit *dirbuf-:DirbufQuit*
|
||||
Quits and returns to the original file.
|
||||
|
||||
:DirbufSync [{flag}] *dirbuf-:DirbufSync*
|
||||
Saves and refreshes the current directory buffer, syncing its state with
|
||||
the file system by creating, moving, copying, or deleting entries as
|
||||
necessary.
|
||||
|
||||
Flags: ~
|
||||
-confirm Before changing the filesystem, print out a list of all
|
||||
the actions `:DirbufSync` would perform, like `-dry-run`.
|
||||
Then ask the user to confirm the changes before making
|
||||
them.
|
||||
-dry-run Rather than changing the filesystem, print out a list of
|
||||
all the actions `:DirbufSync` would perform. These are
|
||||
formatted as Unix-like commands (e.g. `mv 'foo' 'bar'`),
|
||||
no matter what platform you are on.
|
||||
|
||||
==============================================================================
|
||||
FUNCTIONS *dirbuf-functions*
|
||||
|
||||
dirbuf.setup({opts}) *dirbuf.setup()*
|
||||
Overwrites the default options with the options in the {opts} table.
|
||||
|
||||
Example with all the default options: >
|
||||
require("dirbuf").setup {
|
||||
hash_padding = 2,
|
||||
show_hidden = true,
|
||||
sort_order = "default",
|
||||
write_cmd = "DirbufSync",
|
||||
}
|
||||
|
||||
|
||||
dirbuf.enter({cmd}) *dirbuf.enter()*
|
||||
Performs {cmd} ("edit", "vsplit", "split", "tabedit") on the path
|
||||
currently under the cursor.
|
||||
|
||||
dirbuf.get_cursor_path() *dirbuf.get_cursor_path()*
|
||||
Returns the absolute path of the filesystem entry under the cursor in the
|
||||
current directory buffer. If there are any errors parsing the current
|
||||
line, then this `error()`s with a descriptive error message.
|
||||
|
||||
==============================================================================
|
||||
OPTIONS *dirbuf-options*
|
||||
|
||||
|hash_padding| (default: `2`)
|
||||
Number of characters of padding between the file hashes and the longest
|
||||
filename. This must be an integer larger than 1.
|
||||
|
||||
|show_hidden| (default: `true`)
|
||||
Whether Dirbuf should display hidden files (i.e. "dot files") by default
|
||||
when opening new directory buffers. This can be changed locally per-buffer
|
||||
with the `gh` mapping.
|
||||
|
||||
|sort_order| (default: `"default"`)
|
||||
What order Dirbuf should sort the directory buffer in when it is created
|
||||
and refreshed.
|
||||
|
||||
This must be given as either a `string` or a `function`.
|
||||
|
||||
If a `string` is given, then it must have one of the following values.
|
||||
Values: ~
|
||||
"default" sort case-insensitively by {fname}
|
||||
"directories_first" groups files of like {ftype} and then sort within
|
||||
groups case-insensitively by {fname}
|
||||
|
||||
If a `function` is given, it must be a comparison function which takes two
|
||||
tables {left} and {right}, each describing a filesystem entry, which
|
||||
returns `true` when {left} should appear before (i.e. above) {right} in
|
||||
the directory buffer.
|
||||
|
||||
Both of the tables {left} and {right} have the following fields.
|
||||
Fields: ~
|
||||
{fname} `string` containing the literal, unescaped name of the
|
||||
filesystem entry without any suffixes (e.g. a directory
|
||||
example/ would have an fname of "example")
|
||||
{ftype} `string` describing the type of the filesystem entry, which
|
||||
must be one of "file", "directory", "link", "fifo", "socket",
|
||||
"char", or "block"
|
||||
{path} `string` containing the full path of the filesystem entry
|
||||
using platform-specific directory separators (i.e. "\" on
|
||||
Windows and "/" on Linux and MacOS) without a suffix
|
||||
|
||||
|write_cmd| (default: `"DirbufSync"`)
|
||||
What command Dirbuf should execute when the user issues a `:write`.
|
||||
|
||||
Examples: ~
|
||||
"DirbufSync -confirm"
|
||||
Requests confirmation from the user before syncing the changes
|
||||
made to the directory buffer.
|
||||
|
||||
"" or "echoerr ':write disabled'"
|
||||
Disables `:write` in directory buffers, forcing users to
|
||||
explicitly invoke `:DirbufSync`.
|
||||
|
||||
==============================================================================
|
||||
FAQ *dirbuf-faq*
|
||||
|
||||
Can I conceal hashes in directory buffers? ~
|
||||
Dirbuf does not natively support `conceal` on hashes because the author
|
||||
believes seeing the hashes is important to making Dirbuf's actions
|
||||
predictable and wants to dissuade new users from hiding the hashes.
|
||||
|
||||
However, if you really want to conceal the hashes, you can create a
|
||||
`after/syntax/dirbuf.vim` file with the following code which modifies the
|
||||
normal DirbufHash definition to support `conceal`. >
|
||||
syntax clear DirbufHash
|
||||
syntax match DirbufHash /^#\x\{8}\t/ms=s-1 conceal cchar=#
|
||||
setlocal conceallevel=2
|
||||
setlocal concealcursor=n
|
||||
|
||||
If you feel strongly that Dirbuf should natively support `conceal` on hashes,
|
||||
+1 this issue and I will consider it: >
|
||||
https://github.com/elihunter173/dirbuf.nvim/issues/23
|
||||
|
||||
==============================================================================
|
||||
CREDITS *dirbuf-credits*
|
||||
|
||||
Dirbuf was initially conceived of as a Lua rewrite of the file manager plugin
|
||||
Dirvish and eventually grew in scope to become an editable directory buffer
|
||||
similiar to vidir. However, it still owes many of its ideas to Dirvish as well
|
||||
as much of its literal Vimscript and help documentation.
|
||||
|
||||
==============================================================================
|
||||
vim:tw=78:ts=4:et:ft=help:norl:
|
|
@ -0,0 +1,14 @@
|
|||
dirbuf dirbuf.txt /*dirbuf*
|
||||
dirbuf-:Dirbuf dirbuf.txt /*dirbuf-:Dirbuf*
|
||||
dirbuf-:DirbufQuit dirbuf.txt /*dirbuf-:DirbufQuit*
|
||||
dirbuf-:DirbufSync dirbuf.txt /*dirbuf-:DirbufSync*
|
||||
dirbuf-commands dirbuf.txt /*dirbuf-commands*
|
||||
dirbuf-credits dirbuf.txt /*dirbuf-credits*
|
||||
dirbuf-faq dirbuf.txt /*dirbuf-faq*
|
||||
dirbuf-functions dirbuf.txt /*dirbuf-functions*
|
||||
dirbuf-mappings dirbuf.txt /*dirbuf-mappings*
|
||||
dirbuf-options dirbuf.txt /*dirbuf-options*
|
||||
dirbuf.enter() dirbuf.txt /*dirbuf.enter()*
|
||||
dirbuf.get_cursor_path() dirbuf.txt /*dirbuf.get_cursor_path()*
|
||||
dirbuf.setup() dirbuf.txt /*dirbuf.setup()*
|
||||
dirbuf.txt dirbuf.txt /*dirbuf.txt*
|
|
@ -0,0 +1,14 @@
|
|||
if !hasmapto('<Plug>(dirbuf_enter)')
|
||||
nmap <buffer> <cr> <Plug>(dirbuf_enter)
|
||||
endif
|
||||
if !hasmapto('<Plug>(dirbuf_toggle_hide)')
|
||||
nmap <buffer> gh <Plug>(dirbuf_toggle_hide)
|
||||
endif
|
||||
if !hasmapto('<Plug>(dirbuf_up)')
|
||||
nmap <buffer> - <Plug>(dirbuf_up)
|
||||
endif
|
||||
|
||||
augroup dirbuf_local
|
||||
autocmd! * <buffer>
|
||||
autocmd BufWriteCmd <buffer> execute v:lua.require('dirbuf.config').get('write_cmd')
|
||||
augroup END
|
|
@ -0,0 +1,383 @@
|
|||
local api = vim.api
|
||||
|
||||
local buffer = require("dirbuf.buffer")
|
||||
local config = require("dirbuf.config")
|
||||
local fs = require("dirbuf.fs")
|
||||
local planner = require("dirbuf.planner")
|
||||
|
||||
local M = {}
|
||||
|
||||
local CURRENT_BUFFER = 0
|
||||
local CURRENT_WINDOW = 0
|
||||
|
||||
function M.setup(opts)
|
||||
local errors = config.update(opts)
|
||||
if #errors == 1 then
|
||||
api.nvim_err_writeln("dirbuf.setup: " .. errors[1])
|
||||
elseif #errors > 1 then
|
||||
api.nvim_err_writeln("dirbuf.setup:")
|
||||
for _, err in ipairs(errors) do
|
||||
api.nvim_err_writeln(" " .. err)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- `normalize_path` takes a `path` entered by the user, potentially containing
|
||||
-- duplicate path separators, "..", or trailing path separators, and ensures
|
||||
-- that all duplicate path separators are removed, there is no trailing path
|
||||
-- separator, and all ".."s are simplified. This does not resolve symlinks.
|
||||
--
|
||||
-- This exists to ensure that all paths are displayed in a consistent way and
|
||||
-- to simplify path manipulation logic.
|
||||
local function normalize_path(path)
|
||||
path = vim.fn.simplify(vim.fn.fnamemodify(path, ":p"))
|
||||
-- On Windows, simplify keeps the path_separator on directories
|
||||
if path:sub(-1, -1) == fs.path_separator then
|
||||
path = vim.fn.fnamemodify(path, ":h")
|
||||
end
|
||||
return path
|
||||
end
|
||||
|
||||
-- `fill_dirbuf` fills the current buffer with the contents of its
|
||||
-- corresponding directory. Note that the current buffer must have the name of
|
||||
-- a valid directory.
|
||||
--
|
||||
-- If `on_fname` is set, then the cursor will be put on the line corresponding
|
||||
-- to `on_fname`.
|
||||
--
|
||||
-- Returns: err
|
||||
local function fill_dirbuf(on_fname)
|
||||
local dir = api.nvim_buf_get_name(CURRENT_BUFFER)
|
||||
local err, fs_entries = fs.get_fs_entries(dir, vim.b.dirbuf_show_hidden)
|
||||
if err ~= nil then
|
||||
return err
|
||||
end
|
||||
|
||||
-- Before we set lines, we set undolevels to -1 so we delete the history when
|
||||
-- we set the lines. This prevents people going back to now-invalid hashes
|
||||
-- and potentially messing up their directory on accident
|
||||
local buf_lines, fname_line = buffer.write_fs_entries(fs_entries, on_fname)
|
||||
local undolevels = vim.bo.undolevels
|
||||
vim.bo.undolevels = -1
|
||||
api.nvim_buf_set_lines(CURRENT_BUFFER, 0, -1, true, buf_lines)
|
||||
vim.bo.undolevels = undolevels
|
||||
vim.b.dirbuf = fs_entries
|
||||
|
||||
vim.bo.tabstop = #"#" + buffer.HASH_LEN + config.get("hash_padding")
|
||||
api.nvim_win_set_cursor(CURRENT_WINDOW, { fname_line or 1, #"#" + buffer.HASH_LEN + #"\t" })
|
||||
vim.bo.modified = false
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
function M.init_dirbuf(history, history_index, update_history, from_path)
|
||||
-- Preserve altbuf
|
||||
local altbuf = vim.fn.bufnr("#")
|
||||
|
||||
local path = normalize_path(vim.fn.expand("%"))
|
||||
api.nvim_buf_set_name(CURRENT_BUFFER, path)
|
||||
|
||||
-- Determine where to place cursor
|
||||
-- We ignore errors in case the buffer is empty
|
||||
local _, _, cursor_fname, _ = buffer.parse_line(api.nvim_get_current_line())
|
||||
-- See if we're coming from a path below this dirbuf.
|
||||
if from_path ~= nil and vim.startswith(from_path, path) then
|
||||
-- Make sure we're clipping past the "/" in from_path
|
||||
local fname_start = #path + 1
|
||||
if path:sub(-1, -1) ~= fs.path_separator then
|
||||
fname_start = fname_start + 1
|
||||
end
|
||||
local last_path_separator = from_path:find(fs.path_separator, fname_start, true)
|
||||
if last_path_separator ~= nil then
|
||||
cursor_fname = from_path:sub(fname_start, last_path_separator - 1)
|
||||
else
|
||||
cursor_fname = from_path:sub(fname_start)
|
||||
end
|
||||
end
|
||||
|
||||
-- Update history
|
||||
if history == nil then
|
||||
history = {}
|
||||
history_index = 0
|
||||
end
|
||||
if update_history then
|
||||
-- Clear old history
|
||||
while #history > history_index do
|
||||
table.remove(history)
|
||||
end
|
||||
-- We don't add to history if we're just refreshing the dirbuf
|
||||
if path ~= history[history_index] then
|
||||
table.insert(history, path)
|
||||
history_index = history_index + 1
|
||||
end
|
||||
end
|
||||
vim.b.dirbuf_history = history
|
||||
vim.b.dirbuf_history_index = history_index
|
||||
|
||||
-- Set dirbuf options
|
||||
vim.bo.filetype = "dirbuf"
|
||||
vim.bo.buftype = "acwrite"
|
||||
vim.bo.bufhidden = "wipe"
|
||||
-- Normally unnecessary but sometimes other plugins make things unmodifiable,
|
||||
-- so we have to do this to prevent running into errors in fill_dirbuf
|
||||
vim.bo.modifiable = true
|
||||
|
||||
-- Set "dirbuf_show_hidden" to default if it is unset
|
||||
if vim.b.dirbuf_show_hidden == nil then
|
||||
vim.b.dirbuf_show_hidden = config.get("show_hidden")
|
||||
end
|
||||
|
||||
if altbuf ~= -1 then
|
||||
vim.fn.setreg("#", altbuf)
|
||||
end
|
||||
local err = fill_dirbuf(cursor_fname)
|
||||
if err ~= nil then
|
||||
api.nvim_err_writeln(err)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
function M.get_cursor_path()
|
||||
local err, _, fname, _ = buffer.parse_line(api.nvim_get_current_line())
|
||||
if err ~= nil then
|
||||
error(err)
|
||||
end
|
||||
local dir = normalize_path(vim.fn.expand("%"))
|
||||
return fs.join_paths(dir, fname)
|
||||
end
|
||||
|
||||
-- If `path` is a file, this returns the absolute path to its parent. Otherwise
|
||||
-- it returns the absolute path of `path`.
|
||||
local function directify(path)
|
||||
if fs.is_directory(path) then
|
||||
return vim.fn.fnamemodify(path, ":p")
|
||||
else
|
||||
return vim.fn.fnamemodify(path, ":h:p")
|
||||
end
|
||||
end
|
||||
|
||||
function M.open(path)
|
||||
if path == "" then
|
||||
path = api.nvim_buf_get_name(CURRENT_BUFFER)
|
||||
end
|
||||
path = normalize_path(directify(path))
|
||||
|
||||
local from_path = normalize_path(vim.fn.expand("%"))
|
||||
if from_path == path then
|
||||
-- If we're not leaving, we want to keep the cursor on the same line
|
||||
local err, _, fname, _ = buffer.parse_line(api.nvim_get_current_line())
|
||||
if err ~= nil then
|
||||
api.nvim_err_writeln("Error placing cursor: " .. err)
|
||||
return
|
||||
end
|
||||
from_path = fs.join_paths(path, fname)
|
||||
end
|
||||
|
||||
local keepalt = ""
|
||||
if vim.bo.filetype == "dirbuf" then
|
||||
-- If we're leaving a dirbuf, keep our alternate buffer
|
||||
keepalt = "keepalt"
|
||||
end
|
||||
local history, history_index = vim.b.dirbuf_history, vim.b.dirbuf_history_index
|
||||
vim.cmd(keepalt .. " noautocmd edit " .. vim.fn.fnameescape(path))
|
||||
-- Sanity check: If we're not in the file we just edited, something went
|
||||
-- wrong. This can happen if someone has `:set nohidden confirm`,
|
||||
-- accidentally opens dirbuf, and hits escape at the save prompt. The edit
|
||||
-- "fails" without raising an error
|
||||
if api.nvim_buf_get_name(CURRENT_BUFFER) ~= path then
|
||||
return
|
||||
end
|
||||
M.init_dirbuf(history, history_index, true, from_path)
|
||||
end
|
||||
|
||||
function M.enter(cmd)
|
||||
if cmd == nil then
|
||||
cmd = "edit"
|
||||
end
|
||||
|
||||
if vim.bo.filetype ~= "dirbuf" then
|
||||
api.nvim_err_writeln("Operation only supports 'filetype=dirbuf'")
|
||||
return
|
||||
end
|
||||
|
||||
local err, _, fname, _ = buffer.parse_line(api.nvim_get_current_line())
|
||||
if err ~= nil then
|
||||
api.nvim_err_writeln(err)
|
||||
return
|
||||
end
|
||||
if vim.bo.modified then
|
||||
api.nvim_err_writeln(string.format("Cannot enter '%s'. Dirbuf must be saved first", fname))
|
||||
return
|
||||
end
|
||||
|
||||
local dir = normalize_path(vim.fn.expand("%"))
|
||||
local path = fs.join_paths(dir, fname)
|
||||
local noautocmd = ""
|
||||
if fs.is_directory(path) then
|
||||
noautocmd = "noautocmd"
|
||||
end
|
||||
local history, history_index = vim.b.dirbuf_history, vim.b.dirbuf_history_index
|
||||
vim.cmd("keepalt " .. noautocmd .. " " .. cmd .. " " .. vim.fn.fnameescape(path))
|
||||
if fs.is_directory(path) then
|
||||
M.init_dirbuf(history, history_index, true)
|
||||
end
|
||||
end
|
||||
|
||||
function M.jump_history(n)
|
||||
if vim.bo.filetype ~= "dirbuf" then
|
||||
api.nvim_err_writeln("Operation only supports 'filetype=dirbuf'")
|
||||
return
|
||||
end
|
||||
local history, history_index = vim.b.dirbuf_history, vim.b.dirbuf_history_index
|
||||
local next_index = math.max(1, math.min(#history, history_index + n))
|
||||
vim.cmd("keepalt noautocmd edit " .. vim.fn.fnameescape(history[next_index]))
|
||||
M.init_dirbuf(history, next_index, false, history[history_index])
|
||||
end
|
||||
|
||||
function M.quit()
|
||||
if vim.bo.filetype ~= "dirbuf" then
|
||||
api.nvim_err_writeln(":DirbufQuit only supports 'filetype=dirbuf'")
|
||||
return
|
||||
end
|
||||
|
||||
local altbuf = vim.fn.bufnr("#")
|
||||
if altbuf == -1 or altbuf == api.nvim_get_current_buf() then
|
||||
vim.cmd("bdelete")
|
||||
else
|
||||
api.nvim_set_current_buf(altbuf)
|
||||
end
|
||||
end
|
||||
|
||||
-- Ensure that the directory has not changed since our last snapshot
|
||||
local function check_dirbuf(buf)
|
||||
local dir = api.nvim_buf_get_name(buf)
|
||||
local err, current_fs_entries = fs.get_fs_entries(dir, vim.b.dirbuf_show_hidden)
|
||||
if err ~= nil then
|
||||
return "Error while checking: " .. err
|
||||
end
|
||||
|
||||
if not vim.deep_equal(vim.b.dirbuf, current_fs_entries) then
|
||||
return "Snapshot out of date with current directory. Run :edit! to refresh"
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
-- print_plan() should only be called from dirbuf.sync()
|
||||
local function print_plan(plan)
|
||||
local function fmt_fs_entry(fs_entry)
|
||||
return vim.fn.shellescape(buffer.display_fs_entry(fs_entry))
|
||||
end
|
||||
|
||||
for _, action in ipairs(plan) do
|
||||
if action.type == "create" then
|
||||
if action.fs_entry.ftype == "directory" then
|
||||
print("mkdir " .. fmt_fs_entry(action.fs_entry))
|
||||
else
|
||||
print("touch " .. fmt_fs_entry(action.fs_entry))
|
||||
end
|
||||
elseif action.type == "copy" then
|
||||
print("cp " .. fmt_fs_entry(action.src_fs_entry) .. " " .. fmt_fs_entry(action.dst_fs_entry))
|
||||
elseif action.type == "delete" then
|
||||
print("rm " .. fmt_fs_entry(action.fs_entry))
|
||||
elseif action.type == "move" then
|
||||
print("mv " .. fmt_fs_entry(action.src_fs_entry) .. " " .. fmt_fs_entry(action.dst_fs_entry))
|
||||
else
|
||||
error("Unrecognized action: " .. vim.inspect(action))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- do_plan() should only be called from dirbuf.sync()
|
||||
local function do_plan(plan)
|
||||
local err = planner.execute_plan(plan)
|
||||
if err ~= nil then
|
||||
api.nvim_err_writeln("Error making changes: " .. err)
|
||||
api.nvim_err_writeln("WARNING: Dirbuf in inconsistent state. Run :edit! to refresh")
|
||||
return
|
||||
end
|
||||
|
||||
-- Leave cursor on the same file
|
||||
local fname
|
||||
err, _, fname, _ = buffer.parse_line(api.nvim_get_current_line())
|
||||
if err ~= nil then
|
||||
api.nvim_err_writeln(err)
|
||||
return
|
||||
end
|
||||
err = fill_dirbuf(fname)
|
||||
if err ~= nil then
|
||||
api.nvim_err_writeln(err)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
function M.sync(opt)
|
||||
if opt == nil then
|
||||
opt = ""
|
||||
end
|
||||
|
||||
if vim.bo.filetype ~= "dirbuf" then
|
||||
api.nvim_err_writeln(":DirbufSync only supports 'filetype=dirbuf'")
|
||||
return
|
||||
end
|
||||
|
||||
if opt ~= "" and opt ~= "-confirm" and opt ~= "-dry-run" then
|
||||
api.nvim_err_writeln(":DirbufSync unrecognized option: " .. opt)
|
||||
end
|
||||
|
||||
if not vim.bo.modified then
|
||||
return
|
||||
end
|
||||
|
||||
local err = check_dirbuf(CURRENT_BUFFER)
|
||||
if err ~= nil then
|
||||
api.nvim_err_writeln("Cannot save dirbuf: " .. err)
|
||||
return
|
||||
end
|
||||
|
||||
local dir = api.nvim_buf_get_name(CURRENT_BUFFER)
|
||||
local lines = api.nvim_buf_get_lines(CURRENT_BUFFER, 0, -1, true)
|
||||
local changes
|
||||
err, changes = planner.build_changes(dir, vim.b.dirbuf, lines)
|
||||
if err ~= nil then
|
||||
api.nvim_err_writeln(err)
|
||||
return
|
||||
end
|
||||
|
||||
local plan = planner.determine_plan(changes)
|
||||
|
||||
if opt == "-confirm" then
|
||||
print_plan(plan)
|
||||
-- We pcall to make Ctrl-C work
|
||||
local ok, response = pcall(vim.fn.confirm, "Sync changes?", "&Yes\n&No", 2)
|
||||
if ok and response == 1 then
|
||||
do_plan(plan)
|
||||
end
|
||||
elseif opt == "-dry-run" then
|
||||
print_plan(plan)
|
||||
else
|
||||
do_plan(plan)
|
||||
end
|
||||
end
|
||||
|
||||
function M.toggle_hide()
|
||||
if vim.bo.filetype ~= "dirbuf" then
|
||||
api.nvim_err_writeln("Operation only supports 'filetype=dirbuf'")
|
||||
return
|
||||
end
|
||||
|
||||
vim.b.dirbuf_show_hidden = not vim.b.dirbuf_show_hidden
|
||||
-- Leave cursor on the same file
|
||||
local err, _, fname, _ = buffer.parse_line(api.nvim_get_current_line())
|
||||
if err ~= nil then
|
||||
api.nvim_err_writeln(err)
|
||||
return
|
||||
end
|
||||
err = fill_dirbuf(fname)
|
||||
if err ~= nil then
|
||||
api.nvim_err_writeln(err)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
|
@ -0,0 +1,203 @@
|
|||
local fs = require("dirbuf.fs")
|
||||
|
||||
local M = {}
|
||||
|
||||
M.HASH_LEN = 8
|
||||
|
||||
--[[
|
||||
local record Dirbuf
|
||||
dir: string
|
||||
fs_entries: {string: FSEntry}
|
||||
end
|
||||
--]]
|
||||
|
||||
local function is_suffix(c)
|
||||
return c == "/" or c == "\\" or c == "@" or c == "|" or c == "=" or c == "%" or c == "#"
|
||||
end
|
||||
|
||||
-- These suffixes are taken from `ls --classify` and zsh's tab completion
|
||||
local function suffix_to_ftype(suffix)
|
||||
if suffix == nil then
|
||||
return "file"
|
||||
elseif suffix == "/" or suffix == "\\" then
|
||||
return "directory"
|
||||
elseif suffix == "@" then
|
||||
return "link"
|
||||
elseif suffix == "|" then
|
||||
return "fifo"
|
||||
elseif suffix == "=" then
|
||||
return "socket"
|
||||
elseif suffix == "%" then
|
||||
return "char"
|
||||
elseif suffix == "#" then
|
||||
return "block"
|
||||
else
|
||||
error(
|
||||
string.format("Unrecognized suffix %s. This should be impossible and is a bug in dirbuf.", vim.inspect(suffix))
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
local function ftype_to_suffix(ftype)
|
||||
if ftype == "file" then
|
||||
return ""
|
||||
elseif ftype == "directory" then
|
||||
return fs.path_separator
|
||||
elseif ftype == "link" then
|
||||
return "@"
|
||||
elseif ftype == "fifo" then
|
||||
return "|"
|
||||
elseif ftype == "socket" then
|
||||
return "="
|
||||
elseif ftype == "char" then
|
||||
return "%"
|
||||
elseif ftype == "block" then
|
||||
return "#"
|
||||
else
|
||||
error(string.format("Unrecognized ftype %s. This should be impossible and is a bug in dirbuf", vim.inspect(ftype)))
|
||||
end
|
||||
end
|
||||
|
||||
-- escaped char -> unescaped
|
||||
-- We treat "\\" separately to avoid confusion where we duplicate backslashes
|
||||
-- when trying to programmatically escape characters
|
||||
local ESCAPE_CHARS = { n = "\n", t = "\t" }
|
||||
|
||||
-- Returns err, fname, ftype
|
||||
local function parse_fname(chars)
|
||||
local string_builder = {}
|
||||
|
||||
local last_suffix = nil
|
||||
while true do
|
||||
local c = chars()
|
||||
if c == nil or c == "\t" then
|
||||
break
|
||||
end
|
||||
|
||||
if last_suffix ~= nil then
|
||||
-- This suffix wasn't it :)
|
||||
table.insert(string_builder, last_suffix)
|
||||
last_suffix = nil
|
||||
end
|
||||
|
||||
if c == "\\" then
|
||||
local next_c = chars()
|
||||
if next_c == nil or next_c == "\t" then
|
||||
-- `c` was a terminal backslash
|
||||
last_suffix = "\\"
|
||||
break
|
||||
end
|
||||
|
||||
-- Convert escape sequence
|
||||
if next_c == "\\" then
|
||||
last_suffix = nil
|
||||
table.insert(string_builder, "\\")
|
||||
elseif ESCAPE_CHARS[next_c] ~= nil then
|
||||
last_suffix = nil
|
||||
table.insert(string_builder, ESCAPE_CHARS[next_c])
|
||||
else
|
||||
return string.format("Invalid escape sequence %s", vim.inspect(c .. next_c))
|
||||
end
|
||||
elseif is_suffix(c) then
|
||||
last_suffix = c
|
||||
else
|
||||
table.insert(string_builder, c)
|
||||
end
|
||||
end
|
||||
|
||||
if #string_builder == 0 and last_suffix ~= nil then
|
||||
table.insert(string_builder, last_suffix)
|
||||
last_suffix = nil
|
||||
end
|
||||
|
||||
if #string_builder > 0 then
|
||||
local fname = table.concat(string_builder)
|
||||
local ftype = suffix_to_ftype(last_suffix)
|
||||
return nil, fname, ftype
|
||||
else
|
||||
return nil, nil, nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Returns err, hash
|
||||
local function parse_hash(chars)
|
||||
local c = chars()
|
||||
if c == nil then
|
||||
-- Ended line before hash
|
||||
return nil, nil
|
||||
elseif c ~= "#" then
|
||||
return string.format("Unexpected character %s after fname", vim.inspect(c))
|
||||
end
|
||||
|
||||
local string_builder = {}
|
||||
for _ = 1, M.HASH_LEN do
|
||||
c = chars()
|
||||
if c == nil then
|
||||
return "Unexpected end of line in hash"
|
||||
elseif not c:match("%x") then
|
||||
return string.format("Invalid hash character %s", vim.inspect(c))
|
||||
else
|
||||
table.insert(string_builder, c)
|
||||
end
|
||||
end
|
||||
return nil, tonumber(table.concat(string_builder), 16)
|
||||
end
|
||||
|
||||
-- The language of valid dirbuf lines is regular, so normally I would use
|
||||
-- regex. However, Lua's patterns cannot parse dirbuf lines because of escaping
|
||||
-- and I want better error messages, so I parse lines by hand.
|
||||
--
|
||||
-- Returns err, hash, fname, ftype
|
||||
function M.parse_line(line)
|
||||
local chars = line:gmatch(".")
|
||||
|
||||
-- We throw away the error because if there's an error in parsing the hash,
|
||||
-- we treat the whole thing as an fname
|
||||
local _, hash = parse_hash(chars)
|
||||
if hash == nil or chars() ~= "\t" then
|
||||
hash = nil
|
||||
chars = line:gmatch(".")
|
||||
end
|
||||
|
||||
local err, fname, ftype = parse_fname(chars)
|
||||
if err ~= nil then
|
||||
return err
|
||||
end
|
||||
|
||||
-- Ensure that we parsed the whole line
|
||||
local c = chars()
|
||||
if c ~= nil then
|
||||
return string.format("Unexpected character %s after fname", vim.inspect(c))
|
||||
end
|
||||
|
||||
return nil, hash, fname, ftype
|
||||
end
|
||||
|
||||
function M.display_fs_entry(fs_entry)
|
||||
local escaped = fs_entry.fname:gsub("\\", "\\\\")
|
||||
for escape_char, unescaped in pairs(ESCAPE_CHARS) do
|
||||
escaped = escaped:gsub(unescaped, "\\" .. escape_char)
|
||||
end
|
||||
return escaped .. ftype_to_suffix(fs_entry.ftype)
|
||||
end
|
||||
|
||||
function M.write_fs_entries(fs_entries, track_fname)
|
||||
local fname_line = nil
|
||||
for lnum, fs_entry in ipairs(fs_entries) do
|
||||
if fs_entry.fname == track_fname then
|
||||
fname_line = lnum
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local buf_lines = {}
|
||||
for idx, fs_entry in ipairs(fs_entries) do
|
||||
local hash = string.format("%08x", idx)
|
||||
local display = M.display_fs_entry(fs_entry)
|
||||
table.insert(buf_lines, "#" .. hash .. "\t" .. display)
|
||||
end
|
||||
|
||||
return buf_lines, fname_line
|
||||
end
|
||||
|
||||
return M
|
|
@ -0,0 +1,103 @@
|
|||
local M = {}
|
||||
|
||||
local function sort_default(left, right)
|
||||
return left.fname:lower() < right.fname:lower()
|
||||
end
|
||||
|
||||
local function sort_directories_first(left, right)
|
||||
if left.ftype ~= right.ftype then
|
||||
return left.ftype < right.ftype
|
||||
else
|
||||
return left.fname:lower() < right.fname:lower()
|
||||
end
|
||||
end
|
||||
|
||||
local CONFIG_SPEC = {
|
||||
hash_padding = {
|
||||
default = 2,
|
||||
check = function(val)
|
||||
if type(val) ~= "number" or math.floor(val) ~= val or val < 1 then
|
||||
return "must be integer larger than 1"
|
||||
end
|
||||
end,
|
||||
},
|
||||
show_hidden = {
|
||||
default = true,
|
||||
check = function(val)
|
||||
if type(val) ~= "boolean" then
|
||||
return "must be boolean, received " .. type(val)
|
||||
end
|
||||
end,
|
||||
},
|
||||
sort_order = {
|
||||
default = sort_default,
|
||||
check = function(val)
|
||||
if val == "default" then
|
||||
return nil, sort_default
|
||||
elseif val == "directories_first" then
|
||||
return nil, sort_directories_first
|
||||
elseif type(val) == "function" then
|
||||
return nil, val
|
||||
else
|
||||
return 'must be one of "default", "directories_first", or function'
|
||||
end
|
||||
end,
|
||||
},
|
||||
write_cmd = {
|
||||
default = "DirbufSync",
|
||||
check = function(val)
|
||||
if type(val) ~= "string" then
|
||||
return "must be string, received " .. type(val)
|
||||
end
|
||||
end,
|
||||
},
|
||||
}
|
||||
|
||||
local user_config = {}
|
||||
|
||||
function M.update(opts)
|
||||
local errors = {}
|
||||
|
||||
for option_name, spec in pairs(CONFIG_SPEC) do
|
||||
local val = opts[option_name]
|
||||
if val == nil then
|
||||
-- Don't check unset options
|
||||
user_config[option_name] = nil
|
||||
else
|
||||
local err, converted = spec.check(val)
|
||||
if err ~= nil then
|
||||
table.insert(errors, string.format("`%s` %s", option_name, err))
|
||||
elseif converted == nil then
|
||||
user_config[option_name] = val
|
||||
else
|
||||
user_config[option_name] = converted
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local unknown_options = {}
|
||||
for key, _ in pairs(opts) do
|
||||
if CONFIG_SPEC[key] == nil then
|
||||
table.insert(unknown_options, "`" .. key .. "`")
|
||||
end
|
||||
end
|
||||
if #unknown_options > 0 then
|
||||
table.insert(errors, table.concat(unknown_options, ", ") .. " not recognized")
|
||||
end
|
||||
|
||||
return errors
|
||||
end
|
||||
|
||||
function M.get(opt)
|
||||
-- Ensure we don't typo options
|
||||
if CONFIG_SPEC[opt] == nil then
|
||||
error("Unrecognized option: " .. opt)
|
||||
end
|
||||
if user_config[opt] == nil then
|
||||
return CONFIG_SPEC[opt].default
|
||||
else
|
||||
return user_config[opt]
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
|
@ -0,0 +1,322 @@
|
|||
local api = vim.api
|
||||
local uv = vim.loop
|
||||
|
||||
local config = require("dirbuf.config")
|
||||
|
||||
local M = {}
|
||||
|
||||
M.path_separator = package.config:sub(1, 1)
|
||||
|
||||
function M.is_hidden(fname)
|
||||
return fname:sub(1, 1) == "."
|
||||
end
|
||||
|
||||
function M.join_paths(...)
|
||||
local string_builder = {}
|
||||
for _, path in ipairs({ ... }) do
|
||||
if path:sub(-1, -1) == M.path_separator then
|
||||
path = path:sub(0, -2)
|
||||
end
|
||||
table.insert(string_builder, path)
|
||||
end
|
||||
return table.concat(string_builder, M.path_separator)
|
||||
end
|
||||
|
||||
function M.is_directory(path)
|
||||
return vim.fn.isdirectory(path) == 1
|
||||
end
|
||||
|
||||
-- FTypes are taken from
|
||||
-- https://github.com/tbastos/luv/blob/2fed9454ebb870548cef1081a1f8a3dd879c1e70/src/fs.c#L420-L430
|
||||
--[[
|
||||
local enum FType
|
||||
"file"
|
||||
"directory"
|
||||
"link"
|
||||
"fifo"
|
||||
"socket"
|
||||
"char"
|
||||
"block"
|
||||
end
|
||||
local record FSEntry
|
||||
fname: string
|
||||
ftype: FType
|
||||
path: string
|
||||
end
|
||||
--]]
|
||||
|
||||
M.FSEntry = {}
|
||||
local FSEntry = M.FSEntry
|
||||
|
||||
function FSEntry.new(fname, parent, ftype)
|
||||
return { fname = fname, path = M.join_paths(parent, fname), ftype = ftype }
|
||||
end
|
||||
|
||||
function FSEntry.temp(ftype)
|
||||
local temppath = vim.fn.tempname()
|
||||
return {
|
||||
-- XXX: This technically violates fname's assumption that it is alwaies a
|
||||
-- simple name and not a path
|
||||
fname = temppath,
|
||||
path = temppath,
|
||||
ftype = ftype,
|
||||
}
|
||||
end
|
||||
|
||||
function M.get_fs_entries(dir, show_hidden)
|
||||
local fs_entries = {}
|
||||
|
||||
local handle, err, _ = uv.fs_scandir(dir)
|
||||
if handle == nil then
|
||||
return err
|
||||
end
|
||||
|
||||
while true do
|
||||
local fname, ftype = uv.fs_scandir_next(handle)
|
||||
if fname == nil then
|
||||
break
|
||||
end
|
||||
if show_hidden or not M.is_hidden(fname) then
|
||||
table.insert(fs_entries, FSEntry.new(fname, dir, ftype))
|
||||
end
|
||||
end
|
||||
table.sort(fs_entries, config.get("sort_order"))
|
||||
|
||||
return nil, fs_entries
|
||||
end
|
||||
|
||||
M.plan = {}
|
||||
M.actions = {}
|
||||
|
||||
local DEFAULT_FILE_MODE = tonumber("644", 8)
|
||||
-- Directories have to be executable for you to chdir into them
|
||||
local DEFAULT_DIR_MODE = tonumber("755", 8)
|
||||
|
||||
local function cp(src_path, dst_path, ftype)
|
||||
if ftype == "directory" then
|
||||
local ok, err, _ = uv.fs_mkdir(dst_path, DEFAULT_DIR_MODE)
|
||||
if not ok then
|
||||
return err
|
||||
end
|
||||
|
||||
local handle = uv.fs_scandir(src_path)
|
||||
while true do
|
||||
local next_fname, next_ftype = uv.fs_scandir_next(handle)
|
||||
if next_fname == nil then
|
||||
break
|
||||
end
|
||||
err = cp(M.join_paths(src_path, next_fname), M.join_paths(dst_path, next_fname), next_ftype)
|
||||
if err ~= nil then
|
||||
return err
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
elseif ftype == "link" then
|
||||
local src_points_to, err, _ = uv.fs_readlink(src_path)
|
||||
if src_points_to == nil then
|
||||
return err
|
||||
end
|
||||
local ok
|
||||
ok, err, _ = uv.fs_symlink(src_points_to, dst_path)
|
||||
if not ok then
|
||||
return err
|
||||
end
|
||||
|
||||
return nil
|
||||
else
|
||||
local ok, err, _ = uv.fs_copyfile(src_path, dst_path)
|
||||
if not ok then
|
||||
return err
|
||||
end
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function rm(path, ftype)
|
||||
if ftype == "directory" then
|
||||
local handle = uv.fs_scandir(path)
|
||||
while true do
|
||||
local next_fname, next_ftype = uv.fs_scandir_next(handle)
|
||||
if next_fname == nil then
|
||||
break
|
||||
end
|
||||
local err = rm(M.join_paths(path, next_fname), next_ftype)
|
||||
if err ~= nil then
|
||||
return err
|
||||
end
|
||||
end
|
||||
local ok, err, _ = uv.fs_rmdir(path)
|
||||
if not ok then
|
||||
return err
|
||||
end
|
||||
return nil
|
||||
else
|
||||
local ok, err, _ = uv.fs_unlink(path)
|
||||
if not ok then
|
||||
return err
|
||||
end
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function mv(src_path, dst_path, ftype)
|
||||
-- FIXME: This is a TOCTOU
|
||||
if uv.fs_access(dst_path, "W") then
|
||||
return string.format("'%s' already exists", dst_path)
|
||||
end
|
||||
local ok, err, err_type = uv.fs_rename(src_path, dst_path)
|
||||
|
||||
if not ok and err_type == "EXDEV" then
|
||||
err = cp(src_path, dst_path, ftype)
|
||||
if err ~= nil then
|
||||
return err
|
||||
end
|
||||
err = rm(src_path, ftype)
|
||||
if err ~= nil then
|
||||
return err
|
||||
end
|
||||
elseif not ok then
|
||||
return err
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local function is_child_of(maybe_child, parent)
|
||||
local exact_match = maybe_child == parent
|
||||
local child_match = vim.startswith(maybe_child, parent .. M.path_separator)
|
||||
return exact_match or child_match
|
||||
end
|
||||
|
||||
-- `rename_loaded_buffers` finds all renamed buffers under `old_path` and
|
||||
-- renames them to be under `new_path`.
|
||||
local function rename_loaded_buffers(old_path, new_path)
|
||||
for _, buf in ipairs(api.nvim_list_bufs()) do
|
||||
if not api.nvim_buf_is_loaded(buf) then
|
||||
goto continue
|
||||
end
|
||||
|
||||
-- api.nvim_buf_get_name() returns absolute path so no post-processing
|
||||
local buf_name = api.nvim_buf_get_name(buf)
|
||||
if is_child_of(buf_name, old_path) then
|
||||
api.nvim_buf_set_name(buf, new_path .. buf_name:sub(#old_path + 1))
|
||||
|
||||
-- We have to :write! normal files to avoid `E13: File exists (add ! to
|
||||
-- override)` error when manually calling :write
|
||||
if api.nvim_buf_get_option(buf, "buftype") == "" then
|
||||
api.nvim_buf_call(buf, function()
|
||||
vim.cmd("silent! write!")
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
::continue::
|
||||
end
|
||||
end
|
||||
|
||||
-- `delete_loaded_buffers` finds all deleted buffers under `path` and replaces
|
||||
-- them with their alternate buffer, or a [No Name] buffer if its alternate
|
||||
-- buffer doesn't exist.
|
||||
local function delete_loaded_buffers(path)
|
||||
for _, buf in ipairs(api.nvim_list_bufs()) do
|
||||
if not api.nvim_buf_is_loaded(buf) then
|
||||
goto continue
|
||||
end
|
||||
|
||||
-- api.nvim_buf_get_name() returns absolute path so no post-processing
|
||||
local buf_name = api.nvim_buf_get_name(buf)
|
||||
if is_child_of(buf_name, path) then
|
||||
for _, win in ipairs(vim.fn.win_findbuf(buf)) do
|
||||
api.nvim_win_call(win, function()
|
||||
local altbuf = vim.fn.bufnr("#")
|
||||
if api.nvim_buf_is_valid(altbuf) then
|
||||
api.nvim_win_set_buf(win, altbuf)
|
||||
else
|
||||
vim.cmd("enew!")
|
||||
end
|
||||
end)
|
||||
end
|
||||
api.nvim_buf_delete(buf, { force = true })
|
||||
end
|
||||
|
||||
::continue::
|
||||
end
|
||||
end
|
||||
|
||||
function M.plan.create(fs_entry)
|
||||
return { type = "create", fs_entry = fs_entry }
|
||||
end
|
||||
|
||||
function M.actions.create(args)
|
||||
local fs_entry = args.fs_entry
|
||||
|
||||
-- FIXME: This is a TOCTOU
|
||||
if uv.fs_access(fs_entry.path, "W") then
|
||||
return string.format("'%s' already exists", fs_entry.ftype, fs_entry.path)
|
||||
end
|
||||
|
||||
if fs_entry.ftype == "file" then
|
||||
local fd, err = uv.fs_open(fs_entry.path, "w", DEFAULT_FILE_MODE)
|
||||
if fd == nil then
|
||||
return err
|
||||
end
|
||||
local ok
|
||||
ok, err = uv.fs_close(fd)
|
||||
if not ok then
|
||||
return err
|
||||
end
|
||||
elseif fs_entry.ftype == "directory" then
|
||||
local ok, err = uv.fs_mkdir(fs_entry.path, DEFAULT_DIR_MODE)
|
||||
if not ok then
|
||||
return err
|
||||
end
|
||||
else
|
||||
return string.format("Cannot create %s", fs_entry.ftype)
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
function M.plan.copy(src_fs_entry, dst_fs_entry)
|
||||
return { type = "copy", src_fs_entry = src_fs_entry, dst_fs_entry = dst_fs_entry }
|
||||
end
|
||||
|
||||
function M.actions.copy(args)
|
||||
local src_fs_entry, dst_fs_entry = args.src_fs_entry, args.dst_fs_entry
|
||||
-- planner ensures src and dst have same ftype
|
||||
return cp(src_fs_entry.path, dst_fs_entry.path, src_fs_entry.ftype)
|
||||
end
|
||||
|
||||
function M.plan.delete(fs_entry)
|
||||
return { type = "delete", fs_entry = fs_entry }
|
||||
end
|
||||
|
||||
function M.actions.delete(args)
|
||||
local fs_entry = args.fs_entry
|
||||
local err = rm(fs_entry.path, fs_entry.ftype)
|
||||
if err ~= nil then
|
||||
return string.format("Delete %s: %s", fs_entry.path, err)
|
||||
end
|
||||
|
||||
delete_loaded_buffers(fs_entry.path)
|
||||
return nil
|
||||
end
|
||||
|
||||
function M.plan.move(src_fs_entry, dst_fs_entry)
|
||||
return { type = "move", src_fs_entry = src_fs_entry, dst_fs_entry = dst_fs_entry }
|
||||
end
|
||||
|
||||
function M.actions.move(args)
|
||||
local src_fs_entry, dst_fs_entry = args.src_fs_entry, args.dst_fs_entry
|
||||
-- planner ensures src and dst have same ftype
|
||||
local err = mv(src_fs_entry.path, dst_fs_entry.path, src_fs_entry.ftype)
|
||||
if err ~= nil then
|
||||
return string.format("Move failed for %s -> %s: %s", src_fs_entry.path, dst_fs_entry.path, err)
|
||||
end
|
||||
|
||||
rename_loaded_buffers(src_fs_entry.path, dst_fs_entry.path)
|
||||
return nil
|
||||
end
|
||||
|
||||
return M
|
|
@ -0,0 +1,213 @@
|
|||
local buffer = require("dirbuf.buffer")
|
||||
local fs = require("dirbuf.fs")
|
||||
|
||||
local FSEntry = fs.FSEntry
|
||||
local create, copy, delete, move = fs.plan.create, fs.plan.copy, fs.plan.delete, fs.plan.move
|
||||
|
||||
local M = {}
|
||||
|
||||
--[[
|
||||
local record Changes
|
||||
new_files: {FSEntry},
|
||||
change_map: {string: Change},
|
||||
}
|
||||
local record Change
|
||||
{FSEntry} -- dst_fs_entries
|
||||
current_fs_entry: FSEntry
|
||||
stays: bool
|
||||
progress: Progress
|
||||
end
|
||||
local enum Progress
|
||||
"unhandled"
|
||||
"handling"
|
||||
"handled"
|
||||
end
|
||||
--]]
|
||||
|
||||
-- `build_changes` creates a diff between the snapshotted state of the
|
||||
-- directory buffer `dirbuf` and the updated state of the directory buffer
|
||||
-- `lines`.
|
||||
--
|
||||
-- TODO: It's kinda gross that I just store `lines` because then I have to deal
|
||||
-- with parsing here, but I'm not sure of a better way to do it
|
||||
--
|
||||
-- Returns: err, changes
|
||||
function M.build_changes(dir, fs_entries, lines)
|
||||
local new_files = {}
|
||||
local change_map = {}
|
||||
for _, fs_entry in pairs(fs_entries) do
|
||||
change_map[fs_entry.fname] = {
|
||||
current_fs_entry = fs_entry,
|
||||
stays = false,
|
||||
handled = false,
|
||||
}
|
||||
end
|
||||
|
||||
-- No duplicate fnames
|
||||
local used_fnames = {}
|
||||
for lnum, line in ipairs(lines) do
|
||||
local err, hash, fname, ftype = buffer.parse_line(line)
|
||||
if err ~= nil then
|
||||
return string.format("Line %d: %s", lnum, err)
|
||||
end
|
||||
if fname == nil then
|
||||
goto continue
|
||||
end
|
||||
|
||||
if used_fnames[fname] ~= nil then
|
||||
return string.format("Line %d: Duplicate name '%s'", lnum, fname)
|
||||
end
|
||||
|
||||
local dst_fs_entry = FSEntry.new(fname, dir, ftype)
|
||||
|
||||
if hash == nil then
|
||||
table.insert(new_files, dst_fs_entry)
|
||||
else
|
||||
local current_fs_entry = fs_entries[hash]
|
||||
if current_fs_entry.ftype ~= dst_fs_entry.ftype then
|
||||
return string.format("line %d: cannot change %s -> %s", lnum, current_fs_entry.ftype, dst_fs_entry.ftype)
|
||||
end
|
||||
|
||||
if current_fs_entry.fname == dst_fs_entry.fname then
|
||||
change_map[current_fs_entry.fname].stays = true
|
||||
else
|
||||
table.insert(change_map[current_fs_entry.fname], dst_fs_entry)
|
||||
end
|
||||
end
|
||||
used_fnames[dst_fs_entry.fname] = true
|
||||
|
||||
::continue::
|
||||
end
|
||||
|
||||
return nil, { change_map = change_map, new_files = new_files }
|
||||
end
|
||||
|
||||
-- TODO: Currently we don't always find the optimal unsticking point
|
||||
-- Also, sorry this is hard to read...
|
||||
local function resolve_change(plan, change_map, change)
|
||||
if change.progress == "handled" then
|
||||
return
|
||||
elseif change.progress == "handling" then
|
||||
error("unhandled cycle detected")
|
||||
end
|
||||
|
||||
change.progress = "handling"
|
||||
|
||||
-- If there's a cycle, we need to "unstick" it by moving one file to a
|
||||
-- temporary location. However, we need to remember to move that temporary
|
||||
-- file back to where we want after everything else in the cycle has been
|
||||
-- resolved.
|
||||
--
|
||||
-- It's not obvious that we can get away with only returning one action.
|
||||
-- However, due to our guarantee that the `Changes` we're given only use each
|
||||
-- `fname` once (i.e. the max in-degree of the graph of filename changes is
|
||||
-- 1), we know that we can only ever have one cycle from any given starting
|
||||
-- point.
|
||||
local post_resolution_action = nil
|
||||
|
||||
-- If the file doesn't stay, we prevent an extra copy by moving the file
|
||||
-- as the last change. We arbitrarily pick the first file to move it after
|
||||
-- everything
|
||||
local move_to = nil
|
||||
local stuck_fs_entry = nil
|
||||
for _, dst_fs_entry in ipairs(change) do
|
||||
local dependent_change = change_map[dst_fs_entry.fname]
|
||||
if dependent_change ~= nil then
|
||||
if dependent_change.progress == "handling" then
|
||||
-- We have a cycle, we need to unstick it
|
||||
if stuck_fs_entry ~= nil then
|
||||
error("my assumption about `stuck_change` was wrong")
|
||||
end
|
||||
-- We handle this later
|
||||
stuck_fs_entry = dst_fs_entry
|
||||
goto continue
|
||||
else
|
||||
-- We can handle the dependent_change directly
|
||||
-- Double check that my assumption holds
|
||||
local rtn = resolve_change(plan, change_map, dependent_change)
|
||||
if rtn ~= nil and post_resolution_action ~= nil then
|
||||
error("my assumption about `post_resolution_action` was wrong")
|
||||
end
|
||||
post_resolution_action = rtn
|
||||
end
|
||||
end
|
||||
|
||||
if not change.stays and move_to == nil then
|
||||
move_to = dst_fs_entry
|
||||
else
|
||||
table.insert(plan, copy(change.current_fs_entry, dst_fs_entry))
|
||||
end
|
||||
|
||||
::continue::
|
||||
end
|
||||
|
||||
local gone = false
|
||||
if move_to ~= nil then
|
||||
table.insert(plan, move(change.current_fs_entry, move_to))
|
||||
gone = true
|
||||
end
|
||||
|
||||
if stuck_fs_entry ~= nil then
|
||||
if move_to ~= nil then
|
||||
-- We have a safe place to copy from
|
||||
post_resolution_action = copy(move_to, stuck_fs_entry)
|
||||
elseif change.stays then
|
||||
-- We have a safe place to copy from
|
||||
post_resolution_action = copy(change.current_fs_entry, stuck_fs_entry)
|
||||
else
|
||||
-- We have NO safe place to copy from and we don't stay, so move to a
|
||||
-- temporary and then move again
|
||||
local temp_fs_entry = FSEntry.temp(change.current_fs_entry.ftype)
|
||||
table.insert(plan, move(change.current_fs_entry, temp_fs_entry))
|
||||
post_resolution_action = move(temp_fs_entry, stuck_fs_entry)
|
||||
gone = true
|
||||
end
|
||||
end
|
||||
|
||||
-- The file gets deleted and we never moved it, so we have to directly delete
|
||||
-- it
|
||||
if not change.stays and not gone then
|
||||
table.insert(plan, delete(change.current_fs_entry))
|
||||
end
|
||||
|
||||
change.progress = "handled"
|
||||
return post_resolution_action
|
||||
end
|
||||
|
||||
-- `determine_plan` finds the most efficient sequence of actions necessary to
|
||||
-- apply the set of validated changes we have `changes`.
|
||||
--
|
||||
-- Returns: list of actions as in fs.plan
|
||||
function M.determine_plan(changes)
|
||||
local plan = {}
|
||||
|
||||
for _, change in pairs(changes.change_map) do
|
||||
local extra_action = resolve_change(plan, changes.change_map, change)
|
||||
if extra_action ~= nil then
|
||||
table.insert(plan, extra_action)
|
||||
end
|
||||
end
|
||||
|
||||
for _, fs_entry in ipairs(changes.new_files) do
|
||||
table.insert(plan, create(fs_entry))
|
||||
end
|
||||
|
||||
return plan
|
||||
end
|
||||
|
||||
-- `execute_plan` executes the plan (i.e. sequence of actions) as created by
|
||||
-- `determine_plan` using the `fs.actions` action handlers.
|
||||
--
|
||||
-- Returns: err
|
||||
function M.execute_plan(plan)
|
||||
-- TODO: Make this async
|
||||
for _, action in ipairs(plan) do
|
||||
local err = fs.actions[action.type](action)
|
||||
if err ~= nil then
|
||||
return err
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
return M
|
|
@ -0,0 +1,37 @@
|
|||
if exists('g:loaded_dirbuf')
|
||||
finish
|
||||
endif
|
||||
|
||||
command! -nargs=? -complete=dir Dirbuf lua require'dirbuf'.open(<q-args>)
|
||||
command! DirbufQuit lua require'dirbuf'.quit()
|
||||
command! -nargs=? -complete=customlist,s:DirbufSyncOptions DirbufSync lua require'dirbuf'.sync(<q-args>)
|
||||
|
||||
function! s:DirbufSyncOptions(arg_lead, cmd_line, cursor_pos)
|
||||
let options = ['-confirm', '-dry-run']
|
||||
return filter(options, 'v:val =~ "^'.a:arg_lead.'"')
|
||||
endfunction
|
||||
|
||||
" This (dirbuf_up) mapping was taken from vim-dirvish
|
||||
noremap <unique> <Plug>(dirbuf_up) <cmd>execute 'Dirbuf %:p'.repeat(':h', v:count1 + isdirectory(expand('%')))<cr>
|
||||
noremap <unique> <Plug>(dirbuf_enter) <cmd>execute 'lua require"dirbuf".enter()'<cr>
|
||||
noremap <unique> <Plug>(dirbuf_toggle_hide) <cmd>execute 'lua require"dirbuf".toggle_hide()'<cr>
|
||||
noremap <unique> <Plug>(dirbuf_history_forward) <cmd>execute 'lua require"dirbuf".jump_history('v:count1')'<cr>
|
||||
noremap <unique> <Plug>(dirbuf_history_backward) <cmd>execute 'lua require"dirbuf".jump_history(-'v:count1')'<cr>
|
||||
|
||||
if mapcheck('-', 'n') ==# '' && !hasmapto('<Plug>(dirbuf_up)', 'n')
|
||||
nmap - <Plug>(dirbuf_up)
|
||||
endif
|
||||
|
||||
augroup dirbuf
|
||||
autocmd!
|
||||
" Makes editing a directory open a dirbuf. We always re-init the dirbuf
|
||||
autocmd BufEnter * if isdirectory(expand('%')) && !&modified
|
||||
\ | execute 'lua require"dirbuf".init_dirbuf(vim.b.dirbuf_history, vim.b.dirbuf_history_index, true)'
|
||||
\ | endif
|
||||
" Netrw hijacking for vim-plug and &rtp friends
|
||||
autocmd VimEnter * if exists('#FileExplorer') | execute 'autocmd! FileExplorer *' | endif
|
||||
augroup END
|
||||
" Netrw hijacking for packer and packages friends
|
||||
if exists('#FileExplorer') | execute 'autocmd! FileExplorer *' | endif
|
||||
|
||||
let g:loaded_dirbuf = 1
|
|
@ -0,0 +1,2 @@
|
|||
indent_type = "Spaces"
|
||||
indent_width = 2
|
|
@ -0,0 +1,64 @@
|
|||
" # Regex Breakdown
|
||||
"
|
||||
" /^\([^\\\t]\|\\[\\t]\)\+$/
|
||||
" ^^(a)^^ ^^(b)^^ (c)
|
||||
" (a): all valid single-characters (i.e. not tabs or escape sequences).
|
||||
" (b): all valid escape sequences.
|
||||
" (c): suffix + $ (end of line)
|
||||
"
|
||||
" The longest regex is the one highlighted, so the suffix always controls the
|
||||
" color. We include `me=e-suffix_len` to set the 'match end' to be one before
|
||||
" the normal 'end' so the suffix doesn't get highlighted.
|
||||
"
|
||||
" The suffixes are taken from `ls --classify` and zsh's tab completion.
|
||||
function! s:SetMatch(group_name, suffix, suffix_len)
|
||||
execute 'syntax match 'a:group_name.' /\([^\\\t]\|\\[\\nt]\)\+'.a:suffix.'$/me=e-'.a:suffix_len
|
||||
endfunction
|
||||
call s:SetMatch('DirbufFile', '', 0)
|
||||
call s:SetMatch('DirbufDirectory', '[/\\]', 1)
|
||||
call s:SetMatch('DirbufLink', '@', 1)
|
||||
call s:SetMatch('DirbufFifo', '|', 1)
|
||||
call s:SetMatch('DirbufSocket', '=', 1)
|
||||
call s:SetMatch('DirbufChar', '%', 1)
|
||||
call s:SetMatch('DirbufBlock', '\\$', 1)
|
||||
|
||||
" We include `ms=s-1` to not highlight the tab
|
||||
syntax match DirbufHash /^#\x\{8}\t/ms=s-1
|
||||
|
||||
" /^\(\(The_Regular_Expression\)\@!.\)*$/
|
||||
" Finds every except for the regular expression
|
||||
" See: https://vim.fandom.com/wiki/Search_for_lines_not_containing_pattern_and_other_helpful_searches#Searching_with_.2F
|
||||
syntax match DirbufMalformedLine /^\(\(\_^\(#\x\{8}\t\)\?\([^\\\t]\|\\[\\nt]\)\+\\\?\_$\)\@!.\)*$/
|
||||
|
||||
" Highlight each object according to its color in by ls --color=always. This
|
||||
" fallback system was taken and modified from nvim-tree.lua's colors.lua
|
||||
function! s:SetColor(group_name, color_num, fallback_group, fallback_color)
|
||||
if exists('g:terminal_color_'.a:color_num)
|
||||
let l:color = get(g:, 'terminal_color_'.a:color_num)
|
||||
execute 'highlight '.a:group_name.' ctermfg='.a:color_num.' gui=bold guifg='.l:color
|
||||
return
|
||||
endif
|
||||
let l:id = v:lua.vim.api.nvim_get_hl_id_by_name(a:fallback_group)
|
||||
let l:foreground = synIDattr(synIDtrans(id), "fg")
|
||||
if l:foreground !=# ''
|
||||
execute 'highlight '.a:group_name.' ctermfg='.a:color_num.' gui=bold guifg='.l:foreground
|
||||
else
|
||||
execute 'highlight '.a:group_name.' ctermfg='.a:color_num.' gui=bold guifg='.a:fallback_color
|
||||
endif
|
||||
endfunction
|
||||
|
||||
highlight link DifbufFile Normal
|
||||
if exists('g:terminal_color_4')
|
||||
execute 'highlight DirbufDirectory ctermfg=4 gui=bold guifg='.g:terminal_color_4
|
||||
else
|
||||
highlight link DirbufDirectory Directory
|
||||
endif
|
||||
call s:SetColor('DirbufLink', 6, 'Conditional', 'Cyan')
|
||||
call s:SetColor('DirbufFifo', 2, 'Character', 'Green')
|
||||
call s:SetColor('DirbufSocket', 5, 'Define', 'Purple')
|
||||
call s:SetColor('DirbufChar', 3, 'PreProc', 'Yellow')
|
||||
call s:SetColor('DirbufBlock', 3, 'PreProc', 'Yellow')
|
||||
|
||||
highlight link DirbufHash Special
|
||||
|
||||
highlight link DirbufMalformedLine Error
|
|
@ -0,0 +1,220 @@
|
|||
local buffer = require("dirbuf.buffer")
|
||||
local fs = require("dirbuf.fs")
|
||||
|
||||
local function entry(fname, ftype)
|
||||
return fs.FSEntry.new(fname, "", ftype or "file")
|
||||
end
|
||||
|
||||
describe("parse_line", function()
|
||||
local function expect_parse(line, expected)
|
||||
local err, hash, fname, ftype = buffer.parse_line(line)
|
||||
if expected.err then
|
||||
assert.is_not_nil(err)
|
||||
else
|
||||
assert.is_nil(err)
|
||||
end
|
||||
assert.equal(expected.hash, hash, "hash")
|
||||
assert.equal(expected.fname, fname, "fname")
|
||||
assert.equal(expected.ftype, ftype, "ftype")
|
||||
end
|
||||
|
||||
local function test_suffix(expected_ftype, suffix)
|
||||
it("ftype " .. expected_ftype .. suffix, function()
|
||||
expect_parse("#0000000a\tfoo" .. suffix, {
|
||||
err = false,
|
||||
hash = 10,
|
||||
fname = "foo",
|
||||
ftype = expected_ftype,
|
||||
})
|
||||
end)
|
||||
end
|
||||
|
||||
test_suffix("file", "")
|
||||
test_suffix("directory", "/")
|
||||
test_suffix("directory", "\\")
|
||||
test_suffix("link", "@")
|
||||
test_suffix("fifo", "|")
|
||||
test_suffix("socket", "=")
|
||||
test_suffix("char", "%")
|
||||
test_suffix("block", "#")
|
||||
|
||||
it("interior @", function()
|
||||
expect_parse([[#0000000a foo@bar]], {
|
||||
err = false,
|
||||
hash = 10,
|
||||
fname = "foo@bar",
|
||||
ftype = "file",
|
||||
})
|
||||
end)
|
||||
|
||||
it("interior /", function()
|
||||
expect_parse([[#0000000a foo/bar]], {
|
||||
err = false,
|
||||
hash = 10,
|
||||
fname = "foo/bar",
|
||||
ftype = "file",
|
||||
})
|
||||
end)
|
||||
|
||||
it("fname is @", function()
|
||||
expect_parse([[@]], {
|
||||
err = false,
|
||||
hash = nil,
|
||||
fname = "@",
|
||||
ftype = "file",
|
||||
})
|
||||
end)
|
||||
|
||||
it("only fname", function()
|
||||
expect_parse([[foo]], {
|
||||
err = false,
|
||||
hash = nil,
|
||||
fname = "foo",
|
||||
ftype = "file",
|
||||
})
|
||||
end)
|
||||
|
||||
it("only hash", function()
|
||||
expect_parse([[#0000000a]], {
|
||||
err = false,
|
||||
hash = nil,
|
||||
fname = "#0000000a",
|
||||
ftype = "file",
|
||||
})
|
||||
end)
|
||||
|
||||
it("spaces", function()
|
||||
expect_parse([[#0000000a a b c ]], {
|
||||
err = false,
|
||||
hash = 10,
|
||||
fname = " a b c ",
|
||||
ftype = "file",
|
||||
})
|
||||
end)
|
||||
|
||||
it("escaped tab", function()
|
||||
expect_parse([[#0000000a before\tafter]], {
|
||||
err = false,
|
||||
hash = 10,
|
||||
fname = [[before after]],
|
||||
ftype = "file",
|
||||
})
|
||||
end)
|
||||
|
||||
it("escaped backslash", function()
|
||||
expect_parse([[#0000000a before\\after]], {
|
||||
err = false,
|
||||
hash = 10,
|
||||
fname = [[before\after]],
|
||||
ftype = "file",
|
||||
})
|
||||
end)
|
||||
|
||||
it("escaped backslash end", function()
|
||||
expect_parse([[#0000000a foo\\]], {
|
||||
err = false,
|
||||
hash = 10,
|
||||
fname = [[foo\]],
|
||||
ftype = "file",
|
||||
})
|
||||
end)
|
||||
|
||||
it("unescaped tab", function()
|
||||
expect_parse([[#0000000a foo bar]], {
|
||||
err = true,
|
||||
hash = nil,
|
||||
fname = nil,
|
||||
ftype = nil,
|
||||
})
|
||||
end)
|
||||
|
||||
it("invalid escape sequence", function()
|
||||
expect_parse([[#0000000a \y]], {
|
||||
err = true,
|
||||
hash = nil,
|
||||
fname = nil,
|
||||
ftype = nil,
|
||||
})
|
||||
end)
|
||||
|
||||
it("short hash", function()
|
||||
expect_parse([[#0123456 foo]], {
|
||||
err = true,
|
||||
hash = nil,
|
||||
fname = nil,
|
||||
ftype = nil,
|
||||
})
|
||||
end)
|
||||
|
||||
it("long hash", function()
|
||||
expect_parse([[#012345678 foo]], {
|
||||
err = true,
|
||||
hash = nil,
|
||||
fname = nil,
|
||||
ftype = nil,
|
||||
})
|
||||
end)
|
||||
|
||||
it("invalid hex character hash", function()
|
||||
expect_parse([[#0123456z foo]], {
|
||||
err = true,
|
||||
hash = nil,
|
||||
fname = nil,
|
||||
ftype = nil,
|
||||
})
|
||||
end)
|
||||
|
||||
it("trailing spaces no hash", function()
|
||||
expect_parse([[foo ]], {
|
||||
err = false,
|
||||
hash = nil,
|
||||
fname = "foo ",
|
||||
ftype = "file",
|
||||
})
|
||||
end)
|
||||
|
||||
it("non-ASCII fname", function()
|
||||
expect_parse([[#0000000a 文档]], {
|
||||
err = false,
|
||||
hash = 10,
|
||||
fname = "文档",
|
||||
ftype = "file",
|
||||
})
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("write_fs_entries", function()
|
||||
it("types", function()
|
||||
local fs_entries = {
|
||||
entry("file", "file"),
|
||||
entry("directory", "directory"),
|
||||
entry("link", "link"),
|
||||
entry("fifo", "fifo"),
|
||||
entry("socket", "socket"),
|
||||
entry("char", "char"),
|
||||
entry("block", "block"),
|
||||
}
|
||||
local buf_lines, _ = buffer.write_fs_entries(fs_entries)
|
||||
assert.same({
|
||||
"#00000001 file",
|
||||
"#00000002 directory/",
|
||||
"#00000003 link@",
|
||||
"#00000004 fifo|",
|
||||
"#00000005 socket=",
|
||||
"#00000006 char%",
|
||||
"#00000007 block#",
|
||||
}, buf_lines)
|
||||
end)
|
||||
|
||||
it("escape characters", function()
|
||||
local fs_entries = { entry("a\\\t") }
|
||||
local buf_lines, _ = buffer.write_fs_entries(fs_entries)
|
||||
assert.same({ [[#00000001 a\\\t]] }, buf_lines)
|
||||
end)
|
||||
|
||||
it("track_fname", function()
|
||||
local fs_entries = { entry("a"), entry("b"), entry("c") }
|
||||
local _, fname_line = buffer.write_fs_entries(fs_entries, "b")
|
||||
assert.equal(2, fname_line)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,37 @@
|
|||
local config = require("dirbuf.config")
|
||||
|
||||
describe("update", function()
|
||||
it("legal", function()
|
||||
local errors = config.update({
|
||||
hash_padding = 3,
|
||||
show_hidden = false,
|
||||
sort_order = "directories_first",
|
||||
})
|
||||
assert.equal(0, #errors)
|
||||
assert.equal(3, config.get("hash_padding"))
|
||||
assert.equal(false, config.get("show_hidden"))
|
||||
end)
|
||||
|
||||
it("illegal", function()
|
||||
local errors = config.update({
|
||||
hash_padding = -1,
|
||||
show_hidden = "foo",
|
||||
sort_order = {},
|
||||
unknown = true,
|
||||
})
|
||||
assert.equal(4, #errors)
|
||||
end)
|
||||
|
||||
it("set then unset", function()
|
||||
config.update({ hash_padding = 3 })
|
||||
assert.equal(3, config.get("hash_padding"))
|
||||
config.update({})
|
||||
assert.equal(2, config.get("hash_padding"))
|
||||
end)
|
||||
|
||||
it("unknown option", function()
|
||||
assert.errors(function()
|
||||
config.get("unknown")
|
||||
end)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,108 @@
|
|||
local api = vim.api
|
||||
local uv = vim.loop
|
||||
|
||||
local function scan_directory(path)
|
||||
local directory = {}
|
||||
local handle = assert(uv.fs_scandir(path))
|
||||
while true do
|
||||
local fname, ftype = uv.fs_scandir_next(handle)
|
||||
if fname == nil then
|
||||
break
|
||||
end
|
||||
directory[fname] = ftype
|
||||
end
|
||||
return directory
|
||||
end
|
||||
|
||||
local function lines()
|
||||
return api.nvim_buf_get_lines(0, 0, -1, true)
|
||||
end
|
||||
|
||||
local function expect_lines(expected)
|
||||
assert.same(expected, lines())
|
||||
end
|
||||
|
||||
local function expect_directory(expected)
|
||||
local path = vim.api.nvim_buf_get_name(0)
|
||||
assert.same(expected, scan_directory(path))
|
||||
end
|
||||
|
||||
local function open_dirbuf_of(directory)
|
||||
local path = assert(uv.fs_mkdtemp("/tmp/dirbuf-XXXXXX"))
|
||||
for fname, ftype in pairs(directory) do
|
||||
if ftype == "directory" then
|
||||
vim.fn.mkdir(path .. "/" .. fname)
|
||||
elseif ftype == "file" then
|
||||
vim.fn.writefile({ "file " .. fname }, path .. "/" .. fname)
|
||||
else
|
||||
error("unrecognized ftype: " .. ftype)
|
||||
end
|
||||
end
|
||||
vim.cmd("Dirbuf " .. vim.fn.fnameescape(path))
|
||||
end
|
||||
|
||||
-- TODO: Use feed
|
||||
local function feed(keys)
|
||||
vim.fn.feedkeys(keys, "x")
|
||||
end
|
||||
|
||||
describe("end-to-end", function()
|
||||
it("edits", function()
|
||||
open_dirbuf_of({ a = "file", b = "file", c = "directory" })
|
||||
expect_lines({ "#00000001\ta", "#00000002\tb", "#00000003\tc/" })
|
||||
vim.cmd("g/b/d")
|
||||
vim.cmd("s/c/d/")
|
||||
api.nvim_put({ "new file" }, "l", "p", true)
|
||||
api.nvim_put({ "new directory/" }, "l", "p", true)
|
||||
expect_lines({ "#00000001\ta", "#00000003\td/", "new file", "new directory/" })
|
||||
expect_directory({ a = "file", b = "file", c = "directory" })
|
||||
vim.cmd("DirbufSync")
|
||||
expect_lines({ "#00000001\ta", "#00000002\td/", "#00000003\tnew directory/", "#00000004\tnew file" })
|
||||
expect_directory({ a = "file", d = "directory", ["new file"] = "file", ["new directory"] = "directory" })
|
||||
end)
|
||||
|
||||
it("escape characters", function()
|
||||
open_dirbuf_of({ ["\\hello\n\t"] = "file", normal = "file" })
|
||||
expect_lines({ [[#00000001 \\hello\n\t]], [[#00000002 normal]] })
|
||||
vim.cmd("s/hello/goodbye/")
|
||||
vim.cmd("DirbufSync")
|
||||
expect_lines({ [[#00000001 \\goodbye\n\t]], [[#00000002 normal]] })
|
||||
end)
|
||||
|
||||
it("jump_history()", function()
|
||||
open_dirbuf_of({ a = "directory", b = "file" })
|
||||
expect_lines({ [[#00000001 a/]], [[#00000002 b]] })
|
||||
require("dirbuf").enter()
|
||||
expect_lines({ "" })
|
||||
require("dirbuf").jump_history(-1)
|
||||
expect_lines({ [[#00000001 a/]], [[#00000002 b]] })
|
||||
require("dirbuf").jump_history(1)
|
||||
expect_lines({ "" })
|
||||
end)
|
||||
|
||||
it(":DirbufSync -confirm smoke test", function()
|
||||
open_dirbuf_of({ a = "file", b = "file", c = "file" })
|
||||
expect_lines({ "#00000001\ta", "#00000002\tb", "#00000003\tc" })
|
||||
vim.cmd("g/b/d")
|
||||
expect_lines({ "#00000001\ta", "#00000003\tc" })
|
||||
expect_directory({ a = "file", b = "file", c = "file" })
|
||||
vim.cmd("DirbufSync -confirm")
|
||||
end)
|
||||
|
||||
it(":DirbufSync unrecognized option", function()
|
||||
assert.errors(function()
|
||||
vim.cmd("Dirbuf")
|
||||
vim.cmd("DirbufSync -some-fake-option")
|
||||
end)
|
||||
vim.cmd("bdelete!")
|
||||
end)
|
||||
|
||||
-- TODO: Figure out how to trigger
|
||||
-- https://github.com/elihunter173/dirbuf.nvim/issues/48 on commit e004455
|
||||
pending("works with autochdir", function()
|
||||
vim.opt.autochdir = true
|
||||
feed("-")
|
||||
assert.is_not.same({ "" }, lines())
|
||||
vim.opt.autochdir = false
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,187 @@
|
|||
local buffer = require("dirbuf.buffer")
|
||||
local fs = require("dirbuf.fs")
|
||||
local planner = require("dirbuf.planner")
|
||||
|
||||
local function mkplan(before, after)
|
||||
local fake_fs = {}
|
||||
local before_fs_entries = {}
|
||||
for _, line in ipairs(before) do
|
||||
local err, hash, fname, ftype = buffer.parse_line(line)
|
||||
assert(err == nil, err)
|
||||
fake_fs["/" .. fname] = fname
|
||||
before_fs_entries[hash] = fs.FSEntry.new(fname, "/", ftype)
|
||||
end
|
||||
|
||||
local err, changes = planner.build_changes("/", before_fs_entries, after)
|
||||
assert(err == nil, err)
|
||||
local plan = planner.determine_plan(changes)
|
||||
return fake_fs, plan
|
||||
end
|
||||
|
||||
local function apply_plan(fake_fs, plan)
|
||||
for _, action in ipairs(plan) do
|
||||
if action.type == "create" then
|
||||
fake_fs[action.fs_entry.path] = ""
|
||||
elseif action.type == "copy" then
|
||||
fake_fs[action.dst_fs_entry.path] = fake_fs[action.src_fs_entry.path]
|
||||
elseif action.type == "delete" then
|
||||
fake_fs[action.fs_entry.path] = nil
|
||||
elseif action.type == "move" then
|
||||
fake_fs[action.dst_fs_entry.path] = fake_fs[action.src_fs_entry.path]
|
||||
fake_fs[action.src_fs_entry.path] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function opcount(plan, op)
|
||||
local count = 0
|
||||
for _, action in ipairs(plan) do
|
||||
if action.type == op then
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
describe("determine_plan", function()
|
||||
it("no changes", function()
|
||||
local fake_fs, plan = mkplan({
|
||||
[[#0000000a a]],
|
||||
[[#0000000b b]],
|
||||
}, {
|
||||
[[#0000000a a]],
|
||||
[[#0000000b b]],
|
||||
})
|
||||
apply_plan(fake_fs, plan)
|
||||
assert.same({ ["/a"] = "a", ["/b"] = "b" }, fake_fs)
|
||||
assert.same(0, #plan)
|
||||
end)
|
||||
|
||||
it("reordering", function()
|
||||
local fake_fs, plan = mkplan({
|
||||
[[#0000000a a]],
|
||||
[[#0000000b b]],
|
||||
}, {
|
||||
[[#0000000b b]],
|
||||
[[#0000000a a]],
|
||||
})
|
||||
apply_plan(fake_fs, plan)
|
||||
assert.same({ ["/a"] = "a", ["/b"] = "b" }, fake_fs)
|
||||
assert.same(0, #plan)
|
||||
end)
|
||||
|
||||
it("rename", function()
|
||||
local fake_fs, plan = mkplan({
|
||||
[[#0000000a a]],
|
||||
[[#0000000b b]],
|
||||
}, {
|
||||
[[#0000000a c]],
|
||||
[[#0000000b b]],
|
||||
})
|
||||
apply_plan(fake_fs, plan)
|
||||
assert.same({ ["/c"] = "a", ["/b"] = "b" }, fake_fs)
|
||||
assert.same(1, #plan)
|
||||
end)
|
||||
|
||||
it("delete", function()
|
||||
local fake_fs, plan = mkplan({
|
||||
[[#0000000a a]],
|
||||
[[#0000000b b]],
|
||||
}, {
|
||||
[[#0000000b b]],
|
||||
})
|
||||
apply_plan(fake_fs, plan)
|
||||
assert.same({ ["/b"] = "b" }, fake_fs)
|
||||
assert.same(1, #plan)
|
||||
end)
|
||||
|
||||
it("create", function()
|
||||
local fake_fs, plan = mkplan({
|
||||
[[#0000000b b]],
|
||||
}, {
|
||||
[[a]],
|
||||
[[#0000000b b]],
|
||||
})
|
||||
apply_plan(fake_fs, plan)
|
||||
assert.same({ ["/a"] = "", ["/b"] = "b" }, fake_fs)
|
||||
assert.same(1, #plan)
|
||||
end)
|
||||
|
||||
it("copy", function()
|
||||
local fake_fs, plan = mkplan({
|
||||
[[#0000000a a]],
|
||||
[[#0000000b b]],
|
||||
}, {
|
||||
[[#0000000a a]],
|
||||
[[#0000000a c]],
|
||||
[[#0000000b b]],
|
||||
})
|
||||
apply_plan(fake_fs, plan)
|
||||
assert.same({ ["/a"] = "a", ["/b"] = "b", ["/c"] = "a" }, fake_fs)
|
||||
assert.same(1, #plan)
|
||||
end)
|
||||
|
||||
it("dependent rename", function()
|
||||
local fake_fs, plan = mkplan({
|
||||
[[#0000000a a]],
|
||||
[[#0000000b b]],
|
||||
}, {
|
||||
[[#0000000a b]],
|
||||
[[#0000000b c]],
|
||||
})
|
||||
apply_plan(fake_fs, plan)
|
||||
assert.same({ ["/b"] = "a", ["/c"] = "b" }, fake_fs)
|
||||
assert.same(2, #plan)
|
||||
assert.same(2, opcount(plan, "move"))
|
||||
end)
|
||||
|
||||
it("swap", function()
|
||||
local fake_fs, plan = mkplan({
|
||||
[[#0000000a a]],
|
||||
[[#0000000b b]],
|
||||
}, {
|
||||
[[#0000000a b]],
|
||||
[[#0000000b a]],
|
||||
})
|
||||
apply_plan(fake_fs, plan)
|
||||
assert.same({ ["/a"] = "b", ["/b"] = "a" }, fake_fs)
|
||||
assert.same(3, #plan)
|
||||
assert.same(3, opcount(plan, "move"))
|
||||
end)
|
||||
|
||||
-- FIXME: We skip the "efficient breakpoint" efficiency tests because Dirbuf
|
||||
-- sometimes misses efficient breakpoints. Dirbuf's solutions are always
|
||||
-- correct but not always optimal.
|
||||
it("swap with efficient breakpoint", function()
|
||||
local fake_fs, plan = mkplan({
|
||||
[[#0000000a a]],
|
||||
[[#0000000b b]],
|
||||
}, {
|
||||
[[#0000000a b]],
|
||||
[[#0000000b a]],
|
||||
[[#0000000b c]],
|
||||
})
|
||||
apply_plan(fake_fs, plan)
|
||||
assert.same({ ["/a"] = "b", ["/b"] = "a", ["/c"] = "b" }, fake_fs)
|
||||
-- assert.same(3, #plan)
|
||||
-- assert.same(3, opcount(plan, "move"))
|
||||
end)
|
||||
|
||||
it("cycle with efficient breakpoint", function()
|
||||
local fake_fs, plan = mkplan({
|
||||
[[#0000000a a]],
|
||||
[[#0000000b b]],
|
||||
[[#0000000c c]],
|
||||
}, {
|
||||
[[#0000000a b]],
|
||||
[[#0000000b c]],
|
||||
[[#0000000b d]],
|
||||
[[#0000000c a]],
|
||||
})
|
||||
apply_plan(fake_fs, plan)
|
||||
assert.same({ ["/a"] = "c", ["/b"] = "a", ["/c"] = "b", ["/d"] = "b" }, fake_fs)
|
||||
-- assert.same(4, #plan)
|
||||
-- assert.same(3, opcount(plan, "move"))
|
||||
-- assert.same(1, opcount(plan, "copy"))
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,8 @@
|
|||
if !isdirectory('plenary.nvim')
|
||||
!git clone https://github.com/nvim-lua/plenary.nvim.git plenary.nvim
|
||||
!git -C plenary.nvim reset --hard 1338bbe8ec6503ca1517059c52364ebf95951458
|
||||
endif
|
||||
set runtimepath+=plenary.nvim,.
|
||||
runtime plugin/plenary.vim
|
||||
try | runtime plugin/dirbuf.vim | catch | cquit! 173 | endtry
|
||||
command Test PlenaryBustedDirectory tests/ {minimal_init = 'tests/test_init.vim'}
|
|
@ -0,0 +1,319 @@
|
|||
# filetype.nvim
|
||||
|
||||
Easily speed up your neovim startup time!
|
||||
|
||||
|
||||
## What does this do?
|
||||
|
||||
This plugin is a replacement for the included `filetype.vim` that is sourced on startup.
|
||||
The purpose of that file is to create a series of autocommands that set the `filetype` variable
|
||||
depending on the filename. The issue is that creating autocommands have significant overhead, and
|
||||
creating [800+ of them](https://github.com/vim/vim/blob/master/runtime/filetype.vim) as `filetype.vim` does is a very inefficient way to get the job done.
|
||||
|
||||
As you can see, `filetype.vim` is by far the heaviest nvim runtime file
|
||||
|
||||
```diff
|
||||
13.782 [runtime]
|
||||
- 9.144 /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/filetype.vim
|
||||
1.662 /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/matchit.vim
|
||||
0.459 /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/syntax/synload.vim
|
||||
0.388 /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/netrwPlugin.vim
|
||||
0.334 /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/gzip.vim
|
||||
0.251 /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/rplugin.vim
|
||||
0.248 /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/syntax/syntax.vim
|
||||
0.216 /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/tarPlugin.vim
|
||||
0.205 /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/zipPlugin.vim
|
||||
0.186 /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/syntax/syncolor.vim
|
||||
0.173 /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/matchparen.vim
|
||||
0.123 /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/shada.vim
|
||||
0.114 /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/tohtml.vim
|
||||
0.075 /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/man.vim
|
||||
0.056 /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/ftplugin.vim
|
||||
0.048 /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/indent.vim
|
||||
0.039 /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/spellfile.vim
|
||||
0.038 /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/tutor.vim
|
||||
0.022 /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/health.vim
|
||||
```
|
||||
|
||||
`filetype.nvim` fixes the issue by only creating a single autocommand that resolves the file type
|
||||
when a buffer is opened. This method is ~175x faster\*!
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
First, install using your favorite package manager. Using [packer](https://github.com/wbthomason/packer.nvim):
|
||||
|
||||
```lua
|
||||
use("nathom/filetype.nvim")
|
||||
```
|
||||
|
||||
If using a Neovim version earlier than 0.6.0, add the following to `init.lua`
|
||||
|
||||
```lua
|
||||
-- Do not source the default filetype.vim
|
||||
vim.g.did_load_filetypes = 1
|
||||
```
|
||||
|
||||
That's it! You should now have a much snappier neovim experience!
|
||||
|
||||
## Customization
|
||||
|
||||
`filetype.nvim` allows you to easily add custom filetypes using the `setup` function. Here's an example:
|
||||
|
||||
```lua
|
||||
-- In init.lua or filetype.nvim's config file
|
||||
require("filetype").setup({
|
||||
overrides = {
|
||||
extensions = {
|
||||
-- Set the filetype of *.pn files to potion
|
||||
pn = "potion",
|
||||
},
|
||||
literal = {
|
||||
-- Set the filetype of files named "MyBackupFile" to lua
|
||||
MyBackupFile = "lua",
|
||||
},
|
||||
complex = {
|
||||
-- Set the filetype of any full filename matching the regex to gitconfig
|
||||
[".*git/config"] = "gitconfig", -- Included in the plugin
|
||||
},
|
||||
|
||||
-- The same as the ones above except the keys map to functions
|
||||
function_extensions = {
|
||||
["cpp"] = function()
|
||||
vim.bo.filetype = "cpp"
|
||||
-- Remove annoying indent jumping
|
||||
vim.bo.cinoptions = vim.bo.cinoptions .. "L0"
|
||||
end,
|
||||
["pdf"] = function()
|
||||
vim.bo.filetype = "pdf"
|
||||
-- Open in PDF viewer (Skim.app) automatically
|
||||
vim.fn.jobstart(
|
||||
"open -a skim " .. '"' .. vim.fn.expand("%") .. '"'
|
||||
)
|
||||
end,
|
||||
},
|
||||
function_literal = {
|
||||
Brewfile = function()
|
||||
vim.cmd("syntax off")
|
||||
end,
|
||||
},
|
||||
function_complex = {
|
||||
["*.math_notes/%w+"] = function()
|
||||
vim.cmd("iabbrev $ $$")
|
||||
end,
|
||||
},
|
||||
|
||||
shebang = {
|
||||
-- Set the filetype of files with a dash shebang to sh
|
||||
dash = "sh",
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
The `extensions` and `literal` tables are orders faster than the other ones
|
||||
because they only require a table lookup. Always try to use these before resorting
|
||||
to the `complex` tables, which require looping over the entries and running
|
||||
a regex for each one.
|
||||
|
||||
## Performance Comparison
|
||||
|
||||
**These were measured using [startuptime.vim](https://github.com/tweekmonster/startuptime.vim)**
|
||||
|
||||
### Without `filetype.nvim`
|
||||
|
||||
Average startup time (100 rounds): **36.410 ms**
|
||||
|
||||
<details>
|
||||
<summary>Sample log</summary>
|
||||
|
||||
```diff
|
||||
times in msec
|
||||
clock self+sourced self: sourced script
|
||||
clock elapsed: other lines
|
||||
|
||||
000.008 000.008: --- NVIM STARTING ---
|
||||
000.827 000.819: locale set
|
||||
001.304 000.477: inits 1
|
||||
001.358 000.054: window checked
|
||||
001.369 000.011: parsing arguments
|
||||
002.537 001.168: expanding arguments
|
||||
002.626 000.089: inits 2
|
||||
002.998 000.372: init highlight
|
||||
012.731 000.961 000.961: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/vim-gruvbox8/colors/gruvbox8.vim
|
||||
012.829 009.549 008.588: sourcing /Users/nathan/.config/nvim/init.lua
|
||||
012.837 000.290: sourcing vimrc file(s)
|
||||
019.775 000.035 000.035: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/elixir.vim
|
||||
019.867 000.026 000.026: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/fish.vim
|
||||
019.949 000.022 000.022: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/gdresource.vim
|
||||
020.025 000.017 000.017: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/gdscript.vim
|
||||
020.108 000.018 000.018: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/gomod.vim
|
||||
020.194 000.029 000.029: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/graphql.vim
|
||||
020.280 000.029 000.029: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/hcl.vim
|
||||
020.358 000.021 000.021: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/heex.vim
|
||||
020.436 000.021 000.021: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/json5.vim
|
||||
020.517 000.024 000.024: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/julia.vim
|
||||
020.601 000.028 000.028: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/ledger.vim
|
||||
020.680 000.022 000.022: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/nix.vim
|
||||
020.764 000.028 000.028: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/ql.vim
|
||||
020.851 000.031 000.031: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/query.vim
|
||||
020.933 000.025 000.025: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/surface.vim
|
||||
021.127 000.031 000.031: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/teal.vim
|
||||
021.218 000.025 000.025: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/tlaplus.vim
|
||||
021.301 000.023 000.023: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/yang.vim
|
||||
021.382 000.023 000.023: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/zig.vim
|
||||
- 022.213 009.200 008.722: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/filetype.vim
|
||||
022.820 000.046 000.046: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/ftplugin.vim
|
||||
023.350 000.042 000.042: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/indent.vim
|
||||
025.075 000.180 000.180: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/syntax/syncolor.vim
|
||||
026.263 001.786 001.606: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/vim-gruvbox8/colors/gruvbox8.vim
|
||||
026.338 002.204 000.418: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/syntax/synload.vim
|
||||
026.432 002.447 000.243: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/syntax/syntax.vim
|
||||
030.711 000.317 000.317: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/gzip.vim
|
||||
030.810 000.021 000.021: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/health.vim
|
||||
030.951 000.074 000.074: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/man.vim
|
||||
032.470 000.187 000.187: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/pack/dist/opt/matchit/plugin/matchit.vim
|
||||
032.781 001.760 001.573: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/matchit.vim
|
||||
033.095 000.240 000.240: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/matchparen.vim
|
||||
033.539 000.364 000.364: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/netrwPlugin.vim
|
||||
033.873 000.021 000.021: sourcing /Users/nathan/.local/share/nvim/rplugin.vim
|
||||
033.883 000.251 000.231: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/rplugin.vim
|
||||
034.065 000.106 000.106: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/shada.vim
|
||||
034.185 000.036 000.036: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/spellfile.vim
|
||||
034.472 000.205 000.205: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/tarPlugin.vim
|
||||
034.664 000.104 000.104: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/tohtml.vim
|
||||
034.781 000.034 000.034: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/tutor.vim
|
||||
035.048 000.178 000.178: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/zipPlugin.vim
|
||||
042.395 000.030 000.030: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/opt/vim-markdown/ftdetect/markdown.vim
|
||||
042.409 007.066 007.036: sourcing /Users/nathan/.config/nvim/plugin/packer_compiled.lua
|
||||
043.195 007.867: loading plugins
|
||||
043.813 000.037 000.037: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/easy-replace.nvim/plugin/easy_replace.vim
|
||||
044.564 000.032 000.032: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-bqf/plugin/bqf.vim
|
||||
046.955 001.984 001.984: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/plugin/nvim-treesitter.vim
|
||||
047.595 000.050 000.050: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/elixir.vim
|
||||
047.693 000.030 000.030: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/fish.vim
|
||||
047.851 000.092 000.092: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/gdresource.vim
|
||||
047.978 000.026 000.026: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/gdscript.vim
|
||||
048.082 000.026 000.026: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/gomod.vim
|
||||
048.183 000.031 000.031: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/graphql.vim
|
||||
048.284 000.031 000.031: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/hcl.vim
|
||||
048.378 000.024 000.024: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/heex.vim
|
||||
048.470 000.023 000.023: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/json5.vim
|
||||
048.562 000.022 000.022: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/julia.vim
|
||||
048.659 000.027 000.027: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/ledger.vim
|
||||
048.749 000.021 000.021: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/nix.vim
|
||||
048.842 000.024 000.024: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/ql.vim
|
||||
048.943 000.032 000.032: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/query.vim
|
||||
049.035 000.019 000.019: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/surface.vim
|
||||
049.115 000.018 000.018: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/teal.vim
|
||||
049.197 000.017 000.017: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/tlaplus.vim
|
||||
049.276 000.017 000.017: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/yang.vim
|
||||
049.390 000.017 000.017: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/zig.vim
|
||||
049.772 000.047 000.047: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-web-devicons/plugin/nvim-web-devicons.vim
|
||||
050.319 000.043 000.043: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/plenary.nvim/plugin/plenary.vim
|
||||
051.424 000.301 000.301: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/vim-rooter/plugin/rooter.vim
|
||||
051.751 005.565: loading packages
|
||||
052.307 000.556: loading after plugins
|
||||
052.316 000.010: inits 3
|
||||
052.328 000.012: clearing screen
|
||||
054.268 001.940: opening buffers
|
||||
054.539 000.271: BufEnter autocommands
|
||||
- 054.542 000.003: editing files in windows
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
### With `filetype.nvim`
|
||||
|
||||
Average startup time (100 rounds): **26.492 ms**
|
||||
|
||||
<details>
|
||||
<summary>Sample log</summary>
|
||||
|
||||
```diff
|
||||
times in msec
|
||||
clock self+sourced self: sourced script
|
||||
clock elapsed: other lines
|
||||
|
||||
000.008 000.008: --- NVIM STARTING ---
|
||||
000.813 000.805: locale set
|
||||
001.282 000.470: inits 1
|
||||
001.334 000.052: window checked
|
||||
001.345 000.011: parsing arguments
|
||||
002.386 001.041: expanding arguments
|
||||
002.459 000.073: inits 2
|
||||
002.859 000.400: init highlight
|
||||
013.346 001.066 001.066: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/vim-gruvbox8/colors/gruvbox8.vim
|
||||
013.471 010.343 009.276: sourcing /Users/nathan/.config/nvim/init.lua
|
||||
013.485 000.283: sourcing vimrc file(s)
|
||||
+ 013.666 000.025 000.025: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/filetype.vim
|
||||
014.360 000.057 000.057: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/ftplugin.vim
|
||||
014.993 000.043 000.043: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/indent.vim
|
||||
016.715 000.168 000.168: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/syntax/syncolor.vim
|
||||
017.849 001.667 001.499: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/vim-gruvbox8/colors/gruvbox8.vim
|
||||
017.932 002.321 000.654: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/syntax/synload.vim
|
||||
018.025 002.551 000.230: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/syntax/syntax.vim
|
||||
021.955 000.187 000.187: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/gzip.vim
|
||||
022.056 000.021 000.021: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/health.vim
|
||||
022.175 000.047 000.047: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/man.vim
|
||||
023.777 000.207 000.207: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/pack/dist/opt/matchit/plugin/matchit.vim
|
||||
024.039 001.791 001.584: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/matchit.vim
|
||||
024.276 000.164 000.164: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/matchparen.vim
|
||||
024.668 000.318 000.318: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/netrwPlugin.vim
|
||||
024.992 000.017 000.017: sourcing /Users/nathan/.local/share/nvim/rplugin.vim
|
||||
025.001 000.245 000.228: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/rplugin.vim
|
||||
025.153 000.077 000.077: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/shada.vim
|
||||
025.270 000.035 000.035: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/spellfile.vim
|
||||
025.469 000.118 000.118: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/tarPlugin.vim
|
||||
025.719 000.163 000.163: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/tohtml.vim
|
||||
025.834 000.031 000.031: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/tutor.vim
|
||||
026.077 000.169 000.169: sourcing /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/zipPlugin.vim
|
||||
033.400 000.027 000.027: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/opt/vim-markdown/ftdetect/markdown.vim
|
||||
033.411 007.043 007.016: sourcing /Users/nathan/.config/nvim/plugin/packer_compiled.lua
|
||||
034.214 007.645: loading plugins
|
||||
034.853 000.030 000.030: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/easy-replace.nvim/plugin/easy_replace.vim
|
||||
+ 035.412 000.022 000.022: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/filetype.nvim/plugin/filetype.vim
|
||||
036.064 000.027 000.027: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-bqf/plugin/bqf.vim
|
||||
038.325 001.867 001.867: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/plugin/nvim-treesitter.vim
|
||||
038.937 000.037 000.037: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/elixir.vim
|
||||
039.039 000.032 000.032: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/fish.vim
|
||||
039.132 000.023 000.023: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/gdresource.vim
|
||||
039.284 000.023 000.023: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/gdscript.vim
|
||||
039.427 000.022 000.022: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/gomod.vim
|
||||
039.523 000.028 000.028: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/graphql.vim
|
||||
039.620 000.030 000.030: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/hcl.vim
|
||||
039.711 000.023 000.023: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/heex.vim
|
||||
039.800 000.022 000.022: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/json5.vim
|
||||
039.888 000.021 000.021: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/julia.vim
|
||||
039.983 000.029 000.029: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/ledger.vim
|
||||
040.075 000.026 000.026: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/nix.vim
|
||||
040.169 000.025 000.025: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/ql.vim
|
||||
040.271 000.035 000.035: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/query.vim
|
||||
040.362 000.024 000.024: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/surface.vim
|
||||
040.455 000.027 000.027: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/teal.vim
|
||||
040.547 000.025 000.025: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/tlaplus.vim
|
||||
040.638 000.025 000.025: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/yang.vim
|
||||
040.731 000.027 000.027: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-treesitter/ftdetect/zig.vim
|
||||
041.143 000.047 000.047: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/nvim-web-devicons/plugin/nvim-web-devicons.vim
|
||||
041.688 000.042 000.042: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/plenary.nvim/plugin/plenary.vim
|
||||
042.618 000.203 000.203: sourcing /Users/nathan/.local/share/nvim/site/pack/packer/start/vim-rooter/plugin/rooter.vim
|
||||
042.980 006.026: loading packages
|
||||
043.533 000.553: loading after plugins
|
||||
043.543 000.010: inits 3
|
||||
043.554 000.011: clearing screen
|
||||
045.378 001.823: opening buffers
|
||||
045.676 000.298: BufEnter autocommands
|
||||
+ 045.679 000.003: editing files in windows
|
||||
```
|
||||
</details>
|
||||
|
||||
\* The time my machine takes to source the file goes from 9.1 ms to (0.022 + 0.03) ms, which is a 175x speedup.
|
||||
|
||||
## Contributions
|
||||
|
||||
All contributions are appreciated! But please make sure to follow these guidelines:
|
||||
|
||||
- Format your code with stylua, complying with the rules in the `stylua.toml` file
|
||||
- Document any new functions you write, and update the documentation of functions
|
||||
you edit if appropriate
|
||||
- Set the base branch to `dev`
|
|
@ -0,0 +1,6 @@
|
|||
let g:did_load_filetypes = 1
|
||||
|
||||
augroup filetypedetect
|
||||
au!
|
||||
au BufNewFile,BufRead * lua require('filetype').resolve()
|
||||
augroup END
|
|
@ -0,0 +1,218 @@
|
|||
-- generate the filetype
|
||||
local custom_map = nil
|
||||
|
||||
-- Lua implementation of the setfiletype builtin function.
|
||||
-- See :help setf
|
||||
local function setf(filetype)
|
||||
if vim.fn.did_filetype() == 0 then
|
||||
vim.bo.filetype = filetype
|
||||
end
|
||||
end
|
||||
|
||||
local function set_filetype(name)
|
||||
if type(name) == "string" then
|
||||
setf(name)
|
||||
return true
|
||||
elseif type(name) == "function" then
|
||||
local result = name()
|
||||
if type(result) == "string" then
|
||||
setf(result)
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
if vim.g.ft_ignore_pat == nil then
|
||||
vim.g.ft_ignore_pat = [[\.\(Z\|gz\|bz2\|zip\|tgz\)$]]
|
||||
end
|
||||
local ft_ignore_regex = vim.regex(vim.g.ft_ignore_pat)
|
||||
|
||||
local function star_set_filetype(name)
|
||||
if not ft_ignore_regex:match_str(name) then
|
||||
return set_filetype(name)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Loop through the regex-filetype pairs in the map table
|
||||
-- and check if absolute_path matches any of them
|
||||
-- Returns true if the filetype was set
|
||||
local function try_regex(absolute_path, maps, star_set)
|
||||
if maps == nil then
|
||||
return false
|
||||
end
|
||||
for regexp, ft in pairs(maps) do
|
||||
if absolute_path:find(regexp) then
|
||||
if star_set then
|
||||
if star_set_filetype(ft) then
|
||||
return true
|
||||
end
|
||||
else
|
||||
set_filetype(ft)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function try_lookup(query, map)
|
||||
if query == nil or map == nil then
|
||||
return false
|
||||
end
|
||||
if map[query] ~= nil then
|
||||
set_filetype(map[query])
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Check the first line in the buffer for a shebang
|
||||
-- If there is one, set the filetype appropriately
|
||||
local function analyze_shebang()
|
||||
local fstline = vim.api.nvim_buf_get_lines(0, 0, 1, true)[1]
|
||||
if fstline then
|
||||
return fstline:match("#!%s*/usr/bin/env%s+(%S+)")
|
||||
or fstline:match("#!%s*/%S+/([^ /]+)")
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- Return the value of map.shebang[s]; that is the value of the field indexed
|
||||
-- by the value of s in map.shebang. This could be nil.
|
||||
local function shebang_from_map(s, map)
|
||||
-- Avoid indexing nil.
|
||||
if map and map.shebang then
|
||||
return map.shebang[s]
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local M = {}
|
||||
|
||||
function M.setup(opts)
|
||||
if opts.overrides then
|
||||
custom_map = opts.overrides
|
||||
end
|
||||
end
|
||||
function M.resolve()
|
||||
-- Just in case
|
||||
vim.g.did_load_filetypes = 1
|
||||
|
||||
local absolute_path = vim.api.nvim_buf_get_name(0)
|
||||
|
||||
if vim.bo.filetype == "bqfpreview" then
|
||||
absolute_path = vim.fn.expand("<amatch>")
|
||||
end
|
||||
|
||||
if #absolute_path == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local filename = absolute_path:match(".*[\\/](.*)")
|
||||
local ext = filename:match(".+%.(%w+)")
|
||||
|
||||
-- Try to match the custom defined filetypes
|
||||
if custom_map ~= nil then
|
||||
-- Avoid indexing nil
|
||||
if try_lookup(ext, custom_map.extensions) then
|
||||
return
|
||||
end
|
||||
|
||||
if try_lookup(filename, custom_map.literal) then
|
||||
return
|
||||
end
|
||||
|
||||
if try_lookup(ext, custom_map.function_extensions) then
|
||||
return
|
||||
end
|
||||
|
||||
if try_lookup(filename, custom_map.function_literal) then
|
||||
return
|
||||
end
|
||||
|
||||
if try_regex(absolute_path, custom_map.endswith) then
|
||||
return
|
||||
end
|
||||
|
||||
if try_regex(absolute_path, custom_map.complex) then
|
||||
return
|
||||
end
|
||||
|
||||
if try_regex(absolute_path, custom_map.function_complex) then
|
||||
return
|
||||
end
|
||||
|
||||
if try_regex(absolute_path, custom_map.star_sets, true) then
|
||||
return
|
||||
end
|
||||
|
||||
-- if try_filetype_map(absolute_path, filename, ext, custom_map) then
|
||||
-- return
|
||||
-- end
|
||||
end
|
||||
|
||||
local extension_map = require("filetype.mappings.extensions")
|
||||
if try_lookup(ext, extension_map) then
|
||||
return
|
||||
end
|
||||
|
||||
local literal_map = require("filetype.mappings.literal")
|
||||
if try_lookup(filename, literal_map) then
|
||||
return
|
||||
end
|
||||
|
||||
local function_maps = require("filetype.mappings.function")
|
||||
if try_lookup(ext, function_maps.extensions) then
|
||||
return
|
||||
end
|
||||
if try_lookup(filename, function_maps.literal) then
|
||||
return
|
||||
end
|
||||
|
||||
if try_regex(absolute_path, function_maps.complex) then
|
||||
return
|
||||
end
|
||||
|
||||
local complex_maps = require("filetype.mappings.complex")
|
||||
if try_regex(absolute_path, complex_maps.endswith) then
|
||||
return
|
||||
end
|
||||
if try_regex(absolute_path, complex_maps.complex) then
|
||||
return
|
||||
end
|
||||
if try_regex(absolute_path, complex_maps.star_sets, true) then
|
||||
return
|
||||
end
|
||||
|
||||
-- At this point, no filetype has been detected
|
||||
-- so let's just default to the extension, if it has one
|
||||
if ext then
|
||||
set_filetype(ext)
|
||||
return
|
||||
end
|
||||
|
||||
-- If there is no extension, look for a shebang and set the filetype to
|
||||
-- that. Look for a shebang override in custom_map first. If there is none,
|
||||
-- check the default shebangs defined in function_maps. Otherwise, default
|
||||
-- to setting the filetype to the value of shebang itself.
|
||||
local shebang = analyze_shebang()
|
||||
if shebang then
|
||||
shebang = shebang_from_map(shebang, custom_map)
|
||||
or function_maps.shebang[shebang]
|
||||
or shebang
|
||||
set_filetype(shebang)
|
||||
local mapped_shebang
|
||||
if custom_map and custom_map.shebang then
|
||||
mapped_shebang = custom_map.shebang[shebang]
|
||||
end
|
||||
mapped_shebang = mapped_shebang
|
||||
or function_maps.shebang[shebang]
|
||||
or shebang
|
||||
set_filetype(mapped_shebang)
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
|
@ -0,0 +1,181 @@
|
|||
local M = {}
|
||||
-- mapping of lua regex to filetype
|
||||
M.endswith = {
|
||||
["/%.aptitude/config$"] = "aptconf",
|
||||
["/%.config/git/config$"] = "gitconfig",
|
||||
["/%.gnupg/gpg.conf$"] = "gpg",
|
||||
["/%.gnupg/options$"] = "gpg",
|
||||
["/%.icewm/menu$"] = "icemenu",
|
||||
["/%.libao$"] = "libao",
|
||||
["/%.mplayer/config$"] = "mplayerconf",
|
||||
["/%.pinforc$"] = "pinfo",
|
||||
["/%.ssh/config$"] = "sshconfig",
|
||||
["/boot/grub/grub%.conf$"] = "grub",
|
||||
["/boot/grub/menu%.lst$"] = "grub",
|
||||
["/debian/control$"] = "debcontrol",
|
||||
["/debian/copyright$"] = "debcopyright",
|
||||
["/etc/DIR_COLORS$"] = "dircolors",
|
||||
["/etc/a2ps%.cfg$"] = "a2ps",
|
||||
["/etc/aliases$"] = "mailaliases",
|
||||
["/etc/apt/sources%.list$"] = "debsources",
|
||||
["/etc/asound%.conf$"] = "alsaconf",
|
||||
["/etc/blkid%.tab$"] = "xml",
|
||||
["/etc/blkid%.tab.old$"] = "xml",
|
||||
["/etc/cdrdao%.conf$"] = "cdrdaoconf",
|
||||
["/etc/conf%.modules$"] = "modconf",
|
||||
["/etc/default/cdrdao$"] = "cdrdaoconf",
|
||||
["/etc/defaults/cdrdao$"] = "cdrdaoconf",
|
||||
["/etc/dnsmasq%.conf$"] = "dnsmasq",
|
||||
["/etc/grub%.conf$"] = "grub",
|
||||
["/etc/host%.conf$"] = "hostconf",
|
||||
["/etc/hosts%.allow$"] = "hostsaccess",
|
||||
["/etc/hosts%.deny$"] = "hostsaccess",
|
||||
["/etc/libao%.conf$"] = "libao",
|
||||
["/etc/limits$"] = "limits",
|
||||
["/etc/login%.access$"] = "loginaccess",
|
||||
["/etc/login%.defs$"] = "logindefs",
|
||||
["/etc/mail/aliases$"] = "mailaliases",
|
||||
["/etc/man%.conf$"] = "manconf",
|
||||
["/etc/modules$"] = "modconf",
|
||||
["/etc/modules%.conf$"] = "modconf",
|
||||
["/etc/nanorc$"] = "nanorc",
|
||||
["/etc/pacman%.conf$"] = "dosini",
|
||||
["/etc/pam%.conf$"] = "pamconf",
|
||||
["/etc/pinforc$"] = "pinfo",
|
||||
["/etc/protocols$"] = "protocols",
|
||||
["/etc/sensors%.conf$"] = "sensors",
|
||||
["/etc/sensors3%.conf$"] = "sensors",
|
||||
["/etc/serial%.conf$"] = "setserial",
|
||||
["/etc/services$"] = "services",
|
||||
["/etc/slp%.conf$"] = "slpconf",
|
||||
["/etc/slp%.reg$"] = "slpreg",
|
||||
["/etc/slp%.spi$"] = "slpspi",
|
||||
["/etc/sudoers$"] = "sudoers",
|
||||
["/etc/sysctl%.conf$"] = "sysctl",
|
||||
["/etc/udev/cdsymlinks%.conf$"] = "sh",
|
||||
["/etc/udev/udev%.conf$"] = "udevconf",
|
||||
["/etc/updatedb%.conf$"] = "updatedb",
|
||||
["/etc/xinetd%.conf$"] = "xinetd",
|
||||
["/etc/yum%.conf$"] = "dosini",
|
||||
["/etc/zprofile$"] = "zsh",
|
||||
["/usr/share/alsa/alsa%.conf$"] = "alsaconf",
|
||||
["Xmodmap$"] = "xmodmap",
|
||||
["bsd$"] = "bsdl",
|
||||
["esmtprc$"] = "esmtprc",
|
||||
["hgrc$"] = "cfg",
|
||||
["lftp/rc$"] = "lftp",
|
||||
["lpe$"] = "dracula",
|
||||
["lvs$"] = "dracula",
|
||||
}
|
||||
|
||||
M.complex = {
|
||||
["%.tmux.*%.conf"] = "tmux",
|
||||
[".*%.git/modules/.*/config"] = "gitconfig",
|
||||
[".*git/config"] = "gitconfig",
|
||||
[".*/%.config/systemd/user/.*%.d/.*%.conf"] = "systemd",
|
||||
[".*/%.config/upstart/.*%.conf"] = "upstart",
|
||||
[".*/%.config/upstart/.*%.override"] = "upstart",
|
||||
[".*/%.init/.*%.conf"] = "upstart",
|
||||
[".*/%.init/.*%.override"] = "upstart",
|
||||
[".*/LiteStep/.*/.*%.rc"] = "litestep",
|
||||
[".*/etc/.*limits%.conf"] = "limits",
|
||||
[".*/etc/.*limits%.d/.*%.conf"] = "limits",
|
||||
[".*/etc/a2ps/.*%.cfg"] = "a2ps",
|
||||
[".*/etc/apt/sources%.list%.d/.*%.list"] = "debsources",
|
||||
[".*/etc/httpd/.*%.conf"] = "apache",
|
||||
[".*/etc/init/.*%.conf"] = "upstart",
|
||||
[".*/etc/init/.*%.override"] = "upstart",
|
||||
[".*/etc/initng/.*/.*%.i"] = "initng",
|
||||
[".*/etc/ssh/ssh_config%.d/.*%.conf"] = "sshconfig",
|
||||
[".*/etc/ssh/sshd_config%.d/.*%.conf"] = "sshdconfig",
|
||||
[".*/etc/sysctl%.d/.*%.conf"] = "sysctl",
|
||||
["/etc/gitconfig"] = "gitconfig",
|
||||
[".*/etc/systemd/.*%.conf%.d/.*%.conf"] = "systemd",
|
||||
[".*/etc/systemd/system/.*%.d/.*%.conf"] = "systemd",
|
||||
[".*/etc/udev/permissions%.d/.*%.permissions"] = "udevperm",
|
||||
[".*/etc/xdg/menus/.*%.menu"] = "xml",
|
||||
[".*/usr/.*/gnupg/options%.skel"] = "gpg",
|
||||
[".*/usr/share/upstart/.*%.conf"] = "upstart",
|
||||
[".*/usr/share/upstart/.*%.override"] = "upstart",
|
||||
[".*Eterm/.*%.cfg"] = "eterm",
|
||||
[".*enlightenment/.*%.cfg"] = "c",
|
||||
["bzr_log%..*"] = "bzr",
|
||||
["named.*%.conf"] = "named",
|
||||
["rndc.*%.conf"] = "named",
|
||||
["rndc.*%.key"] = "named",
|
||||
}
|
||||
|
||||
-- These require a special set_ft function
|
||||
M.star_sets = {
|
||||
[".*/etc/Muttrc%.d/.*"] = [[muttrc]],
|
||||
[".*/etc/proftpd/.*%.conf.*"] = [[apachestyle]],
|
||||
[".*/etc/proftpd/conf%..*/.*"] = [[apachestyle]],
|
||||
["proftpd%.conf.*"] = [[apachestyle]],
|
||||
["access%.conf.*"] = [[apache]],
|
||||
["apache%.conf.*"] = [[apache]],
|
||||
["apache2%.conf.*"] = [[apache]],
|
||||
["httpd%.conf.*"] = [[apache]],
|
||||
["srm%.conf.*"] = [[apache]],
|
||||
[".*/etc/apache2/.*%.conf.*"] = [[apache]],
|
||||
[".*/etc/apache2/conf%..*/.*"] = [[apache]],
|
||||
[".*/etc/apache2/mods-.*/.*"] = [[apache]],
|
||||
[".*/etc/apache2/sites-.*/.*"] = [[apache]],
|
||||
[".*/etc/httpd/conf%.d/.*%.conf.*"] = [[apache]],
|
||||
[".*asterisk/.*%.conf.*"] = [[asterisk]],
|
||||
[".*asterisk.*/.*voicemail%.conf.*"] = [[asteriskvm]],
|
||||
[".*/named/db%..*"] = [[bindzone]],
|
||||
[".*/bind/db%..*"] = [[bindzone]],
|
||||
["cabal%.project%..*"] = [[cabalproject]],
|
||||
["crontab"] = [[crontab]],
|
||||
["crontab%..*"] = [[crontab]],
|
||||
[".*/etc/cron%.d/.*"] = [[crontab]],
|
||||
[".*/etc/dnsmasq%.d/.*"] = [[dnsmasq]],
|
||||
["drac%..*"] = [[dracula]],
|
||||
[".*/%.fvwm/.*"] = [[fvwm]],
|
||||
[".*/tmp/lltmp.*"] = [[gedcom]],
|
||||
[".*/%.gitconfig%.d/.*"] = [[gitconfig]],
|
||||
["/etc/gitconfig%.d/.*"] = [[gitconfig]],
|
||||
[".*/gitolite-admin/conf/.*"] = [[gitolite]],
|
||||
["%.gtkrc.*"] = [[gtkrc]],
|
||||
["gtkrc.*"] = [[gtkrc]],
|
||||
["Prl.*%..*"] = [[jam]],
|
||||
["JAM.*%..*"] = [[jam]],
|
||||
[".*%.properties_??_??_.*"] = [[jproperties]],
|
||||
["Kconfig%..*"] = [[kconfig]],
|
||||
["lilo%.conf.*"] = [[lilo]],
|
||||
[".*/etc/logcheck/.*%.d.*/.*"] = [[logcheck]],
|
||||
["[mM]akefile.*"] = [[make]],
|
||||
["mk"] = [[make]],
|
||||
["mak"] = [[make]],
|
||||
["dsp"] = [[make]],
|
||||
["[rR]akefile.*"] = [[ruby]],
|
||||
["reportbug-.*"] = [[mail]],
|
||||
[".*/etc/modprobe%..*"] = [[modconf]],
|
||||
["%.mutt{ng,}rc.*"] = [[muttrc]],
|
||||
[".*/%.mutt{ng,}/mutt{ng,}rc.*"] = [[muttrc]],
|
||||
["mutt{ng,}rc.*,Mutt{ng,}rc.*"] = [[muttrc]],
|
||||
["%.neomuttrc.*"] = [[neomuttrc]],
|
||||
[".*/%.neomutt/neomuttrc.*"] = [[neomuttrc]],
|
||||
["neomuttrc.*"] = [[neomuttrc]],
|
||||
["Neomuttrc.*"] = [[neomuttrc]],
|
||||
["tmac%..*"] = [[nroff]],
|
||||
["/etc/hostname%..*"] = [[config]],
|
||||
[".*/etc/pam%.d/.*"] = [[pamconf]],
|
||||
["%.reminders.*"] = [[remind]],
|
||||
["sgml%.catalog.*"] = [[catalog]],
|
||||
[".*%.vhdl_[0-9].*"] = [[vhdl]],
|
||||
[".*vimrc.*"] = [[vim]],
|
||||
["Xresources.*"] = [[xdefaults]],
|
||||
[".*/app-defaults/.*"] = [[xdefaults]],
|
||||
[".*/Xresources/.*"] = [[xdefaults]],
|
||||
[".*xmodmap.*"] = [[xmodmap]],
|
||||
[".*/etc/xinetd%.d/.*"] = [[xinetd]],
|
||||
[".*/etc/yum%.repos%.d/.*"] = [[dosini]],
|
||||
["%.zsh.*"] = [[zsh]],
|
||||
["%.zlog.*"] = [[zsh]],
|
||||
["%.zcompdump.*"] = [[zsh]],
|
||||
["zsh.*"] = [[zsh]],
|
||||
["zlog.*"] = [[zsh]],
|
||||
}
|
||||
|
||||
return M
|
|
@ -0,0 +1,646 @@
|
|||
return {
|
||||
[".ch"] = "chill",
|
||||
["4gh"] = "fgl",
|
||||
["4gl"] = "fgl",
|
||||
["8th"] = "8th",
|
||||
["ACE"] = "lace",
|
||||
["BUILD"] = "bzl",
|
||||
["C"] = "cpp",
|
||||
["DEF"] = "modula2",
|
||||
["Dockerfile"] = "dockerfile",
|
||||
["EC"] = "esqlc",
|
||||
["F"] = "fortran",
|
||||
["F03"] = "fortran",
|
||||
["F08"] = "fortran",
|
||||
["F77"] = "fortran",
|
||||
["F90"] = "fortran",
|
||||
["F95"] = "fortran",
|
||||
["FOR"] = "fortran",
|
||||
["FPP"] = "fortran",
|
||||
["FTN"] = "fortran",
|
||||
["H"] = "cpp",
|
||||
["INF"] = "inform",
|
||||
["JAL"] = "jal",
|
||||
["L"] = "lisp",
|
||||
["MOD"] = "modula2",
|
||||
["Rd"] = "rhelp",
|
||||
["Rmd"] = "rmd",
|
||||
["Rnw"] = "rnoweb",
|
||||
["Rrst"] = "rrst",
|
||||
["Smd"] = "rmd",
|
||||
["Snw"] = "rnoweb",
|
||||
["Srst"] = "rrst",
|
||||
["a65"] = "a65",
|
||||
["aap"] = "aap",
|
||||
["abap"] = "abap",
|
||||
["abc"] = "abc",
|
||||
["abl"] = "abel",
|
||||
["ace"] = "lace",
|
||||
["action"] = "privoxy",
|
||||
["ada"] = "ada",
|
||||
["adb"] = "ada",
|
||||
["ado"] = "stata",
|
||||
["adoc"] = "asciidoc",
|
||||
["ads"] = "ada",
|
||||
["afm"] = "postscr",
|
||||
["ahk"] = "autohotkey",
|
||||
["ai"] = "postscr",
|
||||
["aidl"] = "aidl",
|
||||
["al"] = "perl",
|
||||
["aml"] = "aml",
|
||||
["art"] = "art",
|
||||
["as"] = "atlas",
|
||||
["asciidoc"] = "asciidoc",
|
||||
["asn"] = "asn",
|
||||
["asn1"] = "asn",
|
||||
["at"] = "m4",
|
||||
["atg"] = "coco",
|
||||
["atl"] = "atlas",
|
||||
["atom"] = "xml",
|
||||
["au3"] = "autoit",
|
||||
["ave"] = "ave",
|
||||
["awk"] = "awk",
|
||||
["bat"] = "dosbatch",
|
||||
["bbl"] = "tex",
|
||||
["bc"] = "bc",
|
||||
["bdf"] = "bdf",
|
||||
["beancount"] = "beancount",
|
||||
["bi"] = "freebasic",
|
||||
["bib"] = "bib",
|
||||
["bl"] = "blank",
|
||||
["bsdl"] = "bsdl",
|
||||
["bst"] = "bst",
|
||||
["bu"] = "yaml",
|
||||
["builder"] = "ruby",
|
||||
["c++"] = "cpp",
|
||||
["cabal"] = "cabal",
|
||||
["cbl"] = "cobol",
|
||||
["cdf"] = "skill",
|
||||
["cdl"] = "cdl",
|
||||
["cdxml"] = "xml",
|
||||
["cfc"] = "cf",
|
||||
["cfg"] = "cfg",
|
||||
["cfi"] = "cf",
|
||||
["cfm"] = "cf",
|
||||
["chf"] = "ch",
|
||||
["cho"] = "chordpro",
|
||||
["chopro"] = "chordpro",
|
||||
["chordpro"] = "chordpro",
|
||||
["chs"] = "chaskell",
|
||||
["cjs"] = "javascript",
|
||||
["cl"] = "lisp",
|
||||
["clj"] = "clojure",
|
||||
["cljc"] = "clojure",
|
||||
["cljs"] = "clojure",
|
||||
["cljx"] = "clojure",
|
||||
["clp"] = "jess",
|
||||
["cm"] = "voscm",
|
||||
["cmake"] = "cmake",
|
||||
["cmake.in"] = "cmake",
|
||||
["cmod"] = "cmod",
|
||||
["cob"] = "cobol",
|
||||
["comp"] = "mason",
|
||||
["con"] = "cterm",
|
||||
["crd"] = "chordpro",
|
||||
["crdpro"] = "chordpro",
|
||||
["crm"] = "crm",
|
||||
["cs"] = "cs",
|
||||
["csc"] = "csc",
|
||||
["csdl"] = "csdl",
|
||||
["csp"] = "csp",
|
||||
["csproj"] = "xml",
|
||||
["csproj.user"] = "xml",
|
||||
["css"] = "css",
|
||||
["ctl"] = "vb",
|
||||
["cu"] = "cuda",
|
||||
["cuh"] = "cuda",
|
||||
["cxx"] = "cpp",
|
||||
["cyn"] = "cynpp",
|
||||
["dart"] = "dart",
|
||||
["dat"] = "nastran",
|
||||
["dcd"] = "dcd",
|
||||
["dcl"] = "clean",
|
||||
["def"] = "def",
|
||||
["desc"] = "desc",
|
||||
["desktop"] = "desktop",
|
||||
["diff"] = "diff",
|
||||
["directory"] = "desktop",
|
||||
["do"] = "stata",
|
||||
["dot"] = "dot",
|
||||
["dpr"] = "pascal",
|
||||
["drac"] = "dracula",
|
||||
["drc"] = "dracula",
|
||||
["ds"] = "datascript",
|
||||
["dsl"] = "dsl",
|
||||
["dsm"] = "vb",
|
||||
["dtd"] = "dtd",
|
||||
["dts"] = "dts",
|
||||
["dtsi"] = "dts",
|
||||
["dtx"] = "tex",
|
||||
["dylan"] = "dylan",
|
||||
["ec"] = "esqlc",
|
||||
["ecd"] = "ecd",
|
||||
["el"] = "lisp",
|
||||
["elm"] = "elm",
|
||||
["eni"] = "cl",
|
||||
["epp"] = "epuppet",
|
||||
["eps"] = "postscr",
|
||||
["epsf"] = "postscr",
|
||||
["epsi"] = "postscr",
|
||||
["erb"] = "eruby",
|
||||
["erl"] = "erlang",
|
||||
["errsum"] = "hercules",
|
||||
["es"] = "javascript",
|
||||
["ev"] = "hercules",
|
||||
["ex"] = "elixir",
|
||||
["exp"] = "expect",
|
||||
["exs"] = "elixir",
|
||||
["f"] = "fortran",
|
||||
["f03"] = "fortran",
|
||||
["f08"] = "fortran",
|
||||
["f77"] = "fortran",
|
||||
["f90"] = "fortran",
|
||||
["f95"] = "fortran",
|
||||
["factor"] = "factor",
|
||||
["fal"] = "falcon",
|
||||
["fan"] = "fan",
|
||||
["fb"] = "freebasic",
|
||||
["fdr"] = "csp",
|
||||
["feature"] = "cucumber",
|
||||
["fex"] = "focexec",
|
||||
["fnl"] = "fennel",
|
||||
["focexec"] = "focexec",
|
||||
["for"] = "fortran",
|
||||
["fortran"] = "fortran",
|
||||
["fpc"] = "fpcmake",
|
||||
["fpp"] = "fortran",
|
||||
["frt"] = "reva",
|
||||
["fs"] = "forth",
|
||||
["fsl"] = "framescript",
|
||||
["ft"] = "forth",
|
||||
["fth"] = "forth",
|
||||
["ftn"] = "fortran",
|
||||
["fwt"] = "fan",
|
||||
["g"] = "pccts",
|
||||
["gawk"] = "awk",
|
||||
["gdmo"] = "gdmo",
|
||||
["ged"] = "gedcom",
|
||||
["gemspec"] = "ruby",
|
||||
["go"] = "go",
|
||||
["gp"] = "gp",
|
||||
["gpi"] = "gnuplot",
|
||||
["gpr"] = "ada",
|
||||
["gql"] = "graphql",
|
||||
["gradle"] = "groovy",
|
||||
["graphql"] = "graphql",
|
||||
["gretl"] = "gretl",
|
||||
["groovy"] = "groovy",
|
||||
["gs"] = "grads",
|
||||
["gsp"] = "gsp",
|
||||
["gv"] = "dot",
|
||||
["h32"] = "hex",
|
||||
["haml"] = "haml",
|
||||
["hs"] = "haskell",
|
||||
["hsc"] = "haskell",
|
||||
["hs-boot"] = "haskell",
|
||||
["hsig"] = "haskell",
|
||||
["hb"] = "hb",
|
||||
["hdl"] = "vhdl",
|
||||
["hex"] = "hex",
|
||||
["hgrc"] = "cfg",
|
||||
["hh"] = "cpp",
|
||||
["hlp"] = "smcl",
|
||||
["hog"] = "hog",
|
||||
["hpp"] = "cpp",
|
||||
["hrl"] = "erlang",
|
||||
["hsm"] = "hamster",
|
||||
["ht"] = "haste",
|
||||
["htb"] = "httest",
|
||||
["html.m4"] = "htmlm4",
|
||||
["htpp"] = "hastepreproc",
|
||||
["htt"] = "httest",
|
||||
["hx"] = "haxe",
|
||||
["hxml"] = "hxml",
|
||||
["hxx"] = "cpp",
|
||||
["iba"] = "ibasic",
|
||||
["ibi"] = "ibasic",
|
||||
["ice"] = "slice",
|
||||
["icl"] = "clean",
|
||||
["icn"] = "icon",
|
||||
["ih"] = "ppwiz",
|
||||
["ihlp"] = "smcl",
|
||||
["ii"] = "initng",
|
||||
["ijs"] = "j",
|
||||
["il"] = "skill",
|
||||
["ils"] = "skill",
|
||||
["imata"] = "stata",
|
||||
["imp"] = "b",
|
||||
["inf"] = "inform",
|
||||
["ini"] = "dosini",
|
||||
["inl"] = "cpp",
|
||||
["ino"] = "arduino",
|
||||
["intr"] = "dylanintr",
|
||||
["ipp"] = "cpp",
|
||||
["ipynb"] = "json",
|
||||
["isc"] = "monk",
|
||||
["iss"] = "iss",
|
||||
["ist"] = "ist",
|
||||
["it"] = "ppwiz",
|
||||
["itcl"] = "tcl",
|
||||
["itk"] = "tcl",
|
||||
["j73"] = "jovial",
|
||||
["jacl"] = "tcl",
|
||||
["jal"] = "jal",
|
||||
["jav"] = "java",
|
||||
["java"] = "java",
|
||||
["javascript"] = "javascript",
|
||||
["jgr"] = "jgraph",
|
||||
["jj"] = "javacc",
|
||||
["jjt"] = "javacc",
|
||||
["jl"] = "julia",
|
||||
["jov"] = "jovial",
|
||||
["jovial"] = "jovial",
|
||||
["jpl"] = "jam",
|
||||
["jpr"] = "jam",
|
||||
["jrexx"] = "rexx",
|
||||
["js"] = "javascript",
|
||||
["json"] = "json",
|
||||
["jsonp"] = "json",
|
||||
["jsp"] = "jsp",
|
||||
["jsx"] = "javascriptreact",
|
||||
["k"] = "kwt",
|
||||
["kix"] = "kix",
|
||||
["ks"] = "kscript",
|
||||
["kt"] = "kotlin",
|
||||
["ktm"] = "kotlin",
|
||||
["kts"] = "kotlin",
|
||||
["kv"] = "kivy",
|
||||
["latex"] = "tex",
|
||||
["latte"] = "latte",
|
||||
["ld"] = "ld",
|
||||
["ldif"] = "ldif",
|
||||
["less"] = "less",
|
||||
["lgt"] = "logtalk",
|
||||
["lhs"] = "lhaskell",
|
||||
["lib"] = "cobol",
|
||||
["lid"] = "dylanlid",
|
||||
["liquid"] = "liquid",
|
||||
["lisp"] = "lisp",
|
||||
["lite"] = "lite",
|
||||
["ll"] = "lifelines",
|
||||
["lot"] = "lotos",
|
||||
["lotos"] = "lotos",
|
||||
["lou"] = "lout",
|
||||
["lout"] = "lout",
|
||||
["lpc"] = "lpc",
|
||||
["lpr"] = "pascal",
|
||||
["lsl"] = "lsl",
|
||||
["lsp"] = "lisp",
|
||||
["lss"] = "lss",
|
||||
["lt"] = "lite",
|
||||
["lte"] = "latte",
|
||||
["ltx"] = "tex",
|
||||
["lua"] = "lua",
|
||||
["lock"] = "toml",
|
||||
["m2"] = "modula2",
|
||||
["m4gl"] = "fgl",
|
||||
["man"] = "nroff",
|
||||
["map"] = "map",
|
||||
["mar"] = "vmasm",
|
||||
["markdown"] = "markdown",
|
||||
["mas"] = "master",
|
||||
["mason"] = "mason",
|
||||
["master"] = "master",
|
||||
["mat"] = "radiance",
|
||||
["mata"] = "stata",
|
||||
["mch"] = "b",
|
||||
["md"] = "markdown",
|
||||
["mdown"] = "markdown",
|
||||
["mdwn"] = "markdown",
|
||||
["mel"] = "mel",
|
||||
["mf"] = "mf",
|
||||
["mgl"] = "mgl",
|
||||
["mgp"] = "mgp",
|
||||
["mhtml"] = "mason",
|
||||
["mi"] = "modula2",
|
||||
["mib"] = "mib",
|
||||
["mix"] = "mix",
|
||||
["mixal"] = "mix",
|
||||
["mjs"] = "javascript",
|
||||
["mkd"] = "markdown",
|
||||
["mkdn"] = "markdown",
|
||||
["mkii"] = "context",
|
||||
["mkiv"] = "context",
|
||||
["mklx"] = "context",
|
||||
["mkvi"] = "context",
|
||||
["mkxl"] = "context",
|
||||
["ml"] = "ocaml",
|
||||
["ml.cppo"] = "ocaml",
|
||||
["mli"] = "ocaml",
|
||||
["mli.cppo"] = "ocaml",
|
||||
["mlip"] = "ocaml",
|
||||
["mll"] = "ocaml",
|
||||
["mlp"] = "ocaml",
|
||||
["mlt"] = "ocaml",
|
||||
["mly"] = "ocaml",
|
||||
["mmp"] = "mmp",
|
||||
["mo"] = "gdmo",
|
||||
["moc"] = "cpp",
|
||||
["mof"] = "msidl",
|
||||
["mom"] = "nroff",
|
||||
["monk"] = "monk",
|
||||
["moo"] = "moo",
|
||||
["mot"] = "srec",
|
||||
["mp"] = "mp",
|
||||
["mpl"] = "maple",
|
||||
["msc"] = "xmath",
|
||||
["msf"] = "xmath",
|
||||
["msql"] = "msql",
|
||||
["mst"] = "ist",
|
||||
["mush"] = "mush",
|
||||
["mv"] = "maple",
|
||||
["mws"] = "maple",
|
||||
["my"] = "mib",
|
||||
["mysql"] = "mysql",
|
||||
["nanorc"] = "nanorc",
|
||||
["nb"] = "mma",
|
||||
["ncf"] = "ncf",
|
||||
["ninja"] = "ninja",
|
||||
["nqc"] = "nqc",
|
||||
["nr"] = "nroff",
|
||||
["nse"] = "lua",
|
||||
["nsh"] = "nsis",
|
||||
["nsi"] = "nsis",
|
||||
["obj"] = "obj",
|
||||
["occ"] = "occam",
|
||||
["odl"] = "msidl",
|
||||
["opam"] = "opam",
|
||||
["opam.template"] = "opam",
|
||||
["or"] = "openroad",
|
||||
["ora"] = "ora",
|
||||
["org"] = "org",
|
||||
["orx"] = "rexx",
|
||||
["p36"] = "plm",
|
||||
["p6"] = "raku",
|
||||
["pac"] = "plm",
|
||||
["page"] = "mallard",
|
||||
["papp"] = "papp",
|
||||
["pas"] = "pascal",
|
||||
["patch"] = "diff",
|
||||
["pbtxt"] = "pbtxt",
|
||||
["pc"] = "proc",
|
||||
["pcmk"] = "pcmk",
|
||||
["pdb"] = "prolog",
|
||||
["pde"] = "arduino",
|
||||
["pdf"] = "pdf",
|
||||
["pfa"] = "postscr",
|
||||
["pike"] = "pike",
|
||||
["pk"] = "poke",
|
||||
["pkb"] = "sql",
|
||||
["pks"] = "sql",
|
||||
["pl1"] = "pli",
|
||||
["pld"] = "cupl",
|
||||
["pli"] = "pli",
|
||||
["plm"] = "plm",
|
||||
["plp"] = "plp",
|
||||
["pls"] = "plsql",
|
||||
["plsql"] = "plsql",
|
||||
["plx"] = "perl",
|
||||
["pm6"] = "raku",
|
||||
["pml"] = "promela",
|
||||
["pmod"] = "pike",
|
||||
["po"] = "po",
|
||||
["pod"] = "pod",
|
||||
["pod6"] = "raku",
|
||||
["pot"] = "po",
|
||||
["pov"] = "pov",
|
||||
["ppd"] = "ppd",
|
||||
["pr"] = "sdl",
|
||||
["proto"] = "proto",
|
||||
["ps"] = "postscr",
|
||||
["ps1"] = "ps1",
|
||||
["ps1xml"] = "ps1xml",
|
||||
["psc1"] = "xml",
|
||||
["psd1"] = "ps1",
|
||||
["psf"] = "psf",
|
||||
["psgi"] = "perl",
|
||||
["psl"] = "psl",
|
||||
["psm1"] = "ps1",
|
||||
["pssc"] = "ps1",
|
||||
["ptl"] = "python",
|
||||
["pxd"] = "pyrex",
|
||||
["pxml"] = "papp",
|
||||
["pxsl"] = "papp",
|
||||
["py"] = "python",
|
||||
["pyi"] = "python",
|
||||
["pyw"] = "python",
|
||||
["pyx"] = "pyrex",
|
||||
["qc"] = "c",
|
||||
["quake"] = "m3quake",
|
||||
["rad"] = "radiance",
|
||||
["rake"] = "ruby",
|
||||
["raku"] = "raku",
|
||||
["rakudoc"] = "raku",
|
||||
["rakumod"] = "raku",
|
||||
["rakutest"] = "raku",
|
||||
["raml"] = "raml",
|
||||
["rb"] = "ruby",
|
||||
["rbs"] = "rbs",
|
||||
["rbw"] = "ruby",
|
||||
["rc"] = "rc",
|
||||
["rch"] = "rc",
|
||||
["rcp"] = "pilrc",
|
||||
["rd"] = "rhelp",
|
||||
["recipe"] = "conaryrecipe",
|
||||
["ref"] = "b",
|
||||
["rego"] = "rego",
|
||||
["rej"] = "diff",
|
||||
["rem"] = "remind",
|
||||
["remind"] = "remind",
|
||||
["res"] = "rescript",
|
||||
["resi"] = "rescript",
|
||||
["rex"] = "rexx",
|
||||
["rexx"] = "rexx",
|
||||
["rexxj"] = "rexx",
|
||||
["rhtml"] = "eruby",
|
||||
["rib"] = "rib",
|
||||
["rjs"] = "ruby",
|
||||
["rkt"] = "scheme",
|
||||
["rmd"] = "rmd",
|
||||
["rnc"] = "rnc",
|
||||
["rng"] = "rng",
|
||||
["rnw"] = "rnoweb",
|
||||
["rockspec"] = "lua",
|
||||
["roff"] = "nroff",
|
||||
["rpl"] = "rpl",
|
||||
["rq"] = "sparql",
|
||||
["rrst"] = "rrst",
|
||||
["rs"] = "rust",
|
||||
["rss"] = "xml",
|
||||
["rst"] = "rst",
|
||||
["rtf"] = "rtf",
|
||||
["ru"] = "ruby",
|
||||
["run"] = "ampl",
|
||||
["rxj"] = "rexx",
|
||||
["rxml"] = "ruby",
|
||||
["rxo"] = "rexx",
|
||||
["s19"] = "srec",
|
||||
["s28"] = "srec",
|
||||
["s37"] = "srec",
|
||||
["s85"] = "sinda",
|
||||
["sa"] = "sather",
|
||||
["sas"] = "sas",
|
||||
["sass"] = "sass",
|
||||
["sba"] = "vb",
|
||||
["sbt"] = "sbt",
|
||||
["sc"] = "scala",
|
||||
["scala"] = "scala",
|
||||
["sce"] = "scilab",
|
||||
["sci"] = "scilab",
|
||||
["scm"] = "scheme",
|
||||
["score"] = "slrnsc",
|
||||
["scpt"] = "applescript",
|
||||
["scss"] = "scss",
|
||||
["sd"] = "sd",
|
||||
["sdc"] = "sdc",
|
||||
["sdl"] = "sdl",
|
||||
["sed"] = "sed",
|
||||
["sexp"] = "sexplib",
|
||||
["si"] = "cuplsim",
|
||||
["sieve"] = "sieve",
|
||||
["sig"] = "lprolog",
|
||||
["sil"] = "sil",
|
||||
["sim"] = "simula",
|
||||
["sin"] = "sinda",
|
||||
["siv"] = "sieve",
|
||||
["sl"] = "slang",
|
||||
["slt"] = "tsalt",
|
||||
["sol"] = "solidity",
|
||||
["smcl"] = "smcl",
|
||||
["smd"] = "rmd",
|
||||
["smith"] = "smith",
|
||||
["sml"] = "sml",
|
||||
["smt"] = "smith",
|
||||
["sno"] = "snobol4",
|
||||
["snw"] = "rnoweb",
|
||||
["sp"] = "spice",
|
||||
["sparql"] = "sparql",
|
||||
["spd"] = "spup",
|
||||
["spdata"] = "spup",
|
||||
["spec"] = "spec",
|
||||
["speedup"] = "spup",
|
||||
["spi"] = "spyce",
|
||||
["spice"] = "spice",
|
||||
["spt"] = "snobol4",
|
||||
["spy"] = "spyce",
|
||||
["sqi"] = "sqr",
|
||||
["sqlj"] = "sqlj",
|
||||
["sqr"] = "sqr",
|
||||
["srec"] = "srec",
|
||||
["srst"] = "rrst",
|
||||
["ss"] = "scheme",
|
||||
["ssc"] = "monk",
|
||||
["st"] = "st",
|
||||
["stp"] = "stp",
|
||||
["strl"] = "esterel",
|
||||
["sty"] = "tex",
|
||||
["sum"] = "hercules",
|
||||
["sv"] = "systemverilog",
|
||||
["svelte"] = "svelte",
|
||||
["svg"] = "svg",
|
||||
["svh"] = "systemverilog",
|
||||
["swift"] = "swift",
|
||||
["swift.gyb"] = "swiftgyb",
|
||||
["sys"] = "dosbatch",
|
||||
["t.html"] = "tilde",
|
||||
["t6"] = "raku",
|
||||
["tak"] = "tak",
|
||||
["tcc"] = "cpp",
|
||||
["tcl"] = "tcl",
|
||||
["tdf"] = "ahdl",
|
||||
["testGroup"] = "rexx",
|
||||
["testUnit"] = "rexx",
|
||||
["texi"] = "texinfo",
|
||||
["texinfo"] = "texinfo",
|
||||
["text"] = "text",
|
||||
["tf"] = "tf",
|
||||
["ti"] = "terminfo",
|
||||
["tk"] = "tcl",
|
||||
["tlh"] = "cpp",
|
||||
["tli"] = "tli",
|
||||
["tmac"] = "nroff",
|
||||
["tmpl"] = "template",
|
||||
["toc"] = "cdrtoc",
|
||||
["toml"] = "toml",
|
||||
["tpl"] = "smarty",
|
||||
["tpm"] = "xml",
|
||||
["tpp"] = "cpp",
|
||||
["tr"] = "nroff",
|
||||
["tsc"] = "monk",
|
||||
["tsx"] = "typescriptreact",
|
||||
["txi"] = "texinfo",
|
||||
["tyb"] = "sql",
|
||||
["tyc"] = "sql",
|
||||
["typ"] = "sql",
|
||||
["uc"] = "uc",
|
||||
["ui"] = "xml",
|
||||
["uil"] = "uil",
|
||||
["uit"] = "uil",
|
||||
["ulpc"] = "lpc",
|
||||
["v"] = "verilog",
|
||||
["va"] = "verilogams",
|
||||
["vams"] = "verilogams",
|
||||
["vb"] = "vb",
|
||||
["vba"] = "vim",
|
||||
["vbe"] = "vhdl",
|
||||
["vbs"] = "vb",
|
||||
["vc"] = "hercules",
|
||||
["vhd"] = "vhdl",
|
||||
["vhdl"] = "vhdl",
|
||||
["vho"] = "vhdl",
|
||||
["vim"] = "vim",
|
||||
["vr"] = "vera",
|
||||
["vrh"] = "vera",
|
||||
["vri"] = "vera",
|
||||
["vroom"] = "vroom",
|
||||
["vst"] = "vhdl",
|
||||
["vue"] = "vue",
|
||||
["wast"] = "wast",
|
||||
["wat"] = "wast",
|
||||
["wbt"] = "winbatch",
|
||||
["webmanifest"] = "json",
|
||||
["wiki"] = "flexwiki",
|
||||
["wm"] = "webmacro",
|
||||
["wml"] = "wml",
|
||||
["wpl"] = "xml",
|
||||
["wrap"] = "dosini",
|
||||
["wrl"] = "vrml",
|
||||
["wrm"] = "acedb",
|
||||
["wsdl"] = "xml",
|
||||
["wsml"] = "wsml",
|
||||
["x"] = "rpcgen",
|
||||
["xht"] = "xhtml",
|
||||
["xhtml"] = "xhtml",
|
||||
["xin"] = "omnimark",
|
||||
["xlf"] = "xml",
|
||||
["xliff"] = "xml",
|
||||
["xmi"] = "xml",
|
||||
["xom"] = "omnimark",
|
||||
["xq"] = "xquery",
|
||||
["xql"] = "xquery",
|
||||
["xqm"] = "xquery",
|
||||
["xquery"] = "xquery",
|
||||
["xqy"] = "xquery",
|
||||
["xs"] = "xs",
|
||||
["xsd"] = "xsd",
|
||||
["xsl"] = "xslt",
|
||||
["xslt"] = "xslt",
|
||||
["xul"] = "xml",
|
||||
["yaml"] = "yaml",
|
||||
["yaws"] = "erlang",
|
||||
["yml"] = "yaml",
|
||||
["z8a"] = "z8a",
|
||||
["zsh"] = "zsh",
|
||||
["zu"] = "zimbu",
|
||||
["zut"] = "zimbutempl",
|
||||
}
|
|
@ -0,0 +1,596 @@
|
|||
local M = {}
|
||||
|
||||
local function getlines(i, j)
|
||||
return table.concat(
|
||||
vim.api.nvim_buf_get_lines(0, i - 1, j or i, true),
|
||||
"\n"
|
||||
)
|
||||
end
|
||||
|
||||
M.extensions = {
|
||||
["ms"] = function()
|
||||
vim.cmd([[if !dist#ft#FTnroff() | setf xmath | endif]])
|
||||
end,
|
||||
["xpm"] = function()
|
||||
if getlines(1):find("XPM2") then
|
||||
return "xpm2"
|
||||
else
|
||||
return "xpm"
|
||||
end
|
||||
end,
|
||||
["module"] = function()
|
||||
if getlines(1):find("%<%?php") then
|
||||
return "php"
|
||||
else
|
||||
return "virata"
|
||||
end
|
||||
end,
|
||||
["pkg"] = function()
|
||||
if getlines(1):find("%<%?php") then
|
||||
return "php"
|
||||
else
|
||||
return "virata"
|
||||
end
|
||||
end,
|
||||
["hw"] = function()
|
||||
if getlines(1):find("%<%?php") then
|
||||
return "php"
|
||||
else
|
||||
return "virata"
|
||||
end
|
||||
end,
|
||||
["ts"] = function()
|
||||
if getlines(1):find("<%?xml") then
|
||||
return "xml"
|
||||
else
|
||||
return "typescript"
|
||||
end
|
||||
end,
|
||||
["ttl"] = function()
|
||||
if getlines(1):find("^@?(prefix|base)") then
|
||||
return "stata"
|
||||
end
|
||||
end,
|
||||
["t"] = function()
|
||||
-- Don't know how to translate this :(
|
||||
vim.cmd(
|
||||
[[if !dist#ft#FTnroff() && !dist#ft#FTperl() | setf tads | endif]]
|
||||
)
|
||||
end,
|
||||
["class"] = function()
|
||||
-- Decimal escape sequence
|
||||
-- The original was "^\xca\xfe\xba\xbe"
|
||||
if getlines(1):find("^\x202\x254\x186\x190") then
|
||||
return "stata"
|
||||
end
|
||||
end,
|
||||
["smi"] = function()
|
||||
if getlines(1):find("smil") then
|
||||
return "smil"
|
||||
else
|
||||
return "mib"
|
||||
end
|
||||
end,
|
||||
["smil"] = function()
|
||||
if getlines(1):find("<?%s*xml.*?>") then
|
||||
return "xml"
|
||||
else
|
||||
return "smil"
|
||||
end
|
||||
end,
|
||||
["cls"] = function()
|
||||
local first_line = getlines(1)
|
||||
if first_line:find("^%%") then
|
||||
return "tex"
|
||||
elseif first_line:sub(1, 1) == "#" and first_line:find("rexx") then
|
||||
return "rexx"
|
||||
else
|
||||
return "st"
|
||||
end
|
||||
end,
|
||||
["install"] = function()
|
||||
if getlines(1):find("%<%?php") then
|
||||
return "php"
|
||||
else
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH("bash")]])
|
||||
end
|
||||
end,
|
||||
["decl"] = function()
|
||||
if getlines(1, 3):find("^%<%!SGML") then
|
||||
return "sgmldecl"
|
||||
end
|
||||
end,
|
||||
["sgm"] = function()
|
||||
local top_file = getlines(1, 5)
|
||||
if top_file:find("linuxdoc") then
|
||||
return "sgmlnx"
|
||||
elseif
|
||||
getlines(1):find("%<%!DOCTYPE.*DocBook")
|
||||
or getlines(2):find("<!DOCTYPE.*DocBook")
|
||||
then
|
||||
vim.b.docbk_type = "sgml"
|
||||
vim.b.docbk_ver = 4
|
||||
return "docbk"
|
||||
else
|
||||
return "sgml"
|
||||
end
|
||||
end,
|
||||
["sgml"] = function()
|
||||
local top_file = getlines(1, 5)
|
||||
if top_file:find("linuxdoc") then
|
||||
return "sgmlnx"
|
||||
elseif
|
||||
getlines(1):find("%<%!DOCTYPE.*DocBook")
|
||||
or getlines(2):find("<!DOCTYPE.*DocBook")
|
||||
then
|
||||
vim.b.docbk_type = "sgml"
|
||||
vim.b.docbk_ver = 4
|
||||
return "docbk"
|
||||
else
|
||||
return "sgml"
|
||||
end
|
||||
end,
|
||||
["reg"] = function()
|
||||
if
|
||||
getlines(1):find(
|
||||
"^REGEDIT[0-9]*%s*$|^Windows Registry Editor Version %d*%.%d*%s*$"
|
||||
)
|
||||
then
|
||||
return "registry"
|
||||
end
|
||||
end,
|
||||
["pm"] = function()
|
||||
if getlines(1):find("XPM2") then
|
||||
return "xpm2"
|
||||
elseif getlines(1):find("XPM") then
|
||||
return "xpm"
|
||||
else
|
||||
return "perl"
|
||||
end
|
||||
end,
|
||||
["me"] = function()
|
||||
if
|
||||
vim.fn.expand("<afile>") ~= "read.me"
|
||||
and vim.fn.expand("<afile>") ~= "click.me"
|
||||
then
|
||||
return "nroff"
|
||||
end
|
||||
end,
|
||||
["m4"] = function()
|
||||
if not vim.fn.expand("<afile>"):find("(html.m4$|fvwm2rc)") then
|
||||
return "m4"
|
||||
end
|
||||
end,
|
||||
["edn"] = function()
|
||||
if getlines(1):find("^%s*%(%s*edif") then
|
||||
return "edif"
|
||||
else
|
||||
return "clojure"
|
||||
end
|
||||
end,
|
||||
["rul"] = function()
|
||||
local top_file = getlines(1, 6)
|
||||
if top_file:find("InstallShield") then
|
||||
return "ishd"
|
||||
else
|
||||
return "diva"
|
||||
end
|
||||
end,
|
||||
["prg"] = function()
|
||||
if vim.fn.exists("g:filetype_prg") == 1 then
|
||||
return vim.g.filetype_prg
|
||||
else
|
||||
return "clipper"
|
||||
end
|
||||
end,
|
||||
["cpy"] = function()
|
||||
if getlines(1):find("^%#%#") then
|
||||
return "python"
|
||||
else
|
||||
return "cobol"
|
||||
end
|
||||
end,
|
||||
-- Complicated functions
|
||||
["asp"] = function()
|
||||
if vim.g.filetype_asp ~= nil then
|
||||
return vim.g.filetype_asp
|
||||
elseif getlines(1, 3):find("perlscript") then
|
||||
return "aspperl"
|
||||
else
|
||||
return "aspvbs"
|
||||
end
|
||||
end,
|
||||
["asa"] = function()
|
||||
if vim.g.filetype_asa ~= nil then
|
||||
return vim.g.filetype_asa
|
||||
else
|
||||
return "aspvbs"
|
||||
end
|
||||
end,
|
||||
["cmd"] = function()
|
||||
if getlines(1):find("^%/%*") then
|
||||
return "rexx"
|
||||
else
|
||||
return "dosbatch"
|
||||
end
|
||||
end,
|
||||
["cc"] = function()
|
||||
if vim.fn.exists("cynlib_syntax_for_cc") == 1 then
|
||||
return "cynlib"
|
||||
else
|
||||
return "cpp"
|
||||
end
|
||||
end,
|
||||
["cpp"] = function()
|
||||
if vim.fn.exists("cynlib_syntax_for_cpp") == 1 then
|
||||
return "cynlib"
|
||||
else
|
||||
return "cpp"
|
||||
end
|
||||
end,
|
||||
["inp"] = function()
|
||||
vim.cmd([[call dist#ft#Check_inp()]])
|
||||
end,
|
||||
["asm"] = function()
|
||||
vim.cmd([[call dist#ft#FTasm()]])
|
||||
end,
|
||||
["s"] = function()
|
||||
vim.cmd([[call dist#ft#FTasm()]])
|
||||
end,
|
||||
["S"] = function()
|
||||
vim.cmd([[call dist#ft#FTasm()]])
|
||||
end,
|
||||
["a"] = function()
|
||||
vim.cmd([[call dist#ft#FTasm()]])
|
||||
end,
|
||||
["A"] = function()
|
||||
vim.cmd([[call dist#ft#FTasm()]])
|
||||
end,
|
||||
["mac"] = function()
|
||||
vim.cmd([[call dist#ft#FTasm()]])
|
||||
end,
|
||||
["lst"] = function()
|
||||
vim.cmd([[call dist#ft#FTasm()]])
|
||||
end,
|
||||
["bas"] = function()
|
||||
vim.cmd([[call dist#ft#FTVB("basic")]])
|
||||
end,
|
||||
["btm"] = function()
|
||||
vim.cmd([[call dist#ft#FTbtm()]])
|
||||
end,
|
||||
["db"] = function()
|
||||
vim.cmd([[call dist#ft#BindzoneCheck('')]])
|
||||
end,
|
||||
["c"] = function()
|
||||
vim.cmd([[call dist#ft#FTlpc()]])
|
||||
end,
|
||||
["h"] = function()
|
||||
vim.cmd([[call dist#ft#FTheader()]])
|
||||
end,
|
||||
["ch"] = function()
|
||||
vim.cmd([[call dist#ft#FTchange()]])
|
||||
end,
|
||||
["ent"] = function()
|
||||
vim.cmd([[call dist#ft#FTent()]])
|
||||
end,
|
||||
["ex"] = function()
|
||||
vim.cmd([[call dist#ft#ExCheck()]])
|
||||
end,
|
||||
["eu"] = function()
|
||||
vim.cmd([[call dist#ft#EuphoriaCheck()]])
|
||||
end,
|
||||
["ew"] = function()
|
||||
vim.cmd([[call dist#ft#EuphoriaCheck()]])
|
||||
end,
|
||||
["exu"] = function()
|
||||
vim.cmd([[call dist#ft#EuphoriaCheck()]])
|
||||
end,
|
||||
["exw"] = function()
|
||||
vim.cmd([[call dist#ft#EuphoriaCheck()]])
|
||||
end,
|
||||
["EU"] = function()
|
||||
vim.cmd([[call dist#ft#EuphoriaCheck()]])
|
||||
end,
|
||||
["EW"] = function()
|
||||
vim.cmd([[call dist#ft#EuphoriaCheck()]])
|
||||
end,
|
||||
["EX"] = function()
|
||||
vim.cmd([[call dist#ft#EuphoriaCheck()]])
|
||||
end,
|
||||
["EXU"] = function()
|
||||
vim.cmd([[call dist#ft#EuphoriaCheck()]])
|
||||
end,
|
||||
["EXW"] = function()
|
||||
vim.cmd([[call dist#ft#EuphoriaCheck()]])
|
||||
end,
|
||||
["d"] = function()
|
||||
vim.cmd([[call dist#ft#DtraceCheck()]])
|
||||
end,
|
||||
["com"] = function()
|
||||
vim.cmd([[call dist#ft#BindzoneCheck('dcl')]])
|
||||
end,
|
||||
["e"] = function()
|
||||
vim.cmd([[call dist#ft#FTe()]])
|
||||
end,
|
||||
["E"] = function()
|
||||
vim.cmd([[call dist#ft#FTe()]])
|
||||
end,
|
||||
["html"] = function()
|
||||
vim.cmd([[call dist#ft#FThtml()]])
|
||||
end,
|
||||
["htm"] = function()
|
||||
vim.cmd([[call dist#ft#FThtml()]])
|
||||
end,
|
||||
["shtml"] = function()
|
||||
vim.cmd([[call dist#ft#FThtml()]])
|
||||
end,
|
||||
["stm"] = function()
|
||||
vim.cmd([[call dist#ft#FThtml()]])
|
||||
end,
|
||||
["idl"] = function()
|
||||
vim.cmd([[call dist#ft#FTidl()]])
|
||||
end,
|
||||
["pro"] = function()
|
||||
vim.cmd([[call dist#ft#ProtoCheck('idlang')]])
|
||||
end,
|
||||
["m"] = function()
|
||||
vim.cmd([[call dist#ft#FTm()]])
|
||||
end,
|
||||
["mms"] = function()
|
||||
vim.cmd([[call dist#ft#FTmms()]])
|
||||
end,
|
||||
["*.mm"] = function()
|
||||
vim.cmd([[call dist#ft#FTmm()]])
|
||||
end,
|
||||
["pp"] = function()
|
||||
vim.cmd([[call dist#ft#FTpp()]])
|
||||
end,
|
||||
["pl"] = function()
|
||||
vim.cmd([[call dist#ft#FTpl()]])
|
||||
end,
|
||||
["PL"] = function()
|
||||
vim.cmd([[call dist#ft#FTpl()]])
|
||||
end,
|
||||
["inc"] = function()
|
||||
vim.cmd([[call dist#ft#FTinc()]])
|
||||
end,
|
||||
["w"] = function()
|
||||
vim.cmd([[call dist#ft#FTprogress_cweb()]])
|
||||
end,
|
||||
["i"] = function()
|
||||
vim.cmd([[call dist#ft#FTprogress_asm()]])
|
||||
end,
|
||||
["p"] = function()
|
||||
vim.cmd([[call dist#ft#FTprogress_pascal()]])
|
||||
end,
|
||||
["r"] = function()
|
||||
vim.cmd([[call dist#ft#FTr()]])
|
||||
end,
|
||||
["R"] = function()
|
||||
vim.cmd([[call dist#ft#FTr()]])
|
||||
end,
|
||||
["mc"] = function()
|
||||
vim.cmd([[call dist#ft#McSetf()]])
|
||||
end,
|
||||
["ebuild"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH("bash")]])
|
||||
end,
|
||||
["bash"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH("bash")]])
|
||||
end,
|
||||
["eclass"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH("bash")]])
|
||||
end,
|
||||
["ksh"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH("ksh")]])
|
||||
end,
|
||||
["etc/profile"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH(getline(1))]])
|
||||
end,
|
||||
["sh"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH(getline(1))]])
|
||||
end,
|
||||
["env"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH(getline(1))]])
|
||||
end,
|
||||
["tcsh"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeShell("tcsh")]])
|
||||
end,
|
||||
["csh"] = function()
|
||||
vim.cmd([[call dist#ft#CSH()]])
|
||||
end,
|
||||
["rules"] = function()
|
||||
vim.cmd([[call dist#ft#FTRules()]])
|
||||
end,
|
||||
["sql"] = function()
|
||||
vim.cmd([[call dist#ft#SQL()]])
|
||||
end,
|
||||
["tex"] = function()
|
||||
vim.cmd([[call dist#ft#FTtex()]])
|
||||
end,
|
||||
["frm"] = function()
|
||||
vim.cmd([[call dist#ft#FTVB("form")]])
|
||||
end,
|
||||
["xml"] = function()
|
||||
vim.cmd([[call dist#ft#FTxml()]])
|
||||
end,
|
||||
["y"] = function()
|
||||
vim.cmd([[call dist#ft#FTy()]])
|
||||
end,
|
||||
["dtml"] = function()
|
||||
vim.cmd([[call dist#ft#FThtml()]])
|
||||
end,
|
||||
["pt"] = function()
|
||||
vim.cmd([[call dist#ft#FThtml()]])
|
||||
end,
|
||||
["cpt"] = function()
|
||||
vim.cmd([[call dist#ft#FThtml()]])
|
||||
end,
|
||||
["zsql"] = function()
|
||||
vim.cmd([[call dist#ft#SQL()]])
|
||||
end,
|
||||
}
|
||||
M.literal = {
|
||||
["xorg.conf-4"] = function()
|
||||
vim.b.xf86conf_xfree86_version = 4
|
||||
return "xf86conf"
|
||||
end,
|
||||
["xorg.conf"] = function()
|
||||
vim.b.xf86conf_xfree86_version = 4
|
||||
return "xf86conf"
|
||||
end,
|
||||
["XF86Config"] = function()
|
||||
if getlines(1):find("XConfigurator") then
|
||||
vim.b.xf86conf_xfree86_version = 3
|
||||
end
|
||||
return "xf86conf"
|
||||
end,
|
||||
["INDEX"] = function()
|
||||
if
|
||||
getlines(1):find(
|
||||
"^%s*(distribution|installed_software|root|bundle|product)%s*$"
|
||||
)
|
||||
then
|
||||
return "psf"
|
||||
end
|
||||
end,
|
||||
["INFO"] = function()
|
||||
if
|
||||
getlines(1):find(
|
||||
"^%s*(distribution|installed_software|root|bundle|product)%s*$"
|
||||
)
|
||||
then
|
||||
return "psf"
|
||||
end
|
||||
end,
|
||||
["control"] = function()
|
||||
if getlines(1):find("^Source%:") then
|
||||
return "debcontrol"
|
||||
end
|
||||
end,
|
||||
["NEWS"] = function()
|
||||
if getlines(1):find("%; urgency%=") then
|
||||
return "debchangelog"
|
||||
end
|
||||
end,
|
||||
["indent.pro"] = function()
|
||||
vim.cmd([[call dist#ft#ProtoCheck('indent')]])
|
||||
end,
|
||||
[".bashrc"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH("bash")]])
|
||||
end,
|
||||
["bashrc"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH("bash")]])
|
||||
end,
|
||||
["bash.bashrc"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH("bash")]])
|
||||
end,
|
||||
["PKGBUILD"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH("bash")]])
|
||||
end,
|
||||
["APKBUILD"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH("bash")]])
|
||||
end,
|
||||
[".kshrc"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH("ksh")]])
|
||||
end,
|
||||
[".profile"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH(getline(1))]])
|
||||
end,
|
||||
[".tcshrc"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeShell("tcsh")]])
|
||||
end,
|
||||
["tcsh.tcshrc"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeShell("tcsh")]])
|
||||
end,
|
||||
["tcsh.login"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeShell("tcsh")]])
|
||||
end,
|
||||
[".login"] = function()
|
||||
vim.cmd([[call dist#ft#CSH()]])
|
||||
end,
|
||||
[".cshrc"] = function()
|
||||
vim.cmd([[call dist#ft#CSH()]])
|
||||
end,
|
||||
["csh.cshrc"] = function()
|
||||
vim.cmd([[call dist#ft#CSH()]])
|
||||
end,
|
||||
["csh.login"] = function()
|
||||
vim.cmd([[call dist#ft#CSH()]])
|
||||
end,
|
||||
["csh.logout"] = function()
|
||||
vim.cmd([[call dist#ft#CSH()]])
|
||||
end,
|
||||
[".alias"] = function()
|
||||
vim.cmd([[call dist#ft#CSH()]])
|
||||
end,
|
||||
[".d"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH("bash")]])
|
||||
end,
|
||||
}
|
||||
|
||||
M.complex = {
|
||||
[".*/xorg%.conf%.d/.*%.conf"] = function()
|
||||
vim.b.xf86conf_xfree86_version = 4
|
||||
return "xf86conf"
|
||||
end,
|
||||
[".*printcap"] = function()
|
||||
vim.b.ptcap_type = "print"
|
||||
return "ptcap"
|
||||
end,
|
||||
[".*termcap"] = function()
|
||||
vim.b.ptcap_type = "term"
|
||||
return "ptcap"
|
||||
end,
|
||||
["[cC]hange[lL]og"] = function()
|
||||
if getlines(1):find("%; urgency%=") then
|
||||
return "debchangelog"
|
||||
else
|
||||
return "changelog"
|
||||
end
|
||||
end,
|
||||
["%.bashrc.*"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH("bash")]])
|
||||
end,
|
||||
["%.bash[_-]profile"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH("bash")]])
|
||||
end,
|
||||
["%.bash[_-]logout"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH("bash")]])
|
||||
end,
|
||||
["%.bash[_-]aliases"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH("bash")]])
|
||||
end,
|
||||
["%.bash%-fc[_-]"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH("bash")]])
|
||||
end,
|
||||
["PKGBUILD.*"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH("bash")]])
|
||||
end,
|
||||
["APKBUILD.*"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH("bash")]])
|
||||
end,
|
||||
["%.kshrc.*"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH("ksh")]])
|
||||
end,
|
||||
["%.profile.*"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeSH(getline(1))]])
|
||||
end,
|
||||
["%.tcshrc.*"] = function()
|
||||
vim.cmd([[call dist#ft#SetFileTypeShell("tcsh")]])
|
||||
end,
|
||||
["%.login.*"] = function()
|
||||
vim.cmd([[call dist#ft#CSH()]])
|
||||
end,
|
||||
["%.cshrc.*"] = function()
|
||||
vim.cmd([[call dist#ft#CSH()]])
|
||||
end,
|
||||
}
|
||||
|
||||
M.shebang = {
|
||||
["bash"] = "sh",
|
||||
["node"] = "javascript",
|
||||
["python3"] = "python",
|
||||
}
|
||||
|
||||
return M
|
|
@ -0,0 +1,164 @@
|
|||
return {
|
||||
[".a2psrc"] = "a2ps",
|
||||
[".asoundrc"] = "alsaconf",
|
||||
[".babelrc"] = "json",
|
||||
[".cdrdao"] = "cdrdaoconf",
|
||||
[".cvsrc"] = "cvsrc",
|
||||
[".dictrc"] = "dictconf",
|
||||
[".dir_colors"] = "dircolors",
|
||||
[".dircolors"] = "dircolors",
|
||||
[".editorconfig"] = "dosini",
|
||||
[".emacs"] = "lisp",
|
||||
[".eslintrc"] = "json",
|
||||
[".exrc"] = "vim",
|
||||
[".fetchmailrc"] = "fetchmail",
|
||||
[".firebaserc"] = "json",
|
||||
[".gdbinit"] = "gdb",
|
||||
[".gitconfig"] = "gitconfig",
|
||||
[".gitmodules"] = "gitconfig",
|
||||
[".gnashpluginrc"] = "gnash",
|
||||
[".gnashrc"] = "gnash",
|
||||
[".gprc"] = "gp",
|
||||
[".gtkrc"] = "gtkrc",
|
||||
[".htaccess"] = "apache",
|
||||
[".indent.pro"] = "indent",
|
||||
[".inputrc"] = "readline",
|
||||
[".irbrc"] = "ruby",
|
||||
[".lftprc"] = "lftp",
|
||||
[".mailcap"] = "mailcap",
|
||||
[".mrxvtrc"] = "mrxvtrc",
|
||||
[".netrc"] = "netrc",
|
||||
[".npmrc"] = "dosini",
|
||||
[".ocamlinit"] = "ocaml",
|
||||
[".pam_environment"] = "pamenv",
|
||||
[".pinerc"] = "pine",
|
||||
[".pinercex"] = "pine",
|
||||
[".povrayrc"] = "povini",
|
||||
[".prettierrc"] = "json",
|
||||
[".procmail"] = "procmail",
|
||||
[".procmailrc"] = "procmail",
|
||||
[".pythonrc"] = "python",
|
||||
[".pythonstartup"] = "python",
|
||||
[".ratpoisonrc"] = "ratpoison",
|
||||
[".reminders"] = "remind",
|
||||
[".Rprofile"] = "r",
|
||||
[".sawfishrc"] = "lisp",
|
||||
[".sbclrc"] = "lisp",
|
||||
[".screenrc"] = "screen",
|
||||
[".slrnrc"] = "slrnrc",
|
||||
[".stylelintrc"] = "json",
|
||||
[".tfrc"] = "tf",
|
||||
[".tidyrc"] = "tidy",
|
||||
[".viminfo"] = "viminfo",
|
||||
[".wgetrc"] = "wget",
|
||||
[".wvdialrc"] = "wvdial",
|
||||
[".zcompdump"] = "zsh",
|
||||
[".zfbfmarks"] = "zsh",
|
||||
[".zlogin"] = "zsh",
|
||||
[".zlogout"] = "zsh",
|
||||
[".zprofile"] = "zsh",
|
||||
[".zshenv"] = "zsh",
|
||||
[".zshrc"] = "zsh",
|
||||
["Appfile"] = "ruby",
|
||||
["Brewfile"] = "ruby",
|
||||
["BUILD"] = "bzl",
|
||||
["CMakeLists.txt"] = "cmake",
|
||||
["COMMIT_EDITMSG"] = "gitcommit",
|
||||
["Containerfile"] = "dockerfile",
|
||||
["Dockerfile"] = "dockerfile",
|
||||
["Fastfile"] = "ruby",
|
||||
["Gemfile"] = "ruby",
|
||||
["Kconfig"] = "kconfig",
|
||||
["Kconfig.debug"] = "kconfig",
|
||||
["MERGE_MSG"] = "gitcommit",
|
||||
["Neomuttrc"] = "neomuttrc",
|
||||
["Pipfile"] = "config",
|
||||
["Pipfile.lock"] = "json",
|
||||
["Podfile"] = "ruby",
|
||||
["Puppetfile"] = "ruby",
|
||||
["README"] = "text",
|
||||
["SConstruct"] = "python",
|
||||
["TAG_EDITMSG"] = "gitcommit",
|
||||
["_exrc"] = "vim",
|
||||
["_viminfo"] = "viminfo",
|
||||
["a2psrc"] = "a2ps",
|
||||
["apt.conf"] = "aptconf",
|
||||
["auto.master"] = "conf",
|
||||
["build.xml"] = "ant",
|
||||
["cabal.config"] = "cabalconfig",
|
||||
["cabal.project"] = "cabalproject",
|
||||
["calendar"] = "calendar",
|
||||
["catalog"] = "catalog",
|
||||
["cfengine.conf"] = "cfengine",
|
||||
[".clang-format"] = "yaml",
|
||||
["_clang-format"] = "yaml",
|
||||
["cm3.cfg"] = "m3quake",
|
||||
["configure.ac"] = "config",
|
||||
["configure.in"] = "config",
|
||||
["denyhosts.conf"] = "denyhosts",
|
||||
["dict.conf"] = "dictconf",
|
||||
["dictd.conf"] = "dictdconf",
|
||||
["elinks.conf"] = "elinks",
|
||||
["exim.conf"] = "exim",
|
||||
["exports"] = "exports",
|
||||
["fglrxrc"] = "xml",
|
||||
["fstab"] = "fstab",
|
||||
["gitconfig"] = "gitconfig",
|
||||
["git-rebase-todo"] = "gitrebase",
|
||||
["gitolite.conf"] = "gitolite",
|
||||
["gnashpluginrc"] = "gnash",
|
||||
["gnashrc"] = "gnash",
|
||||
["go.mod"] = "gomod",
|
||||
["gtkrc"] = "gtkrc",
|
||||
["indentrc"] = "indent",
|
||||
["inittab"] = "inittab",
|
||||
["inputrc"] = "readline",
|
||||
["ipf.conf"] = "ipfilter",
|
||||
["ipf.rules"] = "ipfilter",
|
||||
["ipf6.conf"] = "ipfilter",
|
||||
["irbrc"] = "ruby",
|
||||
["Jenkinsfile"] = "groovy",
|
||||
["lftp.conf"] = "lftp",
|
||||
["lilo.conf"] = "lilo",
|
||||
["lltxxxxx.txt"] = "gedcom",
|
||||
["lynx.cfg"] = "lynx",
|
||||
["m3makefile"] = "m3build",
|
||||
["m3overrides"] = "m3build",
|
||||
["mailcap"] = "mailcap",
|
||||
["main.cf"] = "pfmain",
|
||||
["man.config"] = "manconf",
|
||||
["meson.build"] = "meson",
|
||||
["meson_options.txt"] = "meson",
|
||||
["mplayer.conf"] = "mplayerconf",
|
||||
["mrxvtrc"] = "mrxvtrc",
|
||||
["mtab"] = "fstab",
|
||||
["named.root"] = "bindzone",
|
||||
["npmrc"] = "dosini",
|
||||
["opam"] = "opam",
|
||||
["pam_env.conf"] = "pamenv",
|
||||
["pf.conf"] = "pf",
|
||||
["pinerc"] = "pine",
|
||||
["pinercex"] = "pine",
|
||||
["ratpoisonrc"] = "ratpoison",
|
||||
["resolv.conf"] = "resolv",
|
||||
["robots.txt"] = "robots",
|
||||
["sbclrc"] = "lisp",
|
||||
["screenrc"] = "screen",
|
||||
["sendmail.cf"] = "sm",
|
||||
["smb.conf"] = "samba",
|
||||
["snort.conf"] = "hog",
|
||||
["squid.conf"] = "squid",
|
||||
["ssh_config"] = "sshconfig",
|
||||
["sshd_config"] = "sshdconfig",
|
||||
["sudoers.tmp"] = "sudoers",
|
||||
["tags"] = "tags",
|
||||
["texmf.cnf"] = "texmf",
|
||||
["tfrc"] = "tf",
|
||||
["tidy.conf"] = "tidy",
|
||||
["tidyrc"] = "tidy",
|
||||
["trustees.conf"] = "trustees",
|
||||
["vgrindefs"] = "vgrindefs",
|
||||
["vision.conf"] = "hog",
|
||||
["wgetrc"] = "wget",
|
||||
["wvdial.conf"] = "wvdial",
|
||||
}
|
|
@ -0,0 +1,238 @@
|
|||
"""This is the script I used to automatically convert most of the vim autocommands into lua.
|
||||
|
||||
Warning: This is crappy code. Please don't use this for anything! I only included this file
|
||||
in case anyone was curious how I made the plugin.
|
||||
"""
|
||||
|
||||
import pprint
|
||||
import re
|
||||
|
||||
VIM_SCRIPT = "misc/filetype.vim"
|
||||
|
||||
|
||||
def find_normal_globs():
|
||||
ext_glob = re.compile(r"au\s+BufNewFile,BufRead\s+([\+\*\,\/\.\w]+)\s+setf\s+(\w+)")
|
||||
asterisks = re.compile(r"\*")
|
||||
mapping: dict[str, str] = {}
|
||||
for line in lines:
|
||||
# if "cxx" in line:
|
||||
# print(line)
|
||||
# exit(1)
|
||||
match = ext_glob.search(line)
|
||||
if match is None:
|
||||
continue
|
||||
|
||||
glob, ft = match.groups()
|
||||
for g in glob.split(","):
|
||||
mapping[g] = ft
|
||||
# print(f'["{g}"] = "{ft}"')
|
||||
|
||||
# pprint.pprint(mapping)
|
||||
extensions: dict[str, str] = {}
|
||||
simple: dict[str, str] = {}
|
||||
complex: dict[str, str] = {}
|
||||
|
||||
for glob, ft in mapping.items():
|
||||
num_asts = len(asterisks.findall(glob))
|
||||
if glob.startswith("*") and num_asts == 1:
|
||||
extensions[glob] = ft
|
||||
elif num_asts == 0:
|
||||
simple[glob] = ft
|
||||
|
||||
else:
|
||||
complex[glob] = ft
|
||||
|
||||
lua_print(simple)
|
||||
lua_print(extensions)
|
||||
lua_print(complex)
|
||||
|
||||
|
||||
def find_function_cmds(flines):
|
||||
non_setf = re.compile(r"au\s+BufNewFile,BufRead\s+(\S+)\s*$")
|
||||
for line in flines:
|
||||
m = non_setf.search(line)
|
||||
if m is not None:
|
||||
print(m.group(1))
|
||||
elif "|" in line and "au" in line:
|
||||
print(line)
|
||||
|
||||
|
||||
def find_star_setfs():
|
||||
star_glob = re.compile(
|
||||
r"au\s+BufNewFile,BufRead\s+(\S+)\s+call\s+s:StarSetf\('(\w+)'\)"
|
||||
)
|
||||
mappings: dict[str, str] = {}
|
||||
for line in lines:
|
||||
match = star_glob.search(line)
|
||||
if match is None:
|
||||
continue
|
||||
|
||||
glob, ft = match.groups()
|
||||
mappings[glob] = ft
|
||||
|
||||
for k, v in mappings.items():
|
||||
print(f"['{k}'] = '{v}',")
|
||||
|
||||
|
||||
text = """M.star_sets = {
|
||||
[".*/etc/Muttrc%.d/.*"] = "muttrc",
|
||||
[".*/etc/proftpd/.*%.conf.*,.*/etc/proftpd/conf%..*/.*"] = "apachestyle",
|
||||
["proftpd%.conf.*"] = "apachestyle",
|
||||
["access%.conf.*,apache%.conf.*,apache2%.conf.*,httpd%.conf.*,srm%.conf.*"] = "apache",
|
||||
[".*/etc/apache2/.*%.conf.*,.*/etc/apache2/conf%..*/.*,.*/etc/apache2/mods-.*/.*,.*/etc/apache2/sites-.*/.*,.*/etc/httpd/conf%.d/.*%.conf.*"] = "apache",
|
||||
[".*asterisk/.*%.conf.*"] = "asterisk",
|
||||
[".*asterisk.*/.*voicemail%.conf.*"] = "asteriskvm",
|
||||
[".*/named/db%..*,.*/bind/db%..*"] = "bindzone",
|
||||
["cabal%.project%..*"] = "cabalproject",
|
||||
["crontab,crontab%..*,.*/etc/cron%.d/.*"] = "crontab",
|
||||
[".*/etc/dnsmasq%.d/.*"] = "dnsmasq",
|
||||
["drac%..*"] = "dracula",
|
||||
[".*/%.fvwm/.*"] = "fvwm",
|
||||
[".*/tmp/lltmp.*"] = "gedcom",
|
||||
[".*/%.gitconfig%.d/.*,/etc/gitconfig%.d/.*"] = "gitconfig",
|
||||
[".*/gitolite-admin/conf/.*"] = "gitolite",
|
||||
["%.gtkrc.*,gtkrc.*"] = "gtkrc",
|
||||
["Prl.*%..*,JAM.*%..*"] = "jam",
|
||||
[".*%.properties_??_??_.*"] = "jproperties",
|
||||
["Kconfig%..*"] = "kconfig",
|
||||
["lilo%.conf.*"] = "lilo",
|
||||
[".*/etc/logcheck/.*%.d.*/.*"] = "logcheck",
|
||||
["[mM]akefile.*"] = "make",
|
||||
["[rR]akefile.*"] = "ruby",
|
||||
["reportbug-.*"] = "mail",
|
||||
[".*/etc/modprobe%..*"] = "modconf",
|
||||
["%.mutt{ng,}rc.*,.*/%.mutt{ng,}/mutt{ng,}rc.*"] = "muttrc",
|
||||
["mutt{ng,}rc.*,Mutt{ng,}rc.*"] = "muttrc",
|
||||
["%.neomuttrc.*,.*/%.neomutt/neomuttrc.*"] = "neomuttrc",
|
||||
["neomuttrc.*,Neomuttrc.*"] = "neomuttrc",
|
||||
["tmac%..*"] = "nroff",
|
||||
["/etc/hostname%..*"] = "config",
|
||||
[".*/etc/pam%.d/.*"] = "pamconf",
|
||||
["%.reminders.*"] = "remind",
|
||||
["sgml%.catalog.*"] = "catalog",
|
||||
[".*%.vhdl_[0-9].*"] = "vhdl",
|
||||
[".*vimrc.*"] = "vim",
|
||||
["Xresources.*,.*/app-defaults/.*,.*/Xresources/.*"] = "xdefaults",
|
||||
[".*xmodmap.*"] = "xmodmap",
|
||||
[".*/etc/xinetd%.d/.*"] = "xinetd",
|
||||
[".*/etc/yum%.repos%.d/.*"] = "dosini",
|
||||
["%.zsh.*,%.zlog.*,%.zcompdump.*"] = "zsh",
|
||||
["zsh.*,zlog.*"] = "zsh",
|
||||
}"""
|
||||
# def find_call_setfs():
|
||||
# star_glob = re.compile(r"au\s+BufNewFile,BufRead\s+(\S+)\s+call\s+(.+)")
|
||||
# mappings: dict[str, str] = {}
|
||||
# for line in lines:
|
||||
# match = star_glob.search(line)
|
||||
# if match is None:
|
||||
# continue
|
||||
|
||||
# glob, ft = match.groups()
|
||||
# if "StarS" in ft:
|
||||
# continue
|
||||
# mappings[glob] = ft
|
||||
|
||||
# extension = {}
|
||||
# literal = {}
|
||||
# complex = {}
|
||||
# for glob, ft in mappings.items():
|
||||
# num_asts = len(asterisks.findall(glob))
|
||||
# if glob.startswith('*') and num_asts == 1:
|
||||
|
||||
# print(f"['{k}'] = [[call {v}]],")
|
||||
|
||||
|
||||
def lua_print(d: dict):
|
||||
print("M.thing = {")
|
||||
for k, v in d.items():
|
||||
print(f"['{k}'] = [[{v}]],")
|
||||
print("}")
|
||||
|
||||
|
||||
def find_function_globs():
|
||||
ext_glob = re.compile(r"au\s+BufNewFile,BufRead\s+(\S+)\s+call\s+(.+)")
|
||||
asterisks = re.compile(r"\*")
|
||||
mapping: dict[str, str] = {}
|
||||
for line in lines:
|
||||
match = ext_glob.search(line)
|
||||
|
||||
if match is None:
|
||||
continue
|
||||
|
||||
glob, ft = match.groups()
|
||||
if "s:Star" in ft:
|
||||
continue
|
||||
|
||||
for g in glob.split(","):
|
||||
mapping[g] = ft
|
||||
|
||||
extensions: dict[str, str] = {}
|
||||
simple: dict[str, str] = {}
|
||||
complex: dict[str, str] = {}
|
||||
|
||||
for glob, ft in mapping.items():
|
||||
num_asts = len(asterisks.findall(glob))
|
||||
if glob.startswith("*") and num_asts == 1:
|
||||
extensions[glob] = ft
|
||||
elif num_asts == 0:
|
||||
simple[glob] = ft
|
||||
|
||||
else:
|
||||
complex[glob] = ft
|
||||
|
||||
lua_print(simple)
|
||||
lua_print(extensions)
|
||||
lua_print(complex)
|
||||
|
||||
|
||||
def convert_glob_to_lua_regex(glob):
|
||||
glob = glob.replace(".", "%.")
|
||||
glob = glob.replace("*", ".*")
|
||||
return glob
|
||||
|
||||
# ["*.git/modules/*/config",
|
||||
|
||||
|
||||
def fix_lua_regexes(text):
|
||||
keyvals = re.compile(r'\["([^"]+)"\] = "([^"]+)"')
|
||||
matches = {}
|
||||
for line in text.split("\n"):
|
||||
result = keyvals.search(line)
|
||||
if result is None:
|
||||
continue
|
||||
|
||||
glob, ft = result.groups()
|
||||
matches[glob] = ft
|
||||
|
||||
fixed = {}
|
||||
for glob, ft in matches.items():
|
||||
if "{" not in glob:
|
||||
for pat in glob.split(","):
|
||||
fixed[pat] = ft
|
||||
else:
|
||||
fixed[glob] = ft
|
||||
lua_print(fixed)
|
||||
|
||||
|
||||
def convert_lua_regex_vim(e):
|
||||
e = re.sub(r"%\.", r"\.", e)
|
||||
e = re.sub("/", r"\/", e)
|
||||
return e
|
||||
|
||||
|
||||
def convert_lua_regexps_to_vim(flines):
|
||||
for line in flines:
|
||||
matches = re.search(r'"([^"]+)":', line)
|
||||
if matches is None:
|
||||
continue
|
||||
|
||||
matches = matches.group(1)
|
||||
matches = re.sub(r"%\.", r"\.", matches)
|
||||
matches = re.sub("/", r"\/", matches)
|
||||
|
||||
new_line = re.sub(r'"([^"]+)":', '"' + matches + '":', line)
|
||||
print(new_line.strip())
|
||||
|
||||
|
||||
lines = open(VIM_SCRIPT).readlines()
|
||||
find_function_cmds(lines)
|
|
@ -0,0 +1,8 @@
|
|||
# Customized
|
||||
indent_type = "Spaces"
|
||||
column_width = 79
|
||||
# Default
|
||||
line_endings = "Unix"
|
||||
indent_width = 4
|
||||
quote_style = "AutoPreferDouble"
|
||||
no_call_parentheses = false
|
|
@ -0,0 +1,71 @@
|
|||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: CI
|
||||
|
||||
# Controls when the workflow will run
|
||||
on:
|
||||
# Triggers the workflow on push or pull request events but only for the main branch
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
# This workflow contains a single job called "build"
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
neovim_branch: ['v0.7.0', 'master']
|
||||
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
NEOVIM_BRANCH: ${{ matrix.neovim_branch }}
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup build dependencies
|
||||
run: |
|
||||
sudo apt update &&
|
||||
sudo apt install -y \
|
||||
autoconf \
|
||||
automake \
|
||||
cmake \
|
||||
g++ \
|
||||
gettext \
|
||||
gperf \
|
||||
libjemalloc-dev \
|
||||
libluajit-5.1-dev \
|
||||
libmsgpack-dev \
|
||||
libtermkey-dev \
|
||||
libtool \
|
||||
libtool-bin \
|
||||
libunibilium-dev \
|
||||
libvterm-dev \
|
||||
lua-bitop \
|
||||
lua-lpeg \
|
||||
lua-mpack \
|
||||
ninja-build \
|
||||
pkg-config \
|
||||
unzip
|
||||
|
||||
- name: Cache neovim
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: neovim-${{env.NEOVIM_BRANCH}}
|
||||
key: build-${{env.NEOVIM_BRANCH}}
|
||||
|
||||
- name: Build Neovim
|
||||
run: make neovim NEOVIM_BRANCH=$NEOVIM_BRANCH
|
||||
|
||||
- name: Run Test
|
||||
run: make test
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022 Lewis Russell
|
||||
|
||||
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.
|
|
@ -0,0 +1,33 @@
|
|||
.DEFAULT_GOAL := test
|
||||
|
||||
NEOVIM_BRANCH := master
|
||||
|
||||
FILTER=.*
|
||||
|
||||
NEOVIM := neovim-$(NEOVIM_BRANCH)
|
||||
|
||||
.PHONY: neovim
|
||||
neovim: $(NEOVIM)
|
||||
|
||||
$(NEOVIM):
|
||||
git clone --depth 1 https://github.com/neovim/neovim --branch $(NEOVIM_BRANCH) $@
|
||||
make -C $@
|
||||
|
||||
export VIMRUNTIME=$(PWD)/$(NEOVIM)/runtime
|
||||
|
||||
.PHONY: test
|
||||
test: $(NEOVIM)
|
||||
$(NEOVIM)/.deps/usr/bin/busted \
|
||||
-v \
|
||||
--lazy \
|
||||
--helper=$(PWD)/test/preload.lua \
|
||||
--output test.busted.outputHandlers.nvim \
|
||||
--lpath=$(PWD)/$(NEOVIM)/?.lua \
|
||||
--lpath=$(PWD)/$(NEOVIM)/build/?.lua \
|
||||
--lpath=$(PWD)/$(NEOVIM)/runtime/lua/?.lua \
|
||||
--lpath=$(PWD)/?.lua \
|
||||
--lpath=$(PWD)/lua/?.lua \
|
||||
--filter=$(FILTER) \
|
||||
$(PWD)/test
|
||||
|
||||
-@stty sane
|
|
@ -0,0 +1,805 @@
|
|||
# impatient.nvim
|
||||
|
||||
[![CI](https://github.com/lewis6991/impatient.nvim/workflows/CI/badge.svg?branch=main)](https://github.com/lewis6991/impatient.nvim/actions?query=workflow%3ACI)
|
||||
|
||||
Speed up loading Lua modules in Neovim to improve startup time.
|
||||
|
||||
## Optimisations
|
||||
|
||||
This plugin does several things to speed loading Lua modules and files.
|
||||
|
||||
### Implements a chunk cache
|
||||
|
||||
This is done by using `loadstring` to compile the Lua modules to bytecode and stores them in a cache file. The cache is invalidated using as hash consisting of:
|
||||
|
||||
- The modified time (`sec` and `nsec`) of the file path.
|
||||
- The file size.
|
||||
|
||||
The cache file is located in `$XDG_CACHE_HOME/nvim/luacache_chunks`.
|
||||
|
||||
### Implements a module resolution cache
|
||||
|
||||
This is done by maintaining a table of module name to path. The cache is invalidated only if a path no longer exists.
|
||||
|
||||
The cache file is located in `$XDG_CACHE_HOME/nvim/luacache_modpaths`.
|
||||
|
||||
**Note**: This optimization breaks the loading order guarantee of the paths in `'runtimepath'`.
|
||||
If you rely on this ordering then you can disable this cache (`_G.__luacache_config = { modpaths = { enable = false } }`.
|
||||
See configuration below for more details.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Neovim v0.7
|
||||
|
||||
## Installation
|
||||
|
||||
[packer.nvim](https://github.com/wbthomason/packer.nvim):
|
||||
```lua
|
||||
-- Is using a standard Neovim install, i.e. built from source or using a
|
||||
-- provided appimage.
|
||||
use 'lewis6991/impatient.nvim'
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
To use impatient, you need only to include it near the top of your `init.lua` or `init.vim`.
|
||||
|
||||
init.lua:
|
||||
|
||||
```lua
|
||||
require('impatient')
|
||||
```
|
||||
|
||||
init.vim:
|
||||
|
||||
```viml
|
||||
lua require('impatient')
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
`:LuaCacheClear`:
|
||||
|
||||
Remove the loaded cache and delete the cache file. A new cache file will be created the next time you load Neovim.
|
||||
|
||||
`:LuaCacheLog`:
|
||||
|
||||
View log of impatient.
|
||||
|
||||
`:LuaCacheProfile`:
|
||||
|
||||
View profiling data. To enable, Impatient must be setup with:
|
||||
|
||||
```viml
|
||||
lua require'impatient'.enable_profile()
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Unlike most plugins which provide a `setup()` function, Impatient uses a configuration table stored in the global state, `_G.__luacache_config`.
|
||||
If you modify the default configuration, it must be done before `require('impatient')` is run.
|
||||
|
||||
Default config:
|
||||
|
||||
```lua
|
||||
_G.__luacache_config = {
|
||||
chunks = {
|
||||
enable = true,
|
||||
path = vim.fn.stdpath('cache')..'/luacache_chunks',
|
||||
},
|
||||
modpaths = {
|
||||
enable = true,
|
||||
path = vim.fn.stdpath('cache')..'/luacache_modpaths',
|
||||
}
|
||||
}
|
||||
require('impatient')
|
||||
```
|
||||
|
||||
## Performance Example
|
||||
|
||||
Measured on a M1 MacBook Air.
|
||||
|
||||
<details>
|
||||
<summary>Standard</summary>
|
||||
|
||||
```
|
||||
────────────┬────────────┐
|
||||
Resolve │ Load │
|
||||
────────────┼────────────┼─────────────────────────────────────────────────────────────────
|
||||
Time │ Time │ Module
|
||||
────────────┼────────────┼─────────────────────────────────────────────────────────────────
|
||||
54.337ms │ 34.257ms │ Total
|
||||
────────────┼────────────┼─────────────────────────────────────────────────────────────────
|
||||
7.264ms │ 0.470ms │ octo.colors
|
||||
3.154ms │ 0.128ms │ diffview.bootstrap
|
||||
2.086ms │ 0.231ms │ gitsigns
|
||||
0.320ms │ 0.982ms │ octo.date
|
||||
0.296ms │ 1.004ms │ octo.writers
|
||||
0.322ms │ 0.893ms │ octo.utils
|
||||
0.293ms │ 0.854ms │ vim.diagnostic
|
||||
0.188ms │ 0.819ms │ vim.lsp.util
|
||||
0.261ms │ 0.739ms │ vim.lsp
|
||||
0.330ms │ 0.620ms │ octo.model.octo-buffer
|
||||
0.392ms │ 0.422ms │ packer.load
|
||||
0.287ms │ 0.436ms │ octo.reviews
|
||||
0.367ms │ 0.325ms │ octo
|
||||
0.309ms │ 0.381ms │ octo.graphql
|
||||
0.454ms │ 0.221ms │ octo.base64
|
||||
0.295ms │ 0.338ms │ octo.reviews.file-panel
|
||||
0.305ms │ 0.306ms │ octo.reviews.file-entry
|
||||
0.183ms │ 0.386ms │ vim.treesitter.query
|
||||
0.418ms │ 0.149ms │ vim.uri
|
||||
0.342ms │ 0.213ms │ octo.config
|
||||
0.110ms │ 0.430ms │ nvim-lsp-installer.ui.status-win
|
||||
0.296ms │ 0.209ms │ octo.window
|
||||
0.202ms │ 0.288ms │ vim.lsp.rpc
|
||||
0.352ms │ 0.120ms │ octo.gh
|
||||
0.287ms │ 0.184ms │ octo.reviews.layout
|
||||
0.209ms │ 0.260ms │ vim.lsp.handlers
|
||||
0.108ms │ 0.360ms │ luasnip.nodes.snippet
|
||||
0.243ms │ 0.212ms │ dirvish
|
||||
0.289ms │ 0.159ms │ octo.mappings
|
||||
0.228ms │ 0.220ms │ trouble.view
|
||||
0.145ms │ 0.293ms │ plenary.job
|
||||
0.188ms │ 0.244ms │ vim.lsp.diagnostic
|
||||
0.032ms │ 0.391ms │ packer_compiled
|
||||
0.188ms │ 0.228ms │ vim.lsp.buf
|
||||
0.186ms │ 0.227ms │ vim.lsp.protocol
|
||||
0.141ms │ 0.264ms │ nvim-treesitter.install
|
||||
0.205ms │ 0.190ms │ vim.lsp._snippet
|
||||
0.114ms │ 0.281ms │ colorizer
|
||||
0.124ms │ 0.262ms │ nvim-treesitter.parsers
|
||||
0.331ms │ 0.052ms │ octo.model.body-metadata
|
||||
0.325ms │ 0.054ms │ octo.constants
|
||||
0.296ms │ 0.081ms │ octo.reviews.renderer
|
||||
0.326ms │ 0.050ms │ octo.model.thread-metadata
|
||||
0.258ms │ 0.117ms │ trouble
|
||||
0.106ms │ 0.267ms │ cmp.core
|
||||
0.286ms │ 0.085ms │ octo.completion
|
||||
0.120ms │ 0.250ms │ luasnip
|
||||
0.286ms │ 0.084ms │ octo.ui.bubbles
|
||||
0.068ms │ 0.298ms │ diffview.utils
|
||||
0.325ms │ 0.039ms │ octo.model.title-metadata
|
||||
0.126ms │ 0.234ms │ treesitter-context
|
||||
0.282ms │ 0.073ms │ octo.signs
|
||||
0.299ms │ 0.043ms │ octo.folds
|
||||
0.112ms │ 0.228ms │ luasnip.util.util
|
||||
0.181ms │ 0.156ms │ vim.treesitter.languagetree
|
||||
0.260ms │ 0.073ms │ vim.keymap
|
||||
0.101ms │ 0.231ms │ cmp.entry
|
||||
0.182ms │ 0.145ms │ vim.treesitter.highlighter
|
||||
0.191ms │ 0.121ms │ trouble.util
|
||||
0.190ms │ 0.119ms │ vim.lsp.codelens
|
||||
0.190ms │ 0.117ms │ vim.lsp.sync
|
||||
0.197ms │ 0.105ms │ vim.highlight
|
||||
0.170ms │ 0.132ms │ spellsitter
|
||||
0.086ms │ 0.213ms │ github_dark
|
||||
0.200ms │ 0.099ms │ persistence
|
||||
0.100ms │ 0.196ms │ cmp.view.custom_entries_view
|
||||
0.118ms │ 0.176ms │ nvim-treesitter.configs
|
||||
0.090ms │ 0.201ms │ gitsigns.git
|
||||
0.114ms │ 0.170ms │ nvim-lsp-installer.ui.display
|
||||
0.217ms │ 0.064ms │ plenary.async.async
|
||||
0.195ms │ 0.078ms │ vim.lsp.log
|
||||
0.191ms │ 0.081ms │ trouble.renderer
|
||||
0.122ms │ 0.150ms │ nvim-treesitter.ts_utils
|
||||
0.235ms │ 0.035ms │ plenary
|
||||
0.100ms │ 0.168ms │ cmp.source
|
||||
0.191ms │ 0.076ms │ vim.treesitter
|
||||
0.106ms │ 0.160ms │ lspconfig.util
|
||||
0.118ms │ 0.147ms │ nvim-treesitter.query
|
||||
0.088ms │ 0.176ms │ gitsigns.config
|
||||
0.108ms │ 0.150ms │ cmp
|
||||
0.193ms │ 0.063ms │ trouble.providers
|
||||
0.206ms │ 0.050ms │ tmux.version.parse
|
||||
0.103ms │ 0.151ms │ cmp.view.wildmenu_entries_view
|
||||
0.070ms │ 0.178ms │ diffview.path
|
||||
0.189ms │ 0.058ms │ trouble.providers.lsp
|
||||
0.096ms │ 0.147ms │ luasnip.util.parser
|
||||
0.093ms │ 0.150ms │ gitsigns.manager
|
||||
0.097ms │ 0.145ms │ null-ls.utils
|
||||
0.155ms │ 0.087ms │ plenary.async.control
|
||||
0.105ms │ 0.135ms │ nvim-lsp-installer.installers.std
|
||||
0.107ms │ 0.130ms │ lspconfig.configs
|
||||
0.097ms │ 0.140ms │ null-ls.helpers.generator_factory
|
||||
0.188ms │ 0.047ms │ trouble.providers.telescope
|
||||
0.191ms │ 0.040ms │ trouble.config
|
||||
0.099ms │ 0.131ms │ cmp.utils.window
|
||||
0.096ms │ 0.133ms │ luasnip.nodes.choiceNode
|
||||
0.192ms │ 0.036ms │ trouble.providers.qf
|
||||
0.104ms │ 0.124ms │ cmp.utils.keymap
|
||||
0.089ms │ 0.139ms │ gitsigns.hunks
|
||||
0.104ms │ 0.122ms │ nvim-lsp-installer.process
|
||||
0.096ms │ 0.129ms │ null-ls.sources
|
||||
0.116ms │ 0.108ms │ nvim-lsp-installer
|
||||
0.096ms │ 0.128ms │ luasnip.nodes.dynamicNode
|
||||
0.162ms │ 0.062ms │ tmux.copy
|
||||
0.197ms │ 0.025ms │ trouble.folds
|
||||
0.156ms │ 0.066ms │ plenary.async.util
|
||||
0.150ms │ 0.071ms │ cmp.utils.highlight
|
||||
0.105ms │ 0.116ms │ nvim-lsp-installer.server
|
||||
0.118ms │ 0.100ms │ nvim-treesitter.utils
|
||||
0.182ms │ 0.035ms │ trouble.providers.diagnostic
|
||||
0.103ms │ 0.114ms │ luasnip.nodes.node
|
||||
0.185ms │ 0.031ms │ trouble.colors
|
||||
0.180ms │ 0.035ms │ vim.ui
|
||||
0.162ms │ 0.053ms │ spaceless
|
||||
0.118ms │ 0.097ms │ nvim-treesitter.shell_command_selectors
|
||||
0.160ms │ 0.053ms │ tmux.wrapper.tmux
|
||||
0.182ms │ 0.031ms │ vim.treesitter.language
|
||||
0.178ms │ 0.035ms │ trouble.text
|
||||
0.157ms │ 0.054ms │ plenary.vararg.rotate
|
||||
0.106ms │ 0.104ms │ nvim-lsp-installer.installers.context
|
||||
0.181ms │ 0.028ms │ tmux
|
||||
0.158ms │ 0.050ms │ nvim-treesitter-playground
|
||||
0.067ms │ 0.140ms │ diffview.oop
|
||||
0.158ms │ 0.047ms │ tmux.resize
|
||||
0.166ms │ 0.039ms │ tmux.log.convert
|
||||
0.161ms │ 0.044ms │ tmux.layout
|
||||
0.155ms │ 0.048ms │ plenary.async.structs
|
||||
0.101ms │ 0.102ms │ cmp.view
|
||||
0.096ms │ 0.105ms │ luasnip.util.environ
|
||||
0.145ms │ 0.055ms │ plenary.async
|
||||
0.163ms │ 0.037ms │ tmux.navigation.navigate
|
||||
0.179ms │ 0.020ms │ tmux.keymaps
|
||||
0.155ms │ 0.044ms │ plenary.functional
|
||||
0.102ms │ 0.097ms │ cmp.matcher
|
||||
0.103ms │ 0.095ms │ cmp.view.ghost_text_view
|
||||
0.106ms │ 0.091ms │ colorizer.nvim
|
||||
0.168ms │ 0.029ms │ tmux.log
|
||||
0.106ms │ 0.090ms │ nvim-lsp-installer._generated.filetype_map
|
||||
0.122ms │ 0.073ms │ nvim-treesitter.info
|
||||
0.098ms │ 0.097ms │ null-ls.client
|
||||
0.105ms │ 0.089ms │ nvim-lsp-installer.log
|
||||
0.170ms │ 0.024ms │ tmux.navigation
|
||||
0.109ms │ 0.084ms │ nvim-lsp-installer.servers
|
||||
0.098ms │ 0.095ms │ null-ls.helpers.diagnostics
|
||||
0.160ms │ 0.033ms │ tmux.configuration.options
|
||||
0.100ms │ 0.091ms │ cmp.utils.misc
|
||||
0.044ms │ 0.148ms │ lewis6991
|
||||
0.104ms │ 0.088ms │ colorizer.trie
|
||||
0.163ms │ 0.028ms │ ts_context_commentstring
|
||||
0.054ms │ 0.136ms │ cmp-rg
|
||||
0.130ms │ 0.060ms │ nvim-treesitter.query_predicates
|
||||
0.151ms │ 0.039ms │ plenary.reload
|
||||
0.096ms │ 0.094ms │ luasnip.nodes.insertNode
|
||||
0.160ms │ 0.028ms │ tmux.layout.parse
|
||||
0.096ms │ 0.093ms │ luasnip.nodes.restoreNode
|
||||
0.166ms │ 0.022ms │ tmux.configuration.validate
|
||||
0.100ms │ 0.088ms │ cmp.view.native_entries_view
|
||||
0.155ms │ 0.033ms │ plenary.tbl
|
||||
0.126ms │ 0.062ms │ lspconfig.server_configurations.sumneko_lua
|
||||
0.029ms │ 0.160ms │ cmp_buffer.buffer
|
||||
0.105ms │ 0.083ms │ cmp.utils.str
|
||||
0.162ms │ 0.025ms │ tmux.log.severity
|
||||
0.164ms │ 0.024ms │ tmux.wrapper.nvim
|
||||
0.107ms │ 0.081ms │ nvim-lsp-installer.ui.status-win.components.settings-schema
|
||||
0.021ms │ 0.167ms │ lewis6991.null-ls
|
||||
0.163ms │ 0.024ms │ tmux.configuration
|
||||
0.116ms │ 0.071ms │ nvim-treesitter.tsrange
|
||||
0.161ms │ 0.026ms │ tmux.log.channels
|
||||
0.094ms │ 0.091ms │ gitsigns.debug
|
||||
0.163ms │ 0.021ms │ plenary.vararg
|
||||
0.166ms │ 0.018ms │ tmux.version
|
||||
0.160ms │ 0.022ms │ tmux.configuration.logging
|
||||
0.155ms │ 0.026ms │ plenary.errors
|
||||
0.127ms │ 0.053ms │ nvim-treesitter
|
||||
0.094ms │ 0.085ms │ null-ls.info
|
||||
0.100ms │ 0.079ms │ cmp.config
|
||||
0.095ms │ 0.084ms │ null-ls.diagnostics
|
||||
0.055ms │ 0.123ms │ cmp_path
|
||||
0.139ms │ 0.038ms │ plenary.async.tests
|
||||
0.098ms │ 0.078ms │ null-ls.config
|
||||
0.100ms │ 0.076ms │ cmp.view.docs_view
|
||||
0.102ms │ 0.074ms │ cmp.utils.feedkeys
|
||||
0.089ms │ 0.085ms │ gitsigns.current_line_blame
|
||||
0.127ms │ 0.047ms │ null-ls
|
||||
0.107ms │ 0.066ms │ nvim-lsp-installer.installers
|
||||
0.095ms │ 0.078ms │ luasnip.util.mark
|
||||
0.106ms │ 0.066ms │ nvim-lsp-installer.fs
|
||||
0.142ms │ 0.030ms │ persistence.config
|
||||
0.100ms │ 0.070ms │ cmp.config.default
|
||||
0.078ms │ 0.091ms │ foldsigns
|
||||
0.120ms │ 0.048ms │ lua-dev
|
||||
0.113ms │ 0.053ms │ nvim-lsp-installer.ui
|
||||
0.029ms │ 0.138ms │ lewis6991.status
|
||||
0.118ms │ 0.047ms │ lspconfig
|
||||
0.113ms │ 0.051ms │ nvim-lsp-installer.jobs.outdated-servers
|
||||
0.105ms │ 0.058ms │ nvim-lsp-installer.installers.npm
|
||||
0.106ms │ 0.057ms │ nvim-lsp-installer.core.receipt
|
||||
0.101ms │ 0.061ms │ cmp.utils.char
|
||||
0.091ms │ 0.071ms │ gitsigns.signs
|
||||
0.097ms │ 0.065ms │ luasnip.nodes.util
|
||||
0.126ms │ 0.034ms │ treesitter-context.utils
|
||||
0.096ms │ 0.065ms │ lua-dev.config
|
||||
0.109ms │ 0.052ms │ nvim-lsp-installer.core.fetch
|
||||
0.103ms │ 0.055ms │ cmp.types.lsp
|
||||
0.099ms │ 0.059ms │ luasnip.nodes.functionNode
|
||||
0.090ms │ 0.067ms │ gitsigns.util
|
||||
0.110ms │ 0.047ms │ nvim-lsp-installer.jobs.outdated-servers.cargo
|
||||
0.096ms │ 0.061ms │ luasnip.config
|
||||
0.100ms │ 0.057ms │ cmp.utils.async
|
||||
0.101ms │ 0.055ms │ cmp.context
|
||||
0.091ms │ 0.064ms │ gitsigns.highlight
|
||||
0.094ms │ 0.061ms │ lua-dev.sumneko
|
||||
0.094ms │ 0.061ms │ gitsigns.subprocess
|
||||
0.067ms │ 0.088ms │ cmp_luasnip
|
||||
0.105ms │ 0.050ms │ nvim-lsp-installer.data
|
||||
0.105ms │ 0.049ms │ nvim-lsp-installer.installers.pip3
|
||||
0.120ms │ 0.034ms │ lspconfig.server_configurations.bashls
|
||||
0.107ms │ 0.046ms │ nvim-lsp-installer.core.clients.github
|
||||
0.107ms │ 0.045ms │ nvim-lsp-installer.installers.shell
|
||||
0.099ms │ 0.053ms │ cmp.config.compare
|
||||
0.109ms │ 0.043ms │ lspconfig.server_configurations.clangd
|
||||
0.115ms │ 0.036ms │ lspconfig.server_configurations.vimls
|
||||
0.097ms │ 0.054ms │ luasnip.util.pattern_tokenizer
|
||||
0.097ms │ 0.053ms │ null-ls.helpers.make_builtin
|
||||
0.101ms │ 0.049ms │ cmp.utils.api
|
||||
0.118ms │ 0.032ms │ lspconfig.server_configurations.jedi_language_server
|
||||
0.106ms │ 0.043ms │ nvim-lsp-installer.jobs.outdated-servers.pip3
|
||||
0.106ms │ 0.043ms │ nvim-lsp-installer.jobs.outdated-servers.gem
|
||||
0.108ms │ 0.040ms │ nvim-lsp-installer._generated.language_autocomplete_map
|
||||
0.104ms │ 0.043ms │ nvim-lsp-installer.installers.composer
|
||||
0.101ms │ 0.046ms │ cmp.config.mapping
|
||||
0.047ms │ 0.100ms │ cmp_nvim_lsp_signature_help
|
||||
0.109ms │ 0.037ms │ nvim-lsp-installer.servers.sumneko_lua
|
||||
0.115ms │ 0.028ms │ nvim-treesitter.caching
|
||||
0.096ms │ 0.047ms │ null-ls.state
|
||||
0.090ms │ 0.053ms │ gitsigns.debounce
|
||||
0.059ms │ 0.084ms │ cmp_tmux.tmux
|
||||
0.096ms │ 0.045ms │ null-ls.builtins.diagnostics.flake8
|
||||
0.106ms │ 0.034ms │ nvim-lsp-installer.jobs.pool
|
||||
0.106ms │ 0.033ms │ nvim-lsp-installer.ui.status-win.server_hints
|
||||
0.105ms │ 0.034ms │ nvim-lsp-installer.installers.gem
|
||||
0.107ms │ 0.032ms │ nvim-lsp-installer.jobs.outdated-servers.npm
|
||||
0.106ms │ 0.031ms │ nvim-lsp-installer.jobs.outdated-servers.git
|
||||
0.114ms │ 0.022ms │ nvim-lsp-installer.servers.jedi_language_server
|
||||
0.105ms │ 0.031ms │ nvim-lsp-installer.jobs.outdated-servers.composer
|
||||
0.098ms │ 0.038ms │ null-ls.methods
|
||||
0.109ms │ 0.026ms │ nvim-lsp-installer.jobs.outdated-servers.version-check-result
|
||||
0.106ms │ 0.029ms │ nvim-lsp-installer.settings
|
||||
0.107ms │ 0.027ms │ cmp.utils.debug
|
||||
0.103ms │ 0.031ms │ cmp.types.cmp
|
||||
0.070ms │ 0.064ms │ diffview.events
|
||||
0.108ms │ 0.026ms │ nvim-lsp-installer.platform
|
||||
0.097ms │ 0.037ms │ null-ls.helpers.command_resolver
|
||||
0.104ms │ 0.029ms │ cmp.config.sources
|
||||
0.107ms │ 0.026ms │ nvim-lsp-installer.jobs.outdated-servers.github_release_file
|
||||
0.099ms │ 0.033ms │ cmp.utils.cache
|
||||
0.107ms │ 0.025ms │ nvim-lsp-installer.path
|
||||
0.101ms │ 0.030ms │ cmp.utils.autocmd
|
||||
0.097ms │ 0.034ms │ null-ls.logger
|
||||
0.100ms │ 0.031ms │ cmp.utils.event
|
||||
0.088ms │ 0.042ms │ gitsigns.cache
|
||||
0.103ms │ 0.027ms │ cmp.utils.pattern
|
||||
0.108ms │ 0.022ms │ nvim-lsp-installer.jobs.outdated-servers.jdtls
|
||||
0.103ms │ 0.027ms │ cmp.utils.buffer
|
||||
0.095ms │ 0.034ms │ luasnip.nodes.textNode
|
||||
0.096ms │ 0.033ms │ luasnip.util.dict
|
||||
0.108ms │ 0.021ms │ nvim-lsp-installer.servers.bashls
|
||||
0.108ms │ 0.021ms │ nvim-lsp-installer.ui.state
|
||||
0.110ms │ 0.018ms │ nvim-lsp-installer.servers.vimls
|
||||
0.101ms │ 0.027ms │ null-ls.helpers.range_formatting_args_factory
|
||||
0.057ms │ 0.071ms │ cmp_treesitter.lru
|
||||
0.105ms │ 0.022ms │ nvim-lsp-installer.dispatcher
|
||||
0.097ms │ 0.030ms │ luasnip.extras.filetype_functions
|
||||
0.103ms │ 0.024ms │ luasnip.session
|
||||
0.105ms │ 0.021ms │ nvim-lsp-installer.core.clients.crates
|
||||
0.105ms │ 0.021ms │ nvim-lsp-installer.jobs.outdated-servers.github_tag
|
||||
0.110ms │ 0.016ms │ cmp.types
|
||||
0.105ms │ 0.021ms │ nvim-lsp-installer.core.clients.eclipse
|
||||
0.105ms │ 0.021ms │ nvim-lsp-installer.notify
|
||||
0.089ms │ 0.036ms │ gitsigns.status
|
||||
0.096ms │ 0.029ms │ null-ls.builtins.diagnostics.teal
|
||||
0.097ms │ 0.027ms │ null-ls.builtins
|
||||
0.103ms │ 0.021ms │ cmp.types.vim
|
||||
0.060ms │ 0.062ms │ cmp_tmux.source
|
||||
0.100ms │ 0.022ms │ null-ls.helpers
|
||||
0.098ms │ 0.024ms │ null-ls.builtins.diagnostics.gitlint
|
||||
0.065ms │ 0.056ms │ cmp_treesitter
|
||||
0.024ms │ 0.097ms │ buftabline.buftab
|
||||
0.095ms │ 0.026ms │ null-ls.builtins.diagnostics.shellcheck
|
||||
0.095ms │ 0.026ms │ null-ls.builtins.diagnostics.luacheck
|
||||
0.097ms │ 0.021ms │ null-ls.helpers.formatter_factory
|
||||
0.097ms │ 0.022ms │ luasnip.util.events
|
||||
0.097ms │ 0.021ms │ luasnip.util.types
|
||||
0.096ms │ 0.022ms │ luasnip.util.functions
|
||||
0.037ms │ 0.078ms │ cmp_cmdline
|
||||
0.032ms │ 0.083ms │ cmp_buffer.source
|
||||
0.040ms │ 0.074ms │ lewis6991.cmp
|
||||
0.060ms │ 0.054ms │ cmp_treesitter.treesitter
|
||||
0.089ms │ 0.025ms │ gitsigns.message
|
||||
0.039ms │ 0.073ms │ cmp_nvim_lsp.source
|
||||
0.055ms │ 0.054ms │ buftabline.build
|
||||
0.026ms │ 0.083ms │ lewis6991.lsp
|
||||
0.051ms │ 0.055ms │ cmp_nvim_lua
|
||||
0.033ms │ 0.065ms │ cleanfold
|
||||
0.071ms │ 0.025ms │ cmp_tmux
|
||||
0.043ms │ 0.053ms │ cmp_nvim_lsp
|
||||
0.058ms │ 0.033ms │ cmp-spell
|
||||
0.043ms │ 0.037ms │ cmp_emoji
|
||||
0.029ms │ 0.049ms │ lewis6991.floating_man
|
||||
0.032ms │ 0.042ms │ cmp_buffer.timer
|
||||
0.024ms │ 0.050ms │ lewis6991.treesitter
|
||||
0.019ms │ 0.054ms │ lewis6991.cmp_gh
|
||||
0.025ms │ 0.046ms │ buftabline.buffers
|
||||
0.021ms │ 0.048ms │ lewis6991.telescope
|
||||
0.024ms │ 0.031ms │ buftabline
|
||||
0.035ms │ 0.019ms │ cmp_buffer
|
||||
0.019ms │ 0.035ms │ buftabline.utils
|
||||
0.021ms │ 0.030ms │ buftabline.highlights
|
||||
0.020ms │ 0.032ms │ buftabline.tabpage-tab
|
||||
0.019ms │ 0.030ms │ buftabline.options
|
||||
0.020ms │ 0.026ms │ buftabline.tabpages
|
||||
────────────┴────────────┴─────────────────────────────────────────────────────────────────
|
||||
```
|
||||
</details>
|
||||
|
||||
Total resolve: 54.337ms, total load: 34.257ms
|
||||
|
||||
<details>
|
||||
<summary>With cache</summary>
|
||||
|
||||
```
|
||||
────────────┬────────────┐
|
||||
Resolve │ Load │
|
||||
────────────┼────────────┼─────────────────────────────────────────────────────────────────
|
||||
Time │ Time │ Module
|
||||
────────────┼────────────┼─────────────────────────────────────────────────────────────────
|
||||
6.357ms │ 6.796ms │ Total
|
||||
────────────┼────────────┼─────────────────────────────────────────────────────────────────
|
||||
0.041ms │ 2.021ms │ octo.writers
|
||||
0.118ms │ 0.160ms │ lewis6991.plugins
|
||||
0.050ms │ 0.144ms │ octo.date
|
||||
0.035ms │ 0.153ms │ octo.utils
|
||||
0.057ms │ 0.099ms │ octo.model.octo-buffer
|
||||
0.047ms │ 0.105ms │ packer
|
||||
0.058ms │ 0.080ms │ octo.colors
|
||||
0.121ms │ 0.015ms │ gitsigns.cache
|
||||
0.082ms │ 0.037ms │ packer.load
|
||||
0.107ms │ 0.008ms │ gitsigns.debounce
|
||||
0.048ms │ 0.064ms │ octo.config
|
||||
0.048ms │ 0.061ms │ octo.graphql
|
||||
0.049ms │ 0.051ms │ octo
|
||||
0.043ms │ 0.057ms │ vim.diagnostic
|
||||
0.085ms │ 0.013ms │ gitsigns.highlight
|
||||
0.065ms │ 0.032ms │ octo.base64
|
||||
0.035ms │ 0.060ms │ vim.lsp
|
||||
0.056ms │ 0.035ms │ octo.gh
|
||||
0.045ms │ 0.045ms │ octo.mappings
|
||||
0.026ms │ 0.060ms │ octo.reviews
|
||||
0.037ms │ 0.045ms │ packer.plugin_utils
|
||||
0.030ms │ 0.049ms │ octo.reviews.file-panel
|
||||
0.018ms │ 0.056ms │ vim.lsp.util
|
||||
0.043ms │ 0.030ms │ packer.log
|
||||
0.036ms │ 0.032ms │ packer.util
|
||||
0.032ms │ 0.035ms │ octo.reviews.file-entry
|
||||
0.021ms │ 0.045ms │ packer_compiled
|
||||
0.052ms │ 0.014ms │ octo.model.body-metadata
|
||||
0.033ms │ 0.027ms │ octo.reviews.layout
|
||||
0.014ms │ 0.047ms │ nvim-treesitter.parsers
|
||||
0.035ms │ 0.024ms │ vim.lsp.handlers
|
||||
0.014ms │ 0.044ms │ nvim-lsp-installer.ui.status-win
|
||||
0.046ms │ 0.012ms │ octo.completion
|
||||
0.037ms │ 0.021ms │ octo.constants
|
||||
0.032ms │ 0.025ms │ lewis6991
|
||||
0.040ms │ 0.017ms │ persistence
|
||||
0.030ms │ 0.026ms │ diffview.utils
|
||||
0.035ms │ 0.020ms │ packer.result
|
||||
0.015ms │ 0.040ms │ gitsigns.config
|
||||
0.031ms │ 0.024ms │ packer.async
|
||||
0.041ms │ 0.013ms │ vim.uri
|
||||
0.044ms │ 0.010ms │ octo.model.thread-metadata
|
||||
0.018ms │ 0.035ms │ gitsigns.debug
|
||||
0.023ms │ 0.030ms │ github_dark
|
||||
0.030ms │ 0.023ms │ packer.jobs
|
||||
0.039ms │ 0.013ms │ buftabline.build
|
||||
0.037ms │ 0.014ms │ octo.model.title-metadata
|
||||
0.025ms │ 0.025ms │ vim.lsp.buf
|
||||
0.022ms │ 0.027ms │ gitsigns
|
||||
0.027ms │ 0.022ms │ lewis6991.status
|
||||
0.016ms │ 0.032ms │ gitsigns.git
|
||||
0.026ms │ 0.020ms │ octo.window
|
||||
0.033ms │ 0.012ms │ octo.folds
|
||||
0.037ms │ 0.008ms │ trouble.providers.lsp
|
||||
0.016ms │ 0.028ms │ vim.lsp.protocol
|
||||
0.028ms │ 0.016ms │ octo.signs
|
||||
0.028ms │ 0.014ms │ null-ls
|
||||
0.027ms │ 0.014ms │ octo.reviews.renderer
|
||||
0.018ms │ 0.024ms │ trouble.view
|
||||
0.017ms │ 0.025ms │ luasnip.nodes.snippet
|
||||
0.023ms │ 0.018ms │ colorizer.nvim
|
||||
0.017ms │ 0.024ms │ vim.lsp._snippet
|
||||
0.015ms │ 0.025ms │ nvim-treesitter.install
|
||||
0.018ms │ 0.022ms │ plenary.async.structs
|
||||
0.018ms │ 0.021ms │ dirvish
|
||||
0.027ms │ 0.012ms │ octo.ui.bubbles
|
||||
0.019ms │ 0.020ms │ treesitter-context
|
||||
0.015ms │ 0.024ms │ vim.lsp.diagnostic
|
||||
0.016ms │ 0.023ms │ vim.lsp.rpc
|
||||
0.022ms │ 0.016ms │ trouble
|
||||
0.022ms │ 0.016ms │ null-ls.helpers.generator_factory
|
||||
0.020ms │ 0.017ms │ luasnip
|
||||
0.014ms │ 0.023ms │ plenary.job
|
||||
0.026ms │ 0.011ms │ lewis6991.cmp
|
||||
0.027ms │ 0.010ms │ trouble.providers
|
||||
0.022ms │ 0.014ms │ nvim-treesitter.query
|
||||
0.018ms │ 0.018ms │ vim.treesitter.highlighter
|
||||
0.017ms │ 0.018ms │ nvim-treesitter.shell_command_selectors
|
||||
0.014ms │ 0.021ms │ nvim-treesitter.configs
|
||||
0.025ms │ 0.010ms │ lewis6991.floating_man
|
||||
0.022ms │ 0.012ms │ vim.keymap
|
||||
0.013ms │ 0.021ms │ cmp.entry
|
||||
0.024ms │ 0.010ms │ lspconfig.server_configurations.bashls
|
||||
0.018ms │ 0.016ms │ gitsigns.hunks
|
||||
0.017ms │ 0.017ms │ gitsigns.status
|
||||
0.014ms │ 0.019ms │ cmp.core
|
||||
0.018ms │ 0.015ms │ spellsitter
|
||||
0.014ms │ 0.019ms │ colorizer
|
||||
0.024ms │ 0.009ms │ diffview.bootstrap
|
||||
0.016ms │ 0.016ms │ null-ls.utils
|
||||
0.021ms │ 0.011ms │ nvim-treesitter.info
|
||||
0.022ms │ 0.010ms │ vim.highlight
|
||||
0.016ms │ 0.016ms │ null-ls.info
|
||||
0.019ms │ 0.013ms │ cmp_path
|
||||
0.026ms │ 0.006ms │ cmp.utils.autocmd
|
||||
0.021ms │ 0.011ms │ foldsigns
|
||||
0.014ms │ 0.018ms │ lewis6991.null-ls
|
||||
0.018ms │ 0.013ms │ cmp.view
|
||||
0.017ms │ 0.014ms │ null-ls.client
|
||||
0.016ms │ 0.015ms │ gitsigns.manager
|
||||
0.013ms │ 0.018ms │ cmp.view.custom_entries_view
|
||||
0.015ms │ 0.015ms │ nvim-lsp-installer.ui.display
|
||||
0.020ms │ 0.010ms │ null-ls.methods
|
||||
0.016ms │ 0.014ms │ plenary.async.control
|
||||
0.019ms │ 0.011ms │ null-ls.diagnostics
|
||||
0.014ms │ 0.015ms │ luasnip.util.util
|
||||
0.017ms │ 0.013ms │ gitsigns.current_line_blame
|
||||
0.013ms │ 0.016ms │ buftabline.buftab
|
||||
0.015ms │ 0.015ms │ trouble.util
|
||||
0.015ms │ 0.015ms │ luasnip.config
|
||||
0.019ms │ 0.010ms │ plenary.async.async
|
||||
0.018ms │ 0.012ms │ nvim-treesitter.tsrange
|
||||
0.021ms │ 0.007ms │ cmp_nvim_lua
|
||||
0.014ms │ 0.015ms │ vim.treesitter.query
|
||||
0.015ms │ 0.014ms │ cmp.source
|
||||
0.014ms │ 0.015ms │ vim.treesitter.languagetree
|
||||
0.012ms │ 0.016ms │ nvim-lsp-installer._generated.filetype_map
|
||||
0.015ms │ 0.014ms │ nvim-lsp-installer.servers
|
||||
0.014ms │ 0.014ms │ lspconfig.util
|
||||
0.011ms │ 0.017ms │ cmp
|
||||
0.015ms │ 0.013ms │ cmp.view.wildmenu_entries_view
|
||||
0.021ms │ 0.007ms │ lspconfig.server_configurations.jedi_language_server
|
||||
0.015ms │ 0.013ms │ lua-dev
|
||||
0.018ms │ 0.010ms │ gitsigns.util
|
||||
0.014ms │ 0.014ms │ vim.lsp.codelens
|
||||
0.017ms │ 0.011ms │ plenary.async.util
|
||||
0.013ms │ 0.014ms │ null-ls.sources
|
||||
0.015ms │ 0.012ms │ nvim-treesitter.query_predicates
|
||||
0.013ms │ 0.015ms │ luasnip.nodes.choiceNode
|
||||
0.015ms │ 0.013ms │ null-ls.helpers.diagnostics
|
||||
0.017ms │ 0.011ms │ trouble.renderer
|
||||
0.015ms │ 0.013ms │ luasnip.nodes.node
|
||||
0.014ms │ 0.013ms │ lua-dev.sumneko
|
||||
0.013ms │ 0.014ms │ cmp.utils.window
|
||||
0.021ms │ 0.006ms │ treesitter-context.utils
|
||||
0.018ms │ 0.009ms │ cleanfold
|
||||
0.015ms │ 0.012ms │ nvim-treesitter.ts_utils
|
||||
0.012ms │ 0.015ms │ nvim-lsp-installer.installers.std
|
||||
0.015ms │ 0.012ms │ nvim-lsp-installer.server
|
||||
0.014ms │ 0.012ms │ lewis6991.lsp
|
||||
0.016ms │ 0.011ms │ gitsigns.signs
|
||||
0.020ms │ 0.006ms │ buftabline
|
||||
0.019ms │ 0.007ms │ plenary.tbl
|
||||
0.013ms │ 0.013ms │ nvim-lsp-installer
|
||||
0.018ms │ 0.008ms │ plenary
|
||||
0.015ms │ 0.010ms │ cmp_luasnip
|
||||
0.019ms │ 0.007ms │ null-ls.logger
|
||||
0.016ms │ 0.010ms │ vim.lsp.sync
|
||||
0.016ms │ 0.010ms │ spaceless
|
||||
0.017ms │ 0.009ms │ gitsigns.subprocess
|
||||
0.016ms │ 0.009ms │ plenary.functional
|
||||
0.016ms │ 0.010ms │ buftabline.buffers
|
||||
0.016ms │ 0.009ms │ vim.lsp.log
|
||||
0.019ms │ 0.006ms │ cmp_tmux
|
||||
0.013ms │ 0.012ms │ luasnip.nodes.dynamicNode
|
||||
0.017ms │ 0.008ms │ vim.treesitter
|
||||
0.013ms │ 0.013ms │ nvim-lsp-installer.process
|
||||
0.013ms │ 0.012ms │ luasnip.util.environ
|
||||
0.015ms │ 0.009ms │ lewis6991.treesitter
|
||||
0.015ms │ 0.010ms │ null-ls.config
|
||||
0.019ms │ 0.006ms │ ts_context_commentstring
|
||||
0.013ms │ 0.012ms │ cmp_buffer.buffer
|
||||
0.018ms │ 0.007ms │ null-ls.builtins.diagnostics.shellcheck
|
||||
0.015ms │ 0.010ms │ null-ls.helpers.make_builtin
|
||||
0.012ms │ 0.012ms │ diffview.path
|
||||
0.016ms │ 0.008ms │ null-ls.builtins.diagnostics.gitlint
|
||||
0.017ms │ 0.007ms │ trouble.providers.telescope
|
||||
0.013ms │ 0.011ms │ diffview.oop
|
||||
0.015ms │ 0.010ms │ cmp-rg
|
||||
0.013ms │ 0.011ms │ cmp.utils.keymap
|
||||
0.014ms │ 0.011ms │ nvim-treesitter
|
||||
0.018ms │ 0.007ms │ cmp.utils.highlight
|
||||
0.016ms │ 0.008ms │ lspconfig.server_configurations.sumneko_lua
|
||||
0.015ms │ 0.009ms │ colorizer.trie
|
||||
0.016ms │ 0.007ms │ plenary.vararg.rotate
|
||||
0.015ms │ 0.009ms │ trouble.config
|
||||
0.011ms │ 0.012ms │ lspconfig.configs
|
||||
0.014ms │ 0.009ms │ null-ls.helpers.command_resolver
|
||||
0.016ms │ 0.007ms │ cmp_tmux.source
|
||||
0.016ms │ 0.007ms │ lspconfig
|
||||
0.017ms │ 0.006ms │ plenary.vararg
|
||||
0.012ms │ 0.011ms │ nvim-lsp-installer.installers.context
|
||||
0.014ms │ 0.009ms │ cmp.view.native_entries_view
|
||||
0.014ms │ 0.009ms │ cmp.config.default
|
||||
0.017ms │ 0.006ms │ tmux.version.parse
|
||||
0.016ms │ 0.007ms │ gitsigns.message
|
||||
0.017ms │ 0.006ms │ persistence.config
|
||||
0.013ms │ 0.010ms │ cmp_nvim_lsp_signature_help
|
||||
0.012ms │ 0.010ms │ cmp.view.docs_view
|
||||
0.017ms │ 0.006ms │ cmp.config.sources
|
||||
0.013ms │ 0.009ms │ luasnip.nodes.restoreNode
|
||||
0.014ms │ 0.009ms │ vim.ui
|
||||
0.013ms │ 0.010ms │ luasnip.nodes.insertNode
|
||||
0.013ms │ 0.010ms │ null-ls.state
|
||||
0.014ms │ 0.008ms │ lspconfig.server_configurations.vimls
|
||||
0.016ms │ 0.006ms │ plenary.errors
|
||||
0.014ms │ 0.008ms │ null-ls.builtins.diagnostics.flake8
|
||||
0.016ms │ 0.006ms │ null-ls.helpers
|
||||
0.015ms │ 0.008ms │ null-ls.builtins.diagnostics.luacheck
|
||||
0.014ms │ 0.008ms │ luasnip.util.mark
|
||||
0.015ms │ 0.008ms │ cmp.utils.buffer
|
||||
0.012ms │ 0.010ms │ nvim-lsp-installer.log
|
||||
0.015ms │ 0.007ms │ luasnip.nodes.util
|
||||
0.015ms │ 0.007ms │ null-ls.builtins.diagnostics.teal
|
||||
0.016ms │ 0.006ms │ null-ls.helpers.range_formatting_args_factory
|
||||
0.012ms │ 0.010ms │ nvim-treesitter.utils
|
||||
0.015ms │ 0.007ms │ cmp.utils.event
|
||||
0.013ms │ 0.009ms │ tmux.wrapper.tmux
|
||||
0.015ms │ 0.007ms │ nvim-treesitter-playground
|
||||
0.012ms │ 0.010ms │ cmp_buffer.source
|
||||
0.015ms │ 0.007ms │ cmp_treesitter
|
||||
0.013ms │ 0.009ms │ luasnip.util.parser
|
||||
0.015ms │ 0.006ms │ trouble.providers.qf
|
||||
0.014ms │ 0.008ms │ lewis6991.telescope
|
||||
0.014ms │ 0.007ms │ cmp_tmux.tmux
|
||||
0.014ms │ 0.007ms │ cmp_nvim_lsp.source
|
||||
0.015ms │ 0.006ms │ plenary.reload
|
||||
0.014ms │ 0.008ms │ buftabline.highlights
|
||||
0.015ms │ 0.006ms │ trouble.providers.diagnostic
|
||||
0.015ms │ 0.007ms │ nvim-lsp-installer.core.clients.github
|
||||
0.014ms │ 0.007ms │ nvim-lsp-installer.installers.shell
|
||||
0.016ms │ 0.005ms │ cmp-spell
|
||||
0.014ms │ 0.007ms │ null-ls.builtins
|
||||
0.013ms │ 0.008ms │ cmp_treesitter.lru
|
||||
0.016ms │ 0.005ms │ buftabline.tabpages
|
||||
0.015ms │ 0.006ms │ buftabline.options
|
||||
0.016ms │ 0.005ms │ lua-dev.config
|
||||
0.015ms │ 0.006ms │ nvim-lsp-installer.jobs.outdated-servers.cargo
|
||||
0.014ms │ 0.007ms │ diffview.events
|
||||
0.013ms │ 0.008ms │ nvim-lsp-installer.fs
|
||||
0.013ms │ 0.008ms │ cmp.utils.feedkeys
|
||||
0.013ms │ 0.007ms │ nvim-treesitter.caching
|
||||
0.013ms │ 0.008ms │ nvim-lsp-installer._generated.language_autocomplete_map
|
||||
0.013ms │ 0.007ms │ cmp.view.ghost_text_view
|
||||
0.013ms │ 0.008ms │ cmp_nvim_lsp
|
||||
0.013ms │ 0.007ms │ luasnip.nodes.functionNode
|
||||
0.013ms │ 0.007ms │ nvim-lsp-installer.jobs.outdated-servers
|
||||
0.012ms │ 0.008ms │ nvim-lsp-installer.ui.status-win.components.settings-schema
|
||||
0.012ms │ 0.009ms │ lewis6991.cmp_gh
|
||||
0.015ms │ 0.006ms │ luasnip.util.dict
|
||||
0.013ms │ 0.007ms │ plenary.async
|
||||
0.014ms │ 0.006ms │ nvim-lsp-installer.installers.composer
|
||||
0.013ms │ 0.007ms │ cmp_treesitter.treesitter
|
||||
0.014ms │ 0.006ms │ nvim-lsp-installer.jobs.outdated-servers.gem
|
||||
0.015ms │ 0.005ms │ nvim-lsp-installer.platform
|
||||
0.014ms │ 0.006ms │ buftabline.utils
|
||||
0.013ms │ 0.007ms │ trouble.text
|
||||
0.011ms │ 0.008ms │ cmp.config
|
||||
0.013ms │ 0.006ms │ trouble.colors
|
||||
0.012ms │ 0.007ms │ cmp.utils.misc
|
||||
0.012ms │ 0.008ms │ nvim-lsp-installer.installers.npm
|
||||
0.013ms │ 0.007ms │ lspconfig.server_configurations.clangd
|
||||
0.012ms │ 0.007ms │ cmp_cmdline
|
||||
0.011ms │ 0.008ms │ cmp.types.lsp
|
||||
0.014ms │ 0.006ms │ vim.treesitter.language
|
||||
0.014ms │ 0.006ms │ cmp.config.mapping
|
||||
0.015ms │ 0.004ms │ luasnip.util.events
|
||||
0.014ms │ 0.005ms │ luasnip.extras.filetype_functions
|
||||
0.012ms │ 0.007ms │ cmp.utils.async
|
||||
0.012ms │ 0.007ms │ cmp.config.compare
|
||||
0.013ms │ 0.005ms │ cmp_emoji
|
||||
0.015ms │ 0.004ms │ cmp_buffer
|
||||
0.011ms │ 0.007ms │ nvim-lsp-installer.core.receipt
|
||||
0.012ms │ 0.007ms │ nvim-lsp-installer.ui
|
||||
0.013ms │ 0.006ms │ cmp.utils.api
|
||||
0.012ms │ 0.007ms │ nvim-lsp-installer.core.fetch
|
||||
0.013ms │ 0.005ms │ nvim-lsp-installer.jobs.pool
|
||||
0.011ms │ 0.007ms │ nvim-lsp-installer.installers
|
||||
0.012ms │ 0.007ms │ nvim-lsp-installer.data
|
||||
0.013ms │ 0.006ms │ cmp.matcher
|
||||
0.014ms │ 0.005ms │ tmux
|
||||
0.011ms │ 0.008ms │ tmux.copy
|
||||
0.013ms │ 0.005ms │ luasnip.util.types
|
||||
0.014ms │ 0.004ms │ nvim-lsp-installer.servers.jedi_language_server
|
||||
0.014ms │ 0.004ms │ nvim-lsp-installer.servers.vimls
|
||||
0.014ms │ 0.004ms │ cmp.utils.cache
|
||||
0.013ms │ 0.006ms │ luasnip.util.pattern_tokenizer
|
||||
0.012ms │ 0.006ms │ luasnip.nodes.textNode
|
||||
0.013ms │ 0.005ms │ null-ls.helpers.formatter_factory
|
||||
0.013ms │ 0.006ms │ plenary.async.tests
|
||||
0.013ms │ 0.005ms │ nvim-lsp-installer.jobs.outdated-servers.version-check-result
|
||||
0.012ms │ 0.005ms │ nvim-lsp-installer.settings
|
||||
0.011ms │ 0.006ms │ cmp.context
|
||||
0.011ms │ 0.006ms │ cmp.utils.str
|
||||
0.013ms │ 0.004ms │ luasnip.session
|
||||
0.013ms │ 0.005ms │ nvim-lsp-installer.jobs.outdated-servers.composer
|
||||
0.012ms │ 0.006ms │ nvim-lsp-installer.servers.sumneko_lua
|
||||
0.012ms │ 0.005ms │ cmp_buffer.timer
|
||||
0.011ms │ 0.006ms │ cmp.utils.char
|
||||
0.013ms │ 0.004ms │ cmp.utils.pattern
|
||||
0.011ms │ 0.006ms │ nvim-lsp-installer.installers.pip3
|
||||
0.013ms │ 0.004ms │ luasnip.util.functions
|
||||
0.013ms │ 0.005ms │ tmux.log.channels
|
||||
0.012ms │ 0.005ms │ tmux.navigation
|
||||
0.013ms │ 0.005ms │ trouble.folds
|
||||
0.012ms │ 0.005ms │ nvim-lsp-installer.ui.status-win.server_hints
|
||||
0.012ms │ 0.005ms │ nvim-lsp-installer.jobs.outdated-servers.pip3
|
||||
0.012ms │ 0.005ms │ nvim-lsp-installer.jobs.outdated-servers.npm
|
||||
0.011ms │ 0.006ms │ cmp.utils.debug
|
||||
0.013ms │ 0.004ms │ nvim-lsp-installer.notify
|
||||
0.011ms │ 0.006ms │ tmux.layout
|
||||
0.013ms │ 0.004ms │ nvim-lsp-installer.servers.bashls
|
||||
0.012ms │ 0.004ms │ nvim-lsp-installer.dispatcher
|
||||
0.012ms │ 0.005ms │ buftabline.tabpage-tab
|
||||
0.012ms │ 0.005ms │ nvim-lsp-installer.path
|
||||
0.010ms │ 0.006ms │ tmux.resize
|
||||
0.013ms │ 0.004ms │ cmp.types.vim
|
||||
0.012ms │ 0.004ms │ nvim-lsp-installer.ui.state
|
||||
0.011ms │ 0.005ms │ nvim-lsp-installer.installers.gem
|
||||
0.012ms │ 0.005ms │ tmux.configuration.options
|
||||
0.012ms │ 0.005ms │ nvim-lsp-installer.jobs.outdated-servers.git
|
||||
0.012ms │ 0.004ms │ nvim-lsp-installer.jobs.outdated-servers.github_release_file
|
||||
0.012ms │ 0.005ms │ cmp.types.cmp
|
||||
0.013ms │ 0.004ms │ cmp.types
|
||||
0.011ms │ 0.005ms │ tmux.log
|
||||
0.011ms │ 0.005ms │ tmux.navigation.navigate
|
||||
0.012ms │ 0.005ms │ tmux.configuration
|
||||
0.012ms │ 0.004ms │ nvim-lsp-installer.jobs.outdated-servers.github_tag
|
||||
0.011ms │ 0.005ms │ tmux.layout.parse
|
||||
0.012ms │ 0.004ms │ nvim-lsp-installer.jobs.outdated-servers.jdtls
|
||||
0.011ms │ 0.005ms │ tmux.log.convert
|
||||
0.011ms │ 0.005ms │ tmux.log.severity
|
||||
0.011ms │ 0.004ms │ tmux.version
|
||||
0.012ms │ 0.004ms │ nvim-lsp-installer.core.clients.eclipse
|
||||
0.011ms │ 0.004ms │ nvim-lsp-installer.core.clients.crates
|
||||
0.011ms │ 0.004ms │ tmux.configuration.logging
|
||||
0.011ms │ 0.004ms │ tmux.wrapper.nvim
|
||||
0.011ms │ 0.004ms │ tmux.configuration.validate
|
||||
0.011ms │ 0.004ms │ tmux.keymaps
|
||||
────────────┴────────────┴─────────────────────────────────────────────────────────────────
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
Total resolve: 6.357ms, total load: 6.796ms
|
||||
|
||||
## Relevant Neovim PR's
|
||||
|
||||
[libs: vendor libmpack and libmpack-lua](https://github.com/neovim/neovim/pull/15566) [merged]
|
||||
|
||||
[fix(vim.mpack): rename pack/unpack => encode/decode](https://github.com/neovim/neovim/pull/16175) [merged]
|
||||
|
||||
[fix(runtime): add compressed representation to &rtp](https://github.com/neovim/neovim/pull/15867) [merged]
|
||||
|
||||
[fix(runtime): don't use regexes inside lua require'mod'](https://github.com/neovim/neovim/pull/15973) [merged]
|
||||
|
||||
[fix(lua): restore priority of the preloader](https://github.com/neovim/neovim/pull/17302) [merged]
|
||||
|
||||
[refactor(lua): call loadfile internally instead of luaL_loadfile](https://github.com/neovim/neovim/pull/17200) [merged]
|
||||
|
||||
[feat(lua): startup profiling](https://github.com/neovim/neovim/pull/15436)
|
||||
|
||||
## Credit
|
||||
|
||||
All credit goes to @bfredl who implemented the majority of this plugin in https://github.com/neovim/neovim/pull/15436.
|
|
@ -0,0 +1,470 @@
|
|||
local vim = vim
|
||||
local api = vim.api
|
||||
local uv = vim.loop
|
||||
local _loadfile = loadfile
|
||||
local get_runtime = api.nvim__get_runtime
|
||||
local fs_stat = uv.fs_stat
|
||||
local mpack = vim.mpack
|
||||
local loadlib = package.loadlib
|
||||
|
||||
local std_cache = vim.fn.stdpath('cache')
|
||||
|
||||
local sep = ''
|
||||
if (jit.os == 'Windows') then
|
||||
sep = '\\'
|
||||
else
|
||||
sep = '/'
|
||||
end
|
||||
|
||||
local std_dirs = {
|
||||
['<APPDIR>'] = os.getenv('APPDIR'),
|
||||
['<VIMRUNTIME>'] = os.getenv('VIMRUNTIME'),
|
||||
['<STD_DATA>'] = vim.fn.stdpath('data'),
|
||||
['<STD_CONFIG>'] = vim.fn.stdpath('config'),
|
||||
}
|
||||
|
||||
local function modpath_mangle(modpath)
|
||||
for name, dir in pairs(std_dirs) do
|
||||
modpath = modpath:gsub(dir, name)
|
||||
end
|
||||
return modpath
|
||||
end
|
||||
|
||||
local function modpath_unmangle(modpath)
|
||||
for name, dir in pairs(std_dirs) do
|
||||
modpath = modpath:gsub(name, dir)
|
||||
end
|
||||
return modpath
|
||||
end
|
||||
|
||||
-- Overridable by user
|
||||
local default_config = {
|
||||
chunks = {
|
||||
enable = true,
|
||||
path = std_cache .. sep .. 'luacache_chunks',
|
||||
},
|
||||
modpaths = {
|
||||
enable = true,
|
||||
path = std_cache.. sep .. 'luacache_modpaths',
|
||||
},
|
||||
}
|
||||
|
||||
-- State used internally
|
||||
local default_state = {
|
||||
chunks = {
|
||||
cache = {},
|
||||
profile = nil,
|
||||
dirty = false,
|
||||
get = function(self, path)
|
||||
return self.cache[modpath_mangle(path)]
|
||||
end,
|
||||
set = function(self, path, chunk)
|
||||
self.cache[modpath_mangle(path)] = chunk
|
||||
end
|
||||
},
|
||||
modpaths = {
|
||||
cache = {},
|
||||
profile = nil,
|
||||
dirty = false,
|
||||
get = function(self, mod)
|
||||
if self.cache[mod] then
|
||||
return modpath_unmangle(self.cache[mod])
|
||||
end
|
||||
end,
|
||||
set = function(self, mod, path)
|
||||
self.cache[mod] = modpath_mangle(path)
|
||||
end
|
||||
},
|
||||
log = {}
|
||||
}
|
||||
|
||||
---@diagnostic disable-next-line: undefined-field
|
||||
local M = vim.tbl_deep_extend('keep', _G.__luacache_config or {}, default_config, default_state)
|
||||
_G.__luacache = M
|
||||
|
||||
local function log(...)
|
||||
M.log[#M.log+1] = table.concat({string.format(...)}, ' ')
|
||||
end
|
||||
|
||||
local function print_log()
|
||||
for _, l in ipairs(M.log) do
|
||||
print(l)
|
||||
end
|
||||
end
|
||||
|
||||
local function hash(modpath)
|
||||
local stat = fs_stat(modpath)
|
||||
if stat then
|
||||
return stat.mtime.sec..stat.mtime.nsec..stat.size
|
||||
end
|
||||
error('Could not hash '..modpath)
|
||||
end
|
||||
|
||||
local function profile(m, entry, name, loader)
|
||||
if m.profile then
|
||||
local mp = m.profile
|
||||
mp[entry] = mp[entry] or {}
|
||||
if not mp[entry].loader and loader then
|
||||
mp[entry].loader = loader
|
||||
end
|
||||
if not mp[entry][name] then
|
||||
mp[entry][name] = uv.hrtime()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function mprofile(mod, name, loader)
|
||||
profile(M.modpaths, mod, name, loader)
|
||||
end
|
||||
|
||||
local function cprofile(path, name, loader)
|
||||
if M.chunks.profile then
|
||||
path = modpath_mangle(path)
|
||||
end
|
||||
profile(M.chunks, path, name, loader)
|
||||
end
|
||||
|
||||
function M.enable_profile()
|
||||
local P = require('impatient.profile')
|
||||
|
||||
M.chunks.profile = {}
|
||||
M.modpaths.profile = {}
|
||||
|
||||
loadlib = function(path, fun)
|
||||
cprofile(path, 'load_start')
|
||||
local f, err = package.loadlib(path, fun)
|
||||
cprofile(path, 'load_end', 'standard')
|
||||
return f, err
|
||||
end
|
||||
|
||||
P.setup(M.modpaths.profile)
|
||||
|
||||
api.nvim_create_user_command('LuaCacheProfile', function()
|
||||
P.print_profile(M, std_dirs)
|
||||
end, {})
|
||||
end
|
||||
|
||||
local function get_runtime_file_from_parent(basename, paths)
|
||||
-- Look in the cache to see if we have already loaded a parent module.
|
||||
-- If we have then try looking in the parents directory first.
|
||||
local parents = vim.split(basename, sep)
|
||||
for i = #parents, 1, -1 do
|
||||
local parent = table.concat(vim.list_slice(parents, 1, i), sep)
|
||||
local ppath = M.modpaths:get(parent)
|
||||
if ppath then
|
||||
if (ppath:sub(-9) == (sep .. 'init.lua')) then
|
||||
ppath = ppath:sub(1, -10) -- a/b/init.lua -> a/b
|
||||
else
|
||||
ppath = ppath:sub(1, -5) -- a/b.lua -> a/b
|
||||
end
|
||||
|
||||
for _, path in ipairs(paths) do
|
||||
-- path should be of form 'a/b/c.lua' or 'a/b/c/init.lua'
|
||||
local modpath = ppath..sep..path:sub(#('lua'..sep..parent)+2)
|
||||
if fs_stat(modpath) then
|
||||
return modpath, 'cache(p)'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local rtp = vim.split(vim.o.rtp, ',')
|
||||
|
||||
-- Make sure modpath is in rtp and that modpath is in paths.
|
||||
local function validate_modpath(modpath, paths)
|
||||
local match = false
|
||||
for _, p in ipairs(paths) do
|
||||
if vim.endswith(modpath, p) then
|
||||
match = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not match then
|
||||
return false
|
||||
end
|
||||
for _, dir in ipairs(rtp) do
|
||||
if vim.startswith(modpath, dir) then
|
||||
return fs_stat(modpath) ~= nil
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function get_runtime_file_cached(basename, paths)
|
||||
local modpath, loader
|
||||
local mp = M.modpaths
|
||||
if mp.enable then
|
||||
local modpath_cached = mp:get(basename)
|
||||
if modpath_cached then
|
||||
modpath, loader = modpath_cached, 'cache'
|
||||
else
|
||||
modpath, loader = get_runtime_file_from_parent(basename, paths)
|
||||
end
|
||||
|
||||
if modpath and not validate_modpath(modpath, paths) then
|
||||
modpath = nil
|
||||
|
||||
-- Invalidate
|
||||
mp.cache[basename] = nil
|
||||
mp.dirty = true
|
||||
end
|
||||
end
|
||||
|
||||
if not modpath then
|
||||
-- What Neovim does by default; slowest
|
||||
modpath, loader = get_runtime(paths, false, {is_lua=true})[1], 'standard'
|
||||
end
|
||||
|
||||
if modpath then
|
||||
mprofile(basename, 'resolve_end', loader)
|
||||
if mp.enable and loader ~= 'cache' then
|
||||
log('Creating cache for module %s', basename)
|
||||
mp:set(basename, modpath)
|
||||
mp.dirty = true
|
||||
end
|
||||
end
|
||||
|
||||
return modpath
|
||||
end
|
||||
|
||||
local function extract_basename(pats)
|
||||
local basename
|
||||
|
||||
-- Deconstruct basename from pats
|
||||
for _, pat in ipairs(pats) do
|
||||
for i, npat in ipairs{
|
||||
-- Ordered by most specific
|
||||
'lua'.. sep ..'(.*)'..sep..'init%.lua',
|
||||
'lua'.. sep ..'(.*)%.lua'
|
||||
} do
|
||||
local m = pat:match(npat)
|
||||
if i == 2 and m and m:sub(-4) == 'init' then
|
||||
m = m:sub(0, -6)
|
||||
end
|
||||
if not basename then
|
||||
if m then
|
||||
basename = m
|
||||
end
|
||||
elseif m and m ~= basename then
|
||||
-- matches are inconsistent
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return basename
|
||||
end
|
||||
|
||||
local function get_runtime_cached(pats, all, opts)
|
||||
local fallback = false
|
||||
if all or not opts or not opts.is_lua then
|
||||
-- Fallback
|
||||
fallback = true
|
||||
end
|
||||
|
||||
local basename
|
||||
|
||||
if not fallback then
|
||||
basename = extract_basename(pats)
|
||||
end
|
||||
|
||||
if fallback or not basename then
|
||||
return get_runtime(pats, all, opts)
|
||||
end
|
||||
|
||||
return {get_runtime_file_cached(basename, pats)}
|
||||
end
|
||||
|
||||
-- Copied from neovim/src/nvim/lua/vim.lua with two lines changed
|
||||
local function load_package(name)
|
||||
local basename = name:gsub('%.', sep)
|
||||
local paths = {"lua"..sep..basename..".lua", "lua"..sep..basename..sep.."init.lua"}
|
||||
|
||||
-- Original line:
|
||||
-- local found = vim.api.nvim__get_runtime(paths, false, {is_lua=true})
|
||||
local found = {get_runtime_file_cached(basename, paths)}
|
||||
if #found > 0 then
|
||||
local f, err = loadfile(found[1])
|
||||
return f or error(err)
|
||||
end
|
||||
|
||||
local so_paths = {}
|
||||
for _,trail in ipairs(vim._so_trails) do
|
||||
local path = "lua"..trail:gsub('?', basename) -- so_trails contains a leading slash
|
||||
table.insert(so_paths, path)
|
||||
end
|
||||
|
||||
-- Original line:
|
||||
-- found = vim.api.nvim__get_runtime(so_paths, false, {is_lua=true})
|
||||
found = {get_runtime_file_cached(basename, so_paths)}
|
||||
if #found > 0 then
|
||||
-- Making function name in Lua 5.1 (see src/loadlib.c:mkfuncname) is
|
||||
-- a) strip prefix up to and including the first dash, if any
|
||||
-- b) replace all dots by underscores
|
||||
-- c) prepend "luaopen_"
|
||||
-- So "foo-bar.baz" should result in "luaopen_bar_baz"
|
||||
local dash = name:find("-", 1, true)
|
||||
local modname = dash and name:sub(dash + 1) or name
|
||||
local f, err = loadlib(found[1], "luaopen_"..modname:gsub("%.", "_"))
|
||||
return f or error(err)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function load_from_cache(path)
|
||||
local mc = M.chunks
|
||||
|
||||
local cache = mc:get(path)
|
||||
|
||||
if not cache then
|
||||
return nil, string.format('No cache for path %s', path)
|
||||
end
|
||||
|
||||
local mhash, codes = unpack(cache)
|
||||
|
||||
if mhash ~= hash(path) then
|
||||
mc:set(path)
|
||||
mc.dirty = true
|
||||
return nil, string.format('Stale cache for path %s', path)
|
||||
end
|
||||
|
||||
local chunk = loadstring(codes)
|
||||
|
||||
if not chunk then
|
||||
mc:set(path)
|
||||
mc.dirty = true
|
||||
return nil, string.format('Cache error for path %s', path)
|
||||
end
|
||||
|
||||
return chunk
|
||||
end
|
||||
|
||||
local function loadfile_cached(path)
|
||||
cprofile(path, 'load_start')
|
||||
|
||||
local chunk, err
|
||||
|
||||
if M.chunks.enable then
|
||||
chunk, err = load_from_cache(path)
|
||||
if chunk and not err then
|
||||
log('Loaded cache for path %s', path)
|
||||
cprofile(path, 'load_end', 'cache')
|
||||
return chunk
|
||||
end
|
||||
log(err)
|
||||
end
|
||||
|
||||
chunk, err = _loadfile(path)
|
||||
|
||||
if not err and M.chunks.enable then
|
||||
log('Creating cache for path %s', path)
|
||||
M.chunks:set(path, {hash(path), string.dump(chunk)})
|
||||
M.chunks.dirty = true
|
||||
end
|
||||
|
||||
cprofile(path, 'load_end', 'standard')
|
||||
return chunk, err
|
||||
end
|
||||
|
||||
function M.save_cache()
|
||||
local function _save_cache(t)
|
||||
if not t.enable then
|
||||
return
|
||||
end
|
||||
if t.dirty then
|
||||
log('Updating chunk cache file: %s', t.path)
|
||||
local f = assert(io.open(t.path, 'w+b'))
|
||||
f:write(mpack.encode(t.cache))
|
||||
f:flush()
|
||||
t.dirty = false
|
||||
end
|
||||
end
|
||||
_save_cache(M.chunks)
|
||||
_save_cache(M.modpaths)
|
||||
end
|
||||
|
||||
local function clear_cache()
|
||||
local function _clear_cache(t)
|
||||
t.cache = {}
|
||||
os.remove(t.path)
|
||||
end
|
||||
_clear_cache(M.chunks)
|
||||
_clear_cache(M.modpaths)
|
||||
end
|
||||
|
||||
local function init_cache()
|
||||
local function _init_cache(t)
|
||||
if not t.enable then
|
||||
return
|
||||
end
|
||||
if fs_stat(t.path) then
|
||||
log('Loading cache file %s', t.path)
|
||||
local f = assert(io.open(t.path, 'rb'))
|
||||
local ok
|
||||
ok, t.cache = pcall(function()
|
||||
return mpack.decode(f:read'*a')
|
||||
end)
|
||||
|
||||
if not ok then
|
||||
log('Corrupted cache file, %s. Invalidating...', t.path)
|
||||
os.remove(t.path)
|
||||
t.cache = {}
|
||||
end
|
||||
t.dirty = not ok
|
||||
end
|
||||
end
|
||||
|
||||
if not uv.fs_stat(std_cache) then
|
||||
vim.fn.mkdir(std_cache, 'p')
|
||||
end
|
||||
|
||||
_init_cache(M.chunks)
|
||||
_init_cache(M.modpaths)
|
||||
end
|
||||
|
||||
local function setup()
|
||||
init_cache()
|
||||
|
||||
-- Usual package loaders
|
||||
-- 1. package.preload
|
||||
-- 2. vim._load_package
|
||||
-- 3. package.path
|
||||
-- 4. package.cpath
|
||||
-- 5. all-in-one
|
||||
|
||||
-- Override default functions
|
||||
for i, loader in ipairs(package.loaders) do
|
||||
if loader == vim._load_package then
|
||||
package.loaders[i] = load_package
|
||||
break
|
||||
end
|
||||
end
|
||||
vim._load_package = load_package
|
||||
|
||||
vim.api.nvim__get_runtime = get_runtime_cached
|
||||
loadfile = loadfile_cached
|
||||
|
||||
local augroup = api.nvim_create_augroup('impatient', {})
|
||||
|
||||
api.nvim_create_user_command('LuaCacheClear', clear_cache, {})
|
||||
api.nvim_create_user_command('LuaCacheLog' , print_log , {})
|
||||
|
||||
api.nvim_create_autocmd({'VimEnter', 'VimLeave'}, {
|
||||
group = augroup,
|
||||
callback = M.save_cache
|
||||
})
|
||||
|
||||
api.nvim_create_autocmd('OptionSet', {
|
||||
group = augroup,
|
||||
pattern = 'runtimepath',
|
||||
callback = function()
|
||||
rtp = vim.split(vim.o.rtp, ',')
|
||||
end
|
||||
})
|
||||
|
||||
end
|
||||
|
||||
setup()
|
||||
|
||||
return M
|
|
@ -0,0 +1,253 @@
|
|||
local M = {}
|
||||
|
||||
local sep = ''
|
||||
if (jit.os == 'Windows') then
|
||||
sep = '\\'
|
||||
else
|
||||
sep = '/'
|
||||
end
|
||||
|
||||
local api, uv = vim.api, vim.loop
|
||||
|
||||
local function load_buffer(title, lines)
|
||||
local bufnr = api.nvim_create_buf(false, false)
|
||||
api.nvim_buf_set_lines(bufnr, 0, 0, false, lines)
|
||||
api.nvim_buf_set_option(bufnr, 'bufhidden', 'wipe')
|
||||
api.nvim_buf_set_option(bufnr, 'buftype', 'nofile')
|
||||
api.nvim_buf_set_option(bufnr, 'swapfile', false)
|
||||
api.nvim_buf_set_option(bufnr, "modifiable", false)
|
||||
api.nvim_buf_set_name(bufnr, title)
|
||||
api.nvim_set_current_buf(bufnr)
|
||||
end
|
||||
|
||||
local function time_tostr(x)
|
||||
if x == 0 then
|
||||
return '?'
|
||||
end
|
||||
return string.format('%8.3fms', x)
|
||||
end
|
||||
|
||||
local function mem_tostr(x)
|
||||
local unit = ''
|
||||
for _, u in ipairs{'K', 'M', 'G'} do
|
||||
if x < 1000 then
|
||||
break
|
||||
end
|
||||
x = x / 1000
|
||||
unit = u
|
||||
end
|
||||
return string.format('%1.1f%s', x, unit)
|
||||
end
|
||||
|
||||
function M.print_profile(I, std_dirs)
|
||||
local mod_profile = I.modpaths.profile
|
||||
local chunk_profile = I.chunks.profile
|
||||
|
||||
if not mod_profile and not chunk_profile then
|
||||
print('Error: profiling was not enabled')
|
||||
return
|
||||
end
|
||||
|
||||
local total_resolve = 0
|
||||
local total_load = 0
|
||||
local modules = {}
|
||||
|
||||
for path, m in pairs(chunk_profile) do
|
||||
m.load = m.load_end - m.load_start
|
||||
m.load = m.load / 1000000
|
||||
m.path = path or '?'
|
||||
end
|
||||
|
||||
local module_content_width = 0
|
||||
|
||||
local unloaded = {}
|
||||
|
||||
for module, m in pairs(mod_profile) do
|
||||
local module_dot = module:gsub(sep, '.')
|
||||
m.module = module_dot
|
||||
|
||||
if not package.loaded[module_dot] and not package.loaded[module] then
|
||||
unloaded[#unloaded+1] = m
|
||||
else
|
||||
m.resolve = 0
|
||||
if m.resolve_start and m.resolve_end then
|
||||
m.resolve = m.resolve_end - m.resolve_start
|
||||
m.resolve = m.resolve / 1000000
|
||||
end
|
||||
|
||||
m.loader = m.loader or m.loader_guess
|
||||
|
||||
local path = I.modpaths.cache[module]
|
||||
local path_prof = chunk_profile[path]
|
||||
m.path = path or '?'
|
||||
|
||||
if path_prof then
|
||||
chunk_profile[path] = nil
|
||||
m.load = path_prof.load
|
||||
m.ploader = path_prof.loader
|
||||
else
|
||||
m.load = 0
|
||||
m.ploader = 'NA'
|
||||
end
|
||||
|
||||
total_resolve = total_resolve + m.resolve
|
||||
total_load = total_load + m.load
|
||||
|
||||
if #module > module_content_width then
|
||||
module_content_width = #module
|
||||
end
|
||||
|
||||
modules[#modules+1] = m
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(modules, function(a, b)
|
||||
return (a.resolve + a.load) > (b.resolve + b.load)
|
||||
end)
|
||||
|
||||
local paths = {}
|
||||
|
||||
local total_paths_load = 0
|
||||
for _, m in pairs(chunk_profile) do
|
||||
paths[#paths+1] = m
|
||||
total_paths_load = total_paths_load + m.load
|
||||
end
|
||||
|
||||
table.sort(paths, function(a, b)
|
||||
return a.load > b.load
|
||||
end)
|
||||
|
||||
|
||||
local lines = {}
|
||||
local function add(fmt, ...)
|
||||
local args = {...}
|
||||
for i, a in ipairs(args) do
|
||||
if type(a) == 'number' then
|
||||
args[i] = time_tostr(a)
|
||||
end
|
||||
end
|
||||
|
||||
lines[#lines+1] = string.format(fmt, unpack(args))
|
||||
end
|
||||
|
||||
local time_cell_width = 12
|
||||
local loader_cell_width = 11
|
||||
local time_content_width = time_cell_width - 2
|
||||
local loader_content_width = loader_cell_width - 2
|
||||
local module_cell_width = module_content_width + 2
|
||||
|
||||
local tcwl = string.rep('─', time_cell_width)
|
||||
local lcwl = string.rep('─', loader_cell_width)
|
||||
local mcwl = string.rep('─', module_cell_width+2)
|
||||
|
||||
local n = string.rep('─', 200)
|
||||
|
||||
local module_cell_format = '%-'..module_cell_width..'s'
|
||||
local loader_format = '%-'..loader_content_width..'s'
|
||||
local line_format = '%s │ %s │ %s │ %s │ %s │ %s'
|
||||
|
||||
local row_fmt = line_format:format(
|
||||
' %'..time_content_width..'s',
|
||||
loader_format,
|
||||
'%'..time_content_width..'s',
|
||||
loader_format,
|
||||
module_cell_format,
|
||||
'%s')
|
||||
|
||||
local title_fmt = line_format:format(
|
||||
' %-'..time_content_width..'s',
|
||||
loader_format,
|
||||
'%-'..time_content_width..'s',
|
||||
loader_format,
|
||||
module_cell_format,
|
||||
'%s')
|
||||
|
||||
local title1_width = time_cell_width+loader_cell_width-1
|
||||
local title1_fmt = ('%s │ %s │'):format(
|
||||
' %-'..title1_width..'s', '%-'..title1_width..'s')
|
||||
|
||||
add('Note: this report is not a measure of startup time. Only use this for comparing')
|
||||
add('between cached and uncached loads of Lua modules')
|
||||
add('')
|
||||
|
||||
add('Cache files:')
|
||||
for _, f in ipairs{ I.chunks.path, I.modpaths.path } do
|
||||
local size = vim.loop.fs_stat(f).size
|
||||
add(' %s %s', f, mem_tostr(size))
|
||||
end
|
||||
add('')
|
||||
|
||||
add('Standard directories:')
|
||||
for alias, path in pairs(std_dirs) do
|
||||
add(' %-12s -> %s', alias, path)
|
||||
end
|
||||
add('')
|
||||
|
||||
add('%s─%s┬%s─%s┐', tcwl, lcwl, tcwl, lcwl)
|
||||
add(title1_fmt, 'Resolve', 'Load')
|
||||
add('%s┬%s┼%s┬%s┼%s┬%s', tcwl, lcwl, tcwl, lcwl, mcwl, n)
|
||||
add(title_fmt, 'Time', 'Method', 'Time', 'Method', 'Module', 'Path')
|
||||
add('%s┼%s┼%s┼%s┼%s┼%s', tcwl, lcwl, tcwl, lcwl, mcwl, n)
|
||||
add(row_fmt, total_resolve, '', total_load, '', 'Total', '')
|
||||
add('%s┼%s┼%s┼%s┼%s┼%s', tcwl, lcwl, tcwl, lcwl, mcwl, n)
|
||||
for _, p in ipairs(modules) do
|
||||
add(row_fmt, p.resolve, p.loader, p.load, p.ploader, p.module, p.path)
|
||||
end
|
||||
add('%s┴%s┴%s┴%s┴%s┴%s', tcwl, lcwl, tcwl, lcwl, mcwl, n)
|
||||
|
||||
if #paths > 0 then
|
||||
add('')
|
||||
add(n)
|
||||
local f3 = ' %'..time_content_width..'s │ %'..loader_content_width..'s │ %s'
|
||||
add('Files loaded with no associated module')
|
||||
add('%s┬%s┬%s', tcwl, lcwl, n)
|
||||
add(f3, 'Time', 'Loader', 'Path')
|
||||
add('%s┼%s┼%s', tcwl, lcwl, n)
|
||||
add(f3, total_paths_load, '', 'Total')
|
||||
add('%s┼%s┼%s', tcwl, lcwl, n)
|
||||
for _, p in ipairs(paths) do
|
||||
add(f3, p.load, p.loader, p.path)
|
||||
end
|
||||
add('%s┴%s┴%s', tcwl, lcwl, n)
|
||||
end
|
||||
|
||||
if #unloaded > 0 then
|
||||
add('')
|
||||
add(n)
|
||||
add('Modules which were unable to loaded')
|
||||
add(n)
|
||||
for _, p in ipairs(unloaded) do
|
||||
lines[#lines+1] = p.module
|
||||
end
|
||||
add(n)
|
||||
end
|
||||
|
||||
load_buffer('Impatient Profile Report', lines)
|
||||
end
|
||||
|
||||
M.setup = function(profile)
|
||||
local _require = require
|
||||
|
||||
require = function(mod)
|
||||
local basename = mod:gsub('%.', sep)
|
||||
if not profile[basename] then
|
||||
profile[basename] = {}
|
||||
profile[basename].resolve_start = uv.hrtime()
|
||||
profile[basename].loader_guess = ''
|
||||
end
|
||||
return _require(mod)
|
||||
end
|
||||
|
||||
-- Add profiling around all the loaders
|
||||
local pl = package.loaders
|
||||
for i = 1, #pl do
|
||||
local l = pl[i]
|
||||
pl[i] = function(mod)
|
||||
local basename = mod:gsub('%.', sep)
|
||||
profile[basename].loader_guess = i == 1 and 'preloader' or 'loader #'..i
|
||||
return l(mod)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
|
@ -0,0 +1,280 @@
|
|||
local helpers = require('test.functional.helpers')()
|
||||
|
||||
local clear = helpers.clear
|
||||
local exec_lua = helpers.exec_lua
|
||||
local eq = helpers.eq
|
||||
local cmd = helpers.command
|
||||
|
||||
local nvim07
|
||||
|
||||
local function gen_exp(exp)
|
||||
local neovim_dir = nvim07 and 'neovim-v0.7.0' or 'neovim-master'
|
||||
local cwd = exec_lua('return vim.loop.cwd()')
|
||||
|
||||
local exp1 = {}
|
||||
for _, v in pairs(exp) do
|
||||
if type(v) == 'string' then
|
||||
v = v:gsub('{CWD}', cwd)
|
||||
v = v:gsub('{NVIM}', neovim_dir)
|
||||
exp1[#exp1+1] = v
|
||||
end
|
||||
end
|
||||
|
||||
return exp1
|
||||
end
|
||||
|
||||
local gen_exp_cold = function()
|
||||
return gen_exp{
|
||||
'Creating cache for module plugins',
|
||||
'No cache for path ./test/lua/plugins.lua',
|
||||
'Creating cache for path ./test/lua/plugins.lua',
|
||||
'Creating cache for module telescope',
|
||||
'No cache for path {CWD}/scratch/telescope.nvim/lua/telescope/init.lua',
|
||||
'Creating cache for path {CWD}/scratch/telescope.nvim/lua/telescope/init.lua',
|
||||
'Creating cache for module telescope/_extensions',
|
||||
'No cache for path {CWD}/scratch/telescope.nvim/lua/telescope/_extensions/init.lua',
|
||||
'Creating cache for path {CWD}/scratch/telescope.nvim/lua/telescope/_extensions/init.lua',
|
||||
'Creating cache for module gitsigns',
|
||||
'No cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns.lua',
|
||||
'Creating cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns.lua',
|
||||
'Creating cache for module plenary/async/async',
|
||||
'No cache for path {CWD}/scratch/plenary.nvim/lua/plenary/async/async.lua',
|
||||
'Creating cache for path {CWD}/scratch/plenary.nvim/lua/plenary/async/async.lua',
|
||||
'Creating cache for module plenary/vararg',
|
||||
'No cache for path {CWD}/scratch/plenary.nvim/lua/plenary/vararg/init.lua',
|
||||
'Creating cache for path {CWD}/scratch/plenary.nvim/lua/plenary/vararg/init.lua',
|
||||
'Creating cache for module plenary/vararg/rotate',
|
||||
'No cache for path {CWD}/scratch/plenary.nvim/lua/plenary/vararg/rotate.lua',
|
||||
'Creating cache for path {CWD}/scratch/plenary.nvim/lua/plenary/vararg/rotate.lua',
|
||||
'Creating cache for module plenary/tbl',
|
||||
'No cache for path {CWD}/scratch/plenary.nvim/lua/plenary/tbl.lua',
|
||||
'Creating cache for path {CWD}/scratch/plenary.nvim/lua/plenary/tbl.lua',
|
||||
'Creating cache for module plenary/errors',
|
||||
'No cache for path {CWD}/scratch/plenary.nvim/lua/plenary/errors.lua',
|
||||
'Creating cache for path {CWD}/scratch/plenary.nvim/lua/plenary/errors.lua',
|
||||
'Creating cache for module plenary/functional',
|
||||
'No cache for path {CWD}/scratch/plenary.nvim/lua/plenary/functional.lua',
|
||||
'Creating cache for path {CWD}/scratch/plenary.nvim/lua/plenary/functional.lua',
|
||||
'Creating cache for module plenary/async/util',
|
||||
'No cache for path {CWD}/scratch/plenary.nvim/lua/plenary/async/util.lua',
|
||||
'Creating cache for path {CWD}/scratch/plenary.nvim/lua/plenary/async/util.lua',
|
||||
'Creating cache for module plenary/async/control',
|
||||
'No cache for path {CWD}/scratch/plenary.nvim/lua/plenary/async/control.lua',
|
||||
'Creating cache for path {CWD}/scratch/plenary.nvim/lua/plenary/async/control.lua',
|
||||
'Creating cache for module plenary/async/structs',
|
||||
'No cache for path {CWD}/scratch/plenary.nvim/lua/plenary/async/structs.lua',
|
||||
'Creating cache for path {CWD}/scratch/plenary.nvim/lua/plenary/async/structs.lua',
|
||||
'Creating cache for module gitsigns/status',
|
||||
'No cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/status.lua',
|
||||
'Creating cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/status.lua',
|
||||
'Creating cache for module gitsigns/git',
|
||||
'No cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/git.lua',
|
||||
'Creating cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/git.lua',
|
||||
'Creating cache for module plenary/job',
|
||||
'No cache for path {CWD}/scratch/plenary.nvim/lua/plenary/job.lua',
|
||||
'Creating cache for path {CWD}/scratch/plenary.nvim/lua/plenary/job.lua',
|
||||
'Creating cache for module gitsigns/debug',
|
||||
'No cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/debug.lua',
|
||||
'Creating cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/debug.lua',
|
||||
'Creating cache for module gitsigns/util',
|
||||
'No cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/util.lua',
|
||||
'Creating cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/util.lua',
|
||||
'Creating cache for module gitsigns/hunks',
|
||||
'No cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/hunks.lua',
|
||||
'Creating cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/hunks.lua',
|
||||
'Creating cache for module gitsigns/signs',
|
||||
'No cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/signs.lua',
|
||||
'Creating cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/signs.lua',
|
||||
'Creating cache for module gitsigns/config',
|
||||
'No cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/config.lua',
|
||||
'Creating cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/config.lua',
|
||||
'Creating cache for module gitsigns/manager',
|
||||
'No cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/manager.lua',
|
||||
'Creating cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/manager.lua',
|
||||
'Creating cache for module gitsigns/cache',
|
||||
'No cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/cache.lua',
|
||||
'Creating cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/cache.lua',
|
||||
'Creating cache for module gitsigns/debounce',
|
||||
'No cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/debounce.lua',
|
||||
'Creating cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/debounce.lua',
|
||||
'Creating cache for module gitsigns/highlight',
|
||||
'No cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/highlight.lua',
|
||||
'Creating cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/highlight.lua',
|
||||
'Creating cache for module spellsitter',
|
||||
'No cache for path {CWD}/scratch/spellsitter.nvim/lua/spellsitter.lua',
|
||||
'Creating cache for path {CWD}/scratch/spellsitter.nvim/lua/spellsitter.lua',
|
||||
'Creating cache for module vim/treesitter/query',
|
||||
'No cache for path {CWD}/{NVIM}/runtime/lua/vim/treesitter/query.lua',
|
||||
'Creating cache for path {CWD}/{NVIM}/runtime/lua/vim/treesitter/query.lua',
|
||||
'Creating cache for module vim/treesitter/language',
|
||||
'No cache for path {CWD}/{NVIM}/runtime/lua/vim/treesitter/language.lua',
|
||||
'Creating cache for path {CWD}/{NVIM}/runtime/lua/vim/treesitter/language.lua',
|
||||
'Creating cache for module vim/treesitter',
|
||||
'No cache for path {CWD}/{NVIM}/runtime/lua/vim/treesitter.lua',
|
||||
'Creating cache for path {CWD}/{NVIM}/runtime/lua/vim/treesitter.lua',
|
||||
'Creating cache for module vim/treesitter/languagetree',
|
||||
'No cache for path {CWD}/{NVIM}/runtime/lua/vim/treesitter/languagetree.lua',
|
||||
'Creating cache for path {CWD}/{NVIM}/runtime/lua/vim/treesitter/languagetree.lua',
|
||||
'Creating cache for module colorizer',
|
||||
'No cache for path {CWD}/scratch/nvim-colorizer.lua/lua/colorizer.lua',
|
||||
'Creating cache for path {CWD}/scratch/nvim-colorizer.lua/lua/colorizer.lua',
|
||||
'Creating cache for module colorizer/nvim',
|
||||
'No cache for path {CWD}/scratch/nvim-colorizer.lua/lua/colorizer/nvim.lua',
|
||||
'Creating cache for path {CWD}/scratch/nvim-colorizer.lua/lua/colorizer/nvim.lua',
|
||||
'Creating cache for module colorizer/trie',
|
||||
'No cache for path {CWD}/scratch/nvim-colorizer.lua/lua/colorizer/trie.lua',
|
||||
'Creating cache for path {CWD}/scratch/nvim-colorizer.lua/lua/colorizer/trie.lua',
|
||||
'Creating cache for module lspconfig',
|
||||
'No cache for path {CWD}/scratch/nvim-lspconfig/lua/lspconfig.lua',
|
||||
'Creating cache for path {CWD}/scratch/nvim-lspconfig/lua/lspconfig.lua',
|
||||
'Creating cache for module lspconfig/configs',
|
||||
'No cache for path {CWD}/scratch/nvim-lspconfig/lua/lspconfig/configs.lua',
|
||||
'Creating cache for path {CWD}/scratch/nvim-lspconfig/lua/lspconfig/configs.lua',
|
||||
'Creating cache for module lspconfig/util',
|
||||
'No cache for path {CWD}/scratch/nvim-lspconfig/lua/lspconfig/util.lua',
|
||||
'Creating cache for path {CWD}/scratch/nvim-lspconfig/lua/lspconfig/util.lua',
|
||||
'Creating cache for module vim/lsp',
|
||||
'No cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp.lua',
|
||||
'Creating cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp.lua',
|
||||
'Creating cache for module vim/lsp/handlers',
|
||||
'No cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/handlers.lua',
|
||||
'Creating cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/handlers.lua',
|
||||
'Creating cache for module vim/lsp/log',
|
||||
'No cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/log.lua',
|
||||
'Creating cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/log.lua',
|
||||
'Creating cache for module vim/lsp/protocol',
|
||||
'No cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/protocol.lua',
|
||||
'Creating cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/protocol.lua',
|
||||
'Creating cache for module vim/lsp/util',
|
||||
'No cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/util.lua',
|
||||
'Creating cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/util.lua',
|
||||
'Creating cache for module vim/lsp/_snippet',
|
||||
'No cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/_snippet.lua',
|
||||
'Creating cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/_snippet.lua',
|
||||
'Creating cache for module vim/highlight',
|
||||
'No cache for path {CWD}/{NVIM}/runtime/lua/vim/highlight.lua',
|
||||
'Creating cache for path {CWD}/{NVIM}/runtime/lua/vim/highlight.lua',
|
||||
'Creating cache for module vim/lsp/rpc',
|
||||
'No cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/rpc.lua',
|
||||
'Creating cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/rpc.lua',
|
||||
'Creating cache for module vim/lsp/sync',
|
||||
'No cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/sync.lua',
|
||||
'Creating cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/sync.lua',
|
||||
'Creating cache for module vim/lsp/buf',
|
||||
'No cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/buf.lua',
|
||||
'Creating cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/buf.lua',
|
||||
'Creating cache for module vim/lsp/diagnostic',
|
||||
'No cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/diagnostic.lua',
|
||||
'Creating cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/diagnostic.lua',
|
||||
'Creating cache for module vim/lsp/codelens',
|
||||
'No cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/codelens.lua',
|
||||
'Creating cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/codelens.lua',
|
||||
'Creating cache for module bufferline',
|
||||
'No cache for path {CWD}/scratch/bufferline.nvim/lua/bufferline.lua',
|
||||
'Creating cache for path {CWD}/scratch/bufferline.nvim/lua/bufferline.lua',
|
||||
'Creating cache for module bufferline/constants',
|
||||
'No cache for path {CWD}/scratch/bufferline.nvim/lua/bufferline/constants.lua',
|
||||
'Creating cache for path {CWD}/scratch/bufferline.nvim/lua/bufferline/constants.lua',
|
||||
'Creating cache for module bufferline/utils',
|
||||
'No cache for path {CWD}/scratch/bufferline.nvim/lua/bufferline/utils.lua',
|
||||
'Creating cache for path {CWD}/scratch/bufferline.nvim/lua/bufferline/utils.lua',
|
||||
'Updating chunk cache file: scratch/cache/nvim/luacache_chunks',
|
||||
'Updating chunk cache file: scratch/cache/nvim/luacache_modpaths'
|
||||
}
|
||||
end
|
||||
|
||||
local gen_exp_hot = function()
|
||||
return gen_exp{
|
||||
'Loading cache file scratch/cache/nvim/luacache_chunks',
|
||||
'Loading cache file scratch/cache/nvim/luacache_modpaths',
|
||||
'Loaded cache for path ./test/lua/plugins.lua',
|
||||
'Loaded cache for path {CWD}/scratch/telescope.nvim/lua/telescope/init.lua',
|
||||
'Loaded cache for path {CWD}/scratch/telescope.nvim/lua/telescope/_extensions/init.lua',
|
||||
'Loaded cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns.lua',
|
||||
'Loaded cache for path {CWD}/scratch/plenary.nvim/lua/plenary/async/async.lua',
|
||||
'Loaded cache for path {CWD}/scratch/plenary.nvim/lua/plenary/vararg/init.lua',
|
||||
'Loaded cache for path {CWD}/scratch/plenary.nvim/lua/plenary/vararg/rotate.lua',
|
||||
'Loaded cache for path {CWD}/scratch/plenary.nvim/lua/plenary/tbl.lua',
|
||||
'Loaded cache for path {CWD}/scratch/plenary.nvim/lua/plenary/errors.lua',
|
||||
'Loaded cache for path {CWD}/scratch/plenary.nvim/lua/plenary/functional.lua',
|
||||
'Loaded cache for path {CWD}/scratch/plenary.nvim/lua/plenary/async/util.lua',
|
||||
'Loaded cache for path {CWD}/scratch/plenary.nvim/lua/plenary/async/control.lua',
|
||||
'Loaded cache for path {CWD}/scratch/plenary.nvim/lua/plenary/async/structs.lua',
|
||||
'Loaded cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/status.lua',
|
||||
'Loaded cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/git.lua',
|
||||
'Loaded cache for path {CWD}/scratch/plenary.nvim/lua/plenary/job.lua',
|
||||
'Loaded cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/debug.lua',
|
||||
'Loaded cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/util.lua',
|
||||
'Loaded cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/hunks.lua',
|
||||
'Loaded cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/signs.lua',
|
||||
'Loaded cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/config.lua',
|
||||
'Loaded cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/manager.lua',
|
||||
'Loaded cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/cache.lua',
|
||||
'Loaded cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/debounce.lua',
|
||||
'Loaded cache for path {CWD}/scratch/gitsigns.nvim/lua/gitsigns/highlight.lua',
|
||||
'Loaded cache for path {CWD}/scratch/spellsitter.nvim/lua/spellsitter.lua',
|
||||
'Loaded cache for path {CWD}/{NVIM}/runtime/lua/vim/treesitter/query.lua',
|
||||
'Loaded cache for path {CWD}/{NVIM}/runtime/lua/vim/treesitter/language.lua',
|
||||
'Loaded cache for path {CWD}/{NVIM}/runtime/lua/vim/treesitter.lua',
|
||||
'Loaded cache for path {CWD}/{NVIM}/runtime/lua/vim/treesitter/languagetree.lua',
|
||||
'Loaded cache for path {CWD}/scratch/nvim-colorizer.lua/lua/colorizer.lua',
|
||||
'Loaded cache for path {CWD}/scratch/nvim-colorizer.lua/lua/colorizer/nvim.lua',
|
||||
'Loaded cache for path {CWD}/scratch/nvim-colorizer.lua/lua/colorizer/trie.lua',
|
||||
'Loaded cache for path {CWD}/scratch/nvim-lspconfig/lua/lspconfig.lua',
|
||||
'Loaded cache for path {CWD}/scratch/nvim-lspconfig/lua/lspconfig/configs.lua',
|
||||
'Loaded cache for path {CWD}/scratch/nvim-lspconfig/lua/lspconfig/util.lua',
|
||||
'Loaded cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp.lua',
|
||||
'Loaded cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/handlers.lua',
|
||||
'Loaded cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/log.lua',
|
||||
'Loaded cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/protocol.lua',
|
||||
'Loaded cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/util.lua',
|
||||
'Loaded cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/_snippet.lua',
|
||||
'Loaded cache for path {CWD}/{NVIM}/runtime/lua/vim/highlight.lua',
|
||||
'Loaded cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/rpc.lua',
|
||||
'Loaded cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/sync.lua',
|
||||
'Loaded cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/buf.lua',
|
||||
'Loaded cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/diagnostic.lua',
|
||||
'Loaded cache for path {CWD}/{NVIM}/runtime/lua/vim/lsp/codelens.lua',
|
||||
'Loaded cache for path {CWD}/scratch/bufferline.nvim/lua/bufferline.lua',
|
||||
'Loaded cache for path {CWD}/scratch/bufferline.nvim/lua/bufferline/constants.lua',
|
||||
'Loaded cache for path {CWD}/scratch/bufferline.nvim/lua/bufferline/utils.lua'
|
||||
}
|
||||
end
|
||||
|
||||
describe('impatient', function()
|
||||
local function reset()
|
||||
clear()
|
||||
nvim07 = exec_lua('return vim.version().minor') == 7
|
||||
cmd [[set runtimepath=$VIMRUNTIME,.,./test]]
|
||||
cmd [[let $XDG_CACHE_HOME='scratch/cache']]
|
||||
cmd [[set packpath=]]
|
||||
end
|
||||
|
||||
before_each(function()
|
||||
reset()
|
||||
end)
|
||||
|
||||
it('load plugins without impatient', function()
|
||||
exec_lua([[require('plugins')]])
|
||||
end)
|
||||
|
||||
local function run()
|
||||
exec_lua[[
|
||||
require('impatient')
|
||||
require('plugins')
|
||||
_G.__luacache.save_cache()
|
||||
]]
|
||||
end
|
||||
|
||||
it('creates cache', function()
|
||||
os.execute[[rm -rf scratch/cache]]
|
||||
run()
|
||||
eq(gen_exp_cold(), exec_lua("return _G.__luacache.log"))
|
||||
end)
|
||||
|
||||
it('loads cache', function()
|
||||
run()
|
||||
eq(gen_exp_hot(), exec_lua("return _G.__luacache.log"))
|
||||
end)
|
||||
|
||||
end)
|
|
@ -0,0 +1,43 @@
|
|||
|
||||
local init = {
|
||||
['neovim/nvim-lspconfig'] = '2f026f21',
|
||||
['nvim-lua/plenary.nvim'] = '06266e7b',
|
||||
['nvim-lua/telescope.nvim'] = 'ac42f0c2',
|
||||
['lewis6991/gitsigns.nvim'] = 'daa233aa',
|
||||
['lewis6991/spellsitter.nvim'] = '7f9e8471',
|
||||
['norcalli/nvim-colorizer.lua'] = '36c610a9',
|
||||
['akinsho/bufferline.nvim'] = 'bede234e'
|
||||
}
|
||||
|
||||
local testdir = 'scratch'
|
||||
|
||||
vim.fn.system{"mkdir", testdir}
|
||||
|
||||
for plugin, sha in pairs(init) do
|
||||
local plugin_dir = plugin:match('.*/(.*)')
|
||||
local plugin_dir2 = testdir..'/'..plugin_dir
|
||||
vim.fn.system{
|
||||
'git', '-C', testdir, 'clone',
|
||||
'https://github.com/'..plugin, plugin_dir
|
||||
}
|
||||
|
||||
-- local rev = (vim.fn.system{
|
||||
-- 'git', '-C', plugin_dir2,
|
||||
-- 'rev-list', 'HEAD', '-n', '1', '--first-parent', '--before=2021-09-05'
|
||||
-- }):sub(1,-2)
|
||||
|
||||
-- if sha then
|
||||
-- assert(vim.startswith(rev, sha), ('Plugin sha for %s does match %s != %s'):format(plugin, rev, sha))
|
||||
-- end
|
||||
|
||||
vim.fn.system{'git', '-C', plugin_dir2, 'checkout', sha}
|
||||
|
||||
vim.opt.rtp:prepend(vim.loop.fs_realpath("scratch/"..plugin_dir))
|
||||
end
|
||||
|
||||
require'telescope'
|
||||
require'gitsigns'
|
||||
require'spellsitter'
|
||||
require'colorizer'
|
||||
require'lspconfig'
|
||||
require'bufferline'
|
|
@ -0,0 +1,10 @@
|
|||
-- Modules loaded here will not be cleared and reloaded by Busted.
|
||||
-- Busted started doing this to help provide more isolation.
|
||||
local global_helpers = require('test.helpers')
|
||||
|
||||
-- Bypoass CI behaviour logic
|
||||
global_helpers.isCI = function(_)
|
||||
return false
|
||||
end
|
||||
|
||||
local helpers = require('test.functional.helpers')()
|
51
dotfiles/pack/plugins/start/mini.nvim/.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Executable file
51
dotfiles/pack/plugins/start/mini.nvim/.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Executable file
|
@ -0,0 +1,51 @@
|
|||
name: Bug report
|
||||
description: Report a problem
|
||||
labels: [bug]
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Contributing guidelines
|
||||
options:
|
||||
- label: I have read [CONTRIBUTING.md](https://github.com/echasnovski/mini.nvim/blob/main/CONTRIBUTING.md)
|
||||
required: true
|
||||
- label: I have read [CODE_OF_CONDUCT.md](https://github.com/echasnovski/mini.nvim/blob/main/CODE_OF_CONDUCT.md)
|
||||
required: true
|
||||
- label: I have updated 'mini.nvim' to latest version
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: "Module(s)"
|
||||
description: "List one or several modules this bug is coming from"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Description"
|
||||
description: "A short description of a problem"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: "Neovim version"
|
||||
description: "Something like `0.5`, `0.5.1`, Neovim nightly (please, include latest commit)"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Steps to reproduce"
|
||||
description: "Steps to reproduce using as minimal config as possible"
|
||||
value: |
|
||||
1. `nvim -nu minimal.lua`
|
||||
2. ...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Expected behavior"
|
||||
description: "A description of behavior you expected"
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Actual behavior"
|
||||
description: "A description of behavior you observed (feel free to include images, gifs, etc.)"
|
||||
validations:
|
||||
required: true
|
|
@ -0,0 +1 @@
|
|||
blank_issues_enabled: true
|
24
dotfiles/pack/plugins/start/mini.nvim/.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Executable file
24
dotfiles/pack/plugins/start/mini.nvim/.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Executable file
|
@ -0,0 +1,24 @@
|
|||
name: Feature request
|
||||
description: Describe a feature you want to see
|
||||
labels: [feature-request]
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Contributing guidelines
|
||||
options:
|
||||
- label: I have read [CONTRIBUTING.md](https://github.com/echasnovski/mini.nvim/blob/main/CONTRIBUTING.md)
|
||||
required: true
|
||||
- label: I have read [CODE_OF_CONDUCT.md](https://github.com/echasnovski/mini.nvim/blob/main/CODE_OF_CONDUCT.md)
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: "Module(s)"
|
||||
description: "List one or several modules this feature is related to"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Description"
|
||||
description: "A concise and justified description of a feature"
|
||||
validations:
|
||||
required: true
|
|
@ -0,0 +1,2 @@
|
|||
- [ ] I have read [CONTRIBUTING.md](https://github.com/echasnovski/mini.nvim/blob/main/CONTRIBUTING.md)
|
||||
- [ ] I have read [CODE_OF_CONDUCT.md](https://github.com/echasnovski/mini.nvim/blob/main/CODE_OF_CONDUCT.md)
|
|
@ -0,0 +1,60 @@
|
|||
name: Linting and style checking
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
stylua:
|
||||
name: Formatting
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: JohnnyMorganz/stylua-action@1.0.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
version: v0.14.0
|
||||
# CLI arguments
|
||||
args: --color always --check .
|
||||
|
||||
gendoc:
|
||||
name: Document generation
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Neovim
|
||||
uses: rhysd/action-setup-vim@v1
|
||||
id: neovim
|
||||
with:
|
||||
neovim: true
|
||||
- uses: actions/checkout@v2
|
||||
- name: Generate documentation
|
||||
run: make --silent documentation
|
||||
- name: Check for changes
|
||||
run: if [[ -n $(git status -s) ]]; then exit 1; fi
|
||||
|
||||
case-sensitivity:
|
||||
name: File case sensitivity
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Check Case Sensitivity
|
||||
uses: credfeto/action-case-checker@v1.2.1
|
||||
|
||||
checkout:
|
||||
# Test possibility of checking out and setting up 'mini.nvim' on non-Linux
|
||||
# This can guard from possible problems:
|
||||
# - Long file names (particularly from reference screenshots).
|
||||
name: Test checkout
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup neovim
|
||||
uses: rhysd/action-setup-vim@v1
|
||||
with:
|
||||
# Uses latest stable by default
|
||||
neovim: true
|
||||
- name: Test setup
|
||||
run: make basic_setup
|
|
@ -0,0 +1,28 @@
|
|||
name: Run tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Run tests
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
strategy:
|
||||
matrix:
|
||||
neovim_version: ['v0.5.1', 'v0.6.1', 'v0.7.0', 'nightly']
|
||||
|
||||
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: Setup neovim
|
||||
uses: rhysd/action-setup-vim@v1
|
||||
with:
|
||||
neovim: true
|
||||
version: ${{ matrix.neovim_version }}
|
||||
- name: Run tests
|
||||
run: make test
|
|
@ -0,0 +1,2 @@
|
|||
doc/tags
|
||||
deps
|
|
@ -0,0 +1,13 @@
|
|||
repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: stylua
|
||||
name: StyLua
|
||||
language: system
|
||||
entry: stylua
|
||||
types: [lua]
|
||||
- id: gendocs
|
||||
name: Gendocs
|
||||
language: system
|
||||
entry: make --silent documentation
|
||||
types: []
|
|
@ -0,0 +1,7 @@
|
|||
column_width = 120
|
||||
line_endings = "Unix"
|
||||
indent_type = "Spaces"
|
||||
indent_width = 2
|
||||
quote_style = "AutoPreferSingle"
|
||||
no_call_parentheses = false
|
||||
collapse_simple_statement = "Always"
|
|
@ -0,0 +1 @@
|
|||
deps
|
|
@ -0,0 +1,200 @@
|
|||
# Version 0.5.0.9000
|
||||
|
||||
## mini.align
|
||||
|
||||
Introduction of new module.
|
||||
|
||||
## mini.base16
|
||||
|
||||
- FEATURE: Add support for many plugin integrations.
|
||||
- FEATURE: Implement `MiniBase16.config.plugins` for configuring plugin integrations.
|
||||
- BREAKING: Change some 'mini.nvim' highlights:
|
||||
- `MiniCompletionActiveParameter` now highlights with background instead of underline.
|
||||
- `MiniJump2dSpot` now explicitly defined to use plugin's palette.
|
||||
- `MiniStarterItemPrefix` and `MiniStarterQuery` are now bold for better visibility.
|
||||
- BREAKING: Update highlight for changed git diff to be more visible and to comply more with general guidelines.
|
||||
|
||||
## mini.jump
|
||||
|
||||
- BREAKING: Allow cursor to be positioned past the end of previous/current line (#113).
|
||||
|
||||
## mini.starter
|
||||
|
||||
- Item evaluation is now prepended with query reset, as it is rarely needed any more (#105).
|
||||
|
||||
## mini.surround
|
||||
|
||||
- BREAKING FEATURE: update 'mini.surround' to share as much with 'mini.ai' as possible. This provides more integrated experience while enabling more useful features. Details:
|
||||
- Custom surrounding specification for input action has changed. Instead of `{ find = <string>, extract = <string> }` it is now `{ <function or composed pattern> }`. Previous format will work until the next release. See more in help file.
|
||||
- Algorithm for finding surrounding is now more powerful. It allows searching for more complex surroundings (via composed patterns or array of region pairs) and respects `v:count`.
|
||||
- Multiline input and output surroundings are now supported.
|
||||
- Opening brackets (`(`, `[`, `{`, `<`) now include whitespace in surrounding: input surrounding selects all inner edge whitespace, output surrounding is padded with single space.
|
||||
- Surrounding identifier `i` ("interactive") is soft deprecated in favor of `?` ("user prompt").
|
||||
- New surrounding aliases:
|
||||
- `b` for "brackets". Input - any of balanced `()`, `[]` `{}`. Output - `()`.
|
||||
- `q` for "quotes". Input - any of `"`, `'`, `` ` ``. Output - `""`.
|
||||
- Three new search methods `'prev'`, `'next'`, and `'nearest'` for finding non-covering previous and next surrounding.
|
||||
- BREAKING FEATURE: Implement "last"/"next" extended mappings which force `'prev'` or `'next'` search method. Controlled with `config.mappings.suffix_last` and `config.mappings.suffix_next`respectively. This also means that custom surroundings with identifier equal to "last"/"next" mappings suffixes (defaults to 'l' and 'n') will work only with long enough delay after typing action mapping.
|
||||
- FEATURE: Implement `MiniSurround.gen_spec` with generators of common surrounding specifications (like `MiniSurround.gen_spec.input.treesitter` for tree-sitter based input surrounding).
|
||||
|
||||
|
||||
# Version 0.5.0
|
||||
|
||||
- Update all tests to use new 'mini.test' module.
|
||||
- FEATURE: Implement buffer local configuration. This is done with `vim.b.mini*_config` buffer variables.
|
||||
- Add new `minicyan` color scheme.
|
||||
|
||||
## mini.ai
|
||||
|
||||
Introduction of new module.
|
||||
|
||||
## mini.comment
|
||||
|
||||
- FEATURE: Now hooks can be used to terminate further actions by returning `false` (#108).
|
||||
|
||||
## mini.indentscope
|
||||
|
||||
- BREAKING: Soft deprecate `vim.b.miniindentscope_options` in favor of using `options` field of `miniindentscope_config`.
|
||||
|
||||
## mini.sessions
|
||||
|
||||
- FEATURE: Hooks are now called with active session data as argument.
|
||||
|
||||
## mini.starter
|
||||
|
||||
- FEATURE: Now it is possible to open multiple Starter buffers at the same time (#82). This comes with several changes which won't affect most users:
|
||||
- BREAKING: `MiniStarter.content` is deprecated. Use `MiniStarter.get_content()`.
|
||||
- All functions dealing with Starter buffer now have `buf_id` as argument (no breaking behavior).
|
||||
|
||||
## mini.statusline
|
||||
|
||||
- FEATURE: Implement `config.use_icons` which controls whether to use icons by default.
|
||||
|
||||
## mini.test
|
||||
|
||||
Introduction of new module.
|
||||
|
||||
## mini.trailspace
|
||||
|
||||
- FEATURE: Implement `MiniTrailspace.trim_last_lines()`.
|
||||
|
||||
|
||||
# Version 0.4.0
|
||||
|
||||
- Update all modules to supply mapping description for Neovim>=0.7.
|
||||
- Add new module 'mini.jump2d'.
|
||||
- Cover all modules with extensive tests.
|
||||
|
||||
## mini.comment
|
||||
|
||||
- FEATURE: Implement `config.hooks` with `pre` and `post` hooks (executed before and after successful commenting). Fixes #50, #59.
|
||||
|
||||
## mini.completion
|
||||
|
||||
- Implement support for `additionalTextEdits` (issue #61).
|
||||
|
||||
## mini.jump
|
||||
|
||||
- FEATURE: Implement idle timeout to stop jumping automatically (@annenpolka, #56).
|
||||
- FEATURE: Implement `MiniJump.state`: table with useful model-related information.
|
||||
- BREAKING: Soft deprecate `config.highlight_delay` in favor of `config.delay.highlight`.
|
||||
- Update process of querying target symbol: show help message after delay, allow `<C-c>` to stop selecting target.
|
||||
|
||||
## mini.jump2d
|
||||
|
||||
Introduction of new module.
|
||||
|
||||
## mini.pairs
|
||||
|
||||
- Create mappings for `<BS>` and `<CR>` in certain mode only after some pair is registered in that mode.
|
||||
|
||||
## mini.sessions
|
||||
|
||||
- FEATURE: Implement `MiniSessions.select()` to select session interactively and perform action on it.
|
||||
- FEATURE: Implement `config.hooks` to execute hook functions before and after successful action.
|
||||
- BREAKING: All feedback about incorrect behavior is now an error instead of message notifications.
|
||||
|
||||
## mini.starter
|
||||
|
||||
- Allow `config.header` and `config.footer` be any value, which will be converted to string via `tostring()`.
|
||||
- Update query logic to not allow queries which result into no items.
|
||||
- Add `<C-n>` and `<C-p>` to default mappings.
|
||||
|
||||
## mini.statusline
|
||||
|
||||
- BREAKING: change default icon for `MiniStatusline.section_diagnostics()` from ﯭ to due to former having issues in some terminal emulators.
|
||||
|
||||
## mini.surround
|
||||
|
||||
- FEATURE: Implement `config.search_method`.
|
||||
- FEATURE: Implement custom surroundings via `config.custom_surroundings`.
|
||||
- FEATURE: Implement `MiniSurround.user_input()`.
|
||||
- BREAKING: Deprecate `config.funname_pattern` option in favor of manually modifying `f` surrounding.
|
||||
- BREAKING: Always move cursor to the right of left surrounding in `add()`, `delete()`, and `replace()` (instead of moving only if it was on the same line as left surrounding).
|
||||
- Update process of getting user input: allow `<C-c>` to cancel and make empty string a valid input.
|
||||
|
||||
## mini.tabline
|
||||
|
||||
- FEATURE: Implement `config.tabpage_section`.
|
||||
- BREAKING: Show listed buffers also in case of multiple tabpages (instead of using builtin behavior).
|
||||
- Show quickfix/loclist buffers with special `*quickfix*` label.
|
||||
|
||||
|
||||
# Version 0.3.0
|
||||
|
||||
- Update all modules to have annotations formatted for 'mini.doc'.
|
||||
|
||||
## mini.cursorword
|
||||
|
||||
- Current word under cursor now can be highlighted differently.
|
||||
|
||||
## mini.doc
|
||||
|
||||
Introduction of new module.
|
||||
|
||||
## mini.indentscope
|
||||
|
||||
Introduction of new module.
|
||||
|
||||
## mini.starter
|
||||
|
||||
- Implement `MiniStarter.set_query()` and make `<Esc>` mapping for resetting query.
|
||||
|
||||
|
||||
# Version 0.2.0
|
||||
|
||||
## mini.base16
|
||||
|
||||
- Use new `Diagnostic*` highlight groups in Neovim 0.6.0.
|
||||
|
||||
## mini.comment
|
||||
|
||||
- Respect tab indentation (#20).
|
||||
|
||||
## mini.jump
|
||||
|
||||
Introduction of new module.
|
||||
|
||||
## mini.pairs
|
||||
|
||||
- Implement pair registration with custom mapping functions. More detailed:
|
||||
- Implement `MiniPairs.map()`, `MiniPairs.map_buf()`, `MiniPairs.unmap()`, `MiniPairs.unmap_buf()` to (un)make mappings for pairs which automatically register them for `<BS>` and `<CR>`. Note, that this has a minor break of previous behavior: now `MiniPairs.bs()` and `MiniPairs.cr()` don't have any input argument. But default behavior didn't change.
|
||||
- Allow setting global pair mappings inside `config` of `MiniPairs.setup()`.
|
||||
|
||||
## mini.sessions
|
||||
|
||||
Introduction of new module.
|
||||
|
||||
## mini.starter
|
||||
|
||||
Introduction of new module.
|
||||
|
||||
## mini.statusline
|
||||
|
||||
- Implement new section `MiniStatusline.section_searchcount()`.
|
||||
- Update `section_diagnostics` to use `vim.diagnostic` in Neovim 0.6.0.
|
||||
|
||||
|
||||
# Version 0.1.0
|
||||
|
||||
- Initial stable version.
|
|
@ -0,0 +1,132 @@
|
|||
# Contributor Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||
identity and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the overall
|
||||
community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or advances of
|
||||
any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email address,
|
||||
without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
`evgeni <dot> chasnovski |at| gmail >dot< com` .
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series of
|
||||
actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or permanent
|
||||
ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||
community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.1, available at
|
||||
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
||||
[https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
|
@ -0,0 +1,135 @@
|
|||
# Contributing
|
||||
|
||||
Thank you for your willingness to contribute to 'mini.nvim'. It means a lot!
|
||||
|
||||
You can make contributions in the following ways:
|
||||
|
||||
- **Mention it** somehow to help reach broader audience. This helps a lot.
|
||||
- **Create a GitHub issue**. It can be one of two types:
|
||||
- **Bug report**. Describe your actions in a reproducible way along with their effect and what you expected should happen. Before making one, please make your best efforts to make sure that it is not an intended behavior (not described in documentation as such).
|
||||
- **Feature request**. A concise and justified description of what one or several modules should be able to do. Before making one, please make your best efforts to make sure that it is not a feature that won't get implemented (these should be described in documentation; for example: block comments in 'mini.comment').
|
||||
- **Create a pull request (PR)**. It can be one of two types:
|
||||
- **Code related**. For example, fix a bug or implement a feature. **Before even starting one, please make sure that it is aligned with project vision and goals**. The best way to do it is to receive a positive feedback from maintainer on your initiative in one of the GitHub issues (existing one or created by you otherwise). Please, make sure to regenerate latest help file and that all tests are passed (see later sections).
|
||||
- **Add plugin integration to 'mini.base16' color scheme**. See [](#implementation-notes) for checklist.
|
||||
- **Documentation related**. For example, fix typo/wording in 'README.md', code comments or annotations (which are used to generate Neovim documentation; see later section). Feel free to make these without creating a GitHub issue.
|
||||
- **Add explicit support to colorschemes**. Any 'mini.nvim' module supports any colorscheme right out of the box. This is done by making most highlight groups be linked to a semantically similar builtin highlight group. Other groups are hard-coded based on personal preference. However, these choices might be out of tune with a particular colorscheme. Updating as many colorschemes as possible to have explicit 'mini.nvim' support is highly appreciated. For your convenience, there is a list of all highlight groups in later section of this file.
|
||||
- **Participate in [discussions](https://github.com/echasnovski/mini.nvim/discussions)**.
|
||||
|
||||
All well-intentioned, polite, and respectful contributions are always welcome! Thanks for reading this!
|
||||
|
||||
## Generating help file
|
||||
|
||||
If your contribution updates annotations used to generate help file, please regenerate it. You can make this with one of the following (assuming current directory being project root):
|
||||
|
||||
- From command line execute `make documentation`.
|
||||
- Inside Neovim instance run `:luafile scripts/minidoc.lua`.
|
||||
|
||||
## Running tests
|
||||
|
||||
If your contribution updates code and you use Linux (not Windows or MacOS), please make sure that it doesn't break existing tests. If it adds new functionality or fixes a recognized bug, add new test case(s). There are two ways of running tests:
|
||||
|
||||
- From command line execute `make test` to run all tests or `FILE=<name of file> make test_file` to run tests only from file `<name of file>`.
|
||||
- Inside Neovim instance execute `:lua require('mini.test').setup(); MiniTest.run()` to run all tests or `:lua require('mini.test').setup(); MiniTest.run_file()` to run tests only from current buffer.
|
||||
|
||||
This plugin uses 'mini.test' to manage its tests. For more hands-on introduction, see [TESTING.md](TESTING.md).
|
||||
|
||||
If you have Windows or MacOS and want to contribute code related change, make you best effort to not break existing behavior. It will later be tested automatically after making Pull Request. The reason for this distinction is that tests are not well designed to be run on those operating systems.
|
||||
|
||||
## Formatting
|
||||
|
||||
This project uses [StyLua](https://github.com/JohnnyMorganz/StyLua) version 0.14.0 for formatting Lua code. Before making changes to code, please:
|
||||
|
||||
- [Install StyLua](https://github.com/JohnnyMorganz/StyLua#installation). NOTE: use `v0.14.0`.
|
||||
- Format with it. Currently there are two ways to do this:
|
||||
- Manually run `stylua .` from the root directory of this project.
|
||||
- [Install pre-commit](https://pre-commit.com/#install) and enable it with `pre-commit install` (from the root directory). This will auto-format relevant code before making commits.
|
||||
|
||||
## Implementation notes
|
||||
|
||||
- Use module's `H.get_config()` helper to get its `config`. This way allows using buffer local configuration.
|
||||
- Checklist for adding new config setting:
|
||||
- Add code which uses new setting.
|
||||
- Update module's `H.setup_config()` with type check of new setting.
|
||||
- Add default value to `Mini*.config` definition.
|
||||
- Regenerate help file.
|
||||
- Update module's README in 'readmes' directory.
|
||||
- Update 'CHANGELOG.md'. In module's section of current version add line starting with `- FEATURE: Implement ...`.
|
||||
- Checklist for adding new plugin integration:
|
||||
- Update file 'lua/mini/base16.lua' in a way similar to other already added plugins:
|
||||
- Add definitions for highlight groups.
|
||||
- Add plugin entry in a list of supported plugins in help annotations.
|
||||
- Regenerate documentation (see [](#generating-help-file)).
|
||||
- Checklist for adding new module:
|
||||
- Add Lua source code in 'lua' directory.
|
||||
- Add tests in 'tests' directory. Use 'tests/dir-xxx' name for module-specific non-test helpers.
|
||||
- Update 'lua/init.lua' to mention new module: both in initial table of contents and list of modules.
|
||||
- Update 'scripts/minidoc.lua' to generate separate help file.
|
||||
- Generate help files.
|
||||
- Add README to 'readmes' directory.
|
||||
- Update main README to mention new module: both in table of contents and subsection.
|
||||
- Update 'CHANGELOG.md' to mention introduction of new module.
|
||||
|
||||
## List of highlight groups
|
||||
|
||||
Here is a list of all highlight groups defined inside 'mini.nvim' modules. See documentation in 'doc' directory to find out what they are used for.
|
||||
|
||||
- 'mini.completion':
|
||||
- `MiniCompletionActiveParameter`
|
||||
|
||||
- 'mini.cursorword':
|
||||
- `MiniCursorword`
|
||||
- `MiniCursorwordCurrent`
|
||||
|
||||
- 'mini.indentscope':
|
||||
- `MiniIndentscopeSymbol`
|
||||
- `MiniIndentscopePrefix`
|
||||
|
||||
- 'mini.jump':
|
||||
- `MiniJump`
|
||||
|
||||
- 'mini.jump2d':
|
||||
- `MiniJump2dSpot`
|
||||
|
||||
- 'mini.starter':
|
||||
- `MiniStarterCurrent`
|
||||
- `MiniStarterFooter`
|
||||
- `MiniStarterHeader`
|
||||
- `MiniStarterInactive`
|
||||
- `MiniStarterItem`
|
||||
- `MiniStarterItemBullet`
|
||||
- `MiniStarterItemPrefix`
|
||||
- `MiniStarterSection`
|
||||
- `MiniStarterQuery`
|
||||
|
||||
- 'mini.statusline':
|
||||
- `MiniStatuslineDevinfo`
|
||||
- `MiniStatuslineFileinfo`
|
||||
- `MiniStatuslineFilename`
|
||||
- `MiniStatuslineInactive`
|
||||
- `MiniStatuslineModeCommand`
|
||||
- `MiniStatuslineModeInsert`
|
||||
- `MiniStatuslineModeNormal`
|
||||
- `MiniStatuslineModeOther`
|
||||
- `MiniStatuslineModeReplace`
|
||||
- `MiniStatuslineModeVisual`
|
||||
|
||||
- 'mini.surround':
|
||||
- `MiniSurround`
|
||||
|
||||
- 'mini.tabline':
|
||||
- `MiniTablineCurrent`
|
||||
- `MiniTablineFill`
|
||||
- `MiniTablineHidden`
|
||||
- `MiniTablineModifiedCurrent`
|
||||
- `MiniTablineModifiedHidden`
|
||||
- `MiniTablineModifiedVisible`
|
||||
- `MiniTablineTabpagesection`
|
||||
- `MiniTablineVisible`
|
||||
|
||||
- 'mini.test':
|
||||
- `MiniTestEmphasis`
|
||||
- `MiniTestFail`
|
||||
- `MiniTestPass`
|
||||
|
||||
- 'mini.trailspace':
|
||||
- `MiniTrailspace`
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 Evgeni Chasnovski
|
||||
|
||||
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.
|
|
@ -0,0 +1,22 @@
|
|||
GROUP_DEPTH ?= 1
|
||||
NVIM_EXEC ?= nvim
|
||||
|
||||
all: test documentation
|
||||
|
||||
test:
|
||||
$(NVIM_EXEC) --version | head -n 1 && echo ''
|
||||
$(NVIM_EXEC) --headless --noplugin -u ./scripts/minimal_init.lua \
|
||||
-c "lua require('mini.test').setup()" \
|
||||
-c "lua MiniTest.run({ execute = { reporter = MiniTest.gen_reporter.stdout({ group_depth = $(GROUP_DEPTH) }) } })"
|
||||
|
||||
test_file:
|
||||
$(NVIM_EXEC) --version | head -n 1 && echo ''
|
||||
$(NVIM_EXEC) --headless --noplugin -u ./scripts/minimal_init.lua \
|
||||
-c "lua require('mini.test').setup()" \
|
||||
-c "lua MiniTest.run_file('$(FILE)', { execute = { reporter = MiniTest.gen_reporter.stdout({ group_depth = $(GROUP_DEPTH) }) } })"
|
||||
|
||||
documentation:
|
||||
$(NVIM_EXEC) --headless --noplugin -u ./scripts/minimal_init.lua -c "lua require('mini.doc').generate()" -c "qa!"
|
||||
|
||||
basic_setup:
|
||||
$(NVIM_EXEC) --headless --noplugin -u ./scripts/basic-setup_init.lua
|
|
@ -0,0 +1,324 @@
|
|||
<img src="logo.png" width="800em"/> <br>
|
||||
|
||||
<!-- badges: start -->
|
||||
[![GitHub license](https://badgen.net/github/license/echasnovski/mini.nvim)](https://github.com/echasnovski/mini.nvim/blob/main/LICENSE)
|
||||
[![GitHub tag](https://badgen.net/github/tag/echasnovski/mini.nvim)](https://github.com/echasnovski/mini.nvim/tags/)
|
||||
[![Current version](https://badgen.net/badge/Current%20version/development/cyan)](https://github.com/echasnovski/mini.nvim/blob/main/CHANGELOG.md)
|
||||
<!-- badges: end -->
|
||||
|
||||
Library of 20+ independent Lua modules improving overall [Neovim](https://github.com/neovim/neovim) (version 0.5 and higher) experience with minimal effort. They all share same configuration approaches and general design principles.
|
||||
|
||||
Think about this project as "Swiss Army knife" among Neovim plugins: it has many different independent tools (modules) suitable for most common tasks. Each module can be used separately without any startup and usage overhead.
|
||||
|
||||
If you want to help this project grow but don't know where to start, check out [contributing guides](CONTRIBUTING.md) or leave a Github star.
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Modules](#modules)
|
||||
- [General principles](#general-principles)
|
||||
- [Plugin colorschemes](#plugin-colorschemes)
|
||||
- [Planned modules](#planned-modules)
|
||||
|
||||
## Installation
|
||||
|
||||
There are two branches to install from:
|
||||
|
||||
- `main` (default, **recommended**) will have latest development version of plugin. All changes since last stable release should be perceived as being in beta testing phase (meaning they already passed alpha-testing and are moderately settled).
|
||||
- `stable` will be updated only upon releases with code tested during public beta-testing phase in `main` branch.
|
||||
|
||||
Here are code snippets for some common installation methods:
|
||||
|
||||
- Using [wbthomason/packer.nvim](https://github.com/wbthomason/packer.nvim):
|
||||
|
||||
| Branch | Code snippet |
|
||||
|--------|------------------------------------------------------|
|
||||
| Main | `use 'echasnovski/mini.nvim'` |
|
||||
| Stable | `use { 'echasnovski/mini.nvim', branch = 'stable' }` |
|
||||
|
||||
- Using [junegunn/vim-plug](https://github.com/junegunn/vim-plug):
|
||||
|
||||
| Branch | Code snippet |
|
||||
|--------|--------------------------------------------------------|
|
||||
| Main | `Plug 'echasnovski/mini.nvim'` |
|
||||
| Stable | `Plug 'echasnovski/mini.nvim', { 'branch': 'stable' }` |
|
||||
|
||||
**Important**: don't forget to call module's `setup()` (if required) to enable its functionality.
|
||||
|
||||
**Note**: if you are on Windows, there might be problems with too long file paths (like `error: unable to create file <some file name>: Filename too long`). Try doing one of the following:
|
||||
- Enable corresponding git global config value: `git config --system core.longpaths true`. Then try to reinstall.
|
||||
- Install plugin in other place with shorter path.
|
||||
|
||||
## Modules
|
||||
|
||||
| Module | Description | Overview | Details |
|
||||
|------------------|---------------------------------------------|---------------------------------------|---------------------------------------|
|
||||
| mini.ai | Extend and create `a`/`i` textobjects | [README](readmes/mini-ai.md) | [Help file](doc/mini-ai.txt) |
|
||||
| mini.align | Align text interactively | [README](readmes/mini-align.md) | [Help file](doc/mini-align.txt) |
|
||||
| mini.base16 | Base16 colorscheme creation | [README](readmes/mini-base16.md) | [Help file](doc/mini-base16.txt) |
|
||||
| mini.bufremove | Remove buffers | [README](readmes/mini-bufremove.md) | [Help file](doc/mini-bufremove.txt) |
|
||||
| mini.comment | Comment | [README](readmes/mini-comment.md) | [Help file](doc/mini-comment.txt) |
|
||||
| mini.completion | Completion and signature help | [README](readmes/mini-completion.md) | [Help file](doc/mini-completion.txt) |
|
||||
| mini.cursorword | Autohighlight word under cursor | [README](readmes/mini-cursorword.md) | [Help file](doc/mini-cursorword.txt) |
|
||||
| mini.doc | Generate Neovim help files | [README](readmes/mini-doc.md) | [Help file](doc/mini-doc.txt) |
|
||||
| mini.fuzzy | Fuzzy matching | [README](readmes/mini-fuzzy.md) | [Help file](doc/mini-fuzzy.txt) |
|
||||
| mini.indentscope | Visualize and operate on indent scope | [README](readmes/mini-indentscope.md) | [Help file](doc/mini-indentscope.txt) |
|
||||
| mini.jump | Jump forward/backward to a single character | [README](readmes/mini-jump.md) | [Help file](doc/mini-jump.txt) |
|
||||
| mini.jump2d | Jump within visible lines | [README](readmes/mini-jump2d.md) | [Help file](doc/mini-jump2d.txt) |
|
||||
| mini.misc | Miscellaneous functions | [README](readmes/mini-misc.md) | [Help file](doc/mini-misc.txt) |
|
||||
| mini.pairs | Autopairs | [README](readmes/mini-pairs.md) | [Help file](doc/mini-pairs.txt) |
|
||||
| mini.sessions | Session management | [README](readmes/mini-sessions.md) | [Help file](doc/mini-sessions.txt) |
|
||||
| mini.starter | Start screen | [README](readmes/mini-starter.md) | [Help file](doc/mini-starter.txt) |
|
||||
| mini.statusline | Statusline | [README](readmes/mini-statusline.md) | [Help file](doc/mini-statusline.txt) |
|
||||
| mini.surround | Surround actions | [README](readmes/mini-surround.md) | [Help file](doc/mini-surround.txt) |
|
||||
| mini.tabline | Tabline | [README](readmes/mini-tabline.md) | [Help file](doc/mini-tabline.txt) |
|
||||
| mini.test | Test Neovim plugins | [README](readmes/mini-test.md) | [Help file](doc/mini-test.txt) |
|
||||
| mini.trailspace | Trailspace (highlight and remove) | [README](readmes/mini-trailspace.md) | [Help file](doc/mini-trailspace.txt) |
|
||||
|
||||
<a name='mini.ai'></a>
|
||||
### mini.ai
|
||||
|
||||
Extend and create `a`/`i` textobjects (like in `di(` or `va"`).
|
||||
|
||||
- It enhances some builtin textobjects (like `a(`, `a)`, `a'`, and more), creates new ones (like `a*`, `a<Space>`, `af`, `a?`, and more), and allows user to create their own (like based on treesitter, and more).
|
||||
- Supports dot-repeat, `v:count`, different search methods, consecutive application, and customization via Lua patterns or functions.
|
||||
- Has builtins for brackets, quotes, function call, argument, tag, user prompt, and any punctuation/digit/whitespace character.
|
||||
|
||||
For video demo and quick overview see its [README](readmes/mini-ai.md). For more details see its [help file](doc/mini-ai.txt).
|
||||
|
||||
---
|
||||
|
||||
<a name='mini.align'></a>
|
||||
### mini.align
|
||||
|
||||
Align text interactively (with or without instant preview).
|
||||
|
||||
For video demo and quick overview see its [README](readmes/mini-align.md). For more details see its [help file](doc/mini-align.txt).
|
||||
|
||||
---
|
||||
|
||||
<a name='mini.base16'></a>
|
||||
### mini.base16
|
||||
|
||||
Fast implementation of [chriskempson/base16](https://github.com/chriskempson/base16) theme for manually supplied palette.
|
||||
|
||||
- Supports 30+ plugin integrations.
|
||||
- Has unique palette generator which needs only background and foreground colors.
|
||||
- Comes with several hand-picked color schemes.
|
||||
|
||||
For video demo and quick overview see its [README](readmes/mini-base16.md). For more details see its [help file](doc/mini-base16.txt).
|
||||
|
||||
---
|
||||
|
||||
<a name='mini.bufremove'></a>
|
||||
### mini.bufremove
|
||||
|
||||
Buffer removing (unshow, delete, wipeout), which saves window layout.
|
||||
|
||||
For video demo and quick overview see its [README](readmes/mini-bufremove.md). For more details see its [help file](doc/mini-bufremove.txt).
|
||||
|
||||
---
|
||||
|
||||
<a name='mini.comment'></a>
|
||||
### mini.comment
|
||||
|
||||
Fast and familiar per-line commenting.
|
||||
|
||||
For video demo and quick overview see its [README](readmes/mini-comment.md). For more details see its [help file](doc/mini-comment.txt).
|
||||
|
||||
---
|
||||
|
||||
<a name='mini.completion'></a>
|
||||
### mini.completion
|
||||
|
||||
Autocompletion and signature help plugin.
|
||||
|
||||
- Async (with customizable 'debounce' delay) 'two-stage chain completion': first builtin LSP, then configurable fallback.
|
||||
- Has functionality for completion item info and function signature (both in floating window appearing after customizable delay).
|
||||
|
||||
For video demo and quick overview see its [README](readmes/mini-completion.md). For more details see its [help file](doc/mini-completion.txt).
|
||||
|
||||
---
|
||||
|
||||
<a name='mini.cursorword'></a>
|
||||
### mini.cursorword
|
||||
|
||||
Automatic highlighting of word under cursor (displayed after customizable delay).
|
||||
|
||||
For video demo and quick overview see its [README](readmes/mini-cursorword.md). For more details see its [help file](doc/mini-cursorword.txt).
|
||||
|
||||
---
|
||||
|
||||
<a name='mini.doc'></a>
|
||||
### mini.doc
|
||||
|
||||
Generation of help files from EmmyLua-like annotations. Allows flexible customization of output via hook functions. Used for documenting this plugin.
|
||||
|
||||
For video demo and quick overview see its [README](readmes/mini-doc.md). For more details see its [help file](doc/mini-doc.txt).
|
||||
|
||||
---
|
||||
|
||||
<a name='mini.fuzzy'></a>
|
||||
### mini.fuzzy
|
||||
|
||||
Minimal and fast fuzzy matching.
|
||||
|
||||
For video demo and quick overview see its [README](readmes/mini-fuzzy.md). For more details see its [help file](doc/mini-fuzzy.txt).
|
||||
|
||||
---
|
||||
|
||||
<a name='mini.indentscope'></a>
|
||||
### mini.indentscope
|
||||
|
||||
Visualize and operate on indent scope. Supports customization of debounce delay, animation style, and different granularity of options for scope computing algorithm.
|
||||
|
||||
- Customizable debounce delay, animation style, and scope computation options.
|
||||
- Implements scope-related motions and textobjects.
|
||||
|
||||
For video demo and quick overview see its [README](readmes/mini-indentscope.md). For more details see its [help file](doc/mini-indentscope.txt).
|
||||
|
||||
---
|
||||
|
||||
<a name='mini.jump'></a>
|
||||
### mini.jump
|
||||
|
||||
Smarter forward/backward jumping to a single character.
|
||||
|
||||
For video demo and quick overview see its [README](readmes/mini-jump.md). For more details see its [help file](doc/mini-jump.txt).
|
||||
|
||||
---
|
||||
|
||||
<a name='mini.jump2d'></a>
|
||||
### mini.jump2d
|
||||
|
||||
Jump within visible lines via iterative label filtering.
|
||||
|
||||
For video demo and quick overview see its [README](readmes/mini-jump2d.md). For more details see its [help file](doc/mini-jump2d.txt).
|
||||
|
||||
---
|
||||
|
||||
<a name='mini.misc'></a>
|
||||
### mini.misc
|
||||
|
||||
Miscellaneous useful functions.
|
||||
|
||||
For video demo and quick overview see its [README](readmes/mini-misc.md). For more details see its [help file](doc/mini-misc.txt).
|
||||
|
||||
---
|
||||
|
||||
<a name='mini.pairs'></a>
|
||||
### mini.pairs
|
||||
|
||||
Minimal and fast autopairs.
|
||||
|
||||
For video demo and quick overview see its [README](readmes/mini-pairs.md). For more details see its [help file](doc/mini-pairs.txt).
|
||||
|
||||
---
|
||||
|
||||
<a name='mini.sessions'></a>
|
||||
### mini.sessions
|
||||
|
||||
Session management (read, write, delete).
|
||||
|
||||
For video demo and quick overview see its [README](readmes/mini-sessions.md). For more details see its [help file](doc/mini-sessions.txt).
|
||||
|
||||
---
|
||||
|
||||
<a name='mini.starter'></a>
|
||||
### mini.starter
|
||||
|
||||
Fast and flexible start screen
|
||||
|
||||
For video demo and quick overview see its [README](readmes/mini-starter.md). For more details see its [help file](doc/mini-starter.txt).
|
||||
|
||||
---
|
||||
|
||||
<a name='mini.statusline'></a>
|
||||
### mini.statusline
|
||||
|
||||
Minimal and fast statusline module with opinionated default look.
|
||||
|
||||
For video demo and quick overview see its [README](readmes/mini-statusline.md). For more details see its [help file](doc/mini-statusline.txt).
|
||||
|
||||
---
|
||||
|
||||
<a name='mini.surround'></a>
|
||||
### mini.surround
|
||||
|
||||
Fast and feature-rich surround plugin
|
||||
|
||||
- Add, delete, replace, find, highlight surrounding (like pair of parenthesis, quotes, etc.).
|
||||
- Supports dot-repeat, `v:count`, different search methods, "last"/"next" extended mappings, customization via Lua patterns or functions, and more.
|
||||
- Has builtins for brackets, function call, tag, user prompt, and any alphanumeric/punctuation/whitespace character.
|
||||
- Has maintained configuration of setup similar to 'tpope/vim-surround'.
|
||||
|
||||
For video demo and quick overview see its [README](readmes/mini-surround.md). For more details see its [help file](doc/mini-surround.txt).
|
||||
|
||||
---
|
||||
|
||||
<a name='mini.tabline'></a>
|
||||
### mini.tabline
|
||||
|
||||
Minimal and fast tabline showing listed buffers
|
||||
|
||||
For video demo and quick overview see its [README](readmes/mini-tabline.md). For more details see its [help file](doc/mini-tabline.txt).
|
||||
|
||||
---
|
||||
|
||||
<a name='mini.test'></a>
|
||||
### mini.test
|
||||
|
||||
Write and use extensive Neovim plugin tests
|
||||
|
||||
- Supports hierarchical tests, hooks, parametrization, filtering (like from current file or cursor position), screen tests, "busted-style" emulation, customizable reporters, and more.
|
||||
- Designed to be used with provided wrapper for managing child Neovim processes.
|
||||
|
||||
For video demo and quick overview see its [README](readmes/mini-test.md). For more details see its [help file](doc/mini-test.txt).
|
||||
|
||||
---
|
||||
|
||||
<a name='mini.trailspace'></a>
|
||||
### mini.trailspace
|
||||
|
||||
Work with trailing whitespace
|
||||
|
||||
For video demo and quick overview see its [README](readmes/mini-trailspace.md). For more details see its [help file](doc/mini-trailspace.txt).
|
||||
|
||||
---
|
||||
|
||||
## General principles
|
||||
|
||||
- **Design**. Each module is designed to solve a particular problem targeting balance between feature-richness (handling as many edge-cases as possible) and simplicity of implementation/support. Granted, not all of them ended up with the same balance, but it is the goal nevertheless.
|
||||
- **Independence**. Modules are independent of each other and can be run without external dependencies. Although some of them may need dependencies for full experience.
|
||||
- **Structure**. Each module is a submodule for a placeholder "mini" module. So, for example, "surround" module should be referred to as "mini.surround". As later will be explained, this plugin can also be referred to as "MiniSurround".
|
||||
- **Setup**:
|
||||
- Each module (if needed) should be setup separately with `require(<name of module>).setup({})` (possibly replace {} with your config table or omit to use defaults). You can supply only values which differ from defaults, which will be used for the rest ones.
|
||||
- Call to module's `setup()` always creates a global Lua object with coherent camel-case name: `require('mini.surround').setup()` creates `_G.MiniSurround`. This allows for a simpler usage of plugin functionality: instead of `require('mini.surround')` use `MiniSurround` (or manually `:lua MiniSurround.*` in command line); available from `v:lua` like `v:lua.MiniSurround`. Considering this, "module" and "Lua object" names can be used interchangeably: 'mini.surround' and 'MiniSurround' will mean the same thing.
|
||||
- Each supplied `config` table is stored in `config` field of global object. Like `MiniSurround.config`.
|
||||
- Values of `config`, which affect runtime activity, can be changed on the fly to have effect. For example, `MiniSurround.config.n_lines` can be changed during runtime; but changing `MiniSurround.config.mappings` won't have any effect (as mappings are created once during `setup()`).
|
||||
- **Buffer local configuration**. Each module can be additionally configured to use certain runtime config settings locally to buffer. See `mini.nvim-buffer-local-config` section in help file for more information.
|
||||
- **Disabling**. Each module's core functionality can be disabled globally or locally to buffer by creating appropriate global or buffer-scoped variables equal to `v:true`. See `mini.nvim-disabling-recipes` section in help file for common recipes.
|
||||
- **Highlight groups**. Appearance of module's output is controlled by certain highlight group (see `:h highlight-groups`). To customize them, use `highlight` command. **Note**: currently not many Neovim themes support this plugin's highlight groups; fixing this situation is highly appreciated. To see a more calibrated look, use MiniBase16 or plugin's colorscheme `minischeme`.
|
||||
- **Stability**. Each module upon release is considered to be relatively stable: both in terms of setup and functionality. Any non-bugfix backward-incompatible change will be released gradually as much as possible.
|
||||
|
||||
## Plugin colorschemes
|
||||
|
||||
This plugin comes with several color schemes (all of them are made with 'mini.base16' and have both dark and light variants):
|
||||
|
||||
- `minischeme` - blue and yellow main colors with high contrast and saturation palette. All examples use this colorscheme.
|
||||
- `minicyan` - cyan and grey main colors with moderate contrast and saturation palette.
|
||||
|
||||
Activate them as regular `colorscheme` (for example, `:colorscheme minicyan`). You can see how they look in [demo of 'mini.base16'](readmes/mini-base16.md#demo).
|
||||
|
||||
## Planned modules
|
||||
|
||||
This is the list of modules I currently intend to implement eventually (as my free time and dedication will allow), in alphabetical order:
|
||||
|
||||
- 'mini.align' - align text with respect to some separators. Something like [junegunn/vim-easy-align](https://github.com/junegunn/vim-easy-align).
|
||||
- 'mini.basics' - configurable collection of options and mappings sets intended mostly for quick "up and running" Neovim config. Something like a combination of [tpope/vim-sensible](https://github.com/tpope/vim-sensible) and [tpope/vim-unimpaired](https://github.com/tpope/vim-unimpaired).
|
||||
- 'mini.clue' - "show as you type" floating window with customizable information. Something like [folke/which-key.nvim](https://github.com/folke/which-key.nvim) and [anuvyklack/hydra.nvim](https://github.com/anuvyklack/hydra.nvim)
|
||||
- 'mini.filetree' - file tree viewer. Simplified version of [kyazdani42/nvim-tree](https://github.com/kyazdani42/nvim-tree.lua).
|
||||
- 'mini.root' - automatically change current working directory. Something like [airblade/vim-rooter](https://github.com/airblade/vim-rooter).
|
||||
- 'mini.snippets' - work with snippets. Something like [L3MON4D3/LuaSnip](https://github.com/L3MON4D3/LuaSnip) but only with more straightforward functionality.
|
||||
- 'mini.swap' - exchange two regions of text. Something like [tommcdo/vim-exchange](https://github.com/tommcdo/vim-exchange).
|
||||
- 'mini.terminals' - coherently manage terminal windows and send text from buffers to terminal windows. Something like [kassio/neoterm](https://github.com/kassio/neoterm).
|
|
@ -0,0 +1,964 @@
|
|||
# How to test with 'mini.test'
|
||||
|
||||
Writing tests for Neovim Lua plugin is hard. Writing good tests for Neovim Lua plugin is even harder. The 'mini.test' module is designed to make it reasonably easier while still allowing lots of flexibility. It deliberately favors a more verbose and program-like style of writing tests, opposite to "human readable, DSL like" approach of [nvim-lua/plenary.nvim](https://github.com/nvim-lua/plenary.nvim) ("busted-style testing" from [Olivine-Labs/busted](https://github.com/Olivine-Labs/busted)). Although the latter is also possible.
|
||||
|
||||
This file is intended as a hands-on introduction to 'mini.test' with examples. For more details, see 'mini.test' section of [help file](doc/mini.txt) and tests of this plugin's modules.
|
||||
|
||||
General approach of writing test files:
|
||||
|
||||
- Organize tests in separate Lua files.
|
||||
- Each file should be associated with a test set table (output of `MiniTest.new_set()`). Recommended approach is to create it manually in each test file and then return it.
|
||||
- Each test action should be defined in separate function assign to an entry of test set.
|
||||
- It is strongly encouraged to use custom Neovim processes to do actual testing inside test action. See [Using child process](#using-child-process).
|
||||
|
||||
**NOTES**:
|
||||
|
||||
- All commands are assumed to be executed with current working directory being a root of your Neovim plugin project. That is both for shell and Neovim commands.
|
||||
- All paths are assumed to be relative to current working directory.
|
||||
|
||||
## Example plugin
|
||||
|
||||
In this file we will be testing 'hello_lines' plugin (once some basic concepts are introduced). It will have functionality to add prefix 'Hello ' to lines. It will have single file 'lua/hello_lines/init.lua' with the following content:
|
||||
|
||||
<details><summary>'hello_lines/init.lua'</summary>
|
||||
|
||||
```lua
|
||||
local M = {}
|
||||
|
||||
--- Prepend 'Hello ' to every element
|
||||
---@param lines table Array. Default: { 'world' }.
|
||||
---@return table Array of strings.
|
||||
M.compute = function(lines)
|
||||
lines = lines or { 'world' }
|
||||
return vim.tbl_map(function(x) return 'Hello ' .. tostring(x) end, lines)
|
||||
end
|
||||
|
||||
local ns_id = vim.api.nvim_create_namespace('hello_lines')
|
||||
|
||||
--- Set lines with highlighted 'Hello ' prefix
|
||||
---@param buf_id number Buffer handle where lines should be set. Default: 0.
|
||||
---@param lines table Array. Default: { 'world' }.
|
||||
M.set_lines = function(buf_id, lines)
|
||||
buf_id = buf_id or 0
|
||||
lines = lines or { 'world' }
|
||||
vim.api.nvim_buf_set_lines(buf_id or 0, 0, -1, true, M.compute(lines))
|
||||
for i = 1, #lines do
|
||||
vim.highlight.range(buf_id, ns_id, 'Special', { i - 1, 0 }, { i - 1, 5 }, {})
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Quick demo
|
||||
|
||||
Here is a quick demo of how tests with 'mini.test' look like:
|
||||
|
||||
<details><summary>'tests/test_hello_lines.lua'</summary>
|
||||
|
||||
```lua
|
||||
-- Define helper aliases
|
||||
local new_set = MiniTest.new_set
|
||||
local expect, eq = MiniTest.expect, MiniTest.expect.equality
|
||||
|
||||
-- Create (but not start) child Neovim object
|
||||
local child = MiniTest.new_child_neovim()
|
||||
|
||||
-- Define main test set of this file
|
||||
local T = new_set({
|
||||
-- Register hooks
|
||||
hooks = {
|
||||
-- This will be executed before every (even nested) case
|
||||
pre_case = function()
|
||||
-- Restart child process with custom 'init.lua' script
|
||||
child.restart({ '-u', 'scripts/minimal_init.lua' })
|
||||
-- Load tested plugin
|
||||
child.lua([[M = require('hello_lines')]])
|
||||
end,
|
||||
-- This will be executed one after all tests from this set are finished
|
||||
post_once = child.stop,
|
||||
},
|
||||
})
|
||||
|
||||
-- Test set fields define nested structure
|
||||
T['compute()'] = new_set()
|
||||
|
||||
-- Define test action as callable field of test set.
|
||||
-- If it produces error - test fails.
|
||||
T['compute()']['works'] = function()
|
||||
-- Execute Lua code inside child process, get its result and compare with
|
||||
-- expected result
|
||||
eq(child.lua_get([[M.compute({'a', 'b'})]]), { 'Hello a', 'Hello b' })
|
||||
end
|
||||
|
||||
T['compute()']['uses correct defaults'] = function()
|
||||
eq(child.lua_get([[M.compute()]]), { 'Hello world' })
|
||||
end
|
||||
|
||||
-- Make parametrized tests. This will create three copies of each case
|
||||
T['set_lines()'] = new_set({ parametrize = { {}, { 0, { 'a' } }, { 0, { 1, 2, 3 } } } })
|
||||
|
||||
-- Use arguments from test parametrization
|
||||
T['set_lines()']['works'] = function(buf_id, lines)
|
||||
-- Directly modify some options to make better test
|
||||
child.o.lines, child.o.columns = 10, 20
|
||||
child.bo.readonly = false
|
||||
|
||||
-- Execute Lua code without returning value
|
||||
child.lua('M.set_lines(...)', { buf_id, lines })
|
||||
|
||||
-- Test screen state. On first run it will automatically create reference
|
||||
-- screenshots with text and look information in predefined location. On
|
||||
-- later runs it will compare current screenshot with reference. Will throw
|
||||
-- informative error with helpful information if they don't match exactly.
|
||||
expect.reference_screenshot(child.get_screenshot())
|
||||
end
|
||||
|
||||
-- Return test set which will be collected and execute inside `MiniTest.run()`
|
||||
return T
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## File organization
|
||||
|
||||
It might be a bit overwhelming. It actually is for most of the people. However, it should be done once and then you rarely need to touch it.
|
||||
|
||||
Overview of full file structure used in for testing 'hello_lines' plugin:
|
||||
|
||||
```
|
||||
.
|
||||
├── deps
|
||||
│ └── mini.nvim # Mandatory
|
||||
├── lua
|
||||
│ └── hello_lines
|
||||
│ └── init.lua # Mandatory
|
||||
├── Makefile # Recommended
|
||||
├── scripts
|
||||
│ ├── minimal_init.lua # Mandatory
|
||||
│ └── minitest.lua # Recommended
|
||||
└── tests
|
||||
└── test_hello_lines.lua # Mandatory
|
||||
```
|
||||
|
||||
To write tests, you'll need these files:
|
||||
|
||||
Mandatory:
|
||||
|
||||
- **Your Lua plugin in 'lua' directory**. Here we will be testing 'hello_lines' plugin.
|
||||
- **Test files**. By default they should be Lua files located in 'tests/' directory and named with 'test_' prefix. For example, we will write everything in 'test_hello_lines.lua'. It is usually a good idea to follow this template (will be assumed for the rest of this file):
|
||||
|
||||
<details><summary>Template for test files</summary>
|
||||
|
||||
```lua
|
||||
local new_set = MiniTest.new_set
|
||||
local expect, eq = MiniTest.expect, MiniTest.expect.equality
|
||||
|
||||
local T = new_set()
|
||||
|
||||
-- Actual tests definitions will go here
|
||||
|
||||
return T
|
||||
```
|
||||
|
||||
</details><br>
|
||||
|
||||
- **'mini.nvim' dependency**. It is needed to use its 'mini.test' module. Proposed way to store it is in 'deps/mini.nvim' directory. Create it with `git`:
|
||||
|
||||
```bash
|
||||
mkdir -p deps
|
||||
git clone --depth 1 https://github.com/echasnovski/mini.nvim deps/mini.nvim
|
||||
```
|
||||
|
||||
- **Manual Neovim startup file** (a.k.a 'init.lua') with proposed path 'scripts/minimal_init.lua'. It will be used to ensure that Neovim processes can recognize your tested plugin and 'mini.nvim' dependency. Proposed minimal content:
|
||||
|
||||
<details><summary>'scripts/minimal_init.lua'</summary>
|
||||
|
||||
```lua
|
||||
-- Add current directory to 'runtimepath' to be able to use 'lua' files
|
||||
vim.cmd([[let &rtp.=','.getcwd()]])
|
||||
|
||||
-- Set up 'mini.test' only when calling headless Neovim (like with `make test`)
|
||||
if #vim.api.nvim_list_uis() == 0 then
|
||||
-- Add 'mini.nvim' to 'runtimepath' to be able to use 'mini.test'
|
||||
-- Assumed that 'mini.nvim' is stored in 'deps/mini.nvim'
|
||||
vim.cmd('set rtp+=deps/mini.nvim')
|
||||
|
||||
-- Set up 'mini.test'
|
||||
require('mini.test').setup()
|
||||
end
|
||||
```
|
||||
|
||||
</details><br>
|
||||
|
||||
Recommended:
|
||||
|
||||
- **Makefile**. In order to simplify running tests from shell and inside Continuous Integration services (like Github Actions), it is recommended to define Makefile. It will define steps for running tests. Proposed template:
|
||||
|
||||
<details><summary>Template for Makefile</summary>
|
||||
|
||||
```
|
||||
# Run all test files
|
||||
test: deps/mini.nvim
|
||||
nvim --headless --noplugin -u ./scripts/minimal_init.lua -c "lua MiniTest.run()"
|
||||
|
||||
# Run test from file at `$FILE` environment variable
|
||||
test_file: deps/mini.nvim
|
||||
nvim --headless --noplugin -u ./scripts/minimal_init.lua -c "lua MiniTest.run_file('$(FILE)')"
|
||||
|
||||
# Download 'mini.nvim' to use its 'mini.test' testing module
|
||||
deps/mini.nvim:
|
||||
@mkdir -p deps
|
||||
git clone --depth 1 https://github.com/echasnovski/mini.nvim $@
|
||||
```
|
||||
|
||||
</details><br>
|
||||
|
||||
- **'mini.test' script** at 'scripts/minitest.lua'. Use it to customize what is tested (which files, etc.) and how. Usually not needed, but otherwise should have some variant of a call to `MiniTest.run()`.
|
||||
|
||||
## Running tests
|
||||
|
||||
The 'mini.test' module out of the box supports two major ways of running tests:
|
||||
|
||||
- **Interactive**. All test files will be run directly inside current Neovim session. This proved to be very useful for debugging while writing tests. To run tests, simply execute `:lua MiniTest.run()` or `:lua MiniTest.run_file()` (assuming, you already have 'mini.test' set up with `require('mini.test').setup()`). With default configuration this will result into floating window with information about results of test execution. Press `q` to close it. **Note**: Be careful though, as it might affect your current setup. To avoid this, [use child processes](#using-child-process) inside tests.
|
||||
- **Headless** (from shell). Start headless Neovim process with proper startup file and execute `lua MiniTest.run()`. Assuming full file organization from previous section, this can be achieved with `make test`. This will show information about results of test execution directly in shell.
|
||||
|
||||
|
||||
## Basics
|
||||
|
||||
These sections will show some basic capabilities of 'mini.test' and how to use them. In all examples code blocks represent some whole test file (like 'tests/test_basics.lua').
|
||||
|
||||
### First test
|
||||
|
||||
A test is defined as function assigned to a field of test set. If it throws error, test has failed. Test file should return single test set. Here is an example:
|
||||
|
||||
```lua
|
||||
local T = MiniTest.new_set()
|
||||
|
||||
T['works'] = function()
|
||||
local x = 1 + 1
|
||||
if x ~= 2 then
|
||||
error('`x` is not equal to 2')
|
||||
end
|
||||
end
|
||||
|
||||
return T
|
||||
```
|
||||
|
||||
Writing `if .. error() .. end` is too tiresome. That is why 'mini.test' comes with very minimal but usually quite enough set of *expectations*: `MiniTest.expect`. They display the intended expectation between objects and will throw error with informative message if it doesn't hold. Here is a rewritten previous example:
|
||||
|
||||
```lua
|
||||
local T = MiniTest.new_set()
|
||||
|
||||
T['works'] = function()
|
||||
local x = 1 + 1
|
||||
MiniTest.expect(x, 2)
|
||||
end
|
||||
|
||||
return T
|
||||
```
|
||||
|
||||
Test sets can be nested. This will be useful in combination with [hooks](#hooks) and [parametrization](#test-parametrization):
|
||||
|
||||
```lua
|
||||
local T = MiniTest.new_set()
|
||||
|
||||
T['big scope'] = new_set()
|
||||
|
||||
T['big scope']['works'] = function()
|
||||
local x = 1 + 1
|
||||
MiniTest.expect.equality(x, 2)
|
||||
end
|
||||
|
||||
T['big scope']['also works'] = function()
|
||||
local x = 2 + 2
|
||||
MiniTest.expect.equality(x, 4)
|
||||
end
|
||||
|
||||
T['out of scope'] = function()
|
||||
local x = 3 + 3
|
||||
MiniTest.expect.equality(x, 6)
|
||||
end
|
||||
|
||||
return T
|
||||
```
|
||||
|
||||
**NOTE**: 'mini.test' supports emulation of busted-style testing by default. So previous example can be written like this:
|
||||
|
||||
```lua
|
||||
describe('big scope', function()
|
||||
it('works', function()
|
||||
local x = 1 + 1
|
||||
MiniTest.expect.equality(x, 2)
|
||||
end)
|
||||
|
||||
it('also works', function()
|
||||
local x = 2 + 2
|
||||
MiniTest.expect.equality(x, 4)
|
||||
end)
|
||||
end)
|
||||
|
||||
it('out of scope', function()
|
||||
local x = 3 + 3
|
||||
MiniTest.expect.equality(x, 6)
|
||||
end)
|
||||
|
||||
-- NOTE: when using this style, no test set should be returned
|
||||
```
|
||||
|
||||
Although this is possible, the rest of this file will use a recommended test set approach.
|
||||
|
||||
### Builtin expectations
|
||||
|
||||
There are four builtin expectations:
|
||||
|
||||
```lua
|
||||
local T = MiniTest.new_set()
|
||||
local expect, eq = MiniTest.expect, MiniTest.expect.equality
|
||||
|
||||
local x = 1 + 1
|
||||
|
||||
-- This is so frequently used that having short alias proved useful
|
||||
T['expect.equality'] = function()
|
||||
eq(x, 2)
|
||||
end
|
||||
|
||||
T['expect.no_equality'] = function()
|
||||
expect.no_equality(x, 1)
|
||||
end
|
||||
|
||||
T['expect.error'] = function()
|
||||
-- This expectation will pass because function will throw an error
|
||||
expect.error(function()
|
||||
if x == 2 then error('Deliberate error') end
|
||||
end)
|
||||
end
|
||||
|
||||
T['expect.no_error'] = function()
|
||||
-- This expectation will pass because function will *not* throw an error
|
||||
expect.no_error(function()
|
||||
if x ~= 2 then error('This should not be thrown') end
|
||||
end)
|
||||
end
|
||||
|
||||
return T
|
||||
```
|
||||
|
||||
### Writing custom expectation
|
||||
|
||||
Although you can use `if ... error() ... end` approach, there is `MiniTest.new_expectation()` to simplify this process for some repetitive expectation. Here is an example used in this plugin:
|
||||
|
||||
```lua
|
||||
local T = MiniTest.new_set()
|
||||
|
||||
local expect_match = MiniTest.new_expectation(
|
||||
-- Expectation subject
|
||||
'string matching',
|
||||
-- Predicate
|
||||
function(str, pattern) return str:find(pattern) ~= nil end,
|
||||
-- Fail context
|
||||
function(str, pattern)
|
||||
return string.format('Pattern: %s\nObserved string: %s', vim.inspect(pattern), str)
|
||||
end
|
||||
)
|
||||
|
||||
T['string matching'] = function()
|
||||
local x = 'abcd'
|
||||
-- This will pass
|
||||
expect_match(x, '^a')
|
||||
|
||||
-- This will fail
|
||||
expect_match(x, 'x')
|
||||
end
|
||||
|
||||
return T
|
||||
```
|
||||
|
||||
Executing this content from file 'tests/test_basics.lua' will fail with the following message:
|
||||
|
||||
```
|
||||
FAIL in "tests/test_basics.lua | string matching":
|
||||
Failed expectation for string matching.
|
||||
Pattern: "x"
|
||||
Observed string: abcd
|
||||
Traceback:
|
||||
tests/test_basics.lua:20
|
||||
```
|
||||
|
||||
### Hooks
|
||||
|
||||
Hooks are functions that will be called without arguments at predefined stages of test execution. They are defined for a test set. There are four types of hooks:
|
||||
|
||||
- **pre_once** - executed before first (filtered) node.
|
||||
- **pre_case** - executed before each case (even nested).
|
||||
- **post_case** - executed after each case (even nested).
|
||||
- **post_once** - executed after last (filtered) node.
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
local new_set = MiniTest.new_set
|
||||
local expect, eq = MiniTest.expect, MiniTest.expect.equality
|
||||
|
||||
local T = new_set()
|
||||
|
||||
local n = 0
|
||||
local increase_n = function() n = n + 1 end
|
||||
|
||||
T['hooks'] = new_set({
|
||||
hooks = { pre_once = increase_n, pre_case = increase_n, post_case = increase_n, post_once = increase_n },
|
||||
})
|
||||
|
||||
T['hooks']['work'] = function()
|
||||
-- `n` will be increased twice: in `pre_once` and `pre_case`
|
||||
eq(n, 2)
|
||||
end
|
||||
|
||||
T['hooks']['work again'] = function()
|
||||
-- `n` will be increased twice: in `post_case` from previous case and
|
||||
-- `pre_case` before this one
|
||||
eq(n, 4)
|
||||
end
|
||||
|
||||
T['after hooks set'] = function()
|
||||
-- `n` will be again increased twice: in `post_case` from previous case and
|
||||
-- `post_once` after last case in T['hooks'] test set
|
||||
eq(n, 6)
|
||||
end
|
||||
|
||||
return T
|
||||
```
|
||||
|
||||
### Test parametrization
|
||||
|
||||
One of the distinctive features of 'mini.test' is ability to leverage test parametrization. As hooks, it is a feature of test set.
|
||||
|
||||
Example of simple parametrization:
|
||||
|
||||
```lua
|
||||
local new_set = MiniTest.new_set
|
||||
local eq = MiniTest.expect.equality
|
||||
|
||||
local T = new_set()
|
||||
|
||||
-- Each parameter should be an array to allow parametrizing multiple arguments
|
||||
T['parametrize'] = new_set({ parametrize = { { 1 }, { 2 } } })
|
||||
|
||||
-- This will result into two cases. First will fail.
|
||||
T['parametrize']['works'] = function(x)
|
||||
eq(x, 2)
|
||||
end
|
||||
|
||||
-- Parametrization can be nested. Cases are "multiplied" with every combination
|
||||
-- of parameters.
|
||||
T['parametrize']['nested'] = new_set({ parametrize = { { '1' }, { '2' } } })
|
||||
|
||||
-- This will result into four cases. Two of them will fail.
|
||||
T['parametrize']['nested']['works'] = function(x, y)
|
||||
eq(tostring(x), y)
|
||||
end
|
||||
|
||||
-- Parametrizing multiple arguments
|
||||
T['parametrize multiple arguments'] = new_set({ parametrize = { { 1, 1 }, { 2, 2 } } })
|
||||
|
||||
-- This will result into two cases. Both will pass.
|
||||
T['parametrize multiple arguments']['works'] = function(x, y)
|
||||
eq(x, y)
|
||||
end
|
||||
|
||||
return T
|
||||
```
|
||||
|
||||
### Runtime access to current cases
|
||||
|
||||
There is `MiniTest.current` table containing information about "current" test cases. It has `all_cases` and `case` fields with all currently executed tests and *the* current case.
|
||||
|
||||
Test case is a single unit of sequential test execution. It contains all information needed to execute test case along with data about its execution. Example:
|
||||
|
||||
```lua
|
||||
local new_set = MiniTest.new_set
|
||||
local eq = MiniTest.expect.equality
|
||||
|
||||
local T = new_set()
|
||||
|
||||
T['MiniTest.current.all_cases'] = function()
|
||||
-- A useful hack: show runtime data with expecting it to be something else
|
||||
eq(MiniTest.current.all_cases, 0)
|
||||
end
|
||||
|
||||
T['MiniTest.current.case'] = function()
|
||||
eq(MiniTest.current.case, 0)
|
||||
end
|
||||
|
||||
return T
|
||||
```
|
||||
|
||||
This will result into following lengthy fails:
|
||||
|
||||
<details><summary>Fail information</summary>
|
||||
|
||||
```
|
||||
FAIL in "tests/test_basics.lua | MiniTest.current.all_cases":
|
||||
Failed expectation for equality.
|
||||
Left: { {
|
||||
args = {},
|
||||
data = {},
|
||||
desc = { "tests/test_basics.lua", "MiniTest.current.all_cases" },
|
||||
exec = {
|
||||
fails = {},
|
||||
notes = {},
|
||||
state = "Executing test"
|
||||
},
|
||||
hooks = {
|
||||
post = {},
|
||||
pre = {}
|
||||
},
|
||||
test = <function 1>
|
||||
}, {
|
||||
args = {},
|
||||
data = {},
|
||||
desc = { "tests/test_basics.lua", "MiniTest.current.case" },
|
||||
hooks = {
|
||||
post = {},
|
||||
pre = {}
|
||||
},
|
||||
test = <function 2>
|
||||
} }
|
||||
Right: 0
|
||||
Traceback:
|
||||
tests/test_basics.lua:8
|
||||
|
||||
FAIL in "tests/test_basics.lua | MiniTest.current.case":
|
||||
Failed expectation for equality.
|
||||
Left: {
|
||||
args = {},
|
||||
data = {},
|
||||
desc = { "tests/test_basics.lua", "MiniTest.current.case" },
|
||||
exec = {
|
||||
fails = {},
|
||||
notes = {},
|
||||
state = "Executing test"
|
||||
},
|
||||
hooks = {
|
||||
post = {},
|
||||
pre = {}
|
||||
},
|
||||
test = <function 1>
|
||||
}
|
||||
Right: 0
|
||||
Traceback:
|
||||
tests/test_basics.lua:12
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Case helpers
|
||||
|
||||
There are some functions intended to help writing more robust cases: `skip()`, `finally()`, and `add_note()`. The `MiniTest.current` table with all
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
local T = MiniTest.new_set()
|
||||
|
||||
-- `MiniTest.skip()` allows skipping rest of test execution while giving an
|
||||
-- informative note. This test will pass with notes.
|
||||
T['skip()'] = function()
|
||||
if 1 + 1 == 2 then
|
||||
MiniTest.skip('Apparently, 1 + 1 is 2')
|
||||
end
|
||||
error('1 + 1 is not 2')
|
||||
end
|
||||
|
||||
-- `MiniTest.add_note()` allows adding notes. Final state will have
|
||||
-- "with notes" suffix.
|
||||
T['add_note()'] = function()
|
||||
MiniTest.add_note('This test is not important.')
|
||||
error('Custom error.')
|
||||
end
|
||||
|
||||
-- `MiniTest.finally()` allows registering some function to be executed after
|
||||
-- this case is finished executing (with or without an error).
|
||||
T['finally()'] = function()
|
||||
-- Add note only if test fails
|
||||
MiniTest.finally(function()
|
||||
if #MiniTest.current.case.exec.fails > 0 then
|
||||
MiniTest.add_note('This test is flaky.')
|
||||
end
|
||||
end)
|
||||
error('Expected error from time to time')
|
||||
end
|
||||
|
||||
return T
|
||||
```
|
||||
|
||||
This will result into following messages:
|
||||
|
||||
```
|
||||
NOTE in "tests/test_basics.lua | skip()": Apparently, 1 + 1 is 2
|
||||
|
||||
FAIL in "tests/test_basics.lua | add_note()": tests/test_basics.lua:16: Custom error.
|
||||
NOTE in "tests/test_basics.lua | add_note()": This test is not important.
|
||||
|
||||
FAIL in "tests/test_basics.lua | finally()": tests/test_basics.lua:28: Expected error from time to time
|
||||
NOTE in "tests/test_basics.lua | finally()": This test is flaky.
|
||||
```
|
||||
|
||||
## Customizing test run
|
||||
|
||||
Test run consists from two stages:
|
||||
|
||||
- **Collection**. It will source each appropriate file (customizable), combine all test sets into single test set, convert it from hierarchical to sequential form (array of test cases), and filter cases based on customizable predicate.
|
||||
- **Execution**. It will safely execute array of test cases (with each pre-hooks, test action, post-hooks) one after another in scheduled asynchronous fashion while collecting information about it went and calling customizable reporter methods.
|
||||
|
||||
All configuration goes into `opts` argument of `MiniTest.run()`.
|
||||
|
||||
### Collection: custom files and filter
|
||||
|
||||
You can customize which files will be sourced and which cases will be later executed. Example:
|
||||
|
||||
```lua
|
||||
local new_set = MiniTest.new_set
|
||||
|
||||
T = new_set()
|
||||
|
||||
-- Use `data` field to pass custom information for easier test management
|
||||
T['fast'] = new_set({ data = { type = 'fast' } })
|
||||
T['fast']['first test'] = function() end
|
||||
T['fast']['second test'] = function() end
|
||||
|
||||
T['slow'] = new_set({ data = { type = 'slow' } })
|
||||
T['slow']['first test'] = function() vim.loop.sleep(1000) end
|
||||
T['slow']['second test'] = function() vim.loop.sleep(1000) end
|
||||
|
||||
return T
|
||||
```
|
||||
|
||||
You can run only this file ('tests/test_basics.lua') and only "fast" cases with
|
||||
```lua
|
||||
MiniTest.run({
|
||||
collect = {
|
||||
find_files = function() return { 'tests/test_basics.lua' } end,
|
||||
filter_cases = function(case) return case.data.type == 'fast' end,
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Execution: custom reporter and stop on first error
|
||||
|
||||
You can customize execution of test cases with custom reporter (how test results are displayed in real time) and whether to stop on first error. Execution doesn't result into any output, instead it updates `MiniTest.current.all_cases` in place: each case gets an `exec` field with information about how its execution went.
|
||||
|
||||
Example of showing status summary table in the command line after everything is finished:
|
||||
|
||||
```lua
|
||||
local reporter = {
|
||||
-- Other used methods are `start(cases)` and `update(case_num)`
|
||||
finish = function()
|
||||
local summary = {}
|
||||
for _, c in ipairs(MiniTest.current.all_cases) do
|
||||
local state = c.exec.state
|
||||
summary[state] = summary[state] == nil and 1 or (summary[state] + 1)
|
||||
end
|
||||
|
||||
print(vim.inspect(summary, { newline = ' ', indent = '' }))
|
||||
end,
|
||||
}
|
||||
|
||||
MiniTest.run({ execute = { reporter = reporter } })
|
||||
```
|
||||
|
||||
## Using child process
|
||||
|
||||
Main feature of 'mini.test' which differs it from other Lua testing frameworks is its design towards **custom usage of child Neovim process inside tests**. Ultimately, each test should be done with fresh Neovim process initialized with bare minimum setup (like allowing to load your plugin). To make this easier, there is a dedicated function `MiniTest.new_child_neovim()`. It returns an object with many useful helper methods, like for start/stop/restart, redirected execution (write code in current process, it gets executed in child one), emulating typing keys, **testing screen state**, etc.
|
||||
|
||||
### Start/stop/restart
|
||||
|
||||
You can start/stop/restart child process associated with this child Neovim object. Current (from which testing is initiated) and child Neovim processes can "talk" to each through RPC messages (see `:h RPC`). It means you can programmatically execute code inside child process, get some output, and test if it meets your expectation. Also by default child process is "full" (i.e. not headless) which allows you to test things such as extmarks, floating windows, etc.
|
||||
|
||||
Although this approach proved to be useful and efficient, it is not ideal. Here are some limitations:
|
||||
- Due to current RPC protocol implementation functions and userdata can't be used in both input and output with child process. Indicator of this issue is a `Cannot convert given lua type` error. Usual solution is to move some logic on the side of child process, like create and use global functions (those will be "forgotten" after next restart).
|
||||
- Sometimes hanging process will occur: it stops executing without any output. Most of the time it is because Neovim process is "blocked", i.e. it waits for user input and won't return from other call. Common causes are active hit-enter-prompt (increase prompt height to a bigger value) or Operator-pending mode (exit it). To mitigate this experience, most helper methods will throw an error if they can deduct that immediate execution will lead to hanging state.
|
||||
|
||||
Here is recommended setup for managing child processes. It will make fresh Neovim process before every test case:
|
||||
|
||||
```lua
|
||||
local child = MiniTest.new_child_neovim()
|
||||
|
||||
local T = MiniTest.new_set({
|
||||
hooks = {
|
||||
pre_case = function()
|
||||
-- Restart child process with custom 'init.lua' script
|
||||
child.restart({ '-u', 'scripts/minimal_init.lua' })
|
||||
-- Load tested plugin
|
||||
child.lua([[M = require('hello_lines')]])
|
||||
end,
|
||||
-- Stop once all test cases are finished
|
||||
post_once = child.stop,
|
||||
},
|
||||
})
|
||||
|
||||
-- Define some tests here
|
||||
|
||||
return T
|
||||
```
|
||||
|
||||
### Executing Lua code
|
||||
|
||||
Previous section already demonstrated that there is a `child.lua()` method. It will execute arbitrary Lua code in the form of a single string. This is basically a wrapper for `vim.api.nvim_exec_lua()`. There is also a convenience wrapper `child.lua_get()` which is essentially a `child.lua('return ' .. s, ...)`. Examples:
|
||||
|
||||
```lua
|
||||
local eq = MiniTest.expect.equality
|
||||
|
||||
local child = MiniTest.new_child_neovim()
|
||||
|
||||
local T = MiniTest.new_set({
|
||||
hooks = {
|
||||
pre_case = function()
|
||||
child.restart({ '-u', 'scripts/minimal_init.lua' })
|
||||
child.lua([[M = require('hello_lines')]])
|
||||
end,
|
||||
post_once = child.stop,
|
||||
},
|
||||
})
|
||||
|
||||
T['lua()'] = MiniTest.new_set()
|
||||
|
||||
T['lua()']['works'] = function()
|
||||
child.lua('_G.n = 0; _G.n = _G.n + 1')
|
||||
eq(child.lua('return _G.n'), 1)
|
||||
end
|
||||
|
||||
T['lua()']['can use tested plugin'] = function()
|
||||
eq(child.lua([[return M.compute()]]), { 'Hello world' })
|
||||
eq(child.lua([[return M.compute({'a', 'b'})]]), { 'Hello a', 'Hello b' })
|
||||
end
|
||||
|
||||
T['lua_get()'] = function()
|
||||
child.lua('_G.n = 0')
|
||||
eq(child.lua_get('_G.n'), child.lua('return _G.n'))
|
||||
end
|
||||
|
||||
return T
|
||||
```
|
||||
|
||||
### Managing Neovim options and state
|
||||
|
||||
Although ability to execute arbitrary Lua code is technically enough to write any tests, it gets cumbersome very quickly due to ability to only take string. That is why there are many convenience helpers with the same idea: write code inside current Neovim process that will be automatically executed same way in child process. Here is the showcase:
|
||||
|
||||
```lua
|
||||
local new_set = MiniTest.new_set
|
||||
local eq = MiniTest.expect.equality
|
||||
|
||||
local child = MiniTest.new_child_neovim()
|
||||
|
||||
local T = MiniTest.new_set({
|
||||
hooks = {
|
||||
pre_case = function()
|
||||
child.restart({ '-u', 'scripts/minimal_init.lua' })
|
||||
child.lua([[M = require('hello_lines')]])
|
||||
end,
|
||||
post_once = child.stop,
|
||||
},
|
||||
})
|
||||
|
||||
-- These methods will "redirect" execution to child through `vim.rpcrequest()`
|
||||
-- and `vim.rpcnotify()` respectively. Any call `child.api.xxx(...)` returns
|
||||
-- the output of `vim.api.xxx(...)` executed inside child process.
|
||||
T['api()/api_notify()'] = function()
|
||||
-- Set option. For some reason, first buffer is 'readonly' which leads to
|
||||
-- high delay in test execution
|
||||
child.api.nvim_buf_set_option(0, 'readonly', false)
|
||||
|
||||
-- Set lal lines
|
||||
child.api.nvim_buf_set_lines(0, 0, -1, true, { 'aaa' })
|
||||
|
||||
-- Get all lines and test with expected ones
|
||||
eq(child.api.nvim_buf_get_lines(0, 0, -1, true), { 'aaa' })
|
||||
end
|
||||
|
||||
-- Execute Vimscript with or without capturing its output
|
||||
T['cmd()/cmd()'] = function()
|
||||
child.cmd('hi Comment guifg=#AAAAAA')
|
||||
eq(child.cmd_capture('hi Comment'), 'Comment xxx ctermfg=14 guifg=#aaaaaa')
|
||||
end
|
||||
|
||||
-- There are redirection tables for most of the main Neovim functionality
|
||||
T['various redirection tables with methods'] = function()
|
||||
eq(child.fn.fnamemodify('hello_lines.lua', ':t:r'), 'hello_lines')
|
||||
eq(child.loop.hrtime() > 0, true)
|
||||
eq(child.lsp.get_active_clients(), {})
|
||||
|
||||
-- And more
|
||||
end
|
||||
|
||||
-- There are redirection tables for scoped (buffer, window, etc.) variables
|
||||
-- You can use them to both set and get values
|
||||
T['redirection tables for variables'] = function()
|
||||
child.b.aaa = true
|
||||
eq(child.b.aaa, true)
|
||||
eq(child.b.aaa, child.lua_get('vim.b.aaa'))
|
||||
end
|
||||
|
||||
-- There are redirection tables for scoped (buffer, window, etc.) options
|
||||
-- You can use them to both set and get values
|
||||
T['redirection tables for options'] = function()
|
||||
child.o.lines, child.o.columns = 5, 12
|
||||
eq(child.o.lines, 5)
|
||||
eq({ child.o.lines, child.o.columns }, child.lua_get('{ vim.o.lines, vim.o.columns }'))
|
||||
end
|
||||
|
||||
return T
|
||||
```
|
||||
|
||||
### Emulate typing keys
|
||||
|
||||
Very important part of testing is emulating user typing keys. There is a special `child.type_keys()` helper method for that. Examples:
|
||||
|
||||
```lua
|
||||
local eq = MiniTest.expect.equality
|
||||
|
||||
local child = MiniTest.new_child_neovim()
|
||||
|
||||
local T = MiniTest.new_set({
|
||||
hooks = {
|
||||
pre_case = function()
|
||||
child.restart({ '-u', 'scripts/minimal_init.lua' })
|
||||
child.bo.readonly = false
|
||||
child.lua([[M = require('hello_lines')]])
|
||||
end,
|
||||
post_once = child.stop,
|
||||
},
|
||||
})
|
||||
|
||||
local get_lines = function() return child.api.nvim_buf_get_lines(0, 0, -1, true) end
|
||||
|
||||
T['type_keys()'] = MiniTest.new_set()
|
||||
|
||||
T['type_keys()']['works'] = function()
|
||||
-- It can take one string
|
||||
child.type_keys('iabcde<Esc>')
|
||||
eq(get_lines(), { 'abcde' })
|
||||
eq(child.fn.mode(), 'n')
|
||||
|
||||
-- Or several strings which improves readability
|
||||
child.type_keys('cc', 'fghij', '<Esc>')
|
||||
eq(get_lines(), { 'fghij' })
|
||||
|
||||
-- Or tables of strings (possibly nested)
|
||||
child.type_keys({ 'cc', { 'j', 'k', 'l', 'm', 'n' } })
|
||||
eq(get_lines(), { 'jklmn' })
|
||||
end
|
||||
|
||||
T['type_keys()']['allows custom delay'] = function()
|
||||
-- This adds delay of 500 ms after each supplied string (three times here)
|
||||
child.type_keys(500, 'i', 'abcde', '<Esc>')
|
||||
eq(get_lines(), { 'abcde' })
|
||||
end
|
||||
|
||||
return T
|
||||
```
|
||||
|
||||
### Test screen state with screenshots
|
||||
|
||||
One of the main difficulties in testing Neovim plugins is verifying that something is actually displayed in the way you intend. Like general highlighting, statusline, tabline, sign column, extmarks, etc. Testing screen state with screenshots makes this a lot easier. There is a `child.get_screenshot()` method which basically calls `screenstring()` (`:h screenstring()`) and `screenattr()` (`:h screenattr()`) for every visible cell (row from 1 to 'lines' option, column from 1 to 'columns' option). It then returns two layers of screenshot:
|
||||
|
||||
- <text> - "2d array" (row-column) of single characters displayed at particular cells.
|
||||
- <attr> - "2d array" (row-column) of symbols representing how text is displayed (basically, "coded" appearance/highlighting). They should be used only in relation to each other: same/different symbols for two cells mean same/different visual appearance. Note: there will be false positives if there are more than 94 different attribute values. To make output more portable and visually useful, outputs of `screenattr()` are coded with single character symbols.
|
||||
|
||||
Couple of caveats:
|
||||
|
||||
- As is apparent from use of `screenattr()`, these screenshots **can't tell how exactly cell is highlighted**, only **if two cells are highlighted the same**. This is due to the currently lacking functionality in Neovim itself. This might change in the future.
|
||||
- It works only for Neovim>=0.6 because `screenstring()` was introduced in 0.6.
|
||||
- Due to implementation details of `screenstring()` and `screenattr()` in Neovim<=0.7, this function won't recognize floating windows displayed on screen. It will throw an error if there is a visible floating window. Use Neovim>=0.8 (current nightly) to properly handle floating windows. Details:
|
||||
- https://github.com/neovim/neovim/issues/19013
|
||||
- https://github.com/neovim/neovim/pull/19020
|
||||
|
||||
To help manage testing screen state, there is a special `MiniTest.expect.reference_screenshot(screenshot, path, opts)` method. It takes screenshot table along with optional path of where to save this screenshot (if not supplied, inferred from test case description and put in 'tests/screenshots' directory). On first run it will automatically create reference screenshot at `path`. On later runs it will compare current screenshot with reference. Will throw informative error with helpful information if they don't match exactly.
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
local expect = MiniTest.expect
|
||||
|
||||
local child = MiniTest.new_child_neovim()
|
||||
|
||||
local T = MiniTest.new_set({
|
||||
hooks = {
|
||||
pre_case = function()
|
||||
child.restart({ '-u', 'scripts/minimal_init.lua' })
|
||||
child.bo.readonly = false
|
||||
child.lua([[M = require('hello_lines')]])
|
||||
end,
|
||||
post_once = child.stop,
|
||||
},
|
||||
})
|
||||
|
||||
T['set_lines()'] = MiniTest.new_set({ parametrize = { {}, { 0, { 'a' } }, { 0, { 1, 2, 3 } } } })
|
||||
|
||||
T['set_lines()']['works'] = function(buf_id, lines)
|
||||
child.o.lines, child.o.columns = 10, 15
|
||||
child.lua('M.set_lines(...)', { buf_id, lines })
|
||||
expect.reference_screenshot(child.get_screenshot())
|
||||
end
|
||||
|
||||
return T
|
||||
```
|
||||
|
||||
This will result into three files in 'tests/screenshots' with names containing test case description along with supplied arguments. Here is example reference screenshot for `{ 0, { 1, 2, 3 } }` arguments (line numbers and ruler for columns is added as file specification to make it easier to find differences between two screenshots):
|
||||
|
||||
```
|
||||
--|---------|-----
|
||||
01|Hello 1
|
||||
02|Hello 2
|
||||
03|Hello 3
|
||||
04|~
|
||||
05|~
|
||||
06|~
|
||||
07|~
|
||||
08|~
|
||||
09|<e] [+] 1,1 All
|
||||
10|
|
||||
|
||||
--|---------|-----
|
||||
01|000001111111111
|
||||
02|000001111111111
|
||||
03|000001111111111
|
||||
04|222222222222222
|
||||
05|222222222222222
|
||||
06|222222222222222
|
||||
07|222222222222222
|
||||
08|222222222222222
|
||||
09|333333333333333
|
||||
10|444444444444444
|
||||
```
|
||||
|
||||
## General tips
|
||||
|
||||
- Create a 'tests/helpers.lua' file with code that can be useful in multiple files. It can have "monkey-patched" versions of 'mini.test' functions. Example:
|
||||
|
||||
```lua
|
||||
local Helpers = {}
|
||||
|
||||
Helpers.new_child_neovim = function()
|
||||
local child = MiniTest.new_child_neovim()
|
||||
|
||||
child.setup = function()
|
||||
child.restart({'-u', 'scripts/minimal_init.lua'})
|
||||
child.bo.readonly = false
|
||||
child.lua([[M = require('hello_lines')]])
|
||||
end
|
||||
|
||||
return child
|
||||
end
|
||||
|
||||
return Helpers
|
||||
```
|
||||
|
||||
- Write aliases for commonly used functions at top of the file. It will make your life a little bit easier and usually will lead to more readable tests. Example:
|
||||
|
||||
```lua
|
||||
-- Some code setting up `child`
|
||||
local set_lines = function(lines) child.api.nvim_buf_set_lines(0, 0, -1, true, lines) end
|
||||
```
|
|
@ -0,0 +1,35 @@
|
|||
# Benchmarks for 'mini.starter'
|
||||
|
||||
This directory contains code and results of benchmarking 'mini.starter' and its alternatives. Target benchmarked value is a total startup time using configuration file (with `-u <init-file>`) corresponding to benchmarked setup. Configuration files are created in three different groups:
|
||||
|
||||
- 'init_starter-default.lua' and 'init_empty.lua' represent default 'mini.starter' setup and corresponding 'init.lua' without 'mini.starter'.
|
||||
- 'init_startify-starter', 'init_startify-original', and 'init_startify-alpha' have comparable output imitating default 'vim-startify' with empty header.
|
||||
- 'init_dashboard-starter', 'init_dashboard-original', and 'init_dashboard-alpha' have comparable output imitating default 'dashboard-nvim' enabled keybindings.
|
||||
|
||||
Summary of startup-times for various 'init' files from 'init-files/' directory can be seen in 'startup-summary.md'. Current benchmark was done with Neovim 0.5.1 on Ubuntu 18.04 (i3-6100). Exact states of plugins used:
|
||||
|
||||
- [echasnovski/mini.nvim](https://github.com/echasnovski/mini.nvim/tree/cfa108eeaead1abd8854a1f1cfb02e72482641ce)
|
||||
- [mhinz/vim-startify](https://github.com/mhinz/vim-startify/tree/81e36c352a8deea54df5ec1e2f4348685569bed2)
|
||||
- [glepnir/dashboard-nvim](https://github.com/glepnir/dashboard-nvim/tree/ba98ab86487b8eda3b0934b5423759944b5f7ebd)
|
||||
- [goolord/alpha-nvim](https://github.com/goolord/alpha-nvim/tree/7a49086bf9197f573b396d4ac46262c02dfb9aec)
|
||||
|
||||
To rerun locally execute these commands (preferably without anything else running in the background and monitor always on):
|
||||
|
||||
```bash
|
||||
chmod +x install.sh
|
||||
./install.sh
|
||||
|
||||
# This will create file 'startup-times.csv' and update 'startup-summary.md'
|
||||
# WARNING: this will lead to screen flicker
|
||||
chmod +x benchmark.sh
|
||||
./benchmark.sh
|
||||
```
|
||||
|
||||
Structure:
|
||||
|
||||
- 'init-files/' - directory with all configuration files being benchmarked. NOTE: all of them contain auto-closing command at the end (`defer_fn(...)`) to most accurately measure startup time. To view its output, remove this command.
|
||||
- 'benchmark.sh' - script for performing benchmark which is as close to real-world usage as reasonably possible and computing its summary. Its outputs are 'startup-times.csv' and 'startup-summary.md'. All configuration files are benchmarked in alternate fashion: first 'init' file, second, ..., last, first, etc. WARNING: EXECUTION OF THIS SCRIPT LEADS TO MONITOR FLICKERING WHICH MAY CAUSE HARM TO YOUR HEALTH. This is needed to ensure that Neovim was actually opened and something was drawn.
|
||||
- 'install.sh' - script for installing all required plugins. NOTE: run `chmod +x install.sh` to make it executable.
|
||||
- 'make_summary.py' - Python script to compute summary statistics of csv-file.
|
||||
- 'startup-times.csv' (ignored by Git, latest one can be seen in [this gist](https://gist.github.com/echasnovski/85c334396df6fd0cea7bb42246efb97b)) - csv-file with measured startup times. Each row represent single startup round: when all 'init' files are run alternately. Each column represents startup times of single 'init' file.
|
||||
- 'startup-summary.md' - markdown file as output of 'make_summary.py'. Contains summaries of 'startup-times.csv'.
|
|
@ -0,0 +1,56 @@
|
|||
#! /bin/bash
|
||||
|
||||
# Perform benchmarking of startup times with different Neovim 'init' files.
|
||||
# Execute `nvim -u <*> --startuptime <*>` several times (as closely to actual
|
||||
# usage as possible) in rounds alternating between input 'init' files (to "mix"
|
||||
# possible random noise). Store output in .csv file with rows containing
|
||||
# startup times for a single round, columns - for a single 'init' file.
|
||||
|
||||
# WARNING: EXECUTION OF THIS SCRIPT LEADS TO FLICKERING OF SCREEN WHICH WHICH
|
||||
# MAY CAUSE HARM TO YOUR HEALTH. This is because every 'init' file leads to an
|
||||
# actual opening of Neovim with later automatic closing.
|
||||
|
||||
# Number of rounds to perform benchmark
|
||||
n_rounds=1000
|
||||
|
||||
# Path to output .csv file with startup times per round
|
||||
csv_file=startup-times.csv
|
||||
|
||||
# Path to output .md file with summary table
|
||||
summary_file=startup-summary.md
|
||||
|
||||
# 'Init' files ids with actual paths computed as 'init-files/init_*.lua'
|
||||
init_files=(starter-default empty startify-starter startify-original startify-alpha dashboard-starter dashboard-original dashboard-alpha)
|
||||
|
||||
function comma_join { local IFS=","; shift; echo "$*"; }
|
||||
|
||||
function benchmark {
|
||||
rm -f "$csv_file"
|
||||
touch "$csv_file"
|
||||
|
||||
local tmp_bench_file="tmp-bench.txt"
|
||||
touch "$tmp_bench_file"
|
||||
|
||||
comma_join -- "$@" >> startup-times.csv
|
||||
|
||||
for i in $(seq 1 $n_rounds); do
|
||||
echo "Round $i"
|
||||
|
||||
local bench_times=()
|
||||
|
||||
for init_file in "$@"; do
|
||||
nvim -u "init-files/init_$init_file.lua" --startuptime "$tmp_bench_file"
|
||||
local b_time=$(tail -n 1 "$tmp_bench_file" | cut -d " " -f1)
|
||||
bench_times=("${bench_times[@]}" "$b_time")
|
||||
done
|
||||
|
||||
comma_join -- "${bench_times[@]}" >> "$csv_file"
|
||||
|
||||
rm "$tmp_bench_file"
|
||||
done
|
||||
}
|
||||
|
||||
benchmark "${init_files[@]}"
|
||||
|
||||
# Produce output summary
|
||||
./make_summary.py "${csv_file}" "${summary_file}"
|
|
@ -0,0 +1,21 @@
|
|||
vim.cmd([[set packpath=/tmp/nvim/site]])
|
||||
vim.cmd([[packadd alpha-nvim]])
|
||||
|
||||
local alpha = require('alpha')
|
||||
local dashboard = require('alpha.themes.dashboard')
|
||||
alpha.setup(dashboard.opts)
|
||||
|
||||
vim.g.mapleader = ' '
|
||||
|
||||
-- Some of these keybindings doesn't exactly match with what is shown in
|
||||
-- buffer, but this doesn't really matter. Main point is that some keybindings
|
||||
-- should be pre-made.
|
||||
vim.api.nvim_set_keymap('n', '<Leader>ff', ':Telescope find_files<CR>', { noremap = true, silent = true })
|
||||
vim.api.nvim_set_keymap('n', '<Leader>fh', ':Telescope oldfiles<CR>', { noremap = true, silent = true })
|
||||
vim.api.nvim_set_keymap('n', '<Leader>fr', ':Telescope jumplist<CR>', { noremap = true, silent = true })
|
||||
vim.api.nvim_set_keymap('n', '<Leader>fg', ':Telescope live_grep<CR>', { noremap = true, silent = true })
|
||||
vim.api.nvim_set_keymap('n', '<Leader>fm', ':Telescope marks<CR>', { noremap = true, silent = true })
|
||||
vim.api.nvim_set_keymap('n', '<Leader>sl', ':Telescope command_history<CR>', { noremap = true, silent = true })
|
||||
|
||||
-- Close Neovim just after fully opening it. Randomize to make "more real".
|
||||
vim.defer_fn(function() vim.cmd([[quit]]) end, 100 + 200 * math.random())
|
|
@ -0,0 +1,17 @@
|
|||
vim.cmd([[set packpath=/tmp/nvim/site]])
|
||||
vim.cmd([[packadd dashboard-nvim]])
|
||||
|
||||
vim.g.mapleader = ' '
|
||||
vim.g.dashboard_default_executive = 'telescope'
|
||||
vim.api.nvim_set_keymap('n', '<Leader>ss', ':<C-u>SessionSave<CR>', {})
|
||||
vim.api.nvim_set_keymap('n', '<Leader>sl', ':<C-u>SessionLoad<CR>', {})
|
||||
|
||||
vim.api.nvim_set_keymap('n', '<Leader>fh', ':DashboardFindHistory<CR>', { noremap = true, silent = true })
|
||||
vim.api.nvim_set_keymap('n', '<Leader>ff', ':DashboardFindFile<CR>', { noremap = true, silent = true })
|
||||
vim.api.nvim_set_keymap('n', '<Leader>tc', ':DashboardChangeColorscheme<CR>', { noremap = true, silent = true })
|
||||
vim.api.nvim_set_keymap('n', '<Leader>fa', ':DashboardFindWord<CR>', { noremap = true, silent = true })
|
||||
vim.api.nvim_set_keymap('n', '<Leader>fb', ':DashboardJumpMark<CR>', { noremap = true, silent = true })
|
||||
vim.api.nvim_set_keymap('n', '<Leader>cn', ':DashboardNewFile<CR>', { noremap = true, silent = true })
|
||||
|
||||
-- Close Neovim just after fully opening it. Randomize to make "more real".
|
||||
vim.defer_fn(function() vim.cmd([[quit]]) end, 100 + 200 * math.random())
|
|
@ -0,0 +1,18 @@
|
|||
vim.cmd([[set packpath=/tmp/nvim/site]])
|
||||
vim.cmd([[packadd mini.nvim]])
|
||||
|
||||
local starter = require('mini.starter')
|
||||
starter.setup({
|
||||
items = {
|
||||
{ name = 'Edit file', action = [[enew]], section = 'Actions' },
|
||||
{ name = 'Quit', action = [[quit]], section = 'Actions' },
|
||||
starter.sections.telescope(),
|
||||
},
|
||||
content_hooks = {
|
||||
starter.gen_hook.adding_bullet(),
|
||||
starter.gen_hook.aligning('center', 'center'),
|
||||
},
|
||||
})
|
||||
|
||||
-- Close Neovim just after fully opening it. Randomize to make "more real".
|
||||
vim.defer_fn(function() vim.cmd([[quit]]) end, 100 + 200 * math.random())
|
|
@ -0,0 +1,4 @@
|
|||
vim.cmd([[set packpath=/tmp/nvim/site]])
|
||||
|
||||
-- Close Neovim just after fully opening it. Randomize to make "more real".
|
||||
vim.defer_fn(function() vim.cmd([[quit]]) end, 100 + 200 * math.random())
|
|
@ -0,0 +1,7 @@
|
|||
vim.cmd([[set packpath=/tmp/nvim/site]])
|
||||
vim.cmd([[packadd mini.nvim]])
|
||||
|
||||
require('mini.starter').setup()
|
||||
|
||||
-- Close Neovim just after fully opening it. Randomize to make "more real".
|
||||
vim.defer_fn(function() vim.cmd([[quit]]) end, 100 + 200 * math.random())
|
|
@ -0,0 +1,10 @@
|
|||
vim.cmd([[set packpath=/tmp/nvim/site]])
|
||||
vim.cmd([[packadd alpha-nvim]])
|
||||
|
||||
local alpha = require('alpha')
|
||||
local startify = require('alpha.themes.startify')
|
||||
startify.nvim_web_devicons.enabled = false
|
||||
alpha.setup(startify.opts)
|
||||
|
||||
-- Close Neovim just after fully opening it. Randomize to make "more real".
|
||||
vim.defer_fn(function() vim.cmd([[quit]]) end, 100 + 200 * math.random())
|
|
@ -0,0 +1,7 @@
|
|||
vim.cmd([[set packpath=/tmp/nvim/site]])
|
||||
vim.cmd([[packadd vim-startify]])
|
||||
|
||||
vim.g.startify_custom_header = ''
|
||||
|
||||
-- Close Neovim just after fully opening it. Randomize to make "more real".
|
||||
vim.defer_fn(function() vim.cmd([[quit]]) end, 100 + 200 * math.random())
|
|
@ -0,0 +1,20 @@
|
|||
vim.cmd([[set packpath=/tmp/nvim/site]])
|
||||
vim.cmd([[packadd mini.nvim]])
|
||||
|
||||
local starter = require('mini.starter')
|
||||
starter.setup({
|
||||
evaluate_single = true,
|
||||
items = {
|
||||
starter.sections.builtin_actions(),
|
||||
starter.sections.recent_files(10, false),
|
||||
starter.sections.recent_files(10, true),
|
||||
},
|
||||
content_hooks = {
|
||||
starter.gen_hook.adding_bullet(),
|
||||
starter.gen_hook.indexing('all', { 'Builtin actions' }),
|
||||
starter.gen_hook.padding(3, 2),
|
||||
},
|
||||
})
|
||||
|
||||
-- Close Neovim just after fully opening it. Randomize to make "more real".
|
||||
vim.defer_fn(function() vim.cmd([[quit]]) end, 100 + 200 * math.random())
|
|
@ -0,0 +1,10 @@
|
|||
#! /bin/bash
|
||||
PLUGINPATH=/tmp/nvim/site/pack/bench/opt
|
||||
rm -rf $PLUGINPATH
|
||||
mkdir -p $PLUGINPATH
|
||||
cd $PLUGINPATH
|
||||
|
||||
git clone --depth 1 https://github.com/echasnovski/mini.nvim
|
||||
git clone --depth 1 https://github.com/goolord/alpha-nvim
|
||||
git clone --depth 1 https://github.com/glepnir/dashboard-nvim
|
||||
git clone --depth 1 https://github.com/mhinz/vim-startify
|
|
@ -0,0 +1,68 @@
|
|||
#!/usr/bin/env python
|
||||
import argparse
|
||||
import csv
|
||||
import statistics
|
||||
|
||||
|
||||
def read_csv_columns(csv_path):
|
||||
with open(csv_path, "r") as csvfile:
|
||||
reader = csv.DictReader(csvfile)
|
||||
res = {h: [] for h in reader.fieldnames}
|
||||
for line_dict in reader:
|
||||
for h, val in line_dict.items():
|
||||
res[h].append(float(val))
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def summarise_array(x):
|
||||
return {
|
||||
"median": statistics.median(x),
|
||||
"mean": statistics.mean(x),
|
||||
"stdev": statistics.stdev(x),
|
||||
"minimum": min(x),
|
||||
"maximum": max(x),
|
||||
}
|
||||
|
||||
|
||||
def save_md_summary(summary, output_path):
|
||||
lines = []
|
||||
|
||||
row_names = list(summary.keys())
|
||||
col_names = ["init file"] + list(summary[row_names[0]].keys())
|
||||
lines.append(" | ".join(col_names))
|
||||
lines.append(" | ".join("---" for _ in col_names))
|
||||
|
||||
for row_n in row_names:
|
||||
l = [row_n] + [str(round(x, 1)) + 'ms' for x in summary[row_n].values()]
|
||||
lines.append(" | ".join(l))
|
||||
|
||||
lines = ["| " + l + " |\n" for l in lines]
|
||||
|
||||
with open(output_path, "w") as output:
|
||||
for l in lines:
|
||||
output.write(l)
|
||||
|
||||
|
||||
def compute_summary(csv_path):
|
||||
columns = read_csv_columns(csv_path)
|
||||
return {h: summarise_array(x) for h, x in columns.items()}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"input_csv", help="path to file with startup times in csv format", type=str
|
||||
)
|
||||
parser.add_argument(
|
||||
"output_md",
|
||||
help="output path where markdown summary table will be written",
|
||||
type=str,
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
save_md_summary(compute_summary(args.input_csv), args.output_md)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,10 @@
|
|||
| init file | median | mean | stdev | minimum | maximum |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| starter-default | 69.0ms | 70.8ms | 4.3ms | 63.6ms | 82.7ms |
|
||||
| empty | 64.1ms | 65.8ms | 4.3ms | 60.0ms | 79.4ms |
|
||||
| startify-starter | 70.2ms | 71.9ms | 4.5ms | 64.3ms | 85.7ms |
|
||||
| startify-original | 80.3ms | 82.2ms | 4.5ms | 76.4ms | 107.2ms |
|
||||
| startify-alpha | 72.1ms | 73.8ms | 4.3ms | 66.5ms | 86.9ms |
|
||||
| dashboard-starter | 68.4ms | 70.3ms | 4.5ms | 63.3ms | 102.9ms |
|
||||
| dashboard-original | 70.6ms | 72.3ms | 4.4ms | 65.5ms | 99.6ms |
|
||||
| dashboard-alpha | 69.8ms | 71.5ms | 4.4ms | 64.7ms | 97.2ms |
|
|
@ -0,0 +1,95 @@
|
|||
-- 'Minicyan' color scheme
|
||||
-- Derived from base16 (https://github.com/chriskempson/base16) and
|
||||
-- mini_palette palette generator
|
||||
local use_cterm, palette
|
||||
|
||||
-- Dark palette is an output of 'MiniBase16.mini_palette':
|
||||
-- - Background '#0A2A2A' (LCh(uv) = 15-10-192)
|
||||
-- - Foreground '#D0D0D0' (Lch(uv) = 83-0-0)
|
||||
-- - Accent chroma 50
|
||||
if vim.o.background == 'dark' then
|
||||
palette = {
|
||||
base00 = '#0a2a2a',
|
||||
base01 = '#324747',
|
||||
base02 = '#556868',
|
||||
base03 = '#788a8a',
|
||||
base04 = '#bbbbbb',
|
||||
base05 = '#d0d0d0',
|
||||
base06 = '#e6e6e6',
|
||||
base07 = '#fcfcfc',
|
||||
base08 = '#ebcd91',
|
||||
base09 = '#9f8340',
|
||||
base0A = '#209870',
|
||||
base0B = '#82e3ba',
|
||||
base0C = '#bb6d9b',
|
||||
base0D = '#a9d4ff',
|
||||
base0E = '#ffb9e5',
|
||||
base0F = '#598ab9',
|
||||
}
|
||||
use_cterm = {
|
||||
base00 = 235,
|
||||
base01 = 238,
|
||||
base02 = 241,
|
||||
base03 = 245,
|
||||
base04 = 250,
|
||||
base05 = 252,
|
||||
base06 = 7,
|
||||
base07 = 15,
|
||||
base08 = 186,
|
||||
base09 = 137,
|
||||
base0A = 29,
|
||||
base0B = 115,
|
||||
base0C = 132,
|
||||
base0D = 153,
|
||||
base0E = 218,
|
||||
base0F = 67,
|
||||
}
|
||||
end
|
||||
|
||||
-- Light palette is an 'inverted dark', output of 'MiniBase16.mini_palette':
|
||||
-- - Background '#C0D2D2' (LCh(uv) = 83-10-192)
|
||||
-- - Foreground '#262626' (Lch(uv) = 15-0-0)
|
||||
-- - Accent chroma 80
|
||||
if vim.o.background == 'light' then
|
||||
palette = {
|
||||
base00 = '#c0d2d2',
|
||||
base01 = '#9badad',
|
||||
base02 = '#778989',
|
||||
base03 = '#546767',
|
||||
base04 = '#353535',
|
||||
base05 = '#262626',
|
||||
base06 = '#181818',
|
||||
base07 = '#040404',
|
||||
base08 = '#402100',
|
||||
base09 = '#855f00',
|
||||
base0A = '#007d3c',
|
||||
base0B = '#003d00',
|
||||
base0C = '#b12985',
|
||||
base0D = '#003fb6',
|
||||
base0E = '#7e0052',
|
||||
base0F = '#006cb4',
|
||||
}
|
||||
use_cterm = {
|
||||
base00 = 252,
|
||||
base01 = 248,
|
||||
base02 = 102,
|
||||
base03 = 241,
|
||||
base04 = 236,
|
||||
base05 = 235,
|
||||
base06 = 234,
|
||||
base07 = 0,
|
||||
base08 = 52,
|
||||
base09 = 94,
|
||||
base0A = 29,
|
||||
base0B = 22,
|
||||
base0C = 126,
|
||||
base0D = 25,
|
||||
base0E = 89,
|
||||
base0F = 25,
|
||||
}
|
||||
end
|
||||
|
||||
if palette then
|
||||
require('mini.base16').setup({ palette = palette, use_cterm = use_cterm })
|
||||
vim.g.colors_name = 'minicyan'
|
||||
end
|
|
@ -0,0 +1,95 @@
|
|||
-- 'Minischeme' color scheme
|
||||
-- Derived from base16 (https://github.com/chriskempson/base16) and
|
||||
-- mini_palette palette generator
|
||||
local use_cterm, palette
|
||||
|
||||
-- Dark palette is an output of 'MiniBase16.mini_palette':
|
||||
-- - Background '#112641' (LCh(uv) = 15-20-250)
|
||||
-- - Foreground '#e2e98f' (Lch(uv) = 90-60-90)
|
||||
-- - Accent chroma 75
|
||||
if vim.o.background == 'dark' then
|
||||
palette = {
|
||||
base00 = '#112641',
|
||||
base01 = '#3a475e',
|
||||
base02 = '#606b81',
|
||||
base03 = '#8691a7',
|
||||
base04 = '#d5dc81',
|
||||
base05 = '#e2e98f',
|
||||
base06 = '#eff69c',
|
||||
base07 = '#fcffaa',
|
||||
base08 = '#ffcfa0',
|
||||
base09 = '#cc7e46',
|
||||
base0A = '#46a436',
|
||||
base0B = '#9ff895',
|
||||
base0C = '#ca6ecf',
|
||||
base0D = '#42f7ff',
|
||||
base0E = '#ffc4ff',
|
||||
base0F = '#00a5c5',
|
||||
}
|
||||
use_cterm = {
|
||||
base00 = 235,
|
||||
base01 = 238,
|
||||
base02 = 60,
|
||||
base03 = 103,
|
||||
base04 = 186,
|
||||
base05 = 186,
|
||||
base06 = 229,
|
||||
base07 = 229,
|
||||
base08 = 223,
|
||||
base09 = 173,
|
||||
base0A = 71,
|
||||
base0B = 156,
|
||||
base0C = 170,
|
||||
base0D = 87,
|
||||
base0E = 225,
|
||||
base0F = 38,
|
||||
}
|
||||
end
|
||||
|
||||
-- Light palette is an 'inverted dark', output of 'MiniBase16.mini_palette':
|
||||
-- - Background '#e2e5ca' (LCh(uv) = 90-20-90)
|
||||
-- - Foreground '#002a83' (Lch(uv) = 15-60-250)
|
||||
-- - Accent chroma 75
|
||||
if vim.o.background == 'light' then
|
||||
palette = {
|
||||
base00 = '#e2e5ca',
|
||||
base01 = '#bcbfa4',
|
||||
base02 = '#979a7e',
|
||||
base03 = '#73765a',
|
||||
base04 = '#324490',
|
||||
base05 = '#002a83',
|
||||
base06 = '#0000e4',
|
||||
base07 = '#080500',
|
||||
base08 = '#5e2200',
|
||||
base09 = '#a86400',
|
||||
base0A = '#008818',
|
||||
base0B = '#004500',
|
||||
base0C = '#b34aad',
|
||||
base0D = '#004b76',
|
||||
base0E = '#7d0077',
|
||||
base0F = '#0086ae',
|
||||
}
|
||||
use_cterm = {
|
||||
base00 = 254,
|
||||
base01 = 250,
|
||||
base02 = 246,
|
||||
base03 = 243,
|
||||
base04 = 60,
|
||||
base05 = 18,
|
||||
base06 = 4,
|
||||
base07 = 232,
|
||||
base08 = 52,
|
||||
base09 = 130,
|
||||
base0A = 28,
|
||||
base0B = 22,
|
||||
base0C = 133,
|
||||
base0D = 24,
|
||||
base0E = 90,
|
||||
base0F = 31,
|
||||
}
|
||||
end
|
||||
|
||||
if palette then
|
||||
require('mini.base16').setup({ palette = palette, use_cterm = use_cterm })
|
||||
vim.g.colors_name = 'minischeme'
|
||||
end
|
|
@ -0,0 +1,758 @@
|
|||
==============================================================================
|
||||
------------------------------------------------------------------------------
|
||||
*mini.ai*
|
||||
*MiniAi*
|
||||
Module for extending and creating `a`/`i` textobjects. It enhances some builtin
|
||||
|text-objects| (like |a(|, |a)|, |a'|, and more), creates new ones (like `a*`, `a<Space>`,
|
||||
`af`, `a?`, and more), and allows user to create their own.
|
||||
|
||||
Features:
|
||||
- Customizable creation of `a`/`i` textobjects using Lua patterns and functions.
|
||||
Supports:
|
||||
- Dot-repeat.
|
||||
- |v:count|.
|
||||
- Different search methods (see |MiniAi.config|).
|
||||
- Consecutive application (update selection without leaving Visual mode).
|
||||
- Aliases for multiple textobjects.
|
||||
- Comprehensive builtin textobjects (see more in |MiniAi-textobject-builtin|):
|
||||
- Balanced brackets (with and without whitespace) plus alias.
|
||||
- Balanced quotes plus alias.
|
||||
- Function call.
|
||||
- Argument.
|
||||
- Tag.
|
||||
- Derived from user prompt.
|
||||
- Default for punctuation, digit, or whitespace single character.
|
||||
- Motions for jumping to left/right edge of textobject.
|
||||
- Set of specification generators to tweak some builtin textobjects (see
|
||||
|MiniAi.gen_spec|).
|
||||
- Treesitter textobjects (through |MiniAi.gen_spec.treesitter()| helper).
|
||||
|
||||
This module works by defining mappings for both `a` and `i` in Visual and
|
||||
Operator-pending mode. After typing, they wait for single character user input
|
||||
treated as textobject identifier and apply resolved textobject specification
|
||||
(fall back to other mappings if can't find proper textobject id). For more
|
||||
information see |MiniAi-textobject-specification| and |MiniAi-algorithm|.
|
||||
|
||||
Known issues which won't be resolved:
|
||||
- Search for builtin textobjects is done mostly using Lua patterns
|
||||
(regex-like approach). Certain amount of false positives is to be expected.
|
||||
- During search for builtin textobjects there is no distinction if it is
|
||||
inside string or comment. For example, in the following case there will
|
||||
be wrong match for a function call: `f(a = ")", b = 1)`.
|
||||
|
||||
General rule of thumb: any instrument using available parser for document
|
||||
structure (like treesitter) will usually provide more precise results. This
|
||||
module has builtins mostly for plain text textobjects which are useful
|
||||
most of the times (like "inside brackets", "around quotes/underscore", etc.).
|
||||
For advanced use cases define function specification for custom textobjects.
|
||||
|
||||
What it doesn't (and probably won't) do:
|
||||
- Have special operators to specially handle whitespace (like `I` and `A`
|
||||
in 'targets.vim'). Whitespace handling is assumed to be done inside
|
||||
textobject specification (like `i(` and `i)` handle whitespace differently).
|
||||
|
||||
# Setup~
|
||||
|
||||
This module needs a setup with `require('mini.ai').setup({})` (replace
|
||||
`{}` with your `config` table). It will create global Lua table `MiniAi`
|
||||
which you can use for scripting or manually (with `:lua MiniAi.*`).
|
||||
|
||||
See |MiniAi.config| for available config settings.
|
||||
|
||||
You can override runtime config settings (like `config.custom_textobjects`)
|
||||
locally to buffer inside `vim.b.miniai_config` which should have same structure
|
||||
as `MiniAi.config`. See |mini.nvim-buffer-local-config| for more details.
|
||||
|
||||
# Comparisons~
|
||||
|
||||
- 'wellle/targets.vim':
|
||||
- Has limited support for creating own textobjects: it is constrained
|
||||
to pre-defined detection rules. 'mini.ai' allows creating own rules
|
||||
via Lua patterns and functions (see |MiniAi-textobject-specification|).
|
||||
- Doesn't provide any programmatical API for getting information about
|
||||
textobjects. 'mini.ai' does it via |MiniAi.find_textobject()|.
|
||||
- Has no implementation of "moving to edge of textobject". 'mini.ai'
|
||||
does it via |MiniAi.move_cursor()| and `g[` and `g]` default mappings.
|
||||
- Has elaborate ways to control searching of the next textobject.
|
||||
'mini.ai' relies on handful of 'config.search_method'.
|
||||
- Implements `A`, `I` operators. 'mini.ai' does not by design: it is
|
||||
assumed to be a property of textobject, not operator.
|
||||
- Doesn't implement "function call" and "user prompt" textobjects.
|
||||
'mini.ai' does (with `f` and `?` identifiers).
|
||||
- Has limited support for "argument" textobject. Although it works in
|
||||
most situations, it often misdetects commas as argument separator
|
||||
(like if it is inside quotes or `{}`). 'mini.ai' deals with these cases.
|
||||
- 'nvim-treesitter/nvim-treesitter-textobjects':
|
||||
- Along with textobject functionality provides a curated and maintained
|
||||
set of popular textobject queries for many languages (which can power
|
||||
|MiniAi.gen_spec.treesitter()| functionality).
|
||||
- Operates with custome treesitter directives (see
|
||||
|lua-treesitter-directives|) allowing more fine-tuned textobjects.
|
||||
- Implements only textobjects based on treesitter.
|
||||
- Doesn't support |v:count|.
|
||||
- Doesn't support multiple search method (basically, only 'cover').
|
||||
- Doesn't support consecutive application of target textobject.
|
||||
|
||||
# Disabling~
|
||||
|
||||
To disable, set `g:miniai_disable` (globally) or `b:miniai_disable`
|
||||
(for a buffer) to `v:true`. Considering high number of different scenarios
|
||||
and customization intentions, writing exact rules for disabling module's
|
||||
functionality is left to user. See |mini.nvim-disabling-recipes| for common
|
||||
recipes.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAi-textobject-builtin*
|
||||
Builtin textobjects~
|
||||
|
||||
This table describes all builtin textobjects along with what they
|
||||
represent. Explanation:
|
||||
- `Key` represents the textobject identifier: single character which should
|
||||
be typed after `a`/`i`.
|
||||
- `Name` is a description of textobject.
|
||||
- `Example line` contains a string for which examples are constructed. The
|
||||
`*` denotes the cursor position.
|
||||
- `a`/`i` describe inclusive region representing `a` and `i` textobjects.
|
||||
Use numbers in separators for easier navigation.
|
||||
- `2a`/`2i` describe either `2a`/`2i` (support for |v:count|) textobjects
|
||||
or `a`/`i` textobject followed by another `a`/`i` textobject (consecutive
|
||||
application leads to incremental selection).
|
||||
|
||||
Example: typing `va)` with cursor on `*` leads to selection from column 2
|
||||
to column 12. Another typing `a)` changes selection to [1; 13]. Also, besides
|
||||
visual selection, any |operator| can be used or `g[`/`g]` motions to move
|
||||
to left/right edge of `a` textobject.
|
||||
>
|
||||
|Key| Name | Example line | a | i | 2a | 2i |
|
||||
|---|---------------|-1234567890123456-|--------|--------|--------|--------|
|
||||
| ( | Balanced () | (( *a (bb) )) | | | | |
|
||||
| [ | Balanced [] | [[ *a [bb] ]] | [2;12] | [4;10] | [1;13] | [2;12] |
|
||||
| { | Balanced {} | {{ *a {bb} }} | | | | |
|
||||
| < | Balanced <> | << *a <bb> >> | | | | |
|
||||
|---|---------------|-1234567890123456-|--------|--------|--------|--------|
|
||||
| ) | Balanced () | (( *a (bb) )) | | | | |
|
||||
| ] | Balanced [] | [[ *a [bb] ]] | | | | |
|
||||
| } | Balanced {} | {{ *a {bb} }} | [2;12] | [3;11] | [1;13] | [2;12] |
|
||||
| > | Balanced <> | << *a <bb> >> | | | | |
|
||||
| b | Alias for | [( *a {bb} )] | | | | |
|
||||
| | ), ], or } | | | | | |
|
||||
|---|---------------|-1234567890123456-|--------|--------|--------|--------|
|
||||
| " | Balanced " | "*a" " bb " | | | | |
|
||||
| ' | Balanced ' | '*a' ' bb ' | | | | |
|
||||
| ` | Balanced ` | `*a` ` bb ` | [1;4] | [2;3] | [6;11] | [7;10] |
|
||||
| q | Alias for | '*a' " bb " | | | | |
|
||||
| | ", ', or ` | | | | | |
|
||||
|---|---------------|-1234567890123456-|--------|--------|--------|--------|
|
||||
| ? | User prompt | e*e o e o o | [3;5] | [4;4] | [7;9] | [8;8] |
|
||||
| |(typed e and o)| | | | | |
|
||||
|---|---------------|-1234567890123456-|--------|--------|--------|--------|
|
||||
| t | Tag | <x>*</x><y>b</y> | [1;8] | [4;4] | [9;16] |[12;12] |
|
||||
|---|---------------|-1234567890123456-|--------|--------|--------|--------|
|
||||
| f | Function call | f(a, g(*b, c) ) | [6;13] | [8;12] | [1;15] | [3;14] |
|
||||
|---|---------------|-1234567890123456-|--------|--------|--------|--------|
|
||||
| a | Argument | f(*a, g(b, c) ) | [3;5] | [3;4] | [5;14] | [7;13] |
|
||||
|---|---------------|-1234567890123456-|--------|--------|--------|--------|
|
||||
| | Default | | | | | |
|
||||
| | (digits, | aa_*b__cc___ | [4;7] | [4;5] | [8;12] | [8;9] |
|
||||
| | punctuation, | (example for _) | | | | |
|
||||
| | or whitespace)| | | | | |
|
||||
|---|---------------|-1234567890123456-|--------|--------|--------|--------|
|
||||
<
|
||||
Notes:
|
||||
- All examples assume default `config.search_method`.
|
||||
- Open brackets differ from close brackets by how they treat inner edge
|
||||
whitespace for `i` textobject: open ignores it, close - includes.
|
||||
- Default textobject is activated for identifiers from digits (0, ..., 9),
|
||||
punctuation (like `_`, `*`, `,`, etc.), whitespace (space, tab, etc.).
|
||||
They are designed to be treated as separators, so include only right edge
|
||||
in `a` textobject. To include both edges, use custom textobjects
|
||||
(see |MiniAi-textobject-specification| and |MiniAi.config|).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAi-glossary*
|
||||
- REGION - table representing region in a buffer. Fields: <from> and
|
||||
<to> for inclusive start and end positions (<to> might be `nil` to
|
||||
describe empty region). Each position is also a table with line <line>
|
||||
and column <col> (both start at 1). Examples:
|
||||
- `{ from = { line = 1, col = 1 }, to = { line = 2, col = 1 } }`
|
||||
- `{ from = { line = 10, col = 10 } }` - empty region.
|
||||
- PATTERN - string describing Lua pattern.
|
||||
- SPAN - interval inside a string (end-exclusive). Like [1, 5). Equal
|
||||
`from` and `to` edges describe empty span at that point.
|
||||
- SPAN `A = [a1, a2)` COVERS `B = [b1, b2)` if every element of
|
||||
`B` is within `A` (`a1 <= b < a2`).
|
||||
It also is described as B IS NESTED INSIDE A.
|
||||
- NESTED PATTERN - array of patterns aimed to describe nested spans.
|
||||
- SPAN MATCHES NESTED PATTERN if there is a sequence of consecutively
|
||||
nested spans each matching corresponding pattern within substring of
|
||||
previous span (or input string for first span). Example:
|
||||
Nested patterns: `{ '%b()', '^. .* .$' }` (balanced `()` with inner space)
|
||||
Input string: `( ( () ( ) ) )`
|
||||
`123456789012345`
|
||||
Here are all matching spans [1, 15) and [3, 13). Both [5, 7) and [8, 10)
|
||||
match first pattern but not second. All other combinations of `(` and `)`
|
||||
don't match first pattern (not balanced).
|
||||
- COMPOSED PATTERN: array with each element describing possible pattern
|
||||
(or array of them) at that place. Composed pattern basically defines all
|
||||
possible combinations of nested pattern (their cartesian product).
|
||||
Examples:
|
||||
1. Composed pattern: `{ { '%b()', '%b[]' }, '^. .* .$' }`
|
||||
Composed pattern expanded into equivalent array of nested patterns:
|
||||
`{ '%b()', '^. .* .$' }` and `{ '%b[]', '^. .* .$' }`
|
||||
Description: either balanced `()` or balanced `[]` but both with
|
||||
inner edge space.
|
||||
2. Composed pattern:
|
||||
`{ { { '%b()', '^. .* .$' }, { '%b[]', '^.[^ ].*[^ ].$' } }, '.....' }`
|
||||
Composed pattern expanded into equivalent array of nested patterns:
|
||||
`{ '%b()', '^. .* .$', '.....' }` and
|
||||
`{ '%b[]', '^.[^ ].*[^ ].$', '.....' }`
|
||||
Description: either "balanced `()` with inner edge space" or
|
||||
"balanced `[]` with no inner edge space", both with 5 or more characters.
|
||||
- SPAN MATCHES COMPOSED PATTERN if it matches at least one nested pattern
|
||||
from expanded composed pattern.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAi-textobject-specification*
|
||||
Textobject specification has a structure of composed pattern (see
|
||||
|MiniAi-glossary|) with two differences:
|
||||
- Last pattern(s) should have even number of empty capture groups denoting
|
||||
how the last string should be processed to extract `a` or `i` textobject:
|
||||
- Zero captures mean that whole string represents both `a` and `i`.
|
||||
Example: `xxx` will define textobject matching string `xxx` literally.
|
||||
- Two captures represent `i` textobject inside of them. `a` - whole string.
|
||||
Example: `x()x()x` defines `a` textobject to be `xxx`, `i` - middle `x`.
|
||||
- Four captures define `a` textobject inside captures 1 and 4, `i` -
|
||||
inside captures 2 and 3. Example: `x()()x()x()` defines `a`
|
||||
textobject to be last `xx`, `i` - middle `x`.
|
||||
- Allows callable objects (see |vim.is_callable()|) in certain places
|
||||
(enables more complex textobjects in exchange of increase in configuration
|
||||
complexity and computations):
|
||||
- If specification itself is a callable, it will be called with the same
|
||||
arguments as |MiniAi.find_textobject()| and should return one of:
|
||||
- Composed pattern. Useful for implementing user input. Example of
|
||||
simplified variant of textobject for function call with name taken
|
||||
from user prompt:
|
||||
>
|
||||
function()
|
||||
local left_edge = vim.pesc(vim.fn.input('Function name: '))
|
||||
return { string.format('%s+%%b()', left_edge), '^.-%(().*()%)$' }
|
||||
end
|
||||
<
|
||||
- Single output region. Useful to allow full control over
|
||||
textobject. Will be taken as is. Example of returning whole buffer:
|
||||
>
|
||||
function()
|
||||
local from = { line = 1, col = 1 }
|
||||
local to = {
|
||||
line = vim.fn.line('$'),
|
||||
col = math.max(vim.fn.getline('$'):len(), 1)
|
||||
}
|
||||
return { from = from, to = to }
|
||||
end
|
||||
<
|
||||
- Array of output region(s). Useful for incorporating other
|
||||
instruments, like treesitter (see |MiniAi.gen_spec.treesitter()|).
|
||||
The best region will be picked in the same manner as with composed
|
||||
pattern (respecting options `n_lines`, `search_method`, etc.).
|
||||
Example of selecting "best" line with display width more than 80:
|
||||
>
|
||||
function(_, _, _)
|
||||
local res = {}
|
||||
for i = 1, vim.api.nvim_buf_line_count(0) do
|
||||
local cur_line = vim.fn.getline(i)
|
||||
if vim.fn.strdisplaywidth(cur_line) > 80 then
|
||||
local region = {
|
||||
from = { line = i, col = 1 },
|
||||
to = { line = i, col = cur_line:len() },
|
||||
}
|
||||
table.insert(res, region)
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
<
|
||||
- If there is a callable instead of assumed string pattern, it is expected
|
||||
to have signature `(line, init)` and behave like `pattern:find()`.
|
||||
It should return two numbers representing span in `line` next after
|
||||
or at `init` (`nil` if there is no such span).
|
||||
!IMPORTANT NOTE!: it means that output's `from` shouldn't be strictly
|
||||
to the left of `init` (it will lead to infinite loop). Not allowed as
|
||||
last item (as it should be pattern with captures).
|
||||
Example of matching only balanced parenthesis with big enough width:
|
||||
>
|
||||
{
|
||||
'%b()',
|
||||
function(s, init)
|
||||
if init > 1 or s:len() < 5 then return end
|
||||
return 1, s:len()
|
||||
end,
|
||||
'^.().*().$'
|
||||
}
|
||||
>
|
||||
More examples:
|
||||
- See |MiniAi.gen_spec| for function wrappers to create commonly used
|
||||
textobject specifications.
|
||||
|
||||
- Pair of balanced brackets from set (used for builtin `b` identifier):
|
||||
`{ { '%b()', '%b[]', '%b{}' }, '^.().*().$' }`
|
||||
|
||||
- Imitate word ignoring digits and punctuation (supports only Latin alphabet):
|
||||
`{ '()()%f[%w]%w+()[ \t]*()' }`
|
||||
|
||||
- Word with camel case support (also supports only Latin alphabet):
|
||||
`{`
|
||||
`{`
|
||||
`'%u[%l%d]+%f[^%l%d]',`
|
||||
`'%f[%S][%l%d]+%f[^%l%d]',`
|
||||
`'%f[%P][%l%d]+%f[^%l%d]',`
|
||||
`'^[%l%d]+%f[^%l%d]',`
|
||||
`},`
|
||||
`'^().*()$'`
|
||||
`}`
|
||||
|
||||
- Number: `{ '%f[%d]%d+' }`
|
||||
|
||||
- Date in 'YYYY-MM-DD' format: `{ '()%d%d%d%d%-%d%d%-%d%d()' }`
|
||||
|
||||
- Lua block string: `{ '%[%[().-()%]%]' }`
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAi-algorithm*
|
||||
Algorithm design
|
||||
|
||||
Search for the textobjects relies on these principles:
|
||||
- It uses same input data as described in |MiniAi.find_textobject()|,
|
||||
i.e. whether it is `a` or `i` textobject, its identifier, reference region, etc.
|
||||
- Textobject specification is constructed based on textobject identifier
|
||||
(see |MiniAi-textobject-specification|).
|
||||
- General search is done by converting some 2d buffer region (neighborhood
|
||||
of reference region) into 1d string (each line is appended with `\n`).
|
||||
Then search for a best span matching textobject specification is done
|
||||
inside string (see |MiniAi-glossary|). After that, span is converted back
|
||||
into 2d region. Note: first search is done inside reference region lines,
|
||||
and only after that - inside its neighborhood within `config.n_lines`
|
||||
(see |MiniAi.config|).
|
||||
- The best matching span is chosen by iterating over all spans matching
|
||||
textobject specification and comparing them with "current best".
|
||||
Comparison also depends on reference region (tighter covering is better,
|
||||
otherwise closer is better) and search method (if span is even considered).
|
||||
- Extract span based on extraction pattern (last item in nested pattern).
|
||||
- If task is to perform a consecutive search (`opts.n_times` is greater than 1),
|
||||
steps are repeated with current best match becoming reference region.
|
||||
One such additional step is also done if final region is equal to
|
||||
reference region (this enables consecutive application).
|
||||
|
||||
Notes:
|
||||
- Iteration over all matched spans is done in depth-first fashion with
|
||||
respect to nested pattern.
|
||||
- It is guaranteed that span is compared only once.
|
||||
- For the sake of increasing functionality, during iteration over all
|
||||
matching spans, some Lua patterns in composed pattern are handled
|
||||
specially.
|
||||
- `%bxx` (`xx` is two identical characters). It denotes balanced pair
|
||||
of identical characters and results into "paired" matches. For
|
||||
example, `%b""` for `"aa" "bb"` would match `"aa"` and `"bb"`, but
|
||||
not middle `" "`.
|
||||
- `x.-y` (`x` and `y` are different strings). It results only in matches with
|
||||
smallest width. For example, `e.-o` for `e e o o` will result only in
|
||||
middle `e o`. Note: it has some implications for when parts have
|
||||
quantifiers (like `+`, etc.), which usually can be resolved with
|
||||
frontier pattern `%f[]` (see examples in |MiniAi-textobject-specification|).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAi.setup()*
|
||||
`MiniAi.setup`({config})
|
||||
Module setup
|
||||
|
||||
Parameters~
|
||||
{config} `(table|nil)` Module config table. See |MiniAi.config|.
|
||||
|
||||
Usage~
|
||||
`require('mini.ai').setup({})` (replace `{}` with your `config` table)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAi.config*
|
||||
`MiniAi.config`
|
||||
Module config
|
||||
|
||||
Default values:
|
||||
>
|
||||
MiniAi.config = {
|
||||
-- Table with textobject id as fields, textobject specification as values.
|
||||
-- Also use this to disable builtin textobjects. See |MiniAi.config|.
|
||||
custom_textobjects = nil,
|
||||
|
||||
-- Module mappings. Use `''` (empty string) to disable one.
|
||||
mappings = {
|
||||
-- Main textobject prefixes
|
||||
around = 'a',
|
||||
inside = 'i',
|
||||
|
||||
-- Next/last textobjects
|
||||
around_next = 'an',
|
||||
inside_next = 'in',
|
||||
around_last = 'al',
|
||||
inside_last = 'il',
|
||||
|
||||
-- Move cursor to corresponding edge of `a` textobject
|
||||
goto_left = 'g[',
|
||||
goto_right = 'g]',
|
||||
},
|
||||
|
||||
-- Number of lines within which textobject is searched
|
||||
n_lines = 50,
|
||||
|
||||
-- How to search for object (first inside current line, then inside
|
||||
-- neighborhood). One of 'cover', 'cover_or_next', 'cover_or_prev',
|
||||
-- 'cover_or_nearest', 'next', 'previous', 'nearest'.
|
||||
search_method = 'cover_or_next',
|
||||
}
|
||||
<
|
||||
# Options ~
|
||||
|
||||
## Custom textobjects ~
|
||||
|
||||
Each named entry of `config.custom_textobjects` is a textobject with
|
||||
that identifier and specification (see |MiniAi-textobject-specification|).
|
||||
They are also used to override builtin ones (|MiniAi-textobject-builtin|).
|
||||
Supply non-table input to disable builtin textobject. Examples:
|
||||
>
|
||||
require('mini.ai').setup({
|
||||
custom_textobjects = {
|
||||
-- Disables function call textobject
|
||||
f = false,
|
||||
-- Tweaks argument textobject
|
||||
a = require('mini.ai').gen_spec.argument({ brackets = { '%b()' } }),
|
||||
-- Now `vax` should select `xxx` and `vix` - middle `x`
|
||||
x = { 'x()x()x' },
|
||||
-- Whole buffer
|
||||
g = function()
|
||||
local from = { line = 1, col = 1 }
|
||||
local to = {
|
||||
line = vim.fn.line('$'), col = vim.fn.getline('$'):len()
|
||||
}
|
||||
return { from = from, to = to }
|
||||
end
|
||||
}
|
||||
})
|
||||
|
||||
-- Use `vim.b.miniai_config` to customize per buffer
|
||||
-- Example of specification useful for Markdown files:
|
||||
local spec_pair = require('mini.ai').gen_spec.pair
|
||||
vim.b.miniai_config = {
|
||||
custom_textobjects = {
|
||||
['*'] = spec_pair('*', '*', { type = 'greedy' }),
|
||||
['_'] = spec_pair('_', '_', { type = 'greedy' }),
|
||||
},
|
||||
}
|
||||
<
|
||||
There are more example specifications in |MiniAi-textobject-specification|.
|
||||
|
||||
## Search method~
|
||||
|
||||
Value of `config.search_method` defines how best match search is done.
|
||||
Based on its value, one of the following matches will be selected:
|
||||
- Covering match. Left/right edge is before/after left/right edge of
|
||||
reference region.
|
||||
- Previous match. Left/right edge is before left/right edge of reference
|
||||
region.
|
||||
- Next match. Left/right edge is after left/right edge of reference region.
|
||||
- Nearest match. Whichever is closest among previous and next matches.
|
||||
|
||||
Possible values are:
|
||||
- `'cover'` - use only covering match. Don't use either previous or
|
||||
next; report that there is no textobject found.
|
||||
- `'cover_or_next'` (default) - use covering match. If not found, use next.
|
||||
- `'cover_or_prev'` - use covering match. If not found, use previous.
|
||||
- `'cover_or_nearest'` - use covering match. If not found, use nearest.
|
||||
- `'next'` - use next match.
|
||||
- `'previous'` - use previous match.
|
||||
- `'nearest'` - use nearest match.
|
||||
|
||||
Note: search is first performed on the reference region lines and only
|
||||
after failure - on the whole neighborhood defined by `config.n_lines`. This
|
||||
means that with `config.search_method` not equal to `'cover'`, "previous"
|
||||
or "next" textobject will end up as search result if they are found on
|
||||
first stage although covering match might be found in bigger, whole
|
||||
neighborhood. This design is based on observation that most of the time
|
||||
operation is done within reference region lines (usually cursor line).
|
||||
|
||||
Here is an example of what `a)` textobject is based on a value of
|
||||
`'config.search_method'` when cursor is inside `bbb` word:
|
||||
- `'cover'`: `(a) bbb (c)` -> none
|
||||
- `'cover_or_next'`: `(a) bbb (c)` -> `(c)`
|
||||
- `'cover_or_prev'`: `(a) bbb (c)` -> `(a)`
|
||||
- `'cover_or_nearest'`: depends on cursor position.
|
||||
For first and second `b` - as in `cover_or_prev` (as previous match is
|
||||
nearer), for third - as in `cover_or_next` (as next match is nearer).
|
||||
- `'next'`: `(a) bbb (c)` -> `(c)`. Same outcome for `(bbb)`.
|
||||
- `'prev'`: `(a) bbb (c)` -> `(a)`. Same outcome for `(bbb)`.
|
||||
- `'nearest'`: depends on cursor position (same as in `'cover_or_nearest'`).
|
||||
|
||||
## Mappings~
|
||||
|
||||
Mappings `around_next`/`inside_next` and `around_last`/`inside_last` are
|
||||
essentially `around`/`inside` but using search method `'next'` and `'prev'`.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAi.find_textobject()*
|
||||
`MiniAi.find_textobject`({ai_type}, {id}, {opts})
|
||||
Find textobject region
|
||||
|
||||
Parameters~
|
||||
{ai_type} `(string)` One of `'a'` or `'i'`.
|
||||
{id} `(string)` Single character string representing textobject id. It is
|
||||
used to get specification which is later used to compute textobject region.
|
||||
Note: if specification is a function, it is called with all present
|
||||
arguments (`opts` is populated with default arguments).
|
||||
{opts} `(table|nil)` Options. Possible fields:
|
||||
- <n_lines> - Number of lines within which textobject is searched.
|
||||
Default: `config.n_lines` (see |MiniAi.config|).
|
||||
- <n_times> - Number of times to perform a consecutive search. Each one
|
||||
is done with reference region being previous found textobject region.
|
||||
Default: 1.
|
||||
- <reference_region> - region to try to cover (see |MiniAi-glossary|). It
|
||||
is guaranteed that output region will not be inside or equal to this one.
|
||||
Default: empty region at cursor position.
|
||||
- <search_method> - Search method. Default: `config.search_method`.
|
||||
|
||||
Return~
|
||||
`(table|nil)` Region of textobject or `nil` if no textobject different
|
||||
from `opts.reference_region` was consecutively found `opts.n_times` times.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAi.move_cursor()*
|
||||
`MiniAi.move_cursor`({side}, {ai_type}, {id}, {opts})
|
||||
Move cursor to edge of textobject
|
||||
|
||||
Parameters~
|
||||
{side} `(string)` One of `'left'` or `'right'`.
|
||||
{ai_type} `(string)` One of `'a'` or `'i'`.
|
||||
{id} `(string)` Single character string representing textobject id.
|
||||
{opts} `(table|nil)` Same as in |MiniAi.find_textobject()|.
|
||||
`opts.n_times` means number of *actual* jumps (important when cursor
|
||||
already on the potential jump spot).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAi.gen_spec*
|
||||
`MiniAi.gen_spec`
|
||||
Generate common textobject specifications
|
||||
|
||||
This is a table with function elements. Call to actually get specification.
|
||||
|
||||
Example: >
|
||||
local gen_spec = require('mini.ai').gen_spec
|
||||
require('mini.ai').setup({
|
||||
custom_textobjects = {
|
||||
-- Tweak argument to be recognized only inside `()` between `;`
|
||||
a = gen_spec.argument({ brackets = { '%b()' }, separators = { ';' } }),
|
||||
|
||||
-- Tweak function call to not detect dot in function name
|
||||
f = gen_spec.function_call({ name_pattern = '[%w_]' }),
|
||||
|
||||
-- Function definition (needs treesitter queries with these captures)
|
||||
F = gen_spec.treesitter({ a = '@function.outer', i = '@function.inner' }),
|
||||
|
||||
-- Make `|` select both edges in non-balanced way
|
||||
['|'] = gen_spec.pair('|', '|', { type = 'non-balanced' }),
|
||||
}
|
||||
})
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAi.gen_spec.argument()*
|
||||
`MiniAi.gen_spec.argument`({opts})
|
||||
Argument specification
|
||||
|
||||
Argument textobject (has default `a` identifier) is a region inside
|
||||
balanced bracket between allowed not excluded separators. Use this function
|
||||
to tweak how it works.
|
||||
|
||||
Examples:
|
||||
- `argument({ brackets = { '%b()' } })` will search for an argument only
|
||||
inside balanced `()`.
|
||||
- `argument({ separators = { ',', ';' } })` will consider both `,` and `;`
|
||||
to be separators.
|
||||
- `argument({ exclude_regions = { '%b()' } })` will exclude separators
|
||||
which are inside balanced `()` (inside outer brackets).
|
||||
|
||||
Parameters~
|
||||
{opts} `(table|nil)` Options. Allowed fields:
|
||||
- <brackets> - table with patterns for outer balanced brackets.
|
||||
Default: `{ '%b()', '%b[]', '%b{}' }` (any `()`, `[]`, or `{}` can
|
||||
enclose arguments).
|
||||
- <separators> - table with single character separators.
|
||||
Default: `{ ',' }` (arguments are separated with `,`).
|
||||
- <exclude_regions> - table with patterns for regions inside which
|
||||
separators will be ignored.
|
||||
Default: `{ '%b""', "%b''", '%b()', '%b[]', '%b{}' }` (separators
|
||||
inside balanced quotes or brackets are ignored).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAi.gen_spec.function_call()*
|
||||
`MiniAi.gen_spec.function_call`({opts})
|
||||
Function call specification
|
||||
|
||||
Function call textobject (has default `f` identifier) is a region with some
|
||||
characters followed by balanced `()`. Use this function to tweak how it works.
|
||||
|
||||
Example:
|
||||
- `function_call({ name_pattern = '[%w_]' })` will recognize function name with
|
||||
only alphanumeric or underscore (not dot).
|
||||
|
||||
Parameters~
|
||||
{opts} `(table|nil)` Optsion. Allowed fields:
|
||||
- <name_pattern> - string pattern of character set allowed in function name.
|
||||
Default: `'[%w_%.]'` (alphanumeric, underscore, or dot).
|
||||
Note: should be enclosed in `[]`.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAi.gen_spec.pair()*
|
||||
`MiniAi.gen_spec.pair`({left}, {right}, {opts})
|
||||
Pair specification
|
||||
|
||||
Use it to define textobject for region surrounded with `left` from left and
|
||||
`right` from right. The `a` textobject includes both edges, `i` - excludes them.
|
||||
|
||||
Region can be one of several types (controlled with `opts.type`). All
|
||||
examples are for default search method, `a` textobject, and use `'_'` as
|
||||
both `left` and `right`:
|
||||
- Non-balanced (`{ type = 'non-balanced' }`), default. Equivalent to using
|
||||
`x.-y` as first pattern. Example: on line '_a_b_c_' it consecutively
|
||||
matches '_a_', '_b_', '_c_'.
|
||||
- Balanced (`{ type = 'balanced' }`). Equivalent to using `%bxy` as first
|
||||
pattern. Example: on line '_a_b_c_' it consecutively matches '_a_', '_c_'.
|
||||
Note: both `left` and `right` should be single character.
|
||||
- Greedy (`{ type = 'greedy' }`). Like non-balanced but will select maximum
|
||||
consecutive `left` and `right` edges. Example: on line '__a__b_' it
|
||||
consecutively selects '__a__' and '__b_'. Note: both `left` and `right`
|
||||
should be single character.
|
||||
|
||||
Parameters~
|
||||
{left} `(string)` Left edge.
|
||||
{right} `(string)` Right edge.
|
||||
{opts} `(table|nil)` Options. Possible fields:
|
||||
- <type> - Type of a pair. One of `'non-balanced'` (default), `'balanced'`,
|
||||
`'greedy'`.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAi.gen_spec.treesitter()*
|
||||
`MiniAi.gen_spec.treesitter`({ai_captures}, {opts})
|
||||
Treesitter specification
|
||||
|
||||
This is a specification in function form. When called with a pair of
|
||||
treesitter captures, it returns a specification function outputting an
|
||||
array of regions that match corresponding (`a` or `i`) capture.
|
||||
|
||||
In order for this to work, apart from working treesitter parser for desired
|
||||
language, user should have a reachable language-specific 'textobjects'
|
||||
query (see |get_query()|). The most straightforward way for this is to have
|
||||
'textobjects.scm' query file with treesitter captures stored in some
|
||||
recognized path. This is primarily designed to be compatible with
|
||||
'nvim-treesitter/nvim-treesitter-textobjects' plugin, but can be used
|
||||
without it.
|
||||
|
||||
Two most common approaches for having a query file:
|
||||
- Install 'nvim-treesitter/nvim-treesitter-textobjects'. It has curated and
|
||||
well maintained builtin query files for many languages with a standardized
|
||||
capture names, like `function.outer`, `function.inner`, etc.
|
||||
- Manually create file 'after/queries/<language name>/textobjects.scm' in
|
||||
your |$XDG_CONFIG_HOME| directory. It should contain queries with
|
||||
captures (later used to define textobjects). See |lua-treesitter-query|.
|
||||
To verify that query file is reachable, run (example for "lua" language)
|
||||
`:lua print(vim.inspect(vim.treesitter.get_query_files('lua', 'textobjects')))`
|
||||
(output should have at least an intended file).
|
||||
|
||||
Example configuration for function definition textobject with
|
||||
'nvim-treesitter/nvim-treesitter-textobjects' captures:
|
||||
>
|
||||
local spec_treesitter = require('mini.ai').gen_spec.treesitter
|
||||
require('mini.ai').setup({
|
||||
custom_textobjects = {
|
||||
F = spec_treesitter({ a = '@function.outer', i = '@function.inner' }),
|
||||
o = spec_treesitter({
|
||||
a = { '@conditional.outer', '@loop.outer' },
|
||||
i = { '@conditional.inner', '@loop.inner' },
|
||||
})
|
||||
}
|
||||
})
|
||||
>
|
||||
|
||||
Notes:
|
||||
- By default query is done using 'nvim-treesitter' plugin if it is present
|
||||
(falls back to builtin methods otherwise). This allows for a more
|
||||
advanced features (like multiple buffer languages, custom directives, etc.).
|
||||
See `opts.use_nvim_treesitter` for how to disable this.
|
||||
- It uses buffer's |filetype| to determine query language.
|
||||
- On large files it is slower than pattern-based textobjects. Still very
|
||||
fast though (one search should be magnitude of milliseconds or tens of
|
||||
milliseconds on really large file).
|
||||
|
||||
Parameters~
|
||||
{ai_captures} `(table)` Captures for `a` and `i` textobjects: table with
|
||||
<a> and <i> fields with captures for `a` and `i` textobjects respectively.
|
||||
Each value can be either a string capture (should start with `'@'`) or an
|
||||
array of such captures (best among all matches will be chosen).
|
||||
{opts} `(table)` Options. Possible values:
|
||||
- <use_nvim_treesitter> - whether to try to use 'nvim-treesitter' plugin
|
||||
(if present) to do the query. It implements more advanced behavior at
|
||||
cost of increased execution time. Provides more coherent experience if
|
||||
'nvim-treesitter-textobjects' queries are used. Default: `true`.
|
||||
|
||||
Return~
|
||||
`(function)` Function with |MiniAi.find_textobject()| signature which
|
||||
returns array of current buffer regions representing matches for
|
||||
corresponding (`a` or `i`) treesitter capture.
|
||||
|
||||
See also~
|
||||
|MiniAi-textobject-specification| for how this type of textobject
|
||||
specification is processed.
|
||||
|get_query()| for how query is fetched in case of no 'nvim-treesitter'.
|
||||
|Query:iter_captures()| for how all query captures are iterated in case of
|
||||
no 'nvim-treesitter'.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAi.select_textobject()*
|
||||
`MiniAi.select_textobject`({ai_type}, {id}, {opts})
|
||||
Visually select textobject region
|
||||
|
||||
Does nothing if no region is found.
|
||||
|
||||
Parameters~
|
||||
{ai_type} `(string)` One of `'a'` or `'i'`.
|
||||
{id} `(string)` Single character string representing textobject id.
|
||||
{opts} `(table|nil)` Same as in |MiniAi.find_textobject()|. Extra fields:
|
||||
- <vis_mode> - One of `'v'`, `'V'`, `'<C-v>'`. Default: Latest visual mode.
|
||||
- <operator_pending> - Whether selection is for Operator-pending mode.
|
||||
Used in that mode's mappings, shouldn't be used directly. Default: `false`.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAi.expr_textobject()*
|
||||
`MiniAi.expr_textobject`({mode}, {ai_type}, {opts})
|
||||
Make expression to visually select textobject
|
||||
|
||||
Designed to be used inside expression mapping. No need to use directly.
|
||||
|
||||
Textobject identifier is taken from user single character input.
|
||||
Default `n_times` option is taken from |v:count1|.
|
||||
|
||||
Parameters~
|
||||
{mode} `(string)` One of 'x' (Visual) or 'o' (Operator-pending).
|
||||
{ai_type} `(string)` One of `'a'` or `'i'`.
|
||||
{opts} `(table|nil)` Same as in |MiniAi.select_textobject()|.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAi.expr_motion()*
|
||||
`MiniAi.expr_motion`({side})
|
||||
Make expression for moving cursor to edge of textobject
|
||||
|
||||
Designed to be used inside expression mapping (powers `config.goto_left`
|
||||
and `config.goto_right` mappings). No need to use directly.
|
||||
|
||||
Textobject identifier is taken from user single character input.
|
||||
Default `n_times` option is taken from |v:count1|.
|
||||
|
||||
Parameters~
|
||||
{side} `(string)` One of `'left'` or `'right'`.
|
||||
|
||||
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
|
@ -0,0 +1,912 @@
|
|||
==============================================================================
|
||||
------------------------------------------------------------------------------
|
||||
*mini.align*
|
||||
*MiniAlign*
|
||||
Align text interactively (with or without instant preview). Allows rich and
|
||||
flexible customization of both alignment rules and user interaction. Works
|
||||
with charwise, linewise, and blockwise selections in both Normal mode (on
|
||||
textobject/motion; with dot-repeat) and Visual mode.
|
||||
|
||||
Features:
|
||||
- Alignment is done in three main steps:
|
||||
- <Split> lines into parts based on Lua pattern(s) or user-supplied rule.
|
||||
- <Justify> parts for certain side(s) to be same width inside columns.
|
||||
- <Merge> parts to be lines, with customizable delimiter(s).
|
||||
Each main step can be preceded by other steps (pre-steps) to achieve
|
||||
highly customizable outcome. See `steps` value in |MiniAlign.config|. For
|
||||
more details, see |MiniAlign-glossary| and |MiniAlign-algorithm|.
|
||||
- User can control alignment interactively by pressing customizable modifiers
|
||||
(single keys representing how alignment steps and/or options should change).
|
||||
Some of default modifiers:
|
||||
- Press `s` to enter split Lua pattern.
|
||||
- Press `j` to choose justification side from available ones ("left",
|
||||
"center", "right", "none").
|
||||
- Press `m` to enter merge delimiter.
|
||||
- Press `f` to enter filter Lua expression to configure which parts
|
||||
will be affected (like "align only first column").
|
||||
- Press `i` to ignore some commonly unwanted split matches.
|
||||
- Press `p` to pair neighboring parts so they be aligned together.
|
||||
- Press `t` to trim whitespace from parts.
|
||||
- Press `<BS>` (backspace) to delete some last pre-step.
|
||||
For more details, see |MiniAlign-modifiers-builtin| and |MiniAlign-examples|.
|
||||
- Alignment can be done with instant preview (result is updated after each
|
||||
modifier) or without it (result is shown and accepted after non-default
|
||||
split pattern is set).
|
||||
- Every user interaction is accompanied with helper status message showing
|
||||
relevant information about current alignment process.
|
||||
|
||||
# Setup~
|
||||
|
||||
This module needs a setup with `require('mini.align').setup({})` (replace
|
||||
`{}` with your `config` table). It will create global Lua table `MiniAlign`
|
||||
which you can use for scripting or manually (with `:lua MiniAlign.*`).
|
||||
|
||||
See |MiniAlign.config| for available config settings.
|
||||
|
||||
You can override runtime config settings (like `config.modifiers`) locally
|
||||
to buffer inside `vim.b.minialign_config` which should have same structure
|
||||
as `MiniAlign.config`. See |mini.nvim-buffer-local-config| for more details.
|
||||
|
||||
# Comparisons~
|
||||
|
||||
- 'junegunn/vim-easy-align':
|
||||
- 'mini.align' is mostly designed after 'junegunn/vim-easy-align', so
|
||||
there are a lot of similarities.
|
||||
- Both plugins allow users to change alignment options interactively by
|
||||
pressing modifier keys (albeit completely different default ones).
|
||||
'junegunn/vim-easy-align' has those modifiers fixed, while 'mini.align'
|
||||
allows their full customization. See |MiniAlign.config| for examples.
|
||||
- 'junegunn/vim-easy-align' is designed to treat delimiters differently
|
||||
than other parts of strings. 'mini.align' doesn't distinguish split
|
||||
parts from one another by design: splitting is allowed to be done
|
||||
based on some other logic than by splitting on delimiters.
|
||||
- 'junegunn/vim-easy-align' initially aligns by only first delimiter.
|
||||
'mini.align' initially aligns by all delimiter.
|
||||
- 'junegunn/vim-easy-align' implements special filtering by delimiter
|
||||
row number. 'mini.align' has builtin filtering based on Lua code
|
||||
supplied by user in modifier phase. See |MiniAlign.gen_step.filter|
|
||||
and 'f' builtin modifier.
|
||||
- 'mini.align' treats any non-registered modifier as a plain delimiter
|
||||
pattern, while 'junegunn/vim-easy-align' does not.
|
||||
- 'mini.align' exports core Lua function used for aligning strings
|
||||
(|MiniAlign.align_strings()|).
|
||||
- 'godlygeek/tabular':
|
||||
- 'godlygeek/tabular' is mostly designed around single command which is
|
||||
customized by printing its parameters. 'mini.align' implements
|
||||
different concept of interactive alignment through pressing
|
||||
customizable single character modifiers.
|
||||
- 'godlygeek/tabular' can detect region upon which alignment can be
|
||||
desirable. 'mini.align' does not by design: use Visual selection or
|
||||
textobject/motion to explicitly define region to align.
|
||||
|
||||
# Disabling~
|
||||
|
||||
To disable, set `g:minialign_disable` (globally) or `b:minialign_disable`
|
||||
(for a buffer) to `v:true`. Considering high number of different scenarios
|
||||
and customization intentions, writing exact rules for disabling module's
|
||||
functionality is left to user. See |mini.nvim-disabling-recipes| for common
|
||||
recipes.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAlign-glossary*
|
||||
Glossary
|
||||
|
||||
PARTS 2d array of strings (array of arrays of strings).
|
||||
See more in |MiniAlign.as_parts()|.
|
||||
|
||||
ROW First-level array of parts (like `parts[1]`).
|
||||
|
||||
COLUMN Array of strings, constructed from parts elements with the same
|
||||
second-level index (like `{ parts[1][1],` `parts[2][1], ... }`).
|
||||
|
||||
STEP A named callable. See |MiniAlign.new_step()|. When used in terms of
|
||||
alignment steps, callable takes two arguments: some object (parts
|
||||
or string array) and option table.
|
||||
|
||||
SPLIT Process of taking array of strings and converting it into parts.
|
||||
|
||||
JUSTIFY Process of taking parts and converting them to aligned parts (all
|
||||
elements have same widths inside columns).
|
||||
|
||||
MERGE Process of taking parts and converting it back to array of strings.
|
||||
Usually by concatenating rows into strings.
|
||||
|
||||
REGION Table representing region in a buffer. Fields: <from> and <to> for
|
||||
inclusive start and end positions (<to> might be `nil` to describe
|
||||
empty region). Each position is also a table with line <line> and
|
||||
column <col> (both start at 1).
|
||||
|
||||
MODE Either charwise ("char", `v`, |charwise|), linewise ("line", `V`,
|
||||
|linewise|) or blockwise ("block", `<C-v>`, |blockwise-visual|)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAlign-algorithm*
|
||||
Algorithm design
|
||||
|
||||
There are two main processes implemented in 'mini.align': strings alignment
|
||||
and interactive region alignment. See |MiniAlign-glossary| for more information
|
||||
about used terms.
|
||||
|
||||
Strings alignment ~
|
||||
|
||||
Main implementation is in |MiniAlign.align_strings()|. Its input is array of
|
||||
strings and output - array of aligned strings. The process consists from three
|
||||
main steps (split, justify, merge) which can be preceded by any number of
|
||||
preliminary steps (pre-split, pre-justify, pre-merge).
|
||||
|
||||
Algorithm:
|
||||
- <Pre-split>. Take input array of strings and consecutively apply all
|
||||
pre-split steps (`steps.pre_split`). Each one has `(strings, opts)` signature
|
||||
and should modify array in place.
|
||||
- <Split>. Take array of strings and convert it to parts with `steps.split()`.
|
||||
It has `(strings, opts)` signature and should return parts.
|
||||
- <Pre-justify>. Take parts and consecutively apply all pre-justify
|
||||
steps (`steps.pre_justify`). Each one has `(parts, opts)` signature and
|
||||
should modify parts in place.
|
||||
- <Justify>. Take parts and apply `steps.justify()`. It has `(parts, opts)`
|
||||
signature and should modify parts in place.
|
||||
- <Pre-merge>. Take parts and consecutively apply all pre-merge
|
||||
steps (`steps.pre_merge`). Each one has `(parts, opts)` signature and
|
||||
should modify parts in place.
|
||||
- <Merge>. Take parts and convert it to array of strings with `steps.merge()`.
|
||||
It has `(parts, opts)` signature and should return array of strings.
|
||||
|
||||
Notes:
|
||||
- All table objects are initially copied so that modification in place doesn't
|
||||
affect workflow.
|
||||
- Default main steps are designed to be controlled via options. See
|
||||
|MiniAlign.align_strings()| and default step entries in |MiniAlign.gen_step|.
|
||||
- All steps are guaranteed to take same option table as second argument.
|
||||
This allows steps to "talk" to each other, i.e. earlier steps can pass data
|
||||
to later ones.
|
||||
|
||||
Interactive region alignment ~
|
||||
|
||||
Interactive alignment is a main entry point for most users. It can be done
|
||||
in two flavors:
|
||||
- <Without preview>. Initiated via mapping defined in `start` of
|
||||
`MiniAlign.config.mappings`. Alignment is accepted once split pattern becomes
|
||||
non-default.
|
||||
- <With preview>. Initiated via mapping defined in `start_with_preview` of
|
||||
`MiniAlign.config.mappings`. Alignment result is shown after every modifier
|
||||
and is accepted after `<CR>` (`Enter`) is hit. Note: each preview is done by
|
||||
applying current alignment steps and options to the initial region lines,
|
||||
not the ones currently displaying in preview.
|
||||
|
||||
Lifecycle (assuming default mappings):
|
||||
- <Initiate alignment>:
|
||||
- In Normal mode type `ga` (or `gA` to show preview) followed by textobject
|
||||
or motion defining region to be aligned.
|
||||
- In Visual mode select region and type `ga` (or `gA` to show preview).
|
||||
Strings contained in selected region will be used as input to
|
||||
|MiniAlign.align_strings()|.
|
||||
Beware of mode when selecting region: charwise (`v`), linewise (`V`), or
|
||||
blockwise (`<C-v>`). They all behave differently.
|
||||
- <Press modifiers>. Press single keys one at a time:
|
||||
- If pressed key is among table keys of `modifiers` table of
|
||||
|MiniAlign.config|, its function value is executed. It usually modifies
|
||||
some options(s) and/or affects some pre-step(s).
|
||||
- If pressed key is not among defined modifiers, it is treated as plain
|
||||
split pattern.
|
||||
This process can either end by itself (usually in case of no preview and
|
||||
non-default split pattern being set) or you can choose to end it manually.
|
||||
- <Accept or discard>. In case of active preview, accept current result by
|
||||
pressing `<CR>`. Discard any result and return to initial regions with
|
||||
either `<Esc>` or `<C-c>`.
|
||||
|
||||
See more in |MiniAlign-modifiers-builtin| and |MiniAlign-examples|.
|
||||
|
||||
Notes:
|
||||
- Visual blockwise selection works best with 'virtualedit' equal to "block"
|
||||
or "all".
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAlign-modifiers-builtin*
|
||||
Overview of builtin modifiers
|
||||
|
||||
All examples assume interactive alignment with preview in linewise mode. With
|
||||
default mappings, use `V` to select lines and `gA` to initiate alignment. It
|
||||
might be helpful to copy lines into modifiable buffer and experiment yourself.
|
||||
|
||||
Notes:
|
||||
- Any pressed key which doesn't have defined modifier will be treated as
|
||||
plain split pattern.
|
||||
- All modifiers can be customized inside |MiniAlign.setup|. See "Modifiers"
|
||||
section of |MiniAlign.config|.
|
||||
|
||||
Main option modifiers ~
|
||||
|
||||
<s> Enter split pattern (confirm prompt by pressing `<CR>`). Input is treated
|
||||
as plain delimiter.
|
||||
|
||||
Before: >
|
||||
a-b-c
|
||||
aa-bb-cc
|
||||
<
|
||||
After typing `s-<CR>`: >
|
||||
a -b -c
|
||||
aa-bb-cc
|
||||
<
|
||||
<j> Choose justify side. Prompts user (with helper message) to type single
|
||||
character identifier of side: `l`eft, `c`enter, `r`ight, `n`one.
|
||||
|
||||
Before: >
|
||||
a_b_c
|
||||
aa_bb_cc
|
||||
<
|
||||
After typing `_jr` (first make split by `_`): >
|
||||
a_ b_ c
|
||||
aa_bb_cc
|
||||
<
|
||||
<m> Enter merge delimiter (confirm prompt by pressing `<CR>`).
|
||||
|
||||
Before: >
|
||||
a_b_c
|
||||
aa_bb_cc
|
||||
<
|
||||
After typing `_m--<CR>` (first make split by `_`): >
|
||||
a --_--b --_--c
|
||||
aa--_--bb--_--cc
|
||||
<
|
||||
Modifiers adding pre-steps ~
|
||||
|
||||
<f> Enter filter expression. See more details in |MiniAlign.gen_step.filter()|.
|
||||
|
||||
Before: >
|
||||
a_b_c
|
||||
aa_bb_cc
|
||||
<
|
||||
After typing `_fn==1<CR>` (first make split by `_`): >
|
||||
a _b_c
|
||||
aa_bb_cc
|
||||
<
|
||||
<i> Ignore some split matches. It modifies `split_exclude_patterns` option by
|
||||
adding commonly wanted patterns. See more details in
|
||||
|MiniAlign.gen_step.ignore_split()|.
|
||||
|
||||
Before: >
|
||||
/* This_is_assumed_to_be_comment */
|
||||
a"_"_b
|
||||
aa_bb
|
||||
<
|
||||
After typing `_i` (first make split by `_`): >
|
||||
/* This_is_assumed_to_be_comment */
|
||||
a"_"_b
|
||||
aa _bb
|
||||
<
|
||||
<p> Pair neighboring parts.
|
||||
|
||||
Before: >
|
||||
a_b_c
|
||||
aaa_bbb_ccc
|
||||
<
|
||||
After typing `_p` (first make split by `_`): >
|
||||
a_ b_ c
|
||||
aaa_bbb_ccc
|
||||
<
|
||||
<t> Trim parts from whitespace on both sides (keeping indentation).
|
||||
|
||||
Before: >
|
||||
a _ b _ c
|
||||
aa _bb _cc
|
||||
<
|
||||
After typing `_t` (first make split by `_`): >
|
||||
a _b _c
|
||||
aa_bb_cc
|
||||
<
|
||||
Delete some last pre-step ~
|
||||
|
||||
<BS> Delete one of the pre-steps. If there is only one kind of pre-steps,
|
||||
remove its latest added one. If not, prompt user to choose pre-step kind
|
||||
by entering single character: `s`plit, `j`ustify, `m`erge.
|
||||
|
||||
Examples:
|
||||
- `tp<BS>` results in only "trim" step to be left.
|
||||
- `it<BS>` prompts to choose which step to delete (pre-split or
|
||||
pre-justify in this case).
|
||||
|
||||
Special configurations for common splits ~
|
||||
|
||||
<=> Use special pattern to align by a group of consecutive "=". It can be
|
||||
preceded by any number of punctuation marks and followed by some sommon
|
||||
punctuation characters. Trim whitespace and merge with single space.
|
||||
|
||||
Before: >
|
||||
a=b
|
||||
aa<=bb
|
||||
aaa===bbb
|
||||
aaaa = cccc
|
||||
<
|
||||
After typing `=`: >
|
||||
a = b
|
||||
aa <= bb
|
||||
aaa === bbb
|
||||
aaaa = cccc
|
||||
<
|
||||
<,> Besides splitting by "," character, trim whitespace, pair neighboring
|
||||
parts and merge with single space.
|
||||
|
||||
Before: >
|
||||
a,b
|
||||
aa,bb
|
||||
aaa , bbb
|
||||
<
|
||||
After typing `,`: >
|
||||
a, b
|
||||
aa, bb
|
||||
aaa, bbb
|
||||
<
|
||||
< > (Space bar) Squash consecutive whitespace into single single space (accept
|
||||
possible indentation) and split by `%s+` pattern (keeps indentation).
|
||||
|
||||
Before: >
|
||||
a b c
|
||||
aa bb cc
|
||||
<
|
||||
After typing `<Space>`: >
|
||||
a b c
|
||||
aa bb cc
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAlign-examples*
|
||||
More complex examples to explore functionality
|
||||
|
||||
Copy lines in modifiable buffer, initiate alignment with preview (`gAip`)
|
||||
and try typing suggested key sequences.
|
||||
These are modified examples taken from 'junegunn/vim-easy-align'.
|
||||
|
||||
Equal sign ~
|
||||
|
||||
Lines:
|
||||
|
||||
# This=is=assumed=to be a comment
|
||||
"a ="
|
||||
a =
|
||||
a = 1
|
||||
bbbb = 2
|
||||
ccccccc = 3
|
||||
ccccccccccccccc
|
||||
ddd = 4
|
||||
eeee === eee = eee = eee=f
|
||||
fff = ggg += gg &&= gg
|
||||
g != hhhhhhhh == 888
|
||||
i := 5
|
||||
i %= 5
|
||||
i *= 5
|
||||
j =~ 5
|
||||
j >= 5
|
||||
aa => 123
|
||||
aa <<= 123
|
||||
aa >>= 123
|
||||
bbb => 123
|
||||
c => 1233123
|
||||
d => 123
|
||||
dddddd &&= 123
|
||||
dddddd ||= 123
|
||||
dddddd /= 123
|
||||
gg <=> ee
|
||||
|
||||
Key sequences:
|
||||
- `=`
|
||||
- `=jc`
|
||||
- `=jr`
|
||||
- `=m!<CR>`
|
||||
- `=p`
|
||||
- `=i` (execute `:lua vim.o.commentstring = '# %s'` for full experience)
|
||||
- `=<BS>`
|
||||
- `=<BS>p`
|
||||
- `=fn==1<CR>`
|
||||
- `=<BS>fn==1<CR>t`
|
||||
- `=frow>7<CR>`
|
||||
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAlign.setup()*
|
||||
`MiniAlign.setup`({config})
|
||||
Module setup
|
||||
|
||||
Parameters~
|
||||
{config} `(table|nil)` Module config table. See |MiniAlign.config|.
|
||||
|
||||
Usage~
|
||||
`require('mini.align').setup({})` (replace `{}` with your `config` table)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAlign.config*
|
||||
`MiniAlign.config`
|
||||
Module config
|
||||
|
||||
Default values:
|
||||
>
|
||||
MiniAlign.config = {
|
||||
-- Module mappings. Use `''` (empty string) to disable one.
|
||||
mappings = {
|
||||
start = 'ga',
|
||||
start_with_preview = 'gA',
|
||||
},
|
||||
|
||||
-- Modifiers changing alignment steps and/or options
|
||||
modifiers = {
|
||||
-- Main option modifiers
|
||||
['s'] = --<function: enter split pattern>,
|
||||
['j'] = --<function: choose justify side>,
|
||||
['m'] = --<function: enter merge delimiter>,
|
||||
|
||||
-- Modifiers adding pre-steps
|
||||
['f'] = --<function: filter parts by entering Lua expression>,
|
||||
['i'] = --<function: ignore some split matches>,
|
||||
['p'] = --<function: pair parts>,
|
||||
['t'] = --<function: trim parts>,
|
||||
|
||||
-- Delete some last pre-step
|
||||
['<BS>'] = --<function: delete some last pre-step>,
|
||||
|
||||
-- Special configurations for common splits
|
||||
['='] = --<function: enhanced setup for '='>,
|
||||
[','] = --<function: enhanced setup for ','>,
|
||||
[' '] = --<function: enhanced setup for ' '>,
|
||||
},
|
||||
|
||||
-- Default options controlling alignment process
|
||||
options = {
|
||||
split_pattern = '',
|
||||
justify_side = 'left',
|
||||
merge_delimiter = '',
|
||||
},
|
||||
|
||||
-- Default steps performing alignment (if `nil`, default is used)
|
||||
steps = {
|
||||
pre_split = {},
|
||||
split = nil,
|
||||
pre_justify = {},
|
||||
justify = nil,
|
||||
pre_merge = {},
|
||||
merge = nil,
|
||||
},
|
||||
}
|
||||
<
|
||||
# Options ~
|
||||
|
||||
## Modifiers ~
|
||||
|
||||
`MiniAlign.config.modifiers` is used to define interactive user experience
|
||||
of managing alignment process. It is a table with single character keys and
|
||||
modifier function values.
|
||||
|
||||
Each modifier function:
|
||||
- Is called when corresponding modifier key is pressed.
|
||||
- Has signature `(steps, opts)` and should modify any of its input in place.
|
||||
|
||||
Examples:
|
||||
- Modifier function used for default 'i' modifier:
|
||||
>
|
||||
function(steps, _)
|
||||
table.insert(steps.pre_split, MiniAlign.gen_step.ignore_split())
|
||||
end
|
||||
<
|
||||
- Tweak 't' modifier to use highest indentation instead of keeping it:
|
||||
>
|
||||
require('mini.align').setup({
|
||||
t = function(steps, _)
|
||||
table.insert(steps.pre_justify, MiniAlign.gen_step.trim('both', 'high'))
|
||||
end
|
||||
})
|
||||
<
|
||||
- Tweak `j` modifier to cycle through available "justify_side" option
|
||||
values (like in 'junegunn/vim-easy-align'):
|
||||
>
|
||||
require('mini.align').setup({
|
||||
modifiers = {
|
||||
j = function(_, opts)
|
||||
local next_option = ({
|
||||
left = 'center', center = 'right', right = 'none', none = 'left',
|
||||
})[opts.justify_side]
|
||||
opts.justify_side = next_option or 'left'
|
||||
end,
|
||||
},
|
||||
})
|
||||
<
|
||||
## Options ~
|
||||
|
||||
`MiniAlign.config.options` defines default values of options used to control
|
||||
behavior of steps.
|
||||
|
||||
Examples:
|
||||
- Set `justify_side = 'center'` to center align at initialization.
|
||||
|
||||
For more details about options see |MiniAlign.align_strings()| and entries of
|
||||
|MiniAlign.gen_step| for default main steps.
|
||||
|
||||
## Steps ~
|
||||
|
||||
`MiniAlign.config.steps` defines default steps to be applied during
|
||||
alignment process.
|
||||
|
||||
Examples:
|
||||
- Align by default only first pair of columns:
|
||||
>
|
||||
local align = require('mini.align')
|
||||
align.setup({
|
||||
steps = {
|
||||
pre_justify = { align.gen_step.filter('n == 1') }
|
||||
},
|
||||
})
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAlign.align_strings()*
|
||||
`MiniAlign.align_strings`({strings}, {opts}, {steps})
|
||||
Align strings
|
||||
|
||||
For details about alignment process see |MiniAlign-algorithm|.
|
||||
|
||||
Parameters~
|
||||
{strings} `(table)` Array of strings.
|
||||
{opts} `(table|nil)` Options. Its copy will be passed to steps as second
|
||||
argument. Extended with `MiniAlign.config.options`.
|
||||
This is a place to control default main steps:
|
||||
- `opts.split_pattern` - Lua pattern(s) used to make split parts.
|
||||
- `opts.split_exclude_patterns` - which split matches should be ignored.
|
||||
- `opts.justify_side` - which direction(s) alignment should be done.
|
||||
- `opts.justify_offsets` - offsets tweaking width of first column
|
||||
- `opts.merge_delimiter` - which delimiter(s) to use when merging.
|
||||
For more information see |MiniAlign.gen_step| entry for corresponding
|
||||
default step.
|
||||
{steps} `(table|nil)` Steps. Extended with `MiniAlign.config.steps`.
|
||||
Possible `nil` values are replaced with corresponding default steps:
|
||||
- `split` - |MiniAlign.gen_step.default_split()|.
|
||||
- `justify` - |MiniAlign.gen_step.default_justify()|.
|
||||
- `merge` - |MiniAlign.gen_step.default_merge()|.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAlign.align_user()*
|
||||
`MiniAlign.align_user`({mode})
|
||||
Align current region with user-supplied steps
|
||||
|
||||
Mostly designed to be used inside mappings.
|
||||
|
||||
Will use |MiniAlign.align_strings()| and set the following options in `opts`:
|
||||
- `justify_offsets` - array of offsets used to achieve actual alignment of
|
||||
a region. It is non-trivial (not array of zeros) only for charwise
|
||||
selection: offset of first string is computed as width of prefix to the
|
||||
left of region start.
|
||||
- `region` - current affected region (see |MiniAlign-glossary|). Can be
|
||||
used to create more advanced steps.
|
||||
- `mode` - mode of selection (see |MiniAlign-glossary|).
|
||||
|
||||
Parameters~
|
||||
{mode} `(string)` Selection mode. One of "char", "line", "block".
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAlign.action_normal()*
|
||||
`MiniAlign.action_normal`({with_preview})
|
||||
Perfrom action in Normal mode
|
||||
|
||||
Used in Normal mode mapping. No need to use it directly.
|
||||
|
||||
Parameters~
|
||||
{with_preview} `(boolean|nil)` Whether to align with live preview.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAlign.action_visual()*
|
||||
`MiniAlign.action_visual`({with_preview})
|
||||
Perfrom action in Visual mode
|
||||
|
||||
Used in Visual mode mapping. No need to use it directly.
|
||||
|
||||
Parameters~
|
||||
{with_preview} `(boolean|nil)` Whether to align with live preview.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAlign.as_parts()*
|
||||
`MiniAlign.as_parts`({arr2d})
|
||||
Convert 2d array of strings to parts
|
||||
|
||||
This function verifies if input is a proper 2d array of strings and adds
|
||||
methods to its copy.
|
||||
|
||||
Class~
|
||||
{parts}
|
||||
|
||||
Fields~
|
||||
{apply} `(function)` Takes callable `f` and applies it to every part.
|
||||
Callable should have signature `(s, data)`: `s` is a string part,
|
||||
`data` - table with its data (<row> has row number, <col> has column number).
|
||||
Returns new 2d array.
|
||||
|
||||
{apply_inplace} `(function)` Takes callable `f` and applies it to every part.
|
||||
Should have same signature as in `apply` method. Outputs (should all be
|
||||
strings) are assigned in place to a corresponding parts element. Returns
|
||||
parts itself to enable method chaining.
|
||||
|
||||
{get_dims} `(function)` Return dimensions of parts array: a table with
|
||||
<row> and <col> keys having number of rows and number of columns (maximum
|
||||
number of elements across all rows).
|
||||
|
||||
{group} `(function)` Concatenate neighboring strings based on supplied
|
||||
boolean mask and direction (one of "left", default, or "right"). Has
|
||||
signature `(mask, direction)` and modifies parts in place. Returns parts
|
||||
itself to enable method chaining.
|
||||
Example:
|
||||
- Parts: { { "a", "b", "c" }, { "d", "e" }, { "f" } }
|
||||
- Mask: { { false, false, true }, { true, false }, { false } }
|
||||
- Result for direction "left": { { "abc" }, { "d", "e" }, { "f" } }
|
||||
- Result for direction "right": { { "ab","c" }, { "de" }, { "f" } }
|
||||
|
||||
{pair} `(function)` Concatenate neighboring element pairs. Takes
|
||||
`direction` as input (one of "left", default, or "right") and applies
|
||||
`group()` for an alternating mask.
|
||||
Example:
|
||||
- Parts: { { "a", "b", "c" }, { "d", "e" }, { "f" } }
|
||||
- Result for direction "left": { { "ab", "c" }, { "de" }, { "f" } }
|
||||
- Result for direction "right": { { "a", "bc" }, { "de" }, { "f" } }
|
||||
|
||||
{slice_col} `(function)` Return column with input index `j`. Note: it might
|
||||
not be an array if rows have unequal number of columns.
|
||||
|
||||
{slice_row} `(function)` Return row with input index `i`.
|
||||
|
||||
{trim} `(function)` Trim elements whitespace. Has signature `(direction, indent)`
|
||||
and modifies parts in place. Returns parts itself to enable method chaining.
|
||||
- Possible values of `direction`: "both" (default), "left", "right",
|
||||
"none". Defines from which side whitespaces should be removed.
|
||||
- Possible values of `indent`: "keep" (default), "low", "high", "remove".
|
||||
Defines what to do with possible indent (left whitespace of first string
|
||||
in a row). Value "keep" keeps it; "low" makes all indent equal to the
|
||||
lowest across rows; "high" - highest across rows; "remove" - removes indent.
|
||||
|
||||
Usage~
|
||||
>
|
||||
parts = MiniAlign.as_parts({ { 'a', 'b' }, { 'c' } })
|
||||
print(vim.inspect(parts.get_dims())) -- Should be { row = 2, col = 2 }
|
||||
|
||||
parts.apply_inplace(function(s, data)
|
||||
return ' ' .. data.row .. s .. data.col .. ' '
|
||||
end)
|
||||
print(vim.inspect(parts)) -- Should be { { ' 1a1 ', ' 1b2 ' }, { ' 2c1 ' } }
|
||||
|
||||
parts.trim('both', 'remove').pair()
|
||||
print(vim.inspect(parts)) -- Should be { { '1a11b2' }, { '2c1' } }
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAlign.new_step()*
|
||||
`MiniAlign.new_step`({name}, {action})
|
||||
Create step
|
||||
|
||||
A step is basically a named callable object. Having a name bundled with
|
||||
some action powers helper status message during interactive alignment process.
|
||||
|
||||
Parameters~
|
||||
{name} `(string)` Step name.
|
||||
{action} `(function|table)` Step action. Should be a callable object
|
||||
(see |vim.is_callable()|).
|
||||
|
||||
Return~
|
||||
`(table)` A table with keys: <name> with `name` argument, <action> with `action`.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAlign.gen_step*
|
||||
`MiniAlign.gen_step`
|
||||
Generate common action steps
|
||||
|
||||
This is a table with function elements. Call to actually get step.
|
||||
|
||||
Each step action is a function that has signature `(object, opts)`, where
|
||||
`object` is either parts or array of strings (depends on which stage of
|
||||
alignment process it is assumed to be applied) and `opts` is table of options.
|
||||
|
||||
Outputs of elements named `default_*` are used as default corresponding main
|
||||
step (split, justify, merge). Behavior of all of them depend on values from
|
||||
supplied options (second argument).
|
||||
|
||||
Outputs of other elements depend on both step generator input values and
|
||||
options supplied at execution. This design is mostly because their output
|
||||
can be used several times in pre-steps.
|
||||
|
||||
Usage~
|
||||
>
|
||||
local align = require('mini.align')
|
||||
align.setup({
|
||||
modifiers = {
|
||||
-- Use 'T' modifier to remove both whitespace and indent
|
||||
T = function(steps, _)
|
||||
table.insert(steps.pre_justify, align.gen_step.trim('both', 'remove'))
|
||||
end,
|
||||
},
|
||||
options = {
|
||||
-- By default align "right", "left", "right", "left", ...
|
||||
justify_side = { 'right', 'left' },
|
||||
},
|
||||
steps = {
|
||||
-- Align by default only first pair of columns
|
||||
pre_justify = { align.gen_step.filter('n == 1') },
|
||||
},
|
||||
})
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAlign.gen_step.default_split()*
|
||||
`MiniAlign.gen_step.default_split`()
|
||||
Generate default split step
|
||||
|
||||
Output splits strings using matches of Lua pattern(s) from `split_pattern`
|
||||
option which are not dismissed by `split_exclude_patterns` option.
|
||||
|
||||
Outline of how single string is split:
|
||||
- Convert `split_pattern` option to array of strings (string is converted
|
||||
as one-element array). This array will be recycled in case there are more
|
||||
split matches than in converted `split_pattern` array (which almost always).
|
||||
- Find all forbidden spans (intervals inside string) - all matches of all
|
||||
patterns in `split_exclude_patterns`.
|
||||
- Find match for the next pattern. If it is not inside any forbidden span,
|
||||
add preceding unmatched substring and matched split as two parts. Repeat
|
||||
with the next pattern.
|
||||
- If no pattern match is found, add the rest of string as final part.
|
||||
|
||||
Output uses following options (as part second argument, `opts` table):
|
||||
- <split_pattern> - string or array of strings used to detect split matches
|
||||
and create parts. Default: `''` meaning no matches (whole string is used
|
||||
as part). Examples: `'%s+'`, `{ '<', '>' }`.
|
||||
- <split_exclude_patterns> - array of strings defining which regions to
|
||||
exclude from being matched. Default: `{}`. Examples: `{ '".-"', '^%s*#.*' }`.
|
||||
|
||||
Return~
|
||||
`(table)` A step named "split" and with appropriate callable action.
|
||||
|
||||
See also~
|
||||
|MiniAlign.gen_step.ignore_split()| heavily uses `split_exclude_patterns`.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAlign.gen_step.default_justify()*
|
||||
`MiniAlign.gen_step.default_justify`()
|
||||
Generate default justify step
|
||||
|
||||
Output makes column elements of string parts have equal width by adding
|
||||
left and/or right whitespace padding. Which side(s) to pad is defined by
|
||||
`justify_side` option. Width of first column can be tweaked with `justify_offsets`
|
||||
option.
|
||||
|
||||
Outline of how parts are justified:
|
||||
- Convert `justify_side` option to array of strings (single string is
|
||||
converted as one-element array). Recycle this array to have length equal
|
||||
to number of columns in parts.
|
||||
- For all columns compute maximum width of strings from it (add offsets from
|
||||
`justify_offsets` to first column widths). Note: for left alignment, width
|
||||
of last row element does not affect column width. This is mainly because
|
||||
it won't be padded and helps dealing with "no single match" lines.
|
||||
- Make all elements have same width inside column by adding appropriate
|
||||
amount of whitespace. Which side(s) to add is controlled by the corresponding
|
||||
`justify_side` array element. Note: padding is done with spaces which
|
||||
might conflict with tab indentation.
|
||||
|
||||
Output uses following options (as part second argument, `opts` table):
|
||||
- <justify_side> - string or array of strings. Each element can be one of
|
||||
"left" (pad right side), "center" (pad both sides equally), "right" (pad
|
||||
left side), "none" (no padding). Default: "left".
|
||||
- <justify_offsets> - array of numeric left offsets of rows. Used to adjust
|
||||
for possible not equal indents, like in case of charwise selection when
|
||||
left edge is not on the first column. Default: array of zeros. Set
|
||||
automatically during interactive alignment in charwise mode.
|
||||
|
||||
Return~
|
||||
`(table)` A step named "justify" and with appropriate callable action.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAlign.gen_step.default_merge()*
|
||||
`MiniAlign.gen_step.default_merge`()
|
||||
Generate default merge step
|
||||
|
||||
Output merges rows of parts into strings by placing merge delimiter(s)
|
||||
between them.
|
||||
|
||||
Outline of how parts are converted to array of strings:
|
||||
- Convert `merge_delimiter` option to array of strings (single string is
|
||||
converted as one-element array). Recycle this array to have length equal
|
||||
to number of columns in parts minus 1.
|
||||
- Exclude empty strings from parts. They add nothing to output except extra
|
||||
usage of merge delimiter.
|
||||
- Concatenate each row interleaving with array of merge delimiters.
|
||||
|
||||
Output uses following options (as part second argument, `opts` table):
|
||||
- <merge_delimiter> - string or array of strings. Default: `''`.
|
||||
Examples: `' '`, `{ '', ' ' }`.
|
||||
|
||||
Return~
|
||||
`(table)` A step named "merge" and with appropriate callable action.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAlign.gen_step.filter()*
|
||||
`MiniAlign.gen_step.filter`({expr})
|
||||
Generate filter step
|
||||
|
||||
Construct function predicate from supplied Lua string expression and make
|
||||
step evaluating it on every part element.
|
||||
|
||||
Outline of how filtering is done:
|
||||
- Convert Lua filtering expression into function predicate which can be
|
||||
evaluated in manually created context (some specific variables being set).
|
||||
- Compute boolean mask for parts by applying predicate to each element of
|
||||
2d array with special variables set to specific values (see next section).
|
||||
- Group parts with compted mask. See `group()` method of parts in
|
||||
|MiniAlign.as_parts()|.
|
||||
|
||||
Special variables which can be used in expression:
|
||||
- <row> - row number of current element.
|
||||
- <ROW> - total number of rows in parts.
|
||||
- <col> - column number of current element.
|
||||
- <COL> - total number of columns in current row.
|
||||
- <s> - string value of current element.
|
||||
- <n> - column pair number of current element. Useful when filtering by
|
||||
result of pattern splitting.
|
||||
- <N> - total number of column pairs in current row.
|
||||
- All variables from global table `_G`.
|
||||
|
||||
Tips:
|
||||
- This general filtering approach can be used to both include and exclude
|
||||
certain parts from alignment. Examples:
|
||||
- Use `row ~= 2` to align all parts except from second row.
|
||||
- Use `n == 1` to align only by first pair of columns.
|
||||
- Filtering by last equal sign usually can be done with `n >= (N - 1)`
|
||||
(because there is usually something to the right of it).
|
||||
|
||||
Parameters~
|
||||
{expr} `(string)` Lua expression as a string which will be used as predicate.
|
||||
|
||||
Return~
|
||||
`(table)` A step named "filter" and with appropriate callable action.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAlign.gen_step.ignore_split()*
|
||||
`MiniAlign.gen_step.ignore_split`({patterns}, {exclude_comment})
|
||||
Generate ignore step
|
||||
|
||||
Output adds certain values to `split_exclude_patterns` option. Should be
|
||||
used as pre-split step.
|
||||
|
||||
Parameters~
|
||||
{patterns} `(table)` Array of patterns to be added to
|
||||
`split_exclude_patterns` as is. Default: `{ [[".-"]] }` (excludes strings
|
||||
for most cases).
|
||||
{exclude_comment} `(boolean)` Whether to add comment pattern to
|
||||
`split_exclude_patterns`. Comment pattern is derived from 'commentstring'
|
||||
option. Default: `true`.
|
||||
|
||||
Return~
|
||||
`(table)` A step named "ignore" and with appropriate callable action.
|
||||
|
||||
See also~
|
||||
|MiniAlign.gen_step.default_split()| for details about
|
||||
`split_exclude_patterns` option.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAlign.gen_step.pair()*
|
||||
`MiniAlign.gen_step.pair`({direction})
|
||||
Generate pair step
|
||||
|
||||
Output calls `pair()` method of parts (see |MiniAlign.as_parts()|) with
|
||||
supplied `direction` argument.
|
||||
|
||||
Parameters~
|
||||
{direction} `(string)` Which direction to pair. One of "left" (default) or
|
||||
|
||||
|
||||
Return~
|
||||
`(table)` A step named "pair" and with appropriate callable action.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniAlign.gen_step.trim()*
|
||||
`MiniAlign.gen_step.trim`({direction}, {indent})
|
||||
Generate trim step
|
||||
|
||||
Output calls `trim()` method of parts (see |MiniAlign.as_parts()|) with
|
||||
supplied `direction` and `indent` arguments.
|
||||
|
||||
Parameters~
|
||||
{direction} `(string)` Which sides to trim whitespace. One of "both"
|
||||
(default), "left", "right", "none".
|
||||
{indent} `(string)` What to do with possible indent (left whitespace of first
|
||||
string in a row). One of "keep" (default), "low", "high", "remove".
|
||||
|
||||
Return~
|
||||
`(table)` A step named "trim" and with appropriate callable action.
|
||||
|
||||
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
|
@ -0,0 +1,268 @@
|
|||
==============================================================================
|
||||
------------------------------------------------------------------------------
|
||||
*mini.base16*
|
||||
*MiniBase16*
|
||||
Fast implementation of 'chriskempson/base16' color scheme (with Copyright
|
||||
(C) 2012 Chris Kempson) adapted for modern Neovim Lua plugins.
|
||||
Extra features:
|
||||
- Configurable automatic support of cterm colors (see |highlight-cterm|).
|
||||
- Opinionated palette generator based only on background and foreground
|
||||
colors.
|
||||
|
||||
Supported highlight groups:
|
||||
- Builtin-in Neovim LSP and diagnostic.
|
||||
- Plugins (either with explicit definition or by verification that default
|
||||
highlighting works appropriately):
|
||||
- 'echasnovski/mini.nvim'
|
||||
- 'akinsho/bufferline.nvim'
|
||||
- 'anuvyklack/hydra.nvim'
|
||||
- 'DanilaMihailov/beacon.nvim'
|
||||
- 'folke/todo-comments.nvim'
|
||||
- 'folke/trouble.nvim'
|
||||
- 'folke/which-key.nvim'
|
||||
- 'ggandor/leap.nvim'
|
||||
- 'ggandor/lightspeed.nvim'
|
||||
- 'glepnir/dashboard-nvim'
|
||||
- 'glepnir/lspsaga.nvim'
|
||||
- 'hrsh7th/nvim-cmp'
|
||||
- 'justinmk/vim-sneak'
|
||||
- 'kyazdani42/nvim-tree.lua'
|
||||
- 'lewis6991/gitsigns.nvim'
|
||||
- 'lukas-reineke/indent-blankline.nvim'
|
||||
- 'neoclide/coc.nvim'
|
||||
- 'nvim-lualine/lualine.nvim'
|
||||
- 'nvim-neo-tree/neo-tree.nvim'
|
||||
- 'nvim-telescope/telescope.nvim'
|
||||
- 'p00f/nvim-ts-rainbow'
|
||||
- 'phaazon/hop.nvim'
|
||||
- 'rcarriga/nvim-dap-ui'
|
||||
- 'rcarriga/nvim-notify'
|
||||
- 'rlane/pounce.nvim'
|
||||
- 'romgrk/barbar.nvim'
|
||||
- 'simrat39/symbols-outline.nvim'
|
||||
- 'stevearc/aerial.nvim'
|
||||
- 'TimUntersberger/neogit'
|
||||
- 'williamboman/mason.nvim'
|
||||
|
||||
# Setup~
|
||||
|
||||
This module needs a setup with `require('mini.base16').setup({})` (replace
|
||||
`{}` with your `config` table). It will create global Lua table
|
||||
`MiniBase16` which you can use for scripting or manually (with
|
||||
`:lua MiniBase16.*`).
|
||||
|
||||
See |MiniBase16.config| for `config` structure and default values.
|
||||
|
||||
This module doesn't have runtime options, so using `vim.b.minibase16_config`
|
||||
will have no effect here.
|
||||
|
||||
Example:
|
||||
>
|
||||
require('mini.base16').setup({
|
||||
palette = {
|
||||
base00 = '#112641',
|
||||
base01 = '#3a475e',
|
||||
base02 = '#606b81',
|
||||
base03 = '#8691a7',
|
||||
base04 = '#d5dc81',
|
||||
base05 = '#e2e98f',
|
||||
base06 = '#eff69c',
|
||||
base07 = '#fcffaa',
|
||||
base08 = '#ffcfa0',
|
||||
base09 = '#cc7e46',
|
||||
base0A = '#46a436',
|
||||
base0B = '#9ff895',
|
||||
base0C = '#ca6ecf',
|
||||
base0D = '#42f7ff',
|
||||
base0E = '#ffc4ff',
|
||||
base0F = '#00a5c5',
|
||||
},
|
||||
use_cterm = true,
|
||||
plugins = {
|
||||
default = false,
|
||||
['echasnovski/mini.nvim'] = true,
|
||||
},
|
||||
})
|
||||
<
|
||||
# Notes~
|
||||
|
||||
1. This is used to create plugin's colorschemes (see |mini.nvim-color-schemes|).
|
||||
2. Using `setup()` doesn't actually create a |colorscheme|. It basically
|
||||
creates a coordinated set of |highlight|s. To create your own theme:
|
||||
- Put "myscheme.lua" file (name after your chosen theme name) inside
|
||||
any "colors" directory reachable from 'runtimepath' ("colors" inside
|
||||
your Neovim config directory is usually enough).
|
||||
- Inside "myscheme.lua" call `require('mini.base16').setup()` with your
|
||||
palette and only after that set |g:colors_name| to "myscheme".
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*mini-color-schemes*
|
||||
# Plugin colorschemes~
|
||||
|
||||
This plugin comes with several color schemes. All of them are a
|
||||
|MiniBase16| theme created with faster version of the following Lua code:
|
||||
>
|
||||
require('mini.base16').setup({ palette = palette, use_cterm = true })
|
||||
<
|
||||
Activate them as regular |colorscheme| (for example, `:colorscheme minischeme`).
|
||||
|
||||
## minischeme~
|
||||
|
||||
Blue and yellow main colors with high contrast and saturation palette.
|
||||
Palettes are:
|
||||
- For dark 'background':
|
||||
`MiniBase16.mini_palette('#112641', '#e2e98f', 75)`
|
||||
- For light 'background':
|
||||
`MiniBase16.mini_palette('#e2e5ca', '#002a83', 75)`
|
||||
|
||||
## minicyan~
|
||||
|
||||
Cyan and grey main colors with moderate contrast and saturation palette.
|
||||
Palettes are:
|
||||
- For dark 'background':
|
||||
`MiniBase16.mini_palette('#0A2A2A', '#D0D0D0', 50)`
|
||||
- For light 'background':
|
||||
`MiniBase16.mini_palette('#C0D2D2', '#262626', 80)`
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniBase16.setup()*
|
||||
`MiniBase16.setup`({config})
|
||||
Module setup
|
||||
|
||||
Setup is done by applying base16 palette to enable colorscheme. Highlight
|
||||
groups make an extended set from original
|
||||
[base16-vim](https://github.com/chriskempson/base16-vim/) plugin. It is a
|
||||
good idea to have `config.palette` respect the original [styling
|
||||
principles](https://github.com/chriskempson/base16/blob/master/styling.md).
|
||||
|
||||
By default only 'gui highlighting' (see |highlight-gui| and
|
||||
|termguicolors|) is supported. To support 'cterm highlighting' (see
|
||||
|highlight-cterm|) supply `config.use_cterm` argument in one of the formats:
|
||||
- `true` to auto-generate from `palette` (as closest colors).
|
||||
- Table with similar structure to `palette` but having terminal colors
|
||||
(integers from 0 to 255) instead of hex strings.
|
||||
|
||||
Parameters~
|
||||
{config} `(table)` Module config table. See |MiniBase16.config|.
|
||||
|
||||
Usage~
|
||||
`require('mini.base16').setup({})` (replace `{}` with your `config`
|
||||
table; `config.palette` should be a table with colors)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniBase16.config*
|
||||
`MiniBase16.config`
|
||||
Module config
|
||||
|
||||
Default values:
|
||||
>
|
||||
MiniBase16.config = {
|
||||
-- Table with names from `base00` to `base0F` and values being strings of
|
||||
-- HEX colors with format "#RRGGBB". NOTE: this should be explicitly
|
||||
-- supplied in `setup()`.
|
||||
palette = nil,
|
||||
|
||||
-- Whether to support cterm colors. Can be boolean, `nil` (same as
|
||||
-- `false`), or table with cterm colors. See `setup()` documentation for
|
||||
-- more information.
|
||||
use_cterm = nil,
|
||||
|
||||
-- Plugin integrations. Use `default = false` to disable all integrations.
|
||||
-- Also can be set per plugin (see |MiniBase16.config|).
|
||||
plugins = { default = true },
|
||||
}
|
||||
<
|
||||
# Options ~
|
||||
|
||||
## Plugin integrations ~
|
||||
|
||||
`config.plugins` defines for which supported plugins highlight groups will
|
||||
be created. Limiting number of integrations slightly decreases startup time.
|
||||
It is a table with boolean (`true`/`false`) values which are applied as follows:
|
||||
- If plugin name (as listed in |mini.base16|) has entry, it is used.
|
||||
- Otherwise `config.plugins.default` is used.
|
||||
|
||||
Example which will load only "mini.nvim" integration:
|
||||
>
|
||||
require('mini.base16').setup({
|
||||
palette = require('mini.base16').mini_palette('#112641', '#e2e98f', 75),
|
||||
plugins = {
|
||||
default = false,
|
||||
['echasnovski/mini.nvim'] = true,
|
||||
}
|
||||
})
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniBase16.mini_palette()*
|
||||
`MiniBase16.mini_palette`({background}, {foreground}, {accent_chroma})
|
||||
Create 'mini' palette
|
||||
|
||||
Create base16 palette based on the HEX (string '#RRGGBB') colors of main
|
||||
background and foreground with optional setting of accent chroma (see
|
||||
details).
|
||||
|
||||
# Algorithm design~
|
||||
|
||||
- Main operating color space is
|
||||
[CIELCh(uv)](https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_(CIELCh))
|
||||
which is a cylindrical representation of a perceptually uniform CIELUV
|
||||
color space. It defines color by three values: lightness L (values from 0
|
||||
to 100), chroma (positive values), and hue (circular values from 0 to 360
|
||||
degress). Useful converting tool: https://www.easyrgb.com/en/convert.php
|
||||
- There are four important lightness values: background, foreground, focus
|
||||
(around the middle of background and foreground, leaning towards
|
||||
foreground), and edge (extreme lightness closest to foreground).
|
||||
- First four colors have the same chroma and hue as `background` but
|
||||
lightness progresses from background towards focus.
|
||||
- Second four colors have the same chroma and hue as `foreground` but
|
||||
lightness progresses from foreground towards edge in such a way that
|
||||
'base05' color is main foreground color.
|
||||
- The rest eight colors are accent colors which are created in pairs
|
||||
- Each pair has same hue from set of hues 'most different' to
|
||||
background and foreground hues (if respective chorma is positive).
|
||||
- All colors have the same chroma equal to `accent_chroma` (if not
|
||||
provided, chroma of foreground is used, as they will appear next
|
||||
to each other). Note: this means that in case of low foreground
|
||||
chroma, it is a good idea to set `accent_chroma` manually.
|
||||
Values from 30 (low chorma) to 80 (high chroma) are common.
|
||||
- Within pair there is base lightness (equal to foreground
|
||||
lightness) and alternative (equal to focus lightness). Base
|
||||
lightness goes to colors which will be used more frequently in
|
||||
code: base08 (variables), base0B (strings), base0D (functions),
|
||||
base0E (keywords).
|
||||
How exactly accent colors are mapped to base16 palette is a result of
|
||||
trial and error. One rule of thumb was: colors within one hue pair should
|
||||
be more often seen next to each other. This is because it is easier to
|
||||
distinguish them and seems to be more visually appealing. That is why
|
||||
`base0D` and `base0F` have same hues because they usually represent
|
||||
functions and delimiter (brackets included).
|
||||
|
||||
Parameters~
|
||||
{background} `(string)` Background HEX color (formatted as `#RRGGBB`).
|
||||
{foreground} `(string)` Foreground HEX color (formatted as `#RRGGBB`).
|
||||
{accent_chroma} `(number)` Optional positive number (usually between 0
|
||||
and 100). Default: chroma of foreground color.
|
||||
|
||||
Return~
|
||||
`(table)` Table with base16 palette.
|
||||
|
||||
Usage~
|
||||
`local palette = require('mini.base16').mini_palette('#112641', '#e2e98f', 75)`
|
||||
`require('mini.base16').setup({palette = palette})`
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniBase16.rgb_palette_to_cterm_palette()*
|
||||
`MiniBase16.rgb_palette_to_cterm_palette`({palette})
|
||||
Converts palette with RGB colors to terminal colors
|
||||
|
||||
Useful for caching `use_cterm` variable to increase speed.
|
||||
|
||||
Parameters~
|
||||
{palette} `(table)` Table with base16 palette (same as in
|
||||
`MiniBase16.config.palette`).
|
||||
|
||||
Return~
|
||||
`(table)` Table with base16 palette using |highlight-cterm|.
|
||||
|
||||
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
|
@ -0,0 +1,114 @@
|
|||
==============================================================================
|
||||
------------------------------------------------------------------------------
|
||||
*mini.bufremove*
|
||||
*MiniBufremove*
|
||||
Buffer removing (unshow, delete, wipeout), which saves window layout
|
||||
(opposite to builtin Neovim's commands).
|
||||
|
||||
# Setup~
|
||||
|
||||
This module doesn't need setup, but it can be done to improve usability.
|
||||
Setup with `require('mini.bufremove').setup({})` (replace `{}` with your
|
||||
`config` table). It will create global Lua table `MiniBufremove` which you
|
||||
can use for scripting or manually (with `:lua MiniBufremove.*`).
|
||||
|
||||
See |MiniBufremove.config| for `config` structure and default values.
|
||||
|
||||
This module doesn't have runtime options, so using `vim.b.minibufremove_config`
|
||||
will have no effect here.
|
||||
|
||||
# Notes~
|
||||
|
||||
1. Which buffer to show in window(s) after its current buffer is removed is
|
||||
decided by the algorithm:
|
||||
- If alternate buffer (see |CTRL-^|) is listed (see |buflisted()|), use it.
|
||||
- If previous listed buffer (see |bprevious|) is different, use it.
|
||||
- Otherwise create a scratch one with `nvim_create_buf(true, true)` and use
|
||||
it.
|
||||
|
||||
# Disabling~
|
||||
|
||||
To disable core functionality, set `g:minibufremove_disable` (globally) or
|
||||
`b:minibufremove_disable` (for a buffer) to `v:true`. Considering high
|
||||
number of different scenarios and customization intentions, writing exact
|
||||
rules for disabling module's functionality is left to user. See
|
||||
|mini.nvim-disabling-recipes| for common recipes.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniBufremove.setup()*
|
||||
`MiniBufremove.setup`({config})
|
||||
Module setup
|
||||
|
||||
Parameters~
|
||||
{config} `(table)` Module config table. See |MiniBufremove.config|.
|
||||
|
||||
Usage~
|
||||
`require('mini.bufremove').setup({})` (replace `{}` with your `config` table)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniBufremove.config*
|
||||
`MiniBufremove.config`
|
||||
Module config
|
||||
|
||||
Default values:
|
||||
>
|
||||
MiniBufremove.config = {
|
||||
-- Whether to set Vim's settings for buffers (allow hidden buffers)
|
||||
set_vim_settings = true,
|
||||
}
|
||||
<
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniBufremove.delete()*
|
||||
`MiniBufremove.delete`({buf_id}, {force})
|
||||
Delete buffer `buf_id` with |:bdelete| after unshowing it
|
||||
|
||||
Parameters~
|
||||
{buf_id} `(number)` Buffer identifier (see |bufnr()|) to use. Default:
|
||||
0 for current.
|
||||
{force} `(boolean)` Whether to ignore unsaved changes (using `!` version of
|
||||
command). Default: `false`.
|
||||
|
||||
Return~
|
||||
`(boolean)` Whether operation was successful.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniBufremove.wipeout()*
|
||||
`MiniBufremove.wipeout`({buf_id}, {force})
|
||||
Wipeout buffer `buf_id` with |:bwipeout| after unshowing it
|
||||
|
||||
Parameters~
|
||||
{buf_id} `(number)` Buffer identifier (see |bufnr()|) to use. Default:
|
||||
0 for current.
|
||||
{force} `(boolean)` Whether to ignore unsaved changes (using `!` version of
|
||||
command). Default: `false`.
|
||||
|
||||
Return~
|
||||
`(boolean)` Whether operation was successful.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniBufremove.unshow()*
|
||||
`MiniBufremove.unshow`({buf_id})
|
||||
Stop showing buffer `buf_id` in all windows
|
||||
|
||||
Parameters~
|
||||
{buf_id} `(number)` Buffer identifier (see |bufnr()|) to use. Default:
|
||||
0 for current.
|
||||
|
||||
Return~
|
||||
`(boolean)` Whether operation was successful.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniBufremove.unshow_in_window()*
|
||||
`MiniBufremove.unshow_in_window`({win_id})
|
||||
Stop showing current buffer of window `win_id`
|
||||
|
||||
Parameters~
|
||||
{win_id} `(number)` Window identifier (see |win_getid()|) to use.
|
||||
Default: 0 for current.
|
||||
|
||||
Return~
|
||||
`(boolean)` Whether operation was successful.
|
||||
|
||||
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
|
@ -0,0 +1,137 @@
|
|||
==============================================================================
|
||||
------------------------------------------------------------------------------
|
||||
*mini.comment*
|
||||
*MiniComment*
|
||||
Fast and familiar per-line commenting. Commenting in Normal mode respects
|
||||
|count| and is dot-repeatable. Comment structure is inferred from
|
||||
'commentstring'. Handles both tab and space indenting (but not when they
|
||||
are mixed). Allows custom hooks before and after successful commenting.
|
||||
|
||||
What it doesn't do:
|
||||
- Block and sub-line comments. This will only support per-line commenting.
|
||||
- Configurable (from module) comment structure. Modify |commentstring|
|
||||
instead. To enhance support for commenting in multi-language files, see
|
||||
"JoosepAlviste/nvim-ts-context-commentstring" plugin along with `hooks`
|
||||
option of this module (see |MiniComment.config|).
|
||||
- Handle indentation with mixed tab and space.
|
||||
- Preserve trailing whitespace in empty lines.
|
||||
|
||||
# Setup~
|
||||
|
||||
This module needs a setup with `require('mini.comment').setup({})` (replace
|
||||
`{}` with your `config` table). It will create global Lua table
|
||||
`MiniComment` which you can use for scripting or manually (with
|
||||
`:lua MiniComment.*`).
|
||||
|
||||
See |MiniComment.config| for `config` structure and default values.
|
||||
|
||||
You can override runtime config settings locally to buffer inside
|
||||
`vim.b.minicomment_config` which should have same structure as
|
||||
`MiniComment.config`. See |mini.nvim-buffer-local-config| for more details.
|
||||
|
||||
# Disabling~
|
||||
|
||||
To disable core functionality, set `g:minicomment_disable` (globally) or
|
||||
`b:minicomment_disable` (for a buffer) to `v:true`. Considering high number
|
||||
of different scenarios and customization intentions, writing exact rules
|
||||
for disabling module's functionality is left to user. See
|
||||
|mini.nvim-disabling-recipes| for common recipes.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniComment.setup()*
|
||||
`MiniComment.setup`({config})
|
||||
Module setup
|
||||
|
||||
Parameters~
|
||||
{config} `(table)` Module config table. See |MiniComment.config|.
|
||||
|
||||
Usage~
|
||||
`require('mini.comment').setup({})` (replace `{}` with your `config` table)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniComment.config*
|
||||
`MiniComment.config`
|
||||
Module config
|
||||
|
||||
Default values:
|
||||
>
|
||||
MiniComment.config = {
|
||||
-- Module mappings. Use `''` (empty string) to disable one.
|
||||
mappings = {
|
||||
-- Toggle comment (like `gcip` - comment inner paragraph) for both
|
||||
-- Normal and Visual modes
|
||||
comment = 'gc',
|
||||
|
||||
-- Toggle comment on current line
|
||||
comment_line = 'gcc',
|
||||
|
||||
-- Define 'comment' textobject (like `dgc` - delete whole comment block)
|
||||
textobject = 'gc',
|
||||
},
|
||||
-- Hook functions to be executed at certain stage of commenting
|
||||
hooks = {
|
||||
-- Before successful commenting. Does nothing by default.
|
||||
pre = function() end,
|
||||
-- After successful commenting. Does nothing by default.
|
||||
post = function() end,
|
||||
},
|
||||
}
|
||||
<
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniComment.operator()*
|
||||
`MiniComment.operator`({mode})
|
||||
Main function to be mapped
|
||||
|
||||
It is meant to be used in expression mappings (see |map-<expr>|) to enable
|
||||
dot-repeatability and commenting on range. There is no need to do this
|
||||
manually, everything is done inside |MiniComment.setup()|.
|
||||
|
||||
It has a somewhat unintuitive logic (because of how expression mapping with
|
||||
dot-repeatability works): it should be called without arguments inside
|
||||
expression mapping and with argument when action should be performed.
|
||||
|
||||
Parameters~
|
||||
{mode} `(string)` Optional string with 'operatorfunc' mode (see |g@|).
|
||||
|
||||
Return~
|
||||
`(string)` 'g@' if called without argument, '' otherwise (but after
|
||||
performing action).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniComment.toggle_lines()*
|
||||
`MiniComment.toggle_lines`({line_start}, {line_end})
|
||||
Toggle comments between two line numbers
|
||||
|
||||
It uncomments if lines are comment (every line is a comment) and comments
|
||||
otherwise. It respects indentation and doesn't insert trailing
|
||||
whitespace. Toggle commenting not in visual mode is also dot-repeatable
|
||||
and respects |count|.
|
||||
|
||||
Before successful commenting it executes `config.hooks.pre`.
|
||||
After successful commenting it executes `config.hooks.post`.
|
||||
If hook returns `false`, any further action is terminated.
|
||||
|
||||
# Notes~
|
||||
|
||||
1. Currently call to this function will remove marks inside written range.
|
||||
Use |lockmarks| to preserve marks.
|
||||
|
||||
Parameters~
|
||||
{line_start} `(number)` Start line number (inclusive from 1 to number of lines).
|
||||
{line_end} `(number)` End line number (inclusive from 1 to number of lines).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniComment.textobject()*
|
||||
`MiniComment.textobject`()
|
||||
Comment textobject
|
||||
|
||||
This selects all commented lines adjacent to cursor line (if it itself is
|
||||
commented). Designed to be used with operator mode mappings (see |mapmode-o|).
|
||||
|
||||
Before successful textobject usage it executes `config.hooks.pre`.
|
||||
After successful textobject usage it executes `config.hooks.post`.
|
||||
If hook returns `false`, any further action is terminated.
|
||||
|
||||
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
|
@ -0,0 +1,282 @@
|
|||
==============================================================================
|
||||
------------------------------------------------------------------------------
|
||||
*mini.completion*
|
||||
*MiniCompletion*
|
||||
Autocompletion and signature help plugin. Key design ideas:
|
||||
- Have an async (with customizable "debounce" delay) "two-stage chain
|
||||
completion": first try to get completion items from LSP client (if set
|
||||
up) and if no result, fallback to custom action.
|
||||
- Managing completion is done as much with Neovim's built-in tools as
|
||||
possible.
|
||||
|
||||
Features:
|
||||
- Two-stage chain completion:
|
||||
- First stage is an LSP completion implemented via
|
||||
|MiniCompletion.completefunc_lsp()|. It should be set up as either
|
||||
|completefunc| or |omnifunc|. It tries to get completion items from
|
||||
LSP client (via 'textDocument/completion' request). Custom
|
||||
preprocessing of response items is possible (with
|
||||
`MiniCompletion.config.lsp_completion.process_items`), for example
|
||||
with fuzzy matching. By default items which are not snippets and
|
||||
directly start with completed word are kept and sorted according to
|
||||
LSP specification. Supports `additionalTextEdits`, like auto-import
|
||||
and others (see 'Notes').
|
||||
- If first stage is not set up or resulted into no candidates, fallback
|
||||
action is executed. The most tested actions are Neovim's built-in
|
||||
insert completion (see |ins-completion|).
|
||||
- Automatic display in floating window of completion item info (via
|
||||
'completionItem/resolve' request) and signature help (with highlighting
|
||||
of active parameter if LSP server provides such information). After
|
||||
opening, window for signature help is fixed and is closed when there is
|
||||
nothing to show, text is different or
|
||||
when leaving Insert mode.
|
||||
- Automatic actions are done after some configurable amount of delay. This
|
||||
reduces computational load and allows fast typing (completion and
|
||||
signature help) and item selection (item info)
|
||||
- User can force two-stage completion via
|
||||
|MiniCompletion.complete_twostage()| (by default is mapped to
|
||||
`<C-Space>`) or fallback completion via
|
||||
|MiniCompletion.complete_fallback()| (maped to `<M-Space>`).
|
||||
|
||||
What it doesn't do:
|
||||
- Snippet expansion.
|
||||
- Many configurable sources.
|
||||
- Automatic mapping of `<CR>`, `<Tab>`, etc., as those tend to have highly
|
||||
variable user expectations. See 'Helpful key mappings' for suggestions.
|
||||
|
||||
# Setup~
|
||||
|
||||
This module needs a setup with `require('mini.completion').setup({})`
|
||||
(replace `{}` with your `config` table). It will create global Lua table
|
||||
`MiniCompletion` which you can use for scripting or manually (with
|
||||
`:lua MiniCompletion.*`).
|
||||
|
||||
See |MiniCompletion.config| for `config` structure and default values.
|
||||
|
||||
You can override runtime config settings locally to buffer inside
|
||||
`vim.b.minicompletion_config` which should have same structure as
|
||||
`MiniCompletion.config`. See |mini.nvim-buffer-local-config| for more details.
|
||||
|
||||
# Notes~
|
||||
|
||||
- More appropriate, albeit slightly advanced, LSP completion setup is to set
|
||||
it not on every `BufEnter` event (default), but on every attach of LSP
|
||||
client. To do that:
|
||||
- Use in initial config:
|
||||
`lsp_completion = {source_func = 'omnifunc', auto_setup = false}`.
|
||||
- In `on_attach()` of every LSP client set 'omnifunc' option to exactly
|
||||
`v:lua.MiniCompletion.completefunc_lsp`.
|
||||
- If you have trouble using custom (overriden) |vim.ui.input| (like from
|
||||
'stevearc/dressing.nvim'), make automated disable of 'mini.completion'
|
||||
for input buffer. For example, currently for 'dressing.nvim' it can be
|
||||
with `au FileType DressingInput lua vim.b.minicompletion_disable = true`.
|
||||
- Support of `additionalTextEdits` tries to handle both types of servers:
|
||||
- When `additionalTextEdits` are supplied in response to
|
||||
'textDocument/completion' request (like currently in 'pyright').
|
||||
- When `additionalTextEdits` are supplied in response to
|
||||
'completionItem/resolve' request (like currently in
|
||||
'typescript-language-server'). In this case to apply edits user needs
|
||||
to trigger such request, i.e. select completion item and wait for
|
||||
`MiniCompletion.config.delay.info` time plus server response time.
|
||||
|
||||
# Comparisons~
|
||||
|
||||
- 'nvim-cmp':
|
||||
- More complex design which allows multiple sources each in form of
|
||||
separate plugin. `MiniCompletion` has two built in: LSP and fallback.
|
||||
- Supports snippet expansion.
|
||||
- Doesn't have customizable delays for basic actions.
|
||||
- Doesn't allow fallback action.
|
||||
- Doesn't provide signature help.
|
||||
|
||||
# Helpful key mappings~
|
||||
|
||||
To use `<Tab>` and `<S-Tab>` for navigation through completion list, make
|
||||
these key mappings:
|
||||
`vim.api.nvim_set_keymap('i', '<Tab>', [[pumvisible() ? "\<C-n>" : "\<Tab>"]], { noremap = true, expr = true })`
|
||||
`vim.api.nvim_set_keymap('i', '<S-Tab>', [[pumvisible() ? "\<C-p>" : "\<S-Tab>"]], { noremap = true, expr = true })`
|
||||
|
||||
To get more consistent behavior of `<CR>`, you can use this template in
|
||||
your 'init.lua' to make customized mapping: >
|
||||
local keys = {
|
||||
['cr'] = vim.api.nvim_replace_termcodes('<CR>', true, true, true),
|
||||
['ctrl-y'] = vim.api.nvim_replace_termcodes('<C-y>', true, true, true),
|
||||
['ctrl-y_cr'] = vim.api.nvim_replace_termcodes('<C-y><CR>', true, true, true),
|
||||
}
|
||||
|
||||
_G.cr_action = function()
|
||||
if vim.fn.pumvisible() ~= 0 then
|
||||
-- If popup is visible, confirm selected item or add new line otherwise
|
||||
local item_selected = vim.fn.complete_info()['selected'] ~= -1
|
||||
return item_selected and keys['ctrl-y'] or keys['ctrl-y_cr']
|
||||
else
|
||||
-- If popup is not visible, use plain `<CR>`. You might want to customize
|
||||
-- according to other plugins. For example, to use 'mini.pairs', replace
|
||||
-- next line with `return require('mini.pairs').cr()`
|
||||
return keys['cr']
|
||||
end
|
||||
end
|
||||
|
||||
vim.api.nvim_set_keymap('i', '<CR>', 'v:lua._G.cr_action()', { noremap = true, expr = true })
|
||||
<
|
||||
# Highlight groups~
|
||||
|
||||
* `MiniCompletionActiveParameter` - highlighting of signature active parameter.
|
||||
By default displayed as plain underline.
|
||||
|
||||
To change any highlight group, modify it directly with |:highlight|.
|
||||
|
||||
# Disabling~
|
||||
|
||||
To disable, set `g:minicompletion_disable` (globally) or
|
||||
`b:minicompletion_disable` (for a buffer) to `v:true`. Considering high
|
||||
number of different scenarios and customization intentions, writing exact
|
||||
rules for disabling module's functionality is left to user. See
|
||||
|mini.nvim-disabling-recipes| for common recipes.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniCompletion.setup()*
|
||||
`MiniCompletion.setup`({config})
|
||||
Module setup
|
||||
|
||||
Parameters~
|
||||
{config} `(table)` Module config table. See |MiniCompletion.config|.
|
||||
|
||||
Usage~
|
||||
`require('mini.completion').setup({})` (replace `{}` with your `config` table)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniCompletion.config*
|
||||
`MiniCompletion.config`
|
||||
Module config
|
||||
|
||||
Default values:
|
||||
>
|
||||
MiniCompletion.config = {
|
||||
-- Delay (debounce type, in ms) between certain Neovim event and action.
|
||||
-- This can be used to (virtually) disable certain automatic actions by
|
||||
-- setting very high delay time (like 10^7).
|
||||
delay = { completion = 100, info = 100, signature = 50 },
|
||||
|
||||
-- Maximum dimensions of floating windows for certain actions. Action
|
||||
-- entry should be a table with 'height' and 'width' fields.
|
||||
window_dimensions = {
|
||||
info = { height = 25, width = 80 },
|
||||
signature = { height = 25, width = 80 },
|
||||
},
|
||||
|
||||
-- Way of how module does LSP completion
|
||||
lsp_completion = {
|
||||
-- `source_func` should be one of 'completefunc' or 'omnifunc'.
|
||||
source_func = 'completefunc',
|
||||
|
||||
-- `auto_setup` should be boolean indicating if LSP completion is set up
|
||||
-- on every `BufEnter` event.
|
||||
auto_setup = true,
|
||||
|
||||
-- `process_items` should be a function which takes LSP
|
||||
-- 'textDocument/completion' response items and word to complete. Its
|
||||
-- output should be a table of the same nature as input items. The most
|
||||
-- common use-cases are custom filtering and sorting. You can use
|
||||
-- default `process_items` as `MiniCompletion.default_process_items()`.
|
||||
process_items = --<function: filters out snippets; sorts by LSP specs>,
|
||||
},
|
||||
|
||||
-- Fallback action. It will always be run in Insert mode. To use Neovim's
|
||||
-- built-in completion (see `:h ins-completion`), supply its mapping as
|
||||
-- string. Example: to use 'whole lines' completion, supply '<C-x><C-l>'.
|
||||
fallback_action = --<function: like `<C-n>` completion>,
|
||||
|
||||
-- Module mappings. Use `''` (empty string) to disable one. Some of them
|
||||
-- might conflict with system mappings.
|
||||
mappings = {
|
||||
force_twostep = '<C-Space>', -- Force two-step completion
|
||||
force_fallback = '<A-Space>', -- Force fallback completion
|
||||
},
|
||||
|
||||
-- Whether to set Vim's settings for better experience (modifies
|
||||
-- `shortmess` and `completeopt`)
|
||||
set_vim_settings = true,
|
||||
}
|
||||
<
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniCompletion.auto_completion()*
|
||||
`MiniCompletion.auto_completion`()
|
||||
Auto completion
|
||||
|
||||
Designed to be used with |autocmd|. No need to use it directly, everything
|
||||
is setup in |MiniCompletion.setup|.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniCompletion.complete_twostage()*
|
||||
`MiniCompletion.complete_twostage`({fallback}, {force})
|
||||
Run two-stage completion
|
||||
|
||||
Parameters~
|
||||
{fallback} `(boolean)` Whether to use fallback completion.
|
||||
{force} `(boolean)` Whether to force update of completion popup.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniCompletion.complete_fallback()*
|
||||
`MiniCompletion.complete_fallback`()
|
||||
Run fallback completion
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniCompletion.auto_info()*
|
||||
`MiniCompletion.auto_info`()
|
||||
Auto completion entry information
|
||||
|
||||
Designed to be used with |autocmd|. No need to use it directly, everything
|
||||
is setup in |MiniCompletion.setup|.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniCompletion.auto_signature()*
|
||||
`MiniCompletion.auto_signature`()
|
||||
Auto function signature
|
||||
|
||||
Designed to be used with |autocmd|. No need to use it directly, everything
|
||||
is setup in |MiniCompletion.setup|.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniCompletion.stop()*
|
||||
`MiniCompletion.stop`({actions})
|
||||
Stop actions
|
||||
|
||||
This stops currently active (because of module delay or LSP answer delay)
|
||||
actions.
|
||||
|
||||
Designed to be used with |autocmd|. No need to use it directly, everything
|
||||
is setup in |MiniCompletion.setup|.
|
||||
|
||||
Parameters~
|
||||
{actions} `(table)` Array containing any of 'completion', 'info', or
|
||||
'signature' string.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniCompletion.on_text_changed_i()*
|
||||
`MiniCompletion.on_text_changed_i`()
|
||||
Act on every |TextChangedI|
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniCompletion.on_text_changed_p()*
|
||||
`MiniCompletion.on_text_changed_p`()
|
||||
Act on every |TextChangedP|
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniCompletion.completefunc_lsp()*
|
||||
`MiniCompletion.completefunc_lsp`({findstart}, {base})
|
||||
Module's |complete-function|
|
||||
|
||||
This is the main function which enables two-stage completion. It should be
|
||||
set as one of |completefunc| or |omnifunc|.
|
||||
|
||||
No need to use it directly, everything is setup in |MiniCompletion.setup|.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniCompletion.default_process_items()*
|
||||
`MiniCompletion.default_process_items`({items}, {base})
|
||||
Default `MiniCompletion.config.lsp_completion.process_items`
|
||||
|
||||
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
|
@ -0,0 +1,109 @@
|
|||
==============================================================================
|
||||
------------------------------------------------------------------------------
|
||||
*mini.cursorword*
|
||||
*MiniCursorword*
|
||||
Autohighlight word under cursor with customizable delay. Current word under
|
||||
cursor can be highlighted differently. Highlighting is triggered only if
|
||||
current cursor character is a |[:keyword:]|. "Word under cursor" is meant
|
||||
as in Vim's |<cword>|: something user would get as 'iw' text object.
|
||||
Highlighting stops in insert and terminal modes.
|
||||
|
||||
# Setup~
|
||||
|
||||
This module needs a setup with `require('mini.cursorword').setup({})`
|
||||
(replace `{}` with your `config` table). It will create global Lua table
|
||||
`MiniCursorword` which you can use for scripting or manually (with
|
||||
`:lua MiniCursorword.*`).
|
||||
|
||||
See |MiniCursorword.config| for `config` structure and default values.
|
||||
|
||||
You can override runtime config settings locally to buffer inside
|
||||
`vim.b.minicursorword_config` which should have same structure as
|
||||
`MiniCursorword.config`. See |mini.nvim-buffer-local-config| for more details.
|
||||
|
||||
# Highlight groups~
|
||||
|
||||
* `MiniCursorword` - highlight group of cursor word. Default: plain underline.
|
||||
* `MiniCursorwordCurrent` - highlight group of a current word under
|
||||
cursor. It will be displayed on top of `MiniCursorword`
|
||||
(so `:hi clear MiniCursorwordCurrent` will lead to showing
|
||||
`MiniCursorword` highlight group). Note: To not highlight it, use
|
||||
`:hi! MiniCursorwordCurrent gui=nocombine guifg=NONE guibg=NONE` .
|
||||
|
||||
To change any highlight group, modify it directly with |:highlight|.
|
||||
|
||||
# Disabling~
|
||||
|
||||
To disable core functionality, set `g:minicursorword_disable` (globally) or
|
||||
`b:minicursorword_disable` (for a buffer) to `v:true`. Considering high
|
||||
number of different scenarios and customization intentions, writing exact
|
||||
rules for disabling module's functionality is left to user. See
|
||||
|mini.nvim-disabling-recipes| for common recipes. Note: after disabling
|
||||
there might be highlighting left; it will be removed after next
|
||||
highlighting update.
|
||||
|
||||
Module-specific disabling:
|
||||
- Don't show highlighting if cursor is on the word that is in a blocklist
|
||||
of current filetype. In this example, blocklist for "lua" is "local" and
|
||||
"require" words, for "javascript" - "import":
|
||||
>
|
||||
_G.cursorword_blocklist = function()
|
||||
local curword = vim.fn.expand('<cword>')
|
||||
local filetype = vim.api.nvim_buf_get_option(0, 'filetype')
|
||||
|
||||
-- Add any disabling global or filetype-specific logic here
|
||||
local blocklist = {}
|
||||
if filetype == 'lua' then
|
||||
blocklist = { 'local', 'require' }
|
||||
elseif filetype == 'javascript' then
|
||||
blocklist = { 'import' }
|
||||
end
|
||||
|
||||
vim.b.minicursorword_disable = vim.tbl_contains(blocklist, curword)
|
||||
end
|
||||
|
||||
-- Make sure to add this autocommand *before* calling module's `setup()`.
|
||||
vim.cmd('au CursorMoved * lua _G.cursorword_blocklist()')
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniCursorword.setup()*
|
||||
`MiniCursorword.setup`({config})
|
||||
Module setup
|
||||
|
||||
Parameters~
|
||||
{config} `(table)` Module config table. See |MiniCursorword.config|.
|
||||
|
||||
Usage~
|
||||
`require('mini.cursorword').setup({})` (replace `{}` with your `config` table)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniCursorword.config*
|
||||
`MiniCursorword.config`
|
||||
Module config
|
||||
|
||||
Default values:
|
||||
>
|
||||
MiniCursorword.config = {
|
||||
-- Delay (in ms) between when cursor moved and when highlighting appeared
|
||||
delay = 100,
|
||||
}
|
||||
<
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniCursorword.auto_highlight()*
|
||||
`MiniCursorword.auto_highlight`()
|
||||
Auto highlight word under cursor
|
||||
|
||||
Designed to be used with |autocmd|. No need to use it directly,
|
||||
everything is setup in |MiniCursorword.setup|.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniCursorword.auto_unhighlight()*
|
||||
`MiniCursorword.auto_unhighlight`()
|
||||
Auto unhighlight word under cursor
|
||||
|
||||
Designed to be used with |autocmd|. No need to use it directly, everything
|
||||
is setup in |MiniCursorword.setup|.
|
||||
|
||||
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
|
@ -0,0 +1,426 @@
|
|||
==============================================================================
|
||||
------------------------------------------------------------------------------
|
||||
*mini.doc*
|
||||
*MiniDoc*
|
||||
Generation of help files from EmmyLua-like annotations
|
||||
|
||||
Key design ideas:
|
||||
- Keep documentation next to code by writing EmmyLua-like annotation
|
||||
comments. They will be parsed as is, so formatting should follow built-in
|
||||
guide in |help-writing|. However, custom hooks are allowed at many
|
||||
generation stages for more granular management of output help file.
|
||||
- Generation is done by processing a set of ordered files line by line.
|
||||
Each line can either be considered as a part of documentation block (if
|
||||
it matches certain configurable pattern) or not (considered to be an
|
||||
"afterline" of documentation block). See |MiniDoc.generate()| for more
|
||||
details.
|
||||
- Processing is done by using nested data structures (section, block, file,
|
||||
doc) describing certain parts of help file. See |MiniDoc-data-structures|
|
||||
for more details.
|
||||
- Project specific script can be written as plain Lua file with
|
||||
configuratble path. See |MiniDoc.generate()| for more details.
|
||||
|
||||
What it doesn't do:
|
||||
- It doesn't support markdown or other markup language inside annotations.
|
||||
- It doesn't use treesitter in favor of Lua string manipulation for basic
|
||||
tasks (parsing annotations, formatting, auto-generating tags, etc.). This
|
||||
is done to manage complexity and be dependency free.
|
||||
|
||||
# Setup~
|
||||
|
||||
This module needs a setup with `require('mini.doc').setup({})` (replace
|
||||
`{}` with your `config` table). It will create global Lua table `MiniDoc`
|
||||
which you can use for scripting or manually (with `:lua MiniDoc.*`).
|
||||
|
||||
See |MiniDoc.config| for available config settings.
|
||||
|
||||
You can override runtime config settings locally to buffer inside
|
||||
`vim.b.minidoc_config` which should have same structure as `MiniDoc.config`.
|
||||
See |mini.nvim-buffer-local-config| for more details.
|
||||
|
||||
# Tips~
|
||||
|
||||
- Some settings tips that might make writing annotation comments easier:
|
||||
- Set up appropriate 'comments' for `lua` file type to respect
|
||||
EmmyLua-like's `---` comment leader. Value `:---,:--` seems to work.
|
||||
- Set up appropriate 'formatoptions' (see also |fo-table|). Consider
|
||||
adding `j`, `n`, `q`, and `r` flags.
|
||||
- Set up appropriate 'formatlistpat' to help auto-formatting lists (if
|
||||
`n` flag is added to 'formatoptions'). One suggestion (not entirely
|
||||
ideal) is a value `^\s*[0-9\-\+\*]\+[\.\)]*\s\+`. This reads as 'at
|
||||
least one special character (digit, `-`, `+`, `*`) possibly followed
|
||||
by some punctuation (`.` or `)`) followed by at least one space is a
|
||||
start of list item'.
|
||||
- Probably one of the most reliable resources for what is considered to be
|
||||
best practice when using this module is this whole plugin. Look at source
|
||||
code for the reference.
|
||||
|
||||
# Comparisons~
|
||||
|
||||
- 'tjdevries/tree-sitter-lua':
|
||||
- Its key design is to use treesitter grammar to parse both Lua code
|
||||
and annotation comments. This makes it not easy to install,
|
||||
customize, and support.
|
||||
- It takes more care about automating output formatting (like auto
|
||||
indentation and line width fit). This plugin leans more to manual
|
||||
formatting with option to supply customized post-processing hooks.
|
||||
|
||||
# Disabling~
|
||||
|
||||
To disable, set `g:minidoc_disable` (globally) or `b:minidoc_disable` (for
|
||||
a buffer) to `v:true`. Considering high number of different scenarios and
|
||||
customization intentions, writing exact rules for disabling module's
|
||||
functionality is left to user. See |mini.nvim-disabling-recipes| for common
|
||||
recipes.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniDoc-data-structures*
|
||||
Data structures
|
||||
|
||||
Data structures are basically arrays of other structures accompanied with
|
||||
some fields (keys with data values) and methods (keys with function
|
||||
values):
|
||||
- `Section structure` is an array of string lines describing one aspect
|
||||
(determined by section id like '@param', '@return', '@text') of an
|
||||
annotation subject. All lines will be used directly in help file.
|
||||
- `Block structure` is an array of sections describing one annotation
|
||||
subject like function, table, concept.
|
||||
- `File structure` is an array of blocks describing certain file on disk.
|
||||
Basically, file is split into consecutive blocks: annotation lines go
|
||||
inside block, non-annotation - inside `block_afterlines` element of info.
|
||||
- `Doc structure` is an array of files describing a final help file. Each
|
||||
string line from section (when traversed in depth-first fashion) goes
|
||||
directly into output file.
|
||||
|
||||
All structures have these keys:
|
||||
- Fields:
|
||||
- `info` - contains additional information about current structure.
|
||||
For more details see next section.
|
||||
- `parent` - table of parent structure (if exists).
|
||||
- `parent_index` - index of this structure in its parent's array. Useful
|
||||
for adding to parent another structure near current one.
|
||||
- `type` - string with structure type (section, block, file, doc).
|
||||
- Methods (use them as `x:method(args)`):
|
||||
- `insert(self, [index,] child)` - insert `child` to `self` at position
|
||||
`index` (optional; if not supplied, child will be appended to end).
|
||||
Basically, a `table.insert()`, but adds `parent` and `parent_index`
|
||||
fields to `child` while properly updating `self`.
|
||||
- `remove(self [,index])` - remove from `self` element at position
|
||||
`index`. Basically, a `table.remove()`, but properly updates `self`.
|
||||
- `has_descendant(self, predicate)` - whether there is a descendant
|
||||
(structure or string) for which `predicate` returns `true`. In case of
|
||||
success also returns the first such descendant as second value.
|
||||
- `has_lines(self)` - whether structure has any lines (even empty ones)
|
||||
to be put in output file. For section structures this is equivalent to
|
||||
`#self`, but more useful for higher order structures.
|
||||
- `clear_lines(self)` - remove all lines from structure. As a result,
|
||||
this structure won't contribute to output help file.
|
||||
|
||||
Description of `info` fields per structure type:
|
||||
- `Section`:
|
||||
- `id` - captured section identifier. Can be empty string meaning no
|
||||
identifier is captured.
|
||||
- `line_begin` - line number inside file at which section begins (-1 if
|
||||
not generated from file).
|
||||
- `line_end` - line number inside file at which section ends (-1 if not
|
||||
generated from file).
|
||||
- `Block`:
|
||||
- `afterlines` - array of strings which were parsed from file after
|
||||
this annotation block (up until the next block or end of file).
|
||||
Useful for making automated decisions about what is being documented.
|
||||
- `line_begin` - line number inside file at which block begins (-1 if
|
||||
not generated from file).
|
||||
- `line_end` - line number inside file at which block ends (-1 if not
|
||||
generated from file).
|
||||
- `File`:
|
||||
- `path` - absolute path to a file (`''` if not generated from file).
|
||||
- `Doc`:
|
||||
- `input` - array of input file paths (as in |MiniDoc.generate|).
|
||||
- `output` - output path (as in |MiniDoc.generate|).
|
||||
- `config` - configuration used (as in |MiniDoc.generate|).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniDoc.setup()*
|
||||
`MiniDoc.setup`({config})
|
||||
Module setup
|
||||
|
||||
Parameters~
|
||||
{config} `(table)` Module config table. See |MiniDoc.config|.
|
||||
|
||||
Usage~
|
||||
`require('mini.doc').setup({})` (replace `{}` with your `config` table)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniDoc.config*
|
||||
`MiniDoc.config`
|
||||
Module config
|
||||
|
||||
Default values:
|
||||
>
|
||||
MiniDoc.config = {
|
||||
-- Function which extracts part of line used to denote annotation.
|
||||
-- For more information see 'Notes' in |MiniDoc.config|.
|
||||
annotation_extractor = function(l) return string.find(l, '^%-%-%-(%S*) ?') end,
|
||||
|
||||
-- Identifier of block annotation lines until first captured identifier
|
||||
default_section_id = '@text',
|
||||
|
||||
-- Hooks to be applied at certain stage of document life cycle. Should
|
||||
-- modify its input in place (and not return new one).
|
||||
hooks = {
|
||||
-- Applied to block before anything else
|
||||
block_pre = --<function: infers header sections (tag and/or signature)>,
|
||||
|
||||
-- Applied to section before anything else
|
||||
section_pre = --<function: replaces current aliases>,
|
||||
|
||||
-- Applied if section has specified captured id
|
||||
sections = {
|
||||
['@alias'] = --<function: registers alias in MiniDoc.current.aliases>,
|
||||
['@class'] = --<function>,
|
||||
['@diagnostic'] = --<function: ignores any section content>,
|
||||
-- For most typical usage see |MiniDoc.afterlines_to_code|
|
||||
['@eval'] = --<function: evaluates lines; replaces with their return>,
|
||||
['@field'] = --<function>,
|
||||
['@overload'] = --<function>,
|
||||
['@param'] = --<function>,
|
||||
['@private'] = --<function: registers block for removal>,
|
||||
['@return'] = --<function>,
|
||||
['@seealso'] = --<function>,
|
||||
['@signature'] = --<function: formats signature of documented object>,
|
||||
['@tag'] = --<function: turns its line in proper tag lines>,
|
||||
['@text'] = --<function: purposefully does nothing>,
|
||||
['@toc'] = --<function: clears all section lines>,
|
||||
['@toc_entry'] = --<function: registers lines for table of contents>,
|
||||
['@type'] = --<function>,
|
||||
['@usage'] = --<function>,
|
||||
},
|
||||
|
||||
-- Applied to section after all previous steps
|
||||
section_post = --<function: currently does nothing>,
|
||||
|
||||
-- Applied to block after all previous steps
|
||||
block_post = --<function: does many things>,
|
||||
|
||||
-- Applied to file after all previous steps
|
||||
file = --<function: adds separator>,
|
||||
|
||||
-- Applied to doc after all previous steps
|
||||
doc = --<function: adds modeline>,
|
||||
|
||||
-- Applied to after output help file is written. Takes doc as argument.
|
||||
write_post = --<function: various convenience actions>,
|
||||
},
|
||||
|
||||
-- Path (relative to current directory) to script which handles project
|
||||
-- specific help file generation (like custom input files, hooks, etc.).
|
||||
script_path = 'scripts/minidoc.lua',
|
||||
}
|
||||
<
|
||||
# Notes ~
|
||||
|
||||
- `annotation_extractor` takes single string line as input. Output
|
||||
describes what makes an input to be an annotation (if anything). It
|
||||
should be similar to `string.find` with one capture group: start and end
|
||||
of annotation indicator (whole part will be removed from help line) with
|
||||
third value being string of section id (if input describes first line of
|
||||
section; `nil` or empty string otherwise). Output should be `nil` if line
|
||||
is not part of annotation.
|
||||
Default value means that annotation line should:
|
||||
- Start with `---` at first column.
|
||||
- Any non-whitespace after `---` will be treated as new section id.
|
||||
- Single whitespace at the start of main text will be ignored.
|
||||
- Hooks are expected to be functions. Their default values might do many
|
||||
things which might change over time, so for more information please look
|
||||
at source code. Some more information can be found in
|
||||
|MiniDoc.default_hooks|.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniDoc.current*
|
||||
`MiniDoc.current`
|
||||
Table with information about current state of auto-generation
|
||||
|
||||
It is reset at the beginning and end of `MiniDoc.generate()`.
|
||||
|
||||
At least these keys are supported:
|
||||
- {aliases} - table with keys being alias name and values - alias
|
||||
description and single string (using `\n` to separate lines).
|
||||
- {eval_section} - input section of `@eval` section hook. Can be used for
|
||||
information about current block, etc.
|
||||
- {toc} - array with table of contents entries. Each entry is a whole
|
||||
`@toc_entry` section.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniDoc.default_hooks*
|
||||
`MiniDoc.default_hooks`
|
||||
Default hooks
|
||||
|
||||
This is default value of `MiniDoc.config.hooks`. Use it if only a little
|
||||
tweak is needed.
|
||||
|
||||
Some more insight about their behavior:
|
||||
- Default inference of documented object metadata (tag and object signature
|
||||
at the moment) is done in `block_pre`. Inference is based on string
|
||||
pattern matching, so can lead to false results, although works in most
|
||||
cases. It intentionally works only if first line after block has no
|
||||
indentation and contains all necessary information to determine if
|
||||
inference should happen.
|
||||
- Hooks for sections describing some "variable-like" object ('@class',
|
||||
'@field', '@param') automatically enclose first word in '{}'.
|
||||
- Hooks for sections which supposed to have "type-like" data ('@field',
|
||||
'@param', '@return', '@type') automatically enclose *first found*
|
||||
"type-like" word and its neighbor characters in '`(<type>)`' (expect
|
||||
false positives). Algoritm is far from being 100% correct, but seems to
|
||||
work with present allowed type annotation. For allowed types see
|
||||
https://github.com/sumneko/lua-language-server/wiki/EmmyLua-Annotations#types-and-type
|
||||
or, better yet, look in source code of this module.
|
||||
- Automated creation of table of contents (TOC) is done in the following way:
|
||||
- Put section with `@toc_entry` id in the annotation block. Section's
|
||||
lines will be registered as TOC entry.
|
||||
- Put `@toc` section where you want to insert rendered table of
|
||||
contents. TOC entries will be inserted on the left, references for
|
||||
their respective tag section (only first, if present) on the right.
|
||||
Render is done in default `doc` hook (because it should be done after
|
||||
processing all files).
|
||||
- The `write_post` hook executes some actions convenient for iterative
|
||||
annotations writing:
|
||||
- Generate `:helptags` for directory containing output file.
|
||||
- Silently reload buffer containing output file (if such exists).
|
||||
- Display notification message about result.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniDoc.generate()*
|
||||
`MiniDoc.generate`({input}, {output}, {config})
|
||||
Generate help file
|
||||
|
||||
# Algoritm~
|
||||
|
||||
- Main parameters for help generation are an array of input file paths and
|
||||
path to output help file.
|
||||
- Parse all inputs:
|
||||
- For each file, lines are processed top to bottom in order to create an
|
||||
array of documentation blocks. Each line is tested whether it is an
|
||||
annotation by applying `MiniDoc.config.annotation_extractor`: if
|
||||
anything is extracted, it is considered to be an annotation. Annotation
|
||||
line goes to "current block" after removing extracted annotation
|
||||
indicator, otherwise - to afterlines of "current block".
|
||||
- Each block's annotation lines are processed top to bottom. If line had
|
||||
captured section id, it is a first line of "current section" (first
|
||||
block lines are allowed to not specify section id; by default it is
|
||||
`@text`). All subsequent lines without captured section id go into
|
||||
"current section".
|
||||
- Apply structure hooks (they should modify its input in place, which is
|
||||
possible due to 'table nature' of all inputs):
|
||||
- Each block is processed by `MiniDoc.config.hooks.block_pre`. This is a
|
||||
designated step for auto-generation of sections from descibed
|
||||
annotation subject (like sections with id `@tag`, `@type`).
|
||||
- Each section is processed by `MiniDoc.config.hooks.section_pre`.
|
||||
- Each section is processed by corresponding
|
||||
`MiniDoc.config.hooks.sections` function (table key equals to section
|
||||
id). This is a step where most of formatting should happen (like
|
||||
wrap first word of `@param` section with `{` and `}`, append empty
|
||||
line to section, etc.).
|
||||
- Each section is processed by `MiniDoc.config.hooks.section_post`.
|
||||
- Each block is processed by `MiniDoc.config.hooks.block_post`. This is
|
||||
a step for processing block after formatting is done (like add first
|
||||
line with `----` delimiter).
|
||||
- Each file is processed by `MiniDoc.config.hooks.file`. This is a step
|
||||
for adding any file-related data (like add first line with `====`
|
||||
delimiter).
|
||||
- Doc is processed by `MiniDoc.config.hooks.doc`. This is a step for
|
||||
adding any helpfile-related data (maybe like table of contents).
|
||||
- Collect all strings from sections in depth-first fashion (equivalent to
|
||||
nested "for all files -> for all blocks -> for all sections -> for all
|
||||
strings -> add string to output") and write them to output file. Strings
|
||||
can have `\n` character indicating start of new line.
|
||||
- Execute `MiniDoc.config.write_post` hook. This is useful for showing some
|
||||
feedback and making actions involving newly updated help file (like
|
||||
generate tags, etc.).
|
||||
|
||||
# Project specific script~
|
||||
|
||||
If all arguments have default `nil` values, first there is an attempt to
|
||||
source project specific script. This is basically a `luafile
|
||||
<MiniDoc.config.script_path>` with current Lua runtime while caching and
|
||||
restoring current `MiniDoc.config`. Its successful execution stops any
|
||||
further generation actions while error means proceeding generation as if no
|
||||
script was found.
|
||||
|
||||
Typical script content might include definition of custom hooks, input and
|
||||
output files with eventual call to `require('mini.doc').generate()` (with
|
||||
or without arguments).
|
||||
|
||||
Parameters~
|
||||
{input} `(table)` Array of file paths which will be processed in supplied
|
||||
order. Default: all '.lua' files from current directory following by all
|
||||
such files in these subdirectories: 'lua/', 'after/', 'colors/'. Note:
|
||||
any 'init.lua' file is placed before other files from the same directory.
|
||||
{output} `(string)` Path for output help file. Default:
|
||||
`doc/<current_directory>.txt` (designed to be used for generating help
|
||||
file for plugin).
|
||||
{config} `(table)` Configuration overriding parts of |MiniDoc.config|.
|
||||
|
||||
Return~
|
||||
`(table)` Document structure which was generated and used for output
|
||||
help file. In case `MiniDoc.config.script_path` was successfully used,
|
||||
this is a return from the latest call of this function.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniDoc.afterlines_to_code()*
|
||||
`MiniDoc.afterlines_to_code`({struct})
|
||||
Convert afterlines to code
|
||||
|
||||
This function is designed to be used together with `@eval` section to
|
||||
automate documentation of certain values (notable default values of a
|
||||
table). It processes afterlines based on certain directives and makes
|
||||
output looking like a code block.
|
||||
|
||||
Most common usage is by adding the following section in your annotation:
|
||||
`@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)`
|
||||
|
||||
# Directives ~
|
||||
Directives are special comments that are processed using Lua string pattern
|
||||
capabilities (so beware of false positives). Each directive should be put
|
||||
on its separate line. Supported directives:
|
||||
- `--minidoc_afterlines_end` denotes a line at afterlines end. Only all
|
||||
lines before it will be considered as afterlines. Useful if there is
|
||||
extra code in afterlines which shouldn't be used.
|
||||
- `--minidoc_replace_start <replacement>` and `--minidoc_replace_end`
|
||||
denote lines between them which should be replaced with `<replacement>`.
|
||||
Useful for manually changing what should be placed in output like in case
|
||||
of replacing function body with something else.
|
||||
|
||||
Here is an example. Suppose having these afterlines:
|
||||
>
|
||||
--minidoc_replace_start {
|
||||
M.config = {
|
||||
--minidoc_replace_end
|
||||
param_one = 1,
|
||||
--minidoc_replace_start param_fun = --<function>
|
||||
param_fun = function(x)
|
||||
return x + 1
|
||||
end
|
||||
--minidoc_replace_end
|
||||
}
|
||||
--minidoc_afterlines_end
|
||||
|
||||
return M
|
||||
<
|
||||
|
||||
After adding `@eval` section those will be formatted as:
|
||||
>
|
||||
{
|
||||
param_one = 1,
|
||||
param_fun = --<function>
|
||||
}
|
||||
<
|
||||
Parameters~
|
||||
{struct} `(table)` Block or section structure which after lines will be
|
||||
converted to code.
|
||||
|
||||
Return~
|
||||
`(string)` Single string (using `\n` to separate lines) describing
|
||||
afterlines as code block in help file.
|
||||
|
||||
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
|
@ -0,0 +1,147 @@
|
|||
==============================================================================
|
||||
------------------------------------------------------------------------------
|
||||
*mini.fuzzy*
|
||||
*MiniFuzzy*
|
||||
Minimal and fast fuzzy matching.
|
||||
|
||||
# Setup~
|
||||
|
||||
This module doesn't need setup, but it can be done to improve usability.
|
||||
Setup with `require('mini.fuzzy').setup({})` (replace `{}` with your
|
||||
`config` table). It will create global Lua table `MiniFuzzy` which you can
|
||||
use for scripting or manually (with `:lua MiniFuzzy.*`).
|
||||
|
||||
See |MiniFuzzy.config| for `config` structure and default values.
|
||||
|
||||
You can override runtime config settings locally to buffer inside
|
||||
`vim.b.minifuzzy_config` which should have same structure as
|
||||
`MiniFuzzy.config`.
|
||||
See |mini.nvim-buffer-local-config| for more details.
|
||||
|
||||
# Notes~
|
||||
|
||||
1. Currently there is no explicit design to work with multibyte symbols,
|
||||
but simple examples should work.
|
||||
2. Smart case is used: case insensitive if input word (which is usually a
|
||||
user input) is all lower ase. Case sensitive otherwise.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniFuzzy-algorithm*
|
||||
# Algorithm design~
|
||||
|
||||
General design uses only width of found match and index of first letter
|
||||
match. No special characters or positions (like in fzy and fzf) are used.
|
||||
|
||||
Given input `word` and target `candidate`:
|
||||
- The goal is to find matching between `word`'s letters and letters in
|
||||
`candidate`, which minimizes certain score. It is assumed that order of
|
||||
letters in `word` and those matched in `candidate` should be the same.
|
||||
- Matching is represented by matched positions: an array `positions` of
|
||||
integers with length equal to number of letters in `word`. The following
|
||||
should be always true in case of a match: `candidate`'s letter at index
|
||||
`positions[i]` is letters[i]` for all valid `i`.
|
||||
- Matched positions are evaluated based only on two features: their width
|
||||
(number of indexes between first and last positions) and first match
|
||||
(index of first letter match). There is a global setting `cutoff` for
|
||||
which all feature values greater than it can be considered "equally bad".
|
||||
- Score of matched positions is computed with following explicit formula:
|
||||
`cutoff * min(width, cutoff) + min(first, cutoff)`. It is designed to be
|
||||
equivalent to first comparing widths (lower is better) and then comparing
|
||||
first match (lower is better). For example, if `word = 'time'`:
|
||||
- '_time' (width 4) will have a better match than 't_ime' (width 5).
|
||||
- 'time_a' (width 4, first 1) will have a better match than 'a_time'
|
||||
(width 4, first 3).
|
||||
- Final matched positions are those which minimize score among all possible
|
||||
matched positions of `word` and `candidate`.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniFuzzy.setup()*
|
||||
`MiniFuzzy.setup`({config})
|
||||
Module setup
|
||||
|
||||
Parameters~
|
||||
{config} `(table)` Module config table. See |MiniFuzzy.config|.
|
||||
|
||||
Usage~
|
||||
`require('mini.fuzzy').setup({})` (replace `{}` with your `config` table)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniFuzzy.config*
|
||||
`MiniFuzzy.config`
|
||||
Module config
|
||||
|
||||
Default values:
|
||||
>
|
||||
MiniFuzzy.config = {
|
||||
-- Maximum allowed value of match features (width and first match). All
|
||||
-- feature values greater than cutoff can be considered "equally bad".
|
||||
cutoff = 100,
|
||||
}
|
||||
<
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniFuzzy.match()*
|
||||
`MiniFuzzy.match`({word}, {candidate})
|
||||
Compute match data of input `word` and `candidate` strings
|
||||
|
||||
It tries to find best match for input string `word` (usually user input)
|
||||
and string `candidate`. Returns table with elements:
|
||||
- `positions` - array with letter indexes inside `candidate` which
|
||||
matched to corresponding letters in `word`. Or `nil` if no match.
|
||||
- `score` - positive number representing how good the match is (lower is
|
||||
better). Or `-1` if no match.
|
||||
|
||||
Parameters~
|
||||
{word} `(string)` Input word (usually user input).
|
||||
{candidate} `(string)` Target word (usually with which matching is done).
|
||||
|
||||
Return~
|
||||
`(table)` Table with matching information (see function's description).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniFuzzy.filtersort()*
|
||||
`MiniFuzzy.filtersort`({word}, {candidate_array})
|
||||
Filter string array
|
||||
|
||||
This leaves only those elements of input array which matched with `word`
|
||||
and sorts from best to worst matches (based on score and index in original
|
||||
array, both lower is better).
|
||||
|
||||
Parameters~
|
||||
{word} `(string)` String which will be searched.
|
||||
{candidate_array} `(table)` Lua array of strings inside which word will be
|
||||
searched.
|
||||
|
||||
Return~
|
||||
`(...)` Arrays of matched candidates and their indexes in original input.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniFuzzy.process_lsp_items()*
|
||||
`MiniFuzzy.process_lsp_items`({items}, {base})
|
||||
Fuzzy matching for `lsp_completion.process_items` of |MiniCompletion.config|
|
||||
|
||||
Parameters~
|
||||
{items} `(table)` Lua array with LSP 'textDocument/completion' response items.
|
||||
{base} `(string)` Word to complete.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniFuzzy.get_telescope_sorter()*
|
||||
`MiniFuzzy.get_telescope_sorter`({opts})
|
||||
Custom getter for `telescope.nvim` sorter
|
||||
|
||||
Designed to be used as value for |telescope.defaults.file_sorter| and
|
||||
|telescope.defaults.generic_sorter| inside `setup()` call.
|
||||
|
||||
Parameters~
|
||||
{opts} `(table)` Options (currently not used).
|
||||
|
||||
Usage~
|
||||
>
|
||||
require('telescope').setup({
|
||||
defaults = {
|
||||
generic_sorter = require('mini.fuzzy').get_telescope_sorter
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
|
@ -0,0 +1,397 @@
|
|||
==============================================================================
|
||||
------------------------------------------------------------------------------
|
||||
*mini.indentscope*
|
||||
*MiniIndentscope*
|
||||
Visualize and operate on indent scope
|
||||
|
||||
Indent scope (or just "scope") is a maximum set of consecutive lines which
|
||||
contains certain reference line (cursor line by default) and every member
|
||||
has indent not less than certain reference indent ("indent at cursor" by
|
||||
default: minimum between cursor column and indent of cursor line).
|
||||
|
||||
Features:
|
||||
- Visualize scope with animated vertical line. It is very fast and done
|
||||
automatically in a non-blocking way (other operations can be performed,
|
||||
like moving cursor). You can customize debounce delay and animation rule.
|
||||
- Customization of scope computation options can be done on global level
|
||||
(in |MiniIndentscope.config|), for a certain buffer (using
|
||||
`vim.b.miniindentscope_config` buffer variable), or within a call (using
|
||||
`opts` variable in |MiniIndentscope.get_scope|).
|
||||
- Customizable notion of a border: which adjacent lines with strictly lower
|
||||
indent are recognized as such. This is useful for a certain filetypes
|
||||
(for example, Python or plain text).
|
||||
- Customizable way of line to be considered "border first". This is useful
|
||||
if you want to place cursor on function header and get scope of its body.
|
||||
- There are textobjects and motions to operate on scope. Support |count|
|
||||
and dot-repeat (in operator pending mode).
|
||||
|
||||
# Setup~
|
||||
|
||||
This module needs a setup with `require('mini.indentscope').setup({})`
|
||||
(replace `{}` with your `config` table). It will create global Lua table
|
||||
`MiniIndentscope` which you can use for scripting or manually (with `:lua
|
||||
MiniIndentscope.*`).
|
||||
|
||||
See |MiniIndentscope.config| for available config settings.
|
||||
|
||||
You can override runtime config settings locally to buffer inside
|
||||
`vim.b.miniindentscope_config` which should have same structure as
|
||||
`MiniIndentscope.config`. See |mini.nvim-buffer-local-config| for more details.
|
||||
|
||||
# Comparisons~
|
||||
|
||||
- 'lukas-reineke/indent-blankline.nvim':
|
||||
- Its main functionality is about showing static guides of indent levels.
|
||||
- Implementation of 'mini.indentscope' is similar to
|
||||
'indent-blankline.nvim' (using |extmarks| on first column to be shown
|
||||
even on blank lines). They can be used simultaneously, but it will
|
||||
lead to one of the visualizations being on top (hiding) of another.
|
||||
|
||||
# Highlight groups~
|
||||
|
||||
* `MiniIndentscopeSymbol` - symbol showing on every line of scope.
|
||||
* `MiniIndentscopePrefix` - space before symbol. By default made so as to
|
||||
appear as nothing is displayed.
|
||||
|
||||
To change any highlight group, modify it directly with |:highlight|.
|
||||
|
||||
# Disabling~
|
||||
|
||||
To disable autodrawing, set `g:miniindentscope_disable` (globally) or
|
||||
`b:miniindentscope_disable` (for a buffer) to `v:true`. Considering high
|
||||
number of different scenarios and customization intentions, writing exact
|
||||
rules for disabling module's functionality is left to user. See
|
||||
|mini.nvim-disabling-recipes| for common recipes.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniIndentscope-drawing*
|
||||
Drawing of scope indicator
|
||||
|
||||
Draw of scope indicator is done as iterative animation. It has the
|
||||
following design:
|
||||
- Draw indicator on origin line (where cursor is at) immediately. Indicator
|
||||
is visualized as `MiniIndentscope.config.symbol` placed to the right of
|
||||
scope's border indent. This creates a line from top to bottom scope edges.
|
||||
- Draw upward and downward concurrently per one line. Progression by one
|
||||
line in both direction is considered to be one step of animation.
|
||||
- Before each step wait certain amount of time, which is decided by
|
||||
"animation function". It takes next and total step numbers (both are one
|
||||
or bigger) and returns number of milliseconds to wait before drawing next
|
||||
step. Comparing to a more popular "easing functions" in animation (input:
|
||||
duration since animation start; output: percent of animation done), it is
|
||||
a discrete inverse version of its derivative. Such interface proved to be
|
||||
more appropriate for kind of task at hand.
|
||||
|
||||
Special cases~
|
||||
|
||||
- When scope to be drawn intersects (same indent, ranges overlap) currently
|
||||
visible one (at process or finished drawing), drawing is done immediately
|
||||
without animation. With most common example being typing new text, this
|
||||
feels more natural.
|
||||
- Scope for the whole buffer is not drawn as it is isually redundant.
|
||||
Technically, it can be thought as drawn at column 0 (because border
|
||||
indent is -1) which is not visible.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniIndentscope.setup()*
|
||||
`MiniIndentscope.setup`({config})
|
||||
Module setup
|
||||
|
||||
Parameters~
|
||||
{config} `(table)` Module config table. See |MiniIndentscope.config|.
|
||||
|
||||
Usage~
|
||||
`require('mini.indentscope').setup({})` (replace `{}` with your `config` table)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniIndentscope.config*
|
||||
`MiniIndentscope.config`
|
||||
Module config
|
||||
|
||||
Default values:
|
||||
>
|
||||
MiniIndentscope.config = {
|
||||
draw = {
|
||||
-- Delay (in ms) between event and start of drawing scope indicator
|
||||
delay = 100,
|
||||
|
||||
-- Animation rule for scope's first drawing. A function which, given
|
||||
-- next and total step numbers, returns wait time (in ms). See
|
||||
-- |MiniIndentscope.gen_animation()| for builtin options. To disable
|
||||
-- animation, use `require('mini.indentscope').gen_animation('none')`.
|
||||
animation = --<function: implements constant 20ms between steps>,
|
||||
},
|
||||
|
||||
-- Module mappings. Use `''` (empty string) to disable one.
|
||||
mappings = {
|
||||
-- Textobjects
|
||||
object_scope = 'ii',
|
||||
object_scope_with_border = 'ai',
|
||||
|
||||
-- Motions (jump to respective border line; if not present - body line)
|
||||
goto_top = '[i',
|
||||
goto_bottom = ']i',
|
||||
},
|
||||
|
||||
-- Options which control scope computation
|
||||
options = {
|
||||
-- Type of scope's border: which line(s) with smaller indent to
|
||||
-- categorize as border. Can be one of: 'both', 'top', 'bottom', 'none'.
|
||||
border = 'both',
|
||||
|
||||
-- Whether to use cursor column when computing reference indent.
|
||||
-- Useful to see incremental scopes with horizontal cursor movements.
|
||||
indent_at_cursor = true,
|
||||
|
||||
-- Whether to first check input line to be a border of adjacent scope.
|
||||
-- Use it if you want to place cursor on function header to get scope of
|
||||
-- its body.
|
||||
try_as_border = false,
|
||||
},
|
||||
|
||||
-- Which character to use for drawing scope indicator
|
||||
symbol = '╎',
|
||||
}
|
||||
<
|
||||
# Options ~
|
||||
|
||||
- Options can be supplied globally (from this `config`), locally to buffer
|
||||
(via `options` field of `vim.b.miniindentscope_config` buffer variable),
|
||||
or locally to call (as argument to |MiniIndentscope.get_scope()|).
|
||||
|
||||
- Option `border` controls which line(s) with smaller indent to categorize
|
||||
as border. This matters for textobjects and motions.
|
||||
It also controls how empty lines are treated: they are included in scope
|
||||
only if followed by a border. Another way of looking at it is that indent
|
||||
of blank line is computed based on value of `border` option.
|
||||
Here is an illustration of how `border` works in presense of empty lines:
|
||||
>
|
||||
|both|bottom|top|none|
|
||||
1|function foo() | 0 | 0 | 0 | 0 |
|
||||
2| | 4 | 0 | 4 | 0 |
|
||||
3| print('Hello world') | 4 | 4 | 4 | 4 |
|
||||
4| | 4 | 4 | 2 | 2 |
|
||||
5| end | 2 | 2 | 2 | 2 |
|
||||
<
|
||||
Numbers inside a table are indent values of a line computed with certain
|
||||
value of `border`. So, for example, a scope with reference line 3 and
|
||||
right-most column has body range depending on value of `border` option:
|
||||
- `border` is "both": range is 2-4, border is 1 and 5 with indent 2.
|
||||
- `border` is "top": range is 2-3, border is 1 with indent 0.
|
||||
- `border` is "bottom": range is 3-4, border is 5 with indent 0.
|
||||
- `border` is "none": range is 3-3, border is empty with indent `nil`.
|
||||
|
||||
- Option `indent_at_cursor` controls if cursor position should affect
|
||||
computation of scope. If `true`, reference indent is a minimum of
|
||||
reference line's indent and cursor column. In main example, here how
|
||||
scope's body range differs depending on cursor column and `indent_at_cursor`
|
||||
value (assuming cursor is on line 3 and it is whole buffer):
|
||||
>
|
||||
Column\Option true|false
|
||||
1 and 2 2-5 | 2-4
|
||||
3 and more 2-4 | 2-4
|
||||
<
|
||||
- Option `try_as_border` controls how to act when input line can be
|
||||
recognized as a border of some neighbor indent scope. In main example,
|
||||
when input line is 1 and can be recognized as border for inner scope,
|
||||
value `try_as_border = true` means that inner scope will be returned.
|
||||
Similar, for input line 5 inner scope will be returned if it is
|
||||
recognized as border.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniIndentscope.get_scope()*
|
||||
`MiniIndentscope.get_scope`({line}, {col}, {opts})
|
||||
Compute indent scope
|
||||
|
||||
Indent scope (or just "scope") is a maximum set of consecutive lines which
|
||||
contains certain reference line (cursor line by default) and every member
|
||||
has indent not less than certain reference indent ("indent at column" by
|
||||
default). Here "indent at column" means minimum between input column value
|
||||
and indent of reference line. When using cursor column, this allows for a
|
||||
useful interactive view of nested indent scopes by making horizontal
|
||||
movements within line.
|
||||
|
||||
Options controlling actual computation is taken from these places in order:
|
||||
- Argument `opts`. Use it to ensure independence from other sources.
|
||||
- Buffer local variable `vim.b.miniindentscope_config` (`options` field).
|
||||
Useful to define local behavior (for example, for a certain filetype).
|
||||
- Global options from |MiniIndentscope.config|.
|
||||
|
||||
Algorithm overview~
|
||||
|
||||
- Compute reference "indent at column". Reference line is an input `line`
|
||||
which might be modified to one of its neighbors if `try_as_border` option
|
||||
is `true`: if it can be viewed as border of some neighbor scope, it will.
|
||||
- Process upwards and downwards from reference line to search for line with
|
||||
indent strictly less than reference one. This is like casting rays up and
|
||||
down from reference line and reference indent until meeting "a wall"
|
||||
(character to the right of indent or buffer edge). Latest line before
|
||||
meeting is a respective end of scope body. It always exists because
|
||||
reference line is a such one.
|
||||
- Based on top and bottom lines with strictly lower indent, construct
|
||||
scopes's border. The way it is computed is decided based on `border`
|
||||
option (see |MiniIndentscope.config| for more information).
|
||||
- Compute border indent as maximum indent of border lines (or reference
|
||||
indent minus one in case of no border). This is used during drawing
|
||||
visual indicator.
|
||||
|
||||
Indent computation~
|
||||
|
||||
For every line indent is intended to be computed unambiguously:
|
||||
- For "normal" lines indent is an output of |indent()|.
|
||||
- Indent is `-1` for imaginary lines 0 and past last line.
|
||||
- For blank and empty lines indent is computed based on previous
|
||||
(|prevnonblank()|) and next (|nextnonblank()|) non-blank lines. The way
|
||||
it is computed is decided based on `border` in order to not include blank
|
||||
lines at edge of scope's body if there is no border there. See
|
||||
|MiniIndentscope.config| for a details example.
|
||||
|
||||
Parameters~
|
||||
{line} `(number)` Input line number (starts from 1). Can be modified to a
|
||||
neighbor if `try_as_border` is `true`. Default: cursor line.
|
||||
{col} `(number)` Column number (starts from 1). Default: if
|
||||
`indent_at_cursor` option is `true` - cursor column from `curswant` of
|
||||
|getcurpos()| (allows for more natural behavior on empty lines);
|
||||
`math.huge` otherwise in order to not incorporate cursor in computation.
|
||||
{opts} `(table)` Options to override global or buffer local ones (see
|
||||
|MiniIndentscope.config|).
|
||||
|
||||
Return~
|
||||
`(table)` Table with scope information:
|
||||
- <body> - table with <top> (top line of scope, inclusive), <bottom>
|
||||
(bottom line of scope, inclusive), and <indent> (minimum indent withing
|
||||
scope) keys. Line numbers start at 1.
|
||||
- <border> - table with <top> (line of top border, might be `nil`),
|
||||
<bottom> (line of bottom border, might be `nil`), and <indent> (indent
|
||||
of border) keys. Line numbers start at 1.
|
||||
- <buf_id> - identifier of current buffer.
|
||||
- <reference> - table with <line> (reference line), <column> (reference
|
||||
column), and <indent> ("indent at column") keys.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniIndentscope.auto_draw()*
|
||||
`MiniIndentscope.auto_draw`({opts})
|
||||
Auto draw scope indicator based on movement events
|
||||
|
||||
Designed to be used with |autocmd|. No need to use it directly, everything
|
||||
is setup in |MiniIndentscope.setup|.
|
||||
|
||||
Parameters~
|
||||
{opts} `(table)` Options.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniIndentscope.draw()*
|
||||
`MiniIndentscope.draw`({scope}, {opts})
|
||||
Draw scope manually
|
||||
|
||||
Scope is visualized as a vertical line withing scope's body range at column
|
||||
equal to border indent plus one (or body indent if border is absent).
|
||||
Numbering starts from one.
|
||||
|
||||
Parameters~
|
||||
{scope} `(table)` Scope. Default: output of |MiniIndentscope.get_scope|
|
||||
with default arguments.
|
||||
{opts} `(table)` Options. Currently supported:
|
||||
- <animation_fun> - animation function for drawing. See
|
||||
|MiniIndentscope-drawing| and |MiniIndentscope.gen_animation()|.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniIndentscope.undraw()*
|
||||
`MiniIndentscope.undraw`()
|
||||
Undraw currently visible scope manually
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniIndentscope.gen_animation()*
|
||||
`MiniIndentscope.gen_animation`({easing}, {opts})
|
||||
Generate builtin animation function
|
||||
|
||||
This is a builtin source to generate animation function for usage in
|
||||
`MiniIndentscope.config.draw.animation`. Most of them are variations of
|
||||
common easing functions, which provide certain type of progression for
|
||||
revealing scope visual indicator.
|
||||
|
||||
Supported easing types:
|
||||
- `'none'` - show indicator immediately. Equivalent to animation function
|
||||
always returning 0.
|
||||
- `'linear'` - linear progression.
|
||||
- Quadratic progression:
|
||||
- `'quadraticIn'` - accelerating from zero speed.
|
||||
- `'quadraticOut'` - decelerating to zero speed.
|
||||
- `'quadraticInOut'` - accelerating halfway, decelerating after.
|
||||
- Cubic progression:
|
||||
- `'cubicIn'` - accelerating from zero speed.
|
||||
- `'cubicOut'` - decelerating to zero speed.
|
||||
- `'cubicInOut'` - accelerating halfway, decelerating after.
|
||||
- Quartic progression:
|
||||
- `'quarticIn'` - accelerating from zero speed.
|
||||
- `'quarticOut'` - decelerating to zero speed.
|
||||
- `'quarticInOut'` - accelerating halfway, decelerating after.
|
||||
- Exponential progression:
|
||||
- `'exponentialIn'` - accelerating from zero speed.
|
||||
- `'exponentialOut'` - decelerating to zero speed.
|
||||
- `'exponentialInOut'` - accelerating halfway, decelerating after.
|
||||
|
||||
Customization of duration and other general behavior of output animation
|
||||
function is done through `opts` argument.
|
||||
|
||||
Parameters~
|
||||
{easing} `(string)` One of supported easing types.
|
||||
{opts} `(table)` Options that control progression. Possible keys:
|
||||
- <duration> `(number)` - duration (in ms) of a unit. Default: 20.
|
||||
- <unit> `(string)` - which unit's duration `opts.duration` controls. One
|
||||
of "step" (default; ensures average duration of step to be `opts.duration`)
|
||||
or "total" (ensures fixed total duration regardless of scope's range).
|
||||
|
||||
Return~
|
||||
`(function)` Animation function (see |MiniIndentscope-drawing|).
|
||||
|
||||
Examples~
|
||||
- Don't use animation: `gen_animation('none')`
|
||||
- Use quadratic "out" easing with total duration of 1000 ms:
|
||||
`gen_animation('quadraticOut', { duration = 1000, unit = 'total' })`
|
||||
|
||||
See also~
|
||||
|MiniIndentscope-drawing| for more information about how drawing is done.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniIndentscope.move_cursor()*
|
||||
`MiniIndentscope.move_cursor`({side}, {use_border}, {scope})
|
||||
Move cursor within scope
|
||||
|
||||
Cursor is placed on a first non-blank character of target line.
|
||||
|
||||
Parameters~
|
||||
{side} `(string)` One of "top" or "bottom".
|
||||
{use_border} `(boolean)` Whether to move to border or withing scope's body.
|
||||
If particular border is absent, body is used.
|
||||
{scope} `(table)` Scope to use. Default: output of |MiniIndentscope.get_scope()|.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniIndentscope.operator()*
|
||||
`MiniIndentscope.operator`({side}, {add_to_jumplist})
|
||||
Function for motion mappings
|
||||
|
||||
Move to a certain side of border. Respects |count| and dot-repeat (in
|
||||
operator-pending mode). Doesn't move cursor for scope that is not shown
|
||||
(drawing indent less that zero).
|
||||
|
||||
Parameters~
|
||||
{side} `(string)` One of "top" or "bottom".
|
||||
{add_to_jumplist} `(boolean)` Whether to add movement to jump list. It is
|
||||
`true` only for Normal mode mappings.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniIndentscope.textobject()*
|
||||
`MiniIndentscope.textobject`({use_border})
|
||||
Function for textobject mappings
|
||||
|
||||
Respects |count| and dot-repeat (in operator-pending mode). Doesn't work
|
||||
for scope that is not shown (drawing indent less that zero).
|
||||
|
||||
Parameters~
|
||||
{use_border} `(boolean)` Whether to include border in textobject. When
|
||||
`true` and `try_as_border` option is `false`, allows "chaining" calls for
|
||||
incremental selection.
|
||||
|
||||
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
|
@ -0,0 +1,192 @@
|
|||
==============================================================================
|
||||
------------------------------------------------------------------------------
|
||||
*mini.jump*
|
||||
*MiniJump*
|
||||
Smarter forward/backward jumping to a single character.
|
||||
|
||||
Features:
|
||||
- Extend f, F, t, T to work on multiple lines.
|
||||
- Repeat jump by pressing f, F, t, T again. It is reset when cursor moved
|
||||
as a result of not jumping or timeout after idle time (duration
|
||||
customizable).
|
||||
- Highlight (after customizable delay) all possible target characters and
|
||||
stop it after some (customizable) idle time.
|
||||
- Normal, Visual, and Operator-pending (with full dot-repeat) modes are
|
||||
supported.
|
||||
|
||||
This module follows vim's 'ignorecase' and 'smartcase' options. When
|
||||
'ignorecase' is set, f, F, t, T will match case-insensitively. When
|
||||
'smartcase' is also set, f, F, t, T will only match lowercase
|
||||
characters case-insensitively.
|
||||
|
||||
# Setup~
|
||||
|
||||
This module needs a setup with `require('mini.jump').setup({})`
|
||||
(replace `{}` with your `config` table). It will create global Lua table
|
||||
`MiniJump` which you can use for scripting or manually (with
|
||||
`:lua MiniJump.*`).
|
||||
|
||||
See |MiniJump.config| for `config` structure and default values.
|
||||
|
||||
You can override runtime config settings locally to buffer inside
|
||||
`vim.b.minijump_config` which should have same structure as
|
||||
`MiniJump.config`. See |mini.nvim-buffer-local-config| for more details.
|
||||
|
||||
# Highlight groups~
|
||||
|
||||
* `MiniJump` - all possible cursor positions.
|
||||
|
||||
To change any highlight group, modify it directly with |:highlight|.
|
||||
|
||||
# Disabling~
|
||||
|
||||
To disable core functionality, set `g:minijump_disable` (globally) or
|
||||
`b:minijump_disable` (for a buffer) to `v:true`. Considering high number of
|
||||
different scenarios and customization intentions, writing exact rules for
|
||||
disabling module's functionality is left to user. See
|
||||
|mini.nvim-disabling-recipes| for common recipes.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniJump.setup()*
|
||||
`MiniJump.setup`({config})
|
||||
Module setup
|
||||
|
||||
Parameters~
|
||||
{config} `(table)` Module config table. See |MiniJump.config|.
|
||||
|
||||
Usage~
|
||||
`require('mini.jump').setup({})` (replace `{}` with your `config` table)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniJump.config*
|
||||
`MiniJump.config`
|
||||
Module config
|
||||
|
||||
Default values:
|
||||
>
|
||||
MiniJump.config = {
|
||||
-- Module mappings. Use `''` (empty string) to disable one.
|
||||
mappings = {
|
||||
forward = 'f',
|
||||
backward = 'F',
|
||||
forward_till = 't',
|
||||
backward_till = 'T',
|
||||
repeat_jump = ';',
|
||||
},
|
||||
|
||||
-- Delay values (in ms) for different functionalities. Set any of them to
|
||||
-- a very big number (like 10^7) to virtually disable.
|
||||
delay = {
|
||||
-- Delay between jump and highlighting all possible jumps
|
||||
highlight = 250,
|
||||
|
||||
-- Delay between jump and automatic stop if idle (no jump is done)
|
||||
idle_stop = 10000000,
|
||||
},
|
||||
}
|
||||
<
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniJump.state*
|
||||
`MiniJump.state`
|
||||
Data about jumping state
|
||||
|
||||
It stores various information used in this module. All elements, except
|
||||
`jumping`, is about the latest jump. They are used as default values for
|
||||
similar arguments.
|
||||
|
||||
Class~
|
||||
{JumpingState}
|
||||
|
||||
Fields~
|
||||
{target} `(string)` The string to jump to.
|
||||
{backward} `(boolean)` Whether to jump backward.
|
||||
{till} `(boolean)` Whether to jump just before/after the match instead of
|
||||
exactly on target. This includes positioning cursor past the end of
|
||||
previous/current line. Note that with backward jump this might lead to
|
||||
cursor being on target if can't be put past the line.
|
||||
{n_times} `(number)` Number of times to perform consecutive jumps.
|
||||
{mode} `(string)` Mode of latest jump (output of |mode()| with non-zero argument).
|
||||
{jumping} `(boolean)` Whether module is currently in "jumping mode": usage of
|
||||
|MiniJump.smart_jump| and all mappings won't require target.
|
||||
|
||||
Initial values:
|
||||
>
|
||||
MiniJump.state = {
|
||||
target = nil,
|
||||
backward = false,
|
||||
till = false,
|
||||
n_times = 1,
|
||||
mode = nil,
|
||||
jumping = false,
|
||||
}
|
||||
<
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniJump.jump()*
|
||||
`MiniJump.jump`({target}, {backward}, {till}, {n_times})
|
||||
Jump to target
|
||||
|
||||
Takes a string and jumps to its first occurrence in desired direction.
|
||||
|
||||
All default values are taken from |MiniJump.state| to emulate latest jump.
|
||||
|
||||
Parameters~
|
||||
{target} `(string)` The string to jump to.
|
||||
{backward} `(boolean)` Whether to jump backward.
|
||||
{till} `(boolean)` Whether to jump just before/after the match instead of
|
||||
exactly on target. This includes positioning cursor past the end of
|
||||
previous/current line. Note that with backward jump this might lead to
|
||||
cursor being on target if can't be put past the line.
|
||||
{n_times} `(number)` Number of times to perform consecutive jumps.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniJump.smart_jump()*
|
||||
`MiniJump.smart_jump`({backward}, {till})
|
||||
Make smart jump
|
||||
|
||||
If the last movement was a jump, perform another jump with the same target.
|
||||
Otherwise, wait for a target input (via |getchar()|). Respects |v:count|.
|
||||
|
||||
All default values are taken from |MiniJump.state| to emulate latest jump.
|
||||
|
||||
Parameters~
|
||||
{backward} `(boolean)` Whether to jump backward.
|
||||
{till} `(boolean)` Whether to jump just before/after the match instead of
|
||||
exactly on target. This includes positioning cursor past the end of
|
||||
previous/current line. Note that with backward jump this might lead to
|
||||
cursor being on target if can't be put past the line.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniJump.expr_jump()*
|
||||
`MiniJump.expr_jump`({backward}, {till})
|
||||
Make expression jump
|
||||
|
||||
Cache information about the jump and return string with command to perform
|
||||
jump. Designed to be used inside Operator-pending mapping (see
|
||||
|omap-info|). Always asks for target (via |getchar()|). Respects |v:count|.
|
||||
|
||||
All default values are taken from |MiniJump.state| to emulate latest jump.
|
||||
|
||||
Parameters~
|
||||
{backward} `(boolean)` Whether to jump backward.
|
||||
{till} `(boolean)` Whether to jump just before/after the match instead of
|
||||
exactly on target. This includes positioning cursor past the end of
|
||||
previous/current line. Note that with backward jump this might lead to
|
||||
cursor being on target if can't be put past the line.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniJump.stop_jumping()*
|
||||
`MiniJump.stop_jumping`()
|
||||
Stop jumping
|
||||
|
||||
Removes highlights (if any) and forces the next smart jump to prompt for
|
||||
the target. Automatically called on appropriate Neovim |events|.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniJump.on_cursormoved()*
|
||||
`MiniJump.on_cursormoved`()
|
||||
Act on |CursorMoved|
|
||||
|
||||
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
|
@ -0,0 +1,395 @@
|
|||
==============================================================================
|
||||
------------------------------------------------------------------------------
|
||||
*mini.jump2d*
|
||||
*MiniJump2d*
|
||||
Jump within visible lines via iterative label filtering.
|
||||
|
||||
Features:
|
||||
- Make jump by iterative filtering of possible, equally considered jump
|
||||
spots until there is only one. Filtering is done by typing a label
|
||||
character that is visualized at jump spot.
|
||||
- Customizable:
|
||||
- Way of computing possible jump spots with opinionated default.
|
||||
- Characters used to label jump spots during iterative filtering.
|
||||
- Action hooks to be executed at certain events during jump.
|
||||
- Allowed windows: current and/or not current.
|
||||
- Allowed lines: whether to process blank or folded lines, lines
|
||||
before/at/after cursor line, etc. Example: user can configure to look
|
||||
for spots only inside current window at or after cursor line.
|
||||
Example: user can configure to look for word starts only inside current
|
||||
window at or after cursor line with 'j' and 'k' labels performing some
|
||||
action after jump.
|
||||
- Works in Visual and Operator-pending (with dot-repeat) modes.
|
||||
- Preconfigured ways of computing jump spots (see |MiniJump2d.builtin_opts|).
|
||||
- Works with multibyte characters.
|
||||
|
||||
General overview of how jump is intended to be performed:
|
||||
- Lock eyes on desired location ("spot") recognizable by future jump.
|
||||
Should be within visible lines at place where cursor can be placed.
|
||||
- Initiate jump. Either by custom keybinding or with a call to
|
||||
|MiniJump2d.start()| (allows customization options). This will highlight
|
||||
all possible jump spots with their labels (letters from "a" to "z" by
|
||||
default). For more details, read |MiniJump2d.start()| and |MiniJump2d.config|.
|
||||
- Type character that appeared over desired location. If its label was
|
||||
unique, jump is performed. If it wasn't unique, possible jump spots are
|
||||
filtered to those having the same label character.
|
||||
- Repeat previous step until there is only one possible jump spot or type `<CR>`
|
||||
to jump to first available jump spot. Typing anything else stops jumping
|
||||
without moving cursor.
|
||||
|
||||
# Setup~
|
||||
|
||||
This module needs a setup with `require('mini.jump2d').setup({})` (replace
|
||||
`{}` with your `config` table). It will create global Lua table
|
||||
`MiniJump2d` which you can use for scripting or manually (with
|
||||
`:lua MiniJump2d.*`).
|
||||
|
||||
See |MiniJump2d.config| for available config settings.
|
||||
|
||||
You can override runtime config settings locally to buffer inside
|
||||
`vim.b.minijump2d_config` which should have same structure as
|
||||
`MiniJump2d.config`. See |mini.nvim-buffer-local-config| for more details.
|
||||
|
||||
# Example usage~
|
||||
|
||||
- Modify default jumping to use only current window at or after cursor line: >
|
||||
require('mini.jump2d').setup({
|
||||
allowed_lines = { cursor_before = false },
|
||||
allowed_windows = { not_current = false },
|
||||
})
|
||||
- `lua MiniJump2d.start(MiniJump2d.builtin_opts.line_start)` - jump to word
|
||||
start using combination of options supplied in |MiniJump2d.config| and
|
||||
|MiniJump2d.builtin_opts.line_start|.
|
||||
- `lua MiniJump2d.start(MiniJump2d.builtin_opts.single_character)` - jump
|
||||
to single character typed after executing this command.
|
||||
- See more examples in |MiniJump2d.start| and |MiniJump2d.builtin_opts|.
|
||||
|
||||
# Comparisons~
|
||||
|
||||
- 'phaazon/hop.nvim':
|
||||
- Both are fast, customizable, and extensible (user can write their own
|
||||
ways to define jump spots).
|
||||
- Both have several builtin ways to specify type of jump (word start,
|
||||
line start, one character or query based on user input). 'hop.nvim'
|
||||
does that by exporting many targeted Neovim commands, while this
|
||||
module has preconfigured basic options leaving others to
|
||||
customization with Lua code (see |MiniJump2d.builtin_opts|).
|
||||
- 'hop.nvim' computes labels (called "hints") differently. Contrary to
|
||||
this module deliberately not having preference of one jump spot over
|
||||
another, 'hop.nvim' uses specialized algorithm that produces sequence
|
||||
of keys in a slightly biased manner: some sequences are intentionally
|
||||
shorter than the others (leading to fewer average keystrokes). They
|
||||
are put near cursor (by default) and highlighted differently. Final
|
||||
order of sequences is based on distance to the cursor.
|
||||
- 'hop.nvim' visualizes labels differently. It is designed to show
|
||||
whole sequences at once, while this module intentionally shows only
|
||||
current one at a time.
|
||||
- 'mini.jump2d' has opinionated default algorithm of computing jump
|
||||
spots. See |MiniJump2d.default_spotter|.
|
||||
|
||||
# Highlight groups~
|
||||
|
||||
* `MiniJump2dSpot` - highlighting of jump spots. By default it uses label
|
||||
with highest contrast while not being too visually demanding: white on
|
||||
black for dark 'background', black on white for light. If it doesn't
|
||||
suit your liking, try couple of these alternatives (or choose your own,
|
||||
of course):
|
||||
- `hi MiniJump2dSpot gui=reverse` - reverse underlying highlighting (more
|
||||
colorful while being visible in any colorscheme).
|
||||
- `hi MiniJump2dSpot gui=bold,italic` - bold italic.
|
||||
- `hi MiniJump2dSpot gui=undercurl guisp=red` - red undercurl.
|
||||
|
||||
To change any highlight group, modify it directly with |:highlight|.
|
||||
|
||||
# Disabling~
|
||||
|
||||
To disable, set `g:minijump2d_disable` (globally) or `b:minijump2d_disable`
|
||||
(for a buffer) to `v:true`. Considering high number of different scenarios
|
||||
and customization intentions, writing exact rules for disabling module's
|
||||
functionality is left to user. See |mini.nvim-disabling-recipes| for common
|
||||
recipes.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniJump2d.setup()*
|
||||
`MiniJump2d.setup`({config})
|
||||
Module setup
|
||||
|
||||
Parameters~
|
||||
{config} `(table)` Module config table. See |MiniJump2d.config|.
|
||||
|
||||
Usage~
|
||||
`require('mini.jump2d').setup({})` (replace `{}` with your `config` table)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniJump2d.config*
|
||||
`MiniJump2d.config`
|
||||
Module config
|
||||
|
||||
Default values:
|
||||
>
|
||||
MiniJump2d.config = {
|
||||
-- Function producing jump spots (byte indexed) for a particular line.
|
||||
-- For more information see |MiniJump2d.start|.
|
||||
-- If `nil` (default) - use |MiniJump2d.default_spotter|
|
||||
spotter = nil,
|
||||
|
||||
-- Characters used for labels of jump spots (in supplied order)
|
||||
labels = 'abcdefghijklmnopqrstuvwxyz',
|
||||
|
||||
-- Which lines are used for computing spots
|
||||
allowed_lines = {
|
||||
blank = true, -- Blank line (not sent to spotter even if `true`)
|
||||
cursor_before = true, -- Lines before cursor line
|
||||
cursor_at = true, -- Cursor line
|
||||
cursor_after = true, -- Lines after cursor line
|
||||
fold = true, -- Start of fold (not sent to spotter even if `true`)
|
||||
},
|
||||
|
||||
-- Which windows from current tabpage are used for visible lines
|
||||
allowed_windows = {
|
||||
current = true,
|
||||
not_current = true,
|
||||
},
|
||||
|
||||
-- Functions to be executed at certain events
|
||||
hooks = {
|
||||
before_start = nil, -- Before jump start
|
||||
after_jump = nil, -- After jump was actually done
|
||||
},
|
||||
|
||||
-- Module mappings. Use `''` (empty string) to disable one.
|
||||
mappings = {
|
||||
start_jumping = '<CR>',
|
||||
},
|
||||
}
|
||||
<
|
||||
# Options~
|
||||
|
||||
## Spotter function~
|
||||
|
||||
Actual computation of possible jump spots is done through spotter function.
|
||||
It should have the following arguments:
|
||||
- `line_num` is a line number inside buffer.
|
||||
- `args` - table with additional arguments:
|
||||
- {win_id} - identifier of a window where input line number is from.
|
||||
- {win_id_init} - identifier of a window which was current when
|
||||
`MiniJump2d.start()` was called.
|
||||
|
||||
Its output is a list of byte-indexed positions that should be considered as
|
||||
possible jump spots for this particular line in this particular window.
|
||||
Note: for a more aligned visualization this list should be (but not
|
||||
strictly necessary) sorted increasingly.
|
||||
|
||||
Note: spotter function is always called with `win_id` window being
|
||||
"temporary current" (see |nvim_win_call|). This allows using builtin
|
||||
Vimscript functions that operate only inside current window.
|
||||
|
||||
## Allowed lines~
|
||||
|
||||
Option `allowed_lines` controls which lines will be used for computing
|
||||
possible jump spots:
|
||||
- If `blank` or `fold` is `true`, it is possible to jump to first column of blank
|
||||
line (determined by |prevnonblank|) or first folded one (determined by
|
||||
|foldclosed|) respectively. Otherwise they are skipped. These lines are
|
||||
not processed by spotter function even if the option is `true`.
|
||||
- If `cursor_before`, (`cursor_at`, `cursor_after`) is `true`, lines before
|
||||
(at, after) cursor line of all processed windows are forwarded to spotter
|
||||
function. Otherwise, they don't. This allows control of jump "direction".
|
||||
|
||||
## Hooks~
|
||||
|
||||
Following hook functions can be used to further tweak jumping experience:
|
||||
- `before_start` - called without arguments first thing when jump starts.
|
||||
One of the possible use cases is to ask for user input and update spotter
|
||||
function with it.
|
||||
- `after_jump` - called after jump was actually done. Useful to make
|
||||
post-adjustments (like move cursor to first non-whitespace character).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniJump2d.start()*
|
||||
`MiniJump2d.start`({opts})
|
||||
Start jumping
|
||||
|
||||
Compute possible jump spots, visualize them and wait for iterative filtering.
|
||||
|
||||
First computation of possible jump spots~
|
||||
|
||||
- Process allowed windows (current and/or not current; controlled by
|
||||
`allowed_windows` option) by visible lines from top to bottom. For each
|
||||
one see if it is allowed (controlled by `allowed_lines` option). If not
|
||||
allowed, then do nothing. If allowed and should be processed by
|
||||
`spotter`, process it.
|
||||
- Apply spotter function from `spotter` option for each appropriate line
|
||||
and concatenate outputs. This means that eventual order of jump spots
|
||||
aligns with lexicographical order within "window id" - "line number" -
|
||||
"position in `spotter` output" tuples.
|
||||
- For each possible jump compute its label: a single character from
|
||||
`labels` option used to filter jump spots. Each possible label character
|
||||
might be used more than once to label several "consecutive" jump spots.
|
||||
It is done in an optimal way under assumption of no preference of one
|
||||
spot over another. Basically, it means "use all labels at each step of
|
||||
iterative filtering as equally as possible".
|
||||
|
||||
Visualization~
|
||||
|
||||
Current label for each possible jump spot is shown at that position
|
||||
overriding everything underneath it.
|
||||
|
||||
Iterative filtering~
|
||||
|
||||
Labels of possible jump spots are computed in order to use them as equally
|
||||
as possible.
|
||||
|
||||
Example:
|
||||
- With `abc` as `labels` option, initial labels for 10 possible jumps
|
||||
are "aaaabbbccc". As there are 10 spots which should be "coded" with 3
|
||||
symbols, at least 2 symbols need 3 steps to filter them out. With current
|
||||
implementation those are always the "first ones".
|
||||
- After typing `a`, it filters first four jump spots and recomputes its
|
||||
labels to be "aabc".
|
||||
- After typing `a` again, it filters first two spots and recomputes its
|
||||
labels to be "ab".
|
||||
- After typing either `a` or `b` it filters single spot and makes jump.
|
||||
|
||||
With default 26 labels for most real-world cases 2 steps is enough for
|
||||
default spotter function. Rarely 3 steps are needed with several windows.
|
||||
|
||||
Parameters~
|
||||
{opts} `(table)` Configuration of jumping, overriding global and buffer
|
||||
local values.config|. Has the same structure as |MiniJump2d.config|
|
||||
without <mappings> field. Extra allowed fields:
|
||||
- <hl_group> - which highlight group to use (default: "MiniJump2dSpot").
|
||||
|
||||
Usage~
|
||||
- Start default jumping:
|
||||
`MiniJump2d.start()`
|
||||
- Jump to word start:
|
||||
`MiniJump2d.start(MiniJump2d.builtin_opts.word_start)`
|
||||
- Jump to single character from user input (follow by typing one character):
|
||||
`MiniJump2d.start(MiniJump2d.builtin_opts.single_character)`
|
||||
- Jump to first character of punctuation group only inside current window
|
||||
which is placed at cursor line; visualize with 'hl-Search': >
|
||||
MiniJump2d.start({
|
||||
spotter = MiniJump2d.gen_pattern_spotter('%p+'),
|
||||
allowed_lines = { cursor_before = false, cursor_after = false },
|
||||
allowed_windows = { not_current = false },
|
||||
hl_group = 'Search'
|
||||
})
|
||||
|
||||
See also~
|
||||
|MiniJump2d.config|
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniJump2d.stop()*
|
||||
`MiniJump2d.stop`()
|
||||
Stop jumping
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniJump2d.gen_pattern_spotter()*
|
||||
`MiniJump2d.gen_pattern_spotter`({pattern}, {side})
|
||||
Generate spotter for Lua pattern
|
||||
|
||||
Parameters~
|
||||
{pattern} `(string|nil)` Lua pattern. Default: `'[^%s%p]+'` which matches group
|
||||
of "non-whitespace non-punctuation characters" (basically a way of saying
|
||||
"group of alphanumeric characters" that works with multibyte characters).
|
||||
{side} `(string|nil)` Which side of pattern match should be considered as
|
||||
jumping spot. Should be one of 'start' (start of match, default), 'end'
|
||||
(inclusive end of match), or 'none' (match for spot is done manually
|
||||
inside pattern with plain `()` matching group).
|
||||
|
||||
Usage~
|
||||
- Match any punctuation:
|
||||
`MiniJump2d.gen_pattern_spotter('%p')`
|
||||
- Match first from line start non-whitespace character:
|
||||
`MiniJump2d.gen_pattern_spotter('^%s*%S', 'end')`
|
||||
- Match start of last word:
|
||||
`MiniJump2d.gen_pattern_spotter('[^%s%p]+[%s%p]-$', 'start')`
|
||||
- Match letter followed by another letter (example of manual matching
|
||||
inside pattern):
|
||||
`MiniJump2d.gen_pattern_spotter('%a()%a', 'none')`
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniJump2d.default_spotter*
|
||||
`MiniJump2d.default_spotter`
|
||||
Default spotter function
|
||||
|
||||
Spot is possible for jump if it is one of the following:
|
||||
- Start or end of non-whitespace character group.
|
||||
- Alphanumeric character followed or preceeded by punctuation (useful for
|
||||
snake case names).
|
||||
- Start of uppercase character group (useful for camel case names). Usually
|
||||
only Lating alphabet is recognized due to Lua patterns shortcomings.
|
||||
|
||||
These rules are derived in an attempt to balance between two intentions:
|
||||
- Allow as much useful jumping spots as possible.
|
||||
- Make labeled jump spots easily distinguishable.
|
||||
|
||||
Usually takes from 2 to 3 keystrokes to get to destination.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniJump2d.builtin_opts*
|
||||
`MiniJump2d.builtin_opts`
|
||||
Table with builtin `opts` values for |MiniJump2d.start()|
|
||||
|
||||
Each element of table is itself a table defining one or several options for
|
||||
`MiniJump2d.start()`. Read help description to see which options it defines
|
||||
(like in |MiniJump2d.builtin_opts.line_start|).
|
||||
|
||||
Usage~
|
||||
Using |MiniJump2d.builtin_opts.line_start| as example:
|
||||
- Command:
|
||||
`:lua MiniJump2d.start(MiniJump2d.builtin_opts.line_start)`
|
||||
- Custom mapping: >
|
||||
vim.api.nvim_set_keymap(
|
||||
'n', '<CR>',
|
||||
'<Cmd>lua MiniJump2d.start(MiniJump2d.builtin_opts.line_start)<CR>', {}
|
||||
)
|
||||
- Inside |MiniJump2d.setup| (make sure to use all defined options): >
|
||||
local jump2d = require('mini.jump2d')
|
||||
local jump_line_start = jump2d.builtin_opts.line_start
|
||||
jump2d.setup({
|
||||
spotter = jump_line_start.spotter,
|
||||
hooks = { after_jump = jump_line_start.hooks.after_jump }
|
||||
})
|
||||
<
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniJump2d.builtin_opts.default*
|
||||
`MiniJump2d.builtin_opts.default`
|
||||
Jump with |MiniJump2d.default_spotter()|
|
||||
|
||||
Defines `spotter`.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniJump2d.builtin_opts.line_start*
|
||||
`MiniJump2d.builtin_opts.line_start`
|
||||
Jump to line start
|
||||
|
||||
Defines `spotter` and `hooks.after_jump`.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniJump2d.builtin_opts.word_start*
|
||||
`MiniJump2d.builtin_opts.word_start`
|
||||
Jump to word start
|
||||
|
||||
Defines `spotter`.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniJump2d.builtin_opts.single_character*
|
||||
`MiniJump2d.builtin_opts.single_character`
|
||||
Jump to single character taken from user input
|
||||
|
||||
Defines `spotter`, `allowed_lines.blank`, `allowed_lines.fold`, and
|
||||
`hooks.before_start`.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniJump2d.builtin_opts.query*
|
||||
`MiniJump2d.builtin_opts.query`
|
||||
Jump to query taken from user input
|
||||
|
||||
Defines `spotter`, `allowed_lines.blank`, `allowed_lines.fold`, and
|
||||
`hooks.before_start`.
|
||||
|
||||
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
|
@ -0,0 +1,184 @@
|
|||
==============================================================================
|
||||
------------------------------------------------------------------------------
|
||||
*mini.misc*
|
||||
*MiniMisc*
|
||||
Miscellaneous useful functions.
|
||||
|
||||
# Setup~
|
||||
|
||||
This module doesn't need setup, but it can be done to improve usability.
|
||||
Setup with `require('mini.misc').setup({})` (replace `{}` with your
|
||||
`config` table). It will create global Lua table `MiniMisc` which you can
|
||||
use for scripting or manually (with `:lua MiniMisc.*`).
|
||||
|
||||
See |MiniMisc.config| for `config` structure and default values.
|
||||
|
||||
This module doesn't have runtime options, so using `vim.b.minimisc_config`
|
||||
will have no effect here.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniMisc.setup()*
|
||||
`MiniMisc.setup`({config})
|
||||
Module setup
|
||||
|
||||
Parameters~
|
||||
{config} `(table)` Module config table. See |MiniMisc.config|.
|
||||
|
||||
Usage~
|
||||
`require('mini.misc').setup({})` (replace `{}` with your `config` table)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniMisc.config*
|
||||
`MiniMisc.config`
|
||||
Module config
|
||||
|
||||
Default values:
|
||||
>
|
||||
MiniMisc.config = {
|
||||
-- Array of fields to make global (to be used as independent variables)
|
||||
make_global = { 'put', 'put_text' },
|
||||
}
|
||||
<
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniMisc.bench_time()*
|
||||
`MiniMisc.bench_time`({f}, {n}, {...})
|
||||
Execute `f` several times and time how long it took
|
||||
|
||||
Parameters~
|
||||
{f} `(function)` Function which execution to benchmark.
|
||||
{n} `(number)` Number of times to execute `f(...)`. Default: 1.
|
||||
{...} `(any)` Arguments when calling `f`.
|
||||
|
||||
Return~
|
||||
`(...)` Table with durations (in seconds; up to microseconds) and
|
||||
output of (last) function execution.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniMisc.get_gutter_width()*
|
||||
`MiniMisc.get_gutter_width`({win_id})
|
||||
Compute width of gutter (info column on the left of the window)
|
||||
|
||||
Parameters~
|
||||
{win_id} `(number)` Window identifier (see |win_getid()|) for which gutter
|
||||
width is computed. Default: 0 for current.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniMisc.put()*
|
||||
`MiniMisc.put`({...})
|
||||
Print Lua objects in command line
|
||||
|
||||
Parameters~
|
||||
{...} `(any)` Any number of objects to be printed each on separate line.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniMisc.put_text()*
|
||||
`MiniMisc.put_text`({...})
|
||||
Print Lua objects in current buffer
|
||||
|
||||
Parameters~
|
||||
{...} `(any)` Any number of objects to be printed each on separate line.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniMisc.resize_window()*
|
||||
`MiniMisc.resize_window`({win_id}, {text_width})
|
||||
Resize window to have exact number of editable columns
|
||||
|
||||
Parameters~
|
||||
{win_id} `(number)` Window identifier (see |win_getid()|) to be resized.
|
||||
Default: 0 for current.
|
||||
{text_width} `(number)` Number of editable columns resized window will
|
||||
display. Default: first element of 'colorcolumn' or otherwise 'textwidth'
|
||||
(using screen width as its default but not more than 79).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniMisc.stat_summary()*
|
||||
`MiniMisc.stat_summary`({t})
|
||||
Compute summary statistics of numerical array
|
||||
|
||||
This might be useful to compute summary of time benchmarking with
|
||||
|MiniMisc.bench_time|.
|
||||
|
||||
Parameters~
|
||||
{t} `(table)` Array (table suitable for `ipairs`) of numbers.
|
||||
|
||||
Return~
|
||||
`(table)` Table with summary values under following keys (may be
|
||||
extended in the future): <maximum>, <mean>, <median>, <minimum>, <n>
|
||||
(number of elements), <sd> (sample standard deviation).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniMisc.tbl_head()*
|
||||
`MiniMisc.tbl_head`({t}, {n})
|
||||
Return "first" elements of table as decided by `pairs`
|
||||
|
||||
Note: order of elements might vary.
|
||||
|
||||
Parameters~
|
||||
{t} `(table)` Input table.
|
||||
{n} `(number)` Maximum number of first elements. Default: 5.
|
||||
|
||||
Return~
|
||||
`(table)` Table with at most `n` first elements of `t` (with same keys).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniMisc.tbl_tail()*
|
||||
`MiniMisc.tbl_tail`({t}, {n})
|
||||
Return "last" elements of table as decided by `pairs`
|
||||
|
||||
This function makes two passes through elements of `t`:
|
||||
- First to count number of elements.
|
||||
- Second to construct result.
|
||||
|
||||
Note: order of elements might vary.
|
||||
|
||||
Parameters~
|
||||
{t} `(table)` Input table.
|
||||
{n} `(number)` Maximum number of last elements. Default: 5.
|
||||
|
||||
Return~
|
||||
`(table)` Table with at most `n` last elements of `t` (with same keys).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniMisc.use_nested_comments()*
|
||||
`MiniMisc.use_nested_comments`({buf_id})
|
||||
Add possibility of nested comment leader
|
||||
|
||||
This works by parsing 'commentstring' buffer option, extracting
|
||||
non-whitespace comment leader (symbols on the left of commented line), and
|
||||
locally modifying 'comments' option (by prepending `n:<leader>`). Does
|
||||
nothing if 'commentstring' is empty or has comment symbols both in front
|
||||
and back (like "/*%s*/").
|
||||
|
||||
Nested comment leader added with this function is useful for formatting
|
||||
nested comments. For example, have in Lua "first-level" comments with '--'
|
||||
and "second-level" comments with '----'. With nested comment leader second
|
||||
type can be formatted with `gq` in the same way as first one.
|
||||
|
||||
Recommended usage is with |autocmd|:
|
||||
`autocmd BufEnter * lua pcall(require('mini.misc').use_nested_comments)`
|
||||
|
||||
Note: for most filetypes 'commentstring' option is added only when buffer
|
||||
with this filetype is entered, so using non-current `buf_id` can not lead
|
||||
to desired effect.
|
||||
|
||||
Parameters~
|
||||
{buf_id} `(number)` Buffer identifier (see |bufnr()|) in which function
|
||||
will operate. Default: 0 for current.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniMisc.zoom()*
|
||||
`MiniMisc.zoom`({buf_id}, {config})
|
||||
Zoom in and out of a buffer, making it full screen in a floating window
|
||||
|
||||
This function is useful when working with multiple windows but temporarily
|
||||
needing to zoom into one to see more of the code from that buffer. Call it
|
||||
again (without arguments) to zoom out.
|
||||
|
||||
Parameters~
|
||||
{buf_id} `(number)` Buffer identifier (see |bufnr()|) to be zoomed.
|
||||
Default: 0 for current.
|
||||
{config} `(table)` Optional config for window (as for |nvim_open_win()|).
|
||||
|
||||
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
|
@ -0,0 +1,285 @@
|
|||
==============================================================================
|
||||
------------------------------------------------------------------------------
|
||||
*mini.pairs*
|
||||
*MiniPairs*
|
||||
Minimal and fast autopairs.
|
||||
|
||||
Features:
|
||||
- Functionality to work with 'paired' characters conditional on cursor's
|
||||
neighborhood (two characters to its left and right).
|
||||
- Usage should be through making appropriate mappings using |MiniPairs.map|
|
||||
or in |MiniPairs.setup| (for global mapping), |MiniPairs.map_buf| (for
|
||||
buffer mapping).
|
||||
- Pairs get automatically registered to be recognized by `<BS>` and `<CR>`.
|
||||
|
||||
What it doesn't do:
|
||||
- It doesn't support multiple characters as "open" and "close" symbols. Use
|
||||
snippets for that.
|
||||
- It doesn't support dependency on filetype. Use |i_CTRL-V| to insert
|
||||
single symbol or `autocmd` command or 'after/ftplugin' approach to:
|
||||
- `lua MiniPairs.map_buf(0, 'i', <*>, <pair_info>)` : make new mapping
|
||||
for '<*>' in current buffer.
|
||||
- `lua MiniPairs.unmap_buf(0, 'i', <*>, <pair>)`: unmap key `<*>` while
|
||||
unregistering `<pair>` pair in current buffer. Note: this reverts
|
||||
mapping done by |MiniPairs.map_buf|. If mapping was done with
|
||||
|MiniPairs.map|, unmap for buffer in usual Neovim manner:
|
||||
`inoremap <buffer> <*> <*>` (this maps `<*>` key to do the same it
|
||||
does by default).
|
||||
- Disable module for buffer (see 'Disabling' section).
|
||||
|
||||
# Setup~
|
||||
|
||||
This module needs a setup with `require('mini.pairs').setup({})`
|
||||
(replace `{}` with your `config` table). It will create global Lua table
|
||||
`MiniPairs` which you can use for scripting or manually (with
|
||||
`:lua MiniPairs.*`).
|
||||
|
||||
See |MiniPairs.config| for `config` structure and default values.
|
||||
|
||||
This module doesn't have runtime options, so using `vim.b.minipairs_config`
|
||||
will have no effect here.
|
||||
|
||||
# Example mappings~
|
||||
|
||||
- Register quotes inside `config` of |MiniPairs.setup|: >
|
||||
mappings = {
|
||||
['"'] = { register = { cr = true } },
|
||||
["'"] = { register = { cr = true } },
|
||||
}
|
||||
<
|
||||
- Insert `<>` pair if `<` is typed at line start, don't register for `<CR>`: >
|
||||
lua MiniPairs.map('i', '<', { action = 'open', pair = '<>', neigh_pattern = '\r.', register = { cr = false } })
|
||||
lua MiniPairs.map('i', '>', { action = 'close', pair = '<>', register = { cr = false } })
|
||||
<
|
||||
- Create symmetrical `$$` pair only in Tex files: >
|
||||
au FileType tex lua MiniPairs.map_buf(0, 'i', '$', {action = 'closeopen', pair = '$$'})
|
||||
<
|
||||
# Notes~
|
||||
|
||||
- Make sure to make proper mapping of `<CR>` in order to support completion
|
||||
plugin of your choice:
|
||||
- For |MiniCompletion| see 'Helpful key mappings' section.
|
||||
- For current implementation of "hrsh7th/nvim-cmp" there is no need to
|
||||
make custom mapping. You can use default setup, which will confirm
|
||||
completion selection if popup is visible and expand pair otherwise.
|
||||
- Having mapping in terminal mode can conflict with:
|
||||
- Autopairing capabilities of interpretators (`ipython`, `radian`).
|
||||
- Vim mode of terminal itself.
|
||||
|
||||
# Disabling~
|
||||
|
||||
To disable, set `g:minipairs_disable` (globally) or `b:minipairs_disable`
|
||||
(for a buffer) to `v:true`. Considering high number of different scenarios
|
||||
and customization intentions, writing exact rules for disabling module's
|
||||
functionality is left to user. See |mini.nvim-disabling-recipes| for common
|
||||
recipes.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniPairs.setup()*
|
||||
`MiniPairs.setup`({config})
|
||||
Module setup
|
||||
|
||||
Parameters~
|
||||
{config} `(table)` Module config table. See |MiniPairs.config|.
|
||||
|
||||
Usage~
|
||||
`require('mini.completion').setup({})` (replace `{}` with your `config` table)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniPairs.config*
|
||||
`MiniPairs.config`
|
||||
Module config
|
||||
|
||||
Default values:
|
||||
>
|
||||
MiniPairs.config = {
|
||||
-- In which modes mappings from this `config` should be created
|
||||
modes = { insert = true, command = false, terminal = false },
|
||||
|
||||
-- Global mappings. Each right hand side should be a pair information, a
|
||||
-- table with at least these fields (see more in |MiniPairs.map|):
|
||||
-- - <action> - one of "open", "close", "closeopen".
|
||||
-- - <pair> - two character string for pair to be used.
|
||||
-- By default pair is not inserted after `\`, quotes are not recognized by
|
||||
-- `<CR>`, `'` does not insert pair after a letter.
|
||||
-- Only parts of tables can be tweaked (others will use these defaults).
|
||||
mappings = {
|
||||
['('] = { action = 'open', pair = '()', neigh_pattern = '[^\\].' },
|
||||
['['] = { action = 'open', pair = '[]', neigh_pattern = '[^\\].' },
|
||||
['{'] = { action = 'open', pair = '{}', neigh_pattern = '[^\\].' },
|
||||
|
||||
[')'] = { action = 'close', pair = '()', neigh_pattern = '[^\\].' },
|
||||
[']'] = { action = 'close', pair = '[]', neigh_pattern = '[^\\].' },
|
||||
['}'] = { action = 'close', pair = '{}', neigh_pattern = '[^\\].' },
|
||||
|
||||
['"'] = { action = 'closeopen', pair = '""', neigh_pattern = '[^\\].', register = { cr = false } },
|
||||
["'"] = { action = 'closeopen', pair = "''", neigh_pattern = '[^%a\\].', register = { cr = false } },
|
||||
['`'] = { action = 'closeopen', pair = '``', neigh_pattern = '[^\\].', register = { cr = false } },
|
||||
},
|
||||
}
|
||||
<
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniPairs.map()*
|
||||
`MiniPairs.map`({mode}, {lhs}, {pair_info}, {opts})
|
||||
Make global mapping
|
||||
|
||||
This is a wrapper for |nvim_set_keymap()| but instead of right hand side of
|
||||
mapping (as string) it expects table with pair information:
|
||||
- `action` - one of "open" (for |MiniPairs.open|), "close" (for
|
||||
|MiniPairs.close|), or "closeopen" (for |MiniPairs.closeopen|).
|
||||
- `pair` - two character string to be used as argument for action function.
|
||||
- `neigh_pattern` - optional 'two character' neighborhood pattern to be
|
||||
used as argument for action function. Default: '..' (no restriction from
|
||||
neighborhood).
|
||||
- `register` - optional table with information about whether this pair
|
||||
should be recognized by `<BS>` (in |MiniPairs.bs|) and/or `<CR>` (in
|
||||
|MiniPairs.cr|). Should have boolean elements `bs` and `cr` which are
|
||||
both `true` by default (if not overriden explicitly).
|
||||
|
||||
Using this function instead of |nvim_set_keymap()| allows automatic
|
||||
registration of pairs which will be recognized by `<BS>` and `<CR>`.
|
||||
For Neovim>=0.7 it also infers mapping description from `pair_info`.
|
||||
|
||||
Parameters~
|
||||
{mode} `(string)` `mode` for |nvim_set_keymap()|.
|
||||
{lhs} `(string)` `lhs` for |nvim_set_keymap()|.
|
||||
{pair_info} `(table)` Table with pair information.
|
||||
{opts} `(table)` Optional table `opts` for |nvim_set_keymap()|. Elements
|
||||
`expr` and `noremap` won't be recognized (`true` by default).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniPairs.map_buf()*
|
||||
`MiniPairs.map_buf`({buffer}, {mode}, {lhs}, {pair_info}, {opts})
|
||||
Make buffer mapping
|
||||
|
||||
This is a wrapper for |nvim_buf_set_keymap()| but instead of string right
|
||||
hand side of mapping it expects table with pair information similar to one
|
||||
in |MiniPairs.map|.
|
||||
|
||||
Using this function instead of |nvim_buf_set_keymap()| allows automatic
|
||||
registration of pairs which will be recognized by `<BS>` and `<CR>`.
|
||||
For Neovim>=0.7 it also infers mapping description from `pair_info`.
|
||||
|
||||
Parameters~
|
||||
{buffer} `(number)` `buffer` for |nvim_buf_set_keymap()|.
|
||||
{mode} `(string)` `mode` for |nvim_buf_set_keymap()|.
|
||||
{lhs} `(string)` `lhs` for |nvim_buf_set_keymap()|.
|
||||
{pair_info} `(table)` Table with pair information.
|
||||
{opts} `(table)` Optional table `opts` for |nvim_buf_set_keymap()|.
|
||||
Elements `expr` and `noremap` won't be recognized (`true` by default).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniPairs.unmap()*
|
||||
`MiniPairs.unmap`({mode}, {lhs}, {pair})
|
||||
Remove global mapping
|
||||
|
||||
A wrapper for |nvim_del_keymap()| which registers supplied `pair`.
|
||||
|
||||
Parameters~
|
||||
{mode} `(string)` `mode` for |nvim_del_keymap()|.
|
||||
{lhs} `(string)` `lhs` for |nvim_del_keymap()|.
|
||||
{pair} `(string)` Pair which should be unregistered from both
|
||||
`<BS>` and `<CR>`. Should be explicitly supplied to avoid confusion.
|
||||
Supply `''` to not unregister pair.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniPairs.unmap_buf()*
|
||||
`MiniPairs.unmap_buf`({buffer}, {mode}, {lhs}, {pair})
|
||||
Remove buffer mapping
|
||||
|
||||
Wrapper for |nvim_buf_del_keymap()| which also unregisters supplied `pair`.
|
||||
|
||||
Note: this only reverts mapping done by |MiniPairs.map_buf|. If mapping was
|
||||
done with |MiniPairs.map|, unmap for buffer in usual Neovim manner:
|
||||
`inoremap <buffer> <*> <*>` (this maps `<*>` key to do the same it does by
|
||||
default).
|
||||
|
||||
Parameters~
|
||||
{buffer} `(number)` `buffer` for |nvim_buf_del_keymap()|.
|
||||
{mode} `(string)` `mode` for |nvim_buf_del_keymap()|.
|
||||
{lhs} `(string)` `lhs` for |nvim_buf_del_keymap()|.
|
||||
{pair} `(string)` Pair which should be unregistered from both
|
||||
`<BS>` and `<CR>`. Should be explicitly supplied to avoid confusion.
|
||||
Supply `''` to not unregister pair.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniPairs.open()*
|
||||
`MiniPairs.open`({pair}, {neigh_pattern})
|
||||
Process "open" symbols
|
||||
|
||||
Used as |map-expr| mapping for "open" symbols in asymmetric pair ('(', '[',
|
||||
etc.). If neighborhood doesn't match supplied pattern, function results
|
||||
into "open" symbol. Otherwise, it pastes whole pair and moves inside pair
|
||||
with |<Left>|.
|
||||
|
||||
Used inside |MiniPairs.map| and |MiniPairs.map_buf| for an actual mapping.
|
||||
|
||||
Parameters~
|
||||
{pair} `(string)` String with two characters representing pair.
|
||||
{neigh_pattern} `(string)` Pattern for two neighborhood characters ("\r" line
|
||||
start, "\n" - line end).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniPairs.close()*
|
||||
`MiniPairs.close`({pair}, {neigh_pattern})
|
||||
Process "close" symbols
|
||||
|
||||
Used as |map-expr| mapping for "close" symbols in asymmetric pair (')',
|
||||
']', etc.). If neighborhood doesn't match supplied pattern, function
|
||||
results into "close" symbol. Otherwise it jumps over symbol to the right of
|
||||
cursor (with |<Right>|) if it is equal to "close" one and inserts it
|
||||
otherwise.
|
||||
|
||||
Used inside |MiniPairs.map| and |MiniPairs.map_buf| for an actual mapping.
|
||||
|
||||
Parameters~
|
||||
{pair} `(string)` String with two characters representing pair.
|
||||
{neigh_pattern} `(string)` Pattern for two neighborhood characters ("\r" line
|
||||
start, "\n" - line end).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniPairs.closeopen()*
|
||||
`MiniPairs.closeopen`({pair}, {neigh_pattern})
|
||||
Process "closeopen" symbols
|
||||
|
||||
Used as |map-expr| mapping for 'symmetrical' symbols (from pairs '""',
|
||||
'\'\'', '``'). It tries to perform 'closeopen action': move over right
|
||||
character (with |<Right>|) if it is equal to second character from pair or
|
||||
conditionally paste pair otherwise (with |MiniPairs.open()|).
|
||||
|
||||
Used inside |MiniPairs.map| and |MiniPairs.map_buf| for an actual mapping.
|
||||
|
||||
Parameters~
|
||||
{pair} `(string)` String with two characters representing pair.
|
||||
{neigh_pattern} `(string)` Pattern for two neighborhood characters ("\r" line
|
||||
start, "\n" - line end).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniPairs.bs()*
|
||||
`MiniPairs.bs`()
|
||||
Process |<BS>|
|
||||
|
||||
Used as |map-expr| mapping for `<BS>`. It removes whole pair (via
|
||||
`<BS><Del>`) if neighborhood is equal to a whole pair recognized for
|
||||
current buffer. Pair is recognized for current buffer if it is registered
|
||||
for global or current buffer mapping. Pair is registered as a result of
|
||||
calling |MiniPairs.map| or |MiniPairs.map_buf|.
|
||||
|
||||
Mapped by default inside |MiniPairs.setup|.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniPairs.cr()*
|
||||
`MiniPairs.cr`()
|
||||
Process |i_<CR>|
|
||||
|
||||
Used as |map-expr| mapping for `<CR>` in insert mode. It puts "close"
|
||||
symbol on next line (via `<CR><C-o>O`) if neighborhood is equal to a whole
|
||||
pair recognized for current buffer. Pair is recognized for current buffer
|
||||
if it is registered for global or current buffer mapping. Pair is
|
||||
registered as a result of calling |MiniPairs.map| or |MiniPairs.map_buf|.
|
||||
|
||||
Mapped by default inside |MiniPairs.setup|.
|
||||
|
||||
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
|
@ -0,0 +1,220 @@
|
|||
==============================================================================
|
||||
------------------------------------------------------------------------------
|
||||
*mini.sessions*
|
||||
*MiniSessions*
|
||||
Session management (read, write, delete), which works using |mksession|
|
||||
(meaning 'sessionoptions' is fully respected). This is intended as a
|
||||
drop-in Lua replacement for session management part of 'mhinz/vim-startify'
|
||||
(works out of the box with sessions created by it). Implements both global
|
||||
(from configured directory) and local (from current directory) sessions.
|
||||
|
||||
Key design ideas:
|
||||
- Sessions are represented by readable files (results of applying
|
||||
|mksession|). There are two kinds of sessions:
|
||||
- Global: any file inside a configurable directory.
|
||||
- Local: configurable file inside current working directory (|getcwd|).
|
||||
- All session files are detected during `MiniSessions.setup()` with session
|
||||
names being file names (including their possible extension).
|
||||
- Store information about detected sessions in separate table
|
||||
(|MiniSessions.detected|) and operate only on it. Meaning if this
|
||||
information changes, there will be no effect until next detection. So to
|
||||
avoid confusion, don't directly use |mksession| and |source| for writing
|
||||
and reading sessions files.
|
||||
|
||||
Features:
|
||||
- Autoread default session (local if detected, latest otherwise) if Neovim
|
||||
was called without intention to show something else.
|
||||
- Autowrite current session before quitting Neovim.
|
||||
- Configurable severity level of all actions.
|
||||
|
||||
# Setup~
|
||||
|
||||
This module needs a setup with `require('mini.sessions').setup({})`
|
||||
(replace `{}` with your `config` table). It will create global Lua table
|
||||
`MiniSessions` which you can use for scripting or manually (with
|
||||
`:lua MiniSessions.*`).
|
||||
|
||||
See |MiniSessions.config| for `config` structure and default values.
|
||||
|
||||
This module doesn't benefit from buffer local configuration, so using
|
||||
`vim.b.minimisc_config` will have no effect here.
|
||||
|
||||
# Disabling~
|
||||
|
||||
To disable core functionality, set `g:minisessions_disable` (globally) or
|
||||
`b:minisessions_disable` (for a buffer) to `v:true`. Considering high
|
||||
number of different scenarios and customization intentions, writing exact
|
||||
rules for disabling module's functionality is left to user. See
|
||||
|mini.nvim-disabling-recipes| for common recipes.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSessions.setup()*
|
||||
`MiniSessions.setup`({config})
|
||||
Module setup
|
||||
|
||||
Parameters~
|
||||
{config} `(table)` Module config table. See |MiniSessions.config|.
|
||||
|
||||
Usage~
|
||||
`require('mini.sessions').setup({})` (replace `{}` with your `config` table)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSessions.config*
|
||||
`MiniSessions.config`
|
||||
Module config
|
||||
|
||||
Default values:
|
||||
>
|
||||
MiniSessions.config = {
|
||||
-- Whether to read latest session if Neovim opened without file arguments
|
||||
autoread = false,
|
||||
|
||||
-- Whether to write current session before quitting Neovim
|
||||
autowrite = true,
|
||||
|
||||
-- Directory where global sessions are stored (use `''` to disable)
|
||||
directory = --<"session" subdir of user data directory from |stdpath()|>,
|
||||
|
||||
-- File for local session (use `''` to disable)
|
||||
file = 'Session.vim',
|
||||
|
||||
-- Whether to force possibly harmful actions (meaning depends on function)
|
||||
force = { read = false, write = true, delete = false },
|
||||
|
||||
-- Hook functions for actions. Default `nil` means 'do nothing'.
|
||||
-- Takes table with active session data as argument.
|
||||
hooks = {
|
||||
-- Before successful action
|
||||
pre = { read = nil, write = nil, delete = nil },
|
||||
-- After successful action
|
||||
post = { read = nil, write = nil, delete = nil },
|
||||
},
|
||||
|
||||
-- Whether to print session path after action
|
||||
verbose = { read = false, write = true, delete = true },
|
||||
}
|
||||
<
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSessions.detected*
|
||||
`MiniSessions.detected`
|
||||
Table of detected sessions. Keys represent session name. Values are tables
|
||||
with session information that currently has these fields (but subject to
|
||||
change):
|
||||
- <modify_time> `(number)` modification time (see |getftime|) of session file.
|
||||
- <name> `(string)` name of session (should be equal to table key).
|
||||
- <path> `(string)` full path to session file.
|
||||
- <type> `(string)` type of session ('global' or 'local').
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSessions.read()*
|
||||
`MiniSessions.read`({session_name}, {opts})
|
||||
Read detected session
|
||||
|
||||
What it does:
|
||||
- Delete all current buffers with |bwipeout|. This is needed to correctly
|
||||
restore buffers from target session. If `force` is not `true`, checks
|
||||
beforehand for unsaved listed buffers and stops if there is any.
|
||||
- Source session with supplied name.
|
||||
|
||||
Parameters~
|
||||
{session_name} `(string)` Name of detected session file to read. Default:
|
||||
`nil` for default session: local (if detected) or latest session (see
|
||||
|MiniSessions.get_latest|).
|
||||
{opts} `(table)` Table with options. Current allowed keys:
|
||||
- <force> (whether to delete unsaved buffers; default:
|
||||
`MiniSessions.config.force.read`).
|
||||
- <verbose> (whether to print session path after action; default
|
||||
`MiniSessions.config.verbose.read`).
|
||||
- <hooks> (a table with <pre> and <post> function hooks to be executed
|
||||
with session data argument before and after successful read; overrides
|
||||
`MiniSessions.config.hooks.pre.read` and
|
||||
`MiniSessions.config.hooks.post.read`).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSessions.write()*
|
||||
`MiniSessions.write`({session_name}, {opts})
|
||||
Write session
|
||||
|
||||
What it does:
|
||||
- Check if file for supplied session name already exists. If it does and
|
||||
`force` is not `true`, then stop.
|
||||
- Write session with |mksession| to a file named `session_name`. Its
|
||||
directory is determined based on type of session:
|
||||
- It is at location |v:this_session| if `session_name` is `nil` and
|
||||
there is current session.
|
||||
- It is current working directory (|getcwd|) if `session_name` is equal
|
||||
to `MiniSessions.config.file` (represents local session).
|
||||
- It is `MiniSessions.config.directory` otherwise (represents global
|
||||
session).
|
||||
- Update |MiniSessions.detected|.
|
||||
|
||||
Parameters~
|
||||
{session_name} `(string)` Name of session file to write. Default: `nil` for
|
||||
current session (|v:this_session|).
|
||||
{opts} `(table)` Table with options. Current allowed keys:
|
||||
- <force> (whether to ignore existence of session file; default:
|
||||
`MiniSessions.config.force.write`).
|
||||
- <verbose> (whether to print session path after action; default
|
||||
`MiniSessions.config.verbose.write`).
|
||||
- <hooks> (a table with <pre> and <post> function hooks to be executed
|
||||
with session data argument before and after successful write; overrides
|
||||
`MiniSessions.config.hooks.pre.write` and
|
||||
`MiniSessions.config.hooks.post.write`).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSessions.delete()*
|
||||
`MiniSessions.delete`({session_name}, {opts})
|
||||
Delete detected session
|
||||
|
||||
What it does:
|
||||
- Check if session name is a current one. If yes and `force` is not `true`,
|
||||
then stop.
|
||||
- Delete session.
|
||||
- Update |MiniSessions.detected|.
|
||||
|
||||
Parameters~
|
||||
{session_name} `(string)` Name of detected session file to delete. Default:
|
||||
`nil` for name of current session (taken from |v:this_session|).
|
||||
{opts} `(table)` Table with options. Current allowed keys:
|
||||
- <force> (whether to allow deletion of current session; default:
|
||||
`MiniSessions.config.force.delete`).
|
||||
- <verbose> (whether to print session path after action; default
|
||||
`MiniSessions.config.verbose.delete`).
|
||||
- <hooks> (a table with <pre> and <post> function hooks to be executed
|
||||
with session data argument before and after successful delete; overrides
|
||||
`MiniSessions.config.hooks.pre.delete` and
|
||||
`MiniSessions.config.hooks.post.delete`).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSessions.select()*
|
||||
`MiniSessions.select`({action}, {opts})
|
||||
Select session interactively and perform action
|
||||
|
||||
Note: this uses |vim.ui.select| function, which is present in Neovim
|
||||
starting from 0.6 version. For more user-friendly experience, override it
|
||||
(for example, with external plugins like "stevearc/dressing.nvim").
|
||||
|
||||
Parameters~
|
||||
{action} `(string)` Action to perform. Should be one of "read" (default),
|
||||
"write", or "delete".
|
||||
{opts} `(table)` Options for specified action.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSessions.get_latest()*
|
||||
`MiniSessions.get_latest`()
|
||||
Get name of latest detected session
|
||||
|
||||
Latest session is the session with the latest modification time determined
|
||||
by |getftime|.
|
||||
|
||||
Return~
|
||||
`(string|nil)` Name of latest session or `nil` if there is no sessions.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSessions.on_vimenter()*
|
||||
`MiniSessions.on_vimenter`()
|
||||
Act on |VimEnter|
|
||||
|
||||
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
|
@ -0,0 +1,596 @@
|
|||
==============================================================================
|
||||
------------------------------------------------------------------------------
|
||||
*mini.starter*
|
||||
*MiniStarter*
|
||||
Fast and flexible start screen. Displayed items are fully customizable both
|
||||
in terms of what they do and how they look (with reasonable defaults). Item
|
||||
selection can be done using prefix query with instant visual feedback.
|
||||
|
||||
Key design ideas:
|
||||
- All available actions are defined inside items. Each item should have the
|
||||
following info:
|
||||
- <action> - function or string for |vim.cmd| which is executed when
|
||||
item is chosen. Empty string result in placeholder "inactive" item.
|
||||
- <name> - string which will be displayed and used for choosing.
|
||||
- <section> - string representing to which section item belongs.
|
||||
There are pre-configured whole sections in |MiniStarter.sections|.
|
||||
- Configure what items are displayed by supplying an array which can be
|
||||
normalized to an array of items. Read about how supplied items are
|
||||
normalized in |MiniStarter.refresh|.
|
||||
- Modify the final look by supplying content hooks: functions which take
|
||||
buffer content as input (see |MiniStarter.get_content()| for more
|
||||
information) and return buffer content as output. There are
|
||||
pre-configured content hook generators in |MiniStarter.gen_hook|.
|
||||
- Choosing an item can be done in two ways:
|
||||
- Type prefix query to filter item by matching its name (ignoring
|
||||
case). Displayed information is updated after every typed character.
|
||||
For every item its unique prefix is highlighted.
|
||||
- Use Up/Down arrows and hit Enter.
|
||||
- Allow multiple simultaneously open Starter buffers.
|
||||
|
||||
What is doesn't do:
|
||||
- It doesn't support fuzzy query for items. And probably will never do.
|
||||
|
||||
# Setup~
|
||||
|
||||
This module needs a setup with `require('mini.starter').setup({})`
|
||||
(replace `{}` with your `config` table). It will create global Lua table
|
||||
`MiniStarter` which you can use for scripting or manually (with
|
||||
`:lua MiniStarter.*`).
|
||||
|
||||
See |MiniStarter.config| for `config` structure and default values. For
|
||||
some configuration examples (including one similar to 'vim-startify' and
|
||||
'dashboard-nvim'), see |MiniStarter-example-config|.
|
||||
|
||||
You can override runtime config settings locally to buffer inside
|
||||
`vim.b.ministarter_config` which should have same structure as
|
||||
`MiniStarter.config`. See |mini.nvim-buffer-local-config| for more details.
|
||||
Note: `vim.b.ministarter_config` is copied to Starter buffer from current
|
||||
buffer allowing full customization.
|
||||
|
||||
# Highlight groups~
|
||||
|
||||
* `MiniStarterCurrent` - current item.
|
||||
* `MiniStarterFooter` - footer units.
|
||||
* `MiniStarterHeader` - header units.
|
||||
* `MiniStarterInactive` - inactive item.
|
||||
* `MiniStarterItem` - item name.
|
||||
* `MiniStarterItemBullet` - units from |MiniStarter.gen_hook.adding_bullet|.
|
||||
* `MiniStarterItemPrefix` - unique query for item.
|
||||
* `MiniStarterSection` - section units.
|
||||
* `MiniStarterQuery` - current query in active items.
|
||||
|
||||
To change any highlight group, modify it directly with |:highlight|.
|
||||
|
||||
# Disabling~
|
||||
|
||||
To disable core functionality, set `g:ministarter_disable` (globally) or
|
||||
`b:ministarter_disable` (for a buffer) to `v:true`. Considering high number
|
||||
of different scenarios and customization intentions, writing exact rules
|
||||
for disabling module's functionality is left to user. See
|
||||
|mini.nvim-disabling-recipes| for common recipes.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter-example-config*
|
||||
Example configurations
|
||||
|
||||
Configuration similar to 'mhinz/vim-startify':
|
||||
>
|
||||
local starter = require('mini.starter')
|
||||
starter.setup({
|
||||
evaluate_single = true,
|
||||
items = {
|
||||
starter.sections.builtin_actions(),
|
||||
starter.sections.recent_files(10, false),
|
||||
starter.sections.recent_files(10, true),
|
||||
-- Use this if you set up 'mini.sessions'
|
||||
starter.sections.sessions(5, true)
|
||||
},
|
||||
content_hooks = {
|
||||
starter.gen_hook.adding_bullet(),
|
||||
starter.gen_hook.indexing('all', { 'Builtin actions' }),
|
||||
starter.gen_hook.padding(3, 2),
|
||||
},
|
||||
})
|
||||
<
|
||||
Configuration similar to 'glepnir/dashboard-nvim':
|
||||
>
|
||||
local starter = require('mini.starter')
|
||||
starter.setup({
|
||||
items = {
|
||||
starter.sections.telescope(),
|
||||
},
|
||||
content_hooks = {
|
||||
starter.gen_hook.adding_bullet(),
|
||||
starter.gen_hook.aligning('center', 'center'),
|
||||
},
|
||||
})
|
||||
<
|
||||
Elaborated configuration showing capabilities of custom items,
|
||||
header/footer, and content hooks:
|
||||
>
|
||||
local my_items = {
|
||||
{ name = 'Echo random number', action = 'lua print(math.random())', section = 'Section 1' },
|
||||
function()
|
||||
return {
|
||||
{ name = 'Item #1 from function', action = [[echo 'Item #1']], section = 'From function' },
|
||||
{ name = 'Placeholder (always incative) item', action = '', section = 'From function' },
|
||||
function()
|
||||
return {
|
||||
name = 'Item #1 from double function',
|
||||
action = [[echo 'Double function']],
|
||||
section = 'From double function',
|
||||
}
|
||||
end,
|
||||
}
|
||||
end,
|
||||
{ name = [[Another item in 'Section 1']], action = 'lua print(math.random() + 10)', section = 'Section 1' },
|
||||
}
|
||||
|
||||
local footer_n_seconds = (function()
|
||||
local timer = vim.loop.new_timer()
|
||||
local n_seconds = 0
|
||||
timer:start(0, 1000, vim.schedule_wrap(function()
|
||||
if vim.api.nvim_buf_get_option(0, 'filetype') ~= 'starter' then
|
||||
timer:stop()
|
||||
return
|
||||
end
|
||||
n_seconds = n_seconds + 1
|
||||
MiniStarter.refresh()
|
||||
end))
|
||||
|
||||
return function()
|
||||
return 'Number of seconds since opening: ' .. n_seconds
|
||||
end
|
||||
end)()
|
||||
|
||||
local hook_top_pad_10 = function(content)
|
||||
-- Pad from top
|
||||
for _ = 1, 10 do
|
||||
-- Insert at start a line with single content unit
|
||||
table.insert(content, 1, { { type = 'empty', string = '' } })
|
||||
end
|
||||
return content
|
||||
end
|
||||
|
||||
local starter = require('mini.starter')
|
||||
starter.setup({
|
||||
items = my_items,
|
||||
footer = footer_n_seconds,
|
||||
content_hooks = { hook_top_pad_10 },
|
||||
})
|
||||
<
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter-lifecycle*
|
||||
# Lifecycle of Starter buffer~
|
||||
|
||||
- Open with |MiniStarter.open()|. It includes creating buffer with
|
||||
appropriate options, mappings, behavior; call to |MiniStarter.refresh()|;
|
||||
issue `MiniStarterOpened` |User| event.
|
||||
- Wait for user to choose an item. This is done using following logic:
|
||||
- Typing any character from `MiniStarter.config.query_updaters` leads
|
||||
to updating query. Read more in |MiniStarter.add_to_query|.
|
||||
- <BS> deletes latest character from query.
|
||||
- <Down>/<Up>, <C-n>/<C-p>, <M-j>/<M-k> move current item.
|
||||
- <CR> executes action of current item.
|
||||
- <C-c> closes Starter buffer.
|
||||
- Evaluate current item when appropriate (after `<CR>` or when there is a
|
||||
single item and `MiniStarter.config.evaluate_single` is `true`). This
|
||||
executes item's `action`.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.setup()*
|
||||
`MiniStarter.setup`({config})
|
||||
Module setup
|
||||
|
||||
Parameters~
|
||||
{config} `(table)` Module config table. See |MiniStarter.config|.
|
||||
|
||||
Usage~
|
||||
`require('mini.starter').setup({})` (replace `{}` with your `config` table)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.config*
|
||||
`MiniStarter.config`
|
||||
Module config
|
||||
|
||||
Default values:
|
||||
>
|
||||
MiniStarter.config = {
|
||||
-- Whether to open starter buffer on VimEnter. Not opened if Neovim was
|
||||
-- started with intent to show something else.
|
||||
autoopen = true,
|
||||
|
||||
-- Whether to evaluate action of single active item
|
||||
evaluate_single = false,
|
||||
|
||||
-- Items to be displayed. Should be an array with the following elements:
|
||||
-- - Item: table with <action>, <name>, and <section> keys.
|
||||
-- - Function: should return one of these three categories.
|
||||
-- - Array: elements of these three types (i.e. item, array, function).
|
||||
-- If `nil` (default), default items will be used (see |mini.starter|).
|
||||
items = nil,
|
||||
|
||||
-- Header to be displayed before items. Converted to single string via
|
||||
-- `tostring` (use `\n` to display several lines). If function, it is
|
||||
-- evaluated first. If `nil` (default), polite greeting will be used.
|
||||
header = nil,
|
||||
|
||||
-- Footer to be displayed after items. Converted to single string via
|
||||
-- `tostring` (use `\n` to display several lines). If function, it is
|
||||
-- evaluated first. If `nil` (default), default usage help will be shown.
|
||||
footer = nil,
|
||||
|
||||
-- Array of functions to be applied consecutively to initial content.
|
||||
-- Each function should take and return content for 'Starter' buffer (see
|
||||
-- |mini.starter| and |MiniStarter.get_content()| for more details).
|
||||
content_hooks = nil,
|
||||
|
||||
-- Characters to update query. Each character will have special buffer
|
||||
-- mapping overriding your global ones. Be careful to not add `:` as it
|
||||
-- allows you to go into command mode.
|
||||
query_updaters = 'abcdefghijklmnopqrstuvwxyz0123456789_-.',
|
||||
}
|
||||
<
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.on_vimenter()*
|
||||
`MiniStarter.on_vimenter`()
|
||||
Act on |VimEnter|.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.open()*
|
||||
`MiniStarter.open`({buf_id})
|
||||
Open Starter buffer
|
||||
|
||||
- Create buffer if necessary and move into it.
|
||||
- Set buffer options. Note that settings are done with |noautocmd| to
|
||||
achieve a massive speedup.
|
||||
- Set buffer mappings. Besides basic mappings (described inside "Lifecycle
|
||||
of Starter buffer" of |mini.starter|), map every character from
|
||||
`MiniStarter.config.query_updaters` to add itself to query with
|
||||
|MiniStarter.add_to_query|.
|
||||
- Populate buffer with |MiniStarter.refresh|.
|
||||
- Issue custom `MiniStarterOpened` event to allow acting upon opening
|
||||
Starter buffer. Use it with
|
||||
`autocmd User MiniStarterOpened <your command>`.
|
||||
|
||||
Note: to fully use it in autocommand, it is recommended to utilize
|
||||
|autocmd-nested|. Example:
|
||||
`autocmd TabNewEntered * ++nested lua MiniStarter.open()`
|
||||
|
||||
Parameters~
|
||||
{buf_id} `(number)` Identifier of existing valid buffer (see |bufnr()|) to
|
||||
open inside. Default: create a new one.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.refresh()*
|
||||
`MiniStarter.refresh`({buf_id})
|
||||
Refresh Starter buffer
|
||||
|
||||
- Normalize `MiniStarter.config.items`:
|
||||
- Flatten: recursively (in depth-first fashion) parse its elements. If
|
||||
function is found, execute it and continue with parsing its output
|
||||
(this allows deferring item collection up until it is actually
|
||||
needed). If proper item is found (table with fields `action`,
|
||||
`name`, `section`), add it to output.
|
||||
- Sort: order first by section and then by item id (both in order of
|
||||
appearance).
|
||||
- Normalize `MiniStarter.config.header` and `MiniStarter.config.footer` to
|
||||
be multiple lines by splitting at `\n`. If function - evaluate it first.
|
||||
- Make initial buffer content (see |MiniStarter.get_content()| for a
|
||||
description of what a buffer content is). It consist from content lines
|
||||
with single content unit:
|
||||
- First lines contain strings of normalized header.
|
||||
- Body is for normalized items. Section names have own lines preceded
|
||||
by empty line.
|
||||
- Last lines contain separate strings of normalized footer.
|
||||
- Sequentially apply hooks from `MiniStarter.config.content_hooks` to
|
||||
content. Output of one hook serves as input to the next.
|
||||
- Gather final items from content with |MiniStarter.content_to_items|.
|
||||
- Convert content to buffer lines with |MiniStarter.content_to_lines| and
|
||||
add them to buffer.
|
||||
- Add highlighting of content units.
|
||||
- Position cursor.
|
||||
- Make current query. This results into some items being marked as
|
||||
"inactive" and updating highlighting of current query on "active" items.
|
||||
|
||||
Note: this function is executed on every |VimResized| to allow more
|
||||
responsive behavior.
|
||||
|
||||
Parameters~
|
||||
{buf_id} `(number|nil)` Buffer identifier of a valid Starter buffer.
|
||||
Default: current buffer.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.close()*
|
||||
`MiniStarter.close`({buf_id})
|
||||
Close Starter buffer
|
||||
|
||||
Parameters~
|
||||
{buf_id} `(number|nil)` Buffer identifier of a valid Starter buffer.
|
||||
Default: current buffer.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.sections*
|
||||
`MiniStarter.sections`
|
||||
Table of pre-configured sections
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.sections.builtin_actions()*
|
||||
`MiniStarter.sections.builtin_actions`()
|
||||
Section with builtin actions
|
||||
|
||||
Return~
|
||||
`(table)` Array of items.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.sections.sessions()*
|
||||
`MiniStarter.sections.sessions`({n}, {recent})
|
||||
Section with |MiniSessions| sessions
|
||||
|
||||
Sessions are taken from |MiniSessions.detected|. Notes:
|
||||
- If it shows "'mini.sessions' is not set up", it means that you didn't
|
||||
call `require('mini.sessions').setup()`.
|
||||
- If it shows "There are no detected sessions in 'mini.sessions'", it means
|
||||
that there are no sessions at the current sessions directory. Either
|
||||
create session or supply different directory where session files are
|
||||
stored (see |MiniSessions.setup|).
|
||||
- Local session (if detected) is always displayed first.
|
||||
|
||||
Parameters~
|
||||
{n} `(number)` Number of returned items. Default: 5.
|
||||
{recent} `(boolean)` Whether to use recent sessions (instead of
|
||||
alphabetically by name). Default: true.
|
||||
|
||||
Return~
|
||||
`(function)` Function which returns array of items.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.sections.recent_files()*
|
||||
`MiniStarter.sections.recent_files`({n}, {current_dir}, {show_path})
|
||||
Section with most recently used files
|
||||
|
||||
Files are taken from |vim.v.oldfiles|.
|
||||
|
||||
Parameters~
|
||||
{n} `(number)` Number of returned items. Default: 5.
|
||||
{current_dir} `(boolean)` Whether to return files only from current working
|
||||
directory. Default: `false`.
|
||||
{show_path} `(boolean)` Whether to append file name with its full path.
|
||||
Default: `true`.
|
||||
|
||||
Return~
|
||||
`(function)` Function which returns array of items.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.sections.telescope()*
|
||||
`MiniStarter.sections.telescope`()
|
||||
Section with basic Telescope pickers relevant to start screen
|
||||
|
||||
Return~
|
||||
`(function)` Function which returns array of items.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.gen_hook*
|
||||
`MiniStarter.gen_hook`
|
||||
Table with pre-configured content hook generators
|
||||
|
||||
Each element is a function which returns content hook. So to use them
|
||||
inside |MiniStarter.setup|, call them.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.gen_hook.padding()*
|
||||
`MiniStarter.gen_hook.padding`({left}, {top})
|
||||
Hook generator for padding
|
||||
|
||||
Output is a content hook which adds constant padding from left and top.
|
||||
This allows tweaking the screen position of buffer content.
|
||||
|
||||
Parameters~
|
||||
{left} `(number)` Number of empty spaces to add to start of each content
|
||||
line. Default: 0.
|
||||
{top} `(number)` Number of empty lines to add to start of content.
|
||||
Default: 0.
|
||||
|
||||
Return~
|
||||
`(function)` Content hook.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.gen_hook.adding_bullet()*
|
||||
`MiniStarter.gen_hook.adding_bullet`({bullet}, {place_cursor})
|
||||
Hook generator for adding bullet to items
|
||||
|
||||
Output is a content hook which adds supplied string to be displayed to the
|
||||
left of item.
|
||||
|
||||
Parameters~
|
||||
{bullet} `(string)` String to be placed to the left of item name.
|
||||
Default: "░ ".
|
||||
{place_cursor} `(boolean)` Whether to place cursor on the first character
|
||||
of bullet when corresponding item becomes current. Default: true.
|
||||
|
||||
Return~
|
||||
`(function)` Content hook.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.gen_hook.indexing()*
|
||||
`MiniStarter.gen_hook.indexing`({grouping}, {exclude_sections})
|
||||
Hook generator for indexing items
|
||||
|
||||
Output is a content hook which adds unique index to the start of item's
|
||||
name. It results into shortening queries required to choose an item (at
|
||||
expense of clarity).
|
||||
|
||||
Parameters~
|
||||
{grouping} `(string)` One of "all" (number indexing across all sections) or
|
||||
"section" (letter-number indexing within each section). Default: "all".
|
||||
{exclude_sections} `(table)` Array of section names (values of `section`
|
||||
element of item) for which index won't be added. Default: `{}`.
|
||||
|
||||
Return~
|
||||
`(function)` Content hook.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.gen_hook.aligning()*
|
||||
`MiniStarter.gen_hook.aligning`({horizontal}, {vertical})
|
||||
Hook generator for aligning content
|
||||
|
||||
Output is a content hook which independently aligns content horizontally
|
||||
and vertically. Basically, this computes left and top pads for
|
||||
|MiniStarter.gen_hook.padding| such that output lines would appear aligned
|
||||
in certain way.
|
||||
|
||||
Parameters~
|
||||
{horizontal} `(string)` One of "left", "center", "right". Default: "left".
|
||||
{vertical} `(string)` One of "top", "center", "bottom". Default: "top".
|
||||
|
||||
Return~
|
||||
`(function)` Content hook.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.get_content()*
|
||||
`MiniStarter.get_content`({buf_id})
|
||||
Get content of Starter buffer
|
||||
|
||||
Generally, buffer content is a table in the form of "2d array" (or rather
|
||||
"2d list" because number of elements can differ):
|
||||
- Each element represents content line: an array with content units to be
|
||||
displayed in one buffer line.
|
||||
- Each content unit is a table with at least the following elements:
|
||||
- "type" - string with type of content. Something like "item",
|
||||
"section", "header", "footer", "empty", etc.
|
||||
- "string" - which string should be displayed. May be an empty string.
|
||||
- "hl" - which highlighting should be applied to content string. May be
|
||||
`nil` for no highlighting.
|
||||
|
||||
See |MiniStarter.content_to_lines| for converting content to buffer lines
|
||||
and |MiniStarter.content_to_items| - to list of parsed items.
|
||||
|
||||
Notes:
|
||||
- Content units with type "item" also have `item` element with all
|
||||
information about an item it represents. Those elements are used directly
|
||||
to create an array of items used for query.
|
||||
|
||||
Parameters~
|
||||
{buf_id} `(number|nil)` Buffer identifier of a valid Starter buffer.
|
||||
Default: current buffer.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.content_coords()*
|
||||
`MiniStarter.content_coords`({content}, {predicate})
|
||||
Helper to iterate through content
|
||||
|
||||
Basically, this traverses content "2d array" (in depth-first fashion; top
|
||||
to bottom, left to right) and returns "coordinates" of units for which
|
||||
`predicate` is true-ish.
|
||||
|
||||
Parameters~
|
||||
{content} `(table)` Content "2d array". Default: content of current buffer.
|
||||
{predicate} `(function|string|nil)` Predictate to filter units. If it is:
|
||||
- Function, then it is evaluated with unit as input.
|
||||
- String, then it checks unit to have this type (allows easy getting of
|
||||
units with some type).
|
||||
- `nil`, all units are kept.
|
||||
|
||||
Return~
|
||||
`(table)` Array of resulting units' coordinates. Each coordinate is a
|
||||
table with <line> and <unit> keys. To retrieve actual unit from coordinate
|
||||
`c`, use `content[c.line][c.unit]`.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.content_to_lines()*
|
||||
`MiniStarter.content_to_lines`({content})
|
||||
Convert content to buffer lines
|
||||
|
||||
One buffer line is made by concatenating `string` element of units within
|
||||
same content line.
|
||||
|
||||
Parameters~
|
||||
{content} `(table)` Content "2d array". Default: content of current buffer.
|
||||
|
||||
Return~
|
||||
`(table)` Array of strings for each buffer line.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.content_to_items()*
|
||||
`MiniStarter.content_to_items`({content})
|
||||
Convert content to items
|
||||
|
||||
Parse content (in depth-first fashion) and retrieve each item from `item`
|
||||
element of content units with type "item". This also:
|
||||
- Computes some helper information about how item will be actually
|
||||
displayed (after |MiniStarter.content_to_lines|) and minimum number of
|
||||
prefix characters needed for a particular item to be queried single.
|
||||
- Modifies item's `name` element taking it from corresponing `string`
|
||||
element of content unit. This allows modifying item's `name` at the stage
|
||||
of content hooks (like, for example, in |MiniStarter.gen_hook.indexing|).
|
||||
|
||||
Parameters~
|
||||
{content} `(table)` Content "2d array". Default: content of current buffer.
|
||||
|
||||
Return~
|
||||
`(table)` Array of items.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.eval_current_item()*
|
||||
`MiniStarter.eval_current_item`({buf_id})
|
||||
Evaluate current item
|
||||
|
||||
Note that it resets current query before evaluation, as it is rarely needed
|
||||
any more.
|
||||
|
||||
Parameters~
|
||||
{buf_id} `(number|nil)` Buffer identifier of a valid Starter buffer.
|
||||
Default: current buffer.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.update_current_item()*
|
||||
`MiniStarter.update_current_item`({direction}, {buf_id})
|
||||
Update current item
|
||||
|
||||
This makes next (with respect to `direction`) active item to be current.
|
||||
|
||||
Parameters~
|
||||
{direction} `(string)` One of "next" or "previous".
|
||||
{buf_id} `(number|nil)` Buffer identifier of a valid Starter buffer.
|
||||
Default: current buffer.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.add_to_query()*
|
||||
`MiniStarter.add_to_query`({char}, {buf_id})
|
||||
Add character to current query
|
||||
|
||||
- Update current query by appending `char` to its end (only if it results
|
||||
into at least one active item) or delete latest character if `char` is `nil`.
|
||||
- Recompute status of items: "active" if its name starts with new query,
|
||||
"inactive" otherwise.
|
||||
- Update highlighting: whole strings for "inactive" items, current query
|
||||
for "active" items.
|
||||
|
||||
Parameters~
|
||||
{char} `(string)` Single character to be added to query. If `nil`, deletes
|
||||
latest character from query.
|
||||
{buf_id} `(number|nil)` Buffer identifier of a valid Starter buffer.
|
||||
Default: current buffer.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.set_query()*
|
||||
`MiniStarter.set_query`({query}, {buf_id})
|
||||
Set current query
|
||||
|
||||
Parameters~
|
||||
{query} `(string|nil)` Query to be set (only if it results into at least one
|
||||
active item). Default: `nil` for setting query to empty string, which
|
||||
essentially resets query.
|
||||
{buf_id} `(number|nil)` Buffer identifier of a valid Starter buffer.
|
||||
Default: current buffer.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStarter.on_cursormoved()*
|
||||
`MiniStarter.on_cursormoved`({buf_id})
|
||||
Act on |CursorMoved| by repositioning cursor in fixed place.
|
||||
|
||||
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
|
@ -0,0 +1,308 @@
|
|||
==============================================================================
|
||||
------------------------------------------------------------------------------
|
||||
*mini.statusline*
|
||||
*MiniStatusline*
|
||||
Minimal and fast statusline with opinionated default look.
|
||||
|
||||
Features:
|
||||
- Define own custom statusline structure for active and inactive windows.
|
||||
This is done with a function which should return string appropriate for
|
||||
|statusline|. Its code should be similar to default one with structure:
|
||||
- Compute string data for every section you want to be displayed.
|
||||
- Combine them in groups with |MiniStatusline.combine_groups()|.
|
||||
- Built-in active mode indicator with colors.
|
||||
- Sections can hide information when window is too narrow (specific window
|
||||
width is configurable per section).
|
||||
|
||||
# Dependencies~
|
||||
|
||||
Suggested dependencies (provide extra functionality, statusline will work
|
||||
without them):
|
||||
- Nerd font (to support extra icons).
|
||||
- Plugin 'lewis6991/gitsigns.nvim' for Git information in
|
||||
|MiniStatusline.section_git|. If missing, no section will be shown.
|
||||
- Plugin 'kyazdani42/nvim-web-devicons' for filetype icons in
|
||||
`MiniStatusline.section_fileinfo`. If missing, no icons will be shown.
|
||||
|
||||
# Setup~
|
||||
|
||||
This module needs a setup with `require('mini.statusline').setup({})`
|
||||
(replace `{}` with your `config` table). It will create global Lua table
|
||||
`MiniStatusline` which you can use for scripting or manually (with
|
||||
`:lua MiniStatusline.*`).
|
||||
|
||||
See |MiniStatusline.config| for `config` structure and default values. For
|
||||
some content examples, see |MiniStatusline-example-content|.
|
||||
|
||||
You can override runtime config settings locally to buffer inside
|
||||
`vim.b.ministatusline_config` which should have same structure as
|
||||
`MiniStatusline.config`. See |mini.nvim-buffer-local-config| for more details.
|
||||
|
||||
# Highlight groups~
|
||||
|
||||
Highlight depending on mode (second output from |MiniStatusline.section_mode|):
|
||||
* `MiniStatuslineModeNormal` - Normal mode.
|
||||
* `MiniStatuslineModeInsert` - Insert mode.
|
||||
* `MiniStatuslineModeVisual` - Visual mode.
|
||||
* `MiniStatuslineModeReplace` - Replace mode.
|
||||
* `MiniStatuslineModeCommand` - Command mode.
|
||||
* `MiniStatuslineModeOther` - other modes (like Terminal, etc.).
|
||||
|
||||
Highlight used in default statusline:
|
||||
* `MiniStatuslineDevinfo` - for "dev info" group
|
||||
(|MiniStatusline.section_git| and |MiniStatusline.section_diagnostics|).
|
||||
* `MiniStatuslineFilename` - for |MiniStatusline.section_filename| section.
|
||||
* `MiniStatuslineFileinfo` - for |MiniStatusline.section_fileinfo| section.
|
||||
|
||||
Other groups:
|
||||
* `MiniStatuslineInactive` - highliting in not focused window.
|
||||
|
||||
To change any highlight group, modify it directly with |:highlight|.
|
||||
|
||||
# Disabling~
|
||||
|
||||
To disable (show empty statusline), set `g:ministatusline_disable`
|
||||
(globally) or `b:ministatusline_disable` (for a buffer) to `v:true`.
|
||||
Considering high number of different scenarios and customization
|
||||
intentions, writing exact rules for disabling module's functionality is
|
||||
left to user. See |mini.nvim-disabling-recipes| for common recipes.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStatusline-example-content*
|
||||
Example content
|
||||
|
||||
# Default content~
|
||||
|
||||
This function is used as default value for active content:
|
||||
>
|
||||
function()
|
||||
local mode, mode_hl = MiniStatusline.section_mode({ trunc_width = 120 })
|
||||
local git = MiniStatusline.section_git({ trunc_width = 75 })
|
||||
local diagnostics = MiniStatusline.section_diagnostics({ trunc_width = 75 })
|
||||
local filename = MiniStatusline.section_filename({ trunc_width = 140 })
|
||||
local fileinfo = MiniStatusline.section_fileinfo({ trunc_width = 120 })
|
||||
local location = MiniStatusline.section_location({ trunc_width = 75 })
|
||||
|
||||
return MiniStatusline.combine_groups({
|
||||
{ hl = mode_hl, strings = { mode } },
|
||||
{ hl = 'MiniStatuslineDevinfo', strings = { git, diagnostics } },
|
||||
'%<', -- Mark general truncate point
|
||||
{ hl = 'MiniStatuslineFilename', strings = { filename } },
|
||||
'%=', -- End left alignment
|
||||
{ hl = 'MiniStatuslineFileinfo', strings = { fileinfo } },
|
||||
{ hl = mode_hl, strings = { location } },
|
||||
})
|
||||
end
|
||||
<
|
||||
# Show boolean options~
|
||||
|
||||
To compute section string for boolean option use variation of this code
|
||||
snippet inside content function (you can modify option itself, truncation
|
||||
width, short and long displayed names):
|
||||
>
|
||||
local spell = vim.wo.spell and (MiniStatusline.is_truncated(120) and 'S' or 'SPELL') or ''
|
||||
<
|
||||
Here `x and y or z` is a common Lua way of doing ternary operator: if `x`
|
||||
is `true`-ish then return `y`, if not - return `z`.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStatusline.setup()*
|
||||
`MiniStatusline.setup`({config})
|
||||
Module setup
|
||||
|
||||
Parameters~
|
||||
{config} `(table)` Module config table. See |MiniStatusline.config|.
|
||||
|
||||
Usage~
|
||||
`require('mini.statusline').setup({})` (replace `{}` with your `config` table)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStatusline.config*
|
||||
`MiniStatusline.config`
|
||||
Module config
|
||||
|
||||
Default values:
|
||||
>
|
||||
MiniStatusline.config = {
|
||||
-- Content of statusline as functions which return statusline string. See
|
||||
-- `:h statusline` and code of default contents (used instead of `nil`).
|
||||
content = {
|
||||
-- Content for active window
|
||||
active = nil,
|
||||
-- Content for inactive window(s)
|
||||
inactive = nil,
|
||||
},
|
||||
|
||||
-- Whether to use icons by default
|
||||
use_icons = true,
|
||||
|
||||
-- Whether to set Vim's settings for statusline (make it always shown with
|
||||
-- 'laststatus' set to 2). To use global statusline in Neovim>=0.7.0, set
|
||||
-- this to `false` and 'laststatus' to 3.
|
||||
set_vim_settings = true,
|
||||
}
|
||||
<
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStatusline.active()*
|
||||
`MiniStatusline.active`()
|
||||
Compute content for active window
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStatusline.inactive()*
|
||||
`MiniStatusline.inactive`()
|
||||
Compute content for inactive window
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStatusline.combine_groups()*
|
||||
`MiniStatusline.combine_groups`({groups})
|
||||
Combine groups of sections
|
||||
|
||||
Each group can be either a string or a table with fields `hl` (group's
|
||||
highlight group) and `strings` (strings representing sections).
|
||||
|
||||
General idea of this function is as follows;
|
||||
- String group is used as is (useful for special strings like `%<` or `%=`).
|
||||
- Each table group has own highlighting in `hl` field (if missing, the
|
||||
previous one is used) and string parts in `strings` field. Non-empty
|
||||
strings from `strings` are separated by one space. Non-empty groups are
|
||||
separated by two spaces (one for each highlighting).
|
||||
|
||||
Parameters~
|
||||
{groups} `(string|table)` Array of groups.
|
||||
|
||||
Return~
|
||||
`(string)` String suitable for 'statusline'.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStatusline.is_truncated()*
|
||||
`MiniStatusline.is_truncated`({trunc_width})
|
||||
Decide whether to truncate
|
||||
|
||||
This basically computes window width and compares it to `trunc_width`: if
|
||||
window is smaller then truncate; otherwise don't. Don't truncate by
|
||||
default.
|
||||
|
||||
Use this to manually decide if section needs truncation or not.
|
||||
|
||||
Parameters~
|
||||
{trunc_width} `(number)` Truncation width. If `nil`, output is `false`.
|
||||
|
||||
Return~
|
||||
`(boolean)` Whether to truncate.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStatusline.section_mode()*
|
||||
`MiniStatusline.section_mode`({args})
|
||||
Section for Vim |mode()|
|
||||
|
||||
Short output is returned if window width is lower than `args.trunc_width`.
|
||||
|
||||
Parameters~
|
||||
{args} `(table)` Section arguments.
|
||||
|
||||
Return~
|
||||
`(...)` Section string and mode's highlight group.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStatusline.section_git()*
|
||||
`MiniStatusline.section_git`({args})
|
||||
Section for Git information
|
||||
|
||||
Normal output contains name of `HEAD` (via |b:gitsigns_head|) and chunk
|
||||
information (via |b:gitsigns_status|). Short output - only name of `HEAD`.
|
||||
Note: requires 'lewis6991/gitsigns' plugin.
|
||||
|
||||
Short output is returned if window width is lower than `args.trunc_width`.
|
||||
|
||||
Parameters~
|
||||
{args} `(table)` Section arguments. Use `args.icon` to supply your own icon.
|
||||
|
||||
Return~
|
||||
`(string)` Section string.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStatusline.section_diagnostics()*
|
||||
`MiniStatusline.section_diagnostics`({args})
|
||||
Section for Neovim's builtin diagnostics
|
||||
|
||||
Shows nothing if there is no attached LSP clients or for short output.
|
||||
Otherwise uses builtin Neovim capabilities to compute and show number of
|
||||
errors ('E'), warnings ('W'), information ('I'), and hints ('H').
|
||||
|
||||
Short output is returned if window width is lower than `args.trunc_width`.
|
||||
|
||||
Parameters~
|
||||
{args} `(table)` Section arguments. Use `args.icon` to supply your own icon.
|
||||
|
||||
Return~
|
||||
`(string)` Section string.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStatusline.section_filename()*
|
||||
`MiniStatusline.section_filename`({args})
|
||||
Section for file name
|
||||
|
||||
Show full file name or relative in short output.
|
||||
|
||||
Short output is returned if window width is lower than `args.trunc_width`.
|
||||
|
||||
Parameters~
|
||||
{args} `(table)` Section arguments.
|
||||
|
||||
Return~
|
||||
`(string)` Section string.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStatusline.section_fileinfo()*
|
||||
`MiniStatusline.section_fileinfo`({args})
|
||||
Section for file information
|
||||
|
||||
Short output contains only extension and is returned if window width is
|
||||
lower than `args.trunc_width`.
|
||||
|
||||
Parameters~
|
||||
{args} `(table)` Section arguments.
|
||||
|
||||
Return~
|
||||
`(string)` Section string.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStatusline.section_location()*
|
||||
`MiniStatusline.section_location`({args})
|
||||
Section for location inside buffer
|
||||
|
||||
Show location inside buffer in the form:
|
||||
- Normal: '<cursor line>|<total lines>│<cursor column>|<total columns>'.
|
||||
- Short: '<cursor line>│<cursor column>'.
|
||||
|
||||
Short output is returned if window width is lower than `args.trunc_width`.
|
||||
|
||||
Parameters~
|
||||
{args} `(table)` Section arguments.
|
||||
|
||||
Return~
|
||||
`(string)` Section string.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniStatusline.section_searchcount()*
|
||||
`MiniStatusline.section_searchcount`({args})
|
||||
Section for current search count
|
||||
|
||||
Show the current status of |searchcount()|. Empty output is returned if
|
||||
window width is lower than `args.trunc_width`, search highlighting is not
|
||||
on (see |v:hlsearch|), or if number of search result is 0.
|
||||
|
||||
`args.options` is forwarded to |searchcount()|. By default it recomputes
|
||||
data on every call which can be computationally expensive (although still
|
||||
usually same order of magnitude as 0.1 ms). To prevent this, supply
|
||||
`args.options = {recompute = false}`.
|
||||
|
||||
Parameters~
|
||||
{args} `(table)` Section arguments.
|
||||
|
||||
Return~
|
||||
`(string)` Section string.
|
||||
|
||||
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
|
@ -0,0 +1,785 @@
|
|||
==============================================================================
|
||||
------------------------------------------------------------------------------
|
||||
*mini.surround*
|
||||
*MiniSurround*
|
||||
Fast and feature-rich surrounding. This is mostly a reimplementation of the
|
||||
core features of 'machakann/vim-sandwich' with more on top (find
|
||||
surrounding, highlight surrounding, flexible customization). Can be
|
||||
configured to have experience similar to 'tpope/vim-surround'.
|
||||
|
||||
Features:
|
||||
- Actions (all of them are dot-repeatable out of the box and respect
|
||||
|v:count| for searching surrounding) with configurable keymappings:
|
||||
- Add surrounding with `sa` (in visual mode or on motion).
|
||||
- Delete surrounding with `sd`.
|
||||
- Replace surrounding with `sr`.
|
||||
- Find surrounding with `sf` or `sF` (move cursor right or left).
|
||||
- Highlight surrounding with `sh`.
|
||||
- Change number of neighbor lines with `sn` (see |MiniSurround-algorithm|).
|
||||
- Surrounding is identified by a single character as both "input" (in
|
||||
`delete` and `replace` start, `find`, and `highlight`) and "output" (in
|
||||
`add` and `replace` end):
|
||||
- 'f' - function call (string of alphanumeric symbols or '_' or '.'
|
||||
followed by balanced '()'). In "input" finds function call, in
|
||||
"output" prompts user to enter function name.
|
||||
- 't' - tag. In "input" finds tab with same identifier, in "output"
|
||||
prompts user to enter tag name.
|
||||
- All symbols in brackets '()', '[]', '{}', '<>". In "input' represents
|
||||
balanced brackets (open - with whitespace pad, close - without), in
|
||||
"output" - left and right parts of brackets.
|
||||
- '?' - interactive. Prompts user to enter left and right parts.
|
||||
- All other alphanumeric, punctuation, or space characters represent
|
||||
surrounding with identical left and right parts.
|
||||
- Configurable search methods to find not only covering but possibly next,
|
||||
previous, or nearest surrounding. See more in |MiniSurround.config|.
|
||||
- All actions involving finding surrounding (delete, replace, find,
|
||||
highlight) can be used with suffix that changes search method to find
|
||||
previous/last. See more in |MiniSurround.config|.
|
||||
|
||||
Known issues which won't be resolved:
|
||||
- Search for surrounding is done using Lua patterns (regex-like approach).
|
||||
So certain amount of false positives should be expected.
|
||||
- When searching for "input" surrounding, there is no distinction if it is
|
||||
inside string or comment. So in this case there will be not proper match
|
||||
for a function call: 'f(a = ")", b = 1)'.
|
||||
- Tags are searched using regex-like methods, so issues are inevitable.
|
||||
Overall it is pretty good, but certain cases won't work. Like self-nested
|
||||
tags won't match correctly on both ends: '<a><a></a></a>'.
|
||||
|
||||
# Setup~
|
||||
|
||||
This module needs a setup with `require('mini.surround').setup({})`
|
||||
(replace `{}` with your `config` table). It will create global Lua table
|
||||
`MiniSurround` which you can use for scripting or manually (with
|
||||
`:lua MiniSurround.*`).
|
||||
|
||||
See |MiniSurround.config| for `config` structure and default values. It
|
||||
also has example setup providing experience similar to 'tpope/vim-surround'.
|
||||
|
||||
You can override runtime config settings locally to buffer inside
|
||||
`vim.b.minisurround_config` which should have same structure as
|
||||
`MiniSurround.config`. See |mini.nvim-buffer-local-config| for more details.
|
||||
|
||||
# Example usage~
|
||||
|
||||
Regular mappings:
|
||||
- `saiw)` - add (`sa`) for inner word (`iw`) parenthesis (`)`).
|
||||
- `saiwi[[<CR>]]<CR>` - add (`sa`) for inner word (`iw`) interactive
|
||||
surrounding (`i`): `[[` for left and `]]` for right.
|
||||
- `2sdf` - delete (`sd`) second (`2`) surrounding function call (`f`).
|
||||
- `sr)tdiv<CR>` - replace (`sr`) surrounding parenthesis (`)`) with tag
|
||||
(`t`) with identifier 'div' (`div<CR>` in command line prompt).
|
||||
- `sff` - find right (`sf`) part of surrounding function call (`f`).
|
||||
- `sh}` - highlight (`sh`) for a brief period of time surrounding curly
|
||||
brackets (`}`).
|
||||
|
||||
Extended mappings (temporary force "prev"/"next" search methods):
|
||||
- `sdnf` - delete (`sd`) next (`n`) function call (`f`).
|
||||
- `srlf(` - replace (`sd`) last (`l`) function call (`f`) with padded
|
||||
bracket (`(`).
|
||||
- `2sfnt` - find (`sf`) second (2) next (`n`) tag (`t`).
|
||||
- `shl}` - highlight (`sh`) last (`l`) second (`2`) curly bracket (`}`).
|
||||
|
||||
# Comparisons~
|
||||
|
||||
- 'tpope/vim-surround':
|
||||
- 'vim-surround' has completely different, with other focus set of
|
||||
default mappings, while 'mini.surround' has a more coherent set.
|
||||
- 'mini.surround' supports dot-repeat, customized search path (see
|
||||
|MiniSurround.config|), customized specifications (see
|
||||
|MiniSurround-surround-specification|) allowing usage of tree-sitter
|
||||
queries (see |MiniSurround.gen_spec.input.treesitter()|),
|
||||
highlighting and finding surrounding, "last"/"next" extended
|
||||
mappings. While 'vim-surround' does not.
|
||||
- 'machakann/vim-sandwich':
|
||||
- Both have same keybindings for common actions (add, delete, replace).
|
||||
- Otherwise same differences as with 'tpop/vim-surround' (except
|
||||
dot-repeat because 'vim-sandwich' supports it).
|
||||
- 'kylechui/nvim-surround':
|
||||
- 'nvim-surround' is designed after 'tpope/vim-surround' with same
|
||||
default mappings and logic, while 'mini.surround' has mappings
|
||||
similar to 'machakann/vim-sandwich'.
|
||||
- 'mini.surround' has more flexible customization of input surrounding
|
||||
(with composed patterns, region pair(s), search methods).
|
||||
- 'mini.surround' supports |v:count| in input surrounding while
|
||||
'nvim-surround' doesn't.
|
||||
- 'mini.surround' supports "last"/"next" extended mappings.
|
||||
- |mini.ai|:
|
||||
- Both use similar logic for finding target: textobject in 'mini.ai'
|
||||
and surrounding pair in 'mini.surround'. While 'mini.ai' uses
|
||||
extraction pattern for separate `a` and `i` textobjects,
|
||||
'mini.surround' uses it to select left and right surroundings
|
||||
(basically a difference between `a` and `i` textobjects).
|
||||
- Some builtin specifications are slightly different:
|
||||
- Quotes in 'mini.ai' are balanced, in 'mini.surround' they are not.
|
||||
- The 'mini.surround' doesn't have argument surrounding.
|
||||
- Default behavior in 'mini.ai' selects one of the edges into `a`
|
||||
textobject, while 'mini.surround' - both.
|
||||
|
||||
# Highlight groups~
|
||||
|
||||
* `MiniSurround` - highlighting of requested surrounding.
|
||||
|
||||
To change any highlight group, modify it directly with |:highlight|.
|
||||
|
||||
# Disabling~
|
||||
|
||||
To disable, set `g:minisurround_disable` (globally) or
|
||||
`b:minisurround_disable` (for a buffer) to `v:true`. Considering high
|
||||
number of different scenarios and customization intentions, writing exact
|
||||
rules for disabling module's functionality is left to user. See
|
||||
|mini.nvim-disabling-recipes| for common recipes.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSurround-surround-builtin*
|
||||
Builtin surroundings~
|
||||
|
||||
This table describes all builtin surroundings along with what they
|
||||
represent. Explanation:
|
||||
- `Key` represents the surrounding identifier: single character which should
|
||||
be typed after action mappings (see |MiniSurround.config.mappings|).
|
||||
- `Name` is a description of surrounding.
|
||||
- `Example line` contains a string for which examples are constructed. The
|
||||
`*` denotes the cursor position over `a` character.
|
||||
- `Delete` shows the result of typing `sd` followed by surrounding identifier.
|
||||
It aims to demonstrate "input" surrounding which is also used in replace
|
||||
with `sr` (surrounding id is typed first), highlight with `sh`, find with
|
||||
`sf` and `sF`.
|
||||
- `Replace` shows the result of typing `sr!` followed by surrounding
|
||||
identifier (with possible follow up from user). It aims to demonstrate
|
||||
"output" surrounding which is also used in adding with `sa` (followed by
|
||||
textobject/motion or in Visual mode).
|
||||
|
||||
Example: typing `sd)` with cursor on `*` (covers `a` character) changes line
|
||||
`!( *a (bb) )!` into `! aa (bb) !`. Typing `sr!)` changes same initial line
|
||||
into `(( aa (bb) ))`.
|
||||
>
|
||||
|Key| Name | Example line | Delete | Replace |
|
||||
|---|---------------|---------------|-------------|-----------------|
|
||||
| ( | Balanced () | !( *a (bb) )! | !aa (bb)! | ( ( aa (bb) ) ) |
|
||||
| [ | Balanced [] | ![ *a [bb] ]! | !aa [bb]! | [ [ aa [bb] ] ] |
|
||||
| { | Balanced {} | !{ *a {bb} }! | !aa {bb}! | { { aa {bb} } } |
|
||||
| < | Balanced <> | !< *a <bb> >! | !aa <bb>! | < < aa <bb> > > |
|
||||
|---|---------------|---------------|-------------|-----------------|
|
||||
| ) | Balanced () | !( *a (bb) )! | ! aa (bb) ! | (( aa (bb) )) |
|
||||
| ] | Balanced [] | ![ *a [bb] ]! | ! aa [bb] ! | [[ aa [bb] ]] |
|
||||
| } | Balanced {} | !{ *a {bb} }! | ! aa {bb} ! | {{ aa {bb} }} |
|
||||
| > | Balanced <> | !< *a <bb> >! | ! aa <bb> ! | << aa <bb> >> |
|
||||
| b | Alias for | !( *a {bb} )! | ! aa {bb} ! | (( aa {bb} )) |
|
||||
| | ), ], or } | | | |
|
||||
|---|---------------|---------------|-------------|-----------------|
|
||||
| q | Alias for | !'aa'*a'aa'! | !'aaaaaa'! | "'aa'aa'aa'" |
|
||||
| | ", ', or ` | | | |
|
||||
|---|---------------|---------------|-------------|-----------------|
|
||||
| ? | User prompt | !e * o! | ! a ! | ee a oo |
|
||||
| |(typed e and o)| | | |
|
||||
|---|---------------|---------------|-------------|-----------------|
|
||||
| t | Tag | !<x>*</x>! | !a! | <y><x>a</x></y> |
|
||||
| | | | | (typed y) |
|
||||
|---|---------------|---------------|-------------|-----------------|
|
||||
| f | Function call | !f(*a, bb)! | !aa, bb! | g(f(*a, bb)) |
|
||||
| | | | | (typed g) |
|
||||
|---|---------------|---------------|-------------|-----------------|
|
||||
| | Default | !_a*a_! | !aaa! | __aaa__ |
|
||||
| | (typed _) | | | |
|
||||
|---|---------------|---------------|-------------|-----------------|
|
||||
<
|
||||
Notes:
|
||||
- All examples assume default `config.search_method`.
|
||||
- Open brackets differ from close brackets by how they treat inner edge
|
||||
whitespace: open includes it left and right parts, close does not.
|
||||
- Output value of `b` alias is same as `)`. For `q` alias - same as `"`.
|
||||
- Default surrounding is activated for all characters which are not
|
||||
configured surrounding identifiers. Note: due to special handling of
|
||||
underlying `x.-x` Lua pattern (see |MiniSurround-search-algorithm|), it
|
||||
doesn't really support non-trivial `v:count` for "cover" search method.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSurround-glossary*
|
||||
Note: this is similar to |MiniAi-glossary|.
|
||||
|
||||
- REGION - table representing region in a buffer. Fields: <from> and
|
||||
<to> for inclusive start and end positions (<to> might be `nil` to
|
||||
describe empty region). Each position is also a table with line <line>
|
||||
and column <col> (both start at 1). Examples:
|
||||
- `{ from = { line = 1, col = 1 }, to = { line = 2, col = 1 } }`
|
||||
- `{ from = { line = 10, col = 10 } }` - empty region.
|
||||
- REGION PAIR - table representing regions for left and right surroundings.
|
||||
Fields: <left> and <right> with regions. Examples:
|
||||
`{`
|
||||
`left = { from = { line = 1, col = 1 }, to = { line = 1, col = 1 } },`
|
||||
`right = { from = { line = 1, col = 3 } },`
|
||||
`}`
|
||||
- PATTERN - string describing Lua pattern.
|
||||
- SPAN - interval inside a string (end-exclusive). Like [1, 5). Equal
|
||||
`from` and `to` edges describe empty span at that point.
|
||||
- SPAN `A = [a1, a2)` COVERS `B = [b1, b2)` if every element of
|
||||
`B` is within `A` (`a1 <= b < a2`).
|
||||
It also is described as B IS NESTED INSIDE A.
|
||||
- NESTED PATTERN - array of patterns aimed to describe nested spans.
|
||||
- SPAN MATCHES NESTED PATTERN if there is a sequence of consecutively
|
||||
nested spans each matching corresponding pattern within substring of
|
||||
previous span (or input string for first span). Example:
|
||||
Nested patterns: `{ '%b()', '^. .* .$' }` (balanced `()` with inner space)
|
||||
Input string: `( ( () ( ) ) )`
|
||||
`123456789012345`
|
||||
Here are all matching spans [1, 15) and [3, 13). Both [5, 7) and [8, 10)
|
||||
match first pattern but not second. All other combinations of `(` and `)`
|
||||
don't match first pattern (not balanced).
|
||||
- COMPOSED PATTERN: array with each element describing possible pattern
|
||||
(or array of them) at that place. Composed pattern basically defines all
|
||||
possible combinations of nested pattern (their cartesian product).
|
||||
Examples:
|
||||
1. Composed pattern: `{ { '%b()', '%b[]' }, '^. .* .$' }`
|
||||
Composed pattern expanded into equivalent array of nested patterns:
|
||||
`{ '%b()', '^. .* .$' }` and `{ '%b[]', '^. .* .$' }`
|
||||
Description: either balanced `()` or balanced `[]` but both with
|
||||
inner edge space.
|
||||
2. Composed pattern:
|
||||
`{ { { '%b()', '^. .* .$' }, { '%b[]', '^.[^ ].*[^ ].$' } }, '.....' }`
|
||||
Composed pattern expanded into equivalent array of nested patterns:
|
||||
`{ '%b()', '^. .* .$', '.....' }` and
|
||||
`{ '%b[]', '^.[^ ].*[^ ].$', '.....' }`
|
||||
Description: either "balanced `()` with inner edge space" or
|
||||
"balanced `[]` with no inner edge space", both with 5 or more characters.
|
||||
- SPAN MATCHES COMPOSED PATTERN if it matches at least one nested pattern
|
||||
from expanded composed pattern.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSurround-surround-specification*
|
||||
Surround specification is a table with keys:
|
||||
- <input> - defines how to find and extract surrounding for "input"
|
||||
operations (like `delete`). See more in 'Input surrounding' setction.
|
||||
- <output> - defines what to add on left and right for "output" operations
|
||||
(like `add`). See more in 'Output surrounding' section.
|
||||
|
||||
Example of surround info for builtin `)` identifier: >
|
||||
{
|
||||
input = { '%b()', '^.().*().$' },
|
||||
output = { left = '(', right = ')' }
|
||||
}
|
||||
<
|
||||
# Input surrounding ~
|
||||
|
||||
Specification for input surrounding has a structure of composed pattern
|
||||
(see |MiniSurround-glossary|) with two differences:
|
||||
- Last pattern(s) should have two or four empty capture groups denoting
|
||||
how the last string should be processed to extract surrounding parts:
|
||||
- Two captures represent left part from start of string to first
|
||||
capture and right part - from second capture to end of string.
|
||||
Example: `a()b()c` defines left surrounding as 'a', right - 'c'.
|
||||
- Four captures define left part inside captures 1 and 2, right part -
|
||||
inside captures 3 and 4. Example: `a()()b()c()` defines left part as
|
||||
empty, right part as 'c'.
|
||||
- Allows callable objects (see |vim.is_callable()|) in certain places
|
||||
(enables more complex surroundings in exchange of increase in configuration
|
||||
complexity and computations):
|
||||
- If specification itself is a callable, it will be called without
|
||||
arguments and should return one of:
|
||||
- Composed pattern. Useful for implementing user input. Example of
|
||||
simplified variant of input surrounding for function call with
|
||||
name taken from user prompt:
|
||||
>
|
||||
function()
|
||||
local left_edge = vim.pesc(vim.fn.input('Function name: '))
|
||||
return { string.format('%s+%%b()', left_edge), '^.-%(().*()%)$' }
|
||||
end
|
||||
<
|
||||
- Single region pair (see |MiniSurround-glossary|). Useful to allow
|
||||
full control over surrounding. Will be taken as is. Example of
|
||||
returning first and last lines of a buffer:
|
||||
>
|
||||
function()
|
||||
local n_lines = vim.fn.line('$')
|
||||
return {
|
||||
left = {
|
||||
from = { line = 1, col = 1 },
|
||||
to = { line = 1, col = vim.fn.getline(1):len() },
|
||||
},
|
||||
right = {
|
||||
from = { line = n_lines, col = 1 },
|
||||
to = { line = n_lines, col = vim.fn.getline(n_lines):len() },
|
||||
},
|
||||
}
|
||||
end
|
||||
<
|
||||
- Array of region pairs. Useful for incorporating other instruments,
|
||||
like treesitter (see |MiniSurround.gen_spec.treesitter()|). The
|
||||
best region pair will be picked in the same manner as with composed
|
||||
pattern (respecting options `n_lines`, `search_method`, etc.) using
|
||||
output region (from start of left region to end of right region).
|
||||
Example using edges of "best" line with display width more than 80:
|
||||
>
|
||||
function()
|
||||
local make_line_region_pair = function(n)
|
||||
local left = { line = n, col = 1 }
|
||||
local right = { line = n, col = vim.fn.getline(n):len() }
|
||||
return {
|
||||
left = { from = left, to = left },
|
||||
right = { from = right, to = right },
|
||||
}
|
||||
end
|
||||
|
||||
local res = {}
|
||||
for i = 1, vim.fn.line('$') do
|
||||
if vim.fn.getline(i):len() > 80 then
|
||||
table.insert(res, make_line_region_pair(i))
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
<
|
||||
- If there is a callable instead of assumed string pattern, it is expected
|
||||
to have signature `(line, init)` and behave like `pattern:find()`.
|
||||
It should return two numbers representing span in `line` next after
|
||||
or at `init` (`nil` if there is no such span).
|
||||
!IMPORTANT NOTE!: it means that output's `from` shouldn't be strictly
|
||||
to the left of `init` (it will lead to infinite loop). Not allowed as
|
||||
last item (as it should be pattern with captures).
|
||||
Example of matching only balanced parenthesis with big enough width:
|
||||
>
|
||||
{
|
||||
'%b()',
|
||||
function(s, init)
|
||||
if init > 1 or s:len() < 5 then return end
|
||||
return 1, s:len()
|
||||
end,
|
||||
'^.().*().$'
|
||||
}
|
||||
>
|
||||
More examples:
|
||||
- See |MiniSurround.gen_spec| for function wrappers to create commonly used
|
||||
surrounding specifications.
|
||||
|
||||
- Pair of balanced brackets from set (used for builtin `b` identifier):
|
||||
`{ { '%b()', '%b[]', '%b{}' }, '^.().*().$' }`
|
||||
|
||||
- Lua block string: `{ '%[%[().-()%]%]' }`
|
||||
|
||||
# Output surrounding ~
|
||||
|
||||
A table with <left> (plain text string) and <right> (plain text string)
|
||||
fields. Strings can contain new lines charater `\n` to add multiline parts.
|
||||
|
||||
Examples:
|
||||
- Lua block string: `{ left = '[[', right = ']]' }`
|
||||
- Brackets on separate lines (indentation is not preserved):
|
||||
`{ left = '(\n', right = '\n)' }`
|
||||
|
||||
# Transition from previous specification ~
|
||||
|
||||
Previous specification format for input surrounding was a table with <find>
|
||||
and <extract> fields. They are now replaced with composed pattern (see
|
||||
|MiniSurround-glossary|). Previous format will work until next release.
|
||||
|
||||
To convert, remove `find = ` and `extract = ` while replacing left and right
|
||||
captures in `extract` with appropriate empty capture(s). Example:
|
||||
- Previous: `{ find = '%[%[.-%]%]', extract = '^(..).*(..)$' }`.
|
||||
Current: `{ '%[%[().-()%]%]' }`
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSurround-search-algorithm*
|
||||
Search algorithm design
|
||||
|
||||
Search for the input surrounding relies on these principles:
|
||||
- Input surrounding specification is constructed based on surrounding
|
||||
identifier (see |MiniSurround-surround-specification|).
|
||||
- General search is done by converting some 2d buffer region (neighborhood
|
||||
of reference region) into 1d string (each line is appended with `\n`).
|
||||
Then search for a best span matching specification is done inside string
|
||||
(see |MiniSurround-glossary|). After that, span is converted back into 2d
|
||||
region. Note: first search is done inside reference region lines, and
|
||||
only after that - inside its neighborhood within `config.n_lines` (see
|
||||
|MiniSurround.config|).
|
||||
- The best matching span is chosen by iterating over all spans matching
|
||||
surrounding specification and comparing them with "current best".
|
||||
Comparison also depends on reference region (tighter covering is better,
|
||||
otherwise closer is better) and search method (if span is even considered).
|
||||
- Extract pair of spans (for left and right regions in region pair) based
|
||||
on extraction pattern (last item in nested pattern).
|
||||
- For |v:count| greater than 1, steps are repeated with current best match
|
||||
becoming reference region. One such additional step is also done if final
|
||||
region is equal to reference region. Note: |v:count| is not supported for
|
||||
output surroundings because it brings a lot of inconvenience (for adding
|
||||
it affects textobject/motion, for replacing it will be used for both
|
||||
input and output).
|
||||
|
||||
Notes:
|
||||
- Iteration over all matched spans is done in depth-first fashion with
|
||||
respect to nested pattern.
|
||||
- It is guaranteed that span is compared only once.
|
||||
- For the sake of increasing functionality, during iteration over all
|
||||
matching spans, some Lua patterns in composed pattern are handled
|
||||
specially.
|
||||
- `%bxx` (`xx` is two identical characters). It denotes balanced pair
|
||||
of identical characters and results into "paired" matches. For
|
||||
example, `%b""` for `"aa" "bb"` would match `"aa"` and `"bb"`, but
|
||||
not middle `" "`.
|
||||
- `x.-y` (`x` and `y` are different strings). It results only in matches with
|
||||
smallest width. For example, `e.-o` for `e e o o` will result only in
|
||||
middle `e o`. Note: it has some implications for when parts have
|
||||
quantifiers (like `+`, etc.), which usually can be resolved with
|
||||
frontier pattern `%f[]`.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSurround.setup()*
|
||||
`MiniSurround.setup`({config})
|
||||
Module setup
|
||||
|
||||
Parameters~
|
||||
{config} `(table)` Module config table. See |MiniSurround.config|.
|
||||
|
||||
Usage~
|
||||
`require('mini.surround').setup({})` (replace `{}` with your `config` table)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSurround.config*
|
||||
`MiniSurround.config`
|
||||
Module config
|
||||
|
||||
Default values:
|
||||
>
|
||||
MiniSurround.config = {
|
||||
-- Add custom surroundings to be used on top of builtin ones. For more
|
||||
-- information with examples, see `:h MiniSurround.config`.
|
||||
custom_surroundings = nil,
|
||||
|
||||
-- Duration (in ms) of highlight when calling `MiniSurround.highlight()`
|
||||
highlight_duration = 500,
|
||||
|
||||
-- Module mappings. Use `''` (empty string) to disable one.
|
||||
mappings = {
|
||||
add = 'sa', -- Add surrounding in Normal and Visual modes
|
||||
delete = 'sd', -- Delete surrounding
|
||||
find = 'sf', -- Find surrounding (to the right)
|
||||
find_left = 'sF', -- Find surrounding (to the left)
|
||||
highlight = 'sh', -- Highlight surrounding
|
||||
replace = 'sr', -- Replace surrounding
|
||||
update_n_lines = 'sn', -- Update `n_lines`
|
||||
|
||||
suffix_last = 'l', -- Suffix to search with "prev" method
|
||||
suffix_next = 'n', -- Suffix to search with "next" method
|
||||
},
|
||||
|
||||
-- Number of lines within which surrounding is searched
|
||||
n_lines = 20,
|
||||
|
||||
-- How to search for surrounding (first inside current line, then inside
|
||||
-- neighborhood). One of 'cover', 'cover_or_next', 'cover_or_prev',
|
||||
-- 'cover_or_nearest', 'next', 'prev', 'nearest'. For more details,
|
||||
-- see `:h MiniSurround.config`.
|
||||
search_method = 'cover',
|
||||
}
|
||||
<
|
||||
# Setup similar to 'tpope/vim-surround'~
|
||||
|
||||
This module is primarily designed after 'machakann/vim-sandwich'. To get
|
||||
behavior closest to 'tpope/vim-surround' (but not identical), use this setup:
|
||||
>
|
||||
require('mini.surround').setup({
|
||||
mappings = {
|
||||
add = 'ys',
|
||||
delete = 'ds',
|
||||
find = '',
|
||||
find_left = '',
|
||||
highlight = '',
|
||||
replace = 'cs',
|
||||
update_n_lines = '',
|
||||
|
||||
-- Add this only if you don't want to use extended mappings
|
||||
suffix_last = '',
|
||||
suffix_next = '',
|
||||
},
|
||||
search_method = 'cover_or_next',
|
||||
})
|
||||
|
||||
-- Remap adding surrounding to Visual mode selection
|
||||
vim.api.nvim_del_keymap('x', 'ys')
|
||||
vim.api.nvim_set_keymap('x', 'S', [[:<C-u>lua MiniSurround.add('visual')<CR>]], { noremap = true })
|
||||
|
||||
-- Make special mapping for "add surrounding for line"
|
||||
vim.api.nvim_set_keymap('n', 'yss', 'ys_', { noremap = false })
|
||||
<
|
||||
# Options~
|
||||
|
||||
## Custom surroundings~
|
||||
|
||||
User can define own surroundings by supplying `config.custom_surroundings`.
|
||||
It should be a **table** with keys being single character surrounding
|
||||
identifier and values - surround specification (see
|
||||
|MiniSurround-surround-specification|).
|
||||
|
||||
General recommendations:
|
||||
- In `config.custom_surroundings` only some data can be defined (like only
|
||||
`output`). Other fields will be taken from builtin surroundings.
|
||||
- Function returning surround info at <input> or <output> fields of
|
||||
specification is helpful when user input is needed (like asking for
|
||||
function name). Use |input()| or |MiniSurround.user_input()|. Return
|
||||
`nil` to stop any current surround operation.
|
||||
|
||||
Examples of using `config.custom_surroundings` (see more examples at
|
||||
|MiniSurround.gen_spec|):
|
||||
>
|
||||
local surround = require('mini.surround')
|
||||
surround.setup({
|
||||
custom_surroundings = {
|
||||
-- Make `)` insert parts with spaces. `input` pattern stays the same.
|
||||
[')'] = { output = { left = '( ', right = ' )' } },
|
||||
|
||||
-- Use function to compute surrounding info
|
||||
['*'] = {
|
||||
input = function()
|
||||
local n_star = MiniSurround.user_input('Number of * to find: ')
|
||||
local many_star = string.rep('%*', tonumber(n_star) or 1)
|
||||
return { many_star .. '().-()' .. many_star }
|
||||
end,
|
||||
output = function()
|
||||
local n_star = MiniSurround.user_input('Number of * to output: ')
|
||||
local many_star = string.rep('*', tonumber(n_star) or 1)
|
||||
return { left = many_star, right = many_star }
|
||||
end,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
-- Create custom surrouding for Lua's block string `[[...]]`. Use this inside
|
||||
-- autocommand or 'after/ftplugin/lua.lua' file.
|
||||
vim.b.minisurround_config = {
|
||||
custom_surroundings = {
|
||||
s = {
|
||||
input = { '%[%[().-()%]%]' },
|
||||
output = { left = '[[', right = ']]' },
|
||||
},
|
||||
},
|
||||
}
|
||||
<
|
||||
## Search method~
|
||||
|
||||
Value of `config.search_method` defines how best match search is done.
|
||||
Based on its value, one of the following matches will be selected:
|
||||
- Covering match. Left/right edge is before/after left/right edge of
|
||||
reference region.
|
||||
- Previous match. Left/right edge is before left/right edge of reference
|
||||
region.
|
||||
- Next match. Left/right edge is after left/right edge of reference region.
|
||||
- Nearest match. Whichever is closest among previous and next matches.
|
||||
|
||||
Possible values are:
|
||||
- `'cover'` - use only covering match. Don't use either previous or
|
||||
next; report that there is no surrounding found.
|
||||
- `'cover_or_next'` (default) - use covering match. If not found, use next.
|
||||
- `'cover_or_prev'` - use covering match. If not found, use previous.
|
||||
- `'cover_or_nearest'` - use covering match. If not found, use nearest.
|
||||
- `'next'` - use next match.
|
||||
- `'previous'` - use previous match.
|
||||
- `'nearest'` - use nearest match.
|
||||
|
||||
Note: search is first performed on the reference region lines and only
|
||||
after failure - on the whole neighborhood defined by `config.n_lines`. This
|
||||
means that with `config.search_method` not equal to `'cover'`, "previous"
|
||||
or "next" surrounding will end up as search result if they are found on
|
||||
first stage although covering match might be found in bigger, whole
|
||||
neighborhood. This design is based on observation that most of the time
|
||||
operation is done within reference region lines (usually cursor line).
|
||||
|
||||
Here is an example of how replacing `)` with `]` surrounding is done based
|
||||
on a value of `'config.search_method'` when cursor is inside `bbb` word:
|
||||
- `'cover'`: `(a) bbb (c)` -> `(a) bbb (c)` (with message)
|
||||
- `'cover_or_next'`: `(a) bbb (c)` -> `(a) bbb [c]`
|
||||
- `'cover_or_prev'`: `(a) bbb (c)` -> `[a] bbb (c)`
|
||||
- `'cover_or_nearest'`: depends on cursor position.
|
||||
For first and second `b` - as in `cover_or_prev` (as previous match is
|
||||
nearer), for third - as in `cover_or_next` (as next match is nearer).
|
||||
- `'next'`: `(a) bbb (c)` -> `(a) bbb [c]`. Same outcome for `(bbb)`.
|
||||
- `'prev'`: `(a) bbb (c)` -> `[a] bbb (c)`. Same outcome for `(bbb)`.
|
||||
- `'nearest'`: depends on cursor position (same as in `'cover_or_nearest'`).
|
||||
|
||||
## Search suffixes~
|
||||
|
||||
To provide more searching possibilities, 'mini.surround' creates extended
|
||||
mappings force "prev" and "next" methods for particular search. It does so
|
||||
by appending mapping with certain suffix: `config.mappings.suffix_last` for
|
||||
mappings which will use "prev" search method, `config.mappings.suffix_next`
|
||||
- "next" search method.
|
||||
|
||||
Notes:
|
||||
- It creates new mappings only for actions involving surrounding search:
|
||||
delete, replace, find (right and left), highlight.
|
||||
- All new mappings behave the same way as if `config.search_method` is set
|
||||
to certain search method. They are dot-repeatable, respect |v:count|, etc.
|
||||
- Supply empty string to disable creation of corresponding set of mappings.
|
||||
|
||||
Example with default values (`n` for `suffix_next`, `l` for `suffix_last`)
|
||||
and initial line `(aa) (bb) (cc)`.
|
||||
- Typing `sdn)` with cursor inside `(aa)` results into `(aa) bb (cc)`.
|
||||
- Typing `sdl)` with cursor inside `(cc)` results into `(aa) bb (cc)`.
|
||||
- Typing `2srn)]` with cursor inside `(aa)` results into `(aa) (bb) [cc]`.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSurround.operator()*
|
||||
`MiniSurround.operator`({task}, {cache})
|
||||
Surround operator
|
||||
|
||||
Main function to be used in expression mappings. No need to use it
|
||||
directly, everything is setup in |MiniSurround.setup|.
|
||||
|
||||
Parameters~
|
||||
{task} `(string)` Name of surround task.
|
||||
{cache} `(table)` Task cache.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSurround.add()*
|
||||
`MiniSurround.add`({mode})
|
||||
Add surrounding
|
||||
|
||||
No need to use it directly, everything is setup in |MiniSurround.setup|.
|
||||
|
||||
Parameters~
|
||||
{mode} `(string)` Mapping mode (normal by default).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSurround.delete()*
|
||||
`MiniSurround.delete`()
|
||||
Delete surrounding
|
||||
|
||||
No need to use it directly, everything is setup in |MiniSurround.setup|.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSurround.replace()*
|
||||
`MiniSurround.replace`()
|
||||
Replace surrounding
|
||||
|
||||
No need to use it directly, everything is setup in |MiniSurround.setup|.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSurround.find()*
|
||||
`MiniSurround.find`()
|
||||
Find surrounding
|
||||
|
||||
No need to use it directly, everything is setup in |MiniSurround.setup|.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSurround.highlight()*
|
||||
`MiniSurround.highlight`()
|
||||
Highlight surrounding
|
||||
|
||||
No need to use it directly, everything is setup in |MiniSurround.setup|.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSurround.update_n_lines()*
|
||||
`MiniSurround.update_n_lines`()
|
||||
Update `MiniSurround.config.n_lines`
|
||||
|
||||
Convenient wrapper for updating `MiniSurround.config.n_lines` in case the
|
||||
default one is not appropriate.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSurround.user_input()*
|
||||
`MiniSurround.user_input`({prompt}, {text})
|
||||
Ask user for input
|
||||
|
||||
This is mainly a wrapper for |input()| which allows empty string as input,
|
||||
cancelling with `<Esc>` and `<C-c>`, and slightly modifies prompt. Use it
|
||||
to ask for input inside function custom surrounding (see |MiniSurround.config|).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSurround.gen_spec*
|
||||
`MiniSurround.gen_spec`
|
||||
Generate common surrounding specifications
|
||||
|
||||
This is a table with two sets of generator functions: <input> and <output>
|
||||
(currently empty). Each is a table with values being function generating
|
||||
corresponding surrounding specification.
|
||||
|
||||
Example: >
|
||||
local ts_input = require('mini.surround').gen_spec.input.treesitter
|
||||
require('mini.surround').setup({
|
||||
custom_surroundings = {
|
||||
-- Use tree-sitter to search for function call
|
||||
f = {
|
||||
input = ts_input({ outer = '@call.outer', inner = '@call.inner' })
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
See also~
|
||||
|MiniAi.gen_spec|
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniSurround.gen_spec.input.treesitter()*
|
||||
`MiniSurround.gen_spec.input.treesitter`({captures}, {opts})
|
||||
Treesitter specification for input surrounding
|
||||
|
||||
This is a specification in function form. When called with a pair of
|
||||
treesitter captures, it returns a specification function outputting an
|
||||
array of region pairs derived from <outer> and <inner> captures. It first
|
||||
searches for all matched nodes of outer capture and then completes each one
|
||||
with the biggest match of inner capture inside that node (if any). The result
|
||||
region pair is a difference between regions of outer and inner captures.
|
||||
|
||||
In order for this to work, apart from working treesitter parser for desired
|
||||
language, user should have a reachable language-specific 'textobjects'
|
||||
query (see |get_query()|). The most straightforward way for this is to have
|
||||
'textobjects.scm' query file with treesitter captures stored in some
|
||||
recognized path. This is primarily designed to be compatible with
|
||||
'nvim-treesitter/nvim-treesitter-textobjects' plugin, but can be used
|
||||
without it.
|
||||
|
||||
Two most common approaches for having a query file:
|
||||
- Install 'nvim-treesitter/nvim-treesitter-textobjects'. It has curated and
|
||||
well maintained builtin query files for many languages with a standardized
|
||||
capture names, like `call.outer`, `call.inner`, etc.
|
||||
- Manually create file 'after/queries/<language name>/textobjects.scm' in
|
||||
your |$XDG_CONFIG_HOME| directory. It should contain queries with
|
||||
captures (later used to define surrounding parts). See |lua-treesitter-query|.
|
||||
To verify that query file is reachable, run (example for "lua" language)
|
||||
`:lua print(vim.inspect(vim.treesitter.get_query_files('lua', 'textobjects')))`
|
||||
(output should have at least an intended file).
|
||||
|
||||
Example configuration for function definition textobject with
|
||||
'nvim-treesitter/nvim-treesitter-textobjects' captures:
|
||||
>
|
||||
local ts_input = require('mini.surround').gen_spec.input.treesitter
|
||||
require('mini.surround').setup({
|
||||
custom_textobjects = {
|
||||
f = ts_input({ outer = '@call.outer', inner = '@call.inner' }),
|
||||
}
|
||||
})
|
||||
>
|
||||
|
||||
Notes:
|
||||
- By default query is done using 'nvim-treesitter' plugin if it is present
|
||||
(falls back to builtin methods otherwise). This allows for a more
|
||||
advanced features (like multiple buffer languages, custom directives, etc.).
|
||||
See `opts.use_nvim_treesitter` for how to disable this.
|
||||
- It uses buffer's |filetype| to determine query language.
|
||||
- On large files it is slower than pattern-based textobjects. Still very
|
||||
fast though (one search should be magnitude of milliseconds or tens of
|
||||
milliseconds on really large file).
|
||||
|
||||
Parameters~
|
||||
{captures} `(table)` Captures for outer and inner parts of region pair:
|
||||
table with <outer> and <inner> fields with captures for outer
|
||||
(`[left.form; right.to]`) and inner (`(left.to; right.from)` both edges
|
||||
exclusive, i.e. they won't be a part of surrounding) regions. Each value
|
||||
should be a string capture starting with `'@'`.
|
||||
{opts} `(table)` Options. Possible values:
|
||||
- <use_nvim_treesitter> - whether to try to use 'nvim-treesitter' plugin
|
||||
(if present) to do the query. It implements more advanced behavior at
|
||||
cost of increased execution time. Provides more coherent experience if
|
||||
'nvim-treesitter-textobjects' queries are used. Default: `true`.
|
||||
|
||||
Return~
|
||||
`(function)` Function which returns array of current buffer region pairs
|
||||
representing differences between outer and inner captures.
|
||||
|
||||
See also~
|
||||
|MiniSurround-surround-specification| for how this type of
|
||||
surrounding specification is processed.
|
||||
|get_query()| for how query is fetched in case of no 'nvim-treesitter'.
|
||||
|Query:iter_captures()| for how all query captures are iterated in case of
|
||||
no 'nvim-treesitter'.
|
||||
|MiniAi.gen_spec.treesitter()| for similar 'mini.ai' generator.
|
||||
|
||||
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
|
@ -0,0 +1,103 @@
|
|||
==============================================================================
|
||||
------------------------------------------------------------------------------
|
||||
*mini.tabline*
|
||||
*MiniTabline*
|
||||
Minimal and fast tabline showing listed buffers. General idea: show all
|
||||
listed buffers in readable way with minimal total width. Also allow showing
|
||||
extra information section in case of multiple vim tabpages.
|
||||
|
||||
Features:
|
||||
- Buffers are listed in the order of their identifier (see |bufnr()|).
|
||||
- Different highlight groups for "states" of buffer affecting 'buffer tabs'.
|
||||
- Buffer names are made unique by extending paths to files or appending
|
||||
unique identifier to buffers without name.
|
||||
- Current buffer is displayed "optimally centered" (in center of screen
|
||||
while maximizing the total number of buffers shown) when there are many
|
||||
buffers open.
|
||||
- 'Buffer tabs' are clickable if Neovim allows it.
|
||||
|
||||
What it doesn't do:
|
||||
- Custom buffer order is not supported.
|
||||
|
||||
# Dependencies~
|
||||
|
||||
Suggested dependencies (provide extra functionality, tabline will work
|
||||
without them):
|
||||
- Plugin 'kyazdani42/nvim-web-devicons' for filetype icons near the buffer
|
||||
name. If missing, no icons will be shown.
|
||||
|
||||
# Setup~
|
||||
|
||||
This module needs a setup with `require('mini.tabline').setup({})`
|
||||
(replace `{}` with your `config` table). It will create global Lua table
|
||||
`MiniTabline` which you can use for scripting or manually (with
|
||||
`:lua MiniTabline.*`).
|
||||
|
||||
See |MiniTabline.config| for `config` structure and default values.
|
||||
|
||||
You can override runtime config settings locally to buffer inside
|
||||
`vim.b.minitabline_config` which should have same structure as
|
||||
`MiniTabline.config`. See |mini.nvim-buffer-local-config| for more details.
|
||||
|
||||
# Highlight groups~
|
||||
|
||||
* `MiniTablineCurrent` - buffer is current (has cursor in it).
|
||||
* `MiniTablineVisible` - buffer is visible (displayed in some window).
|
||||
* `MiniTablineHidden` - buffer is hidden (not displayed).
|
||||
* `MiniTablineModifiedCurrent` - buffer is modified and current.
|
||||
* `MiniTablineModifiedVisible` - buffer is modified and visible.
|
||||
* `MiniTablineModifiedHidden` - buffer is modified and hidden.
|
||||
* `MiniTablineFill` - unused right space of tabline.
|
||||
* `MiniTablineTabpagesection` - section with tabpage information.
|
||||
|
||||
To change any highlight group, modify it directly with |:highlight|.
|
||||
|
||||
# Disabling~
|
||||
|
||||
To disable (show empty tabline), set `g:minitabline_disable` (globally) or
|
||||
`b:minitabline_disable` (for a buffer) to `v:true`. Considering high number
|
||||
of different scenarios and customization intentions, writing exact rules
|
||||
for disabling module's functionality is left to user. See
|
||||
|mini.nvim-disabling-recipes| for common recipes. Note: after disabling,
|
||||
tabline is not updated right away, but rather after dedicated event (see
|
||||
|events| and `MiniTabline` |augroup|).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTabline.setup()*
|
||||
`MiniTabline.setup`({config})
|
||||
Module setup
|
||||
|
||||
Parameters~
|
||||
{config} `(table)` Module config table. See |MiniTabline.config|.
|
||||
|
||||
Usage~
|
||||
`require('mini.tabline').setup({})` (replace `{}` with your `config` table)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTabline.config*
|
||||
`MiniTabline.config`
|
||||
Module config
|
||||
|
||||
Default values:
|
||||
>
|
||||
MiniTabline.config = {
|
||||
-- Whether to show file icons (requires 'kyazdani42/nvim-web-devicons')
|
||||
show_icons = true,
|
||||
|
||||
-- Whether to set Vim's settings for tabline (make it always shown and
|
||||
-- allow hidden buffers)
|
||||
set_vim_settings = true,
|
||||
|
||||
-- Where to show tabpage section in case of multiple vim tabpages.
|
||||
-- One of 'left', 'right', 'none'.
|
||||
tabpage_section = 'left',
|
||||
}
|
||||
<
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTabline.make_tabline_string()*
|
||||
`MiniTabline.make_tabline_string`()
|
||||
Make string for |tabline|
|
||||
|
||||
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
|
@ -0,0 +1,884 @@
|
|||
==============================================================================
|
||||
------------------------------------------------------------------------------
|
||||
*mini.test*
|
||||
*MiniTest*
|
||||
Write and use extensive neovim plugin tests
|
||||
|
||||
Features:
|
||||
- Test action is defined as a named callable entry of a table.
|
||||
- Helper for creating child Neovim process which is designed to be used in
|
||||
tests (including taking and verifying screenshots). See
|
||||
|MiniTest.new_child_neovim()| and |Minitest.expect.reference_screenshot()|.
|
||||
- Hierarchical organization of tests with custom hooks, parametrization,
|
||||
and user data. See |MiniTest.new_set()|.
|
||||
- Emulation of 'Olivine-Labs/busted' interface (`describe`, `it`, etc.).
|
||||
- Predefined small yet usable set of expectations (`assert`-like functions).
|
||||
See |MiniTest.expect|.
|
||||
- Customizable definition of what files should be tested.
|
||||
- Test case filtering. There are predefined wrappers for testing a file
|
||||
(|MiniTest.run_file()|) and case at a location like current cursor position
|
||||
(|MiniTest.run_at_location()|).
|
||||
- Customizable reporter of output results. There are two predefined ones:
|
||||
- |MiniTest.gen_reporter.buffer()| for interactive usage.
|
||||
- |MiniTest.gen_reporter.stdout()| for headless Neovim.
|
||||
- Customizable project specific testing script.
|
||||
|
||||
What it doesn't support:
|
||||
- Parallel execution. Due to idea of limiting implementation complexity.
|
||||
- Mocks, stubs, etc. Use child Neovim process and manually override what is
|
||||
needed. Reset child process it afterwards.
|
||||
- "Overly specific" expectations. Tests for (no) equality and (absence of)
|
||||
errors usually cover most of the needs. Adding new expectations is a
|
||||
subject to weighing its usefulness against additional implementation
|
||||
complexity. Use |MiniTest.new_expectation()| to create custom ones.
|
||||
|
||||
For more information see:
|
||||
- 'TESTING.md' file for a hands-on introduction based on examples.
|
||||
- Code of this plugin's tests. Consider it to be an example of intended
|
||||
way to use 'mini.test' for test organization and creation.
|
||||
|
||||
# Workflow
|
||||
|
||||
- Organize tests in separate files. Each test file should return a test set
|
||||
(explicitly or implicitly by using "busted" style functions).
|
||||
- Write test actions as callable entries of test set. Use child process
|
||||
inside test actions (see |MiniTest.new_child_neovim()|) and builtin
|
||||
expectations (see |MiniTest.expect|).
|
||||
- Run tests. This does two steps:
|
||||
- *Collect*. This creates single hierarchical test set, flattens into
|
||||
array of test cases (see |MiniTest-test-case|) while expanding with
|
||||
parametrization, and possibly filters them.
|
||||
- *Execute*. This safely calls hooks and main test actions in specified
|
||||
order while allowing reporting progress in asynchronous fashion.
|
||||
Detected errors means test case fail; otherwise - pass.
|
||||
|
||||
# Setup~
|
||||
|
||||
This module needs a setup with `require('mini.test').setup({})` (replace
|
||||
`{}` with your `config` table). It will create global Lua table `MiniTest`
|
||||
which you can use for scripting or manually (with `:lua MiniTest.*`).
|
||||
|
||||
See |MiniTest.config| for available config settings.
|
||||
|
||||
You can override runtime config settings locally to buffer inside
|
||||
`vim.b.minitest_config` which should have same structure as `MiniTest.config`.
|
||||
See |mini.nvim-buffer-local-config| for more details.
|
||||
|
||||
# Comparisons~
|
||||
|
||||
- Testing infrastructure from 'nvim-lua/plenary.nvim':
|
||||
- Executes each file in separate headless Neovim process with customizable
|
||||
'init.vim' file. While 'mini.test' executes everything in current
|
||||
Neovim process encouraging writing tests with help of manually
|
||||
managed child Neovim process (see |MiniTest.new_child_neovim()|).
|
||||
- Tests are expected to be written with embedded simplified versions of
|
||||
'Olivine-Labs/busted' and 'Olivine-Labs/luassert'. While 'mini.test'
|
||||
uses concepts of test set (see |MiniTest.new_set()|) and test case
|
||||
(see |MiniTest-test-case|). It also can emulate bigger part of
|
||||
"busted" framework.
|
||||
- Has single way of reporting progress (shows result after every case
|
||||
without summary). While 'mini.test' can have customized reporters
|
||||
with defaults for interactive and headless usage (provide more
|
||||
compact and user-friendly summaries).
|
||||
- Allows parallel execution, while 'mini.test' does not.
|
||||
- Allows making mocks, stubs, and spies, while 'mini.test' does not in
|
||||
favor of manually overwriting functionality in child Neovim process.
|
||||
|
||||
Although 'mini.test' supports emulation of "busted style" testing, it will
|
||||
be more stable to use its designed approach of defining tests (with
|
||||
`MiniTest.new_set()` and explicit table fields). Couple of reasons:
|
||||
- "Busted" syntax doesn't support full capabilities offered by 'mini.test'.
|
||||
Mainly it is about parametrization and supplying user data to test sets.
|
||||
- It is an emulation, not full support. So some subtle things might not
|
||||
work the way you expect.
|
||||
|
||||
Some hints for converting from 'plenary.nvim' tests to 'mini.test':
|
||||
- Rename files from "***_spec.lua" to "test_***.lua" and put them in
|
||||
"tests" directory.
|
||||
- Replace `assert` calls with 'mini.test' expectations. See |MiniTest.expect|.
|
||||
- Create main test set `T = MiniTest.new_set()` and eventually return it.
|
||||
- Make new sets (|MiniTest.new_set()|) from `describe` blocks. Convert
|
||||
`before_each()` and `after_each` to `pre_case` and `post_case` hooks.
|
||||
- Make test cases from `it` blocks.
|
||||
|
||||
# Highlight groups~
|
||||
|
||||
* `MiniTestEmphasis` - emphasis highlighting. By default it is a bold text.
|
||||
* `MiniTestFail` - highlighting of failed cases. By default it is a bold
|
||||
text with `vim.g.terminal_color_1` color (red).
|
||||
* `MiniTestPass` - highlighting of passed cases. By default it is a bold
|
||||
text with `vim.g.terminal_color_2` color (green).
|
||||
|
||||
To change any highlight group, modify it directly with |:highlight|.
|
||||
|
||||
# Disabling~
|
||||
|
||||
To disable, set `g:minitest_disable` (globally) or `b:minitest_disable`
|
||||
(for a buffer) to `v:true`. Considering high number of different scenarios
|
||||
and customization intentions, writing exact rules for disabling module's
|
||||
functionality is left to user. See |mini.nvim-disabling-recipes| for common
|
||||
recipes.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.setup()*
|
||||
`MiniTest.setup`({config})
|
||||
Module setup
|
||||
|
||||
Parameters~
|
||||
{config} `(table|nil)` Module config table. See |MiniTest.config|.
|
||||
|
||||
Usage~
|
||||
`require('mini.test').setup({})` (replace `{}` with your `config` table)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.config*
|
||||
`MiniTest.config`
|
||||
Module config
|
||||
|
||||
Default values:
|
||||
>
|
||||
MiniTest.config = {
|
||||
-- Options for collection of test cases. See `:h MiniTest.collect()`.
|
||||
collect = {
|
||||
-- Temporarily emulate functions from 'busted' testing framework
|
||||
-- (`describe`, `it`, `before_each`, `after_each`, and more)
|
||||
emulate_busted = true,
|
||||
|
||||
-- Function returning array of file paths to be collected.
|
||||
-- Default: all Lua files in 'tests' directory starting with 'test_'.
|
||||
find_files = function()
|
||||
return vim.fn.globpath('tests', '**/test_*.lua', true, true)
|
||||
end,
|
||||
|
||||
-- Predicate function indicating if test case should be executed
|
||||
filter_cases = function(case) return true end,
|
||||
},
|
||||
|
||||
-- Options for execution of test cases. See `:h MiniTest.execute()`.
|
||||
execute = {
|
||||
-- Table with callable fields `start()`, `update()`, and `finish()`
|
||||
reporter = nil,
|
||||
|
||||
-- Whether to stop execution after first error
|
||||
stop_on_error = false,
|
||||
},
|
||||
|
||||
-- Path (relative to current directory) to script which handles project
|
||||
-- specific test running
|
||||
script_path = 'scripts/minitest.lua',
|
||||
}
|
||||
<
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.current*
|
||||
`MiniTest.current`
|
||||
Table with information about current state of test execution
|
||||
|
||||
Use it to examine result of |MiniTest.execute()|. It is reset at the
|
||||
beginning of every call.
|
||||
|
||||
At least these keys are supported:
|
||||
- <all_cases> - array with all cases being currently executed. Basically,
|
||||
an input of `MiniTest.execute()`.
|
||||
- <case> - currently executed test case. See |MiniTest-test-case|. Use it
|
||||
to customize execution output (like adding custom notes, etc).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.new_set()*
|
||||
`MiniTest.new_set`({opts}, {tbl})
|
||||
Create test set
|
||||
|
||||
Test set is one of the two fundamental data structures. It is a table that
|
||||
defines hierarchical test organization as opposed to sequential
|
||||
organization with |MiniTest-test-case|.
|
||||
|
||||
All its elements are one of three categories:
|
||||
- A callable (object that can be called; function or table with `__call`
|
||||
metatble entry) is considered to define a test action. It will be called
|
||||
with "current arguments" (result of all nested `parametrize` values, read
|
||||
further). If it throws error, test has failed.
|
||||
- A test set (output of this function) defines nested structure. Its
|
||||
options during collection (see |MiniTest.collect()|) will be extended
|
||||
with options of this (parent) test set.
|
||||
- Any other elements are considered helpers and don't directly participate
|
||||
in test structure.
|
||||
|
||||
Set options allow customization of test collection and execution (more
|
||||
details in `opts` description):
|
||||
- `hooks` - table with elements that will be called without arguments at
|
||||
predefined stages of test execution.
|
||||
- `parametrize` - array defining different arguments with which main test
|
||||
actions will be called. Any non-trivial parametrization will lead to
|
||||
every element (even nested) be "multiplied" and processed with every
|
||||
element of `parametrize`. This allows handling many different combination
|
||||
of tests with little effort.
|
||||
- `data` - table with user data that will be forwarded to cases. Primary
|
||||
objective is to be used for customized case filtering.
|
||||
|
||||
Notes:
|
||||
- Preferred way of adding elements is by using syntax `T[name] = element`.
|
||||
This way order of added elements will be preserved. Any other way won't
|
||||
guarantee any order.
|
||||
- Supplied options `opts` are stored in `opts` field of metatable
|
||||
(`getmetatable(set).opts`).
|
||||
|
||||
Parameters~
|
||||
{opts} `(table|nil)` Allowed options:
|
||||
- <hooks> - table with fields:
|
||||
- <pre_once> - executed before first filtered node.
|
||||
- <pre_case> - executed before each case (even nested).
|
||||
- <post_case> - executed after each case (even nested).
|
||||
- <post_once> - executed after last filtered node.
|
||||
- <parametrize> - array where each element is itself an array of
|
||||
parameters to be appended to "current parameters" of callable fields.
|
||||
Note: don't use plain `{}` as it is equivalent to "parametrization into
|
||||
zero cases", so no cases will be collected from this set. Calling test
|
||||
actions with no parameters is equivalent to `{{}}` or not supplying
|
||||
`parametrize` option at all.
|
||||
- <data> - user data to be forwarded to cases. Can be used for a more
|
||||
granular filtering.
|
||||
{tbl} `(table|nil)` Initial test items (possibly nested). Will be executed
|
||||
without any guarantees on order.
|
||||
|
||||
Return~
|
||||
`(table)` A single test set.
|
||||
|
||||
Usage~
|
||||
>
|
||||
-- Use with defaults
|
||||
T = MiniTest.new_set()
|
||||
T['works'] = function() MiniTest.expect.equality(1, 1) end
|
||||
|
||||
-- Use with custom options. This will result into two actual cases: first
|
||||
-- will pass, second - fail.
|
||||
T['nested'] = MiniTest.new_set({
|
||||
hooks = { pre_case = function() _G.x = 1 end },
|
||||
parametrize = { { 1 }, { 2 } }
|
||||
})
|
||||
|
||||
T['nested']['works'] = function(x)
|
||||
MiniTest.expect.equality(_G.x, x)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest-test-case*
|
||||
Test case
|
||||
|
||||
An item of sequential test organization, as opposed to hierarchical with
|
||||
test set (see |MiniTest.new_set()|). It is created as result of test
|
||||
collection with |MiniTest.collect()| to represent all necessary information
|
||||
of test execution.
|
||||
|
||||
Execution of test case goes by the following rules:
|
||||
- Call functions in order:
|
||||
- All elements of `hooks.pre` from first to last without arguments.
|
||||
- Field `test` with arguments unpacked from `args`.
|
||||
- All elements of `hooks.post` from first to last without arguments.
|
||||
- Error in any call gets appended to `exec.fails`, meaning error in any
|
||||
hook will lead to test fail.
|
||||
- State (`exec.state`) is changed before every call and after last call.
|
||||
|
||||
Class~
|
||||
{Test-case}
|
||||
|
||||
Fields~
|
||||
{args} `(table)` Array of arguments with which `test` will be called.
|
||||
{data} `(table)` User data: all fields of `opts.data` from nested test sets.
|
||||
{desc} `(table)` Description: array of fields from nested test sets.
|
||||
{exec} `(table|nil)` Information about test case execution. Value of `nil` means
|
||||
that this particular case was not (yet) executed. Has following fields:
|
||||
- <fails> - array of strings with failing information.
|
||||
- <notes> - array of strings with non-failing information.
|
||||
- <state> - state of test execution. One of:
|
||||
- 'Executing <name of what is being executed>' (during execution).
|
||||
- 'Pass' (no fails, no notes).
|
||||
- 'Pass with notes' (no fails, some notes).
|
||||
- 'Fail' (some fails, no notes).
|
||||
- 'Fail with notes' (some fails, some notes).
|
||||
{hooks} `(table)` Hooks to be executed as part of test case. Has fields
|
||||
<pre> and <post> with arrays to be consecutively executed before and
|
||||
after execution of `test`.
|
||||
{test} `(function|table)` Main callable object representing test action.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.skip()*
|
||||
`MiniTest.skip`({msg})
|
||||
Skip rest of current callable execution
|
||||
|
||||
Can be used inside hooks and main test callable of test case. Note: at the
|
||||
moment implemented as a specially handled type of error.
|
||||
|
||||
Parameters~
|
||||
{msg} `(string|nil)` Message to be added to current case notes.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.add_note()*
|
||||
`MiniTest.add_note`({msg})
|
||||
Add note to currently executed test case
|
||||
|
||||
Appends `msg` to `exec.notes` field of |MiniTest.current.case|.
|
||||
|
||||
Parameters~
|
||||
{msg} `(string)` Note to add.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.finally()*
|
||||
`MiniTest.finally`({f})
|
||||
Register callable execution after current callable
|
||||
|
||||
Can be used inside hooks and main test callable of test case.
|
||||
|
||||
Parameters~
|
||||
{f} `(function)` Callable to be executed after current callable is finished
|
||||
executing (regardless of whether it ended with error or not).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.run()*
|
||||
`MiniTest.run`({opts})
|
||||
Run tests
|
||||
|
||||
- Try executing project specific script at path `opts.script_path`. If
|
||||
successful (no errors), then stop.
|
||||
- Collect cases with |MiniTest.collect()| and `opts.collect`.
|
||||
- Execute collected cases with |MiniTest.execute()| and `opts.execute`.
|
||||
|
||||
Parameters~
|
||||
{opts} `(table|nil)` Options with structure similar to |MiniTest.config|.
|
||||
Absent values are inferred from there.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.run_file()*
|
||||
`MiniTest.run_file`({file}, {opts})
|
||||
Run specific test file
|
||||
|
||||
Basically a |MiniTest.run()| wrapper with custom `collect.find_files` option.
|
||||
|
||||
Parameters~
|
||||
{file} `(string|nil)` Path to test file. By default a path of current buffer.
|
||||
{opts} `(table|nil)` Options for |MiniTest.run()|.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.run_at_location()*
|
||||
`MiniTest.run_at_location`({location}, {opts})
|
||||
Run case(s) covering location
|
||||
|
||||
Try filtering case(s) covering location, meaning that definition of its
|
||||
main `test` action (as taken from builtin `debug.getinfo`) is located in
|
||||
specified file and covers specified line. Note that it can result in
|
||||
multiple cases if they come from parametrized test set (see `parametrize`
|
||||
option in |MiniTest.new_set()|).
|
||||
|
||||
Basically a |MiniTest.run()| wrapper with custom `collect.find_files` option.
|
||||
|
||||
Parameters~
|
||||
{location} `(table|nil)` Table with fields <file> (path to file) and <line>
|
||||
(line number in that file). Default is taken from current cursor position.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.collect()*
|
||||
`MiniTest.collect`({opts})
|
||||
Collect test cases
|
||||
|
||||
Overview of collection process:
|
||||
- If `opts.emulate_busted` is `true`, temporary make special global
|
||||
functions (removed at the end of collection). They can be used inside
|
||||
test files to create hierarchical structure of test cases.
|
||||
- Source each file from array output of `opts.find_files`. It should output
|
||||
a test set (see |MiniTest.new_set()|) or `nil` (if "busted" style is used;
|
||||
test set is created implicitly).
|
||||
- Combine all test sets into single set with fields equal to its file path.
|
||||
- Convert from hierarchical test configuration to sequential: from single
|
||||
test set to array of test cases (see |MiniTest-test-case|). Conversion is
|
||||
done in the form of "for every table element do: for every `parametrize`
|
||||
element do: ...". Details:
|
||||
- If element is a callable, construct test case with it being main
|
||||
`test` action. Description is appended with key of element in current
|
||||
test set table. Hooks, arguments, and data are taken from "current
|
||||
nested" ones. Add case to output array.
|
||||
- If element is a test set, process it in similar, recursive fashion.
|
||||
The "current nested" information is expanded:
|
||||
- `args` is extended with "current element" from `parametrize`.
|
||||
- `desc` is appended with element key.
|
||||
- `hooks` are appended to their appropriate places. `*_case` hooks
|
||||
will be inserted closer to all child cases than hooks from parent
|
||||
test sets: `pre_case` at end, `post_case` at start.
|
||||
- `data` is extended via |vim.tbl_deep_extend()|.
|
||||
- Any other element is not processed.
|
||||
- Filter array with `opts.filter_cases`. Note that input case doesn't contain
|
||||
all hooks, as `*_once` hooks will be added after filtration.
|
||||
- Add `*_once` hooks to appropriate cases.
|
||||
|
||||
Parameters~
|
||||
{opts} `(table|nil)` Options controlling case collection. Possible fields:
|
||||
- <emulate_busted> - whether to emulate 'Olivine-Labs/busted' interface.
|
||||
It emulates these global functions: `describe`, `it`, `setup`, `teardown`,
|
||||
`before_each`, `after_each`. Use |MiniTest.skip()| instead of `pending()`
|
||||
and |MiniTest.finally()| instead of `finally`.
|
||||
- <find_files> - function which when called without arguments returns
|
||||
array with file paths. Each file should be a Lua file returning single
|
||||
test set or `nil`.
|
||||
- <filter_cases> - function which when called with single test case
|
||||
(see |MiniTest-test-case|) returns `false` if this case should be filtered
|
||||
out; `true` otherwise.
|
||||
|
||||
Return~
|
||||
`(table)` Array of test cases ready to be used by |MiniTest.execute()|.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.execute()*
|
||||
`MiniTest.execute`({cases}, {opts})
|
||||
Execute array of test cases
|
||||
|
||||
Overview of execution process:
|
||||
- Reset `all_cases` in |MiniTest.current| with `cases` input.
|
||||
- Call `reporter.start(cases)` (if present).
|
||||
- Execute each case in natural array order (aligned with their integer
|
||||
keys). Set `MiniTest.current.case` to currently executed case. Detailed
|
||||
test case execution is described in |MiniTest-test-case|. After any state
|
||||
change, call `reporter.update(case_num)` (if present), where `case_num` is an
|
||||
integer key of current test case.
|
||||
- Call `reporter.finish()` (if present).
|
||||
|
||||
Notes:
|
||||
- Execution is done in asynchronous fashion with scheduling. This allows
|
||||
making meaningful progress report during execution.
|
||||
- This function doesn't return anything. Instead, it updates `cases` in
|
||||
place with proper `exec` field. Use `all_cases` at |MiniTest.current| to
|
||||
look at execution result.
|
||||
|
||||
Parameters~
|
||||
{cases} `(table)` Array of test cases (see |MiniTest-test-case|).
|
||||
{opts} `(table|nil)` Options controlling case collection. Possible fields:
|
||||
- <reporter> - table with possible callable fields `start`, `update`,
|
||||
`finish`. Default: |MiniTest.gen_reporter.buffer()| in interactive
|
||||
usage and |MiniTest.gen_reporter.stdout()| in headless usage.
|
||||
- <stop_on_error> - whether to stop execution (see |MiniTest.stop()|)
|
||||
after first error. Default: `false`.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.stop()*
|
||||
`MiniTest.stop`({opts})
|
||||
Stop test execution
|
||||
|
||||
Parameters~
|
||||
{opts} `(table|nil)` Options with fields:
|
||||
- <close_all_child_neovim> - whether to close all child neovim processes
|
||||
created with |MiniTest.new_child_neovim()|. Default: `true`.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.is_executing()*
|
||||
`MiniTest.is_executing`()
|
||||
Check if tests are being executed
|
||||
|
||||
Return~
|
||||
`(boolean)`
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.expect*
|
||||
`MiniTest.expect`
|
||||
Table with expectation functions
|
||||
|
||||
Each function has the following behavior:
|
||||
- Silently returns `true` if expectation is fulfilled.
|
||||
- Throws an informative error with information helpful for debugging.
|
||||
|
||||
Mostly designed to be used within 'mini.test' framework.
|
||||
|
||||
Usage~
|
||||
>
|
||||
local x = 1 + 1
|
||||
MiniTest.expect.equality(x, 2) -- passes
|
||||
MiniTest.expect.equality(x, 1) -- fails
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.expect.equality()*
|
||||
`MiniTest.expect.equality`({left}, {right})
|
||||
Expect equality of two objects
|
||||
|
||||
Equality is tested via |vim.deep_equal()|.
|
||||
|
||||
Parameters~
|
||||
{left} `(any)` First object.
|
||||
{right} `(any)` Second object.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.expect.no_equality()*
|
||||
`MiniTest.expect.no_equality`({left}, {right})
|
||||
Expect no equality of two objects
|
||||
|
||||
Equality is tested via |vim.deep_equal()|.
|
||||
|
||||
Parameters~
|
||||
{left} `(any)` First object.
|
||||
{right} `(any)` Second object.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.expect.error()*
|
||||
`MiniTest.expect.error`({f}, {pattern}, {...})
|
||||
Expect function call to raise error
|
||||
|
||||
Parameters~
|
||||
{f} `(function)` Function to be tested for raising error.
|
||||
{pattern} `(string|nil)` Pattern which error message should match.
|
||||
Use `nil` or empty string to not test for pattern matching.
|
||||
{...} `(any)` Extra arguments with which `f` will be called.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.expect.no_error()*
|
||||
`MiniTest.expect.no_error`({f}, {...})
|
||||
Expect function call to not raise error
|
||||
|
||||
Parameters~
|
||||
{f} `(function)` Function to be tested for raising error.
|
||||
{...} `(any)` Extra arguments with which `f` will be called.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.expect.reference_screenshot()*
|
||||
`MiniTest.expect.reference_screenshot`({screenshot}, {path}, {opts})
|
||||
Expect equality to reference screenshot
|
||||
|
||||
Parameters~
|
||||
{screenshot} `(table|nil)` Array with screenshot information. Usually an output
|
||||
of `child.get_screenshot()` (see |MiniTest-child-neovim.get_screenshot()|).
|
||||
If `nil`, expectation passed.
|
||||
{path} `(string|nil)` Path to reference screenshot. If `nil`, constructed
|
||||
automatically in directory 'tests/screenshots' from current case info and
|
||||
total number of times it was called inside current case. If there is no
|
||||
file at `path`, it is created with content of `screenshot`.
|
||||
{opts} `(table|nil)` Options:
|
||||
- <force> - whether to forcefuly create reference screenshot.
|
||||
Temporary useful during test writing. Default: `false`.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.new_expectation()*
|
||||
`MiniTest.new_expectation`({subject}, {predicate}, {fail_context})
|
||||
Create new expectation function
|
||||
|
||||
Helper for writing custom functions with behavior similar to other methods
|
||||
of |MiniTest.expect|.
|
||||
|
||||
Parameters~
|
||||
{subject} `(string|function)` Subject of expectation. If function, called with
|
||||
expectation input arguments to produce string value.
|
||||
{predicate} `(function)` Predicate function. Called with expectation input
|
||||
arguments. Output `false` or `nil` means failed expectation.
|
||||
{fail_context} `(string|function)` Information about fail. If function, called
|
||||
with expectation input arguments to produce string value.
|
||||
|
||||
Return~
|
||||
`(function)` Expectation function.
|
||||
|
||||
Usage~
|
||||
>
|
||||
local expect_truthy = MiniTest.new_expectation(
|
||||
'truthy',
|
||||
function(x) return x end,
|
||||
function(x) return 'Object: ' .. vim.inspect(x) end
|
||||
)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.gen_reporter*
|
||||
`MiniTest.gen_reporter`
|
||||
Table with pre-configured report generators
|
||||
|
||||
Each element is a function which returns reporter - table with callable
|
||||
`start`, `update`, and `finish` fields.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.gen_reporter.buffer()*
|
||||
`MiniTest.gen_reporter.buffer`({opts})
|
||||
Generate buffer reporter
|
||||
|
||||
This is a default choice for interactive (not headless) usage. Opens a window
|
||||
with dedicated non-terminal buffer and updates it with throttled redraws.
|
||||
|
||||
Opened buffer has the following helpful Normal mode mappings:
|
||||
- `<Esc>` - stop test execution if executing (see |MiniTest.is_executing()|
|
||||
and |MiniTest.stop()|). Close window otherwise.
|
||||
- `q` - same as `<Esc>` for convenience and compatibility.
|
||||
|
||||
General idea:
|
||||
- Group cases by concatenating first `opts.group_depth` elements of case
|
||||
description (`desc` field). Groups by collected files if using default values.
|
||||
- In `start()` show some stats to know how much is scheduled to be executed.
|
||||
- In `update()` show symbolic overview of current group and state of current
|
||||
case. Each symbol represents one case and its state:
|
||||
- `?` - case didn't finish executing.
|
||||
- `o` - pass.
|
||||
- `O` - pass with notes.
|
||||
- `x` - fail.
|
||||
- `X` - fail with notes.
|
||||
- In `finish()` show all fails and notes ordered by case.
|
||||
|
||||
Parameters~
|
||||
{opts} `(table|nil)` Table with options. Used fields:
|
||||
- <group_depth> - number of first elements of case description (can be zero)
|
||||
used for grouping. Higher values mean higher granularity of output.
|
||||
Default: 1.
|
||||
- <throttle_delay> - minimum number of milliseconds to wait between
|
||||
redrawing. Reduces screen flickering but not amount of computations.
|
||||
Default: 10.
|
||||
- <window> - definition of window to open. Can take one of the forms:
|
||||
- Callable. It is called expecting output to be target window id
|
||||
(current window is used if output is `nil`). Use this to open in
|
||||
"normal" window (like `function() vim.cmd('vsplit') end`).
|
||||
- Table. Used as `config` argument in |nvim_open_win()|.
|
||||
Default: table for centered floating window.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.gen_reporter.stdout()*
|
||||
`MiniTest.gen_reporter.stdout`({opts})
|
||||
Generate stdout reporter
|
||||
|
||||
This is a default choice for headless usage. Writes to `stdout`. Uses
|
||||
coloring ANSI escape sequences to make pretty and informative output
|
||||
(should work in most modern terminals and continuous integration providers).
|
||||
|
||||
It has same general idea as |MiniTest.gen_reporter.buffer()| with slightly
|
||||
less output (it doesn't overwrite previous text) to overcome typical
|
||||
terminal limitations.
|
||||
|
||||
Parameters~
|
||||
{opts} `(table|nil)` Table with options. Used fields:
|
||||
- <group_depth> - number of first elements of case description (can be zero)
|
||||
used for grouping. Higher values mean higher granularity of output.
|
||||
Default: 1.
|
||||
- <quit_on_finish> - whether to quit after finishing test execution.
|
||||
Default: `true`.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest.new_child_neovim()*
|
||||
`MiniTest.new_child_neovim`()
|
||||
Create child Neovim process
|
||||
|
||||
This creates an object designed to be a fundamental piece of 'mini.test'
|
||||
methodology. It can start/stop/restart a separate (child) Neovim process in
|
||||
full (non-headless) mode together with convenience helpers to interact with
|
||||
it through |RPC| messages.
|
||||
|
||||
For more information see |MiniTest-child-neovim|.
|
||||
|
||||
Return~
|
||||
`child` Object of |MiniTest-child-neovim|.
|
||||
|
||||
Usage~
|
||||
>
|
||||
-- Initiate
|
||||
local child = MiniTest.new_child_neovim()
|
||||
child.start()
|
||||
|
||||
-- Use API functions
|
||||
child.api.nvim_buf_set_lines(0, 0, -1, true, { 'Line inside child Neovim' })
|
||||
|
||||
-- Execute Lua code, Vimscript commands, etc.
|
||||
child.lua('_G.n = 0')
|
||||
child.cmd('au CursorMoved * lua _G.n = _G.n + 1')
|
||||
child.type_keys('l')
|
||||
print(child.lua_get('_G.n')) -- Should be 1
|
||||
|
||||
-- Use other `vim.xxx` Lua wrappers (executed inside child process)
|
||||
vim.b.aaa = 'current process'
|
||||
child.b.aaa = 'child process'
|
||||
print(child.lua_get('vim.b.aaa')) -- Should be 'child process'
|
||||
|
||||
-- Always stop process after it is not needed
|
||||
child.stop()
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest-child-neovim*
|
||||
Child class
|
||||
|
||||
It offers a great set of tools to write reliable and reproducible tests by
|
||||
allowing to use fresh process in any test action. Interaction with it is done
|
||||
through |RPC| protocol.
|
||||
|
||||
Although quite flexible, at the moment it has certain limitations:
|
||||
- Doesn't allow using functions or userdata for child's both inputs and
|
||||
outputs. Usual solution is to move computations from current Neovim process
|
||||
to child process. Use `child.lua()` and `child.lua_get()` for that.
|
||||
- When writing tests, it is common to end up with "hanging" process: it
|
||||
stops executing without any output. Most of the time it is because Neovim
|
||||
process is "blocked", i.e. it waits for user input and won't return from
|
||||
other call (like `child.api.nvim_exec_lua()`). Common causes are active
|
||||
|hit-enter-prompt| (increase prompt height to a bigger value) or
|
||||
Operator-pending mode (exit it). To mitigate this experience, most helpers
|
||||
will throw an error if its immediate execution will lead to hanging state.
|
||||
Also in case of hanging state try `child.api_notify` instead of `child.api`.
|
||||
|
||||
Notes:
|
||||
- An important type of field is a "redirection table". It acts as a
|
||||
convenience wrapper for corresponding `vim.*` table. Can be used both to
|
||||
return and set values. Examples:
|
||||
- `child.api.nvim_buf_line_count(0)` will execute
|
||||
`vim.api.nvim_buf_line_count(0)` inside child process and return its
|
||||
output to current process.
|
||||
- `child.bo.filetype = 'lua'` will execute `vim.bo.filetype = 'lua'`
|
||||
inside child process.
|
||||
They still have same limitations listed above, so are not perfect. In
|
||||
case of a doubt, use `child.lua()`.
|
||||
- Almost all methods use |vim.rpcrequest()| (i.e. wait for call to finish and
|
||||
then return value). See for `*_notify` variant to use |vim.rpcnotify()|.
|
||||
- All fields and methods should be called with `.`, not `:`.
|
||||
|
||||
Class~
|
||||
{child}
|
||||
|
||||
Fields~
|
||||
{start} `(function)` Start child process. See |MiniTest-child-neovim.start()|.
|
||||
{stop} `(function)` Stop current child process.
|
||||
{restart} `(function)` Restart child process: stop if running and then
|
||||
start a new one. Takes same arguments as `child.start()` but uses values
|
||||
from most recent `start()` call as defaults.
|
||||
|
||||
{type_keys} `(function)` Emulate typing keys.
|
||||
See |MiniTest-child-neovim.type_keys()|. Doesn't check for blocked state.
|
||||
|
||||
{cmd} `(function)` Execute Vimscript code from a string.
|
||||
A wrapper for |nvim_exec()| without capturing output.
|
||||
{cmd_capture} `(function)` Execute Vimscript code from a string and
|
||||
capture output. A wrapper for |nvim_exec()| with capturing output.
|
||||
|
||||
{lua} `(function)` Execute Lua code. A wrapper for |nvim_exec_lua()|.
|
||||
{lua_get} `(function)` Execute Lua code and return result. A wrapper
|
||||
for |nvim_exec_lua()| but prepends string code with `return`.
|
||||
|
||||
{is_blocked} `(function)` Check whether child process is blocked.
|
||||
{is_running} `(function)` Check whether child process is currently running.
|
||||
|
||||
{ensure_normal_mode} `(function)` Ensure normal mode.
|
||||
{get_screenshot} `(function)` Returns table with two "2d arrays" of single
|
||||
characters representing what is displayed on screen and how it looks.
|
||||
Note: works only in Neovim>=0.6. See |MiniTest-child-neovim.get_screenshot()|.
|
||||
|
||||
{job} `(table|nil)` Information about current job. If `nil`, child is not running.
|
||||
|
||||
{api} `(table)` Redirection table for `vim.api`. Doesn't check for blocked state.
|
||||
{api_notify} `(table)` Same as `api`, but uses |vim.rpcnotify()|.
|
||||
|
||||
{diagnostic} `(table)` Redirection table for |vim.diagnostic|.
|
||||
{fn} `(table)` Redirection table for |vim.fn|.
|
||||
{highlight} `(table)` Redirection table for `vim.highlight` (|lua-highlight)|.
|
||||
{json} `(table)` Redirection table for `vim.json`.
|
||||
{loop} `(table)` Redirection table for |vim.loop|.
|
||||
{lsp} `(table)` Redirection table for `vim.lsp` (|lsp-core)|.
|
||||
{mpack} `(table)` Redirection table for |vim.mpack|.
|
||||
{spell} `(table)` Redirection table for |vim.spell|.
|
||||
{treesitter} `(table)` Redirection table for |vim.treesitter|.
|
||||
{ui} `(table)` Redirection table for `vim.ui` (|lua-ui|). Currently of no
|
||||
use because it requires sending function through RPC, which is impossible
|
||||
at the moment.
|
||||
|
||||
{g} `(table)` Redirection table for |vim.g|.
|
||||
{b} `(table)` Redirection table for |vim.b|.
|
||||
{w} `(table)` Redirection table for |vim.w|.
|
||||
{t} `(table)` Redirection table for |vim.t|.
|
||||
{v} `(table)` Redirection table for |vim.v|.
|
||||
{env} `(table)` Redirection table for |vim.env|.
|
||||
|
||||
{o} `(table)` Redirection table for |vim.o|.
|
||||
{go} `(table)` Redirection table for |vim.go|.
|
||||
{bo} `(table)` Redirection table for |vim.bo|.
|
||||
{wo} `(table)` Redirection table for |vim.wo|.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest-child-neovim.start()*
|
||||
child.start(args, opts)~
|
||||
|
||||
Start child process and connect to it. Won't work if child is already running.
|
||||
|
||||
Parameters~
|
||||
{args} `(table)` Array with arguments for executable. Will be prepended
|
||||
with `{'--clean', '-n', '--listen', <some address>}` (see |startup-options|).
|
||||
{opts} `(table)` Options:
|
||||
- <nvim_executable> - name of Neovim executable. Default: |v:progpath|.
|
||||
- <connection_timeout> - stop trying to connect after this amount of
|
||||
milliseconds. Default: 5000.
|
||||
|
||||
Usage~
|
||||
>
|
||||
child = MiniTest.new_child_neovim()
|
||||
|
||||
-- Start default clean Neovim instance
|
||||
child.start()
|
||||
|
||||
-- Start with custom 'init.lua' file
|
||||
child.start({ '-u', 'scripts/minimal_init.lua' })
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest-child-neovim.type_keys()*
|
||||
child.type_keys(wait, ...)~
|
||||
|
||||
Basically a wrapper for |nvim_input()| applied inside child process.
|
||||
Differences:
|
||||
- Can wait after each group of characters.
|
||||
- Raises error if typing keys resulted into error in child process (i.e. its
|
||||
|v:errmsg| was updated).
|
||||
- Key '<' as separate entry may not be escaped as '<LT>'.
|
||||
|
||||
Parameters~
|
||||
{wait} `(number)` Number of milliseconds to wait after each entry. May be
|
||||
omitted, in which case no waiting is done.
|
||||
{...} `(string|table<number,string>)` Separate entries for |nvim_input()|,
|
||||
after which `wait` will be applied. Can be either string or array of strings.
|
||||
|
||||
Usage~
|
||||
>
|
||||
-- All of these type keys 'c', 'a', 'w'
|
||||
child.type_keys('caw')
|
||||
child.type_keys('c', 'a', 'w')
|
||||
child.type_keys('c', { 'a', 'w' })
|
||||
|
||||
-- Waits 5 ms after `c` and after 'w'
|
||||
child.type_keys(5, 'c', { 'a', 'w' })
|
||||
|
||||
-- Special keys can also be used
|
||||
child.type_keys('i', 'Hello world', '<Esc>')
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTest-child-neovim.get_screenshot()*
|
||||
child.get_screenshot()~
|
||||
|
||||
Compute what is displayed on (default TUI) screen and how it is displayed.
|
||||
This basically calls |screenstring()| and |screenattr()| for every visible
|
||||
cell (row from 1 to 'lines', column from 1 to 'columns').
|
||||
|
||||
Notes:
|
||||
- This requires Neovim>=0.6 as `screenstring()` was introduced only in 0.6.
|
||||
- Due to implementation details of `screenstring()` and `screenattr()` in
|
||||
Neovim<=0.7, this function won't recognize floating windows displayed on
|
||||
screen. It will throw an error if there is a visible floating window. Use
|
||||
Neovim>=0.8 (current nightly) to properly handle floating windows. Details:
|
||||
- https://github.com/neovim/neovim/issues/19013
|
||||
- https://github.com/neovim/neovim/pull/19020
|
||||
- To make output more portable and visually useful, outputs of
|
||||
`screenattr()` are coded with single character symbols. Those are taken from
|
||||
94 characters (ASCII codes between 33 and 126), so there will be duplicates
|
||||
in case of more than 94 different ways text is displayed on screen.
|
||||
|
||||
Return~
|
||||
`(table|nil)` Screenshot table with the following fields:
|
||||
- <text> - "2d array" (row-column) of single characters displayed at
|
||||
particular cells.
|
||||
- <attr> - "2d array" (row-column) of symbols representing how text is
|
||||
displayed (basically, "coded" appearance/highlighting). They should be
|
||||
used only in relation to each other: same/different symbols for two
|
||||
cells mean same/different visual appearance. Note: there will be false
|
||||
positives if there are more than 94 different attribute values.
|
||||
It also can be used with `tostring()` to convert to single string (used
|
||||
for writing to reference file). It results into two visual parts
|
||||
(separated by empty line), for `text` and `attr`. Each part has "ruler"
|
||||
above content and line numbers for each line.
|
||||
Returns `nil` if couldn't get a reasonable screenshot.
|
||||
|
||||
Usage~
|
||||
>
|
||||
local screenshot = child.get_screenshot()
|
||||
|
||||
-- Show character displayed row=3 and column=4
|
||||
print(screenshot.text[3][4])
|
||||
|
||||
-- Convert to string
|
||||
tostring(screenshot)
|
||||
|
||||
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
|
@ -0,0 +1,95 @@
|
|||
==============================================================================
|
||||
------------------------------------------------------------------------------
|
||||
*mini.trailspace*
|
||||
*MiniTrailspace*
|
||||
Work with trailing whitespace
|
||||
|
||||
Features:
|
||||
- Highlighting is done only in modifiable buffer by default, only in Normal
|
||||
mode, and stops in Insert mode and when leaving window.
|
||||
- Trim all trailing whitespace with |MiniTrailspace.trim()|.
|
||||
- Trim all trailing empty lines with |MiniTrailspace.trim_last_lines()|.
|
||||
|
||||
# Setup~
|
||||
|
||||
This module needs a setup with `require('mini.trailspace').setup({})`
|
||||
(replace `{}` with your `config` table). It will create global Lua table
|
||||
`MiniTrailspace` which you can use for scripting or manually (with
|
||||
`:lua MiniTrailspace.*`).
|
||||
|
||||
See |MiniTrailspace.config| for `config` structure and default values.
|
||||
|
||||
You can override runtime config settings locally to buffer inside
|
||||
`vim.b.minitrailspace_config` which should have same structure as
|
||||
`MiniTrailspace.config`. See |mini.nvim-buffer-local-config| for more details.
|
||||
|
||||
# Highlight groups~
|
||||
|
||||
* `MiniTrailspace` - highlight group for trailing space.
|
||||
|
||||
To change any highlight group, modify it directly with |:highlight|.
|
||||
|
||||
# Disabling~
|
||||
|
||||
To disable, set `g:minitrailspace_disable` (globally) or
|
||||
`b:minitrailspace_disable` (for a buffer) to `v:true`. Considering high
|
||||
number of different scenarios and customization intentions, writing exact
|
||||
rules for disabling module's functionality is left to user. See
|
||||
|mini.nvim-disabling-recipes| for common recipes. Note: after disabling
|
||||
there might be highlighting left; it will be removed after next
|
||||
highlighting update (see |events| and `MiniTrailspace` |augroup|).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTrailspace.setup()*
|
||||
`MiniTrailspace.setup`({config})
|
||||
Module setup
|
||||
|
||||
Parameters~
|
||||
{config} `(table)` Module config table. See |MiniTrailspace.config|.
|
||||
|
||||
Usage~
|
||||
`require('mini.trailspace').setup({})` (replace `{}` with your `config` table)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTrailspace.config*
|
||||
`MiniTrailspace.config`
|
||||
Module config
|
||||
|
||||
Default values:
|
||||
>
|
||||
MiniTrailspace.config = {
|
||||
-- Highlight only in normal buffers (ones with empty 'buftype'). This is
|
||||
-- useful to not show trailing whitespace where it usually doesn't matter.
|
||||
only_in_normal_buffers = true,
|
||||
}
|
||||
<
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTrailspace.highlight()*
|
||||
`MiniTrailspace.highlight`()
|
||||
Highlight trailing whitespace in current window
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTrailspace.unhighlight()*
|
||||
`MiniTrailspace.unhighlight`()
|
||||
Unhighlight trailing whitespace in current window
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTrailspace.trim()*
|
||||
`MiniTrailspace.trim`()
|
||||
Trim trailing whitespace
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTrailspace.trim_last_lines()*
|
||||
`MiniTrailspace.trim_last_lines`()
|
||||
Trim last blank lines
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
*MiniTrailspace.track_normal_buffer()*
|
||||
`MiniTrailspace.track_normal_buffer`()
|
||||
Track normal buffer
|
||||
|
||||
Designed to be used with |autocmd|. No need to use it directly.
|
||||
|
||||
|
||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue