├── .gitignore ├── LICENSE ├── README.md ├── doc ├── terminal_help.txt └── update.sh ├── images └── term.png ├── plugin └── terminal_help.vim └── tools └── utils ├── drop └── drop.cmd /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Generated tags 69 | doc/tags 70 | 71 | # Scrapy stuff: 72 | .scrapy 73 | 74 | # Sphinx documentation 75 | docs/_build/ 76 | 77 | # PyBuilder 78 | target/ 79 | 80 | # Jupyter Notebook 81 | .ipynb_checkpoints 82 | 83 | # IPython 84 | profile_default/ 85 | ipython_config.py 86 | 87 | # pyenv 88 | .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 98 | __pypackages__/ 99 | 100 | # Celery stuff 101 | celerybeat-schedule 102 | celerybeat.pid 103 | 104 | # SageMath parsed files 105 | *.sage.py 106 | 107 | # Environments 108 | .env 109 | .venv 110 | env/ 111 | venv/ 112 | ENV/ 113 | env.bak/ 114 | venv.bak/ 115 | 116 | # Spyder project settings 117 | .spyderproject 118 | .spyproject 119 | 120 | # Rope project settings 121 | .ropeproject 122 | 123 | # mkdocs documentation 124 | /site 125 | 126 | # mypy 127 | .mypy_cache/ 128 | .dmypy.json 129 | dmypy.json 130 | 131 | # Pyre type checker 132 | .pyre/ 133 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Linwei 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Preface 2 | 3 | I use vscode for markdown editing and vscode has a `CTRL+backtick` hot key to toggle internal terminal. I use it a lot and it's very handy. At the meantime both vim/nvim have internal terminal too, but most of us still prefer working in a tmux split rather than using the internal terminal. 4 | 5 | Therefor, I started to wonder, is there something I can do, to make vim's internal terminal has a better experience ? As a result, here this plugin exists with three little changes: 6 | 7 | Firstly, this plugin setup a keymap `ALT+=` (can be changed) to toggle terminal window, like vscode's `CTRL+backtick`. When you press `ALT+=` it will open a new terminal below your current window, and initiate the shell working directory to where the parent directory of current file. Most time you want to do something to the current file, so open the shell in the current file directory will make life easier. 8 | 9 | When you finished, just press `ALT+=` again to hide the terminal, so you always press `ALT+=` to toggle your terminal window, but if you run `exit` and quit the previous terminal session and hit `ALT+=` again, a new terminal will be created. 10 | 11 | In addition, you are able to use `ALT+SHIFT+h/j/k/l` to move around between windows. Most vim users uses `CTRL+h/j/k/l` for window switching, but these keys are heavily used in the terminal applications, for example if you use `tnoremap` to override `CTRL+j` or `CTRL+k`, you will not be able to use them in fzf. So `CTRL+h/j/k/l` will not be used for `tnoremap`, `terminal-help` encourage you to use the new `ALT+SHIFT+h/j/k/l` to jump between windows. 12 | 13 | Finally, it provides a `drop` command in the internal terminal to tell outside vim to open a file. When you are working in the internal terminal and you want to edit a file in the current directory (not vim's `pwd`), how do you do ? Especially the `pwd` in the terminal is different of vim's `pwd`. You have to switch to terminal normal mode and use vim `e` command with a long path name. 14 | 15 | With `drop` command, it is simple to tell outside vim open a specific file precisely: 16 | 17 | ```bash 18 | cd /xxx/some/where 19 | drop abc.txt 20 | ``` 21 | 22 | I always believe that small changes can make big difference. 23 | 24 | ## Requirements 25 | 26 | - vim: 8.1 27 | - neovim: 0.3 and install [neovim-remote](https://github.com/mhinz/neovim-remote) package if you need the drop command. 28 | 29 | ## Installation 30 | 31 | ```VimL 32 | Plug 'skywind3000/vim-terminal-help' 33 | ``` 34 | 35 | ## Usage 36 | 37 | - `ALT` + `=`: toggle terminal below. 38 | - `ALT` + `SHIFT` + `h`: move to the window on the left. 39 | - `ALT` + `SHIFT` + `l`: move to the window on the right. 40 | - `ALT` + `SHIFT` + `j`: move to the window below. 41 | - `ALT` + `SHIFT` + `k`: move to the window above. 42 | - `ALT` + `SHIFT` + `n`: move to the previous window. 43 | - `ALT` + `-`: paste register 0 to terminal. 44 | - `ALT` + `q`: switch to terminal normal mode. 45 | 46 | Inside the terminal: 47 | 48 | ```bash 49 | drop abc.txt 50 | ``` 51 | 52 | tell vim to open `abc.txt` 53 | 54 | ## Command 55 | 56 | This plugin provide a single command `H`: 57 | 58 | ```VimL 59 | :H {shell command} 60 | ``` 61 | 62 | You can type ":H uname -a" in vim's command line, it will send to the terminal directly without actually enter the terminal. 63 | 64 | 65 | ## Settings 66 | 67 | - `g:terminal_key`: which key will be used to toggle terminal window, default to ``. 68 | - `g:terminal_cwd`: initialize working dir: `0` for unchanged, `1` for file path and `2` for project root. 69 | - `g:terminal_height`: new terminal height, default to 10. 70 | - `g:terminal_pos`: where to open the terminal, default to `rightbelow`. 71 | - `g:terminal_shell`: specify shell rather than default one. 72 | - `g:terminal_edit`: command to open the file in vim, default to `tab drop`. 73 | - `g:terminal_kill`: set to `term` to kill term session when exiting vim. 74 | - `g:terminal_list`: set to 0 to hide terminal buffer in the buffer list. 75 | - `g:terminal_fixheight`: set to 1 to set `winfixheight` for the terminal window. 76 | - `g:terminal_close`: set to 1 to close window if process finished. 77 | 78 | 79 | ## Remember 80 | 81 | The internal terminal in both vim/neovim has `NORMAL` and `INSERT` mode. When you are in `INSERT` mode, you can enter shell commands. And if you want to scroll terminal screen or copy / paste texts to a normal vim buffer, you need to switch to `NORMAL` mode by `` (like tmux's `` + left square bracket). 82 | 83 | This plugin has defined a `` map for ``, which makes switching to terminal normal mode a little easier. 84 | 85 | If you want to re-enter `INSERT` mode, just press `i` or `a`, and you can input shell commands again. 86 | 87 | ## Integration 88 | 89 | This plugin defined a runner `thelp` for [asyncrun.vim](https://github.com/skywind3000/asyncrun.vim), which enables you use terminal-help to execute asyncrun commands: 90 | 91 | ```VimL 92 | :AsyncRun -mode=term -pos=thelp echo 123 93 | ``` 94 | 95 | And it is also useful when you are using [asynctasks.vim](https://github.com/skywind3000/asynctasks.vim): 96 | 97 | ```ini 98 | [test-task] 99 | command=echo 123 100 | output=terminal 101 | pos=thelp 102 | ``` 103 | 104 | You can either set `g:asynctasks_term_pos` to `thelp` or use `pos` field in task option directly. After this: 105 | 106 | ```VimL 107 | :AsyncTask test-task 108 | ``` 109 | 110 | Can run the task in the terminal-help's window. 111 | 112 | -------------------------------------------------------------------------------- /doc/terminal_help.txt: -------------------------------------------------------------------------------- 1 | *terminal_help* Preface 2 | 3 | =============================================================================== 4 | Contents ~ 5 | 6 | 1. Introduction |terminal_help-introduction| 7 | 2. Requirements |terminal_help-requirements| 8 | 3. Installation |terminal_help-installation| 9 | 4. Usage |terminal_help-usage| 10 | 5. Settings |terminal_help-settings| 11 | 6. Remember |terminal_help-remember| 12 | 7. Integration |terminal_help-integration| 13 | 8. References |terminal_help-references| 14 | 15 | =============================================================================== 16 | *terminal_help-introduction* 17 | Introduction ~ 18 | 19 | I use vscode for markdown editing and vscode has a 'CTRL+backtick' hot key to 20 | toggle internal terminal. I use it a lot and it's very handy. At the meantime 21 | both vim/nvim have internal terminal too, but most of us still prefer working 22 | in a tmux split rather than using the internal terminal. 23 | 24 | Therefor, I started to wonder, is there something I can do, to make vim's 25 | internal terminal has a better experience ? As a result, here this plugin 26 | exists with three little changes: 27 | 28 | Firstly, this plugin setup a keymap 'ALT+=' (can be changed) to toggle terminal 29 | window, like vscode's 'CTRL+backtick'. When you press 'ALT+=' it will open a 30 | new terminal below your current window, and initiate the shell working 31 | directory to where the parent directory of current file. Most time you want to 32 | do something to the current file, so open the shell in the current file 33 | directory will make life easier. 34 | 35 | When you finished, just press 'ALT+=' again to hide the terminal, so you always 36 | press 'ALT+=' to toggle your terminal window, but if you run 'exit' and quit 37 | the previous terminal session and hit 'ALT+=' again, a new terminal will be 38 | created. 39 | 40 | In addition, you are able to use 'ALT+SHIFT+h/j/k/l' to move around between 41 | windows. Most vim users uses 'CTRL+h/j/k/l' for window switching, but these 42 | keys are heavily used in the terminal applications, for example if you use 43 | 'tnoremap' to override 'CTRL+j' or 'CTRL+k', you will not be able to use them 44 | in fzf. So 'CTRL+h/j/k/l' will not be used for 'tnoremap', 'terminal-help' 45 | encourage you to use the new 'ALT+SHIFT+h/j/k/l' to jump between windows. 46 | 47 | Finally, it provides a 'drop' command in the internal terminal to tell outside 48 | vim to open a file. When you are working in the internal terminal and you want 49 | to edit a file in the current directory (not vim's 'pwd'), how do you do ? 50 | Especially the 'pwd' in the terminal is different of vim's 'pwd'. You have to 51 | switch to terminal normal mode and use vim 'e' command with a long path name. 52 | 53 | With 'drop' command, it is simple to tell outside vim open a specific file 54 | precisely: 55 | > 56 | cd /xxx/some/where 57 | drop abc.txt 58 | < 59 | I always believe that small changes can make big difference. 60 | 61 | =============================================================================== 62 | *terminal_help-requirements* 63 | Requirements ~ 64 | 65 | - vim: 8.1 66 | - neovim: 0.3 and install neovim-remote [1] package if you need the drop 67 | command. 68 | 69 | =============================================================================== 70 | *terminal_help-installation* 71 | Installation ~ 72 | > 73 | Plug 'skywind3000/vim-terminal-help' 74 | < 75 | =============================================================================== 76 | *terminal_help-usage* 77 | Usage ~ 78 | 79 | - 'ALT' + '=': toggle terminal below. 80 | - 'ALT' + 'SHIFT' + 'h': move to the window on the left. 81 | - 'ALT' + 'SHIFT' + 'l': move to the window on the right. 82 | - 'ALT' + 'SHIFT' + 'j': move to the window below. 83 | - 'ALT' + 'SHIFT' + 'k': move to the window above. 84 | - 'ALT' + 'SHIFT' + 'p': move to the previous window. 85 | - 'ALT' + '-': paste register 0 to terminal. 86 | - 'ALT' + 'q': switch to terminal normal mode. 87 | 88 | Inside the terminal: 89 | > 90 | drop abc.txt 91 | < 92 | tell vim to open 'abc.txt' 93 | 94 | =============================================================================== 95 | *terminal_help-settings* 96 | Settings ~ 97 | 98 | - 'g:terminal_key': which key will be used to toggle terminal window, default 99 | to ''. 100 | - 'g:terminal_cwd': initialize working dir: '0' for unchanged, '1' for file 101 | path and '2' for project root. 102 | - 'g:terminal_height': new terminal height, default to 10. 103 | - 'g:terminal_pos': where to open the terminal, default to 'rightbelow'. 104 | - 'g:terminal_shell': specify shell rather than default one. 105 | - 'g:terminal_edit': command to open the file in vim, default to 'tab drop'. 106 | - 'g:terminal_kill': set to 'term' to kill term session when exiting vim. 107 | - 'g:terminal_list': set to 0 to hide terminal buffer in the buffer list. 108 | - 'g:terminal_fixheight': set to 1 to set 'winfixheight' for the terminal 109 | window. 110 | - 'g:terminal_close': set to 1 to close window if process finished. 111 | 112 | =============================================================================== 113 | *terminal_help-remember* 114 | Remember ~ 115 | 116 | The internal terminal in both vim/neovim has 'NORMAL' and 'INSERT' mode. When 117 | you are in 'INSERT' mode, you can enter shell commands. And if you want to 118 | scroll terminal screen or copy / paste texts to a normal vim buffer, you need 119 | to switch to 'NORMAL' mode by '' (like tmux's '' + left square 120 | bracket). 121 | 122 | This plugin has defined a '' map for '', which makes switching 123 | to terminal normal mode a little easier. 124 | 125 | If you want to re-enter 'INSERT' mode, just press 'i' or 'a', and you can input 126 | shell commands again. 127 | 128 | =============================================================================== 129 | *terminal_help-integration* 130 | Integration ~ 131 | 132 | This plugin defined a runner 'thelp' for asyncrun.vim [2], which enables you 133 | use terminal-help to execute asyncrun commands: 134 | > 135 | :AsyncRun -mode=term -pos=thelp echo 123 136 | < 137 | And it is also useful when you are using asynctasks.vim [3]: 138 | > 139 | [test-task] 140 | command=echo 123 141 | output=terminal 142 | pos=thelp 143 | < 144 | You can either set 'g:asynctasks_term_pos' to 'thelp' or use 'pos' field in 145 | task option directly. After this: 146 | > 147 | :AsyncTask test-task 148 | < 149 | Can run the task in the terminal-help's window. 150 | 151 | =============================================================================== 152 | *terminal_help-references* 153 | References ~ 154 | 155 | [1] https://github.com/mhinz/neovim-remote 156 | [2] https://github.com/skywind3000/asyncrun.vim 157 | [3] https://github.com/skywind3000/asynctasks.vim 158 | 159 | vim: ft=help 160 | -------------------------------------------------------------------------------- /doc/update.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | ~/github/vim-tools/html2vimdoc/bin/python ~/github/vim-tools/html2vimdoc.py -f terminal_help ../README.md > terminal_help.txt 3 | 4 | -------------------------------------------------------------------------------- /images/term.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skywind3000/vim-terminal-help/ffbec497306b89da99055170a12daead8fca904e/images/term.png -------------------------------------------------------------------------------- /plugin/terminal_help.vim: -------------------------------------------------------------------------------- 1 | "====================================================================== 2 | " 3 | " terminal_help.vim - 4 | " 5 | " Created by skywind on 2020/01/01 6 | " Last Modified: 2020/03/19 18:33 7 | " 8 | "====================================================================== 9 | 10 | "---------------------------------------------------------------------- 11 | " check compatible 12 | "---------------------------------------------------------------------- 13 | if has('patch-8.1.1') == 0 && has('nvim') == 0 14 | finish 15 | endif 16 | 17 | 18 | "---------------------------------------------------------------------- 19 | " configuration 20 | "---------------------------------------------------------------------- 21 | 22 | " which key is used to toggle terminal 23 | if !exists('g:terminal_key') 24 | let g:terminal_key = '' 25 | endif 26 | 27 | " initialize shell directory 28 | " 0: vim's current working directory (which :pwd returns) 29 | " 1: file path of current file. 30 | " 2: project root of current file. 31 | if !exists('g:terminal_cwd') 32 | let g:terminal_cwd = 1 33 | endif 34 | 35 | " root markers to identify the project root 36 | if !exists('g:terminal_rootmarkers') 37 | let g:terminal_rootmarkers = ['.git', '.svn', '.project', '.root', '.hg'] 38 | endif 39 | 40 | if !exists('g:terminal_newline') 41 | let g:terminal_newline = "\r" 42 | endif 43 | 44 | "---------------------------------------------------------------------- 45 | " Initialize 46 | "---------------------------------------------------------------------- 47 | let $VIM_SERVERNAME = v:servername 48 | let $VIM_EXE = v:progpath 49 | 50 | let s:home = fnamemodify(resolve(expand(':p')), ':h') 51 | let s:script = fnamemodify(s:home . '/../tools/utils', ':p') 52 | let s:windows = has('win32') || has('win64') || has('win95') || has('win16') 53 | 54 | " setup PATH for utils 55 | if stridx($PATH, s:script) < 0 56 | if s:windows == 0 57 | let $PATH .= ':' . s:script 58 | else 59 | let $PATH .= ';' . s:script 60 | endif 61 | endif 62 | 63 | " search for neovim-remote for nvim 64 | let $VIM_NVR = '' 65 | if has('nvim') 66 | let name = get(g:, 'terminal_nvr', 'nvr') 67 | if executable(name) 68 | let $VIM_NVR=name 69 | endif 70 | endif 71 | 72 | 73 | "---------------------------------------------------------------------- 74 | " internal utils 75 | "---------------------------------------------------------------------- 76 | 77 | " returns nearest parent directory contains one of the markers 78 | function! s:find_root(name, markers, strict) 79 | let name = fnamemodify((a:name != '')? a:name : bufname('%'), ':p') 80 | let finding = '' 81 | " iterate all markers 82 | for marker in a:markers 83 | if marker != '' 84 | " search as a file 85 | let x = findfile(marker, name . '/;') 86 | let x = (x == '')? '' : fnamemodify(x, ':p:h') 87 | " search as a directory 88 | let y = finddir(marker, name . '/;') 89 | let y = (y == '')? '' : fnamemodify(y, ':p:h:h') 90 | " which one is the nearest directory ? 91 | let z = (strchars(x) > strchars(y))? x : y 92 | " keep the nearest one in finding 93 | let finding = (strchars(z) > strchars(finding))? z : finding 94 | endif 95 | endfor 96 | if finding == '' 97 | let path = (a:strict == 0)? fnamemodify(name, ':h') : '' 98 | else 99 | let path = fnamemodify(finding, ':p') 100 | endif 101 | if has('win32') || has('win16') || has('win64') || has('win95') 102 | let path = substitute(path, '\/', '\', 'g') 103 | endif 104 | if path =~ '[\/\\]$' 105 | let path = fnamemodify(path, ':h') 106 | endif 107 | return path 108 | endfunc 109 | 110 | " returns project root of current file 111 | function! s:project_root() 112 | let name = expand('%:p') 113 | return s:find_root(name, g:terminal_rootmarkers, 0) 114 | endfunc 115 | 116 | 117 | "---------------------------------------------------------------------- 118 | " open a new/previous terminal 119 | "---------------------------------------------------------------------- 120 | function! TerminalOpen(...) 121 | let bid = get(t:, '__terminal_bid__', -1) 122 | let pos = get(g:, 'terminal_pos', 'rightbelow') 123 | let height = get(g:, 'terminal_height', 10) 124 | let succeed = 0 125 | function! s:terminal_view(mode) 126 | if a:mode == 0 127 | let w:__terminal_view__ = winsaveview() 128 | elseif exists('w:__terminal_view__') 129 | call winrestview(w:__terminal_view__) 130 | unlet w:__terminal_view__ 131 | endif 132 | endfunc 133 | let uid = win_getid() 134 | keepalt noautocmd windo call s:terminal_view(0) 135 | keepalt noautocmd call win_gotoid(uid) 136 | if bid > 0 137 | let name = bufname(bid) 138 | if name != '' 139 | let wid = bufwinnr(bid) 140 | if wid < 0 141 | exec pos . ' ' . height . 'split' 142 | exec 'b '. bid 143 | if mode() != 't' 144 | if has('nvim') 145 | startinsert 146 | else 147 | exec "normal! i" 148 | endif 149 | endif 150 | else 151 | exec "normal! ". wid . "\w" 152 | endif 153 | let succeed = 1 154 | endif 155 | endif 156 | if has('nvim') 157 | let cd = haslocaldir()? 'lcd' : (haslocaldir(-1, 0)? 'tcd' : 'cd') 158 | else 159 | let cd = haslocaldir()? ((haslocaldir() == 1)? 'lcd' : 'tcd') : 'cd' 160 | endif 161 | if succeed == 0 162 | let shell = get(g:, 'terminal_shell', '') 163 | let command = (shell != '')? shell : &shell 164 | let close = get(g:, 'terminal_close', 0) 165 | if has('nvim') && 0 166 | for ii in range(winnr('$') + 8) 167 | let info = nvim_win_get_config(0) 168 | if has_key(info, 'anchor') == 0 169 | break 170 | endif 171 | keepalt noautocmd exec "normal! \w" 172 | endfor 173 | let uid = win_getid() 174 | endif 175 | let savedir = getcwd() 176 | if &bt == '' 177 | if g:terminal_cwd == 1 178 | let workdir = (expand('%') == '')? getcwd() : expand('%:p:h') 179 | silent noautocmd execute cd . ' '. fnameescape(workdir) 180 | elseif g:terminal_cwd == 2 181 | silent noautocmd execute cd . ' '. fnameescape(s:project_root()) 182 | endif 183 | endif 184 | if has('nvim') == 0 185 | exec pos . ' ' . height . 'split' 186 | let opts = {'curwin':1, 'norestore':1, 'term_finish':'open'} 187 | let opts.term_kill = get(g:, 'terminal_kill', 'term') 188 | let opts.exit_cb = function('s:terminal_exit') 189 | let bid = term_start(command, opts) 190 | setlocal nonumber norelativenumber signcolumn=no 191 | let jid = term_getjob(bid) 192 | let b:__terminal_jid__ = jid 193 | else 194 | exec pos . ' ' . height . 'split' 195 | exec 'enew' 196 | let opts = {} 197 | let opts.on_exit = function('s:terminal_exit') 198 | let jid = termopen(command, opts) 199 | setlocal nonumber norelativenumber signcolumn=no 200 | let b:__terminal_jid__ = jid 201 | startinsert 202 | endif 203 | silent noautocmd execute cd . ' '. fnameescape(savedir) 204 | let t:__terminal_bid__ = bufnr('') 205 | setlocal bufhidden=hide 206 | if get(g:, 'terminal_list', 1) == 0 207 | setlocal nobuflisted 208 | endif 209 | if get(g:, 'terminal_auto_insert', 0) != 0 210 | if has('nvim') == 0 211 | autocmd WinEnter exec "normal! i" 212 | else 213 | autocmd WinEnter startinsert 214 | endif 215 | endif 216 | endif 217 | let x = win_getid() 218 | noautocmd windo call s:terminal_view(1) 219 | noautocmd call win_gotoid(uid) " take care of previous window 220 | noautocmd call win_gotoid(x) 221 | if get(g:, 'terminal_fixheight', 0) 222 | setlocal winfixheight 223 | endif 224 | endfunc 225 | 226 | 227 | "---------------------------------------------------------------------- 228 | " hide terminal 229 | "---------------------------------------------------------------------- 230 | function! TerminalClose() 231 | let bid = get(t:, '__terminal_bid__', -1) 232 | if bid < 0 233 | return 234 | endif 235 | let name = bufname(bid) 236 | if name == '' 237 | return 238 | endif 239 | let wid = bufwinnr(bid) 240 | if wid < 0 241 | return 242 | endif 243 | let sid = win_getid() 244 | noautocmd windo call s:terminal_view(0) 245 | call win_gotoid(sid) 246 | if wid != winnr() 247 | let uid = win_getid() 248 | exec "normal! ". wid . "\w" 249 | close 250 | call win_gotoid(uid) 251 | else 252 | close 253 | endif 254 | let sid = win_getid() 255 | noautocmd windo call s:terminal_view(1) 256 | call win_gotoid(sid) 257 | let jid = getbufvar(bid, '__terminal_jid__', -1) 258 | let dead = 0 259 | if has('nvim') == 0 260 | if type(jid) == v:t_job 261 | let dead = (job_status(jid) == 'dead')? 1 : 0 262 | endif 263 | else 264 | if jid >= 0 265 | try 266 | let pid = jobpid(jid) 267 | catch /^Vim\%((\a\+)\)\=:E900:/ 268 | let dead = 1 269 | endtry 270 | endif 271 | endif 272 | if dead 273 | exec 'bdelete! '. bid 274 | endif 275 | endfunc 276 | 277 | 278 | "---------------------------------------------------------------------- 279 | " process exit callback 280 | "---------------------------------------------------------------------- 281 | function! s:terminal_exit(...) 282 | let close = get(g:, 'terminal_close', 0) 283 | if close != 0 284 | let bid = get(t:, '__terminal_bid__', -1) 285 | let alive = 0 286 | if bid > 0 && bufname(bid) != '' 287 | let alive = (bufwinnr(bid) > 0)? 1 : 0 288 | endif 289 | if alive 290 | call TerminalClose() 291 | elseif bid > 0 292 | exec 'bdelete! '.bid 293 | endif 294 | endif 295 | endfunc 296 | 297 | 298 | "---------------------------------------------------------------------- 299 | " toggle open/close 300 | "---------------------------------------------------------------------- 301 | function! TerminalToggle() 302 | let bid = get(t:, '__terminal_bid__', -1) 303 | let alive = 0 304 | if bid > 0 && bufname(bid) != '' 305 | let alive = (bufwinnr(bid) > 0)? 1 : 0 306 | endif 307 | if alive == 0 308 | call TerminalOpen() 309 | else 310 | call TerminalClose() 311 | endif 312 | endfunc 313 | 314 | 315 | "---------------------------------------------------------------------- 316 | " send text to terminal 317 | "---------------------------------------------------------------------- 318 | function! TerminalSend(text) 319 | let bid = get(t:, '__terminal_bid__', -1) 320 | let alive = 0 321 | if bid > 0 && bufname(bid) != '' 322 | let wid = bufwinnr(bid) 323 | if wid > 0 324 | let alive = (bufname(bid) != '')? 1 : 0 325 | endif 326 | endif 327 | " check if buffer exists 328 | if alive 329 | " check if job stopped 330 | let jid = getbufvar(bid, '__terminal_jid__', -1) 331 | if has('nvim') == 0 332 | if type(jid) == v:t_job 333 | let alive = (job_status(jid) == 'dead')? 0 : 1 334 | endif 335 | else 336 | if jid >= 0 337 | try 338 | let pid = jobpid(jid) 339 | catch /^Vim\%((\a\+)\)\=:E900:/ 340 | let alive = 0 341 | endtry 342 | endif 343 | endif 344 | endif 345 | let x = win_getid() 346 | if alive == 0 347 | call TerminalClose() 348 | call TerminalOpen() 349 | if has('nvim') 350 | stopinsert 351 | endif 352 | endif 353 | let bid = get(t:, '__terminal_bid__', -1) 354 | if bid > 0 355 | let jid = getbufvar(bid, '__terminal_jid__', '') 356 | if string(jid) != '' 357 | if has('nvim') == 0 358 | let ch = job_getchannel(jid) 359 | call ch_sendraw(ch, a:text) 360 | else 361 | call chansend(jid, a:text) 362 | endif 363 | endif 364 | endif 365 | if has('nvim') 366 | let bid = get(t:, '__terminal_bid__', -1) 367 | if bid > 0 && bufname(bid) != '' 368 | let wid = bufwinnr(bid) 369 | if wid > 0 370 | exec '' . wid . 'wincmd w' 371 | endif 372 | startinsert 373 | stopinsert 374 | exec 'normal! G' 375 | endif 376 | endif 377 | call win_gotoid(x) 378 | endfunc 379 | 380 | 381 | "---------------------------------------------------------------------- 382 | " command H to send 383 | "---------------------------------------------------------------------- 384 | command! -nargs=* H call TerminalSend( . g:terminal_newline) 385 | 386 | 387 | "---------------------------------------------------------------------- 388 | " can be called from internal terminal. 389 | "---------------------------------------------------------------------- 390 | function! Tapi_TerminalEdit(bid, arglist) 391 | let name = (type(a:arglist) == v:t_string)? a:arglist : a:arglist[0] 392 | let cmd = get(g:, 'terminal_edit', 'tab drop') 393 | silent exec cmd . ' ' . fnameescape(name) 394 | return '' 395 | endfunc 396 | 397 | 398 | "---------------------------------------------------------------------- 399 | " enable alt key in terminal vim 400 | "---------------------------------------------------------------------- 401 | if has('nvim') == 0 && has('gui_running') == 0 402 | set ttimeout 403 | if $TMUX != '' 404 | set ttimeoutlen=35 405 | elseif &ttimeoutlen > 80 || &ttimeoutlen <= 0 406 | set ttimeoutlen=85 407 | endif 408 | function! s:meta_code(key) 409 | if get(g:, 'terminal_skip_key_init', 0) == 0 410 | exec "set =\e".a:key 411 | endif 412 | endfunc 413 | for i in range(10) 414 | call s:meta_code(nr2char(char2nr('0') + i)) 415 | endfor 416 | for i in range(26) 417 | call s:meta_code(nr2char(char2nr('a') + i)) 418 | endfor 419 | for i in range(15) + range(16, 25) 420 | call s:meta_code(nr2char(char2nr('A') + i)) 421 | endfor 422 | for c in [',', '.', '/', ';', '{', '}'] 423 | call s:meta_code(c) 424 | endfor 425 | for c in ['?', ':', '-', '_', '+', '=', "'"] 426 | call s:meta_code(c) 427 | endfor 428 | function! s:key_escape(name, code) 429 | if get(g:, 'terminal_skip_key_init', 0) == 0 430 | exec "set ".a:name."=\e".a:code 431 | endif 432 | endfunc 433 | call s:key_escape('', 'OP') 434 | call s:key_escape('', 'OQ') 435 | call s:key_escape('', 'OR') 436 | call s:key_escape('', 'OS') 437 | endif 438 | 439 | 440 | "---------------------------------------------------------------------- 441 | " fast window switching: ALT+SHIFT+HJKL 442 | "---------------------------------------------------------------------- 443 | if get(g:, 'terminal_default_mapping', 1) 444 | noremap h 445 | noremap l 446 | noremap j 447 | noremap k 448 | inoremap h 449 | inoremap l 450 | inoremap j 451 | inoremap k 452 | 453 | if has('terminal') && exists(':terminal') == 2 && has('patch-8.1.1') 454 | set termwinkey= 455 | tnoremap h 456 | tnoremap l 457 | tnoremap j 458 | tnoremap k 459 | tnoremap p 460 | tnoremap 461 | tnoremap "0 462 | elseif has('nvim') 463 | tnoremap h 464 | tnoremap l 465 | tnoremap j 466 | tnoremap k 467 | tnoremap p 468 | tnoremap 469 | tnoremap "0pa 470 | endif 471 | 472 | let s:cmd = 'nnoremap '.(g:terminal_key). ' ' 473 | exec s:cmd . ':call TerminalToggle()' 474 | 475 | if has('nvim') == 0 476 | let s:cmd = 'tnoremap '.(g:terminal_key). ' ' 477 | exec s:cmd . ':call TerminalToggle()' 478 | else 479 | let s:cmd = 'tnoremap '.(g:terminal_key). ' ' 480 | exec s:cmd . ':call TerminalToggle()' 481 | endif 482 | endif 483 | 484 | 485 | "---------------------------------------------------------------------- 486 | " drop a file and ask user to select a window for dropping if there 487 | " are multiple modifiable windows 488 | "---------------------------------------------------------------------- 489 | function! s:SelectiveDrop(filename) 490 | let modifiable_wins = [] 491 | for i in range(1, winnr('$')) 492 | if expand('#' . winbufnr(i) . ':p') ==# fnamemodify(a:filename, ':p') 493 | execute i . 'wincmd w' 494 | return 495 | endif 496 | 497 | if getwinvar(i, '&modifiable') 498 | call add(modifiable_wins, i) 499 | endif 500 | endfor 501 | let escaped_filename = fnameescape(a:filename) 502 | if len(modifiable_wins) >= 2 503 | let key = char2nr('A') 504 | let saved_statuslines = {} 505 | let choices = [] 506 | for i in modifiable_wins 507 | let saved_statuslines[i] = getwinvar(i, '&statusline') 508 | let c = nr2char(key) 509 | let w = winwidth(i) 510 | call setwinvar(i, '&statusline', '%#Search#' . repeat(' ', (w - 1) / 2) . c . repeat(' ', w - 1 - (w - 1) / 2)) 511 | call add(choices, c) 512 | let key += 1 513 | endfor 514 | redraw 515 | echohl ModeMsg | echon 'Choose window (' . join(choices, '/') . '): ' | echohl None 516 | let choice_nr = getchar() 517 | let choice = index(choices, toupper(nr2char(choice_nr))) 518 | for [i, saved_statusline] in items(saved_statuslines) 519 | call setwinvar(i, '&statusline', saved_statusline) 520 | endfor 521 | if choice >= 0 522 | execute modifiable_wins[choice] . 'wincmd w' 523 | execute 'edit ' . escaped_filename 524 | endif 525 | redraw 526 | echon 527 | elseif len(modifiable_wins) == 1 528 | execute modifiable_wins[0] . 'wincmd w' 529 | execute 'edit ' . escaped_filename 530 | else 531 | execute 'split ' . escaped_filename 532 | endif 533 | endfunction 534 | 535 | command! -complete=file -nargs=1 SelectiveDrop call SelectiveDrop() 536 | 537 | " set twt=conpty 538 | 539 | "---------------------------------------------------------------------- 540 | " new asyncrun runner: 'thelp' 541 | "---------------------------------------------------------------------- 542 | function! s:runner_proc(opts) 543 | let cwd = getcwd() 544 | let cd = (has('win32') || has('win64') || has('win16'))? 'cd /D' : 'cd' 545 | let cd = get(g:, 'terminal_cd', cd) 546 | call TerminalSend(cd . ' ' . shellescape(cwd) . g:terminal_newline) 547 | call TerminalSend(a:opts.cmd . g:terminal_newline) 548 | endfunc 549 | 550 | let g:asyncrun_runner = get(g:, 'asyncrun_runner', {}) 551 | let g:asyncrun_runner.thelp = function('s:runner_proc') 552 | 553 | 554 | 555 | -------------------------------------------------------------------------------- /tools/utils/drop: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | if [ -z "$1" ]; then 4 | echo "usage: drop {filename}" 5 | exit 0 6 | fi 7 | 8 | if [ -z "$VIM_EXE" ]; then 9 | echo "Must be called inside vim/neovim" 10 | exit 1 11 | fi 12 | 13 | absolute_path() { 14 | local result="" 15 | if [ -x "$(which realpath 2> /dev/null)" ]; then 16 | result="$(realpath -s """$1""" 2> /dev/null)" 17 | fi 18 | if [ -z "$result" ] && [ -x "$(which perl 2> /dev/null)" ]; then 19 | result="$(perl -MCwd -e 'print Cwd::realpath($ARGV[0])' """$1""")" 20 | fi 21 | if [ -z "$result" ] && [ -x "$(which python 2> /dev/null)" ]; then 22 | result="$(python -c 'import sys, os;sys.stdout.write(os.path.abspath(sys.argv[1]))' """$1""")" 23 | fi 24 | if [ -z "$result" ] && [ -x "$(which python3 2> /dev/null)" ]; then 25 | result="$(python3 -c 'import sys, os;sys.stdout.write(os.path.abspath(sys.argv[1]))' """$1""")" 26 | fi 27 | if [ -z "$result" ] && [ -x "$(which python2 2> /dev/null)" ]; then 28 | result="$(python2 -c 'import sys, os;sys.stdout.write(os.path.abspath(sys.argv[1]))' """$1""")" 29 | fi 30 | if [ -z "$result" ] && [ -x "$(which realpath 2> /dev/null)" ]; then 31 | result="$(realpath """$1""")" 32 | fi 33 | if [ -z "$result" ]; then 34 | result="$1" 35 | fi 36 | echo $result 37 | } 38 | 39 | name="$(absolute_path """$1""")" 40 | 41 | if [ -z "$NVIM_LISTEN_ADDRESS" ]; then 42 | printf '\033]51;["call", "Tapi_TerminalEdit", ["%s"]]\007' "$name" 43 | else 44 | if [ -x "$(which nvr 2> /dev/null)" ]; then 45 | nvr --servername "$VIM_SERVERNAME" --remote-expr "Tapi_TerminalEdit(0, '$name')" 46 | else 47 | echo "cannot find nvr executable, please install neovim-remote" 48 | exit 2 49 | fi 50 | fi 51 | 52 | 53 | -------------------------------------------------------------------------------- /tools/utils/drop.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal EnableDelayedExpansion 3 | 4 | if "%1" == "" GOTO help 5 | if "%VIM_EXE%" == "" GOTO missing 6 | 7 | REM Get absolute name 8 | for /f "delims=" %%i in ("%1") do set "NAME=%%~fi" 9 | REM echo fullpath: %NAME% 10 | 11 | if "%NVIM_LISTEN_ADDRESS%" == "" GOTO vim 12 | goto neovim 13 | 14 | :vim 15 | call "%VIM_EXE%" --servername "%VIM_SERVERNAME%" --remote-expr "Tapi_TerminalEdit(0, '%NAME%')" 16 | goto end 17 | 18 | :neovim 19 | call "nvr" --servername "%VIM_SERVERNAME%" --remote-expr "Tapi_TerminalEdit(0, '%NAME%')" 20 | goto end 21 | 22 | :nonvr 23 | echo missing nvr, you need install neovim-remote 24 | goto end 25 | 26 | :help 27 | echo usage: drop {filename} 28 | goto end 29 | 30 | :missing 31 | echo Must be called inside vim/neovim 32 | goto end 33 | 34 | 35 | :end 36 | 37 | 38 | --------------------------------------------------------------------------------