diff --git a/etc/soft/nvim/+plugins/vim-floaterm/README.md b/etc/soft/nvim/+plugins/vim-floaterm/README.md index 3578523..8dcd120 100644 --- a/etc/soft/nvim/+plugins/vim-floaterm/README.md +++ b/etc/soft/nvim/+plugins/vim-floaterm/README.md @@ -1,10 +1,57 @@ -# vim-floaterm +![CI](https://github.com/voldikss/vim-floaterm/workflows/CI/badge.svg) + +【[Introduction in Chinese|中文文档](https://zhuanlan.zhihu.com/p/107749687)】 + +Use (neo)vim terminal in the floating/popup window. + +![](https://user-images.githubusercontent.com/20282795/74799912-de268200-530c-11ea-9831-d412a7700505.png) + +- [Features](#features) +- [Requirements](#requirements) +- [Installation](#installation) +- [Basic Usage](#basic-usage) + - [Commands](#commands) + - [Global variables](#global-variables) + - [Keymaps](#keymaps) + - [Change highlight](#change-highlight) +- [More use cases and demos](#more-use-cases-and-demos) + - [General](#general) + - [Use as the git editor](#use-as-the-git-editor) + - [Use as an fzf plugin](#use-as-an-fzf-plugin) + - [Use as an fff plugin](#use-as-an-fff-plugin) + - [Use as an nnn plugin](#use-as-an-nnn-plugin) + - [Use as an lf plugin](#use-as-an-lf-plugin) + - [Use as a ranger plugin](#use-as-a-ranger-plugin) + - [Use as a vifm plugin](#use-as-a-vifm-plugin) + - [Use as a Python REPL plugin](#use-as-a-python-repl-plugin) + - [Use with other command line tools](#use-with-other-command-line-tools) + - [Integrate with vim-clap](#integrate-with-vim-clap) + - [Integrate with denite.nvim](#integrate-with-denitenvim) + - [Integrate with coc.nvim](#integrate-with-cocnvim) + - [Integrate with asynctasks.vim](#integrate-with-asynctasksvim) +- [How to define more wrappers](#how-to-define-more-wrappers) +- [How to write sources for fuzzy finder plugins](#how-to-write-sources-for-fuzzy-finder-plugins) +- [APIs](#apis) +- [F.A.Q](#f.a.q) +- [Break Changes](#break-changes) +- [Credits](#credits) +- [License](#license) -[![Build Status](https://travis-ci.org/voldikss/vim-floaterm.svg?branch=master)](https://travis-ci.org/voldikss/vim-floaterm) +## Features + +- NeoVim floatwin and Vim8 popup support +- Open and toggle terminal window quickly +- Multiple terminal instances +- Customizable floating terminal style +- Switch/Preview floating terminal buffers using [vim-clap](https://github.com/liuchengxu/vim-clap), [denite.nvim](https://github.com/Shougo/denite.nvim) or [coc.nvim](https://github.com/neoclide/coc.nvim) +- Integrate with other external command-line tools(ranger, lf, fzf, etc.) +- Use as a custom task runner for [asynctasks.vim](https://github.com/skywind3000/asynctasks.vim) + +## Requirements -Use neovim terminal in the floating window. +- Vim or NeoVim with `terminal` feature -![](https://user-images.githubusercontent.com/20282795/71553203-f59c6980-2a45-11ea-88f2-747e938f7f49.gif) +Run `:checkhealth` to check the environment. ## Installation @@ -20,92 +67,412 @@ Plug 'voldikss/vim-floaterm' call dein#add('voldikss/vim-floaterm') ``` -## Keymaps +## Basic Usage + +Use `:FloatermNew` command to open a terminal window, use `:FloatermToggle` to hide/reopen that. The filetype of the terminal buffer is set to `floaterm`. + +If you've opened multiple floaterm instances, they will be attached to a double-circular-linkedlist. Then you can use `:FloatermNext` or `:FloatermPrev` to switch between them. + +### Commands + +#### `:FloatermNew [options] [cmd]` Open a floaterm window. + +- If `cmd` exists, it will be executed automatically after the shell startup. +- The `options` is formed as `key=value`, it is used to specify some attributes of the floaterm instance, including `height`, `width`, `wintype`, `position` and `name`. + - `height` see `g:floaterm_height` + - `width` see `g:floaterm_width` + - `wintype` see `g:floaterm_wintype` + - `position` see `g:floaterm_position` + - `name` name of the floaterm +- Use `` to get completion. + +For example, command + +```vim +:FloatermNew height=0.6 width=0.4 wintype=floating name=floaterm1 position=topleft ranger --cmd="cd ~" +``` + +will open a new `floating` floaterm instance named `floaterm1` running `ranger --cmd="cd ~"` in the `topleft` corner of the main window. + +#### `:FloatermUpdate [options]` Update floaterm window attributes(`height`, `width`, etc.). + +- The `options` is the same as in `:FloatermNew`. +- Use `` to get completion. + +#### `:FloatermToggle [floaterm_name]` Open or hide the floaterm window. + +- If `floaterm_name` exists, toggle the floaterm instance whose `name` attribute is `floaterm_name`. +- Use `` to get completion. + +#### `:FloatermPrev` Switch to the previous floaterm instance + +#### `:FloatermNext` Switch to the next floaterm instance + +#### `:FloatermHide` Hide all visible floaterms + +#### `:'<,'>FloatermSend [floaterm_name]` Send selected lines to a job in floaterm. + +- If `floaterm_name` exists, send to the floaterm instance whose `name` is `floaterm_name`. +- Use `` to get completion. + +Note: This command must be executed with a range! + +Also you may try `:'<,'>FloatermSend!`, which trims the whitespace in the begin of the line, and the latter removes the whitespace but still keeps the indent. + +### Global variables + +#### **`g:floaterm_wintype`** + +Type `string`. `'floating'`(neovim) or `'popup'`(vim) by default. Set it to `'normal'` if your vim/nvim doesn't support `floatwin` or `popup`. + +#### **`g:floaterm_wintitle`** + +Type `bool`. Whether to show floaterm info(e.g., `'floaterm: 1/3'`) at the top left corner of floaterm window. Default: `v:true` + +#### **`g:floaterm_width`** + +Type `int` (number of columns) or `float` (between 0 and 1). If `float`, the width is relative to `&columns`. Default: `0.6` + +#### **`g:floaterm_height`** + +Type `int` (number of lines) or `float` (between 0 and 1). If `float`, the height is relative to `&lines`. Default: `0.6` + +#### **`g:floaterm_winblend`** + +Type `int`. The transparency of the floating terminal. Only works in neovim. Default: `0` + +#### **`g:floaterm_position`** + +Type `string`. The position of the floating window. Available values: + +- If `wintype` is `normal`: `'top'`, `'right'`, `'bottom'`, `'left'`. Default: `'bottom'` +- If `wintype` is `floating` or `popup`: `'top'`, `'right'`, `'bottom'`, `'left'`, `'center'`, `'topleft'`, `'topright'`, `'bottomleft'`, `'bottomright'`, `'auto'(at the cursor place)`. Default: `'center'` + +#### **`g:floaterm_borderchars`** + +Type `array of string`. Characters of the floating window border. -This plugin doesn't supply any default mappings. +Default: `['─', '│', '─', '│', '┌', '┐', '┘', '└']` + +#### **`g:floaterm_rootmarkers`** + +Type `array of string`. If not empty, floaterm will be opened in the project root directory. + +Example: `['.project', '.git', '.hg', '.svn', '.root', '.gitignore']`, Default: `[]` + +#### **`g:floaterm_autoinsert`** + +Type `bool`. Enter terminal mode after opening a floaterm. Default: `v:true` + +#### **`g:floaterm_open_command`** + +Type `string`. Command used for opening a file from within `:terminal`. + +Available: `'edit'`, `'split'`, `'vsplit'`, `'tabe'`, `'drop'`. Default: `'edit'` + +#### **`g:floaterm_gitcommit`** + +Type `string`. Opening strategy for running `git commit` in floaterm window. Only works in neovim. + +Available: `'floaterm'`(open `gitcommit` file in the floaterm window), `'split'`, `'vsplit'`, `'tabe'`. + +Default: `v:null` which means this is disabled by default(use your own `$GIT_EDITOR`). + +### Keymaps + +This plugin doesn't supply any default mappings. To use a recommended mappings, put the following code in your `vimrc`. ```vim -""" Example configuration +""" Configuration example let g:floaterm_keymap_new = '' let g:floaterm_keymap_prev = '' let g:floaterm_keymap_next = '' let g:floaterm_keymap_toggle = '' ``` -## Features +You can also use other keys as shown below: -- Toggle terminal window quickly -- Multiple terminal instances -- Customizable floating terminal style -- Switch/Preview floating terminal buffer using [vim-clap](https://github.com/liuchengxu/vim-clap)(try `:Clap floaterm`) +```vim +let g:floaterm_keymap_new = 'fn' +``` -## Configurations +Note that this key mapping is installed from the [plugin](./plugin) directory, so if you use on-demand loading provided by some plugins-managers, the keymap above won't take effect(`:help load-plugins`). Then you have to define the key bindings yourself by putting the code used to define the key bindings in your `vimrc`. For example, -#### **`g:floaterm_type`** +```vim +nnoremap :FloatermNew +tnoremap :FloatermNew +nnoremap :FloatermPrev +tnoremap :FloatermPrev +nnoremap :FloatermNext +tnoremap :FloatermNext +nnoremap :FloatermToggle +tnoremap :FloatermToggle +``` -- Available: `'floating'`(neovim only), `'normal'`(vim8 and neovim) +### Change highlight -- Default: `'floating'` +This plugin provides two `highlight-groups` to specify the background/foreground color of floaterm (also the border color if `g:floaterm_wintype` is `'floating'` or `'popup'`) window. -#### **`g:floaterm_width`** +By default, they are both linked to `Normal`. To customize, use `hi` command together with the colors you prefer. + +```vim +" Configuration example -- Type: `int` (number of columns) or `float` (between 0 and 1). If `float`, the width is relative to `&columns`. -- Default: `0.6` +" Set floaterm window's background to black +hi Floaterm guibg=black +" Set floating window border line color to cyan, and background to orange +hi FloatermBorder guibg=orange guifg=cyan +``` -#### **`g:floaterm_height`** +![](https://user-images.githubusercontent.com/20282795/74794098-42d9e080-52fd-11ea-9ccf-661dd748aa03.png) -- Type: `int` (number of lines) or `float` (between 0 and 1). If `float`, the height is relative to `&lines`. -- Default: `0.6` +## More use cases and demos -#### `g:floaterm_winblend` +vim-floaterm is a nvim/vim terminal plugin, it can run all the command-line programs in the terminal even `nvim/vim` itself. -- Description: The opacity of the floating terminal +**❗️Note**: The following cases should work both in Vim and NeoVim unless otherwise specifically noted. -- Default: `0` +### General -#### **`g:floaterm_position`** +Requirements: For neovim users, `nvr` is required, please install it via pip using `pip3 install neovim-remote`. -- Available: `'center'`, `'topleft'`, `'topright'`, `'bottomleft'`, `'bottomright'`, `'auto'(at the cursor place)` +Normally if you run `vim/nvim somefile.txt` within a builtin terminal, you will get another nvim/vim instance running in the subprocess. This plugin allows you to open files from within `:terminal` without starting a nested nvim process. To archive that, just replace `vim/nvim` with `floaterm`, i.e., `floaterm somefile.txt` -- Default: `'center'` +![](https://user-images.githubusercontent.com/20282795/74755351-06cb5f00-52ae-11ea-84ba-d0b3e88e9377.gif) -#### **`g:floaterm_background`** +### Use as the git editor -- Type: string(e.g. `'#000000'`, `'black'`) +See `g:floaterm_gitcommit` option. -- Default: background color of normal floating window +Execute `git commit` in the terminal window without starting a nested nvim. -#### **`g:floaterm_borderchars`** +![](https://user-images.githubusercontent.com/20282795/76213003-b0b26180-6244-11ea-85ad-1632adfd07d9.gif) + +### Use as an fzf plugin + +This plugin has implemented a [wrapper](./autoload/floaterm/wrapper/fzf.vim) for fzf command. So it can be used as a tiny fzf plugin. + +Try `:FloatermNew fzf` or even wrap this to a new command like this: + +```vim +command! FZF FloatermNew fzf +``` + +![](https://user-images.githubusercontent.com/20282795/78089550-60b95b80-73fa-11ea-8ac8-8fab2025b4d8.gif) + +### Use as an fff plugin + +There is also an [fff wrapper](./autoload/floaterm/wrapper/fff.vim) + +Try `:FloatermNew fff` or define a new command: + +```vim +command! FFF FloatermNew fff +``` + +![](https://user-images.githubusercontent.com/1472981/75105718-9f315d00-567b-11ea-82d1-6f9a6365391f.gif) + +### Use as an nnn plugin + +There is also an [nnn wrapper](./autoload/floaterm/wrapper/nnn.vim) + +Try `:FloatermNew nnn` or define a new command: + +```vim +command! NNN FloatermNew nnn +``` + +![](https://user-images.githubusercontent.com/20282795/75599726-7a594180-5ae2-11ea-80e2-7a33df1433f6.gif) + +### Use as an lf plugin + +There is also an [lf wrapper](./autoload/floaterm/wrapper/lf.vim) + +Try `:FloatermNew lf` or define a new command: + +```vim +command! LF FloatermNew lf +``` + +![](https://user-images.githubusercontent.com/20282795/77142551-6e4a1980-6abb-11ea-9525-73e1a1844e83.gif) + +### Use as a ranger plugin + +This plugin can also be a handy ranger plugin since it also has a [ranger wrapper](./autoload/floaterm/wrapper/ranger.vim) + +Try `:FloatermNew ranger` or define a new command: + +```vim +command! Ranger FloatermNew ranger +``` + +![](https://user-images.githubusercontent.com/20282795/74800026-2e054900-530d-11ea-8e2a-67168a9532a9.gif) + +### Use as a Vifm plugin + +There is also a [vifm wrapper](./autoload/floaterm/wrapper/vifm.vim) + +Try `:FloatermNew vifm` or define a new command: + +```vim +command! Vifm FloatermNew vifm +``` + +![](https://user-images.githubusercontent.com/43941510/77137476-3c888100-6ac2-11ea-90f2-2345c881aa8f.gif) + +### Use as a Python REPL plugin + +Use `:FloatermNew python` to open a python shell. After that you can use `:FloatermSend` to send lines to the Python interactive shell. + +This can also work for other languages which have interactive shells, such as lua, node, etc. + +![](https://user-images.githubusercontent.com/20282795/78530892-0c0d4a80-7817-11ea-8934-835a6e6d0628.gif) + +### Use with other command line tools + +Furthermore, you can also use other command-line programs, such as lazygit, htop, ncdu, etc. + +Use `lazygit` for instance: + +![](https://user-images.githubusercontent.com/20282795/74755376-0f239a00-52ae-11ea-9261-44d94abe5924.png) + +### Integrate with [vim-clap](https://github.com/liuchengxu/vim-clap) + +Use vim-clap to switch/preview floating terminal buffers. + +Try `:Clap floaterm` + +![](https://user-images.githubusercontent.com/20282795/74755336-00d57e00-52ae-11ea-8afc-030ff55c2145.gif) + +### Integrate with [denite.nvim](https://github.com/Shougo/denite.nvim) + +Use denite to switch/preview/open floating terminal buffers. + +Try `:Denite floaterm` + +![](https://user-images.githubusercontent.com/1239245/73604753-17ef4d00-45d9-11ea-967f-ef75927e2beb.gif) + +### Integrate with [coc.nvim](https://github.com/neoclide/coc.nvim) + +Use CocList to switch/preview/open floating terminal buffers. + +Install [coc-floaterm](https://github.com/voldikss/coc-floaterm) and try `:CocList floaterm` + +![](https://user-images.githubusercontent.com/20282795/75005925-fcc27f80-54aa-11ea-832e-59ea5b02fc04.gif) + +### Integrate with [asynctasks.vim](https://github.com/skywind3000/asynctasks.vim) + +This plugin can be a runner for [asynctasks.vim](https://github.com/skywind3000/asynctasks.vim/). To use it, copy the following code to your `vimrc` set `g:asynctasks_term_pos` to `"floaterm"` or add a `"pos=floaterm"` filed in your asynctasks configuration files. -- Default: `['─', '│', '─', '│', '┌', '┐', '┘', '└']` +```vim +function! s:runner_proc(opts) + let curr_bufnr = floaterm#curr() + if has_key(a:opts, 'silent') && a:opts.silent == 1 + call floaterm#hide() + endif + let cmd = 'cd ' . shellescape(getcwd()) + call floaterm#terminal#send(curr_bufnr, [cmd]) + call floaterm#terminal#send(curr_bufnr, [a:opts.cmd]) + stopinsert + if &filetype == 'floaterm' && g:floaterm_autoinsert + call floaterm#util#startinsert() + endif +endfunction + +let g:asyncrun_runner = get(g:, 'asyncrun_runner', {}) +let g:asyncrun_runner.floaterm = function('s:runner_proc') +``` + +Then your task will be ran in the floaterm instance. See asynctasks.vim [Wiki](https://github.com/skywind3000/asynctasks.vim/wiki/Customize-Runner) for more information. + +## How to define more wrappers + +There are two ways for a command to be spawned: + +- To be executed after `&shell` was startup. see [fzf wrapper](./autoload/floaterm/wrapper/fzf.vim) + + ```vim + function! floaterm#wrapper#fzf#() abort + return ['floaterm $(fzf)', {}, v:true] + endfunction + ``` + + The code above returns an array. `floaterm $(fzf)` is the command to be executed. `v:true` means the command will be executed after the `&shell` startup. + +- To be executed through `termopen()`/`term_start()` function, in that case, a callback function is can be provided. See [ranger wrapper](./autoload/floaterm/wrapper/ranger.vim) + + ```vim + function! floaterm#wrapper#ranger#() abort + let s:ranger_tmpfile = tempname() + let cmd = 'ranger --choosefiles=' . s:ranger_tmpfile + return [cmd, {'on_exit': funcref('s:ranger_callback')}, v:false] + endfunction + + function! s:ranger_callback(...) abort + if filereadable(s:ranger_tmpfile) + let filenames = readfile(s:ranger_tmpfile) + if !empty(filenames) + for filename in filenames + execute 'edit ' . fnameescape(filename) + endfor + endif + endif + endfunction + ``` + + Here `v:false` means `cmd` will be passed through `termopen()`(neovim) or `term_start()`(vim). Function `s:ranger_callback()` will be invoked when `cmd` exits. + +## How to write sources for fuzzy finder plugins + +Function `floaterm#buflist#gather()` returns a list contains all the floaterm buffers. + +Function `floaterm#terminal#open({bufnr})` opens the floaterm whose buffer number is `bufnr`. + +For reference, see [floaterm source for vim-clap](./autoload/clap/provider/floaterm.vim). -#### **`g:floaterm_border_color`** +## APIs -- Type: string(e.g. `'#FFFFFF'`, `'blue'`) +- `floaterm#new(cmd, win_opts, job_opts)` create a new floaterm instance and return the bufnum -- Default: foreground color of normal floating window + - `cmd` type `string`, if empty(`''`), will use `&shell` + - `win_opts` type `dict`. See [FloatermNew options](#floatermnew-options-cmd-open-a-floaterm-window), e.g., `{'name': 'floaterm1', 'wintype': 'floating', 'position': 'top'}` + - `job_opts` type `dict`. For reference, see `:help job-options`(for vim) or `:help jobstart-options`(for nvim) -## Commands +- `floaterm#update(win_opts)` update floaterm window attributes -- `:FloatermNew` +- `floaterm#toggle(name)` toggle on/off a floaterm -- `:FloatermToggle` + - `name` name of the floaterm, if empty(`''`), toggle the current floaterm -- `:FloatermPrev` +- `floaterm#prev()` switch to the previous floaterm buffeum and return the bufnum -- `:FloatermNext` +- `floaterm#next()` switch to the next floaterm buffer and return the bufnum -## Q & A +- `floaterm#curr()` return current floaterm buffer number -- #### This plugin leaves an empty buffer on startify window +- `floaterm#hide()` hide all visible floaterms - Put this code in `vimrc` +- `floaterm#window#hide_floaterm(bufnr)` hide the floaterm whose bufnum is `bufnr` + +- `floaterm#terminal#send(bufnr, cmds)` send commands to a terminal whose bufnum is `bufnr` + + - `cmd`: a list contains some commands + +- `floaterm#window#open_floating(bufnr, width, height, pos)` open a generic floating window with a border, return window id + +There are some other functions which can be served as APIs, for detail infomation, go and check source files yourself. + +## F.A.Q + +- #### This plugin leaves an empty buffer/window on startify window + + Put this code in your `vimrc` ```vim autocmd User Startified setlocal buflisted ``` -- #### I want to use another shell in the terminal. (e.g. Use fish instead of bash) +- #### I want to use another shell in the terminal. (e.g., Use fish instead of bash) Set `shell` option in your `vimrc`: @@ -113,7 +480,7 @@ let g:floaterm_keymap_toggle = '' set shell=/path/to/shell ``` -- #### I would like to customize the style of the floating terminal window +- #### I would like to customize the style of the floaterm window Use `autocmd`. For example @@ -125,3 +492,36 @@ let g:floaterm_keymap_toggle = '' autocmd FileType floaterm call s:floatermSettings() ``` + +- #### I want to open normal(non-floating) floaterm in a vsplit window. + + Use `:FloatermUpdate` + + ```vim + :FloatermUpdate wintype=normal position=right + ``` + +- #### Can not enter insert mode after creating a new floaterm... + + See option [g:floaterm_autoinsert](#gfloaterm_autoinsert), also [#52](https://github.com/voldikss/vim-floaterm/issues/52) might be helpful. + +- #### Why the plugin is named "vim-floaterm" instead of "vim-popterm" or others? + + Because this was firstly developed based on nvim's floating window. But now it supports both floaterm and popup, you can get similar experience in both. + +## Break Changes + +- Command `FloatermSend` must be used with a range +- Rename: `g:floaterm_type` --> `g:floaterm_wintype` +- Rename: `FloatermNF` --> `Floaterm` +- Rename: `FloatermBorderNF` --> `FloatermBorder` + +## Credits + +- [floaterm executable](https://github.com/voldikss/vim-floaterm/blob/master/bin/floaterm) is modified from [vim-terminal-help](https://github.com/skywind3000/vim-terminal-help/blob/master/tools/utils/drop) + +- Some features require [neovim-remote](https://github.com/mhinz/neovim-remote) + +## License + +MIT diff --git a/etc/soft/nvim/+plugins/vim-floaterm/autoload/clap/provider/floaterm.vim b/etc/soft/nvim/+plugins/vim-floaterm/autoload/clap/provider/floaterm.vim new file mode 100644 index 0000000..e22ba19 --- /dev/null +++ b/etc/soft/nvim/+plugins/vim-floaterm/autoload/clap/provider/floaterm.vim @@ -0,0 +1,44 @@ +" vim:sw=2: +" ============================================================================ +" FileName: floaterm.vim +" Description: +" Author: voldikss +" GitHub: https://github.com/voldikss +" ============================================================================ + +let s:floaterm = {} +let s:preview_height = 10 +let s:bar = '[bufnr] [name]' + +function! s:floaterm.source() abort + let candidates = [s:bar] + let bufs = floaterm#buflist#gather() + for bufnr in bufs + let bufinfo = getbufinfo(bufnr)[0] + let name = bufinfo['name'] + let title = getbufvar(bufnr, 'term_title') + let line = printf(' %s %s %s', bufnr, name, title) + call add(candidates, line) + endfor + return candidates +endfunction + +function! s:floaterm.on_move() abort + let curline = g:clap.display.getcurline() + if curline == s:bar + return + endif + let bufnr = str2nr(matchstr(curline, '\S')) + let lnum = getbufinfo(bufnr)[0]['lnum'] + let lines = getbufline(bufnr, max([lnum-s:preview_height, 0]), '$') + let lines = lines[max([len(lines)-s:preview_height, 0]):] + call g:clap.preview.show(lines) +endfunction + +function! s:floaterm.sink(curline) abort + if a:curline == s:bar | return | endif + let bufnr = str2nr(matchstr(a:curline, '\S')) + call floaterm#terminal#open_existing(bufnr) +endfunction + +let g:clap#provider#floaterm# = s:floaterm diff --git a/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm.vim b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm.vim index f545bab..46e8eb3 100644 --- a/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm.vim +++ b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm.vim @@ -1,323 +1,220 @@ +" vim:sw=2: " ============================================================================ -" FileName: autocmd/floaterm.vim -" Description: +" FileName: floaterm.vim " Author: voldikss " GitHub: https://github.com/voldikss " ============================================================================ -" `hidden` option must be set, otherwise the floating terminal would be wiped -" out, see #17 -set hidden +"----------------------------------------------------------------------------- +" script level variables and environment variables +"----------------------------------------------------------------------------- +let $VIM_SERVERNAME = v:servername +let $VIM_EXE = v:progpath -" Note: -" The data structure of the floaterm chain is a double circular linkedlist -" g:floaterm.count is the count of the terminal node -" g:floaterm.index is the pointer -" g:floaterm.head is the HEAD node which only have 'prev' and 'next' -" g:floaterm_node is the node prototype to create a terminal node -let g:floaterm = {} -let g:floaterm.count = 0 -let g:floaterm.head = {} -let g:floaterm.head.next = g:floaterm.head -let g:floaterm.head.prev = g:floaterm.head -let g:floaterm.index = g:floaterm.head +let s:home = fnamemodify(resolve(expand(':p')), ':h') +let s:script = fnamemodify(s:home . '/../bin', ':p') +let s:wrappers = fnamemodify(s:home . '/floaterm/wrapper', ':p') +let s:windows = has('win32') || has('win64') -let g:floaterm_node = { - \ 'bufnr': 0, - \ 'border_bufnr': 0, - \ 'next': v:null, - \ 'prev': v:null - \ } - -if g:floaterm_border_color == v:null - let g:floaterm_border_color = floaterm#util#get_normalfloat_fg() +if stridx($PATH, s:script) < 0 + if s:windows == 0 + let $PATH .= ':' . s:script + else + let $PATH .= ';' . s:script + endif endif -if g:floaterm_background == v:null - let g:floaterm_background = floaterm#util#get_normalfloat_bg() +if g:floaterm_gitcommit != v:null + autocmd FileType gitcommit,gitrebase,gitconfig set bufhidden=delete + if g:floaterm_gitcommit == 'floaterm' + let $GIT_EDITOR = 'nvr --remote-wait' + else + let $GIT_EDITOR = printf( + \ 'nvr -cc "call floaterm#hide() | %s" --remote-wait', + \ g:floaterm_gitcommit + \ ) + endif endif -" Remove a node if it was closed(the buffer doesn't exist) -function! g:floaterm.kickout() dict abort - if self.count == 0 | return | endif - let self.index.prev.next = self.index.next - let self.index.next.prev = self.index.prev - let self.count -= 1 +"----------------------------------------------------------------------------- +" script level functions +"----------------------------------------------------------------------------- +function! s:get_wrappers() abort + let files = split(glob(s:wrappers . '/*.vim'), "\n") + return map(files, "substitute(fnamemodify(v:val, ':t'), '\\..\\{-}$', '', '')") endfunction -function! g:floaterm.toggle() dict abort - let found_winnr = self.find_term_win() - if found_winnr > 0 - if &buftype ==# 'terminal' - execute found_winnr . ' wincmd q' - else - execute found_winnr . ' wincmd w | startinsert' - endif - else - while v:true - if self.count == 0 - call self.open(0) - return - endif - " If the current node is HEAD(which doesn't have 'bufnr' key), - " skip and point to the node after HEAD - if self.index == self.head - let self.index = self.head.next - endif - let found_bufnr = self.index.bufnr - if found_bufnr != 0 && bufexists(found_bufnr) - call self.open(found_bufnr) - return - else - call self.kickout() - let self.index = self.index.next - endif - endwhile +" ---------------------------------------------------------------------------- +" wrapper function for `floaterm#new()` and `floaterm#update()` since they +" share the same argument: `winopts` +" ---------------------------------------------------------------------------- +function! floaterm#run(action, ...) abort + if a:action == 'new' + let [cmd, winopts] = floaterm#cmdline#parse(a:000) + call floaterm#new(cmd, winopts, {}) + elseif a:action == 'update' + let [_, winopts] = floaterm#cmdline#parse(a:000) + call floaterm#update(winopts) endif endfunction -function! g:floaterm.new() dict abort - call self.hide() - call self.open(0) -endfunction - -function! g:floaterm.next() dict abort - call self.hide() - while v:true - if self.count == 0 - call floaterm#util#show_msg('No more terminal buffers', 'warning') - return - endif - " If the current node is the end node(whose next node is HEAD), - " skip and point to the HEAD's next node - if self.index.next == self.head - let self.index = self.head.next - else - let self.index = self.index.next - endif - let next_bufnr = self.index.bufnr - if next_bufnr != 0 && bufexists(next_bufnr) - call self.open(next_bufnr) - return +" ---------------------------------------------------------------------------- +" create a floaterm. `jobopts` is not used inside this pugin actually, it's +" reserved for outer invoke +" ---------------------------------------------------------------------------- +function! floaterm#new(cmd, winopts, jobopts) abort + if a:cmd != '' + let wrappers = s:get_wrappers() + let maybe_wrapper = split(a:cmd, '\s')[0] + if index(wrappers, maybe_wrapper) >= 0 + let WrapFunc = function(printf('floaterm#wrapper#%s#', maybe_wrapper)) + let [name, jobopts, send2shell] = WrapFunc(a:cmd) + if send2shell + let bufnr = floaterm#terminal#open(-1, &shell, {}, a:winopts) + call floaterm#terminal#send(bufnr, [name]) + else + let bufnr = floaterm#terminal#open(-1, name, jobopts, a:winopts) + endif else - call self.kickout() + let bufnr = floaterm#terminal#open(-1, &shell, a:jobopts, a:winopts) + call floaterm#terminal#send(bufnr, [a:cmd]) endif - endwhile + else + let bufnr = floaterm#terminal#open(-1, &shell, a:jobopts, a:winopts) + endif + return bufnr endfunction -function! g:floaterm.prev() dict abort - call self.hide() - while v:true - if self.count == 0 - call floaterm#util#show_msg('No more terminal buffers', 'warning') - return - endif - " If the current node is the node after HEAD(whose previous node is HEAD), - " skip and point to the HEAD's prev node(the end node) - if self.index.prev == self.head - let self.index = self.head.prev - else - let self.index = self.index.prev - endif - let prev_bufnr = self.index.bufnr - if prev_bufnr != 0 && bufexists(prev_bufnr) - call self.open(prev_bufnr) +" ---------------------------------------------------------------------------- +" toggle on/off the floaterm named `name` +" ---------------------------------------------------------------------------- +function! floaterm#toggle(name) abort + if a:name != '' + let bufnr = floaterm#terminal#get_bufnr(a:name) + if bufnr == -1 + call floaterm#util#show_msg('No floaterm found with name: ' . a:name, 'error') return + elseif bufnr == bufnr('%') + call floaterm#window#hide_floaterm(bufnr) + elseif bufwinnr(bufnr) > -1 + execute bufwinnr(bufnr) . 'wincmd w' else - call self.kickout() + call floaterm#terminal#open_existing(bufnr) endif - endwhile -endfunction - -" Hide the current terminal before opening another terminal window -" Therefore, you cannot have two terminals displayed at once -function! g:floaterm.hide() dict abort - while v:true - let found_winnr = self.find_term_win() + elseif &filetype == 'floaterm' + call floaterm#window#hide_floaterm(bufnr('%')) + else + let found_winnr = floaterm#window#find_floaterm_window() if found_winnr > 0 - execute found_winnr . ' wincmd q' + execute found_winnr . 'wincmd w' + call floaterm#util#startinsert() else - break + call floaterm#curr() endif - endwhile -endfunction - -" Find if there is a terminal among all opened windows -" If found, hide it or jump into it -function! g:floaterm.find_term_win() abort - let found_winnr = 0 - for winnr in range(1, winnr('$')) - if getbufvar(winbufnr(winnr), '&filetype') ==# 'floaterm' - let found_winnr = winnr - endif - endfor - return found_winnr + endif endfunction -function! g:floaterm.open(found_bufnr) dict abort - let height = g:floaterm_height == v:null ? 0.6 : g:floaterm_height - if type(height) == v:t_float | let height = height * &lines | endif - let height = float2nr(height) +" ---------------------------------------------------------------------------- +" update the attributes of a floaterm +" ---------------------------------------------------------------------------- +function! floaterm#update(winopts) abort + if &filetype !=# 'floaterm' + call floaterm#util#show_msg('You have to be in a floaterm window to change window opts.', 'error') + return + endif - let width = g:floaterm_width == v:null ? 0.6 : g:floaterm_width - if type(width) == v:t_float | let width = width * &columns | endif - let width = float2nr(width) + let bufnr = bufnr('%') + call floaterm#window#hide_floaterm(bufnr) + call floaterm#buffer#update_winopts(bufnr, a:winopts) + call floaterm#terminal#open_existing(bufnr) +endfunction - if g:floaterm_type ==# 'floating' - let [bufnr, border_bufnr] = s:open_floating_terminal(a:found_bufnr, height, width) +function! floaterm#next() abort + call floaterm#window#hide_floaterm(bufnr('%')) + let next_bufnr = floaterm#buflist#find_next() + if next_bufnr == -1 + let msg = 'No more floaterms' + call floaterm#util#show_msg(msg, 'warning') else - let bufnr = s:open_floating_normaml(a:found_bufnr, height, width) - let border_bufnr = 0 - endif - if bufnr != 0 - " Build a terminal node - let node = deepcopy(g:floaterm_node) - let node.bufnr = bufnr - let node.prev = self.index - let node.next = self.index.next - " If current node is the end node, let HEAD's prev point to the new node - if self.index.next == self.head - let self.head.prev = node - endif - let self.index.next = node - let self.index = self.index.next - let self.count += 1 + call floaterm#terminal#open_existing(next_bufnr) endif - if border_bufnr != 0 - let self.index.border_bufnr = border_bufnr - endif - call s:on_open() endfunction -function! s:on_open() abort - setlocal cursorline - setlocal filetype=floaterm - - " Find the true background(not 'hi link') for floating - if has('nvim') - execute 'setlocal winblend=' . g:floaterm_winblend - execute 'hi FloatTermNormal term=NONE guibg='. g:floaterm_background - setlocal winhighlight=NormalFloat:FloatTermNormal,FoldColumn:FloatTermNormal - - augroup close_floaterm_window - autocmd! - autocmd TermClose if &filetype ==# 'floaterm' | - \ bdelete! | - \ endif - autocmd TermClose,BufHidden if exists('g:floaterm.index.border_bufnr') - \ && bufexists(g:floaterm.index.border_bufnr) - \ && g:floaterm.index.border_bufnr != 0 | - \ execute 'bw ' . g:floaterm.index.border_bufnr | - \ endif - augroup END +function! floaterm#prev() abort + call floaterm#window#hide_floaterm(bufnr('%')) + let prev_bufnr = floaterm#buflist#find_prev() + if prev_bufnr == -1 + let msg = 'No more floaterms' + call floaterm#util#show_msg(msg, 'warning') + else + call floaterm#terminal#open_existing(prev_bufnr) endif - - startinsert endfunction -function! s:open_floating_terminal(found_bufnr, height, width) abort - let [row, col, vert, hor] = floaterm#util#floating_win_pos(a:width, a:height) - - let border_opts = { - \ 'relative': 'editor', - \ 'anchor': vert . hor, - \ 'row': row, - \ 'col': col, - \ 'width': a:width + 2, - \ 'height': a:height + 2, - \ 'style':'minimal' - \ } - let top = g:floaterm_borderchars[4] . - \ repeat(g:floaterm_borderchars[0], a:width) . - \ g:floaterm_borderchars[5] - let mid = g:floaterm_borderchars[3] . - \ repeat(' ', a:width) . - \ g:floaterm_borderchars[1] - let bot = g:floaterm_borderchars[7] . - \ repeat(g:floaterm_borderchars[2], a:width) . - \ g:floaterm_borderchars[6] - let lines = [top] + repeat([mid], a:height) + [bot] - let border_bufnr = nvim_create_buf(v:false, v:true) - call nvim_buf_set_option(border_bufnr, 'synmaxcol', 3000) " #27 - call nvim_buf_set_lines(border_bufnr, 0, -1, v:true, lines) - call nvim_open_win(border_bufnr, v:false, border_opts) - " Floating window border highlight - augroup floaterm_border_highlight - autocmd! - autocmd FileType floaterm_border ++once execute printf( - \ 'syn match Border /.*/ | hi Border guibg=%s guifg=%s', - \ g:floaterm_background, - \ g:floaterm_border_color - \ ) - augroup END - call nvim_buf_set_option(border_bufnr, 'filetype', 'floaterm_border') - - "" - " TODO: - " Use 'relative': 'cursor' for the border window - " Use 'relative':'win'(which behaviors not as expected...) for content window - let opts = { - \ 'relative': 'editor', - \ 'anchor': vert . hor, - \ 'row': row + (vert ==# 'N' ? 1 : -1), - \ 'col': col + (hor ==# 'W' ? 1 : -1), - \ 'width': a:width, - \ 'height': a:height, - \ 'style':'minimal' - \ } - - if a:found_bufnr > 0 - call nvim_open_win(a:found_bufnr, v:true, opts) - return [0, border_bufnr] +function! floaterm#curr() abort + let curr_bufnr = floaterm#buflist#find_curr() + if curr_bufnr == -1 + let curr_bufnr = floaterm#new('', {}, {}) else - let bufnr = nvim_create_buf(v:false, v:true) - call nvim_open_win(bufnr, v:true, opts) - terminal - return [bufnr, border_bufnr] + call floaterm#terminal#open_existing(curr_bufnr) endif + return curr_bufnr endfunction -function! s:open_floating_normaml(found_bufnr, height, width) abort - if a:found_bufnr > 0 - if &lines > 30 - execute 'botright ' . a:height . 'split' - execute 'buffer ' . a:found_bufnr - else - botright split - execute 'buffer ' . a:found_bufnr - endif - return - else - if &lines > 30 - if has('nvim') - execute 'botright ' . a:height . 'split term://' . &shell - else - botright terminal - resize a:height - endif - else - if has('nvim') - execute 'botright split term://' . &shell - else - botright terminal - endif - endif - return bufnr('%') - endif +"----------------------------------------------------------------------------- +" hide all floaterms +"----------------------------------------------------------------------------- +function! floaterm#hide() abort + let buffers = floaterm#buflist#gather() + for bufnr in buffers + call floaterm#window#hide_floaterm(bufnr) + endfor endfunction -function! floaterm#start(action) abort - if !floaterm#util#is_floaterm_available() +function! floaterm#send(bang, termname) abort + if &filetype ==# 'floaterm' + let msg = "FloatermSend can't be used in the floaterm window" + call floaterm#util#show_msg(msg, 'warning') return endif - if a:action ==# 'new' - call g:floaterm.new() - elseif a:action ==# 'next' - call g:floaterm.next() - elseif a:action ==# 'prev' - call g:floaterm.prev() - elseif a:action ==# 'toggle' - call g:floaterm.toggle() + if a:termname != '' + let bufnr = floaterm#terminal#get_bufnr(a:termname) + if bufnr == -1 + call floaterm#util#show_msg('No floaterm found with name: ' . a:termname, 'error') + return + endif + else + let bufnr = floaterm#buflist#find_curr() + if bufnr == -1 + let bufnr = floaterm#new('', {}, {}) + call floaterm#toggle('') + call floaterm#send(a:bang, a:termname) + call floaterm#toggle('') + return + endif + endif + + " https://vi.stackexchange.com/a/11028/17515 + let [lnum1, col1] = getpos("'<")[1:2] + let [lnum2, col2] = getpos("'>")[1:2] + let lines = getline(lnum1, lnum2) + let lines[-1] = lines[-1][: col2 - 1] + let lines[0] = lines[0][col1 - 1:] + + let linelist = [] + if a:bang ==# '!' + let line1 = lines[0] + let trim_line = substitute(line1, '\v^\s+', '', '') + let indent = len(line1) - len(trim_line) + for line in lines + if line[:indent] =~# '\s\+' + let line = line[indent:] + endif + call add(linelist, line) + endfor + else + let linelist = lines endif + call floaterm#terminal#send(bufnr, linelist) endfunction diff --git a/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/buffer.vim b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/buffer.vim new file mode 100644 index 0000000..30c23f7 --- /dev/null +++ b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/buffer.vim @@ -0,0 +1,23 @@ +" vim:sw=2: +" ============================================================================ +" FileName: buffer.vim +" Author: voldikss +" GitHub: https://github.com/voldikss +" ============================================================================ + +function! floaterm#buffer#create(linelist, opts) abort + let bufnr = nvim_create_buf(v:false, v:true) + call nvim_buf_set_lines(bufnr, 0, -1, v:true, a:linelist) + for [name, value] in items(a:opts) + call nvim_buf_set_option(bufnr, name, value) + endfor + return bufnr +endfunction + +function! floaterm#buffer#update_winopts(bufnr, winopts) abort + let winopts = getbufvar(a:bufnr, 'floaterm_winopts', {}) + for item in items(a:winopts) + let winopts[item[0]] = item[1] + endfor + call setbufvar(a:bufnr, 'floaterm_winopts', winopts) +endfunction diff --git a/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/buflist.vim b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/buflist.vim new file mode 100644 index 0000000..85d4d31 --- /dev/null +++ b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/buflist.vim @@ -0,0 +1,201 @@ +" vim:sw=2: +" ============================================================================ +" FileName: buflist.vim +" Author: voldikss +" GitHub: https://github.com/voldikss +" ============================================================================ + +" ---------------------------------------------------------------------------- +" Node type +" ---------------------------------------------------------------------------- + +" @type +" { +" \'next': s:node, +" \'prev': s:node, +" \'bufnr': int +" \} +let s:node = {} + +function! s:node.new(bufnr) dict abort + let node = deepcopy(self) + let node.bufnr = a:bufnr + return node +endfunction + +function! s:node.to_string() dict abort + return string(self.bufnr) +endfunction + +function! s:node.is_valid() dict abort + return bufexists(self.bufnr) + " return bufexists(self.bufnr) && floaterm#terminal#jobexists(self.bufnr) +endfunction + + +" ---------------------------------------------------------------------------- +" Linkedlist type and functions +" ---------------------------------------------------------------------------- + +" @type +" { +" \'head': s:none, +" \'index': s:node, +" \'size': int +" \} +let s:buflist = {} +let s:buflist.head = s:node.new(-1) +let s:buflist.head.next = s:buflist.head +let s:buflist.head.prev = s:buflist.head +let s:buflist.index = s:buflist.head +let s:buflist.size = 0 + +function! s:buflist.insert(node) dict abort + let a:node.prev = self.index + let a:node.next = self.index.next + let self.index.next.prev = a:node + let self.index.next = a:node + let self.index = a:node + let self.size += 1 +endfunction + +function! s:buflist.remove(node) dict abort + if self.empty() || a:node == self.head + return v:false + endif + if bufexists(a:node.bufnr) + execute a:node.bufnr . 'bdelete!' + endif + let a:node.prev.next = a:node.next + let a:node.next.prev = a:node.prev + let self.index = a:node.next + let self.size -= 1 + return v:true +endfunction + +function! s:buflist.empty() dict abort + " Method 1: use self.size + " return self.size == 0 + " Method 2: only head node + return self.head.next == self.head +endfunction + +" Find next bufnr with bufexists(bufnr) == v:true +" If not found, return -1 +" If bufexists(bufnr) != v:true, remove that node +function! s:buflist.find_next() dict abort + let node = self.index.next + while !node.is_valid() + call self.remove(node) + if self.empty() + return -1 + endif + let node = node.next + endwhile + let self.index = node + return node.bufnr +endfunction + +" Find prev bufnr with bufexists(bufnr) == v:true +" If not found, return -1 +" If bufexists(bufnr) != v:true, remove that node +function! s:buflist.find_prev() dict abort + let node = self.index.prev + while !node.is_valid() + call self.remove(node) + if self.empty() + return -1 + endif + let node = node.prev + endwhile + let self.index = node + return node.bufnr +endfunction + +" Find current bufnr with bufexists(bufnr) == v:true +" If not found, find next and next +" If bufexists(bufnr) != v:true, remove that node +function! s:buflist.find_curr() dict abort + let node = self.index + while !node.is_valid() + call self.remove(node) + if self.empty() + return -1 + endif + let node = node.next + endwhile + let self.index = node + return node.bufnr +endfunction + +" Return buflist str, note that node.bufnr may not exist +function! s:buflist.to_string() dict abort + let str = '[-' + let curr = self.head + let str .= printf('(%s)', curr.to_string()) + let curr = curr.next + while curr != self.head + let str .= printf('--(%s)', curr.to_string()) + let curr = curr.next + endwhile + let str .= '-]' + let str .= ' current index: ' . self.index.bufnr + return str +endfunction + +" For source extensions(vim-clap, denite) +" Return a list containing floaterm bufnr +" Every bufnr should exist +function! s:buflist.gather() dict abort + let candidates = [] + let curr = self.head.next + while curr != self.head + if curr.is_valid() + call add(candidates, curr.bufnr) + endif + let curr = curr.next + endwhile + return candidates +endfunction + + +" ---------------------------------------------------------------------------- +" Wrap functions to allow to be involved +" ---------------------------------------------------------------------------- +function! floaterm#buflist#add(bufnr) abort + let node = s:node.new(a:bufnr) + call s:buflist.insert(node) +endfunction +function! floaterm#buflist#find_next() abort + return s:buflist.find_next() +endfunction +function! floaterm#buflist#find_prev() abort + return s:buflist.find_prev() +endfunction +function! floaterm#buflist#find_curr() abort + return s:buflist.find_curr() +endfunction +function! floaterm#buflist#info() abort + echom s:buflist.to_string() +endfunction +function! floaterm#buflist#gather() abort + return s:buflist.gather() +endfunction + + +" ---------------------------------------------------------------------------- +" UNIT TEST +" ---------------------------------------------------------------------------- +function! floaterm#buflist#test() abort + let list = deepcopy(s:buflist) + echo list.index.bufnr + call list.insert(s:node.new(1)) + echo list.index.bufnr + call list.insert(s:node.new(2)) + echo list.index.bufnr + call list.insert(s:node.new(3)) + echo list.index.bufnr + echo list.to_string() +endfunction +" call floaterm#buflist#test() +" ---------------------------------------------------------------------------- diff --git a/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/cmdline.vim b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/cmdline.vim new file mode 100644 index 0000000..d080db5 --- /dev/null +++ b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/cmdline.vim @@ -0,0 +1,94 @@ +" vim:sw=2: +" ============================================================================ +" FileName: cmdline.vim +" Author: voldikss +" GitHub: https://github.com/voldikss +" ============================================================================ + +" ---------------------------------------------------------------------------- +" used for `:FloatermNew` and `:FloatermUpdate` +" parse argument list to `cmd`(string, default '') and `winopts`(dict) +" ---------------------------------------------------------------------------- +function! floaterm#cmdline#parse(arglist) abort + let winopts = {} + let cmd = '' + if a:arglist != [] + let c = 0 + for arg in a:arglist + let opt = split(arg, '=') + if len(opt) == 1 + let cmd = join(a:arglist[c:]) + break + elseif len(opt) == 2 + let [key, value] = opt + if key == 'height' || key == 'width' + let value = eval(value) + endif + let winopts[key] = value + endif + let c += 1 + endfor + endif + return [cmd, winopts] +endfunction + +" ---------------------------------------------------------------------------- +" used for `:FloatermNew` and `:FloatermUpdate` +" ---------------------------------------------------------------------------- +function! floaterm#cmdline#complete(arg_lead, cmd_line, cursor_pos) abort + let winopts_key = ['height=', 'width=', 'wintype=', 'name=', 'position='] + if a:cmd_line =~ '^FloatermNew' + let candidates = winopts_key + sort(getcompletion('', 'shellcmd')) + elseif a:cmd_line =~ '^FloatermUpdate' + let candidates = winopts_key + endif + + let cmd_line_before_cursor = a:cmd_line[:a:cursor_pos - 1] + let args = split(cmd_line_before_cursor, '\v\\@ -1 + if has('nvim') + let wintypes = ['normal', 'floating'] + else + let wintypes = ['normal', 'popup'] + endif + let candidates = map(wintypes, {idx -> 'wintype=' . wintypes[idx]}) + elseif match(prefix, 'position=') > -1 + let position = ['top', 'right', 'bottom', 'left', 'center', 'topleft', 'topright', 'bottomleft', 'bottomright', 'auto'] + let candidates = map(position, {idx -> 'position=' . position[idx]}) + endif + return filter(candidates, 'v:val[:len(prefix) - 1] ==# prefix') +endfunction + +" ---------------------------------------------------------------------------- +" used for `:FloatermToggle` +" ---------------------------------------------------------------------------- +function! floaterm#cmdline#floaterm_names(arg_lead, cmd_line, cursor_pos) abort + let buflist = floaterm#buflist#gather() + let ret = [] + let pattern = '^floaterm://' + for bufnr in buflist + let winopts = getbufvar(bufnr, 'floaterm_winopts', {}) + if !empty(winopts) + let termname = get(winopts, 'name', '') + if !empty(termname) + call add(ret, termname) + endif + endif + endfor + return ret +endfunction diff --git a/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/resolver.vim b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/resolver.vim new file mode 100644 index 0000000..0d31efc --- /dev/null +++ b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/resolver.vim @@ -0,0 +1,149 @@ +" vim:sw=2: +" ============================================================================ +" FileName: resolver.vim +" Author: voldikss +" GitHub: https://github.com/voldikss +" Description: This is modified from part of skywind3000/asyncrun +" ============================================================================ + +if has('win32') || has('win64') + let s:is_windows = 1 +else + let s:is_windows = 0 +endif + +" find project root +function! s:find_root(path, markers, strict) abort + function! s:guess_root(filename, markers) abort + let fullname = s:fullname(a:filename) + if exists('b:asyncrun_root') + return b:asyncrun_root + endif + if fullname =~ '^fugitive:/' + if exists('b:git_dir') + return fnamemodify(b:git_dir, ':h') + endif + return '' " skip any fugitive buffers early + endif + let pivot = fullname + if !isdirectory(pivot) + let pivot = fnamemodify(pivot, ':h') + endif + while 1 + let prev = pivot + for marker in a:markers + let newname = s:path_join(pivot, marker) + if newname =~ '[\*\?\[\]]' + if glob(newname) != '' + return pivot + endif + elseif filereadable(newname) + return pivot + elseif isdirectory(newname) + return pivot + endif + endfor + let pivot = fnamemodify(pivot, ':h') + if pivot == prev + break + endif + endwhile + return '' + endfunction + let root = s:guess_root(a:path, a:markers) + if root != '' + return s:fullname(root) + elseif a:strict != 0 + return '' + endif + " Not found: return parent directory of current file / file itself. + let fullname = s:fullname(a:path) + if isdirectory(fullname) + return fullname + endif + return s:fullname(fnamemodify(fullname, ':h')) +endfunction + +" Replace string +function! s:StringReplace(text, old, new) abort + let l:data = split(a:text, a:old, 1) + return join(l:data, a:new) +endfunction + +function! s:fullname(f) abort + let f = a:f + if f =~ "'." + try + redir => m + silent exe ':marks' f[1] + redir END + let f = split(split(m, '\n')[-1])[-1] + let f = filereadable(f)? f : '' + catch + let f = '%' + endtry + endif + let f = (f != '%')? f : expand('%') + let f = fnamemodify(f, ':p') + if s:is_windows + let f = substitute(f, "\\", '/', 'g') + endif + if len(f) > 1 + let size = len(f) + if f[size - 1] == '/' + let f = strpart(f, 0, size - 1) + endif + endif + return f +endfunction + +function! s:path_join(home, name) abort + let l:size = strlen(a:home) + if l:size == 0 | return a:name | endif + let l:last = strpart(a:home, l:size - 1, 1) + if has("win32") || has("win64") || has("win16") || has('win95') + let l:first = strpart(a:name, 0, 1) + if l:first == "/" || l:first == "\\" + let head = strpart(a:home, 1, 2) + if index([":\\", ":/"], head) >= 0 + return strpart(a:home, 0, 2) . a:name + endif + return a:name + elseif index([":\\", ":/"], strpart(a:name, 1, 2)) >= 0 + return a:name + endif + if l:last == "/" || l:last == "\\" + return a:home . a:name + else + return a:home . '/' . a:name + endif + else + if strpart(a:name, 0, 1) == '/' + return a:name + endif + if l:last == "/" + return a:home . a:name + else + return a:home . '/' . a:name + endif + endif +endfunction + +function! floaterm#resolver#get_root() abort + let markers = g:floaterm_rootmarkers + let strict = 0 + let l:hr = s:find_root(getcwd(), markers, strict) + if s:is_windows + let l:hr = s:StringReplace(l:hr, '/', "\\") + endif + return l:hr +endfunction + +function! floaterm#resolver#chdir(path) abort + if has('nvim') + let cmd = haslocaldir()? 'lcd' : (haslocaldir(-1, 0)? 'tcd' : 'cd') + else + let cmd = haslocaldir()? ((haslocaldir() == 1)? 'lcd' : 'tcd') : 'cd' + endif + silent execute cmd . ' '. fnameescape(a:path) +endfunction diff --git a/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/terminal.vim b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/terminal.vim new file mode 100644 index 0000000..79949da --- /dev/null +++ b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/terminal.vim @@ -0,0 +1,189 @@ +" vim:sw=2: +" ============================================================================ +" FileName: terminal.vim +" Author: voldikss +" GitHub: https://github.com/voldikss +" ============================================================================ + +let s:channel_map = {} +let s:is_win = has('win32') || has('win64') +let s:has_popup = has('textprop') && has('patch-8.2.0286') +let s:has_float = has('nvim') && exists('*nvim_win_set_config') + +if g:floaterm_wintype == v:null + if s:has_float + let s:wintype = 'floating' + elseif s:has_popup + let s:wintype = 'popup' + else + let s:wintype = 'normal' + endif +elseif g:floaterm_wintype == 'floating' && !s:has_float + call floaterm#util#show_msg("floating window is not supported in your nvim, fall back to normal window", 'warning') + let s:wintype = 'normal' +elseif g:floaterm_wintype == 'popup' && !s:popup + call floaterm#util#show_msg("popup window is not supported in your vim, fall back to normal window", 'warning') + let s:wintype = 'normal' +else + let s:wintype = g:floaterm_wintype +endif + +function! s:on_floaterm_open(bufnr, winid, winopts) abort + call setbufvar(a:bufnr, 'floaterm_winid', a:winid) + call setbufvar(a:bufnr, 'floaterm_winopts', a:winopts) + let termname = get(a:winopts, 'name', '') + if termname != '' + let termname = 'floaterm://' . termname + execute 'file ' . termname + endif + + call setbufvar(a:bufnr, '&buflisted', 0) + call setbufvar(a:bufnr, '&filetype', 'floaterm') + if has('nvim') + let winnr = bufwinnr(a:bufnr) + call setwinvar(winnr, '&winblend', g:floaterm_winblend) + call setwinvar(winnr, '&winhl', 'NormalFloat:Floaterm,Normal:Floaterm') + augroup close_floaterm_window + execute 'autocmd! TermClose call s:on_floaterm_close(' . a:bufnr .')' + execute 'autocmd! BufHidden call floaterm#window#hide_floaterm_border(' . a:bufnr . ')' + augroup END + endif + if g:floaterm_autoinsert == v:true + call floaterm#util#startinsert() + endif +endfunction + +function! s:on_floaterm_close(bufnr) abort + if getbufvar(a:bufnr, '&filetype') != 'floaterm' + return + endif + " NOTE: MUST hide border BEFORE deleting floaterm buffer + call floaterm#window#hide_floaterm_border(a:bufnr) + bdelete! + doautocmd BufDelete " call lightline#update() +endfunction + +function! floaterm#terminal#open(bufnr, cmd, jobopts, winopts) abort + " for vim's popup, must close popup can we open and jump to a new window + if !has('nvim') + call floaterm#window#hide_floaterm(bufnr('%')) + endif + + " change to root directory + if !empty(g:floaterm_rootmarkers) + let dest = floaterm#resolver#get_root() + if dest !=# '' + call floaterm#resolver#chdir(dest) + endif + endif + + let width = type(g:floaterm_width) == 7 ? 0.6 : g:floaterm_width + let width = get(a:winopts, 'width', width) + if type(width) == v:t_float | let width = width * &columns | endif + let width = float2nr(width) + + let height = type(g:floaterm_height) == 7 ? 0.6 : g:floaterm_height + let height = get(a:winopts, 'height', height) + if type(height) == v:t_float | let height = height * &lines | endif + let height = float2nr(height) + + let wintype = get(a:winopts, 'wintype', s:wintype) + let pos = get(a:winopts, 'position', g:floaterm_position) + + if a:bufnr > 0 + if wintype == 'floating' + let winid = floaterm#window#open_floating(a:bufnr, width, height, pos) + elseif wintype == 'popup' + let winid = floaterm#window#open_popup(a:bufnr, width, height, pos) + else + let winid = floaterm#window#open_split(a:bufnr, height, width, pos) + endif + call s:on_floaterm_open(a:bufnr, winid, a:winopts) + return 0 + endif + + if has('nvim') + let bufnr = nvim_create_buf(v:false, v:true) + call floaterm#buflist#add(bufnr) + if wintype == 'floating' + let winid = floaterm#window#open_floating(bufnr, width, height, pos) + call nvim_set_current_win(winid) + let ch = termopen(a:cmd, a:jobopts) + let s:channel_map[bufnr] = ch + else + let winid = floaterm#window#open_split(bufnr, height, width, pos) + let ch = termopen(a:cmd, a:jobopts) + let s:channel_map[bufnr] = ch + endif + else + if has_key(a:jobopts, 'on_exit') + let a:jobopts['exit_cb'] = a:jobopts.on_exit + unlet a:jobopts.on_exit + endif + let a:jobopts.hidden = 1 + let a:jobopts.term_finish = 'close' + if has('patch-8.1.2080') + let a:jobopts.term_api = 'floaterm#util#edit' + endif + let bufnr = term_start(a:cmd, a:jobopts) + call floaterm#buflist#add(bufnr) + let job = term_getjob(bufnr) + let s:channel_map[bufnr] = job_getchannel(job) + if wintype == 'popup' + let winid = floaterm#window#open_popup(bufnr, width, height, pos) + else + let winid = floaterm#window#open_split(bufnr, height, width, pos) + endif + endif + + let a:winopts.width = width + let a:winopts.height = height + let a:winopts.wintype = wintype + let a:winopts.pos = pos + call s:on_floaterm_open(bufnr, winid, a:winopts) + return bufnr +endfunction + +function! floaterm#terminal#open_existing(bufnr) abort + let winopts = getbufvar(a:bufnr, 'floaterm_winopts', {}) + call floaterm#terminal#open(a:bufnr, '', {}, winopts) +endfunction + +function! floaterm#terminal#send(bufnr, cmds) abort + let ch = get(s:channel_map, a:bufnr, v:null) + if empty(ch) | return | endif + if has('nvim') + if !empty(a:cmds[len(a:cmds) - 1]) + call add(a:cmds, '') + endif + call chansend(ch, a:cmds) + let curr_winnr = winnr() + let ch_winnr = bufwinnr(a:bufnr) + if ch_winnr > 0 + execute ch_winnr . 'wincmd w' + execute 'normal! G' + endif + execute curr_winnr . 'wincmd w' + else + let newline = s:is_win ? "\r\n" : "\n" + call ch_sendraw(ch, join(a:cmds, newline) . newline) + endif +endfunction + +function! floaterm#terminal#get_bufnr(termname) abort + return bufnr('floaterm://' . a:termname) +endfunction + + +"----------------------------------------------------------------------------- +" check if a job is running in the buffer(not used) +"----------------------------------------------------------------------------- +function! floaterm#terminal#jobexists(bufnr) abort + if has('nvim') + let jobid = getbufvar(a:bufnr, '&channel') + return jobwait([jobid], 0)[0] == -1 + else + let job = term_getjob(a:bufnr) + return job_status(job) !=# 'dead' + endif +endfunction diff --git a/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/util.vim b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/util.vim index 8e2e7b0..ce9738e 100644 --- a/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/util.vim +++ b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/util.vim @@ -1,79 +1,10 @@ +" vim:sw=2: " ============================================================================ " FileName: autoload/floaterm/util.vim -" Description: " Author: voldikss " GitHub: https://github.com/voldikss " ============================================================================ -function! floaterm#util#floating_win_pos(width, height) abort - if g:floaterm_position ==# 'topright' - let row = 0 - let col = &columns - let vert = 'N' - let hor = 'E' - elseif g:floaterm_position ==# 'topleft' - let row = 0 - let col = 0 - let vert = 'N' - let hor = 'W' - elseif g:floaterm_position ==# 'bottomright' - let row = &lines - let col = &columns - let vert = 'S' - let hor = 'E' - elseif g:floaterm_position ==# 'bottomleft' - let row = &lines - let col = 0 - let vert = 'S' - let hor = 'W' - elseif g:floaterm_position ==# 'center' - let row = (&lines - a:height)/2 - let col = (&columns - a:width)/2 - let vert = 'N' - let hor = 'W' - - if row < 0 - let row = 0 - endif - if col < 0 - let col = 0 - endif - else " at the cursor place - let curr_pos = getpos('.') - let row = curr_pos[1] - line('w0') - let col = curr_pos[2] - - if row + a:height <= &lines - let vert = 'N' - else - let vert = 'S' - endif - - if col + a:width <= &columns - let hor = 'W' - else - let hor = 'E' - endif - endif - - return [row, col, vert, hor] -endfunction - -function! floaterm#util#is_floaterm_available() abort - if exists('*nvim_win_set_config') - if g:floaterm_type == v:null - let g:floaterm_type = 'floating' - endif - elseif has('terminal') - let g:floaterm_type = 'normal' - else - let message = 'Terminal feature is required, please upgrade your vim/nvim' - call floaterm#util#show_msg(message, 'error') - return v:false - endif - return v:true -endfunction - function! s:echo(group, msg) abort if a:msg ==# '' | return | endif execute 'echohl' a:group @@ -114,36 +45,41 @@ function! floaterm#util#show_msg(message, ...) abort endif endfunction -function! floaterm#util#get_normalfloat_fg() abort - let hiGroup = 'NormalFloat' - while v:true - let hiInfo = execute('hi ' . hiGroup) - let fgcolor = matchstr(hiInfo, 'guifg=\zs\S*') - let hiGroup = matchstr(hiInfo, 'links to \zs\S*') - if fgcolor !=# '' || hiGroup ==# '' - break - endif - endwhile - " If the foreground color isn't found eventually, use white - if fgcolor ==# '' - let fgcolor = '#FFFFFF' +function! floaterm#util#edit(_bufnr, filename) abort + call floaterm#hide() + silent execute g:floaterm_open_command . ' ' . a:filename +endfunction + +function! floaterm#util#startinsert() abort + if mode() == 'i' | return | endif + if has('nvim') + startinsert + else + silent! execute 'normal! i' endif - return fgcolor endfunction -function! floaterm#util#get_normalfloat_bg() abort - let hiGroup = 'NormalFloat' - while v:true - let hiInfo = execute('hi ' . hiGroup) - let bgcolor = matchstr(hiInfo, 'guibg=\zs\S*') - let hiGroup = matchstr(hiInfo, 'links to \zs\S*') - if bgcolor !=# '' || hiGroup ==# '' - break - endif - endwhile - " If the background color isn't found eventually, use black - if bgcolor ==# '' - let bgcolor = '#000000' +"----------------------------------------------------------------------------- +" compose two string(thank skywind3000/vim-quickui) +"----------------------------------------------------------------------------- +function! floaterm#util#string_compose(target, pos, source) + if a:source == '' + return a:target + endif + let pos = a:pos + let source = a:source + if pos < 0 + let source = strcharpart(a:source, -pos) + let pos = 0 + endif + let target = strcharpart(a:target, 0, pos) + if strchars(target) < pos + let target .= repeat(' ', pos - strchars(target)) endif - return bgcolor + let target .= source + " vim popup will pad the end of title but not begin part + " so we build the title as ' floaterm idx/cnt' + " therefore, we need to add a space here + let target .= ' ' . strcharpart(a:target, pos + strchars(source) + 1) + return target endfunction diff --git a/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/window.vim b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/window.vim new file mode 100644 index 0000000..7de6969 --- /dev/null +++ b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/window.vim @@ -0,0 +1,222 @@ +" vim:sw=2: +" ============================================================================ +" FileName: floatwin.vim +" Author: voldikss +" GitHub: https://github.com/voldikss +" ============================================================================ + +" winid: floaterm window id +function! s:add_border(winid, title) abort + let winopts = nvim_win_get_config(a:winid) + let top = g:floaterm_borderchars[4] . + \ repeat(g:floaterm_borderchars[0], winopts.width) . + \ g:floaterm_borderchars[5] + let mid = g:floaterm_borderchars[3] . + \ repeat(' ', winopts.width) . + \ g:floaterm_borderchars[1] + let bot = g:floaterm_borderchars[7] . + \ repeat(g:floaterm_borderchars[2], winopts.width) . + \ g:floaterm_borderchars[6] + let top = floaterm#util#string_compose(top, 1, a:title) + let lines = [top] + repeat([mid], winopts.height) + [bot] + let buf_opts = {} + let buf_opts.synmaxcol = 3000 " #17 + let buf_opts.filetype = 'floaterm_border' + let border_bufnr = floaterm#buffer#create(lines, buf_opts) + call nvim_buf_set_option(border_bufnr, 'bufhidden', 'wipe') + let winopts.row -= (winopts.anchor[0] == 'N' ? 1 : -1) + " adjust offset + if winopts.row < 0 + let winopts.row = 1 + call nvim_win_set_config(a:winid, winopts) + let winopts.row = 0 + endif + let winopts.col -= (winopts.anchor[1] == 'W' ? 1 : -1) + let winopts.width += 2 + let winopts.height += 2 + let winopts.style = 'minimal' + let winopts.focusable = v:false + let border_winid = nvim_open_win(border_bufnr, v:false, winopts) + call nvim_win_set_option(border_winid, 'winhl', 'NormalFloat:FloatermBorder') + return border_winid +endfunction + +function! s:build_title(bufnr) abort + if !g:floaterm_wintitle + return '' + endif + let buffers = floaterm#buflist#gather() + let cnt = len(buffers) + let idx = index(buffers, a:bufnr) + 1 + return printf(' floaterm: %s/%s', idx, cnt) +endfunction + +function! s:floatwin_pos(width, height, pos) abort + if a:pos == 'topright' + let row = 2 + let col = &columns - 1 + let anchor = 'NE' + elseif a:pos == 'topleft' + let row = 2 + let col = 1 + let anchor = 'NW' + elseif a:pos == 'bottomright' + let row = &lines - 3 + let col = &columns - 1 + let anchor = 'SE' + elseif a:pos == 'bottomleft' + let row = &lines - 3 + let col = 1 + let anchor = 'SW' + elseif a:pos == 'top' + let row = 2 + let col = (&columns - a:width)/2 + let anchor = 'NW' + elseif a:pos == 'right' + let row = (&lines - a:height)/2 + let col = &columns - 1 + let anchor = 'NE' + elseif a:pos == 'bottom' + let row = &lines - 3 + let col = (&columns - a:width)/2 + let anchor = 'SW' + elseif a:pos == 'left' + let row = (&lines - a:height)/2 + let col = 1 + let anchor = 'NW' + elseif a:pos == 'center' + let row = (&lines - a:height)/2 + let col = (&columns - a:width)/2 + let anchor = 'NW' + if row < 0 + let row = 0 + endif + if col < 0 + let col = 0 + endif + else " at the cursor place + let curr_pos = getpos('.') + let row = curr_pos[1] - line('w0') + let col = curr_pos[2] + if row + a:height <= &lines + let vert = 'N' + else + let vert = 'S' + endif + if col + a:width <= &columns + let hor = 'W' + else + let hor = 'E' + endif + let anchor = vert . hor + endif + if !has('nvim') + let anchor = substitute(anchor, '\CN', 'top', '') + let anchor = substitute(anchor, '\CS', 'bot', '') + let anchor = substitute(anchor, '\CW', 'left', '') + let anchor = substitute(anchor, '\CE', 'right', '') + endif + return [row, col, anchor] +endfunction + +function! s:winexists(winid) abort + return !empty(getwininfo(a:winid)) +endfunction + +function! floaterm#window#open_floating(bufnr, width, height, pos) abort + let [row, col, anchor] = s:floatwin_pos(a:width, a:height, a:pos) + let opts = { + \ 'relative': 'editor', + \ 'anchor': anchor, + \ 'row': row, + \ 'col': col, + \ 'width': a:width, + \ 'height': a:height, + \ 'style':'minimal' + \ } + let winid = nvim_open_win(a:bufnr, v:true, opts) + let border_winid = getbufvar(a:bufnr, 'floaterm_border_winid', v:null) + if border_winid == v:null || !s:winexists(border_winid) + let title = s:build_title(a:bufnr) + let border_winid = s:add_border(winid, title) + call setbufvar(a:bufnr, 'floaterm_border_winid', border_winid) + endif + return winid +endfunction + +function! floaterm#window#open_popup(bufnr, width, height, pos) abort + let [row, col, anchor] = s:floatwin_pos(a:width, a:height, a:pos) + let width = a:width + let height = a:height + let opts = { + \ 'pos': anchor, + \ 'line': row, + \ 'col': col, + \ 'maxwidth': width, + \ 'minwidth': width, + \ 'maxheight': height, + \ 'minheight': height, + \ 'border': [1, 1, 1, 1], + \ 'borderchars': g:floaterm_borderchars, + \ 'borderhighlight': ['FloatermBorder'], + \ 'padding': [0,1,0,1], + \ 'highlight': 'Floaterm' + \ } + let opts.title = s:build_title(a:bufnr) + let opts.zindex = len(floaterm#buflist#gather()) + 1 + let winid = popup_create(a:bufnr, opts) + call setbufvar(a:bufnr, '&filetype', 'floaterm') + return winid +endfunction + +function! floaterm#window#open_split(bufnr, height, width, pos) abort + if a:pos == 'top' + execute 'topleft' . a:height . 'split' + elseif a:pos == 'left' + execute 'topleft' . a:width . 'vsplit' + elseif a:pos == 'right' + execute 'botright' . a:width . 'vsplit' + else " default position: bottom + execute 'botright' . a:height . 'split' + endif + wincmd J + execute 'buffer ' . a:bufnr + return win_getid() +endfunction + +function! floaterm#window#hide_floaterm_border(bufnr, ...) abort + let winid = getbufvar(a:bufnr, 'floaterm_border_winid', v:null) + if winid != v:null && s:winexists(winid) + call nvim_win_close(winid, v:true) + endif + call setbufvar(a:bufnr, 'floaterm_border_winid', v:null) +endfunction + +function! floaterm#window#hide_floaterm(bufnr) abort + let winid = getbufvar(a:bufnr, 'floaterm_winid', -1) + if winid == -1 | return | endif + if has('nvim') + if !s:winexists(winid) | return | endif + call nvim_win_close(winid, v:true) + else + try " there should be a function like `win_type()` + call popup_close(winid) + catch + hide + endtry + endif +endfunction + +"----------------------------------------------------------------------------- +" find **one** visible floaterm window +"----------------------------------------------------------------------------- +function! floaterm#window#find_floaterm_window() abort + let found_winnr = 0 + for winnr in range(1, winnr('$')) + if getbufvar(winbufnr(winnr), '&filetype') ==# 'floaterm' + let found_winnr = winnr + break + endif + endfor + return found_winnr +endfunction diff --git a/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/wrapper/fff.vim b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/wrapper/fff.vim new file mode 100644 index 0000000..0da989d --- /dev/null +++ b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/wrapper/fff.vim @@ -0,0 +1,44 @@ +" vim:sw=2: +" ============================================================================ +" FileName: fff.vim +" Author: benwoodward +" GitHub: https://github.com/benwoodward +" ============================================================================ + +function! floaterm#wrapper#fff#(cmd) abort + let original_dir = getcwd() + lcd %:p:h + + let cmdlist = split(a:cmd) + let cmd = 'fff -p' + if len(cmdlist) > 1 + let cmd .= ' ' . join(cmdlist[1:], ' ') + else + let cmd .= ' ' . getcwd() + endif + + exe "lcd " . original_dir + return [cmd, {'on_exit': funcref('s:fff_callback')}, v:false] +endfunction + +function! s:fff_callback(...) abort + let s:fff_tmpfile = $XDG_CACHE_HOME + + if !isdirectory(s:fff_tmpfile) + let s:fff_tmpfile = $HOME . "/.cache" + endif + + let s:fff_tmpfile .= "/fff/opened_file" + let s:fff_tmpfile = fnameescape(s:fff_tmpfile) + + if filereadable(s:fff_tmpfile) + let file_data = readfile(s:fff_tmpfile) + execute delete(s:fff_tmpfile) + else + return + endif + + if filereadable(file_data[0]) + execute g:floaterm_open_command . ' ' . file_data[0] + endif +endfunction diff --git a/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/wrapper/fzf.vim b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/wrapper/fzf.vim new file mode 100644 index 0000000..edc43d5 --- /dev/null +++ b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/wrapper/fzf.vim @@ -0,0 +1,18 @@ +" vim:sw=2: +" ============================================================================ +" FileName: fzf.vim +" Author: voldikss +" GitHub: https://github.com/voldikss +" ============================================================================ + +function! floaterm#wrapper#fzf#(...) abort + if stridx(&shell, 'fish') >= 0 + let cmd = 'floaterm (fzf)' + elseif stridx(&shell, 'csh') + let cmd = 'floaterm `fzf`' + else + " sh/bash/zsh + let cmd = 'floaterm $(fzf)' + endif + return [cmd, {}, v:true] +endfunction diff --git a/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/wrapper/lf.vim b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/wrapper/lf.vim new file mode 100644 index 0000000..6b7e2f1 --- /dev/null +++ b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/wrapper/lf.vim @@ -0,0 +1,34 @@ +" vim:sw=2: +" ============================================================================ +" FileName: lf.vim +" Author: benwoodward +" GitHub: https://github.com/benwoodward +" ============================================================================ + +function! floaterm#wrapper#lf#(cmd) abort + let s:lf_tmpfile = tempname() + let original_dir = getcwd() + lcd %:p:h + + let cmdlist = split(a:cmd) + let cmd = 'lf -selection-path=' . s:lf_tmpfile + if len(cmdlist) > 1 + let cmd .= ' ' . join(cmdlist[1:], ' ') + else + let cmd .= ' ' . getcwd() + endif + + exe "lcd " . original_dir + return [cmd, {'on_exit': funcref('s:lf_callback')}, v:false] +endfunction + +function! s:lf_callback(...) abort + if filereadable(s:lf_tmpfile) + let filenames = readfile(s:lf_tmpfile) + if !empty(filenames) + for filename in filenames + execute g:floaterm_open_command . ' ' . fnameescape(filename) + endfor + endif + endif +endfunction diff --git a/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/wrapper/nnn.vim b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/wrapper/nnn.vim new file mode 100644 index 0000000..ba0b3dc --- /dev/null +++ b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/wrapper/nnn.vim @@ -0,0 +1,34 @@ +" vim:sw=2: +" ============================================================================ +" FileName: nnn.vim +" Author: voldikss +" GitHub: https://github.com/voldikss +" ============================================================================ + +function! floaterm#wrapper#nnn#(cmd) abort + let s:nnn_tmpfile = tempname() + let original_dir = getcwd() + lcd %:p:h + + let cmdlist = split(a:cmd) + let cmd = 'nnn -p ' . s:nnn_tmpfile + if len(cmdlist) > 1 + let cmd .= ' ' . join(cmdlist[1:], ' ') + else + let cmd .= ' ' . getcwd() + endif + + exe "lcd " . original_dir + return [cmd, {'on_exit': funcref('s:nnn_callback')}, v:false] +endfunction + +function! s:nnn_callback(...) abort + if filereadable(s:nnn_tmpfile) + let filenames = readfile(s:nnn_tmpfile) + if !empty(filenames) + for filename in filenames + execute g:floaterm_open_command . ' ' . fnameescape(filename) + endfor + endif + endif +endfunction diff --git a/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/wrapper/ranger.vim b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/wrapper/ranger.vim new file mode 100644 index 0000000..84ce46a --- /dev/null +++ b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/wrapper/ranger.vim @@ -0,0 +1,38 @@ +" vim:sw=2: +" ============================================================================ +" FileName: ranger.vim +" Author: voldikss +" GitHub: https://github.com/voldikss +" ============================================================================ + +function! floaterm#wrapper#ranger#(cmd) abort + let s:ranger_tmpfile = tempname() + let original_dir = getcwd() + lcd %:p:h + + let cmdlist = split(a:cmd) + let cmd = 'ranger --choosefiles=' . s:ranger_tmpfile + if len(cmdlist) > 1 + let cmd .= ' ' . join(cmdlist[1:], ' ') + else + if expand('%:p') != '' + let cmd .= ' --selectfile="' . expand('%:p') . '"' + else + let cmd .= ' ' . getcwd() + endif + endif + + exe "lcd " . original_dir + return [cmd, {'on_exit': funcref('s:ranger_callback')}, v:false] +endfunction + +function! s:ranger_callback(...) abort + if filereadable(s:ranger_tmpfile) + let filenames = readfile(s:ranger_tmpfile) + if !empty(filenames) + for filename in filenames + execute g:floaterm_open_command . ' ' . fnameescape(filename) + endfor + endif + endif +endfunction diff --git a/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/wrapper/vifm.vim b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/wrapper/vifm.vim new file mode 100644 index 0000000..ef58bf9 --- /dev/null +++ b/etc/soft/nvim/+plugins/vim-floaterm/autoload/floaterm/wrapper/vifm.vim @@ -0,0 +1,34 @@ +" vim:sw=2: +" ============================================================================ +" FileName: vifm.vim +" Author: kazhala +" GitHub: https://github.com/kazhala +" ============================================================================ + +function! floaterm#wrapper#vifm#(cmd) abort + let s:vifm_tmpfile = tempname() + let original_dir = getcwd() + lcd %:p:h + + let cmdlist = split(a:cmd) + let cmd = 'vifm --choose-files ' . s:vifm_tmpfile + if len(cmdlist) > 1 + let cmd .= ' ' . join(cmdlist[1:], ' ') + else + let cmd .= ' ' . getcwd() + endif + + exe "lcd " . original_dir + return [cmd, {'on_exit': funcref('s:vifm_callback')}, v:false] +endfunction + +function! s:vifm_callback(...) abort + if filereadable(s:vifm_tmpfile) + let filenames = readfile(s:vifm_tmpfile) + if !empty(filenames) + for filename in filenames + execute g:floaterm_open_command . ' ' . fnameescape(filename) + endfor + endif + endif +endfunction diff --git a/etc/soft/nvim/+plugins/vim-floaterm/autoload/health/floaterm.vim b/etc/soft/nvim/+plugins/vim-floaterm/autoload/health/floaterm.vim new file mode 100644 index 0000000..d1793a4 --- /dev/null +++ b/etc/soft/nvim/+plugins/vim-floaterm/autoload/health/floaterm.vim @@ -0,0 +1,36 @@ +" vim:sw=2: +" ============================================================================ +" FileName: floaterm.vim +" Author: voldikss +" GitHub: https://github.com/voldikss +" ============================================================================ + +function! s:check_terminal() abort + if exists(':terminal') > 0 + call health#report_ok('Terminal feature is OK') + else + call health#report_error('Terminal feature is required but not found') + endif +endfunction + +function! s:check_floating() abort + if has('nvim') && exists('*nvim_win_set_config') + call health#report_ok('Floating window feature is OK') + else + call health#report_error('Floating window feature is required but not found, will use normal window') + endif +endfunction + +function! s:check_nvr() abort + if executable('nvr') + call health#report_ok('nvr is OK') + else + call health#report_error('nvr executable is not found, run `pip install neovim-remote` to install') + endif +endfunction + +function! health#floaterm#check() abort + call s:check_terminal() + call s:check_floating() + call s:check_nvr() +endfunction diff --git a/etc/soft/nvim/+plugins/vim-floaterm/doc/floaterm.txt b/etc/soft/nvim/+plugins/vim-floaterm/doc/floaterm.txt index bc8a22c..c6f3bd4 100644 --- a/etc/soft/nvim/+plugins/vim-floaterm/doc/floaterm.txt +++ b/etc/soft/nvim/+plugins/vim-floaterm/doc/floaterm.txt @@ -1,7 +1,11 @@ -*floaterm.txt* Neovim's floating terminal plugin Last change: 2019-12-28 +*floaterm.txt* Neovim floating terminal plugin Last change: 2019-02-07 Author : voldikss License: MIT license +NOTE: This is outdated, please refer to the README file: + ../README.md + or + https://github.com/voldikss/vim-floaterm/blob/master/README.md ============================================================================== CONTENTS *floaterm-contents* @@ -11,6 +15,8 @@ CONTENTS *floaterm-contents* Variables |floaterm-variables| Keymappings |floaterm-key-mappings| Commands |floaterm-commands| + Highlight |floaterm-highlight| + Extensions |floaterm-extensions| Q-A |floaterm-q&a| Repository |floaterm-repository| @@ -18,15 +24,15 @@ CONTENTS *floaterm-contents* ============================================================================== INTRODUCTION *floaterm-introduction* - Use neovim terminal in the floating window. +Use neovim terminal in the floating window. ============================================================================== INSTALL *floaterm-install* - With vim-plug: - > - Plug 'voldikss/vim-floaterm' + With vim-plug: + > + Plug 'voldikss/vim-floaterm' < ============================================================================== @@ -38,60 +44,55 @@ FEATURES *floaterm-features* - Customizable floating terminal style -- Switch/Preview floating terminal buffer using [vim-clap](https://github.com/liuchengxu/vim-clap)(try `:Clap floaterm`) +- Switch/Preview floating terminal buffer using |vim-clap| + https://github.com/liuchengxu/vim-clap + +- Switch/Preview/Open floating terminal buffer using |denite.nvim| + https://github.com/Shougo/denite.nvim ============================================================================== VARIABLES *floaterm-variables* -g:floaterm_type *g:floaterm_type* +g:floaterm_wintype *g:floaterm_wintype* - Available:`'floating'`(neovim only), `'normal'`(vim8 and neovim) + Available: "floating"(neovim only), "normal"(vim8 and neovim) - Default: `'floating'` + Default: "floating" g:floaterm_width *g:floaterm_width* - Type: `int` (number of columns) or `float` (between 0 and 1). - If `float`, the width is relative to `&columns`. - Default: `0.6` + Type: |v:t_number| (number of columns) or |v:t_float| (between 0 and 1). + If |v:t_float|, the width is relative to `&columns`. + + Default: 0.6 g:floaterm_height *g:floaterm_height* - Type: `int` (number of lines) or `float` (between 0 and 1). - If `float`, the height is relative to `&lines`. - Default: `0.6` + Type: |v:t_number| (number of lines) or |v:t_float| (between 0 and 1). + If |v:t_float|, the height is relative to `&lines`. + + Default: 0.6 g:floaterm_winblend *g:floaterm_winblend* - Description: The opacity of the floating terminal + Description: The transparency of the floating terminal - Default: `0` + Default: 0 g:floaterm_position *g:floaterm_position* - Available: `'center'`, `'topleft'`, `'topright'`, `'bottomleft'`, - `'bottomright'`, `'auto'` + Available: "center", "topleft", "topright", "bottomleft", + "bottomright", "auto" - Default: `'center'` - -g:floaterm_background *g:floaterm_background* - - Type: string(e.g. `'#000000'`, `'black'`) - - Default: background color of normal floating window + Default: "center" g:floaterm_borderchars *g:floaterm_borderchars* - Default: `['─', '│', '─', '│', '┌', '┐', '┘', '└']` - -g:floaterm_border_color *g:floaterm_border_color* - - Type: string(e.g. `'#FFFFFF'`, `'white'`) + Default: "['─', '│', '─', '│', '┌', '┐', '┘', '└']" - Default: foreground color of normal floating window ============================================================================== @@ -99,14 +100,13 @@ MAPPINGS *floaterm-key-mappings* This plugin doesn't supply any default mappings. > - """ Example configuration - let g:floaterm_keymap_new = '' - let g:floaterm_keymap_prev = '' - let g:floaterm_keymap_next = '' - let g:floaterm_keymap_toggle = '' + """ Configuration example + let g:floaterm_keymap_new = '' + let g:floaterm_keymap_prev = '' + let g:floaterm_keymap_next = '' + let g:floaterm_keymap_toggle = '' < - ============================================================================== COMMANDS *floaterm-commands* @@ -121,25 +121,87 @@ COMMANDS *floaterm-commands* :FloatermToggle *:FloatermToggle* +============================================================================== +HIGHLIGHT *floaterm-highlight* + +This plugin supplies two `highlight-groups` to specify the +background/foregrond color of floaterm(border) window + +By default, they are both linked to `Normal` + > + " Configuration example + hi Floaterm guibg=black + hi FloatermBorder guibg=gray guifg=blue +< + +============================================================================== +EXTENSIONS *floaterm-extensions* + +------------------------------------------------------------------------------ +VIM-CLAP *Clap-floaterm* + +Start |vim-clap| to manage floaterms. + > + :Clap floaterm +< +------------------------------------------------------------------------------ +DENITE.NVIM *Denite-floaterm* + + *denite-source-floaterm* +SOURCES~ + +floaterm Gather floaterms as candidates. This accepts an argument + below. + + Source arguments: + 1. "new" adds a candidate to open a new floaterm. + + *denite-floaterm-actions* +ACTIONS~ + +open (default) + Open the selected floaterm. + +new + Open a new floaterm. + +preview + Preview the selected floaterm. + + *denite-floaterm-usage* +USAGE~ + +Start to manage existent floaterms. + > + :Denite floaterm +< +Start to open a new floaterm. + > + :Denite floaterm:new +< +Start with all features and preview automatically with moving the cursor. + > + :Denite floaterm floaterm:new -auto-action=preview +< ============================================================================== Q-A *floaterm-q&a* - This plugin leaves an empty buffer on startify window - Put this code in `vimrc` + Put this code in "vimrc" > autocmd User Startified setlocal buflisted - < +< - I want to use another shell in the terminal. (e.g. Use fish instead of bash) - Set `shell` option in your `vimrc`: + Set 'shell' option in your "vimrc": > set shell=/path/to/shell - I would like to customize the style of the floating terminal window - Use `autocmd`. For example + Use |autocmd|. For example, > function s:floatermSettings() setlocal number diff --git a/etc/soft/nvim/+plugins/vim-floaterm/plugin/floaterm.vim b/etc/soft/nvim/+plugins/vim-floaterm/plugin/floaterm.vim index 63bb619..e9127c5 100644 --- a/etc/soft/nvim/+plugins/vim-floaterm/plugin/floaterm.vim +++ b/etc/soft/nvim/+plugins/vim-floaterm/plugin/floaterm.vim @@ -1,30 +1,43 @@ +" vim:sw=2: " ============================================================================ " FileName: plugin/floaterm.vim -" Description: " Author: voldikss " GitHub: https://github.com/voldikss " ============================================================================ scriptencoding utf-8 -let g:floaterm_type = get(g:, 'floaterm_type', v:null) -let g:floaterm_width = get(g:, 'floaterm_width', v:null) -let g:floaterm_height = get(g:, 'floaterm_height', v:null) -let g:floaterm_winblend = get(g:, 'floaterm_winblend', 0) -let g:floaterm_position = get(g:, 'floaterm_position', 'auto') -let g:floaterm_background = get(g:, 'floaterm_background', v:null) -let g:floaterm_borderchars = get(g:, 'floaterm_borderchars', ['─', '│', '─', '│', '┌', '┐', '┘', '└']) -let g:floaterm_border_color = get(g:, 'floaterm_border_color', v:null) +let g:floaterm_wintype = get(g:, 'floaterm_wintype', v:null) +let g:floaterm_wintitle = get(g:, 'floaterm_wintitle', v:true) +let g:floaterm_width = get(g:, 'floaterm_width', v:null) +let g:floaterm_height = get(g:, 'floaterm_height', v:null) +let g:floaterm_winblend = get(g:, 'floaterm_winblend', 0) +let g:floaterm_position = get(g:, 'floaterm_position', 'center') +let g:floaterm_borderchars = get(g:, 'floaterm_borderchars', ['─', '│', '─', '│', '┌', '┐', '┘', '└']) +let g:floaterm_rootmarkers = get(g:, 'floaterm_rootmarkers', []) +let g:floaterm_autoinsert = get(g:, 'floaterm_autoinsert', v:true) +let g:floaterm_open_command = get(g:, 'floaterm_open_command', 'edit') +let g:floaterm_gitcommit = get(g:, 'floaterm_gitcommit', v:null) let g:floaterm_keymap_new = get(g:, 'floaterm_keymap_new', v:null) let g:floaterm_keymap_prev = get(g:, 'floaterm_keymap_prev', v:null) let g:floaterm_keymap_next = get(g:, 'floaterm_keymap_next', v:null) let g:floaterm_keymap_toggle = get(g:, 'floaterm_keymap_toggle', v:null) -command! -nargs=0 FloatermNew call floaterm#start('new') -command! -nargs=0 FloatermPrev call floaterm#start('prev') -command! -nargs=0 FloatermNext call floaterm#start('next') -command! -nargs=0 FloatermToggle call floaterm#start('toggle') +command! -nargs=0 FloatermPrev call floaterm#prev() +command! -nargs=0 FloatermNext call floaterm#next() +command! -nargs=0 FloatermHide call floaterm#hide() +command! -nargs=* -complete=customlist,floaterm#cmdline#complete + \ FloatermNew call floaterm#run('new', ) +command! -nargs=* -complete=customlist,floaterm#cmdline#complete + \ FloatermUpdate call floaterm#run('update', ) +command! -nargs=? -complete=customlist,floaterm#cmdline#floaterm_names + \ FloatermToggle call floaterm#toggle() +command! -nargs=? -range -bang -complete=customlist,floaterm#cmdline#floaterm_names + \ FloatermSend call floaterm#send('', ) + +hi def link Floaterm Normal +hi def link FloatermBorder Normal function! s:install_keymap() if g:floaterm_keymap_new != v:null diff --git a/etc/soft/nvim/+plugins/vim-floaterm/rplugin/python3/denite/kind/floaterm.py b/etc/soft/nvim/+plugins/vim-floaterm/rplugin/python3/denite/kind/floaterm.py new file mode 100644 index 0000000..8aee899 --- /dev/null +++ b/etc/soft/nvim/+plugins/vim-floaterm/rplugin/python3/denite/kind/floaterm.py @@ -0,0 +1,87 @@ +from denite.kind.base import Base +from denite.util import Nvim, UserContext + +PREVIEW_FILENAME = "[denite-floaterm-preview]" + + +class Kind(Base): + def __init__(self, vim: Nvim) -> None: + super().__init__(vim) + + self.name = "floaterm" + self.default_action = "open" + self._previewed_bufnr = -1 + self._is_nvim = bool(vim.funcs.has("nvim")) + + def action_new(self, context: UserContext) -> None: + self.vim.command("FloatermNew") + + def action_open(self, context: UserContext) -> None: + target = context["targets"][0] + if target.get("action__is_new", False): + self.action_new(context) + return + + bufnr = target["action__bufnr"] + self.vim.call("floaterm#terminal#open_existing", bufnr) + + def action_preview(self, context: UserContext) -> None: + target = context["targets"][0] + + if "action__bufnr" not in target: + self.vim.command("pclose!") + return + + bufnr = target["action__bufnr"] + + if ( + context["auto_action"] != "preview" + and self._is_preview_window_opened() + and self._previewed_bufnr == bufnr + ): + self.vim.command("pclose!") + self._previewed_bufnr = -1 + return + + self._save_win() + + self.vim.call("denite#helper#preview_file", context, PREVIEW_FILENAME) + self.vim.command("wincmd P") + self.vim.current.buffer.options["swapfile"] = False + self.vim.current.buffer.options["bufhidden"] = "wipe" + self.vim.current.buffer.options["buftype"] = "nofile" + + buf = self.vim.buffers[bufnr] + last_line = len(buf) - 1 + last_non_empty_line = next( + filter(lambda x: buf[x] != "", range(last_line, 0, -1)), last_line + ) + start = max(0, last_non_empty_line - self.vim.options["previewheight"] + 1) + end = last_non_empty_line + 1 + self.vim.current.buffer[:] = buf[start:end] + + self._restore_win() + self._previewed_bufnr = bufnr + + def _is_preview_window_opened(self) -> bool: + # NOTE: Using `vim.windows` is better, but vim does not recognize it. + # So here uses an odd way to list windows. + return next( + filter( + lambda x: bool(self.vim.call("getwinvar", x, "&previewwindow")), + range(1, self.vim.call("winnr", "$") + 1), + ), + False, + ) + + def _save_win(self) -> None: + if self._is_nvim: + self._current_window = self.vim.current.window + else: + self._current_window = self.vim.funcs.win_getid() + + def _restore_win(self) -> None: + if self._is_nvim: + self.vim.current.window = self._current_window + else: + self.vim.funcs.win_gotoid(self._current_window) diff --git a/etc/soft/nvim/+plugins/vim-floaterm/rplugin/python3/denite/source/floaterm.py b/etc/soft/nvim/+plugins/vim-floaterm/rplugin/python3/denite/source/floaterm.py new file mode 100644 index 0000000..c0c3ddf --- /dev/null +++ b/etc/soft/nvim/+plugins/vim-floaterm/rplugin/python3/denite/source/floaterm.py @@ -0,0 +1,71 @@ +from denite.base.source import Base +from denite.util import Nvim, UserContext, Candidate, Candidates + +DELIMITER = "\u00a0:\u00a0" +FLOATERM_HIGHLIGHT_SYNTAX = [ + {"name": "Bufnr", "link": "Constant", "re": r"\d\+ ", "next": "Name"}, + {"name": "Name", "link": "Function", "delimiter": DELIMITER, "next": "Title"}, + {"name": "Title", "link": "Title", "re": r".*"}, +] + + +class Source(Base): + def __init__(self, vim: Nvim) -> None: + super().__init__(vim) + + self.name = "floaterm" + self.kind = "floaterm" + self._is_nvim = bool(vim.funcs.has("nvim")) + + def on_init(self, context: UserContext) -> None: + self.vim.call("floaterm#hide") + + def gather_candidates(self, context: UserContext) -> Candidates: + return ( + [{"word": "[open new floaterm]", "action__is_new": True}] + if "new" in context["args"] + else [ + self._make_candidate(x) + for x in self.vim.call("floaterm#buflist#gather") + ] + ) + + def highlight(self) -> None: + for i, syn in enumerate(FLOATERM_HIGHLIGHT_SYNTAX): + + def syn_name(key: str) -> str: + return "_".join([self.syntax_name, syn[key]]) + + self.vim.command( + f"highlight default link {syn_name('name')} {syn['link']}") + containedin = f" containedin={self.syntax_name}" if i == 0 else "" + nextgroup = f" nextgroup={syn_name('next')}" if "next" in syn else "" + if "delimiter" in syn: + self.vim.command( + "syntax region {0} matchgroup=Delimiter start=/{1}/ end=/{1}/ concealends contained{2}{3}".format( + syn_name( + "name"), syn["delimiter"], containedin, nextgroup + ) + ) + else: + self.vim.command( + "syntax match {0} /{1}/ contained{2}{3}".format( + syn_name("name"), syn["re"], containedin, nextgroup + ) + ) + + def _make_candidate(self, bufnr: int) -> Candidate: + name = self.vim.buffers[bufnr].name + title = self._term_title(bufnr) + return { + "word": name, + "abbr": f"{bufnr: >2} {DELIMITER}{name}{DELIMITER} {title}", + "action__bufnr": bufnr, + } + + def _term_title(self, bufnr: int) -> str: + return str( + self.vim.api.buf_get_var(bufnr, "term_title") + if self._is_nvim + else self.vim.funcs.term_gettitle(bufnr) + ) diff --git a/etc/soft/nvim/+plugins/vim-floaterm/test/command.vader b/etc/soft/nvim/+plugins/vim-floaterm/test/command.vader new file mode 100644 index 0000000..7d6b639 --- /dev/null +++ b/etc/soft/nvim/+plugins/vim-floaterm/test/command.vader @@ -0,0 +1,118 @@ +Execute (Setup functions): + function! AssertFiletype(filetype) abort + AssertEqual a:filetype, &filetype + endfunction + function! AssertBufnr(bufnr) abort + AssertEqual a:bufnr, bufnr('%') + endfunction + function! CheckWindow(if_floaterm) abort + let has_floaterm = 0 + let has_floaterm_border = 0 + for i in range(1, winnr('$')) + let filetype = getbufvar(winbufnr(i), '&filetype') + if filetype ==# 'floaterm' + let has_floaterm = 1 + elseif filetype ==# 'floaterm_border' + let has_floaterm_border = 1 + endif + endfor + if a:if_floaterm + AssertEqual 1, has_floaterm + AssertEqual 1, has_floaterm_border + else + AssertEqual 0, has_floaterm + AssertEqual 0, has_floaterm_border + endif + endfunction + +Execute (Get original bufnr): + let buffer0 = bufnr('%') + +Execute (Open first floaterm): + FloatermNew height=0.3 width=0.4 wintype=floating name=floating_floaterm_1 +Then: + call AssertFiletype('floaterm') + call CheckWindow(v:true) + let buffer1 = bufnr('%') + +Execute (Open second floaterm): + FloatermNew height=0.3 width=0.4 wintype=floating name=normal_floaterm_2 +Then: + call AssertFiletype('floaterm') + call CheckWindow(v:true) + let buffer2 = bufnr('%') + +Execute (Open third floaterm): + FloatermNew height=0.3 width=0.4 wintype=floating name=floating_floaterm_3 +Then: + call AssertFiletype('floaterm') + call CheckWindow(v:true) + let buffer3 = bufnr('%') + +Do (Toggle close floaterm): + \ +Then: + call AssertFiletype('') + call CheckWindow(v:false) + call AssertBufnr(buffer0) + +Execute (Toggle open floaterm): + FloatermToggle +Then: + call AssertFiletype('floaterm') + call CheckWindow(v:true) + call AssertBufnr(buffer3) + +Do (Toggle close floaterm): + \ +Then: + call AssertFiletype('') + call CheckWindow(v:false) + call AssertBufnr(buffer0) + +Execute (Next floaterm): + FloatermNext +Then: + call AssertFiletype('floaterm') + call CheckWindow(v:true) + call AssertBufnr(buffer1) + +Execute (Next floaterm): + FloatermNext +Then: + call AssertFiletype('floaterm') + call CheckWindow(v:true) + call AssertBufnr(buffer2) + +Execute (Next floaterm): + FloatermNext +Then: + call AssertFiletype('floaterm') + call CheckWindow(v:true) + call AssertBufnr(buffer3) + +Execute (Prev floaterm): + FloatermPrev +Then: + call AssertFiletype('floaterm') + call CheckWindow(v:true) + call AssertBufnr(buffer2) + +Execute (Prev floaterm): + FloatermPrev +Then: + call AssertFiletype('floaterm') + call CheckWindow(v:true) + call AssertBufnr(buffer1) + +Execute (Insert a floaterm between first and second): + FloatermNew +Then: + call AssertFiletype('floaterm') + call CheckWindow(v:true) + let buffer1_5 = bufnr('%') + AssertEqual floaterm#buflist#gather(), [buffer1,buffer1_5,buffer2,buffer3] + +Execute (Exit): + call floaterm#hide() + sleep 100m diff --git a/etc/soft/nvim/+plugins/vim-floaterm/test/keymap.vader b/etc/soft/nvim/+plugins/vim-floaterm/test/keymap.vader new file mode 100644 index 0000000..f210c01 --- /dev/null +++ b/etc/soft/nvim/+plugins/vim-floaterm/test/keymap.vader @@ -0,0 +1,118 @@ +Execute (Setup functions): + function! AssertFiletype(filetype) abort + AssertEqual a:filetype, &filetype + endfunction + function! AssertBufnr(bufnr) abort + AssertEqual a:bufnr, bufnr('%') + endfunction + function! CheckWindow(if_floaterm) abort + let has_floaterm = 0 + let has_floaterm_border = 0 + for i in range(1, winnr('$')) + let filetype = getbufvar(winbufnr(i), '&filetype') + if filetype ==# 'floaterm' + let has_floaterm = 1 + elseif filetype ==# 'floaterm_border' + let has_floaterm_border = 1 + endif + endfor + if a:if_floaterm + AssertEqual 1, has_floaterm + AssertEqual 1, has_floaterm_border + else + AssertEqual 0, has_floaterm + AssertEqual 0, has_floaterm_border + endif + endfunction + +Execute (Get original bufnr): + let buffer0 = bufnr('%') + +Do (Open first floaterm): + \ +Then: + call AssertFiletype('floaterm') + call CheckWindow(v:true) + let buffer1 = bufnr('%') + +Do (Open second floaterm): + \ +Then: + call AssertFiletype('floaterm') + call CheckWindow(v:true) + let buffer2 = bufnr('%') + +Do (Open third floaterm): + \ +Then: + call AssertFiletype('floaterm') + call CheckWindow(v:true) + let buffer3 = bufnr('%') + +Do (Toggle close floaterm): + \ +Then: + call AssertFiletype('') + call CheckWindow(v:false) + call AssertBufnr(buffer0) + +Do (Toggle open floaterm): + \ +Then: + call AssertFiletype('floaterm') + call CheckWindow(v:true) + call AssertBufnr(buffer3) + +Do (Toggle close floaterm): + \ +Then: + call AssertFiletype('') + call CheckWindow(v:false) + call AssertBufnr(buffer0) + +Do (Next floaterm): + \ +Then: + call AssertFiletype('floaterm') + call CheckWindow(v:true) + call AssertBufnr(buffer1) + +Do (Next floaterm): + \ +Then: + call AssertFiletype('floaterm') + call CheckWindow(v:true) + call AssertBufnr(buffer2) + +Do (Next floaterm): + \ +Then: + call AssertFiletype('floaterm') + call CheckWindow(v:true) + call AssertBufnr(buffer3) + +Do (Prev floaterm): + \ +Then: + call AssertFiletype('floaterm') + call CheckWindow(v:true) + call AssertBufnr(buffer2) + +Do (Prev floaterm): + \ +Then: + call AssertFiletype('floaterm') + call CheckWindow(v:true) + call AssertBufnr(buffer1) + +Do (Insert a floaterm between first and second): + \ +Then: + call AssertFiletype('floaterm') + call CheckWindow(v:true) + let buffer1_5 = bufnr('%') + AssertEqual floaterm#buflist#gather(), [buffer1,buffer1_5,buffer2,buffer3] + +Execute (Exit): + call floaterm#hide() + sleep 100m diff --git a/etc/soft/nvim/+plugins/vim-floaterm/test/vimrc b/etc/soft/nvim/+plugins/vim-floaterm/test/vimrc new file mode 100644 index 0000000..4966671 --- /dev/null +++ b/etc/soft/nvim/+plugins/vim-floaterm/test/vimrc @@ -0,0 +1,12 @@ +filetype off +let &runtimepath .= ',' . expand(':p:h:h') +let &runtimepath .= ',' . expand(':p:h:h') . '/vader.vim' +echom &runtimepath +filetype plugin indent on +syntax enable + +let g:floaterm_wintype = 'floating' +let g:floaterm_keymap_new = '' +let g:floaterm_keymap_prev = '' +let g:floaterm_keymap_next = '' +let g:floaterm_keymap_toggle = ''