diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dda07746d..207bdeb50 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,8 +3,8 @@ name: ci on: [push, pull_request] jobs: - cpoptions_checker: - name: Check cpoptions + vimscript: + name: VimScript runs-on: ubuntu-latest strategy: fail-fast: false @@ -14,16 +14,6 @@ jobs: uses: actions/checkout@v2 - name: Ensure each vim source file declares cpoptions run: test/autoload_should_check_cpo.sh - - vint: - name: Vint - runs-on: ubuntu-latest - strategy: - fail-fast: false - - steps: - - name: Checkout - uses: actions/checkout@v2 - name: Run vint with reviewdog uses: reviewdog/action-vint@v1 with: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 000000000..b729f0180 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,24 @@ +name: Docs + +on: + push: + branches: [ master ] + +jobs: + build: + name: Build, Test and Deploy + runs-on: ubuntu-latest + permissions: + contents: write # To push a branch + pull-requests: write # To create a PR from that branch + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + - run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4" mdbook) + - run: mdbook build docs && mdbook test docs + - uses: JamesIves/github-pages-deploy-action@4.1.7 + with: + branch: gh-pages + folder: docs/book diff --git a/Cargo.lock b/Cargo.lock index 4db45ddc8..7ce945f42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -177,7 +177,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" dependencies = [ "memchr", - "regex-automata", + "regex-automata 0.3.8", "serde", ] @@ -1686,14 +1686,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.5" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick 1.0.4", "memchr", - "regex-automata", - "regex-syntax 0.7.5", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", ] [[package]] @@ -1701,10 +1701,16 @@ name = "regex-automata" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick 1.0.4", "memchr", - "regex-syntax 0.7.5", + "regex-syntax 0.8.2", ] [[package]] @@ -1719,6 +1725,12 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + [[package]] name = "reqwest" version = "0.11.19" diff --git a/README.md b/README.md index 705ad922b..0387d8f2a 100644 --- a/README.md +++ b/README.md @@ -4,372 +4,22 @@ [![CI](https://github.com/liuchengxu/vim-clap/workflows/ci/badge.svg)](https://github.com/liuchengxu/vim-clap/actions?workflow=ci) [![Gitter][g1]][g2] -[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://paypal.me/liuchengxu) [g1]: https://badges.gitter.im/liuchengxu/vim-clap.svg [g2]: https://gitter.im/liuchengxu/vim-clap?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge -Vim-clap is a modern generic performant finder using the `floating_win` of neovim or `popup` of vim, powered by an external backend written in Rust. +Vim-clap stands as a comprehensive and efficient solution, providing powerful fuzzy pickers and replacements for various established Vim plugins, designed to support both Vim and NeoVim.

-[>>>> More screenshots](https://github.com/liuchengxu/vim-clap/issues/1) - -## Table of Contents - - - -* [Features](#features) -* [Caveats](#caveats) -* [Requirement](#requirement) -* [Installation](#installation) - * [vim-plug](#vim-plug) -* [Usage](#usage) - * [Commands](#commands) - * [Providers](#providers) - * [Global variables](#global-variables) - * [Keybindings](#keybindings) - * [Insert mode](#insert-mode) - * [NeoVim only](#neovim-only) - * [Normal mode](#normal-mode) - * [Cmdline mode](#cmdline-mode) - * [Execute some code during the process](#execute-some-code-during-the-process) - * [Change highlights](#change-highlights) - * [Search syntax](#search-syntax) - * [Fzf search syntax](#fzf-search-syntax) - * [Extended search syntax](#extended-search-syntax) - * [Config file](#config-file) -* [How to define your own provider](#how-to-define-your-own-provider) -* [Disable auto-completion plugin in clap input window](#disable-auto-completion-plugin-in-clap-input-window) -* [Contribution](#contribution) -* [Credit](#credit) -* [License](#license) - - - -## Features - -- [x] ~~Pure vimscript~~. - - Pin to some early version of vim-clap if you prefer the pure vimscript plugin. -- [x] ~~Work out of the box, without any extra dependency~~. - - The Rust binary is now a must-have to make everything work smoothly. -- [x] Blazingly fast thanks to the powerful Rust backend. -- [x] Consistent command interface with [clap-rs/clap](https://github.com/clap-rs/clap) -- [x] Support writing new providers in both Vimscript and Rust. -- [x] Support [the search syntax borrowed from fzf](https://github.com/junegunn/fzf#search-syntax) and more. -- [x] Flexible UI layout. -- [ ] Support searching by multiple providers simultaneously. - -## Caveats - -- Vim-clap is in a very early stage, breaking changes and bugs are expected. - -- The Windows support is not fully tested. The providers without using any system related command should work smoothly, that is to say, most sync providers are just able to work. Please [create an issue](https://github.com/liuchengxu/vim-clap/issues/new?assignees=&labels=&template=bug_report.md&title=) if you run into any error in Windows. And any help would be appreciated. - -- Although a lot of effort has been made to unify the behavior of vim-clap between vim and neovim, and most part works in the same way, it just can't be exactly the same, for `floating_win` and `popup` are actually two different things anyway. - -## Requirement - -- Vim: `:echo has('patch-8.1.2114')`. -- NeoVim: `:echo has('nvim-0.4.2')`. - -## Installation - -### [vim-plug](https://github.com/junegunn/vim-plug) - -```vim -" Build the Rust binary if `cargo` exists on your system. -Plug 'liuchengxu/vim-clap', { 'do': ':Clap install-binary' } - -" The bang version will try to download the prebuilt binary if `cargo` does not exist. -Plug 'liuchengxu/vim-clap', { 'do': ':Clap install-binary!' } - -" `:Clap install-binary[!]` will always try to compile the binary locally. -" If you do care about the disk used for the compilation, use the way of force download, -" which will directly download the prebuilt binary even if `cargo` is available. -Plug 'liuchengxu/vim-clap', { 'do': { -> clap#installer#force_download() } } - -" `:Clap install-binary[!]` will run using the terminal feature which is inherently async. -" If you don't want that and hope to run the hook synchorously: -Plug 'liuchengxu/vim-clap', { 'do': has('win32') ? 'cargo build --release' : 'make' } -``` - -It's recommended to install the Rust binary automatically via the `do` hook. If that does not work for you, please refer to [INSTALL.md](INSTALL.md) for installing it manually. - -## Usage - -Vim-clap is utterly easy to use, just type, press Ctrl-J/K to locate the wanted entry, and press Enter to apply and exit. The default settings should work well for most people in most cases, but it's absolutely hackable too. - -### Commands - -The paradigm is `Clap [provider_id_or_alias] {provider_args}`, where the `provider_id_or_alias` is obviously either the name or alias of provider. Technically the `provider_id` can be anything that can be used a key of a Dict, but I recommend you using an _identifier_ like name as the provider id, and use the alias rule if you prefer a special name. - -#### Providers - -| Command | List | Requirement | -| :------------------------------------- | :----------------------------------------------------- | :---------------------------------------------------------------------- | -| `Clap blines` | Lines in the current buffer | _none_ | -| `Clap buffers` | Open buffers | _none_ | -| `Clap colors` | Colorschemes | _none_ | -| `Clap command` | Command | _none_ | -| `Clap hist:` or `Clap command_history` | Command history | _none_ | -| `Clap hist/` or `Clap search_history` | Search history | _none_ | -| `Clap filetypes` | File types | _none_ | -| `Clap help_tags` | Help tags | _none_ | -| `Clap jumps` | Jumps | _none_ | -| `Clap lines` | Lines in the loaded buffers | _none_ | -| `Clap marks` | Marks | _none_ | -| `Clap maps` | Maps | _none_ | -| `Clap quickfix` | Entries of the quickfix list | _none_ | -| `Clap loclist` | Entries of the location list | _none_ | -| `Clap registers` | Registers | _none_ | -| `Clap yanks` | Yank stack of the current vim session | _none_ | -| `Clap history` | Open buffers and `v:oldfiles` | _none_ | -| `Clap windows` | Windows | _none_ | -| `Clap providers` | List the vim-clap providers | _none_ | -| `Clap bcommits` | Git commits for the current buffer | **[git][git]** | -| `Clap commits` | Git commits | **[git][git]** | -| `Clap gfiles` or `Clap git_files` | Files managed by git | **[git][git]** | -| `Clap git_diff_files` | Files managed by git and having uncommitted changes | **[git][git]** | -| `Clap live_grep`**+** | Grep using word-regexp matcher | **[rg][rg]** | -| `Clap dumb_jump` | Definitions/References using regexp with grep fallback | **[rg][rg]** with `--pcre2` | -| `Clap files` | Files | **[maple][maple]** | -| `Clap filer` | Ivy-like file explorer | **[maple][maple]** | -| `Clap grep`**+** | Grep using fuzzy matcher | **[maple][maple]** | -| `Clap igrep` | A combo of `filer` and `grep` | **[maple][maple]** | -| `Clap tags` | Tags in the current buffer | **[maple][maple]** | -| `Clap tagfiles` | Search existing `tagfiles` | **[maple][maple]** | -| `Clap proj_tags` | Tags in the current project | **[maple][maple]** and **[universal-ctags][universal-ctags]** (`+json`) | -| `Clap recent_files` | Persistent ordered history of recent files | **[maple][maple]** | - -[rg]: https://github.com/BurntSushi/ripgrep -[git]: https://github.com/git/git -[maple]: https://github.com/liuchengxu/vim-clap/blob/master/INSTALL.md#maple-binary -[universal-ctags]: https://github.com/universal-ctags/ctags - -- The command with a superscript `!` means that it is not yet implemented or not tested. -- The command with a superscript `+` means that it supports multi-selection via Tab. -- `:Clap grep` - - Use `:Clap grep --query=` to grep the word under cursor. - - Use `:Clap grep --query=@visual` to grep the visual selection. - - `cwd` will be searched by default, specify the extra paths in the end to search multiple directories. - - `:Clap grep --path ~/.vim/plugged/ale` with `cwd` is `~/.vim/plugged/vim-clap` will both search vim-clap and ale. - -[Send a pull request](https://github.com/liuchengxu/vim-clap/pulls) if you want to get your provider listed here. - -### Global variables - -- `g:clap_layout`: Dict, `{ 'width': '67%', 'height': '33%', 'row': '33%', 'col': '17%' }` by default. This variable controls the size and position of vim-clap window. By default, the vim-clap window is placed relative to the currently active window. To make it relative to the whole editor modify this variable as shown below: - - ```vim - let g:clap_layout = { 'relative': 'editor' } - ``` - -- `g:clap_open_action`: Dict, `{ 'ctrl-t': 'tab split', 'ctrl-x': 'split', 'ctrl-v': 'vsplit' }`, extra key bindings for opening the selected file in a different way. NOTE: do not define a key binding which is conflicted with the other default bindings of vim-clap, and only `ctrl-*` is supported for now. - -- `g:clap_provider_alias`: Dict, if you don't want to invoke some clap provider by its id(name), as it's too long or somehow, you can add an alias for that provider. - - ```vim - " The provider name is `command_history`, with the following alias config, - " now you can call it via both `:Clap command_history` and `:Clap hist:`. - let g:clap_provider_alias = {'hist:': 'command_history'} - ``` - -- `g:clap_selected_sign`: Dict, `{ 'text': ' >', 'texthl': "ClapSelectedSign", "linehl": "ClapSelected"}`. - -- `g:clap_current_selection_sign`: Dict, `{ 'text': '>>', 'texthl': "ClapCurrentSelectionSign", "linehl": "ClapCurrentSelection"}`. - -- `g:clap_no_matches_msg`: String, `'NO MATCHES FOUND'`, message to show when there is no matches found. - -- `g:clap_popup_input_delay`: Number, `200ms` by default, delay for actually responsing to the input, vim only. - -- `g:clap_disable_run_rooter`: Bool, `v:false`, vim-clap by default will try to run from the project root by changing `cwd` temporarily. Set it to `v:true` to run from the origin `cwd`. The project root here means the git base directory. Create an issue if you want to see more support about the project root. - -The option naming convention for provider is `g:clap_provider_{provider_id}_{opt}`. - -- `g:clap_provider_grep_blink`: [2, 100] by default, blink 2 times with 100ms timeout when jumping the result. Set it to [0, 0] to disable the blink. - -- `g:clap_provider_grep_opts`: An empty string by default, allows you to enable flags such as `'--hidden -g "!.git/"'`. - -See `:help clap-options` for more information. - -### Keybindings - -#### Insert mode - -- [x] Use Ctrl-j/Down or Ctrl-k/Up to navigate the result list up and down linewise. -- [x] Use PageDown/PageUp to scroll the result list down and up. -- [x] Use Ctrl-a/Home to go to the start of the input. -- [x] Use Ctrl-e/End to go to the end of the input. -- [x] Use Ctrl-c, Ctrl-g, Ctrl-[ or Esc(vim) to exit. -- [x] Use Ctrl-h/BS to delete previous character. -- [x] Use Ctrl-d to delete next character. -- [x] Use Ctrl-b to move cursor left one character. -- [x] Use Ctrl-f to move cursor right one character. -- [x] Use Ctrl-n for next input in the history. -- [x] Use Ctrl-p for previous input in the history. -- [x] Use Enter to select the entry and exit. - - Use Enter to expand the directory or edit the file for `:Clap filer`. -- [x] By default Alt-u does nothing. - - Use Alt-u to go up one directory in `:Clap filer`. -- [x] Use Tab to select multiple entries and open them using the quickfix window.(Need the provider has `sink*` support) - - Use Tab to expand the directory for `:Clap filer`. -- [x] Use Ctrl-t or Ctrl-x, Ctrl-v to open the selected entry in a new tab or a new split. -- [x] Use Ctrl-u to clear inputs. -- [x] Use Ctrl-l to launch the whole provider list panel for invoking another provider at any time. -- [x] Use Shift-Tab to invoke the action dialog(vim only). -- [x] Use Shift-up and Shift-down to scroll the preview. - -#### NeoVim only - -##### Normal mode - -- [x] Use j/Down or k/Up to navigate the result list up and down linewise. -- [x] By default Alt-u does nothing. - - Use Alt-u to go up one directory in `:Clap filer`. -- [x] Use Ctrl-c, Ctrl-g or Esc to exit. -- [x] Use Ctrl-d/Ctrl-u/PageDown/PageUp to scroll the result list down and up. -- [x] Use Ctrl-l to launch the whole provider list panel for invoking another provider at any time. -- [x] Use Ctrl-n for next input in the history. -- [x] Use Ctrl-p for previous input in the history. -- [x] Use Shift-up and Shift-down to scroll the preview. -- [x] Use gg and G to scroll to the first and last item. -- [x] Use Enter to select the entry and exit. -- [x] Use Shift-Tab to invoke the action dialog. -- [x] Actions defined by `g:clap_open_action`. - -##### Cmdline mode - -- [x] Use `:q` to exit. - -See `:help clap-keybindings` for more information. Note that the [keybindings are not consistent](https://github.com/liuchengxu/vim-clap/issues/864) due to discrepancies between Vim/Neovim and different providers. - -### Execute some code during the process - -```vim -augroup YourGroup - autocmd! - autocmd User ClapOnEnter call YourFunction() - autocmd User ClapOnExit call YourFunction() -augroup END -``` - -### Change highlights - -By default vim-clap will use the colors extracted from your colorscheme, which is not guaranteed to suitable for all the colorschemes. Then you can try the built-in `material_design_dark` theme then: - -```vim -let g:clap_theme = 'material_design_dark' -``` - -![clap-highlights](https://user-images.githubusercontent.com/8850248/74818883-6cfdc380-533a-11ea-81fb-d09d90498c96.png) - -You could also set `g:clap_theme` to be a `Dict` to specify the palette: - -```vim -" Change the CamelCase of related highlight group name to under_score_case. -let g:clap_theme = { 'search_text': {'guifg': 'red', 'ctermfg': 'red'} } -``` - -`ClapDisplay` and `ClapPreview` are the most basic highlight groups for the display and preview window, which can be overrided if the provider has its own syntax highlight, then checkout the related [syntax](syntax) file for more granular highlights directly. - -If you want to write your own clap theme, take [autoload/clap/themes/material_design_dark.vim](autoload/clap/themes/material_design_dark.vim) as a reference. - -See `:help clap-highlights` for more information. - -### Search syntax - -#### Fzf search syntax - -vim-clap adopts the almost all fzf search syntax, please refer to [the search syntax section of fzf's README](https://github.com/junegunn/fzf#search-syntax) for more details. Note that the OR operator defined by a single bar character is not yet implemented, but you can achieve that by using multiple exact matches. - -#### Extended search syntax - -Apart from the basic fzf search syntax, more search syntax are supported: - -| Token | Match type | Description | -| ------ | ---------- | ------------------------------------------------------------ | -| `"cli` | word-match | Items that match word `cli` (`clippy` does not match `"cli`) | - -### Config file - -User config file is loaded from: - -- Linux: `~/.config/vimclap/config.toml` -- macOS: `~/Library/Application\ Support/org.vim.Vim-Clap/config.toml` -- Windows: `C:\Users\Alice\AppData\Roaming\Vim\Vim Clap\config\config.toml` - -```toml -[log] -# Note that the log file path must be an absolute path. -log-file = "/tmp/clap.log" -max-level = "debug" - -[matcher] -# There are four sort keys for results: score, begin, end, length, -# you can specify how the records are sorted using `tiebreak`. -tiebreak = "score,-begin,-end,-length" -``` - - - - - - - - - - - -## How to define your own provider - -```vim -" `:Clap quick_open` to open some dotfiles quickly. -" `description` is actually optional, but if you want to show this provider -" when you call `:Clap`, the `description` is neccessary. -let g:clap_provider_quick_open = { - \ 'source': ['~/.vimrc', '~/.spacevim', '~/.bashrc', '~/.tmux.conf'], - \ 'sink': 'e', - \ 'description': 'Quick open some dotfiles', - \ } -``` - -Find more examples at [wiki/Examples](https://github.com/liuchengxu/vim-clap/wiki/Examples). - -For complete guide about writing a clap provider please see [PROVIDER.md](PROVIDER.md). - -## Disable auto-completion plugin in clap input window - -Some of the auto-completion engines need to turn off to prevent bizarre behaviors(#580) - -For nvim-completion, add autocmd to your init.vim: - -```vim -autocmd FileType clap_input let g:completion_enable_auto_pop = 0 -``` - -For nvim-compe: - -```vim -autocmd FileType clap_input call compe#setup({ 'enabled': v:false }, 0) -``` +[User facing documentation and guide](https://liuchengxu.github.io/vim-clap) ## Contribution Vim-clap is still in beta. Any kinds of contributions are highly welcome. -If you would like to see support for more providers or share your own provider, please [create an issue](https://github.com/liuchengxu/vim-clap/issues) or [create a pull request](https://github.com/liuchengxu/vim-clap/pulls). - -If you'd liked to discuss the project more directly, check out [![][g1]][g2]. - -## Credit - -- Vim-clap is initially enlightened by [snails](https://github.com/manateelazycat/snails). -- Some providers' idea and code are borrowed from [fzf.vim](https://github.com/junegunn/fzf.vim). -- The built-in fzy python implementation is based on [sweep.py](https://github.com/aslpavel/sweep.py). - ## [License](LICENSE) MIT diff --git a/autoload/clap/floating_win.vim b/autoload/clap/floating_win.vim index e4e89df50..598a39904 100644 --- a/autoload/clap/floating_win.vim +++ b/autoload/clap/floating_win.vim @@ -471,7 +471,7 @@ function! clap#floating_win#preview.show(lines) abort let max_size = s:max_preview_size() if max_size <= 0 - call g:clap#floating_win#preview.close() + call self.close() return endif let lines = a:lines[:max_size] @@ -482,8 +482,7 @@ function! clap#floating_win#preview.show(lines) abort if clap#preview#direction() !=# 'LR' let opts = nvim_win_get_config(s:preview_winid) if opts.height != height - let opts.height = height - call nvim_win_set_config(s:preview_winid, opts) + call nvim_win_set_height(s:preview_winid, height) endif endif endif @@ -499,7 +498,7 @@ endfunction function! clap#floating_win#preview.hide() abort if !clap#preview#is_always_open() - call g:clap#floating_win#preview.close() + call self.close() endif endfunction diff --git a/autoload/clap/plugin/highlight_cursor_word.vim b/autoload/clap/plugin/cursorword.vim similarity index 52% rename from autoload/clap/plugin/highlight_cursor_word.vim rename to autoload/clap/plugin/cursorword.vim index c9cb61203..321e846f8 100644 --- a/autoload/clap/plugin/highlight_cursor_word.vim +++ b/autoload/clap/plugin/cursorword.vim @@ -1,23 +1,24 @@ " Author: liuchengxu +" Highlight the cursor word and the occurrences let s:save_cpo = &cpoptions set cpoptions&vim hi ClapUnderline gui=underline cterm=underline -hi default link ClapCurrentWord IncSearch -hi default link ClapCurrentWordTwins ClapUnderline +hi default link ClapCursorWord IncSearch +hi default link ClapCursorWordTwins ClapUnderline -function! clap#plugin#highlight_cursor_word#add_highlights(word_highlights) abort +function! clap#plugin#cursorword#add_highlights(word_highlights) abort let cword_len = a:word_highlights.cword_len let match_ids = [] let [lnum, col] = a:word_highlights.cword_highlight - let match_id = matchaddpos('ClapCurrentWord', [[lnum, col+1, cword_len]]) + let match_id = matchaddpos('ClapCursorWord', [[lnum, col+1, cword_len]]) if match_id > -1 call add(match_ids, match_id) endif - for [lnum, col] in a:word_highlights.other_words_highlight - let match_id = matchaddpos('ClapCurrentWordTwins', [[lnum, col+1, cword_len]]) + for [lnum, col] in a:word_highlights.twins_words_highlight + let match_id = matchaddpos('ClapCursorWordTwins', [[lnum, col+1, cword_len]]) if match_id > -1 call add(match_ids, match_id) endif diff --git a/autoload/clap/plugin/linter.vim b/autoload/clap/plugin/linter.vim index 34c885cab..1ce0605e2 100644 --- a/autoload/clap/plugin/linter.vim +++ b/autoload/clap/plugin/linter.vim @@ -90,7 +90,7 @@ function! s:render_on_top_right(lines, line_highlights) abort " Make sure the diagnostic win won't interfere with the existing code " display. let max_available_on_top = s:max_available_length_on_top() - if width > max_available_on_top + if max_available_on_top > 0 && width > max_available_on_top let width = max_available_on_top let height += 1 endif diff --git a/autoload/clap/state.vim b/autoload/clap/state.vim index 929443ed6..7a73e8c4d 100644 --- a/autoload/clap/state.vim +++ b/autoload/clap/state.vim @@ -134,7 +134,6 @@ function! clap#state#render_preview(preview) abort endif if has_key(a:preview, 'scrollbar') - let g:scrollbar = copy(a:preview.scrollbar) let [top_position, length] = a:preview.scrollbar call clap#floating_win#show_preview_scrollbar(top_position, length) endif diff --git a/crates/linter/src/lib.rs b/crates/linter/src/lib.rs index 3c6f44f7b..7f9af4155 100644 --- a/crates/linter/src/lib.rs +++ b/crates/linter/src/lib.rs @@ -128,6 +128,7 @@ pub fn find_workspace(filetype: impl AsRef, source_file: &Path) -> Option<& ("rust", RootMarkers(&["Cargo.toml"])), ("sh", ParentOfSourceFile), ("vim", ParentOfSourceFile), + ("markdown", ParentOfSourceFile), ]) }); diff --git a/crates/linter/src/linters/go.rs b/crates/linter/src/linters/go.rs index 4b71cfe0e..64a82f045 100644 --- a/crates/linter/src/linters/go.rs +++ b/crates/linter/src/linters/go.rs @@ -6,7 +6,7 @@ use std::path::Path; // /home/xlc/Data0/src/github.com/ethereum-optimism/optimism/op-node/rollup/superchain.go:38:27-43: undefined: eth.XXXXSystemConfig static RE: Lazy = Lazy::new(|| { Regex::new(r"(?m)^([^:]+):([0-9]+):([0-9]+)-([0-9]+): (.+)$") - .expect("Regex for parsing gopls output must be correct otherwise the format is changed") + .expect("Regex for parsing gopls output must be correct otherwise the upstream format must have been changed") }); pub async fn run_gopls(source_file: &Path, workspace_root: &Path) -> std::io::Result { diff --git a/crates/maple_core/src/config.rs b/crates/maple_core/src/config.rs index 41259f1df..c82bfe551 100644 --- a/crates/maple_core/src/config.rs +++ b/crates/maple_core/src/config.rs @@ -55,7 +55,7 @@ pub fn config_file() -> &'static PathBuf { CONFIG_FILE.get().expect("Config file uninitialized") } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] #[serde(rename_all = "kebab-case", default, deny_unknown_fields)] pub struct MatcherConfig { pub tiebreak: String, @@ -78,18 +78,7 @@ impl MatcherConfig { } } -#[derive(Serialize, Deserialize, Debug, Default)] -#[serde(rename_all = "kebab-case", default, deny_unknown_fields)] -pub struct PickerConfig { - /// Specifies how many items will be displayed in the results window. - pub max_display_size: Option, - /// Render the preview highlight with specified theme using syntect backend. - /// - /// If the theme is not found, the default theme (`Visual Studio Dark+`) will be used. - pub syntect_highlight_theme: Option, -} - -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] #[serde(rename_all = "kebab-case", default, deny_unknown_fields)] pub struct LogConfig { pub log_file: Option, @@ -105,9 +94,9 @@ impl Default for LogConfig { } } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] #[serde(rename_all = "kebab-case", default, deny_unknown_fields)] -pub struct CursorWordHighlighterConfig { +pub struct CursorWordConfig { /// Whether to enable this plugin. pub enable: bool, /// Whether to ignore the comment line @@ -116,7 +105,7 @@ pub struct CursorWordHighlighterConfig { pub ignore_files: String, } -impl Default for CursorWordHighlighterConfig { +impl Default for CursorWordConfig { fn default() -> Self { Self { enable: false, @@ -126,21 +115,21 @@ impl Default for CursorWordHighlighterConfig { } } -#[derive(Serialize, Deserialize, Debug, Default)] +#[derive(Serialize, Deserialize, Debug, Default, Eq, PartialEq)] #[serde(rename_all = "kebab-case", default, deny_unknown_fields)] pub struct MarkdownPluginConfig { /// Whether to enable this plugin. pub enable: bool, } -#[derive(Serialize, Deserialize, Debug, Default)] +#[derive(Serialize, Deserialize, Debug, Default, Eq, PartialEq)] #[serde(rename_all = "kebab-case", default, deny_unknown_fields)] pub struct CtagsPluginConfig { /// Whether to enable this plugin. pub enable: bool, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] #[serde(rename_all = "kebab-case", default, deny_unknown_fields)] pub struct GitPluginConfig { /// Whether to enable this plugin. @@ -161,24 +150,24 @@ impl Default for GitPluginConfig { } } -#[derive(Serialize, Deserialize, Debug, Default)] +#[derive(Serialize, Deserialize, Debug, Default, Eq, PartialEq)] #[serde(rename_all = "kebab-case", default, deny_unknown_fields)] pub struct LinterPluginConfig { /// Whether to enable this plugin. pub enable: bool, } -#[derive(Serialize, Deserialize, Debug, Default)] +#[derive(Serialize, Deserialize, Debug, Default, Eq, PartialEq)] #[serde(rename_all = "kebab-case", default, deny_unknown_fields)] pub struct PluginConfig { - pub cursor_word_highlighter: CursorWordHighlighterConfig, - pub markdown: MarkdownPluginConfig, + pub cursorword: CursorWordConfig, pub ctags: CtagsPluginConfig, pub git: GitPluginConfig, pub linter: LinterPluginConfig, + pub markdown: MarkdownPluginConfig, } -#[derive(Serialize, Deserialize, Debug, Default)] +#[derive(Serialize, Deserialize, Debug, Default, Eq, PartialEq)] #[serde(rename_all = "kebab-case", default, deny_unknown_fields)] pub struct IgnoreConfig { /// Whether to ignore the comment line when it's possible. @@ -191,7 +180,7 @@ pub struct IgnoreConfig { pub ignore_file_path_pattern: Vec, } -#[derive(Serialize, Deserialize, Debug, Default)] +#[derive(Serialize, Deserialize, Debug, Default, Eq, PartialEq)] #[serde(rename_all = "kebab-case", default, deny_unknown_fields)] pub struct ProviderConfig { /// Delay in milliseconds before the user query will be handled actually. @@ -215,16 +204,20 @@ pub struct ProviderConfig { /// Priorities of the ignore config: /// provider_ignores > provider_ignores > global_ignore pub ignore: HashMap, -} -#[derive(Serialize, Deserialize, Debug, Default)] -#[serde(rename_all = "kebab-case", default, deny_unknown_fields)] -pub struct InputHistoryConfig { + /// Specifies how many items will be displayed in the results window. + pub max_display_size: Option, + + /// Render the preview highlight with specified theme using syntect backend. + /// + /// If the theme is not found, the default theme (`Visual Studio Dark+`) will be used. + pub syntect_highlight_theme: Option, + /// Whether to share the input history of each provider. - pub share_all_inputs: bool, + pub share_input_history: bool, } -#[derive(Serialize, Deserialize, Debug, Default)] +#[derive(Serialize, Deserialize, Debug, Default, Eq, PartialEq)] #[serde(rename_all = "kebab-case", default, deny_unknown_fields)] pub struct Config { /// Log configuration. @@ -233,9 +226,6 @@ pub struct Config { /// Matcher configuration. pub matcher: MatcherConfig, - /// Picker configuration. - pub picker: PickerConfig, - /// Plugin configuration. pub plugin: PluginConfig, @@ -249,9 +239,6 @@ pub struct Config { /// /// The project path must be specified as absolute path or a path relative to the home directory. pub project_ignore: HashMap, - - /// Input history configuration - pub input_history: InputHistoryConfig, } impl Config { @@ -294,7 +281,7 @@ mod tests { [matcher] tiebreak = "score,-begin,-end,-length" - [plugin.cursor-word-highlighter] + [plugin.cursorword] enable = true [provider.debounce] @@ -312,7 +299,44 @@ mod tests { "#; let user_config: Config = toml::from_str(toml_content).expect("Failed to deserialize config"); - println!("{:#?}", user_config); - println!("{}", toml::to_string(&user_config).unwrap()); + + assert_eq!( + user_config, + Config { + log: LogConfig { + log_file: Some("/tmp/clap.log".to_string()), + max_level: "trace".to_string() + }, + matcher: MatcherConfig { + tiebreak: "score,-begin,-end,-length".to_string() + }, + plugin: PluginConfig { + cursorword: CursorWordConfig { + enable: true, + ..Default::default() + }, + ..Default::default() + }, + provider: ProviderConfig { + debounce: HashMap::from_iter([ + ("*".to_string(), 200), + ("files".to_string(), 100) + ]), + ignore: HashMap::from([( + "dumb_jump".to_string(), + IgnoreConfig { + ignore_comments: true, + ..Default::default() + } + )]), + ..Default::default() + }, + global_ignore: IgnoreConfig { + ignore_file_path_pattern: vec!["test".to_string(), "build".to_string()], + ..Default::default() + }, + ..Default::default() + } + ); } } diff --git a/crates/maple_core/src/stdio_server/handler/on_move.rs b/crates/maple_core/src/stdio_server/handler/on_move.rs index 1c4006bdb..c17c51a8c 100644 --- a/crates/maple_core/src/stdio_server/handler/on_move.rs +++ b/crates/maple_core/src/stdio_server/handler/on_move.rs @@ -480,7 +480,7 @@ impl<'a> CachedPreviewImpl<'a> { // 1 (header line) + 1 (1-based line number) let line_number_offset = context_lines.len() + 1 + 1; let maybe_line_highlights = if let Some(theme) = - &crate::config::config().picker.syntect_highlight_theme + &crate::config::config().provider.syntect_highlight_theme { const THEME: &str = "Visual Studio Dark+"; let theme = if HIGHLIGHTER.theme_exists(theme) { diff --git a/crates/maple_core/src/stdio_server/mod.rs b/crates/maple_core/src/stdio_server/mod.rs index cea4f7775..f812b1901 100644 --- a/crates/maple_core/src/stdio_server/mod.rs +++ b/crates/maple_core/src/stdio_server/mod.rs @@ -9,8 +9,8 @@ mod vim; pub use self::input::InputHistory; use self::input::{ActionEvent, Event, ProviderEvent}; use self::plugin::{ - ActionType, ClapPlugin, CtagsPlugin, CursorWordHighlighter, GitPlugin, LinterPlugin, - MarkdownPlugin, PluginId, SyntaxHighlighterPlugin, SystemPlugin, + ActionType, ClapPlugin, CtagsPlugin, CursorWordPlugin, GitPlugin, LinterPlugin, MarkdownPlugin, + PluginId, SyntaxHighlighterPlugin, SystemPlugin, }; use self::provider::{create_provider, Context}; use self::service::ServiceManager; @@ -117,8 +117,8 @@ pub async fn start(config_err: Option) { register_plugin(Box::new(MarkdownPlugin::new(vim.clone())), None); } - if plugin_config.cursor_word_highlighter.enable { - register_plugin(Box::new(CursorWordHighlighter::new(vim.clone())), None); + if plugin_config.cursorword.enable { + register_plugin(Box::new(CursorWordPlugin::new(vim.clone())), None); } tokio::spawn({ diff --git a/crates/maple_core/src/stdio_server/plugin/ctags.rs b/crates/maple_core/src/stdio_server/plugin/ctags.rs index 3266d0eca..d6c7ae192 100644 --- a/crates/maple_core/src/stdio_server/plugin/ctags.rs +++ b/crates/maple_core/src/stdio_server/plugin/ctags.rs @@ -43,6 +43,7 @@ impl CtagsPlugin { } } + /// Updates the buffer variable `clap_current_symbol`. async fn on_cursor_moved(&mut self, bufnr: usize) -> Result<()> { if let Some(buffer_tags) = self.buf_tags.get(&bufnr) { let curlnum = self.vim.line(".").await?; @@ -101,11 +102,7 @@ impl ClapPlugin for CtagsPlugin { let (event_type, params) = autocmd; - let params: Vec = params.parse()?; - let bufnr = params - .into_iter() - .next() - .ok_or_else(|| anyhow::anyhow!("bufnr not found in params"))?; + let bufnr = params.parse_bufnr()?; match event_type { BufEnter | BufWritePost => { diff --git a/crates/maple_core/src/stdio_server/plugin/cursor_word_highlighter.rs b/crates/maple_core/src/stdio_server/plugin/cursorword.rs similarity index 89% rename from crates/maple_core/src/stdio_server/plugin/cursor_word_highlighter.rs rename to crates/maple_core/src/stdio_server/plugin/cursorword.rs index 1cb81acf2..81bb19cde 100644 --- a/crates/maple_core/src/stdio_server/plugin/cursor_word_highlighter.rs +++ b/crates/maple_core/src/stdio_server/plugin/cursorword.rs @@ -11,9 +11,9 @@ use utils::read_lines_from; #[derive(Debug, serde::Serialize)] struct WordHighlights { // (line_number, highlight_col_start) - other_words_highlight: Vec<(usize, usize)>, + twins_words_highlight: Vec<(usize, usize)>, cword_highlight: (usize, usize), - // highlight length. + // highlight length, in bytes. cword_len: usize, } @@ -40,11 +40,13 @@ fn find_word_highlights( ) -> std::io::Result> { let cword_len = cword.len(); let word_matcher = WordMatcher::new(vec![cword.into()]); + // line_start and line_end is 1-based. let line_start = line_start - 1; let line_end = line_end - 1; + let mut cursor_word_highlight = None; - let other_words_highlight = read_lines_from(source_file, line_start, line_end - line_start)? + let twins_words_highlight = read_lines_from(source_file, line_start, line_end - line_start)? .enumerate() .flat_map(|(idx, line)| { let matches_range = word_matcher.find_all_matches_range(&line); @@ -74,9 +76,10 @@ fn find_word_highlights( }) }) .collect(); + if let Some(cword_highlight) = cursor_word_highlight { Ok(Some(WordHighlights { - other_words_highlight, + twins_words_highlight, cword_highlight, cword_len, })) @@ -86,27 +89,27 @@ fn find_word_highlights( } #[derive(Debug)] -struct WinHighlights { +struct CursorHighlights { winid: usize, // Use `i32` as matchaddpos() returns -1 on error. match_ids: Vec, } #[derive(Debug, maple_derive::ClapPlugin)] -#[clap_plugin(id = "cursor-word-highlighter")] -pub struct CursorWordHighlighter { +#[clap_plugin(id = "cursorword")] +pub struct CursorWordPlugin { vim: Vim, bufs: HashMap, - cursor_highlights: Option, + cursor_highlights: Option, ignore_extensions: Vec<&'static str>, ignore_file_names: Vec<&'static str>, } -impl CursorWordHighlighter { +impl CursorWordPlugin { pub fn new(vim: Vim) -> Self { let (ignore_extensions, ignore_file_names): (Vec<_>, Vec<_>) = crate::config::config() .plugin - .cursor_word_highlighter + .cursorword .ignore_files .split(',') .partition(|s| s.starts_with("*.")); @@ -120,7 +123,7 @@ impl CursorWordHighlighter { } } - async fn create_new_highlights(&mut self, bufnr: usize) -> Result> { + async fn create_new_highlights(&mut self, bufnr: usize) -> Result> { let cword = self.vim.expand("").await?; if cword.is_empty() { @@ -138,7 +141,7 @@ impl CursorWordHighlighter { if crate::config::config() .plugin - .cursor_word_highlighter + .cursorword .ignore_comment_line { if let Some(ext) = source_file.extension().and_then(|s| s.to_str()) { @@ -169,12 +172,9 @@ impl CursorWordHighlighter { { let match_ids: Vec = self .vim - .call( - "clap#plugin#highlight_cursor_word#add_highlights", - word_highlights, - ) + .call("clap#plugin#cursorword#add_highlights", word_highlights) .await?; - return Ok(Some(WinHighlights { match_ids, winid })); + return Ok(Some(CursorHighlights { match_ids, winid })); } Ok(None) @@ -189,7 +189,7 @@ impl CursorWordHighlighter { }; // Clear the old highlights after the new added ones so that no flicker occurs. - if let Some(WinHighlights { winid, match_ids }) = old_highlights { + if let Some(CursorHighlights { winid, match_ids }) = old_highlights { self.vim.matchdelete_batch(match_ids, winid).await?; } @@ -232,7 +232,7 @@ impl CursorWordHighlighter { } #[async_trait::async_trait] -impl ClapPlugin for CursorWordHighlighter { +impl ClapPlugin for CursorWordPlugin { async fn handle_action(&mut self, _action: PluginAction) -> Result<()> { Ok(()) } @@ -254,7 +254,7 @@ impl ClapPlugin for CursorWordHighlighter { self.highlight_symbol_under_cursor(bufnr).await? } InsertEnter if self.bufs.contains_key(&bufnr) => { - if let Some(WinHighlights { winid, match_ids }) = self.cursor_highlights.take() { + if let Some(CursorHighlights { winid, match_ids }) = self.cursor_highlights.take() { self.vim.matchdelete_batch(match_ids, winid).await?; } } diff --git a/crates/maple_core/src/stdio_server/plugin/git.rs b/crates/maple_core/src/stdio_server/plugin/git.rs index 8c3beeb8e..aa706a9cb 100644 --- a/crates/maple_core/src/stdio_server/plugin/git.rs +++ b/crates/maple_core/src/stdio_server/plugin/git.rs @@ -125,11 +125,12 @@ impl Git { .stderr(Stdio::null()) .spawn()?; - let lines = lines.into_iter().join("\n"); let stdin = p .stdin .as_mut() .ok_or_else(|| anyhow!("stdin unavailable"))?; + + let lines = lines.into_iter().join("\n"); stdin.write_all(lines.as_bytes())?; let output = p.wait_with_output()?; @@ -328,12 +329,11 @@ impl ClapPlugin for GitPlugin { async fn handle_autocmd(&mut self, autocmd: AutocmdEvent) -> Result<()> { use AutocmdEventType::{BufDelete, BufEnter, BufLeave, CursorMoved, InsertEnter}; - let (autocmd_event_type, params) = autocmd; - if self.toggle.is_off() { return Ok(()); } + let (autocmd_event_type, params) = autocmd; let bufnr = params.parse_bufnr()?; match autocmd_event_type { diff --git a/crates/maple_core/src/stdio_server/plugin/markdown.rs b/crates/maple_core/src/stdio_server/plugin/markdown.rs index 9b13ffb4e..fcf9fae5b 100644 --- a/crates/maple_core/src/stdio_server/plugin/markdown.rs +++ b/crates/maple_core/src/stdio_server/plugin/markdown.rs @@ -1,5 +1,5 @@ use crate::stdio_server::input::{AutocmdEvent, PluginAction}; -use crate::stdio_server::plugin::ClapPlugin; +use crate::stdio_server::plugin::{ClapPlugin, Toggle}; use crate::stdio_server::vim::Vim; use anyhow::{anyhow, Result}; use once_cell::sync::Lazy; @@ -201,17 +201,37 @@ fn find_toc_range(input_file: impl AsRef) -> std::io::Result Self { - Self { vim } + Self { + vim, + toggle: Toggle::Off, + } + } + + async fn update_toc(&self, bufnr: usize) -> Result<()> { + let file = self.vim.bufabspath(bufnr).await?; + if let Some((start, end)) = find_toc_range(&file)? { + let shiftwidth = self.vim.getbufvar("", "&shiftwidth").await?; + // TODO: skip update if the new doc is the same as the old one. + let new_toc = generate_toc(file, start + 1, shiftwidth)?; + self.vim.deletebufline(bufnr, start + 1, end + 1).await?; + self.vim.exec("append_and_write", json!([start, new_toc]))?; + } + Ok(()) } } #[async_trait::async_trait] impl ClapPlugin for MarkdownPlugin { async fn handle_autocmd(&mut self, _autocmd: AutocmdEvent) -> Result<()> { + if self.toggle.is_off() { + return Ok(()); + } + Ok(()) } @@ -219,8 +239,8 @@ impl ClapPlugin for MarkdownPlugin { let PluginAction { method, params: _ } = action; match method.as_str() { Self::GENERATE_TOC => { - let curlnum = self.vim.line(".").await?; let file = self.vim.current_buffer_path().await?; + let curlnum = self.vim.line(".").await?; let shiftwidth = self.vim.getbufvar("", "&shiftwidth").await?; let mut toc = generate_toc(file, curlnum, shiftwidth)?; let prev_line = self.vim.curbufline(curlnum - 1).await?; @@ -231,15 +251,8 @@ impl ClapPlugin for MarkdownPlugin { .exec("append_and_write", json!([curlnum - 1, toc]))?; } Self::UPDATE_TOC => { - let file = self.vim.current_buffer_path().await?; let bufnr = self.vim.bufnr("").await?; - if let Some((start, end)) = find_toc_range(&file)? { - let shiftwidth = self.vim.getbufvar("", "&shiftwidth").await?; - // TODO: skip update if the new doc is the same as the old one. - let new_toc = generate_toc(file, start + 1, shiftwidth)?; - self.vim.deletebufline(bufnr, start + 1, end + 1).await?; - self.vim.exec("append_and_write", json!([start, new_toc]))?; - } + self.update_toc(bufnr).await?; } Self::DELETE_TOC => { let file = self.vim.current_buffer_path().await?; diff --git a/crates/maple_core/src/stdio_server/plugin/mod.rs b/crates/maple_core/src/stdio_server/plugin/mod.rs index 7fe8008fa..71452ca2c 100644 --- a/crates/maple_core/src/stdio_server/plugin/mod.rs +++ b/crates/maple_core/src/stdio_server/plugin/mod.rs @@ -1,5 +1,5 @@ mod ctags; -mod cursor_word_highlighter; +mod cursorword; mod git; mod linter; mod markdown; @@ -11,7 +11,7 @@ use anyhow::Result; use std::fmt::Debug; pub use self::ctags::CtagsPlugin; -pub use self::cursor_word_highlighter::CursorWordHighlighter; +pub use self::cursorword::CursorWordPlugin; pub use self::git::GitPlugin; pub use self::linter::LinterPlugin; pub use self::markdown::MarkdownPlugin; diff --git a/crates/maple_core/src/stdio_server/plugin/syntax_highlighter.rs b/crates/maple_core/src/stdio_server/plugin/syntax_highlighter.rs index 28346fb66..0c17858c3 100644 --- a/crates/maple_core/src/stdio_server/plugin/syntax_highlighter.rs +++ b/crates/maple_core/src/stdio_server/plugin/syntax_highlighter.rs @@ -117,13 +117,13 @@ pub fn highlight_lines( #[async_trait::async_trait] impl ClapPlugin for SyntaxHighlighterPlugin { async fn handle_autocmd(&mut self, autocmd: AutocmdEvent) -> Result<()> { - let (autocmd_event_type, params) = autocmd; use AutocmdEventType::{BufDelete, BufEnter, BufWritePost, CursorMoved}; if self.toggle.is_off() { return Ok(()); } + let (autocmd_event_type, params) = autocmd; let bufnr = params.parse_bufnr()?; match autocmd_event_type { diff --git a/crates/maple_core/src/stdio_server/provider/mod.rs b/crates/maple_core/src/stdio_server/provider/mod.rs index 33bfb39dc..9ba9bfdd5 100644 --- a/crates/maple_core/src/stdio_server/provider/mod.rs +++ b/crates/maple_core/src/stdio_server/provider/mod.rs @@ -325,7 +325,7 @@ impl Context { }; let input_history = crate::datastore::INPUT_HISTORY_IN_MEMORY.lock(); - let inputs = if crate::config::config().input_history.share_all_inputs { + let inputs = if crate::config::config().provider.share_input_history { input_history.all_inputs() } else { input_history.inputs(&env.provider_id) diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..7585238ef --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1 @@ +book diff --git a/docs/book.toml b/docs/book.toml new file mode 100644 index 000000000..e9b48bdcc --- /dev/null +++ b/docs/book.toml @@ -0,0 +1,15 @@ +[book] +authors = ["Liu-Cheng Xu"] +language = "en" +multilingual = false +src = "src" +title = "Vim-Clap Documentation" + +[rust] +edition = "2021" + +[output.html] +mathjax-support = true +additional-css = ["custom.css"] +edit-url-template = "https://github.com/liuchengxu/vim-clap/edit/master/docs/{path}" +git-repository-url = "https://github.com/liuchengxu/vim-clap/tree/master/docs" diff --git a/docs/custom.css b/docs/custom.css new file mode 100644 index 000000000..a6ade392a --- /dev/null +++ b/docs/custom.css @@ -0,0 +1,3 @@ +h2, h3 { + margin-block-start: 1.5em; +} diff --git a/docs/src/README.md b/docs/src/README.md new file mode 100644 index 000000000..3b3408f0d --- /dev/null +++ b/docs/src/README.md @@ -0,0 +1,37 @@ +# Introduction + +[![CI](https://github.com/liuchengxu/vim-clap/workflows/ci/badge.svg)](https://github.com/liuchengxu/vim-clap/actions?workflow=ci) +[![Gitter][g1]][g2] + +[g1]: https://badges.gitter.im/liuchengxu/vim-clap.svg +[g2]: https://gitter.im/liuchengxu/vim-clap?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge + +Vim-clap stands as a comprehensive and efficient solution, providing powerful fuzzy pickers and replacements for various established Vim plugins, designed to support both Vim and NeoVim. + +

+ +

+ +[More screenshots](https://github.com/liuchengxu/vim-clap/issues/1) + +## Features + +Vim-clap was initially written in pure VimScript, but later incorporated a Rust dependency to enhance performance. Presently, the Rust binary `maple` is a must-have for ensuring smooth and optimal functionality. The principle of Vim-Clap in this regard is to offload all the heavy computation to the Rust backend and make Vim/NeoVim a super lightweight layer focusing on UI. + +- [x] Blazingly fast thanks to the powerful Rust backend +- [x] Consistent command interface with [clap-rs/clap](https://github.com/clap-rs/clap) +- [x] Tons of builtin providers +- [x] Support writing new providers in both Vimscript and Rust +- [x] Support [the search syntax borrowed from fzf](https://github.com/junegunn/fzf#search-syntax) and more + +## Caveats + +- While Vim-clap is intended to be compatible with Windows, comprehensive testing on this platform has not been conducted to the same extent as macOS and Linux (specifically Ubuntu), as the plugin author primarily utilizes these operating systems. Consequently, there may be Windows-specific issues yet to be identified. If you encounter any problems on Windows, please [create an issue](https://github.com/liuchengxu/vim-clap/issues/new?assignees=&labels=&template=bug_report.md&title=), and any assistance in addressing these issues would be highly appreciated. + +- While Vim-Clap strives to offer equal support for both Vim and NeoVim, certain nuances arise from the differing implementation details between the two. For example, the focusability of Vim's `popup` differs from NeoVim's `floating_win`. + +## Credit + +- Vim-clap is initially enlightened by [snails](https://github.com/manateelazycat/snails). +- Some providers' idea and code are borrowed from [fzf.vim](https://github.com/junegunn/fzf.vim). +- The built-in fzy python implementation is based on [sweep.py](https://github.com/aslpavel/sweep.py). diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md new file mode 100644 index 000000000..9a5fbe722 --- /dev/null +++ b/docs/src/SUMMARY.md @@ -0,0 +1,15 @@ +# Summary + +[Introduction](README.md) + +# User Guide + +- [Installation](guide/installation.md) + - [Install Rust Dependency](guide/install_rust.md) +- [Clap Providers](providers/intro.md) + - [Theme](providers/theme.md) + - [Keybindings](providers/keybindings.md) + - [Search Syntax](providers/search_syntax.md) +- [Clap Plugins](plugins/intro.md) + - [Configuration](plugins/config.md) + - [Available Plugins](plugins/plugins.md) diff --git a/docs/src/guide/install_rust.md b/docs/src/guide/install_rust.md new file mode 100644 index 000000000..4bfeceb1b --- /dev/null +++ b/docs/src/guide/install_rust.md @@ -0,0 +1,53 @@ +# Install Rust Dependency + +You can download the prebuilt binary from GitHub or compile the binary locally on your own. + +### Compile Rust Binary Locally + +Refer to [https://www.rust-lang.org/tools/install](https://www.rust-lang.org/tools/install) if you haven't installed Rust on your system. + +Assuming Rust has already been installed on your system, specifically, `cargo` executable exists, you can have several ways to compile the binary: + +- Use this helper function `:call clap#installer#build_maple()` within Vim/NeoVim. + +- Run `make` under the clap plugin directory (macOS and Linux). + +- Run the `cargo` command on your own: + + ```bash + cd path/to/vim-clap + + # Compile the release build, you can find the compiled executable at target/release/maple. + cargo build --release + ``` + +### Compile Rust binary via Docker (Linux Only) + +If you run into the libssl error when using the prebuilt binary from GitHub release, you can try building a static Rust binary: + +```bash +$ cd path/to/vim-clap +$ docker run --rm -it -v "$(pwd)":/volume clux/muslrust cargo build --profile production --locked +$ cp target/x86_64-unknown-linux-musl/production/maple bin/maple +# See if it really works +$ ./bin/maple version +``` + +### Download Prebuilt binary + +The prebuilt binary is available from GitHub release. You can call `:call clap#installer#download_binary()` in Vim/NeoVim, or do it manually as follows. + +#### Quick Downloader + +The scripts to download the prebuilt binary quickly are provided out of the box. The downloaded executable can be found at `bin/maple` on success. + +- Unix: `$ bash install.sh` +- Windows: Run `install.ps1` in the powershell. + +#### Download Prebuilt Binary By Hand + +1. Download the binary from the latest release [https://github.com/liuchengxu/vim-clap/releases](https://github.com/liuchengxu/vim-clap/releases) according to your system. +2. Rename the downloaded binary to: + - Unix: `maple` + - Windows: `maple.exe` +3. Move `maple`/`maple.exe` to `bin` directory. Don't forget to assign execute permission to `maple` via `chmod a+x bin/maple` if you are using the Unix system. diff --git a/docs/src/guide/installation.md b/docs/src/guide/installation.md new file mode 100644 index 000000000..31bfaaf0c --- /dev/null +++ b/docs/src/guide/installation.md @@ -0,0 +1,29 @@ +# Installation + +## Requirement + +- Vim: `:echo has('patch-8.1.2114')`. +- NeoVim: `:echo has('nvim-0.4.2')`. + +## Installation + +### [vim-plug](https://github.com/junegunn/vim-plug) + +```vim +" Build the Rust binary if `cargo` exists on your system. +Plug 'liuchengxu/vim-clap', { 'do': ':Clap install-binary' } + +" The bang version will try to download the prebuilt binary if `cargo` does not exist. +Plug 'liuchengxu/vim-clap', { 'do': ':Clap install-binary!' } + +" `:Clap install-binary[!]` will always try to compile the binary locally. +" If you do care about the disk used for the compilation, use the way of force download, +" which will directly download the prebuilt binary even if `cargo` is available. +Plug 'liuchengxu/vim-clap', { 'do': { -> clap#installer#force_download() } } + +" `:Clap install-binary[!]` will run using the terminal feature which is inherently async. +" If you don't want that and hope to run the hook synchorously: +Plug 'liuchengxu/vim-clap', { 'do': has('win32') ? 'cargo build --release' : 'make' } +``` + +Employing the `do` hook of the Vim plugin manager typically facilitates the automatic installation of the additional Rust binary, offering a convenient and recommended solution. However, if this process encounters any issues, manual compilation of the Rust dependency is required, as outlined in [the subsequent section](./install_rust.md). diff --git a/docs/src/plugins/config.md b/docs/src/plugins/config.md new file mode 100644 index 000000000..eda18b306 --- /dev/null +++ b/docs/src/plugins/config.md @@ -0,0 +1,19 @@ +# Configuration + +User config file is loaded from: + +- Linux: `~/.config/vimclap/config.toml` +- macOS: `~/Library/Application\ Support/org.vim.Vim-Clap/config.toml` +- Windows: `C:\Users\Alice\AppData\Roaming\Vim\Vim Clap\config\config.toml` + +```toml +[log] +# Note that the log file path must be an absolute path. +log-file = "/tmp/clap.log" +max-level = "debug" + +[matcher] +# There are four sort keys for results: score, begin, end, length, +# you can specify how the records are sorted using `tiebreak`. +tiebreak = "score,-begin,-end,-length" +``` diff --git a/docs/src/plugins/intro.md b/docs/src/plugins/intro.md new file mode 100644 index 000000000..351ede748 --- /dev/null +++ b/docs/src/plugins/intro.md @@ -0,0 +1,17 @@ +# Clap Plugins + +> WARN: This is an experimental feature, use at your own risk! + +Vim-Clap was originally a mere Vim fuzzy picker plugin, however, the integration of a robust Rust backend unveiled the potential to implement various additional functionalities effortlessly, for enjoyable experimentation and potential performance enhancements. + +Note that the vim-Clap plugins were mainly created for the plugin author's personal uses, thus they may not be feature-complete as their alternatives. Bugs are expected as these plugins are not extensively tested, feel free to use if you are brave enough. + + +All the non-system plugins are disabled by default. To enable the plugins, you must create a [config file](./config.md) first and set `enable = true` explicitly for the plugins in the config file. + +```toml +[plugin.git] +enable = true +``` + +Check out [Available Plugins](./plugins.md) for more info. diff --git a/docs/src/plugins/plugins.md b/docs/src/plugins/plugins.md new file mode 100644 index 000000000..ad42b852c --- /dev/null +++ b/docs/src/plugins/plugins.md @@ -0,0 +1,64 @@ +# Available Plugins + +TODO: elaborate on plugins' usage. + + + +* [ctags](#ctags) +* [cursorword](#cursorword) +* [git](#git) +* [linter](#linter) +* [markdown](#markdown) + + + +## ctags + + + + +## cursorword + +```toml +[plugin.cursorword] +enable = true +``` + +| Features | Alternatives | +| :------------------------------------- | :----------------------------------------------------- | +| Highlight the word under the cursor | [nvim-blame-line](https://github.com/tveskag/nvim-blame-line)
[vim-illuminate](https://github.com/RRethy/vim-illuminate)
[vim-cursorword](https://github.com/itchyny/vim-cursorword)
[vim-brightest](https://github.com/osyo-manga/vim-brightest) | + +## git + +```toml +[plugin.git] +enable = true +``` + +| Features | Alternatives | +| :------------------------------------- | :----------------------------------------------------- | +| Show blame info at the end of line | [nvim-blame-line](https://github.com/tveskag/nvim-blame-line) | +| Open the permalink of current line in browser | _none_ | + +## linter + +```toml +[plugin.linter] +enable = true +``` + +- Features + - Lint files asynchronously + +- Alternatives + - [ale](https://github.com/dense-analysis/ale) + +## markdown + +```toml +[plugin.markdown] +enable = true +``` + +- Features + - Generate/Update/Delete toc diff --git a/docs/src/providers/intro.md b/docs/src/providers/intro.md new file mode 100644 index 000000000..607d9301e --- /dev/null +++ b/docs/src/providers/intro.md @@ -0,0 +1,133 @@ +# Clap Providers + + + +* [Builtin Providers](#builtin-providers) + * [Global Variables](#global-variables) +* [How to Create Your Own Provider](#how-to-create-your-own-provider) +* [Disable Auto-Completion Plugin in Clap Input Window](#disable-auto-completion-plugin-in-clap-input-window) + + + +## Builtin Providers + +Additional requirement means the potential extra tool needed for the particular provider besides the Rust binary [`maple`](../guide/install_rust.md). + +| Command | List | Additional Requirement | +| :------------------------------------- | :----------------------------------------------------- | :---------------------------------------------------------------------- | +| `Clap blines` | Lines in the current buffer | _none_ | +| `Clap buffers` | Open buffers | _none_ | +| `Clap colors` | Colorschemes | _none_ | +| `Clap command` | Command | _none_ | +| `Clap hist:` or `Clap command_history` | Command history | _none_ | +| `Clap hist/` or `Clap search_history` | Search history | _none_ | +| `Clap filetypes` | File types | _none_ | +| `Clap help_tags` | Help tags | _none_ | +| `Clap jumps` | Jumps | _none_ | +| `Clap lines` | Lines in the loaded buffers | _none_ | +| `Clap marks` | Marks | _none_ | +| `Clap maps` | Maps | _none_ | +| `Clap quickfix` | Entries of the quickfix list | _none_ | +| `Clap loclist` | Entries of the location list | _none_ | +| `Clap registers` | Registers | _none_ | +| `Clap yanks` | Yank stack of the current vim session | _none_ | +| `Clap history` | Open buffers and `v:oldfiles` | _none_ | +| `Clap windows` | Windows | _none_ | +| `Clap providers` | List the vim-clap providers | _none_ | +| `Clap bcommits` | Git commits for the current buffer | **[git][git]** | +| `Clap commits` | Git commits | **[git][git]** | +| `Clap gfiles` or `Clap git_files` | Files managed by git | **[git][git]** | +| `Clap git_diff_files` | Files managed by git and having uncommitted changes | **[git][git]** | +| _`Clap live_grep` (**deprecated**)_ | Grep using word-regexp matcher | **[rg][rg]** | +| `Clap dumb_jump` | Definitions/References using regexp with grep fallback | **[rg][rg]** with `--pcre2` | +| `Clap files` | Files | _none_ +| `Clap filer` | Ivy-like file explorer | _none_ +| `Clap grep`**+** | Grep using fuzzy matcher | _none_ +| `Clap igrep` | A combo of `filer` and `grep` | _none_ +| `Clap tags` | Tags in the current buffer | _none_ +| `Clap tagfiles` | Search existing `tagfiles` | _none_ +| `Clap proj_tags` | Tags in the current project | **[universal-ctags][universal-ctags]** (`+json`) +| `Clap recent_files` | Persistent ordered history of recent files | _none_ + +[rg]: https://github.com/BurntSushi/ripgrep +[git]: https://github.com/git/git +[universal-ctags]: https://github.com/universal-ctags/ctags + +- The command with a superscript `+` means that it supports multi-selection via Tab. +- `Clap live_grep` is deprecated now, `Clap grep` is recommended as the successor. +- `:Clap grep` + - Use `:Clap grep --query=` to grep the word under cursor. + - Use `:Clap grep --query=@visual` to grep the visual selection. + - `cwd` will be searched by default, specify the extra paths in the end to search multiple directories. + - `:Clap grep --path ~/.vim/plugged/ale` with `cwd` is `~/.vim/plugged/vim-clap` will both search vim-clap and ale. + +[Send a pull request](https://github.com/liuchengxu/vim-clap/pulls) if certain provider is not listed here. + +### Global Variables + +- `g:clap_layout`: Dict, `{ 'width': '67%', 'height': '33%', 'row': '33%', 'col': '17%' }` by default. This variable controls the size and position of vim-clap window. By default, the vim-clap window is placed relative to the currently active window. To make it relative to the whole editor modify this variable as shown below: + + ```vim + let g:clap_layout = { 'relative': 'editor' } + ``` + +- `g:clap_open_action`: Dict, `{ 'ctrl-t': 'tab split', 'ctrl-x': 'split', 'ctrl-v': 'vsplit' }`, extra key bindings for opening the selected file in a different way. NOTE: do not define a key binding which is conflicted with the other default bindings of vim-clap, and only `ctrl-*` is supported for now. + +- `g:clap_provider_alias`: Dict, if you don't want to invoke some clap provider by its id(name), as it's too long or somehow, you can add an alias for that provider. + + ```vim + " The provider name is `command_history`, with the following alias config, + " now you can call it via both `:Clap command_history` and `:Clap hist:`. + let g:clap_provider_alias = {'hist:': 'command_history'} + ``` + +- `g:clap_selected_sign`: Dict, `{ 'text': ' >', 'texthl': "ClapSelectedSign", "linehl": "ClapSelected"}`. + +- `g:clap_current_selection_sign`: Dict, `{ 'text': '>>', 'texthl': "ClapCurrentSelectionSign", "linehl": "ClapCurrentSelection"}`. + +- `g:clap_no_matches_msg`: String, `'NO MATCHES FOUND'`, message to show when there is no matches found. + +- `g:clap_popup_input_delay`: Number, `200ms` by default, delay for actually responding to the input, vim only. + +- `g:clap_disable_run_rooter`: Bool, `v:false`, vim-clap by default will try to run from the project root by changing `cwd` temporarily. Set it to `v:true` to run from the origin `cwd`. The project root here means the git base directory. Create an issue if you want to see more support about the project root. + +The option naming convention for provider is `g:clap_provider_{provider_id}_{opt}`. + +- `g:clap_provider_grep_blink`: [2, 100] by default, blink 2 times with 100ms timeout when jumping the result. Set it to [0, 0] to disable the blink. + +- `g:clap_provider_grep_opts`: An empty string by default, allows you to enable flags such as `'--hidden -g "!.git/"'`. + +See `:help clap-options` for more information. + +## How to Create Your Own Provider + +```vim +" `:Clap quick_open` to open some dotfiles quickly. +" `description` is actually optional, but if you want to show this provider +" when you call `:Clap`, the `description` is necessary. +let g:clap_provider_quick_open = { + \ 'source': ['~/.vimrc', '~/.spacevim', '~/.bashrc', '~/.tmux.conf'], + \ 'sink': 'e', + \ 'description': 'Quick open some dotfiles', + \ } +``` + +Find more examples at [wiki/Examples](https://github.com/liuchengxu/vim-clap/wiki/Examples). + +For complete guide about writing a clap provider please see [PROVIDER.md](PROVIDER.md). + +## Disable Auto-Completion Plugin in Clap Input Window + +Some of the auto-completion engines need to turn off to prevent bizarre behaviors(#580) + +For nvim-completion, add autocmd to your init.vim: + +```vim +autocmd FileType clap_input let g:completion_enable_auto_pop = 0 +``` + +For nvim-compe: + +```vim +autocmd FileType clap_input call compe#setup({ 'enabled': v:false }, 0) +``` diff --git a/docs/src/providers/keybindings.md b/docs/src/providers/keybindings.md new file mode 100644 index 000000000..a66cd1845 --- /dev/null +++ b/docs/src/providers/keybindings.md @@ -0,0 +1,52 @@ +# Keybindings + +## Keybindings + +### Insert mode + +- [x] Use Ctrl-j/Down or Ctrl-k/Up to navigate the result list up and down linewise. +- [x] Use PageDown/PageUp to scroll the result list down and up. +- [x] Use Ctrl-a/Home to go to the start of the input. +- [x] Use Ctrl-e/End to go to the end of the input. +- [x] Use Ctrl-c, Ctrl-g, Ctrl-[ or Esc(vim) to exit. +- [x] Use Ctrl-h/BS to delete previous character. +- [x] Use Ctrl-d to delete next character. +- [x] Use Ctrl-b to move cursor left one character. +- [x] Use Ctrl-f to move cursor right one character. +- [x] Use Ctrl-n for next input in the history. +- [x] Use Ctrl-p for previous input in the history. +- [x] Use Enter to select the entry and exit. + - Use Enter to expand the directory or edit the file for `:Clap filer`. +- [x] By default Alt-u does nothing. + - Use Alt-u to go up one directory in `:Clap filer`. +- [x] Use Tab to select multiple entries and open them using the quickfix window.(Need the provider has `sink*` support) + - Use Tab to expand the directory for `:Clap filer`. +- [x] Use Ctrl-t or Ctrl-x, Ctrl-v to open the selected entry in a new tab or a new split. +- [x] Use Ctrl-u to clear inputs. +- [x] Use Ctrl-l to launch the whole provider list panel for invoking another provider at any time. +- [x] Use Shift-Tab to invoke the action dialog(vim only). +- [x] Use Shift-up and Shift-down to scroll the preview. + +### NeoVim only + +#### Normal mode + +- [x] Use j/Down or k/Up to navigate the result list up and down linewise. +- [x] By default Alt-u does nothing. + - Use Alt-u to go up one directory in `:Clap filer`. +- [x] Use Ctrl-c, Ctrl-g or Esc to exit. +- [x] Use Ctrl-d/Ctrl-u/PageDown/PageUp to scroll the result list down and up. +- [x] Use Ctrl-l to launch the whole provider list panel for invoking another provider at any time. +- [x] Use Ctrl-n for next input in the history. +- [x] Use Ctrl-p for previous input in the history. +- [x] Use Shift-up and Shift-down to scroll the preview. +- [x] Use gg and G to scroll to the first and last item. +- [x] Use Enter to select the entry and exit. +- [x] Use Shift-Tab to invoke the action dialog. +- [x] Actions defined by `g:clap_open_action`. + +#### Cmdline mode + +- [x] Use `:q` to exit. + +See `:help clap-keybindings` for more information. Note that the [keybindings are not consistent](https://github.com/liuchengxu/vim-clap/issues/864) due to discrepancies between Vim/Neovim and different providers. diff --git a/docs/src/providers/search_syntax.md b/docs/src/providers/search_syntax.md new file mode 100644 index 000000000..6bcf23801 --- /dev/null +++ b/docs/src/providers/search_syntax.md @@ -0,0 +1,24 @@ +# Search Syntax + +## Fzf search syntax + +vim-clap adopts most of [fzf search syntax](https://github.com/junegunn/fzf#search-syntax). Note that the OR operator defined by a single bar character is unsupported, but you can achieve that by using multiple exact matches. + +| Token | Match type | Description | +| ------ | ---------- | ---------------------------------- | +| `sbtrkt` | fuzzy-match | Items that match sbtrkt | +| `'wild` | exact-match (quoted) | Items that include wild | +| `^music` | prefix-exact-match | Items that start with music | +| `.mp3$` | suffix-exact-match | Items that end with .mp3 | +| `!fire` | inverse-exact-match | Items that do not include fire | +| `!^music` | inverse-prefix-exact-match | Items that do not start with music | +| `!.mp3$` | inverse-suffix-exact-match | Items that do not end with .mp3 | + + +### Extended search syntax + +Apart from the basic fzf search syntax, more search syntax are supported: + +| Token | Match type | Description | +| ------ | ---------- | ------------------------------------------------------------ | +| `"cli` | word-match | Items that match word `cli` (`clippy` does not match `"cli`) | diff --git a/docs/src/providers/theme.md b/docs/src/providers/theme.md new file mode 100644 index 000000000..477454dce --- /dev/null +++ b/docs/src/providers/theme.md @@ -0,0 +1,22 @@ +# Theme + +By default vim-clap would use the colors extracted from your current colorscheme, which is not guaranteed to suitable for all the colorschemes. You can try the built-in `material_design_dark` theme if the default theme does not work well: + +```vim +let g:clap_theme = 'material_design_dark' +``` + +![material_design_dark-theme](https://user-images.githubusercontent.com/8850248/74818883-6cfdc380-533a-11ea-81fb-d09d90498c96.png) + +You could also set `g:clap_theme` to be a `Dict` to specify the palette: + +```vim +" Change the CamelCase of related highlight group name to under_score_case. +let g:clap_theme = { 'search_text': {'guifg': 'red', 'ctermfg': 'red'} } +``` + +`ClapDisplay` and `ClapPreview` are the most basic highlight groups for the display and preview window, which can be overridden if the provider has its own syntax highlight, then checkout the related [syntax](syntax) file for more granular highlights directly. + +If you are keen to explore and even want to write your own clap theme, take [autoload/clap/themes/material_design_dark.vim](../../../autoload/clap/themes/material_design_dark.vim) as a reference. + +See `:help clap-highlights` for more information. diff --git a/docs/theme/highlight.js b/docs/theme/highlight.js new file mode 100644 index 000000000..d40a45f3e --- /dev/null +++ b/docs/theme/highlight.js @@ -0,0 +1,489 @@ +/*! + Highlight.js v11.9.0 (git: f47103d4f1) + (c) 2006-2023 undefined and other contributors + License: BSD-3-Clause + */ +var hljs=function(){"use strict";function e(t){ +return t instanceof Map?t.clear=t.delete=t.set=()=>{ +throw Error("map is read-only")}:t instanceof Set&&(t.add=t.clear=t.delete=()=>{ +throw Error("set is read-only") +}),Object.freeze(t),Object.getOwnPropertyNames(t).forEach((n=>{ +const i=t[n],s=typeof i;"object"!==s&&"function"!==s||Object.isFrozen(i)||e(i) +})),t}class t{constructor(e){ +void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1} +ignoreMatch(){this.isMatchIgnored=!0}}function n(e){ +return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'") +}function i(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t] +;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}const s=e=>!!e.scope +;class o{constructor(e,t){ +this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){ +this.buffer+=n(e)}openNode(e){if(!s(e))return;const t=((e,{prefix:t})=>{ +if(e.startsWith("language:"))return e.replace("language:","language-") +;if(e.includes(".")){const n=e.split(".") +;return[`${t}${n.shift()}`,...n.map(((e,t)=>`${e}${"_".repeat(t+1)}`))].join(" ") +}return`${t}${e}`})(e.scope,{prefix:this.classPrefix});this.span(t)} +closeNode(e){s(e)&&(this.buffer+="")}value(){return this.buffer}span(e){ +this.buffer+=``}}const r=(e={})=>{const t={children:[]} +;return Object.assign(t,e),t};class a{constructor(){ +this.rootNode=r(),this.stack=[this.rootNode]}get top(){ +return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){ +this.top.children.push(e)}openNode(e){const t=r({scope:e}) +;this.add(t),this.stack.push(t)}closeNode(){ +if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){ +for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)} +walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){ +return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t), +t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){ +"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{ +a._collapse(e)})))}}class c extends a{constructor(e){super(),this.options=e} +addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){ +this.closeNode()}__addSublanguage(e,t){const n=e.root +;t&&(n.scope="language:"+t),this.add(n)}toHTML(){ +return new o(this,this.options).value()}finalize(){ +return this.closeAllNodes(),!0}}function l(e){ +return e?"string"==typeof e?e:e.source:null}function g(e){return h("(?=",e,")")} +function u(e){return h("(?:",e,")*")}function d(e){return h("(?:",e,")?")} +function h(...e){return e.map((e=>l(e))).join("")}function f(...e){const t=(e=>{ +const t=e[e.length-1] +;return"object"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{} +})(e);return"("+(t.capture?"":"?:")+e.map((e=>l(e))).join("|")+")"} +function p(e){return RegExp(e.toString()+"|").exec("").length-1} +const b=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./ +;function m(e,{joinWith:t}){let n=0;return e.map((e=>{n+=1;const t=n +;let i=l(e),s="";for(;i.length>0;){const e=b.exec(i);if(!e){s+=i;break} +s+=i.substring(0,e.index), +i=i.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?s+="\\"+(Number(e[1])+t):(s+=e[0], +"("===e[0]&&n++)}return s})).map((e=>`(${e})`)).join(t)} +const E="[a-zA-Z]\\w*",x="[a-zA-Z_]\\w*",w="\\b\\d+(\\.\\d+)?",y="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",_="\\b(0b[01]+)",O={ +begin:"\\\\[\\s\\S]",relevance:0},v={scope:"string",begin:"'",end:"'", +illegal:"\\n",contains:[O]},k={scope:"string",begin:'"',end:'"',illegal:"\\n", +contains:[O]},N=(e,t,n={})=>{const s=i({scope:"comment",begin:e,end:t, +contains:[]},n);s.contains.push({scope:"doctag", +begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)", +end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0}) +;const o=f("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/) +;return s.contains.push({begin:h(/[ ]+/,"(",o,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),s +},S=N("//","$"),M=N("/\\*","\\*/"),R=N("#","$");var j=Object.freeze({ +__proto__:null,APOS_STRING_MODE:v,BACKSLASH_ESCAPE:O,BINARY_NUMBER_MODE:{ +scope:"number",begin:_,relevance:0},BINARY_NUMBER_RE:_,COMMENT:N, +C_BLOCK_COMMENT_MODE:M,C_LINE_COMMENT_MODE:S,C_NUMBER_MODE:{scope:"number", +begin:y,relevance:0},C_NUMBER_RE:y,END_SAME_AS_BEGIN:e=>Object.assign(e,{ +"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{ +t.data._beginMatch!==e[1]&&t.ignoreMatch()}}),HASH_COMMENT_MODE:R,IDENT_RE:E, +MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:{begin:"\\.\\s*"+x,relevance:0}, +NUMBER_MODE:{scope:"number",begin:w,relevance:0},NUMBER_RE:w, +PHRASAL_WORDS_MODE:{ +begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ +},QUOTE_STRING_MODE:k,REGEXP_MODE:{scope:"regexp",begin:/\/(?=[^/\n]*\/)/, +end:/\/[gimuy]*/,contains:[O,{begin:/\[/,end:/\]/,relevance:0,contains:[O]}]}, +RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~", +SHEBANG:(e={})=>{const t=/^#![ ]*\// +;return e.binary&&(e.begin=h(t,/.*\b/,e.binary,/\b.*/)),i({scope:"meta",begin:t, +end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)}, +TITLE_MODE:{scope:"title",begin:E,relevance:0},UNDERSCORE_IDENT_RE:x, +UNDERSCORE_TITLE_MODE:{scope:"title",begin:x,relevance:0}});function A(e,t){ +"."===e.input[e.index-1]&&t.ignoreMatch()}function I(e,t){ +void 0!==e.className&&(e.scope=e.className,delete e.className)}function T(e,t){ +t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)", +e.__beforeBegin=A,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords, +void 0===e.relevance&&(e.relevance=0))}function L(e,t){ +Array.isArray(e.illegal)&&(e.illegal=f(...e.illegal))}function B(e,t){ +if(e.match){ +if(e.begin||e.end)throw Error("begin & end are not supported with match") +;e.begin=e.match,delete e.match}}function P(e,t){ +void 0===e.relevance&&(e.relevance=1)}const D=(e,t)=>{if(!e.beforeMatch)return +;if(e.starts)throw Error("beforeMatch cannot be used with starts") +;const n=Object.assign({},e);Object.keys(e).forEach((t=>{delete e[t] +})),e.keywords=n.keywords,e.begin=h(n.beforeMatch,g(n.begin)),e.starts={ +relevance:0,contains:[Object.assign(n,{endsParent:!0})] +},e.relevance=0,delete n.beforeMatch +},H=["of","and","for","in","not","or","if","then","parent","list","value"],C="keyword" +;function $(e,t,n=C){const i=Object.create(null) +;return"string"==typeof e?s(n,e.split(" ")):Array.isArray(e)?s(n,e):Object.keys(e).forEach((n=>{ +Object.assign(i,$(e[n],t,n))})),i;function s(e,n){ +t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((t=>{const n=t.split("|") +;i[n[0]]=[e,U(n[0],n[1])]}))}}function U(e,t){ +return t?Number(t):(e=>H.includes(e.toLowerCase()))(e)?0:1}const z={},W=e=>{ +console.error(e)},X=(e,...t)=>{console.log("WARN: "+e,...t)},G=(e,t)=>{ +z[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),z[`${e}/${t}`]=!0) +},K=Error();function F(e,t,{key:n}){let i=0;const s=e[n],o={},r={} +;for(let e=1;e<=t.length;e++)r[e+i]=s[e],o[e+i]=!0,i+=p(t[e-1]) +;e[n]=r,e[n]._emit=o,e[n]._multi=!0}function Z(e){(e=>{ +e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope, +delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={ +_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope +}),(e=>{if(Array.isArray(e.begin)){ +if(e.skip||e.excludeBegin||e.returnBegin)throw W("skip, excludeBegin, returnBegin not compatible with beginScope: {}"), +K +;if("object"!=typeof e.beginScope||null===e.beginScope)throw W("beginScope must be object"), +K;F(e,e.begin,{key:"beginScope"}),e.begin=m(e.begin,{joinWith:""})}})(e),(e=>{ +if(Array.isArray(e.end)){ +if(e.skip||e.excludeEnd||e.returnEnd)throw W("skip, excludeEnd, returnEnd not compatible with endScope: {}"), +K +;if("object"!=typeof e.endScope||null===e.endScope)throw W("endScope must be object"), +K;F(e,e.end,{key:"endScope"}),e.end=m(e.end,{joinWith:""})}})(e)}function V(e){ +function t(t,n){ +return RegExp(l(t),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(n?"g":"")) +}class n{constructor(){ +this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0} +addRule(e,t){ +t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]), +this.matchAt+=p(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null) +;const e=this.regexes.map((e=>e[1]));this.matcherRe=t(m(e,{joinWith:"|" +}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex +;const t=this.matcherRe.exec(e);if(!t)return null +;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),i=this.matchIndexes[n] +;return t.splice(0,n),Object.assign(t,i)}}class s{constructor(){ +this.rules=[],this.multiRegexes=[], +this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){ +if(this.multiRegexes[e])return this.multiRegexes[e];const t=new n +;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))), +t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){ +return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){ +this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){ +const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex +;let n=t.exec(e) +;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{ +const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)} +return n&&(this.regexIndex+=n.position+1, +this.regexIndex===this.count&&this.considerAll()),n}} +if(e.compilerExtensions||(e.compilerExtensions=[]), +e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.") +;return e.classNameAliases=i(e.classNameAliases||{}),function n(o,r){const a=o +;if(o.isCompiled)return a +;[I,B,Z,D].forEach((e=>e(o,r))),e.compilerExtensions.forEach((e=>e(o,r))), +o.__beforeBegin=null,[T,L,P].forEach((e=>e(o,r))),o.isCompiled=!0;let c=null +;return"object"==typeof o.keywords&&o.keywords.$pattern&&(o.keywords=Object.assign({},o.keywords), +c=o.keywords.$pattern, +delete o.keywords.$pattern),c=c||/\w+/,o.keywords&&(o.keywords=$(o.keywords,e.case_insensitive)), +a.keywordPatternRe=t(c,!0), +r&&(o.begin||(o.begin=/\B|\b/),a.beginRe=t(a.begin),o.end||o.endsWithParent||(o.end=/\B|\b/), +o.end&&(a.endRe=t(a.end)), +a.terminatorEnd=l(a.end)||"",o.endsWithParent&&r.terminatorEnd&&(a.terminatorEnd+=(o.end?"|":"")+r.terminatorEnd)), +o.illegal&&(a.illegalRe=t(o.illegal)), +o.contains||(o.contains=[]),o.contains=[].concat(...o.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((t=>i(e,{ +variants:null},t)))),e.cachedVariants?e.cachedVariants:q(e)?i(e,{ +starts:e.starts?i(e.starts):null +}):Object.isFrozen(e)?i(e):e))("self"===e?o:e)))),o.contains.forEach((e=>{n(e,a) +})),o.starts&&n(o.starts,r),a.matcher=(e=>{const t=new s +;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin" +}))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end" +}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(a),a}(e)}function q(e){ +return!!e&&(e.endsWithParent||q(e.starts))}class J extends Error{ +constructor(e,t){super(e),this.name="HTMLInjectionError",this.html=t}} +const Y=n,Q=i,ee=Symbol("nomatch"),te=n=>{ +const i=Object.create(null),s=Object.create(null),o=[];let r=!0 +;const a="Could not find the language '{}', did you forget to load/include a language module?",l={ +disableAutodetect:!0,name:"Plain text",contains:[]};let p={ +ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i, +languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-", +cssSelector:"pre code",languages:null,__emitter:c};function b(e){ +return p.noHighlightRe.test(e)}function m(e,t,n){let i="",s="" +;"object"==typeof t?(i=e, +n=t.ignoreIllegals,s=t.language):(G("10.7.0","highlight(lang, code, ...args) has been deprecated."), +G("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"), +s=e,i=t),void 0===n&&(n=!0);const o={code:i,language:s};N("before:highlight",o) +;const r=o.result?o.result:E(o.language,o.code,n) +;return r.code=o.code,N("after:highlight",r),r}function E(e,n,s,o){ +const c=Object.create(null);function l(){if(!N.keywords)return void M.addText(R) +;let e=0;N.keywordPatternRe.lastIndex=0;let t=N.keywordPatternRe.exec(R),n="" +;for(;t;){n+=R.substring(e,t.index) +;const s=_.case_insensitive?t[0].toLowerCase():t[0],o=(i=s,N.keywords[i]);if(o){ +const[e,i]=o +;if(M.addText(n),n="",c[s]=(c[s]||0)+1,c[s]<=7&&(j+=i),e.startsWith("_"))n+=t[0];else{ +const n=_.classNameAliases[e]||e;u(t[0],n)}}else n+=t[0] +;e=N.keywordPatternRe.lastIndex,t=N.keywordPatternRe.exec(R)}var i +;n+=R.substring(e),M.addText(n)}function g(){null!=N.subLanguage?(()=>{ +if(""===R)return;let e=null;if("string"==typeof N.subLanguage){ +if(!i[N.subLanguage])return void M.addText(R) +;e=E(N.subLanguage,R,!0,S[N.subLanguage]),S[N.subLanguage]=e._top +}else e=x(R,N.subLanguage.length?N.subLanguage:null) +;N.relevance>0&&(j+=e.relevance),M.__addSublanguage(e._emitter,e.language) +})():l(),R=""}function u(e,t){ +""!==e&&(M.startScope(t),M.addText(e),M.endScope())}function d(e,t){let n=1 +;const i=t.length-1;for(;n<=i;){if(!e._emit[n]){n++;continue} +const i=_.classNameAliases[e[n]]||e[n],s=t[n];i?u(s,i):(R=s,l(),R=""),n++}} +function h(e,t){ +return e.scope&&"string"==typeof e.scope&&M.openNode(_.classNameAliases[e.scope]||e.scope), +e.beginScope&&(e.beginScope._wrap?(u(R,_.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap), +R=""):e.beginScope._multi&&(d(e.beginScope,t),R="")),N=Object.create(e,{parent:{ +value:N}}),N}function f(e,n,i){let s=((e,t)=>{const n=e&&e.exec(t) +;return n&&0===n.index})(e.endRe,i);if(s){if(e["on:end"]){const i=new t(e) +;e["on:end"](n,i),i.isMatchIgnored&&(s=!1)}if(s){ +for(;e.endsParent&&e.parent;)e=e.parent;return e}} +if(e.endsWithParent)return f(e.parent,n,i)}function b(e){ +return 0===N.matcher.regexIndex?(R+=e[0],1):(T=!0,0)}function m(e){ +const t=e[0],i=n.substring(e.index),s=f(N,e,i);if(!s)return ee;const o=N +;N.endScope&&N.endScope._wrap?(g(), +u(t,N.endScope._wrap)):N.endScope&&N.endScope._multi?(g(), +d(N.endScope,e)):o.skip?R+=t:(o.returnEnd||o.excludeEnd||(R+=t), +g(),o.excludeEnd&&(R=t));do{ +N.scope&&M.closeNode(),N.skip||N.subLanguage||(j+=N.relevance),N=N.parent +}while(N!==s.parent);return s.starts&&h(s.starts,e),o.returnEnd?0:t.length} +let w={};function y(i,o){const a=o&&o[0];if(R+=i,null==a)return g(),0 +;if("begin"===w.type&&"end"===o.type&&w.index===o.index&&""===a){ +if(R+=n.slice(o.index,o.index+1),!r){const t=Error(`0 width match regex (${e})`) +;throw t.languageName=e,t.badRule=w.rule,t}return 1} +if(w=o,"begin"===o.type)return(e=>{ +const n=e[0],i=e.rule,s=new t(i),o=[i.__beforeBegin,i["on:begin"]] +;for(const t of o)if(t&&(t(e,s),s.isMatchIgnored))return b(n) +;return i.skip?R+=n:(i.excludeBegin&&(R+=n), +g(),i.returnBegin||i.excludeBegin||(R=n)),h(i,e),i.returnBegin?0:n.length})(o) +;if("illegal"===o.type&&!s){ +const e=Error('Illegal lexeme "'+a+'" for mode "'+(N.scope||"")+'"') +;throw e.mode=N,e}if("end"===o.type){const e=m(o);if(e!==ee)return e} +if("illegal"===o.type&&""===a)return 1 +;if(I>1e5&&I>3*o.index)throw Error("potential infinite loop, way more iterations than matches") +;return R+=a,a.length}const _=O(e) +;if(!_)throw W(a.replace("{}",e)),Error('Unknown language: "'+e+'"') +;const v=V(_);let k="",N=o||v;const S={},M=new p.__emitter(p);(()=>{const e=[] +;for(let t=N;t!==_;t=t.parent)t.scope&&e.unshift(t.scope) +;e.forEach((e=>M.openNode(e)))})();let R="",j=0,A=0,I=0,T=!1;try{ +if(_.__emitTokens)_.__emitTokens(n,M);else{for(N.matcher.considerAll();;){ +I++,T?T=!1:N.matcher.considerAll(),N.matcher.lastIndex=A +;const e=N.matcher.exec(n);if(!e)break;const t=y(n.substring(A,e.index),e) +;A=e.index+t}y(n.substring(A))}return M.finalize(),k=M.toHTML(),{language:e, +value:k,relevance:j,illegal:!1,_emitter:M,_top:N}}catch(t){ +if(t.message&&t.message.includes("Illegal"))return{language:e,value:Y(n), +illegal:!0,relevance:0,_illegalBy:{message:t.message,index:A, +context:n.slice(A-100,A+100),mode:t.mode,resultSoFar:k},_emitter:M};if(r)return{ +language:e,value:Y(n),illegal:!1,relevance:0,errorRaised:t,_emitter:M,_top:N} +;throw t}}function x(e,t){t=t||p.languages||Object.keys(i);const n=(e=>{ +const t={value:Y(e),illegal:!1,relevance:0,_top:l,_emitter:new p.__emitter(p)} +;return t._emitter.addText(e),t})(e),s=t.filter(O).filter(k).map((t=>E(t,e,!1))) +;s.unshift(n);const o=s.sort(((e,t)=>{ +if(e.relevance!==t.relevance)return t.relevance-e.relevance +;if(e.language&&t.language){if(O(e.language).supersetOf===t.language)return 1 +;if(O(t.language).supersetOf===e.language)return-1}return 0})),[r,a]=o,c=r +;return c.secondBest=a,c}function w(e){let t=null;const n=(e=>{ +let t=e.className+" ";t+=e.parentNode?e.parentNode.className:"" +;const n=p.languageDetectRe.exec(t);if(n){const t=O(n[1]) +;return t||(X(a.replace("{}",n[1])), +X("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"} +return t.split(/\s+/).find((e=>b(e)||O(e)))})(e);if(b(n))return +;if(N("before:highlightElement",{el:e,language:n +}),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e) +;if(e.children.length>0&&(p.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."), +console.warn("https://github.com/highlightjs/highlight.js/wiki/security"), +console.warn("The element with unescaped HTML:"), +console.warn(e)),p.throwUnescapedHTML))throw new J("One of your code blocks includes unescaped HTML.",e.innerHTML) +;t=e;const i=t.textContent,o=n?m(i,{language:n,ignoreIllegals:!0}):x(i) +;e.innerHTML=o.value,e.dataset.highlighted="yes",((e,t,n)=>{const i=t&&s[t]||n +;e.classList.add("hljs"),e.classList.add("language-"+i) +})(e,n,o.language),e.result={language:o.language,re:o.relevance, +relevance:o.relevance},o.secondBest&&(e.secondBest={ +language:o.secondBest.language,relevance:o.secondBest.relevance +}),N("after:highlightElement",{el:e,result:o,text:i})}let y=!1;function _(){ +"loading"!==document.readyState?document.querySelectorAll(p.cssSelector).forEach(w):y=!0 +}function O(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]} +function v(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{ +s[e.toLowerCase()]=t}))}function k(e){const t=O(e) +;return t&&!t.disableAutodetect}function N(e,t){const n=e;o.forEach((e=>{ +e[n]&&e[n](t)}))} +"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(()=>{ +y&&_()}),!1),Object.assign(n,{highlight:m,highlightAuto:x,highlightAll:_, +highlightElement:w, +highlightBlock:e=>(G("10.7.0","highlightBlock will be removed entirely in v12.0"), +G("10.7.0","Please use highlightElement now."),w(e)),configure:e=>{p=Q(p,e)}, +initHighlighting:()=>{ +_(),G("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")}, +initHighlightingOnLoad:()=>{ +_(),G("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.") +},registerLanguage:(e,t)=>{let s=null;try{s=t(n)}catch(t){ +if(W("Language definition for '{}' could not be registered.".replace("{}",e)), +!r)throw t;W(t),s=l} +s.name||(s.name=e),i[e]=s,s.rawDefinition=t.bind(null,n),s.aliases&&v(s.aliases,{ +languageName:e})},unregisterLanguage:e=>{delete i[e] +;for(const t of Object.keys(s))s[t]===e&&delete s[t]}, +listLanguages:()=>Object.keys(i),getLanguage:O,registerAliases:v, +autoDetection:k,inherit:Q,addPlugin:e=>{(e=>{ +e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{ +e["before:highlightBlock"](Object.assign({block:t.el},t)) +}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{ +e["after:highlightBlock"](Object.assign({block:t.el},t))})})(e),o.push(e)}, +removePlugin:e=>{const t=o.indexOf(e);-1!==t&&o.splice(t,1)}}),n.debugMode=()=>{ +r=!1},n.safeMode=()=>{r=!0},n.versionString="11.9.0",n.regex={concat:h, +lookahead:g,either:f,optional:d,anyNumberOfTimes:u} +;for(const t in j)"object"==typeof j[t]&&e(j[t]);return Object.assign(n,j),n +},ne=te({});return ne.newInstance=()=>te({}),ne}() +;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);/*! `bash` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const s=e.regex,t={},n={begin:/\$\{/, +end:/\}/,contains:["self",{begin:/:-/,contains:[t]}]};Object.assign(t,{ +className:"variable",variants:[{ +begin:s.concat(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},n]});const a={ +className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},i={ +begin:/<<-?\s*(?=\w+)/,starts:{contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/, +end:/(\w+)/,className:"string"})]}},c={className:"string",begin:/"/,end:/"/, +contains:[e.BACKSLASH_ESCAPE,t,a]};a.contains.push(c);const o={begin:/\$?\(\(/, +end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,t] +},r=e.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10 +}),l={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0, +contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{ +name:"Bash",aliases:["sh"],keywords:{$pattern:/\b[a-z][a-z0-9._-]+\b/, +keyword:["if","then","else","elif","fi","for","while","until","in","do","done","case","esac","function","select"], +literal:["true","false"], +built_in:["break","cd","continue","eval","exec","exit","export","getopts","hash","pwd","readonly","return","shift","test","times","trap","umask","unset","alias","bind","builtin","caller","command","declare","echo","enable","help","let","local","logout","mapfile","printf","read","readarray","source","type","typeset","ulimit","unalias","set","shopt","autoload","bg","bindkey","bye","cap","chdir","clone","comparguments","compcall","compctl","compdescribe","compfiles","compgroups","compquote","comptags","comptry","compvalues","dirs","disable","disown","echotc","echoti","emulate","fc","fg","float","functions","getcap","getln","history","integer","jobs","kill","limit","log","noglob","popd","print","pushd","pushln","rehash","sched","setcap","setopt","stat","suspend","ttyctl","unfunction","unhash","unlimit","unsetopt","vared","wait","whence","where","which","zcompile","zformat","zftp","zle","zmodload","zparseopts","zprof","zpty","zregexparse","zsocket","zstyle","ztcp","chcon","chgrp","chown","chmod","cp","dd","df","dir","dircolors","ln","ls","mkdir","mkfifo","mknod","mktemp","mv","realpath","rm","rmdir","shred","sync","touch","truncate","vdir","b2sum","base32","base64","cat","cksum","comm","csplit","cut","expand","fmt","fold","head","join","md5sum","nl","numfmt","od","paste","ptx","pr","sha1sum","sha224sum","sha256sum","sha384sum","sha512sum","shuf","sort","split","sum","tac","tail","tr","tsort","unexpand","uniq","wc","arch","basename","chroot","date","dirname","du","echo","env","expr","factor","groups","hostid","id","link","logname","nice","nohup","nproc","pathchk","pinky","printenv","printf","pwd","readlink","runcon","seq","sleep","stat","stdbuf","stty","tee","test","timeout","tty","uname","unlink","uptime","users","who","whoami","yes"] +},contains:[r,e.SHEBANG(),l,o,e.HASH_COMMENT_MODE,i,{match:/(\/[a-z._-]+)+/},c,{ +match:/\\"/},{className:"string",begin:/'/,end:/'/},{match:/\\'/},t]}}})() +;hljs.registerLanguage("bash",e)})();/*! `diff` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const a=e.regex;return{name:"Diff", +aliases:["patch"],contains:[{className:"meta",relevance:10, +match:a.either(/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/,/^\*\*\* +\d+,\d+ +\*\*\*\*$/,/^--- +\d+,\d+ +----$/) +},{className:"comment",variants:[{ +begin:a.either(/Index: /,/^index/,/={3,}/,/^-{3}/,/^\*{3} /,/^\+{3}/,/^diff --git/), +end:/$/},{match:/^\*{15}$/}]},{className:"addition",begin:/^\+/,end:/$/},{ +className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/, +end:/$/}]}}})();hljs.registerLanguage("diff",e)})();/*! `dockerfile` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>({name:"Dockerfile",aliases:["docker"], +case_insensitive:!0, +keywords:["from","maintainer","expose","env","arg","user","onbuild","stopsignal"], +contains:[e.HASH_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.NUMBER_MODE,{ +beginKeywords:"run cmd entrypoint volume add copy workdir label healthcheck shell", +starts:{end:/[^\\]$/,subLanguage:"bash"}}],illegal:"{var e=(()=>{"use strict";return e=>{const n=e.regex,a={className:"number", +relevance:0,variants:[{begin:/([+-]+)?[\d]+_[\d_]+/},{begin:e.NUMBER_RE}] +},s=e.COMMENT();s.variants=[{begin:/;/,end:/$/},{begin:/#/,end:/$/}];const i={ +className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{begin:/\$\{(.*?)\}/ +}]},t={className:"literal",begin:/\bon|off|true|false|yes|no\b/},r={ +className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:"'''", +end:"'''",relevance:10},{begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"' +},{begin:"'",end:"'"}]},l={begin:/\[/,end:/\]/,contains:[s,t,i,r,a,"self"], +relevance:0},c=n.either(/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/);return{ +name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/, +contains:[s,{className:"section",begin:/\[+/,end:/\]+/},{ +begin:n.concat(c,"(\\s*\\.\\s*",c,")*",n.lookahead(/\s*=\s*[^#\s]/)), +className:"attr",starts:{end:/$/,contains:[s,l,t,i,r,a]}}]}}})() +;hljs.registerLanguage("ini",e)})();/*! `json` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const a=["true","false","null"],n={ +scope:"literal",beginKeywords:a.join(" ")};return{name:"JSON",keywords:{ +literal:a},contains:[{className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/, +relevance:1.01},{match:/[{}[\],:]/,className:"punctuation",relevance:0 +},e.QUOTE_STRING_MODE,n,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE], +illegal:"\\S"}}})();hljs.registerLanguage("json",e)})();/*! `lua` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const t="\\[=*\\[",a="\\]=*\\]",n={ +begin:t,end:a,contains:["self"] +},o=[e.COMMENT("--(?!"+t+")","$"),e.COMMENT("--"+t,a,{contains:[n],relevance:10 +})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE, +literal:"true false nil", +keyword:"and break do else elseif end for goto if in local not or repeat return then until while", +built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove" +},contains:o.concat([{className:"function",beginKeywords:"function",end:"\\)", +contains:[e.inherit(e.TITLE_MODE,{ +begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params", +begin:"\\(",endsWithParent:!0,contains:o}].concat(o) +},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string", +begin:t,end:a,contains:[n],relevance:5}])}}})();hljs.registerLanguage("lua",e) +})();/*! `makefile` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const i={className:"variable", +variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)", +contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%{var e=(()=>{"use strict";return e=>{const n={begin:/<\/?[A-Za-z_]/, +end:">",subLanguage:"xml",relevance:0},a={variants:[{begin:/\[.+?\]\[.*?\]/, +relevance:0},{ +begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/, +relevance:2},{ +begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/), +relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{ +begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/ +},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0, +returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)", +excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[", +end:"\\]",excludeBegin:!0,excludeEnd:!0}]},i={className:"strong",contains:[], +variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}] +},s={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{ +begin:/_(?![_\s])/,end:/_/,relevance:0}]},c=e.inherit(i,{contains:[] +}),t=e.inherit(s,{contains:[]});i.contains.push(t),s.contains.push(c) +;let g=[n,a];return[i,s,c,t].forEach((e=>{e.contains=e.contains.concat(g) +})),g=g.concat(i,s),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{ +className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:g},{ +begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n", +contains:g}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)", +end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:g, +end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{ +begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{ +begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))", +contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{ +begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{ +className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{ +className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}}})() +;hljs.registerLanguage("markdown",e)})();/*! `plaintext` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var t=(()=>{"use strict";return t=>({name:"Plain text", +aliases:["text","txt"],disableAutodetect:!0})})() +;hljs.registerLanguage("plaintext",t)})();/*! `rust` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{const t=e.regex,n={ +className:"title.function.invoke",relevance:0, +begin:t.concat(/\b/,/(?!let|for|while|if|else|match\b)/,e.IDENT_RE,t.lookahead(/\s*\(/)) +},a="([ui](8|16|32|64|128|size)|f(32|64))?",i=["drop ","Copy","Send","Sized","Sync","Drop","Fn","FnMut","FnOnce","ToOwned","Clone","Debug","PartialEq","PartialOrd","Eq","Ord","AsRef","AsMut","Into","From","Default","Iterator","Extend","IntoIterator","DoubleEndedIterator","ExactSizeIterator","SliceConcatExt","ToString","assert!","assert_eq!","bitflags!","bytes!","cfg!","col!","concat!","concat_idents!","debug_assert!","debug_assert_eq!","env!","eprintln!","panic!","file!","format!","format_args!","include_bytes!","include_str!","line!","local_data_key!","module_path!","option_env!","print!","println!","select!","stringify!","try!","unimplemented!","unreachable!","vec!","write!","writeln!","macro_rules!","assert_ne!","debug_assert_ne!"],r=["i8","i16","i32","i64","i128","isize","u8","u16","u32","u64","u128","usize","f32","f64","str","char","bool","Box","Option","Result","String","Vec"] +;return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?",type:r, +keyword:["abstract","as","async","await","become","box","break","const","continue","crate","do","dyn","else","enum","extern","false","final","fn","for","if","impl","in","let","loop","macro","match","mod","move","mut","override","priv","pub","ref","return","self","Self","static","struct","super","trait","true","try","type","typeof","unsafe","unsized","use","virtual","where","while","yield"], +literal:["true","false","Some","None","Ok","Err"],built_in:i},illegal:""},n]}}})() +;hljs.registerLanguage("rust",e)})();/*! `shell` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var s=(()=>{"use strict";return s=>({name:"Shell Session", +aliases:["console","shellsession"],contains:[{className:"meta.prompt", +begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#][ ]?/,starts:{end:/[^\\](?=\s*$)/, +subLanguage:"bash"}}]})})();hljs.registerLanguage("shell",s)})();/*! `vim` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>({name:"Vim Script",keywords:{ +$pattern:/[!#@\w]+/, +keyword:"N|0 P|0 X|0 a|0 ab abc abo al am an|0 ar arga argd arge argdo argg argl argu as au aug aun b|0 bN ba bad bd be bel bf bl bm bn bo bp br brea breaka breakd breakl bro bufdo buffers bun bw c|0 cN cNf ca cabc caddb cad caddf cal cat cb cc ccl cd ce cex cf cfir cgetb cgete cg changes chd che checkt cl cla clo cm cmapc cme cn cnew cnf cno cnorea cnoreme co col colo com comc comp con conf cope cp cpf cq cr cs cst cu cuna cunme cw delm deb debugg delc delf dif diffg diffo diffp diffpu diffs diffthis dig di dl dell dj dli do doautoa dp dr ds dsp e|0 ea ec echoe echoh echom echon el elsei em en endfo endf endt endw ene ex exe exi exu f|0 files filet fin fina fini fir fix fo foldc foldd folddoc foldo for fu go gr grepa gu gv ha helpf helpg helpt hi hid his ia iabc if ij il im imapc ime ino inorea inoreme int is isp iu iuna iunme j|0 ju k|0 keepa kee keepj lN lNf l|0 lad laddb laddf la lan lat lb lc lch lcl lcs le lefta let lex lf lfir lgetb lgete lg lgr lgrepa lh ll lla lli lmak lm lmapc lne lnew lnf ln loadk lo loc lockv lol lope lp lpf lr ls lt lu lua luad luaf lv lvimgrepa lw m|0 ma mak map mapc marks mat me menut mes mk mks mksp mkv mkvie mod mz mzf nbc nb nbs new nm nmapc nme nn nnoreme noa no noh norea noreme norm nu nun nunme ol o|0 om omapc ome on ono onoreme opt ou ounme ow p|0 profd prof pro promptr pc ped pe perld po popu pp pre prev ps pt ptN ptf ptj ptl ptn ptp ptr pts pu pw py3 python3 py3d py3f py pyd pyf quita qa rec red redi redr redraws reg res ret retu rew ri rightb rub rubyd rubyf rund ru rv sN san sa sal sav sb sbN sba sbf sbl sbm sbn sbp sbr scrip scripte scs se setf setg setl sf sfir sh sim sig sil sl sla sm smap smapc sme sn sni sno snor snoreme sor so spelld spe spelli spellr spellu spellw sp spr sre st sta startg startr star stopi stj sts sun sunm sunme sus sv sw sy synti sync tN tabN tabc tabdo tabe tabf tabfir tabl tabm tabnew tabn tabo tabp tabr tabs tab ta tags tc tcld tclf te tf th tj tl tm tn to tp tr try ts tu u|0 undoj undol una unh unl unlo unm unme uns up ve verb vert vim vimgrepa vi viu vie vm vmapc vme vne vn vnoreme vs vu vunme windo w|0 wN wa wh wi winc winp wn wp wq wqa ws wu wv x|0 xa xmapc xm xme xn xnoreme xu xunme y|0 z|0 ~ Next Print append abbreviate abclear aboveleft all amenu anoremenu args argadd argdelete argedit argglobal arglocal argument ascii autocmd augroup aunmenu buffer bNext ball badd bdelete behave belowright bfirst blast bmodified bnext botright bprevious brewind break breakadd breakdel breaklist browse bunload bwipeout change cNext cNfile cabbrev cabclear caddbuffer caddexpr caddfile call catch cbuffer cclose center cexpr cfile cfirst cgetbuffer cgetexpr cgetfile chdir checkpath checktime clist clast close cmap cmapclear cmenu cnext cnewer cnfile cnoremap cnoreabbrev cnoremenu copy colder colorscheme command comclear compiler continue confirm copen cprevious cpfile cquit crewind cscope cstag cunmap cunabbrev cunmenu cwindow delete delmarks debug debuggreedy delcommand delfunction diffupdate diffget diffoff diffpatch diffput diffsplit digraphs display deletel djump dlist doautocmd doautoall deletep drop dsearch dsplit edit earlier echo echoerr echohl echomsg else elseif emenu endif endfor endfunction endtry endwhile enew execute exit exusage file filetype find finally finish first fixdel fold foldclose folddoopen folddoclosed foldopen function global goto grep grepadd gui gvim hardcopy help helpfind helpgrep helptags highlight hide history insert iabbrev iabclear ijump ilist imap imapclear imenu inoremap inoreabbrev inoremenu intro isearch isplit iunmap iunabbrev iunmenu join jumps keepalt keepmarks keepjumps lNext lNfile list laddexpr laddbuffer laddfile last language later lbuffer lcd lchdir lclose lcscope left leftabove lexpr lfile lfirst lgetbuffer lgetexpr lgetfile lgrep lgrepadd lhelpgrep llast llist lmake lmap lmapclear lnext lnewer lnfile lnoremap loadkeymap loadview lockmarks lockvar lolder lopen lprevious lpfile lrewind ltag lunmap luado luafile lvimgrep lvimgrepadd lwindow move mark make mapclear match menu menutranslate messages mkexrc mksession mkspell mkvimrc mkview mode mzscheme mzfile nbclose nbkey nbsart next nmap nmapclear nmenu nnoremap nnoremenu noautocmd noremap nohlsearch noreabbrev noremenu normal number nunmap nunmenu oldfiles open omap omapclear omenu only onoremap onoremenu options ounmap ounmenu ownsyntax print profdel profile promptfind promptrepl pclose pedit perl perldo pop popup ppop preserve previous psearch ptag ptNext ptfirst ptjump ptlast ptnext ptprevious ptrewind ptselect put pwd py3do py3file python pydo pyfile quit quitall qall read recover redo redir redraw redrawstatus registers resize retab return rewind right rightbelow ruby rubydo rubyfile rundo runtime rviminfo substitute sNext sandbox sargument sall saveas sbuffer sbNext sball sbfirst sblast sbmodified sbnext sbprevious sbrewind scriptnames scriptencoding scscope set setfiletype setglobal setlocal sfind sfirst shell simalt sign silent sleep slast smagic smapclear smenu snext sniff snomagic snoremap snoremenu sort source spelldump spellgood spellinfo spellrepall spellundo spellwrong split sprevious srewind stop stag startgreplace startreplace startinsert stopinsert stjump stselect sunhide sunmap sunmenu suspend sview swapname syntax syntime syncbind tNext tabNext tabclose tabedit tabfind tabfirst tablast tabmove tabnext tabonly tabprevious tabrewind tag tcl tcldo tclfile tearoff tfirst throw tjump tlast tmenu tnext topleft tprevious trewind tselect tunmenu undo undojoin undolist unabbreviate unhide unlet unlockvar unmap unmenu unsilent update vglobal version verbose vertical vimgrep vimgrepadd visual viusage view vmap vmapclear vmenu vnew vnoremap vnoremenu vsplit vunmap vunmenu write wNext wall while winsize wincmd winpos wnext wprevious wqall wsverb wundo wviminfo xit xall xmapclear xmap xmenu xnoremap xnoremenu xunmap xunmenu yank", +built_in:"synIDtrans atan2 range matcharg did_filetype asin feedkeys xor argv complete_check add getwinposx getqflist getwinposy screencol clearmatches empty extend getcmdpos mzeval garbagecollect setreg ceil sqrt diff_hlID inputsecret get getfperm getpid filewritable shiftwidth max sinh isdirectory synID system inputrestore winline atan visualmode inputlist tabpagewinnr round getregtype mapcheck hasmapto histdel argidx findfile sha256 exists toupper getcmdline taglist string getmatches bufnr strftime winwidth bufexists strtrans tabpagebuflist setcmdpos remote_read printf setloclist getpos getline bufwinnr float2nr len getcmdtype diff_filler luaeval resolve libcallnr foldclosedend reverse filter has_key bufname str2float strlen setline getcharmod setbufvar index searchpos shellescape undofile foldclosed setqflist buflisted strchars str2nr virtcol floor remove undotree remote_expr winheight gettabwinvar reltime cursor tabpagenr finddir localtime acos getloclist search tanh matchend rename gettabvar strdisplaywidth type abs py3eval setwinvar tolower wildmenumode log10 spellsuggest bufloaded synconcealed nextnonblank server2client complete settabwinvar executable input wincol setmatches getftype hlID inputsave searchpair or screenrow line settabvar histadd deepcopy strpart remote_peek and eval getftime submatch screenchar winsaveview matchadd mkdir screenattr getfontname libcall reltimestr getfsize winnr invert pow getbufline byte2line soundfold repeat fnameescape tagfiles sin strwidth spellbadword trunc maparg log lispindent hostname setpos globpath remote_foreground getchar synIDattr fnamemodify cscope_connection stridx winbufnr indent min complete_add nr2char searchpairpos inputdialog values matchlist items hlexists strridx browsedir expand fmod pathshorten line2byte argc count getwinvar glob foldtextresult getreg foreground cosh matchdelete has char2nr simplify histget searchdecl iconv winrestcmd pumvisible writefile foldlevel haslocaldir keys cos matchstr foldtext histnr tan tempname getcwd byteidx getbufvar islocked escape eventhandler remote_send serverlist winrestview synstack pyeval prevnonblank readfile cindent filereadable changenr exp" +},illegal:/;/,contains:[e.NUMBER_MODE,{className:"string",begin:"'",end:"'", +illegal:"\\n"},{className:"string",begin:/"(\\"|\n\\|[^"\n])*"/ +},e.COMMENT('"',"$"),{className:"variable",begin:/[bwtglsav]:[\w\d_]+/},{ +begin:[/\b(?:function|function!)/,/\s+/,e.IDENT_RE],className:{1:"keyword", +3:"title"},end:"$",relevance:0,contains:[{className:"params",begin:"\\(", +end:"\\)"}]},{className:"symbol",begin:/<[\w-]+>/}]})})() +;hljs.registerLanguage("vim",e)})();/*! `yaml` grammar compiled for Highlight.js 11.9.0 */ +(()=>{var e=(()=>{"use strict";return e=>{ +const n="true false yes no null",a="[\\w#;/?:@&=+$,.~*'()[\\]]+",s={ +className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/ +},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable", +variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},i=e.inherit(s,{ +variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),l={ +end:",",endsWithParent:!0,excludeEnd:!0,keywords:n,relevance:0},t={begin:/\{/, +end:/\}/,contains:[l],illegal:"\\n",relevance:0},g={begin:"\\[",end:"\\]", +contains:[l],illegal:"\\n",relevance:0},b=[{className:"attr",variants:[{ +begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{ +begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---\\s*$", +relevance:10},{className:"string", +begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{ +begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0, +relevance:0},{className:"type",begin:"!\\w+!"+a},{className:"type", +begin:"!<"+a+">"},{className:"type",begin:"!"+a},{className:"type",begin:"!!"+a +},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta", +begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)", +relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{ +className:"number", +begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b" +},{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},t,g,s],r=[...b] +;return r.pop(),r.push(i),l.contains=r,{name:"YAML",case_insensitive:!0, +aliases:["yml"],contains:b}}})();hljs.registerLanguage("yaml",e)})(); \ No newline at end of file