├── ftplugin ├── scala.vim ├── coffee.vim ├── haskell.vim ├── python.vim ├── scheme.vim └── racket.vim ├── LICENSE ├── README.md └── plugin └── slimux.vim /ftplugin/scala.vim: -------------------------------------------------------------------------------- 1 | function! SlimuxPre_scala(target_pane) 2 | call system("tmux send-keys -t " . a:target_pane . " :paste C-m") 3 | endfunction 4 | 5 | function! SlimuxPost_scala(target_pane) 6 | call system("tmux send-keys -t " . a:target_pane . " C-d") 7 | endfunction 8 | -------------------------------------------------------------------------------- /ftplugin/coffee.vim: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | " CoffeeScript REPL enters multi-line mode with Ctrl+v 5 | function! SlimuxPre_coffee(target_pane) 6 | call system("tmux send-keys -t " . a:target_pane . " C-v") 7 | endfunction 8 | 9 | " Exit multi-line REPL mode with another Ctrl+v 10 | function! SlimuxPost_coffee(target_pane) 11 | call system("tmux send-keys -t " . a:target_pane . " C-v") 12 | endfunction 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Licensed under the standard MIT license: 2 | 3 | Copyright 2012 Esa-Matti Suuronen 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /ftplugin/haskell.vim: -------------------------------------------------------------------------------- 1 | let s:not_prefixable_keywords = [ "import", "data", "instance", "class", "{-#", "type", "case", "do", "let", "default", "foreign", "--"] 2 | let s:spaces = repeat(" ", 4) 3 | let s:tab = " " 4 | 5 | function! ProcessLines(lines) 6 | let l:lines = a:lines 7 | " skip empty lines 8 | let l:first_line = 0 9 | while l:lines[l:first_line] == "" 10 | let first_line += 1 11 | endwhile 12 | 13 | let l:word = split(l:lines[l:first_line], " ")[0] 14 | 15 | if index(s:not_prefixable_keywords, l:word) < 0 16 | " prepend let in the first line 17 | let l:lines[l:first_line] = "let " . l:lines[l:first_line] 18 | 19 | " indent the remaining lines 20 | let l:i = l:first_line + 1 21 | while l:i < len(l:lines) 22 | if l:lines[l:i] != "" 23 | let l:lines[l:i] = s:spaces . l:lines[l:i] 24 | endif 25 | 26 | let l:i += 1 27 | endwhile 28 | return l:lines 29 | else 30 | return l:lines 31 | endif 32 | endfunction 33 | 34 | function! StripComments(lines) 35 | let l:lines = a:lines 36 | let l:ret = [] 37 | let l:i = 0 38 | while l:i < len(l:lines) 39 | let l:stripped_spaces = substitute(l:lines[l:i], "^ *", "", "") 40 | if strpart(l:stripped_spaces, 0, 2) != "--" 41 | call add(l:ret, l:lines[l:i]) 42 | endif 43 | 44 | let l:i += 1 45 | endwhile 46 | return l:ret 47 | endfunction 48 | 49 | function! SlimuxEscape_haskell(text) 50 | let l:text = substitute(a:text, s:tab, s:spaces, "g") 51 | let l:lines = split(l:text, "\n") 52 | let l:lines = StripComments(l:lines) 53 | let l:lines = ProcessLines(l:lines) 54 | let l:lines = [":{"] + l:lines + [":}"] 55 | 56 | return join(l:lines, "\n") . "\n" 57 | endfunction 58 | -------------------------------------------------------------------------------- /ftplugin/python.vim: -------------------------------------------------------------------------------- 1 | " These python keywords should not have extra newline at indentation level 0 2 | let w:slimux_python_allowed_indent0 = ["elif", "else", "except", "finally"] 3 | 4 | 5 | function! SlimuxEscape_python(text) 6 | "" Check if last line is empty in multiline selections 7 | let l:last_line_empty = match(a:text,'\n\W*\n$') 8 | 9 | "" Remove all empty lines and use soft linebreaks 10 | let no_empty_lines = substitute(a:text, '\n\s*\ze\n', "", "g") 11 | let no_empty_lines = substitute(no_empty_lines, "\n", " ", "g") 12 | 13 | "" See if any non-empty lines sent at all 14 | if no_empty_lines == " " 15 | return " " 16 | endif 17 | 18 | "" Process line by line and insert needed linebreaks 19 | let l:non_processed_lines = split(no_empty_lines," ") 20 | let l:processed_lines = [l:non_processed_lines[0]] 21 | " Check initial indent level 22 | let l:first_word = matchstr(l:processed_lines[0],'^[a-zA-Z\"]\+') 23 | if !(l:first_word == "") 24 | let l:at_indent0 = 1 25 | else 26 | let l:at_indent0 = 0 27 | endif 28 | " Only actually anything to do if more than one line 29 | if len(l:non_processed_lines) > 1 30 | " Go through remaining lines 31 | for cur_line in l:non_processed_lines[1:] 32 | let l:first_word = matchstr(cur_line,'^[a-zA-Z\"]\+') 33 | if !(l:first_word == "") 34 | if index(w:slimux_python_allowed_indent0, l:first_word) > 0 35 | " Keyword allowed at indent level 0 36 | let l:processed_lines = l:processed_lines + [cur_line] 37 | else 38 | if l:at_indent0 39 | " Do not insert another newline when we are already 40 | " at indent level 0 41 | let l:processed_lines = l:processed_lines + [cur_line] 42 | else 43 | " Back at indent level 0. We need newline 44 | let l:at_indent0 = 1 45 | let l:processed_lines = l:processed_lines + [" ".cur_line] 46 | endif 47 | endif 48 | else 49 | " Not at indent level 0. Do not touch 50 | let l:at_indent0 = 0 51 | let l:processed_lines = l:processed_lines + [cur_line] 52 | endif 53 | endfor 54 | endif 55 | 56 | "" Return the processed lines 57 | if !l:at_indent0 && l:last_line_empty >= 0 58 | " We ended at indentation and last line was empty 59 | return join(l:processed_lines," ")." " 60 | else 61 | return join(l:processed_lines," ")." " 62 | endif 63 | endfunction 64 | 65 | -------------------------------------------------------------------------------- /ftplugin/scheme.vim: -------------------------------------------------------------------------------- 1 | "============================================================================= 2 | " README. 3 | " 1. What is this plugin? 4 | " This plugin add simple REPL support for scheme. 5 | " 6 | " Like slime for emacs or slimv for vim except that this is much simpler and 7 | " comunicate via slimux instead of swank connection. 8 | " 9 | " 2. Default Key bindings. 10 | " Note: This leader is Slimux_Scheme specific, the same to g:mapleader by 11 | " default. 12 | " 13 | " d -- evaluate the top block which the cursor is in. 14 | " p -- Prompt for input and send it directly to the REPL. 15 | " k -- Prompt for a key and send it. 16 | " q -- quit debug. => (RESTART 1) 17 | " 1..9 -- send (RESTART <1..9>) to the REPL. 18 | " 19 | " 3. Configurations. 20 | " a) To disable this plugin 21 | " add `let g:slimux_scheme_loaded=1` to your vimrc 22 | " 23 | " b) To enable default keybindings (recommended) 24 | " let g:slimux_scheme_keybindings=1 25 | " 26 | " c) To change the default leader, want to change it to ';' for example. 27 | " let g:slimux_scheme_leader=';' 28 | "============================================================================= 29 | 30 | " Settings {{{1 31 | " Whether to load this plugin or not. 32 | if exists("g:slimux_scheme_loaded") 33 | finish 34 | endif 35 | let g:slimux_scheme_loaded= 1 36 | 37 | " set Custom for the slimux_scheme plugin 38 | if !exists('g:slimux_scheme_leader') 39 | if exists( 'mapleader' ) && mapleader != ' ' 40 | let g:slimux_scheme_leader = mapleader 41 | else 42 | let g:slimux_scheme_leader = ',' 43 | endif 44 | endif 45 | 46 | " slimux_scheme keybinding set (0 = no keybindings) 47 | if !exists('g:slimux_scheme_keybindings') 48 | let g:slimux_scheme_keybindings = 0 49 | endif 50 | 51 | " Add scheme support for normal SlimuxSendSelection {{{1 52 | 53 | function! SlimuxEscape_scheme(text) 54 | " if text does not end with newline, add one 55 | if a:text !~ "\n$" 56 | let str_ret = a:text . '\n' 57 | else 58 | let str_ret = a:text 59 | endif 60 | 61 | return str_ret 62 | endfunction 63 | 64 | 65 | " Function Definitions {{{1 66 | 67 | " Evaluate a scheme 'define' statement 68 | function! Slimux_scheme_eval_defun() 69 | let pos = getpos(".") 70 | silent! exec "normal! 99[(yab" 71 | call SlimuxSendCode(@" . "\n") 72 | call setpos('.', pos) 73 | endfunction 74 | 75 | " Evaluate the entire buffer 76 | function! Slimux_scheme_eval_buffer() 77 | call SlimuxSendCode(join(getline(1, '$'), "\n") . "\n") 78 | endfunction 79 | 80 | " invoke restart by number 81 | function! Slimux_scheme_restart_by_number(num) 82 | let sent_text="(restart ". a:num .")\n" 83 | call SlimuxSendCode(sent_text) 84 | endfunction 85 | 86 | " Change functions to commands {{{1 87 | command! SlimuxSchemeEvalDefun call Slimux_scheme_eval_defun() 88 | command! SlimuxSchemeEvalBuffer call Slimux_scheme_eval_buffer() 89 | command! -nargs=1 SlimuxSchemeRestartByNum call Slimux_scheme_restart_by_number() 90 | 91 | " Set keybindings {{{1 92 | if g:slimux_scheme_keybindings == 1 93 | execute 'noremap ' . g:slimux_scheme_leader.'d :SlimuxSchemeEvalDefun' 94 | execute 'noremap ' . g:slimux_scheme_leader.'b :SlimuxSchemeEvalBuffer' 95 | execute 'noremap ' . g:slimux_scheme_leader.'p :SlimuxShellPrompt' 96 | execute 'noremap ' . g:slimux_scheme_leader.'k :SlimuxSendKeysPrompt' 97 | 98 | " bind keys for restart. 99 | for i in range(10) 100 | execute 'noremap ' . g:slimux_scheme_leader . i . ' :SlimuxSchemeRestartByNum ' . i . '' 101 | endfor 102 | execute 'noremap ' . g:slimux_scheme_leader . 'q :SlimuxSchemeRestartByNum 1' 103 | endif 104 | -------------------------------------------------------------------------------- /ftplugin/racket.vim: -------------------------------------------------------------------------------- 1 | "============================================================================= 2 | " README. 3 | " 1. What is this plugin? 4 | " This plugin add simple REPL support for racket. 5 | " 6 | " Like slime for emacs or slimv for vim except that this is much simpler and 7 | " comunicate via slimux instead of swank connection. 8 | " 9 | " 2. Default Key bindings. 10 | " Note: This leader is Slimux_racket specific, the same to g:mapleader by 11 | " default. 12 | " 13 | " d -- evaluate the top block which the cursor is in. 14 | " b -- evaluate the whole buffer 15 | " p -- Prompt for input and send it directly to the REPL. 16 | " q -- Send 'C-c' to the REPL 17 | " k -- Prompt for a key and send it. 18 | " 19 | " If you have enabled xrepl, we have the following addtional key bindings: 20 | " 21 | " t -- Return to the 'top-level' anamespace 22 | " w -- search documentation for the word under curser 23 | " 24 | " 3. Configurations. 25 | " a) To disable this plugin 26 | " add `let g:slimux_racket_loaded=1` to your vimrc 27 | " 28 | " b) To enable default keybindings (recommended) 29 | " let g:slimux_racket_keybindings=1 30 | " let g:slimux_racket_xrepl = 1 " set this if you have xrepl enabled. 31 | " 32 | " c) To change the default leader, want to change it to ';' for example. 33 | " let g:slimux_racket_leader=';' 34 | "============================================================================= 35 | 36 | " Settings {{{1 37 | " Whether to load this plugin or not. 38 | if exists("g:slimux_racket_loaded") 39 | finish 40 | endif 41 | let g:slimux_racket_loaded= 1 42 | 43 | " set Custom for the slimux_racket plugin 44 | if !exists('g:slimux_racket_leader') 45 | if exists( 'mapleader' ) && mapleader != ' ' 46 | let g:slimux_racket_leader = mapleader 47 | else 48 | let g:slimux_racket_leader = ',' 49 | endif 50 | endif 51 | 52 | " slimux_racket keybinding set (0 = no keybindings) 53 | if !exists('g:slimux_racket_keybindings') 54 | let g:slimux_racket_keybindings = 0 55 | endif 56 | 57 | " slimux_racket_xrepl (0 = no xrepl support) 58 | if !exists('g:slimux_racket_xrepl') 59 | let g:slimux_racket_xrepl = 0 60 | else 61 | let g:slimux_racket_xrepl = 1 62 | endif 63 | 64 | " Add racket support for normal SlimuxSendSelection {{{1 65 | 66 | function! SlimuxEscape_racket(text) 67 | " if text does not end with newline, add one 68 | if a:text !~ "\n$" 69 | let str_ret = a:text . '\n' 70 | else 71 | let str_ret = a:text 72 | endif 73 | 74 | return str_ret 75 | endfunction 76 | 77 | 78 | " Function Definitions {{{1 79 | 80 | " Evaluate a racket 'define' statement 81 | function! Slimux_racket_eval_defun() 82 | let pos = getpos(".") 83 | let regContent = @" 84 | let s:skip_sc = 'synIDattr(synID(line("."), col("."), 0), "name") =~ "[Ss]tring\\|[Cc]omment"' 85 | let [lhead, chead] = searchpairpos( '(', '', ')', 'bW', s:skip_sc) 86 | call cursor(lhead, chead) 87 | silent! exec "normal! 99[(yab" 88 | if getline('.')[0] == '(' 89 | call SlimuxSendCode(@" . "\n") 90 | else 91 | call SlimuxSendCode(getline('.') . "\n") 92 | endif 93 | " restore contents 94 | let @" = regContent 95 | call setpos('.', pos) 96 | endfunction 97 | 98 | " Evaluate the entire buffer 99 | function! Slimux_racket_eval_buffer() 100 | if g:slimux_racket_xrepl 101 | call SlimuxSendCode(',enter (file "' . expand('%:p') . '")' . "\n") 102 | else 103 | call SlimuxSendCode(join(getline(1, '$'), "\n") . "\n") 104 | endif 105 | endfunction 106 | 107 | " return to top-level namespace 108 | function! Slimux_racket_top() 109 | if g:slimux_racket_xrepl 110 | call SlimuxSendCode(',top' . "\n") 111 | endif 112 | endfunction 113 | 114 | " lookup word in the online doc 115 | function! Slimux_racket_doc(...) 116 | if g:slimux_racket_xrepl 117 | echomsg a:0 118 | if a:0 <= 0 119 | let s:word = expand("") 120 | else 121 | let s:word = a:1 122 | endif 123 | call SlimuxSendCode(',doc ' . s:word . "\n") 124 | endif 125 | endfunction 126 | 127 | " Send User break 128 | function! Slimux_racket_break() 129 | call SlimuxSendKeys('C-c enter') 130 | endfunction 131 | 132 | " Change functions to commands {{{1 133 | command! SlimuxRacketEvalDefun call Slimux_racket_eval_defun() 134 | command! SlimuxRacketEvalBuffer call Slimux_racket_eval_buffer() 135 | command! SlimuxRacketTop call Slimux_racket_top() 136 | command! -nargs=? SlimuxRacketDoc call Slimux_racket_doc() 137 | command! SlimuxRacketBreak call Slimux_racket_break() 138 | 139 | " Set keybindings {{{1 140 | if g:slimux_racket_keybindings == 1 141 | execute 'noremap ' . g:slimux_racket_leader.'d :SlimuxRacketEvalDefun' 142 | execute 'noremap ' . g:slimux_racket_leader.'b :SlimuxRacketEvalBuffer' 143 | execute 'noremap ' . g:slimux_racket_leader.'p :SlimuxShellPrompt' 144 | execute 'noremap ' . g:slimux_racket_leader.'k :SlimuxSendKeysPrompt' 145 | execute 'noremap ' . g:slimux_racket_leader.'t :SlimuxRacketTop' 146 | execute 'noremap ' . g:slimux_racket_leader.'w :SlimuxRacketDoc' 147 | execute 'noremap ' . g:slimux_racket_leader.'q :SlimuxRacketBreak' 148 | endif 149 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | NOTE: This repository is not actively maintained anymore. You have an active fork 2 | please add a comment to [this issue](https://github.com/esamattis/slimux/issues/86) 3 | so others can find it, thanks! 4 | 5 | 6 | # Slimux 7 | 8 | This is a SLIME inspired tmux integration plugin for Vim. It makes it easy to interact 9 | with different tmux panes directly from Vim. It has two styles for interacting 10 | with panes. REPL and Shell styles. 11 | 12 | REPL commands are designed to work with various Read Eval Print Loops such as 13 | `python`, `irb` (Ruby), `node` (Javascript), `coffee` (CoffeeScript) etc. 14 | This is loosely modelled after [SLIME for Emacs][SLIME]. Shell commands are designed 15 | to work with normal shells such as `bash`. These are useful for running tests for 16 | example. 17 | 18 | Main difference between these is pane configuration visibility. Each buffer has 19 | own configuration for REPL, but for Shell there is only one global 20 | configuration. The configuration is prompted from user when the commands are 21 | used for the first time. The configuration happens interactively. You will see 22 | list of available tmux panes and you can choose one by hitting enter on top of 23 | one. 24 | 25 | Also REPL commands can have custom pre, post and escape hooks. These allows 26 | interaction with some more complex REPLs such as the CoffeeScript REPL. 27 | 28 | This plugin borrows ideas and some code from [vim-slime][]. 29 | 30 | 31 | Blog post 32 | 33 | http://esa-matti.suuronen.org/blog/2012/04/19/slimux-tmux-plugin-for-vim/ 34 | 35 | Ascii.io screencast 36 | 37 | https://asciinema.org/a/409 38 | 39 | 40 | ## Installation 41 | 42 | Use [pathogen][] and put files to 43 | `$HOME/.vim/bundle/slimux/` 44 | 45 | Slimux requires fairly recent tmux version. Be sure you have 1.5.x or later. 46 | 47 | ## REPL Commands 48 | 49 | ### SlimuxREPLSendLine 50 | 51 | Send current line to the configured pane. 52 | 53 | ### SlimuxREPLSendSelection 54 | 55 | Send last visually selected text to the configured pane. 56 | 57 | ### SlimuxREPLSendBuffer 58 | 59 | Send current buffer to the configured pane. 60 | 61 | ### SlimuxREPLConfigure 62 | 63 | Prompt pane configuration for the current buffer. 64 | 65 | 66 | ## Shell Commands 67 | 68 | ### SlimuxShellPrompt 69 | 70 | Prompt for a shell command and send it to the configured tmux pane. 71 | 72 | ### SlimuxShellLast 73 | 74 | Rerun last shell command(prompts for a shell command if none was run before). 75 | 76 | ### SlimuxShellRun 77 | 78 | Specify a shell command to run directly, without the prompt: 79 | 80 | :SlimuxShellRun rspec spec/foo_spec.rb 81 | 82 | Suitable for mapping and other automation. 83 | 84 | Note that you need to escape strings intended for the shell. 85 | E.g. to list files with actual asterisks in their name: 86 | 87 | :SlimuxShellRun ls *\** 88 | 89 | ### SlimuxShellConfigure [pane-id] 90 | 91 | Select the tmux pane to which shell commands will be sent. If executed without a parameter it prompts global pane configuration for the shell commands. If called with parameter it selects the pane without user interaction, which will aid multi-pane workflows. Parameter is the tmux pane identifier in \d:\d:\d form and completed automatically with cline completion. 92 | 93 | 94 | ## Sending Keys 95 | 96 | ### SlimuxSendKeysPrompt 97 | 98 | Prompt for a key sequence using the 'tmux send-keys' syntax and send it to 99 | a configured tmux pane. 100 | E.g. Lets say you want to stop the webserver currently running in a 101 | shell(ctrl+c) and restart it(assuming the command to start is 'make run-server'): 102 | 103 | :SlimuxSendKeysPrompt 104 | KEYS>C-C 'make run-server' Enter 105 | 106 | or run previous command with 107 | 108 | KEYS>Up Enter 109 | 110 | In short, some strings such as 'C-C' or 'Enter' have special meanings, 111 | while others are sent as a sequence of character keys(in the above example, 'make run-server') 112 | 113 | ### SlimuxSendKeysLast 114 | 115 | Resends the last key sequence sent(prompts for a sequence if none was sent before). 116 | 117 | ### SlimuxSendKeysConfigure 118 | 119 | Prompt global pane configuration to send the key sequence. 120 | 121 | 122 | ## Keyboard Shortcuts 123 | 124 | Slimux does not force any shortcuts on your Vim, but here's something you can 125 | put to your `.vimrc` 126 | 127 | map s :SlimuxREPLSendLine 128 | vmap s :SlimuxREPLSendSelection 129 | map b :SlimuxREPLSendBuffer 130 | map a :SlimuxShellLast 131 | map k :SlimuxSendKeysLast 132 | 133 | Or if you like something more Emacs Slime style try something like this: 134 | 135 | map :SlimuxREPLSendLine 136 | vmap :SlimuxREPLSendSelection 137 | 138 | You may also add shortcuts to other commands too. 139 | 140 | For Scheme/Racket Slimux has few extra bindings. Enable them with 141 | 142 | let g:slimux_scheme_keybindings=1 143 | let g:slimux_racket_keybindings=1 144 | 145 | For more information refer to the [scheme plugin header](https://github.com/epeli/slimux/blob/master/ftplugin/scheme.vim). 146 | 147 | 148 | ## Adding support for new languages 149 | 150 | Usually new there is no need to do anything. For example Ruby and Node.js REPLs 151 | works just fine out of box, but for some languages you have to do some preprocessing 152 | before the code can be sent. There are three hooks you can specify for 153 | each language. 154 | 155 | Custom escaping function 156 | 157 | function SlimuxEscape_(text) 158 | return a:text 159 | endfunction 160 | 161 | Pre send hook 162 | 163 | function SlimuxPre_(target_pane) 164 | endfunction 165 | 166 | Post send hook 167 | 168 | function SlimuxPost_(target_pane) 169 | endfunction 170 | 171 | Just add these to ftplugin directory contained within this plugin (and sent a pull request on Github!). 172 | You can use [Python][] and [CoffeeScript][] hooks as examples. 173 | 174 | ## Options 175 | 176 | - `g:slimux_tmux_path = /path/to/your/tmux` sets the path to the 177 | preferred tmux binary, if not specified, defaults to getting tmux 178 | command from path using `system('command -v tmux')`. 179 | 180 | - `g:slimux_select_from_current_window = 1` select panes only from current 181 | window. Defaults to `0` to select panes from all tmux panes. 182 | 183 | - `g:slimux_pane_format` customize the formatting of the panes, see the FORMATS section in `man tmux`. 184 | The string "`#{pane_id}: `" is always prepended to the format so Slimux can identify the selected pane. 185 | 186 | 187 | ## Other Vim Slime plugins 188 | 189 | Before I created this plugin I tried several others, but non of them satisfied me. They where too 190 | complicated or just didn't support the languages I needed. So if Slimux isn't your cup of tea, 191 | maybe one of these is: 192 | 193 | * 194 | * 195 | * 196 | * 197 | * 198 | * 199 | * 200 | 201 | 202 | 203 | [tmux]: http://tmux.sourceforge.net/ 204 | [pathogen]: https://github.com/tpope/vim-pathogen 205 | [vim-slime]: https://github.com/jpalardy/vim-slime 206 | [SLIME]: http://common-lisp.net/project/slime/ 207 | 208 | [Python]: https://github.com/epeli/slimux/blob/master/ftplugin/python.vim 209 | [CoffeeScript]: https://github.com/epeli/slimux/blob/master/ftplugin/coffee.vim 210 | -------------------------------------------------------------------------------- /plugin/slimux.vim: -------------------------------------------------------------------------------- 1 | " Tmux integration for Vim 2 | " Maintainer Esa-Matti Suuronen 3 | " License: MIT. See LICENSE 4 | 5 | if !exists('g:slimux_tmux_path') 6 | let g:slimux_tmux_path = substitute(system('command -v tmux'), '\n\+$', '', '') 7 | endif 8 | if $PREFERRED_TMUX != '' 9 | let g:tmux_preferred_cmd = '' 10 | endif 11 | if $TMUX != "" 12 | let s:vim_inside_tmux = 1 13 | else 14 | let s:vim_inside_tmux = 0 15 | endif 16 | let s:tmux_version = system(g:slimux_tmux_path . ' -V')[5:-1] " skip 5 chars: 'tmux ' 17 | 18 | let s:slimux_panelist_cmd = g:slimux_tmux_path . ' list-panes -a' 19 | let s:retry_send = {} 20 | let s:last_selected_pane = "" 21 | 22 | function! s:PickPaneIdFromLine(line) 23 | let l:pane_match = matchlist(a:line, '\(^[^ ]\+\)\: ') 24 | if len(l:pane_match) == 0 25 | return "" 26 | endif 27 | return l:pane_match[1] 28 | endfunction 29 | 30 | function! SlimuxGetPaneList(lead, ...) 31 | let l:panes = system(s:slimux_panelist_cmd) 32 | let l:lst = map(split(l:panes, '\n'), 's:PickPaneIdFromLine(v:val)') 33 | 34 | if s:vim_inside_tmux == 1 && ( !exists("g:slimux_exclude_vim_pane") || g:slimux_exclude_vim_pane != 0 ) 35 | " Remove current pane from pane list 36 | let l:current_pane_id = system(g:slimux_tmux_path . ' display-message -p "#{session_name}:#{window_index}.#{pane_index}"') 37 | let l:current_pane_id = substitute(l:current_pane_id, "\n", "", "g") 38 | let l:lst = filter(l:lst, 'v:val !~ "' . l:current_pane_id . '"') 39 | endif 40 | 41 | return filter(l:lst, 'v:val =~ ''\V\^''. a:lead') 42 | endfunction 43 | 44 | function! s:ConfSetPane(tmux_packet, target_pane) 45 | " Configure current packet 46 | let a:tmux_packet["target_pane"] = a:target_pane 47 | " Save last selected pane 48 | let s:last_selected_pane = a:target_pane 49 | 50 | let type = a:tmux_packet["type"] 51 | 52 | if type == "global" 53 | if !exists("b:code_packet") 54 | let b:code_packet = { "target_pane": "", "type": "code" } 55 | endif 56 | let b:code_packet["target_pane"] = a:tmux_packet["target_pane"] 57 | let s:cmd_packet["target_pane"] = a:tmux_packet["target_pane"] 58 | let s:keys_packet["target_pane"] = a:tmux_packet["target_pane"] 59 | return 60 | endif 61 | 62 | if !empty(s:retry_send) 63 | call s:Send(s:retry_send) 64 | let s:retry_send = {} 65 | endif 66 | 67 | if !empty(s:retry_send) 68 | call s:Send(s:retry_send) 69 | let s:retry_send = {} 70 | endif 71 | endfunction 72 | 73 | function! g:_SlimuxPickPaneFromBuf(tmux_packet, test) 74 | let l:target_pane = s:PickPaneIdFromLine(getline(".")) 75 | if l:target_pane == "" 76 | echo "Please select a pane with enter or exit with 'q'" 77 | return 78 | endif 79 | 80 | " Test only. Do not send the real packet or configure anything. Instead 81 | " just send line break to see on which pane the cursor is on. 82 | if a:test 83 | return s:Send({ "target_pane": l:target_pane, "text": "\n", "type": "code" }) 84 | endif 85 | 86 | hide 87 | call s:ConfSetPane(a:tmux_packet, l:target_pane) 88 | endfunction 89 | 90 | function! s:SelectPane(tmux_packet, ...) 91 | " Save config dict to global so that it can be accessed later 92 | let g:SlimuxActiveConfigure = a:tmux_packet 93 | 94 | if exists('a:1') 95 | if a:1 != "" 96 | call s:ConfSetPane(g:SlimuxActiveConfigure, a:1) 97 | return 98 | endif 99 | endif 100 | 101 | " Create new buffer in a horizontal split 102 | belowright new 103 | 104 | " Get syntax highlighting from specified filetype 105 | if !exists("g:slimux_buffer_filetype") 106 | let g:slimux_buffer_filetype = 'sh' 107 | endif 108 | let &filetype=g:slimux_buffer_filetype 109 | 110 | " Set header for the menu buffer 111 | call setline(1, "# Enter: Select pane - Space/x: Test - C-c/q: Cancel") 112 | call setline(2, "") 113 | 114 | " Add last used pane as the first 115 | if len(s:last_selected_pane) != 0 116 | call setline(3, s:last_selected_pane . ": (last one used)") 117 | endif 118 | 119 | " List all tmux panes at the end 120 | normal G 121 | 122 | " Put tmux panes in the buffer. 123 | if !exists("g:slimux_pane_format") 124 | let g:slimux_pane_format = '#{session_name}:#{window_index}.#{pane_index}: #{window_name}: #{pane_title} [#{pane_width}x#{pane_height}]#{?pane_active, (active),}' 125 | endif 126 | 127 | " We need the pane_id at the beginning of the line so we can 128 | " identify the selected target pane 129 | let l:format = '#{pane_id}: ' . g:slimux_pane_format 130 | let l:command = g:slimux_tmux_path . " list-panes -F '" . escape(l:format, '#') . "'" 131 | 132 | " if g:slimux_select_from_current_window = 1, then list panes from current 133 | " window only. 134 | if s:vim_inside_tmux == 0 135 | let l:command .= ' -a' 136 | elseif !exists("g:slimux_select_from_current_window") || g:slimux_select_from_current_window != 1 137 | let l:command .= ' -a' 138 | endif 139 | 140 | if s:vim_inside_tmux == 1 && ( !exists("g:slimux_exclude_vim_pane") || g:slimux_exclude_vim_pane != 0 ) 141 | " Remove current pane from pane list 142 | let l:current_pane_id = system(g:slimux_tmux_path . ' display-message -p "\#{pane_id}"') 143 | let l:current_pane_id = substitute(l:current_pane_id, "\n", "", "g") 144 | let l:command .= " | grep -E -v " . shellescape("^" . l:current_pane_id, 1) 145 | endif 146 | 147 | " Warn if no additional pane is found 148 | let l:no_panes_warning = "No additional panes found" 149 | if s:vim_inside_tmux == 1 && ( exists("g:slimux_select_from_current_window") && g:slimux_select_from_current_window == 1 ) 150 | let l:no_panes_warning .= " in current window (g:slimux_select_from_current_window is enabled)" 151 | endif 152 | let l:command .= " || echo '" . l:no_panes_warning . "'" 153 | 154 | " Must use cat here because tmux might fail here due to some libevent bug in linux. 155 | " Try 'tmux list-panes -a > panes.txt' to see if it is fixed 156 | let l:command .= ' | cat' 157 | 158 | " Fill buffer with pane list 159 | execute 'silent read !' . l:command 160 | 161 | " Resize the split to the number of lines in the buffer, 162 | " limit to 10 lines maximum. 163 | execute min([ 10, line('$') ]) . 'wincmd _' 164 | 165 | " Move cursor to first item 166 | normal gg 167 | call setpos(".", [0, 3, 0, 0]) 168 | 169 | " bufhidden=wipe deletes the buffer when it is hidden 170 | setlocal bufhidden=wipe buftype=nofile 171 | setlocal nobuflisted nomodifiable noswapfile nowrap 172 | setlocal cursorline nocursorcolumn nonumber 173 | 174 | " Hide buffer on q, and C-c 175 | nnoremap q :hide 176 | nnoremap :hide 177 | 178 | if !exists("g:slimux_enable_close_with_esc") || g:slimux_enable_close_with_esc != 0 179 | nnoremap :hide 180 | endif 181 | 182 | " Use enter key to pick tmux pane 183 | nnoremap :call g:_SlimuxPickPaneFromBuf(g:SlimuxActiveConfigure, 0) 184 | 185 | nnoremap x :call g:_SlimuxPickPaneFromBuf(g:SlimuxActiveConfigure, 1) 186 | nnoremap :call g:_SlimuxPickPaneFromBuf(g:SlimuxActiveConfigure, 1) 187 | 188 | " Set key mapping for pane index hitns 189 | if !exists("g:slimux_pane_hint_map") 190 | let g:slimux_pane_hint_map = 'd' 191 | endif 192 | execute 'nnoremap ' . g:slimux_pane_hint_map . ' :call system("' . g:slimux_tmux_path . ' display-panes")' 193 | 194 | endfunction 195 | 196 | 197 | function! s:Send(tmux_packet) 198 | 199 | " Pane not selected! Save text and open selection dialog 200 | if len(a:tmux_packet["target_pane"]) == 0 201 | let s:retry_send = a:tmux_packet 202 | return s:SelectPane(a:tmux_packet) 203 | endif 204 | 205 | let target = a:tmux_packet["target_pane"] 206 | let type = a:tmux_packet["type"] 207 | 208 | if type == "code" || type == "cmd" 209 | 210 | let text = a:tmux_packet["text"] 211 | 212 | if type == "code" 213 | call s:ExecFileTypeFn("SlimuxPre_", [target]) 214 | let text = s:ExecFileTypeFn("SlimuxEscape_", [text]) 215 | endif 216 | 217 | let named_buffer = s:tmux_version >= '2.0' ? '-b Slimux' : '' 218 | call system(g:slimux_tmux_path . ' load-buffer ' . named_buffer . ' -', text) 219 | call system(g:slimux_tmux_path . ' paste-buffer ' . named_buffer . ' -t ' . target) 220 | 221 | if type == "code" 222 | call s:ExecFileTypeFn("SlimuxPost_", [target]) 223 | endif 224 | 225 | elseif type == 'keys' 226 | 227 | let keys = a:tmux_packet["keys"] 228 | call system(g:slimux_tmux_path . ' send-keys -t " . target . " " . keys) 229 | 230 | endif 231 | 232 | endfunction 233 | 234 | 235 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 236 | " Helpers 237 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 238 | 239 | function! s:EscapeText(text) 240 | return substitute(shellescape(a:text), "\\\\\\n", "\n", "g") 241 | endfunction 242 | 243 | function! s:ExecFileTypeFn(fn_name, args) 244 | let result = a:args[0] 245 | 246 | if exists("&filetype") 247 | let fullname = a:fn_name . &filetype 248 | if exists("*" . fullname) 249 | let result = call(fullname, a:args) 250 | end 251 | end 252 | 253 | return result 254 | endfunction 255 | 256 | 257 | " Thanks to http://vim.1045645.n5.nabble.com/Is-there-any-way-to-get-visual-selected-text-in-VIM-script-td1171241.html#a1171243 258 | function! s:GetVisual() range 259 | let reg_save = getreg('"') 260 | let regtype_save = getregtype('"') 261 | let cb_save = &clipboard 262 | set clipboard& 263 | 264 | silent normal! ""gvy 265 | let selection = getreg('"') 266 | 267 | if exists("g:slimux_restore_selection_after_visual") && g:slimux_restore_selection_after_visual == 1 268 | " restore the selection, this only works if we don't change 269 | " pane selection buffer 270 | silent normal! gv 271 | endif 272 | call setreg('"', reg_save, regtype_save) 273 | let &clipboard = cb_save 274 | return selection 275 | endfunction 276 | 277 | function! s:GetBuffer() 278 | let l:winview = winsaveview() 279 | let reg_save = getreg('"') 280 | let regtype_save = getregtype('"') 281 | let cb_save = &clipboard 282 | set clipboard& 283 | 284 | silent normal! ggVGy 285 | let selection = getreg('"') 286 | 287 | call setreg('"', reg_save, regtype_save) 288 | let &clipboard = cb_save 289 | call winrestview(l:winview) 290 | return selection 291 | endfunction 292 | 293 | function! s:GetParagraph() 294 | let reg_save = getreg('"') 295 | let regtype_save = getregtype('"') 296 | let cb_save = &clipboard 297 | set clipboard& 298 | let l:l = line(".") 299 | let l:c = col(".") 300 | 301 | " Do the business: 302 | silent normal ""yip 303 | let selection = getreg('"') 304 | 305 | call cursor(l:l, l:c) 306 | call setreg('"', reg_save, regtype_save) 307 | let &clipboard = cb_save 308 | return selection 309 | endfunction 310 | 311 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 312 | " Code interface 313 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 314 | 315 | " Code interface uses per buffer configuration 316 | 317 | function! SlimuxConfigureCode() 318 | if !exists("b:code_packet") 319 | let b:code_packet = { "target_pane": "", "type": "code" } 320 | endif 321 | call s:SelectPane(b:code_packet) 322 | endfunction 323 | 324 | function! SlimuxSendCode(text) 325 | if !exists("b:code_packet") 326 | let b:code_packet = { "target_pane": "", "type": "code" } 327 | endif 328 | let b:code_packet["text"] = a:text 329 | call s:Send(b:code_packet) 330 | endfunction 331 | 332 | function! s:SlimeSendRange() range abort 333 | if !exists("b:code_packet") 334 | let b:code_packet = { "target_pane": "", "type": "code" } 335 | endif 336 | let rv = getreg('"') 337 | let rt = getregtype('"') 338 | sil exe a:firstline . ',' . a:lastline . 'yank' 339 | call SlimuxSendCode(@") 340 | call setreg('"',rv, rt) 341 | endfunction 342 | 343 | command! SlimuxREPLSendLine call SlimuxSendCode(getline(".") . "\n") 344 | command! SlimuxREPLSendParagraph call SlimuxSendCode(s:GetParagraph()) 345 | command! -range=% -bar -nargs=* SlimuxREPLSendSelection call SlimuxSendCode(s:GetVisual()) 346 | command! -range -bar -nargs=0 SlimuxREPLSendLine ,call s:SlimeSendRange() 347 | command! -range=% -bar -nargs=* SlimuxREPLSendBuffer call SlimuxSendCode(s:GetBuffer()) 348 | command! SlimuxREPLConfigure call SlimuxConfigureCode() 349 | 350 | 351 | 352 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 353 | " Shell interface 354 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 355 | 356 | " Command interface has only one global configuration 357 | 358 | let s:cmd_packet = { "target_pane": "", "type": "cmd" } 359 | let s:previous_cmd = "" 360 | 361 | function! SlimuxSendCommand(cmd) 362 | 363 | let s:previous_cmd = a:cmd 364 | let s:cmd_packet["text"] = a:cmd . "\n" 365 | call s:Send(s:cmd_packet) 366 | 367 | endfunction 368 | 369 | command! -nargs=1 -complete=shellcmd SlimuxShellRun call SlimuxSendCommand("") 370 | command! SlimuxShellPrompt call SlimuxSendCommand(input("CMD>", s:previous_cmd)) 371 | command! SlimuxShellLast call SlimuxSendCommand(s:previous_cmd != "" ? s:previous_cmd : input("CMD>", s:previous_cmd)) 372 | command! SlimuxShellConfigure call s:SelectPane(s:cmd_packet) 373 | 374 | 375 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 376 | " Keys interface 377 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 378 | 379 | " Send raw keys using the tmux 'send-keys' syntax. 380 | " Works like the shell interface regarding configuration. 381 | " Here's an example the stops the currently running server(ctrl+c) and starts it again: 382 | " :SlimuxSendKeysPrompt 383 | " KEYS>C-C 'make run-server' Enter) 384 | 385 | let s:keys_packet = { "target_pane": "", "type": "keys" } 386 | let s:previous_keys = "" 387 | 388 | function! SlimuxSendKeys(keys) 389 | 390 | let s:previous_keys = a:keys 391 | let s:keys_packet["keys"] = a:keys 392 | call s:Send(s:keys_packet) 393 | 394 | endfunction 395 | 396 | command! -nargs=1 SlimuxSendKeys call SlimuxSendKeys("") 397 | command! SlimuxSendKeysPrompt call SlimuxSendKeys(input('KEYS>', s:previous_keys)) 398 | command! SlimuxSendKeysLast call SlimuxSendKeys(s:previous_keys != "" ? s:previous_keys : input('KEYS>')) 399 | command! SlimuxSendKeysConfigure call s:SelectPane(s:keys_packet) 400 | 401 | 402 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 403 | " Global interface (i.e. for repl, shell, and keys ) 404 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 405 | let s:global_conf = { "target_pane": "", "type": "global" } 406 | 407 | command! SlimuxGlobalConfigure call s:SelectPane(s:global_conf) 408 | command! -nargs=? -complete=customlist,SlimuxGetPaneList SlimuxShellConfigure call s:SelectPane(s:cmd_packet, ) 409 | --------------------------------------------------------------------------------