├── .github └── workflows │ └── stale.yml ├── .gitignore ├── .luarc.jsonc ├── LICENSE.txt ├── README.md ├── assets ├── doc │ ├── advanced.md │ └── targets │ │ ├── conemu.md │ │ ├── dtach.md │ │ ├── kitty.md │ │ ├── neovim.md │ │ ├── screen.md │ │ ├── tmux.md │ │ ├── vimterminal.md │ │ ├── wezterm.md │ │ ├── whimrepl.md │ │ ├── x11.md │ │ └── zellij.md ├── vim-slime-model.png └── vim-slime.gif ├── autoload ├── slime.vim └── slime │ ├── common.vim │ ├── config.vim │ └── targets │ ├── conemu.vim │ ├── dtach.vim │ ├── kitty.vim │ ├── neovim.vim │ ├── screen.vim │ ├── tmux.vim │ ├── vimterminal.vim │ ├── wezterm.vim │ ├── whimrepl.vim │ ├── x11.vim │ └── zellij.vim ├── doc └── vim-slime.txt ├── ftplugin ├── coffee │ └── slime.vim ├── elm │ └── slime.vim ├── fsharp │ └── slime.vim ├── haskell │ ├── README.md │ └── slime.vim ├── lhaskell │ └── slime.vim ├── matlab │ └── slime.vim ├── ocaml │ └── slime.vim ├── python │ ├── README.md │ └── slime.vim ├── scala │ ├── README.md │ └── slime.vim ├── sh │ └── slime.vim ├── sml │ └── slime.vim └── stata │ └── slime.vim └── plugin └── slime.vim /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | # This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time. 2 | # https://github.com/actions/stale 3 | name: Mark stale issues and pull requests 4 | 5 | on: 6 | schedule: 7 | - cron: '5 10 * * *' 8 | 9 | jobs: 10 | stale: 11 | 12 | runs-on: ubuntu-latest 13 | permissions: 14 | issues: write 15 | pull-requests: write 16 | actions: write 17 | 18 | steps: 19 | - uses: actions/stale@v9 20 | with: 21 | repo-token: ${{ secrets.GITHUB_TOKEN }} 22 | stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.' 23 | stale-pr-message: 'This PR has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.' 24 | 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | doc/tags 2 | -------------------------------------------------------------------------------- /.luarc.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "runtime.version": "LuaJIT", 3 | "runtime.path": [ 4 | "lua/?.lua", 5 | "lua/?/init.lua" 6 | ], 7 | "diagnostics.globals": ["vim"], 8 | "workspace.checkThirdParty": false, 9 | "workspace.library": [ 10 | "$VIMRUNTIME" 11 | //"${3rd}/luv/library" 12 | //"${3rd}/busted/library" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2025 Jonathan Palardy 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | vim-slime 2 | ========= 3 | 4 | Demo 5 | ------------ 6 | 7 | ![vim-slime session with R](assets/vim-slime.gif) 8 | 9 | 10 | What is vim-slime? 11 | ------------------ 12 | 13 | What is [SLIME](https://en.wikipedia.org/wiki/SLIME)? 14 | 15 | SLIME is an Emacs plugin to turn Emacs into a Lisp IDE. You can type text 16 | in a file, send it to a live REPL, and avoid having to reload all your code 17 | every time you make a change. 18 | 19 | So, what is `vim-slime`? 20 | 21 | vim-slime is a humble attempt at getting _some_ of the SLIME features into Vim. 22 | It works with any REPL and isn't tied to Lisp. 23 | 24 | Grab some text and send it to a [target](#targets): 25 | 26 | ![vim-slime sends text to a REPL through a target](assets/vim-slime-model.png) 27 | 28 | The target contains a [REPL](http://en.wikipedia.org/wiki/REPL), maybe Clojure, R or python. If you can type text into it, `vim-slime` can send text to it. 29 | 30 | Why do this? Because you want the benefits of a REPL (instant feedback, no need to reload ...) and the benefits of using Vim (familiar environment, syntax highlighting, persistence ...). 31 | 32 | More details in the [blog post](http://technotales.wordpress.com/2007/10/03/like-slime-for-vim/). 33 | 34 | Targets 35 | ------- 36 | 37 | Configure `vim-slime` for your desired target: 38 | 39 | ```vim 40 | " for all buffers 41 | let g:slime_target = "tmux" 42 | 43 | " and/or as a buffer-level override 44 | let b:slime_target = "wezterm" 45 | 46 | " if not explicitly configured, it defaults to `screen` 47 | ``` 48 | 49 | Many targets are supported, check their documentation for details: 50 | 51 | - [conemu](assets/doc/targets/conemu.md) 52 | - [dtach](assets/doc/targets/dtach.md) 53 | - [kitty](assets/doc/targets/kitty.md) 54 | - [neovim](assets/doc/targets/neovim.md) 55 | - [screen](assets/doc/targets/screen.md) — _default_ 56 | - [tmux](assets/doc/targets/tmux.md) 57 | - [vimterminal](assets/doc/targets/vimterminal.md) 58 | - [wezterm](assets/doc/targets/wezterm.md) 59 | - [whimrepl](assets/doc/targets/whimrepl.md) 60 | - [x11](assets/doc/targets/x11.md) 61 | - [zellij](assets/doc/targets/zellij.md) 62 | 63 | Installation 64 | ------------ 65 | 66 | Use your favorite package manager, or use Vim's built-in package support (since Vim 7.4.1528): 67 | 68 | mkdir -p ~/.vim/pack/plugins/start 69 | cd ~/.vim/pack/plugins/start 70 | git clone https://github.com/jpalardy/vim-slime.git 71 | 72 | You can [try vim-slime in Docker](https://blog.jpalardy.com/posts/trying-vim-slime-in-docker/) before committing to anything else. 73 | 74 | Usage 75 | ------------- 76 | 77 | Put your cursor over the text you want to send and type: 78 | 79 | ctrl-c ctrl-c _--- the same as slime_ 80 | 81 | _(You can just hold `ctrl` and double-tap `c`.)_ 82 | 83 | The current paragraph — what would be selected if you typed `vip` — is automatically selected. 84 | 85 | To control exactly what is sent, you can manually select text before calling `vim-slime`. 86 | 87 | ## Vim Style Mappings 88 | 89 | To send text using vim-style mappings, such as `slime operator+motion` or `slime operator+text object` see the appropriate [section of the advanced configuration documentation](assets/doc/advanced.md#vim-style-mappings). 90 | 91 | Config prompt 92 | -------------- 93 | 94 | `vim-slime` needs to know where to send your text, it will prompt you. 95 | It will remember your answers and won't prompt you again. 96 | 97 | If you want to reconfigure, type: 98 | 99 | ctrl-c v _--- mnemonic: "variables"_ 100 | 101 | or call: 102 | 103 | :SlimeConfig 104 | 105 | Language Support 106 | ---------------- 107 | 108 | `vim-slime` _might_ have to modify its behavior according to the language or REPL 109 | you want to use. 110 | 111 | Many languages are supported without modifications, while [others](ftplugin) 112 | might tweak the text without explicit configuration: 113 | 114 | * [coffee-script](ftplugin/coffee/slime.vim) 115 | * [elm](ftplugin/elm/slime.vim) 116 | * [fsharp](ftplugin/fsharp/slime.vim) 117 | * [haskell](ftplugin/haskell/slime.vim) / [lhaskell](ftplugin/haskell/slime.vim) -- [README](ftplugin/haskell) 118 | * [matlab](ftplugin/matlab/slime.vim) 119 | * [ocaml](ftplugin/ocaml/slime.vim) 120 | * [python](ftplugin/python/slime.vim) / [ipython](ftplugin/python/slime.vim) -- [README](ftplugin/python) 121 | * [scala](ftplugin/scala/slime.vim) / [ammonite](ftplugin/scala/slime.vim) -- [README](ftplugin/scala) 122 | * [sml](ftplugin/sml/slime.vim) 123 | * [stata](ftplugin/stata/slime.vim) 124 | 125 | Advanced Configuration 126 | ---------------------- 127 | 128 | If plain `vim-slime` isn't doing _exactly_ what you want, have a look 129 | at [advanced configuration](assets/doc/advanced.md). 130 | 131 | -------------------------------------------------------------------------------- /assets/doc/advanced.md: -------------------------------------------------------------------------------- 1 | 2 | Advanced Configuration 3 | ---------------------- 4 | 5 | ## Mappings 6 | If you need this, you might as well refer to [the code](https://github.com/jpalardy/vim-slime/blob/master/plugin/slime.vim#L233-L245). 7 | The code is not as complicated as you think. 😄 8 | 9 | If you don't want the default key mappings, set: 10 | 11 | ```vim 12 | let g:slime_no_mappings = 1 13 | ``` 14 | 15 | *before* the plugin is loaded. 16 | 17 | If you are using [lazy.nvim](https://github.com/folke/lazy.nvim) as package manager, this can also be done within the `init` function: 18 | 19 | ```lua 20 | { 21 | "jpalardy/vim-slime", 22 | init = function() 23 | vim.g.slime_no_mappings = 1 24 | end 25 | } 26 | ``` 27 | 28 | The default mappings are: 29 | 30 | ```vim 31 | xmap SlimeRegionSend 32 | nmap SlimeParagraphSend 33 | nmap v SlimeConfig 34 | ``` 35 | 36 | ### Vim Style Mappings 37 | 38 | Example of how to set vim-style mappings: 39 | 40 | ```vim 41 | "disables default bindings 42 | let g:slime_no_mappings = 1 43 | 44 | "send visual selection 45 | xmap s SlimeRegionSend 46 | 47 | "send based on motion or text object 48 | nmap s SlimeMotionSend 49 | 50 | "send line 51 | nmap ss SlimeLineSend 52 | ``` 53 | 54 | Of course these mappings are just examples; you can set them according to your preference. 55 | 56 | ## Set a Custom Default Config 57 | 58 | If you want `vim-slime` to prefill the prompt answers, you can set a default configuration: 59 | 60 | ```vim 61 | " screen: 62 | let g:slime_default_config = {"sessionname": "xxx", "windowname": "0"} 63 | 64 | " tmux: 65 | let g:slime_default_config = {"socket_name": "default", "target_pane": "1"} 66 | ``` 67 | 68 | If you want `vim-slime` to bypass the prompt and use the specified default configuration options, set the `g:slime_dont_ask_default` option: 69 | 70 | ```vim 71 | let g:slime_dont_ask_default = 1 72 | ``` 73 | 74 | ## Don't Restore Cursor Position 75 | 76 | By default, `vim-slime` will try to restore your cursor position after it runs. If you don't want that behavior, unset the `g:slime_preserve_curpos` option: 77 | 78 | ```vim 79 | let g:slime_preserve_curpos = 0 80 | ``` 81 | 82 | ## Send Delimited Cells 83 | 84 | If you want to send blocks of code between two delimiters, emulating the cell-like mode of REPL environments like ipython, matlab, etc., you can set the cell delimiter to any buffer-local variable `b:slime_cell_delimiter` or global `g:slime_cell_delimiter` variable and use the `SlimeSendCell` mapping to send the block of code. For example, if you are using ipython you could use the following: 85 | 86 | ```vim 87 | let g:slime_cell_delimiter = "#%%" 88 | nmap sc SlimeSendCell 89 | ``` 90 | 91 | ⚠️ it's recommended to use `b:slime_cell_delimiter` and set the variable in `ftplugin` for each relevant filetype. 92 | 93 | If you need more advanced cell features, such as syntax highlighting or cell navigation, you might want to have a look at [vim-slime-cells](https://github.com/Klafyvel/vim-slime-cells). 94 | 95 | 96 | Advanced Configuration: Overrides 97 | --------------------------------- 98 | 99 | At the end of the day, you might find that `vim-slime` _ALMOST_ does everything 100 | you need, but not quite the way you like it. You might be tempted to fork it, 101 | but the idea of writing and maintaining vimscript is daunting (trust me: I sympathize 😐). 102 | 103 | You can override _some_ logic and still benefit from the rest of `vim-slime`. 104 | Here's the mental model you need to understand how things work: 105 | 106 | 1. you invoke a key binding and `vim-slime` grabs a chunk of text 107 | 2. depending on which language you are using (see below), the text might be "transformed" and "massaged" to paste correctly 108 | 3. if the config is missing, the user is prompted to fill in the blanks 109 | 4. a target-specific function is called to delegate the "send this text to the right target" part 110 | 5. the target receives the right text, the right way, and everything works 111 | 112 | There is some good news, for step 2, 3, 4, you can override the logic with your 113 | own functions! Put these functions in your `.vimrc` and hijack the part you 114 | need. 115 | 116 | You can override any or all (zero to many) of these functions, as needed. 117 | 118 | Why is this awesome? 119 | 120 | - skip vimscript: delegate to an external script; written in your own preferred language 121 | - optimize for you: treat yourself with just-for-you customizations and hardcoded values 122 | - ultimate power: beyond config and flags, passing a function means you can do anything you want 123 | 124 | You might still need some vimscript to glue things together. Leaning on the 125 | `vim-slime` code for examples might get you 90% of what you need. If not, there's 126 | always [Learn Vimscript the Hard Way](https://learnvimscriptthehardway.stevelosh.com/). 127 | 128 | If you feel others can benefit from your customizations, open a PR and we'll find a way. 129 | 130 | 131 | ### How to override "language transformations"? 132 | 133 | Write a function named `SlimeOverride_EscapeText_#{language}`: 134 | 135 | ```vim 136 | function SlimeOverride_EscapeText_python(text) 137 | return system("some-command-line-script", a:text) 138 | endfunction 139 | ``` 140 | 141 | This example code, for Python in this case, pushes the selected text to `some-command-line-script` 142 | through STDIN and returns whatever that script produced through STDOUT. 143 | 144 | Contract: 145 | - input is selected text 146 | - output is string or an array of strings (see other `ftplugin` for details) 147 | 148 | ### How to override "configuration"? 149 | 150 | Write a function named `SlimeOverrideConfig`: 151 | 152 | ```vim 153 | function SlimeOverrideConfig() 154 | let b:slime_config = {} 155 | let b:slime_config["key"] = input("key: ", "default value") 156 | endfunction 157 | ``` 158 | 159 | Contract: 160 | - no input, but... 161 | - `b:slime_config` might contain `g:slime_default_config` if it was defined, or be undefined otherwise 162 | - no output but... 163 | - `b:slime_config` expected to contain necessary keys and values used by the target send function (up next) 164 | 165 | ### How to override "send to target"? 166 | 167 | Write a function named `SlimeOverrideSend`: 168 | 169 | ```vim 170 | function SlimeOverrideSend(config, text) 171 | echom a:config 172 | call system("send-to-target --key " . a:config["key"], a:text) 173 | endfunction 174 | ``` 175 | 176 | Contract: 177 | - inputs are config (from config function, above or default) and selected text (post transformation) 178 | - no output but... 179 | - expected to do whatever is needed to send to target, probably a call to `system` but see code for details 180 | 181 | -------------------------------------------------------------------------------- /assets/doc/targets/conemu.md: -------------------------------------------------------------------------------- 1 | 2 | ### ConEmu 3 | 4 | [ConEmu](https://conemu.github.io/) is *not* the default, to use it you will have to add this line to your `.vimrc`: 5 | 6 | ```vim 7 | let g:slime_target = "conemu" 8 | ``` 9 | 10 | When you invoke `vim-slime` for the first time, you will be prompted for more configuration. 11 | 12 | ConEmu console server HWND 13 | 14 | This is what you put in the -GuiMacro flag. It will be "0" if you didn't put 15 | anything, addressing the active tab/split of the first found ConEmu window. 16 | 17 | By default the windows clipboard is used to pass the text to ConEmu. If you 18 | experience issues with this, make sure the `conemuc` executable is in your 19 | `path`. 20 | 21 | -------------------------------------------------------------------------------- /assets/doc/targets/dtach.md: -------------------------------------------------------------------------------- 1 | 2 | ### dtach 3 | 4 | [dtach](http://dtach.sourceforge.net/) is *not* the default, to use it you will have to add this line to your `.vimrc`: 5 | 6 | ```vim 7 | let g:slime_target = "dtach" 8 | ``` 9 | 10 | When you invoke `vim-slime` for the first time, you will be prompted for more configuration. 11 | 12 | socket_path: 13 | 14 | The path to the Unix-domain socket that the dtach session is attached to. 15 | The default is /tmp/slime 16 | 17 | -------------------------------------------------------------------------------- /assets/doc/targets/kitty.md: -------------------------------------------------------------------------------- 1 | 2 | ### kitty 3 | 4 | [kitty](https://sw.kovidgoyal.net/kitty/) is *not* the default, to use it you will have to add this line to your `.vimrc`: 5 | 6 | ```vim 7 | let g:slime_target = "kitty" 8 | ``` 9 | 10 | When you invoke `vim-slime` for the first time, you will be prompted for more configuration. 11 | 12 | `kitty target window` 13 | 14 | This is the id of the kitty window that you wish to target. See e.g. the 15 | value of $KITTY_WINDOW_ID in the target window. 16 | 17 | `kitty listen on` 18 | 19 | Specifies where kitty should listen to control messages, by default the 20 | value of $KITTY_LISTEN_ON in the target window. 21 | 22 | To work properly, `kitty` must also be started with the following options: 23 | 24 | ```sh 25 | kitty -o allow_remote_control=yes --listen-on unix:/tmp/mykitty 26 | ``` 27 | 28 | See more [here](https://sw.kovidgoyal.net/kitty/remote-control.html). This can also be added to your `kitty.conf` file as: 29 | 30 | ``` 31 | allow_remote_control yes 32 | listen_on unix:/tmp/mykitty 33 | ``` 34 | 35 | ### SSH 36 | 37 | For slime use over ssh, you can also [forward the remote control](https://sw.kovidgoyal.net/kitty/kittens/ssh/#opt-kitten-ssh.forward_remote_control). 38 | 39 | 40 | Note the additional security concerns, however: **this should only be done on 41 | trusted remote hosts**. 42 | 43 | ### bracketed-paste 44 | 45 | Some REPLs can interfere with your text pasting. The [bracketed-paste](https://cirw.in/blog/bracketed-paste) mode exists to allow raw pasting. 46 | 47 | `kitty` supports bracketed-paste, use: 48 | 49 | ```vim 50 | let g:slime_bracketed_paste = 1 51 | " or 52 | let b:slime_bracketed_paste = 1 53 | ``` 54 | 55 | (It is disabled by default because it can create issues with ipython; see [#265](https://github.com/jpalardy/vim-slime/pull/265)). 56 | 57 | -------------------------------------------------------------------------------- /assets/doc/targets/neovim.md: -------------------------------------------------------------------------------- 1 | # NeoVim :terminal 2 | 3 | [NeoVim :terminal](https://neovim.io/doc/user/nvim_terminal_emulator.html) is *not* the default, to use it you will have to add this line to your `.vimrc`: 4 | 5 | ```vim 6 | let g:slime_target = "neovim" 7 | ``` 8 | 9 | When you invoke `vim-slime` for the first time, `:SlimeConfig` or one of the send functions, you will be prompted for more configuration. 10 | 11 | ## Manual/Prompted Configuration 12 | 13 | If the global variable `g:slime_suggest_default` is: 14 | 15 | - Nonzero (logical True): The last terminal you opened before calling vim-slime will determine which job ID is presented as default. If that terminal is closed, one of the previously opened terminals will be suggested on subsequent configurations. The user can tab through a popup menu of valid configuration values. 16 | 17 | - `0` (logical False), or nonexistent: No default will be suggested. 18 | 19 | 20 | 21 | In either case, in Neovim's default configuration, menu-based completion can be activated with ``/``, and the menu can be navigated with ``/`` or ``/``. Autocompletion plugins such as [nvim-cmp](https://github.com/hrsh7th/nvim-cmp) can interfere with this. 22 | 23 | To use the terminal's PID as input instead of Neovim's internal job ID of the terminal: 24 | 25 | ```vim 26 | let g:slime_input_pid=1 27 | ``` 28 | 29 | The `PID` is included in the terminal buffers' name, visible in the default terminal window status bar. 30 | 31 | 32 | ## Menu Prompted Configuration 33 | 34 | To be prompted with a numbered menu of all available terminals which the user can select from by inputting a number, or, if the mouse is enabled, clicking on an entry, set `g:slime_menu_config` to a nonzero value. 35 | 36 | ```vim 37 | let g:slime_menu_config=1 38 | ``` 39 | 40 | This takes precedence over `g:slime_input_pid`. 41 | 42 | The default order of fields in each terminal description in the menu is 43 | 44 | 1. `pid` The system process identifier of the shell. 45 | 2. `jobid` The Neovim internal job number of the terminal. 46 | 3. `term_title` Usually either the systemname, username, and current directory of the shell, or the name of the currently running process in that shell. (unlabeled by default) 47 | 4. `name` The name of the terminal buffer (unlabeled by default). 48 | 49 | The user can reorder these items and set their labels in the menu by setting a global variable, `g:slime_neovim_menu_order`, that should be an array of dictionaries. Keys should be exactly the names of the fields, shown above, and the values (which must be strings) will be the labels in the menu, according to user preference. Use empty strings for no label. The order of the dictionaries in the array will be the order used in the menu. 50 | 51 | For example: 52 | 53 | ```vim 54 | let g:slime_neovim_menu_order = [{'name': 'buffer name: '}, {'pid': 'shell process identifier: '}, {'jobid': 'neovim internal job identifier: '}, {'term_title': 'process or pwd: '}] 55 | ``` 56 | 57 | The user can also set the delimiter (including whitespace) string between the fields (`, ` by default) with `g:slime_neovim_menu_delimiter`. 58 | 59 | ```vim 60 | let g:slime_neovim_menu_delimiter = ' | ' 61 | ``` 62 | 63 | No validation is performed on these customization values so be sure they are properly set. 64 | 65 | ## Unlisted Terminals 66 | 67 | By default, Slime can send text to unlisted terminals (such as those created by [toggleterm.nvim](https://github.com/akinsho/toggleterm.nvim)). 68 | 69 | To disable this capability, and prevent unlisted terminals from being shown in menu configuration and from being suggested as a default set `g:slime_neovim_ignore_unlisted = 1` (or to any other logically true value). Setting `g:slime_neovim_ignore_unlisted = 0` preserves the default of being able to send to unlisted terminals. 70 | 71 | ## Terminal Process Identification 72 | 73 | As mentioned earlier, the `PID` of a process is included in the name of a terminal buffer. 74 | 75 | To manually check the right value of the terminal job ID: 76 | 77 | ```vim 78 | :echo &channel 79 | ``` 80 | 81 | To manually check the right value of the terminal job PID: 82 | 83 | ```vim 84 | :echo jobpid(&channel) 85 | ``` 86 | 87 | from the buffer running your terminal. 88 | 89 | Another way to easily see the `PID` and job ID is to override the status bar of terminals to show the job ID and PID. 90 | 91 | ```vim 92 | " in case an external process kills the terminal's shell and &channel doesn't exist anymore 93 | function! Safe_jobpid(channel_in) 94 | let pid_out = "" 95 | " in case an external process kills the terminal's shell; jobpid will error 96 | try 97 | let pid_out = string(jobpid(a:channel_in)) 98 | catch /^Vim\%((\a\+)\)\=:E900/ 99 | endtry 100 | return pid_out 101 | endfunction 102 | 103 | autocmd TermOpen * setlocal statusline=%{bufname()}%=id:\ %{&channel}\ pid:\ %{Safe_jobpid(&channel)} 104 | ``` 105 | 106 | See `h:statusline` in Neovim's documentation for more details. 107 | 108 | ### Statusline Plugins 109 | 110 | If you are using a plugin to manage your status line, see that plugin's documentation to see how to configure the status line to display `&channel` and `jobpid(&channel)`. 111 | 112 | Many status line plugins for Neovim are configured using Lua. 113 | 114 | A useful Lua function to return the Job ID of a terminal is: 115 | 116 | ```lua 117 | local function get_chan_jobid() 118 | return vim.api.nvim_eval('&channel > 0 ? &channel : ""') 119 | end 120 | ``` 121 | 122 | A useful Lua function to return the Job PID of a terminal is: 123 | 124 | ```lua 125 | local function get_chan_jobpid() 126 | local out = vim.api.nvim_exec2([[ 127 | let pid_out = "" 128 | 129 | try 130 | let pid_out = string(jobpid(&channel)) 131 | " in case an external process kills the terminal's shell; jobpid will error 132 | catch /^Vim\%((\a\+)\)\=:E900/ 133 | endtry 134 | echo pid_out 135 | ]], {output = true}) 136 | return out["output"] --returns as string 137 | end 138 | ``` 139 | 140 | Those confused by the syntax of the vimscript string passed as an argument to `vim.api.nvim_eval` should consult `:h ternary`. 141 | 142 | ## Status-Line Modifications for Configured Buffers 143 | 144 | Here is an example snippet of vimscript to set the status line for buffers that are configured to send code to a terminal: 145 | 146 | ```vim 147 | " Function to safely check for b:slime_config and return the job ID 148 | function! GetSlimeJobId() 149 | if exists("b:slime_config") && type(b:slime_config) == v:t_dict && has_key(b:slime_config, 'jobid') && !empty(b:slime_config['jobid']) 150 | " vertical bar appended to left as a separator 151 | return ' | jobid: ' . b:slime_config['jobid'] . ' ' 152 | endif 153 | return '' 154 | endfunction 155 | 156 | " Function to safely check for b:slime_config and return the pid 157 | function! GetSlimePid() 158 | if exists("b:slime_config") && type(b:slime_config) == v:t_dict && has_key(b:slime_config, 'pid') && !empty(b:slime_config['pid']) 159 | return 'pid: ' . b:slime_config['pid'] 160 | endif 161 | return '' 162 | endfunction 163 | 164 | 165 | "default statusline with :set ruler 166 | set statusline=%<%f\ %h%m%r%=%-14.(%l,%c%V%)\ %P 167 | " Append the custom function outputs to the right side of the status line 168 | set statusline+=%{GetSlimeJobId()}\ %{GetSlimePid()} 169 | ``` 170 | 171 | ### Lua Functions For Returning Config Components 172 | 173 | 174 | Can be useful for status line plugins: 175 | 176 | ```lua 177 | local function get_slime_jobid() 178 | if vim.b.slime_config and vim.b.slime_config.jobid then 179 | return vim.b.slime_config.jobid 180 | else 181 | return "" 182 | end 183 | end 184 | ``` 185 | 186 | ```lua 187 | local function get_slime_pid() 188 | if vim.b.slime_config and vim.b.slime_config.pid then 189 | return vim.b.slime_config.pid 190 | else 191 | return "" 192 | end 193 | end 194 | ``` 195 | 196 | 197 | ## Automatic Configuration 198 | 199 | Instead of the prompted job ID input method detailed above, you can specify a Lua function that will automatically configure vim-slime with a job id. For example, here is a function that iterates over all buffers and returns the job id of the first terminal it finds. 200 | 201 | ```lua 202 | vim.g.slime_get_jobid = function() 203 | -- iterate over all buffers to find the first terminal with a valid job 204 | for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do 205 | if vim.api.nvim_get_option_value('buftype',{buf = bufnr}) == "terminal" then 206 | local chan = vim.api.nvim_get_option_value( "channel",{buf = bufnr,}) 207 | if chan and chan > 0 then 208 | return chan 209 | end 210 | end 211 | end 212 | return nil 213 | end 214 | ``` 215 | 216 | This is not possible or straightforward to do in pure vimscript due to capitalization rules of functions stored as variables in Vimscript. 217 | 218 | `vim.api.nvim_eval` (see `:h nvim_eval()`) and other Neovim API functions are available to access all or almost all vimscript capabilities from Lua. 219 | 220 | ## Example Installation and Configuration with [lazy.nvim](https://github.com/folke/lazy.nvim) 221 | 222 | 223 | ```lua 224 | { 225 | "jpalardy/vim-slime", 226 | init = function() 227 | -- these two should be set before the plugin loads 228 | vim.g.slime_target = "neovim" 229 | vim.g.slime_no_mappings = true 230 | end, 231 | config = function() 232 | vim.g.slime_input_pid = false 233 | vim.g.slime_suggest_default = true 234 | vim.g.slime_menu_config = false 235 | vim.g.slime_neovim_ignore_unlisted = false 236 | -- options not set here are g:slime_neovim_menu_order, g:slime_neovim_menu_delimiter, and g:slime_get_jobid 237 | -- see the documentation above to learn about those options 238 | 239 | -- called MotionSend but works with textobjects as well 240 | vim.keymap.set("n", "gz", "SlimeMotionSend", { remap = true, silent = false }) 241 | vim.keymap.set("n", "gzz", "SlimeLineSend", { remap = true, silent = false }) 242 | vim.keymap.set("x", "gz", "SlimeRegionSend", { remap = true, silent = false }) 243 | vim.keymap.set("n", "gzc", "SlimeConfig", { remap = true, silent = false }) 244 | end, 245 | } 246 | ``` 247 | -------------------------------------------------------------------------------- /assets/doc/targets/screen.md: -------------------------------------------------------------------------------- 1 | 2 | ### GNU Screen 3 | 4 | By default, [GNU Screen](https://www.gnu.org/software/screen/) is assumed, you don't have to do anything. If you want to be explicit, you can add this line to your `.vimrc`: 5 | 6 | ```vim 7 | let g:slime_target = "screen" 8 | ``` 9 | 10 | Because Screen doesn't accept input from STDIN, a file is used to pipe data 11 | between Vim and Screen. By default this file is set to `$HOME/.slime_paste`. 12 | The name of the file used can be configured through a variable: 13 | 14 | let g:slime_paste_file = expand("$HOME/.slime_paste") 15 | " or maybe... 16 | let g:slime_paste_file = tempname() 17 | 18 | ⚠️ This file is not erased by the plugin and will always contain the last thing you sent over. 19 | 20 | When you invoke `vim-slime` for the first time, you will be prompted for more configuration. 21 | 22 | screen session name: 23 | 24 | This is what you put in the -S flag, or one of the line from "screen -ls". 25 | 26 | screen window name: 27 | 28 | This is the window number or name, zero-based. 29 | 30 | -------------------------------------------------------------------------------- /assets/doc/targets/tmux.md: -------------------------------------------------------------------------------- 1 | 2 | ### tmux 3 | 4 | [Tmux](https://github.com/tmux/tmux) is *not* the default, to use it you will have to add this line to your `.vimrc`: 5 | 6 | ```vim 7 | let g:slime_target = "tmux" 8 | ``` 9 | 10 | When you invoke `vim-slime` for the first time, you will be prompted for more configuration. 11 | 12 | tmux socket name or absolute path: 13 | 14 | If you started tmux with the -L or -S flag, use that same socket name or path here. 15 | If you didn't put anything, the default name is "default". 16 | 17 | tmux target pane: 18 | 19 | Note that all of these ordinals are 0-indexed by default. 20 | 21 | ":" means current window, current pane (a reasonable default) 22 | ":i" means the ith window, current pane 23 | ":i.j" means the ith window, jth pane 24 | "h:i.j" means the tmux session where h is the session identifier 25 | (either session name or number), the ith window and the jth pane 26 | "%i" means i refers the pane's unique id 27 | "{token}" one of tmux's supported special tokens, like "{last}" 28 | 29 | 30 | You can configure the defaults for these options. If you generally run vim in 31 | a split tmux window with a REPL in the other pane: 32 | 33 | ```vim 34 | let g:slime_default_config = {"socket_name": get(split($TMUX, ","), 0), "target_pane": ":.2"} 35 | ``` 36 | 37 | Or, more reliably, by leveraging [a special token](http://man.openbsd.org/OpenBSD-current/man1/tmux.1#_last__2) as pane index: 38 | 39 | ```vim 40 | let g:slime_default_config = {"socket_name": "default", "target_pane": "{last}"} 41 | ``` 42 | 43 | ### bracketed-paste 44 | 45 | Some REPLs can interfere with your text pasting. The [bracketed-paste](https://cirw.in/blog/bracketed-paste) mode exists to allow raw pasting. 46 | 47 | `tmux` supports bracketed-paste, use: 48 | 49 | ```vim 50 | let g:slime_bracketed_paste = 1 51 | " or 52 | let b:slime_bracketed_paste = 1 53 | ``` 54 | 55 | (It is disabled by default because it can create issues with ipython; see [#265](https://github.com/jpalardy/vim-slime/pull/265)). 56 | 57 | -------------------------------------------------------------------------------- /assets/doc/targets/vimterminal.md: -------------------------------------------------------------------------------- 1 | 2 | ### Vim :terminal 3 | 4 | [Vim :terminal](https://vimhelp.org/terminal.txt.html) is *not* the default, to use it you will have to add this line to your `.vimrc`: 5 | 6 | ```vim 7 | let g:slime_target = "vimterminal" 8 | ``` 9 | 10 | When you invoke `vim-slime` for the first time, you will be prompted for more configuration. 11 | 12 | Vim terminal configuration can be set by using the following in your `.vimrc`: 13 | 14 | ```vim 15 | let g:slime_vimterminal_config = {options} 16 | ``` 17 | 18 | You can specify if you have frequently used commands: 19 | 20 | ```vim 21 | let g:slime_vimterminal_cmd = "command" 22 | ``` 23 | 24 | If you use Node, set it as follows: 25 | 26 | ```vim 27 | let g:slime_vimterminal_cmd = "node" 28 | ``` 29 | 30 | You can make the vim terminal closed automatically: 31 | 32 | ```vim 33 | let g:slime_vimterminal_config = {"term_finish": "close"} 34 | ``` 35 | 36 | for possible options, see `:help term_start()` 37 | 38 | -------------------------------------------------------------------------------- /assets/doc/targets/wezterm.md: -------------------------------------------------------------------------------- 1 | 2 | ### wezterm 3 | 4 | [wezterm](https://wezfurlong.org/wezterm/index.html) is *not* the default, to use it you will have to add this line to your `.vimrc`: 5 | 6 | ```vim 7 | let g:slime_target = "wezterm" 8 | ``` 9 | 10 | When you invoke `vim-slime` for the first time, you will be prompted for more configuration. 11 | 12 | wezterm pane id 13 | 14 | This is the id of the wezterm pane that you wish to target. 15 | See e.g. the value of $WEZTERM_PANE in the target pane. 16 | 17 | wezterm pane direction 18 | 19 | If you want the id of the pane in a relative direction to the default, 20 | see `wezterm cli get-pane-direction --help` for possible values. 21 | 22 | You can configure the defaults for these options. If you generally run vim in 23 | a split wezterm window with a REPL to the right it could look like this: 24 | 25 | ```vim 26 | let g:slime_default_config = {"pane_direction": "right"} 27 | ``` 28 | 29 | ### bracketed-paste 30 | 31 | Some REPLs can interfere with your text pasting. The [bracketed-paste](https://cirw.in/blog/bracketed-paste) mode exists to allow raw pasting. 32 | 33 | `wezterm` supports bracketed-paste, use: 34 | 35 | ```vim 36 | let g:slime_bracketed_paste = 1 37 | " or 38 | let b:slime_bracketed_paste = 1 39 | ``` 40 | 41 | (It is disabled by default because it can create issues with ipython; see [#265](https://github.com/jpalardy/vim-slime/pull/265)). 42 | 43 | -------------------------------------------------------------------------------- /assets/doc/targets/whimrepl.md: -------------------------------------------------------------------------------- 1 | 2 | ### whimrepl 3 | 4 | [whimrepl](https://github.com/malyn/lein-whimrepl) is *not* the default, to use it you will have to add this line to your `.vimrc`: 5 | 6 | ```vim 7 | let g:slime_target = "whimrepl" 8 | ``` 9 | 10 | When you invoke `vim-slime` for the first time, you will be prompted for more configuration. 11 | 12 | whimrepl server name 13 | 14 | This is the name of the whimrepl server that you wish to target. whimrepl 15 | displays that name in its banner every time you start up an instance of 16 | whimrepl. 17 | 18 | -------------------------------------------------------------------------------- /assets/doc/targets/x11.md: -------------------------------------------------------------------------------- 1 | 2 | ### X11 3 | 4 | [x11](http://manpages.ubuntu.com/manpages/trusty/man1/xdotool.1.html) is *not* the default, to use it you will have to add this line to your `.vimrc`: 5 | 6 | ```vim 7 | let g:slime_target = "x11" 8 | ``` 9 | 10 | When you invoke `vim-slime` for the first time, you will have to designate a target window by clicking on it. 11 | 12 | #### Troubleshooting 13 | If `SlimeConfig` hangs without asking you to select a target terminal, you may not have `xdotool` installed. 14 | 15 | If `SlimeConfig` works and you can send text to the terminal window running vim but not to other terminal windows, your terminal may be configured to ignore the XSendEvent events generated by `xdotool` by default. See the SENDEVENT NOTES section of the [xdotool man page](http://manpages.ubuntu.com/manpages/trusty/man1/xdotool.1.html#SendEventNotes) for details. Note also that allowing arbitrary programs to send text to your terminal to be executed is potentially a security vulnerability. 16 | 17 | -------------------------------------------------------------------------------- /assets/doc/targets/zellij.md: -------------------------------------------------------------------------------- 1 | 2 | ### Zellij 3 | 4 | [Zellij](https://zellij.dev/) is *not* the default, to use it you will have to add this line to your `.vimrc`: 5 | 6 | ```vim 7 | let g:slime_target = "zellij" 8 | ``` 9 | 10 | When you invoke `vim-slime` for the first time, you will be prompted for more configuration. 11 | 12 | Zellij session id 13 | 14 | This is the id of the zellij session that you wish to target, 15 | the default value is "current" meaning the session containing the vim pane. 16 | See e.g. the value of "zellij list-sessions" in the target window to figure out 17 | specific session names. 18 | 19 | Zellij relative pane 20 | 21 | "current" for the currently active pane 22 | 23 | "up"/"down"/"right"/"left" for the pane in that direction relative to the location of 24 | the active pane 25 | 26 | You can configure the defaults for these options. If you generally run vim in 27 | a split zellij window with a REPL to the right it could look like this: 28 | 29 | ```vim 30 | let g:slime_default_config = {"session_id": "current", "relative_pane": "right"} 31 | ``` 32 | 33 | ### bracketed-paste 34 | 35 | Some REPLs can interfere with your text pasting. The [bracketed-paste](https://cirw.in/blog/bracketed-paste) mode exists to allow raw pasting. 36 | 37 | `zellij` supports bracketed-paste, use: 38 | 39 | ```vim 40 | let g:slime_bracketed_paste = 1 41 | " or 42 | let b:slime_bracketed_paste = 1 43 | ``` 44 | 45 | (It is disabled by default because it can create issues with ipython; see [#265](https://github.com/jpalardy/vim-slime/pull/265)). 46 | 47 | -------------------------------------------------------------------------------- /assets/vim-slime-model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpalardy/vim-slime/507107dd24c9b85721fa589462fd5068e0f70266/assets/vim-slime-model.png -------------------------------------------------------------------------------- /assets/vim-slime.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpalardy/vim-slime/507107dd24c9b85721fa589462fd5068e0f70266/assets/vim-slime.gif -------------------------------------------------------------------------------- /autoload/slime.vim: -------------------------------------------------------------------------------- 1 | 2 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 3 | " Helpers 4 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 5 | 6 | function! s:_EscapeText(text) 7 | let escape_text_fn = "_EscapeText_" . substitute(&filetype, "[.]", "_", "g") 8 | if exists("&filetype") 9 | let override_fn = "SlimeOverride" . escape_text_fn 10 | if exists("*" . override_fn) 11 | let result = call(override_fn, [a:text]) 12 | elseif exists("*" . escape_text_fn) 13 | let result = call(escape_text_fn, [a:text]) 14 | endif 15 | endif 16 | 17 | " use a:text if the ftplugin didn't kick in 18 | if !exists("result") 19 | let result = a:text 20 | endif 21 | 22 | " return an array, regardless 23 | if type(result) == type("") 24 | return [result] 25 | else 26 | return result 27 | endif 28 | endfunction 29 | 30 | function! s:SlimeGetConfig() 31 | " b:slime_config already configured... 32 | if exists("b:slime_config") && s:SlimeDispatchValidate("ValidConfig", b:slime_config, 0) 33 | return 34 | endif 35 | " assume defaults, if they exist 36 | 37 | if exists("g:slime_default_config") 38 | let b:slime_config = copy(g:slime_default_config) 39 | if exists("b:slime_config") && !s:SlimeDispatchValidate("ValidConfig", b:slime_config, 0) 40 | unlet b:slime_config 41 | endif 42 | endif 43 | 44 | " skip confirmation, if configured 45 | if exists("g:slime_dont_ask_default") && g:slime_dont_ask_default 46 | return 47 | endif 48 | " prompt user 49 | call s:SlimeDispatch('config') 50 | 51 | if exists("b:slime_config") && s:SlimeDispatchValidate("ValidConfig", b:slime_config, 0) 52 | return 53 | else 54 | if exists("b:slime_config") 55 | unlet b:slime_config 56 | endif 57 | throw "invalid config" 58 | endif 59 | endfunction 60 | 61 | 62 | 63 | function! slime#send_op(type, ...) abort 64 | let sel_save = &selection 65 | let &selection = "inclusive" 66 | let rv = getreg('"') 67 | let rt = getregtype('"') 68 | 69 | if a:0 " Invoked from Visual mode, use '< and '> marks. 70 | silent exe "normal! `<" . a:type . '`>y' 71 | elseif a:type == 'line' 72 | silent exe "normal! '[V']y" 73 | elseif a:type == 'block' 74 | silent exe "normal! `[\`]\y" 75 | else 76 | silent exe "normal! `[v`]y" 77 | endif 78 | 79 | call setreg('"', @", 'V') 80 | call slime#send(@") 81 | 82 | let &selection = sel_save 83 | call setreg('"', rv, rt) 84 | 85 | call s:SlimeRestoreCurPos() 86 | endfunction 87 | 88 | function! slime#send_range(startline, endline) abort 89 | let rv = getreg('"') 90 | let rt = getregtype('"') 91 | silent exe a:startline . ',' . a:endline . 'yank' 92 | call slime#send(@") 93 | call setreg('"', rv, rt) 94 | endfunction 95 | 96 | function! slime#send_lines(count) abort 97 | let rv = getreg('"') 98 | let rt = getregtype('"') 99 | silent exe 'normal! ' . a:count . 'yy' 100 | call slime#send(@") 101 | call setreg('"', rv, rt) 102 | endfunction 103 | 104 | function! slime#send_cell() abort 105 | let cell_delimiter = slime#config#resolve("cell_delimiter") 106 | if cell_delimiter == v:null 107 | return 108 | endif 109 | 110 | let line_ini = search(cell_delimiter, 'bcnW') 111 | let line_end = search(cell_delimiter, 'nW') 112 | 113 | " line after delimiter or top of file 114 | let line_ini = line_ini ? line_ini + 1 : 1 115 | " line before delimiter or bottom of file 116 | let line_end = line_end ? line_end - 1 : line("$") 117 | 118 | if line_ini <= line_end 119 | call slime#send_range(line_ini, line_end) 120 | endif 121 | endfunction 122 | 123 | function! slime#store_curpos() 124 | if slime#config#resolve("preserve_curpos") 125 | let s:cur = winsaveview() 126 | endif 127 | endfunction 128 | 129 | function! s:SlimeRestoreCurPos() 130 | if slime#config#resolve("preserve_curpos") && exists("s:cur") 131 | call winrestview(s:cur) 132 | unlet s:cur 133 | endif 134 | endfunction 135 | 136 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 137 | " Public interface 138 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 139 | 140 | function! slime#send(text) 141 | if s:SlimeDispatchValidate("ValidEnv") 142 | try 143 | call s:SlimeGetConfig() 144 | catch \invalid config\ 145 | return 146 | endtry 147 | 148 | " this used to return a string, but some receivers (coffee-script) 149 | " will flush the rest of the buffer given a special sequence (ctrl-v) 150 | " so we, possibly, send many strings -- but probably just one 151 | let pieces = s:_EscapeText(a:text) 152 | for piece in pieces 153 | if type(piece) == 0 " a number 154 | if piece > 0 " sleep accepts only positive count 155 | execute 'sleep' piece . 'm' 156 | endif 157 | else 158 | call s:SlimeDispatch('send', b:slime_config, piece) 159 | endif 160 | endfor 161 | endif 162 | endfunction 163 | 164 | function! slime#config() abort 165 | call inputsave() 166 | if s:SlimeDispatchValidate("ValidEnv") 167 | call s:SlimeDispatch('config') 168 | 169 | if exists("b:slime_config") && !s:SlimeDispatchValidate("ValidConfig", b:slime_config, 0) 170 | unlet b:slime_config 171 | endif 172 | endif 173 | call inputrestore() 174 | endfunction 175 | 176 | " delegation 177 | function! s:SlimeDispatchValidate(name, ...) 178 | " using try catch because exists() doesn't detect autoload functions that aren't yet loaded 179 | " the idea is to return the interger 1 for true in cases where a target doesn't have 180 | " the called validation function implemented. E117 is 'Unknown function'. 181 | try 182 | return call ("s:SlimeDispatch", [a:name] + a:000) 183 | catch /^Vim\%((\a\+)\)\=:E117:/ 184 | return 1 185 | endtry 186 | endfunction 187 | 188 | function! s:SlimeDispatch(name, ...) 189 | " allow custom override 190 | let override_fn = "SlimeOverride" . slime#common#capitalize(a:name) 191 | if exists("*" . override_fn) 192 | return call(override_fn, a:000) 193 | endif 194 | return call("slime#targets#" . slime#config#resolve("target") . "#" . a:name, a:000) 195 | endfunction 196 | -------------------------------------------------------------------------------- /autoload/slime/common.vim: -------------------------------------------------------------------------------- 1 | " guess correct number of spaces to indent 2 | " (tabs cause 'no completion found' messages) 3 | function! slime#common#get_indent_string() abort 4 | return repeat(" ", 4) 5 | endfunction 6 | 7 | " replace tabs by spaces 8 | function! slime#common#tab_to_spaces(text) abort 9 | return substitute(a:text, "\t", slime#common#get_indent_string(), "g") 10 | endfunction 11 | 12 | " change string into array of lines 13 | function! slime#common#lines(text) abort 14 | return split(a:text, "\n") 15 | endfunction 16 | 17 | " change lines back into text 18 | function! slime#common#unlines(lines) abort 19 | return join(a:lines, "\n") . "\n" 20 | endfunction 21 | 22 | function! slime#common#write_paste_file(text) 23 | let paste_dir = fnamemodify(slime#config#resolve("paste_file"), ":p:h") 24 | if !isdirectory(paste_dir) 25 | call mkdir(paste_dir, "p") 26 | endif 27 | let lines = split(a:text, "\n", 1) 28 | call writefile(lines, slime#config#resolve("paste_file"), 'b') 29 | endfunction 30 | 31 | function! slime#common#capitalize(text) 32 | return substitute(tolower(a:text), '\(.\)', '\u\1', '') 33 | endfunction 34 | 35 | function! slime#common#system(cmd_template, args, ...) 36 | if &l:shell !=# "cmd.exe" 37 | let escaped_args = map(copy(a:args), "shellescape(v:val)") 38 | else 39 | let escaped_args = a:args 40 | endif 41 | 42 | let cmd = call('printf', [a:cmd_template] + escaped_args) 43 | 44 | if slime#config#resolve("debug") 45 | echom "slime system: " . cmd 46 | endif 47 | 48 | if a:0 == 0 49 | return system(cmd) 50 | endif 51 | return system(cmd, a:1) 52 | endfunction 53 | 54 | function! slime#common#bracketed_paste(text) 55 | let bracketed_paste = slime#config#resolve("bracketed_paste") 56 | 57 | if bracketed_paste == 0 58 | return [bracketed_paste, a:text, 0] 59 | endif 60 | 61 | let text_to_paste = substitute(a:text, '\(\r\n\|\r\|\n\)$', '', '') 62 | let has_crlf = strlen(a:text) != strlen(text_to_paste) 63 | 64 | return [bracketed_paste, text_to_paste, has_crlf] 65 | endfunction 66 | -------------------------------------------------------------------------------- /autoload/slime/config.vim: -------------------------------------------------------------------------------- 1 | 2 | let g:slime_config_defaults = {} 3 | let g:slime_config_defaults["target"] = "screen" 4 | let g:slime_config_defaults["preserve_curpos"] = 1 5 | let g:slime_config_defaults["paste_file"] = expand("$HOME/.slime_paste") 6 | let g:slime_config_defaults["bracketed_paste"] = 0 7 | let g:slime_config_defaults["debug"] = 0 8 | 9 | " ------------------------------------------------- 10 | 11 | " look for `config_name` in unusal places 12 | " fallback to default_Value 13 | function! slime#config#resolve(config_name) 14 | if exists("b:slime_" . a:config_name) 15 | return get(b:, "slime_" . a:config_name) 16 | endif 17 | if exists("g:slime_" . a:config_name) 18 | return get(g:, "slime_" . a:config_name) 19 | endif 20 | if has_key(g:slime_config_defaults, a:config_name) 21 | return get(g:slime_config_defaults, a:config_name) 22 | endif 23 | echoerr "missing config value for: slime_" . a:config_name 24 | return v:null 25 | endfunction 26 | 27 | if slime#config#resolve("target") == "neovim" 28 | if has('nvim') 29 | let g:slime_config_defaults["menu_config"] = 0 30 | 31 | " whether to populate the command line with an identifier when configuring 32 | let g:slime_config_defaults["suggest_default"] = 1 33 | 34 | "input PID rather than job ID on the command line when configuring 35 | let g:slime_config_defaults["input_pid"] = 0 36 | 37 | " can be set to a user-defined function to automatically get a job ID. Set as zero here to evaluate to false. 38 | let g:slime_config_defaults["get_jobid"] = 0 39 | 40 | "order of menu if configuring that way 41 | let g:slime_config_defaults["neovim_menu_order"] = [{'pid': 'pid: '}, {'jobid': 'jobid: '}, {'term_title':''}, {'name': ''}] 42 | 43 | "delimiter of menu if configuring that way 44 | let g:slime_config_defaults["neovim_menu_delimiter"] = ', ' 45 | 46 | "if set to to a true value, will disable ability to send to unlisted buffers 47 | let g:slime_config_defaults["neovim_ignore_unlisted"] = 0 48 | 49 | else 50 | call slime#targets#neovim#EchoWarningMsg("Trying to use Neovim target in standard Vim. This won't work.") 51 | endif 52 | endif 53 | -------------------------------------------------------------------------------- /autoload/slime/targets/conemu.vim: -------------------------------------------------------------------------------- 1 | 2 | function! slime#targets#conemu#config() abort 3 | " set destination for send commands, as specified in http://conemu.github.io/en/GuiMacro.html#Command_line 4 | if !exists("b:slime_config") 5 | " defaults to the active tab/split of the first found ConEmu window 6 | let b:slime_config = {"HWND": "0"} 7 | end 8 | let b:slime_config["HWND"] = input("Console server HWND: ", b:slime_config["HWND"]) 9 | endfunction 10 | 11 | function! slime#targets#conemu#send(config, text) 12 | " use the selection register to send text to ConEmu using the windows clipboard (see help gui-clipboard) 13 | " save the current selection to restore it after send 14 | let tmp = @* 15 | let @* = a:text 16 | call slime#common#system("conemuc -guimacro:%s print", [a:config["HWND"]]) 17 | let @* = tmp 18 | endfunction 19 | 20 | -------------------------------------------------------------------------------- /autoload/slime/targets/dtach.vim: -------------------------------------------------------------------------------- 1 | 2 | function! slime#targets#dtach#config() abort 3 | if !exists("b:slime_config") 4 | let b:slime_config = {"socket_path": "/tmp/slime"} 5 | end 6 | let b:slime_config["socket_path"] = input("dtach socket path: ", b:slime_config["socket_path"]) 7 | endfunction 8 | 9 | function! slime#targets#dtach#send(config, text) 10 | call slime#common#system("dtach -p %s", [a:config["socket_path"]], a:text) 11 | endfunction 12 | 13 | -------------------------------------------------------------------------------- /autoload/slime/targets/kitty.vim: -------------------------------------------------------------------------------- 1 | 2 | function! slime#targets#kitty#config() abort 3 | if !exists("b:slime_config") 4 | let b:slime_config = {"window_id": 1, "listen_on": $KITTY_LISTEN_ON} 5 | end 6 | let b:slime_config["window_id"] = str2nr(slime#common#system("kitty @ select-window --self", [])) 7 | if v:shell_error || b:slime_config["window_id"] == $KITTY_WINDOW_ID 8 | let b:slime_config["window_id"] = input("kitty window_id ($KITTY_WINDOW_ID): ", b:slime_config["window_id"]) 9 | endif 10 | let b:slime_config["listen_on"] = input("kitty listen on ($KITTY_LISTEN_ON): ", b:slime_config["listen_on"]) 11 | endfunction 12 | 13 | function! slime#targets#kitty#send(config, text) 14 | let [bracketed_paste, text_to_paste, has_crlf] = slime#common#bracketed_paste(a:text) 15 | 16 | if bracketed_paste 17 | let text_to_paste = "\e[200~" . text_to_paste . "\e[201~" 18 | endif 19 | 20 | let target_cmd = s:target_cmd(a:config["listen_on"]) 21 | call slime#common#system(target_cmd . " send-text --match id:%s --stdin", [a:config["window_id"]], text_to_paste) 22 | 23 | " trailing newline 24 | if has_crlf 25 | call slime#common#system(target_cmd . " send-text --match id:%s --stdin", [a:config["window_id"]], "\n") 26 | endif 27 | endfunction 28 | 29 | " ------------------------------------------------- 30 | 31 | function! s:target_cmd(listen_on) 32 | if a:listen_on != "" 33 | return "kitty @ --to " . shellescape(a:listen_on) 34 | end 35 | return "kitty @" 36 | endfunction 37 | 38 | -------------------------------------------------------------------------------- /autoload/slime/targets/neovim.vim: -------------------------------------------------------------------------------- 1 | 2 | function! slime#targets#neovim#config() abort 3 | let config_set = 0 4 | 5 | if !config_set && slime#config#resolve("menu_config") 6 | let temp_config = s:config_with_menu() 7 | let config_set = 1 8 | endif 9 | 10 | 11 | " unlet current config if its job ID doesn't exist 12 | if !config_set 13 | let slime_suggest_default = slime#config#resolve("suggest_default") 14 | let last_channels = get(g:, 'slime_last_channel', []) 15 | let most_recent_channel = get(last_channels, -1, {}) 16 | 17 | let last_pid = get(most_recent_channel, 'pid', '') 18 | let last_job = get(most_recent_channel, 'jobid', '') 19 | 20 | let temp_config = {"jobid": last_job, "pid": last_pid } 21 | endif 22 | 23 | " include option to input pid 24 | if !config_set && slime#config#resolve("input_pid") 25 | let default_pid = slime_suggest_default ? s:translate_id_to_pid(temp_config["jobid"]) : "" 26 | if default_pid == -1 27 | let default_pid = "" 28 | endif 29 | let pid_in = input("Configuring vim-slime. Input pid: ", default_pid , 'customlist,Last_channel_to_pid') 30 | redraw 31 | let jobid_in = str2nr(s:translate_pid_to_id(pid_in)) 32 | let temp_config["jobid"] = jobid_in 33 | let temp_config["pid"] = pid_in 34 | let config_set = 1 35 | endif 36 | 37 | if !config_set && exists('g:slime_get_jobid') && type(g:slime_get_jobid) == v:t_func 38 | let jobid_in = luaeval('vim.g.slime_get_jobid()') 39 | let pid_in = s:translate_id_to_pid(jobid_in) 40 | let temp_config["jobid"] = jobid_in 41 | let temp_config["pid"] = pid_in 42 | let config_set = 1 43 | endif 44 | 45 | " passed all guard cases, inputting jobid 46 | if !config_set 47 | let default_jobid = slime_suggest_default ? temp_config["jobid"] : "" 48 | if !empty(default_jobid) 49 | let default_jobid = str2nr(default_jobid) 50 | endif 51 | let jobid_in = input("Configuring vim-slime. Input jobid: ", default_jobid, 'customlist,Last_channel_to_jobid') 52 | redraw 53 | let jobid_in = str2nr(jobid_in) 54 | let pid_in = s:translate_id_to_pid(jobid_in) 55 | 56 | let temp_config["jobid"] = jobid_in 57 | let temp_config["pid"] = pid_in 58 | endif 59 | 60 | let b:slime_config = temp_config 61 | endfunction 62 | 63 | function! slime#targets#neovim#send(config, text) abort 64 | call chansend(str2nr(a:config["jobid"]), split(a:text, "\n", 1)) 65 | endfunction 66 | 67 | function! slime#targets#neovim#SlimeAddChannel(buf_in) abort 68 | let buf_in = str2nr(a:buf_in) 69 | 70 | if slime#config#resolve("neovim_ignore_unlisted") && !buflisted(buf_in) 71 | return 72 | endif 73 | 74 | " only interactive terminals havve the &channel option, it is one of their defining properties 75 | " this is poorly documented 76 | " getbufvar returns "" when the option/variable lit looks for isn't found 77 | let jobid = getbufvar(buf_in, "&channel") 78 | if jobid == "" 79 | return 80 | endif 81 | 82 | let job_pid = jobpid(jobid) 83 | 84 | if !exists("g:slime_last_channel") 85 | let g:slime_last_channel = [{'jobid': jobid, 'pid': job_pid, 'bufnr': buf_in}] 86 | else 87 | call add(g:slime_last_channel, {'jobid': jobid, 'pid': job_pid, 'bufnr': buf_in}) 88 | endif 89 | endfunction 90 | 91 | function! slime#targets#neovim#SlimeClearChannel(buf_in) abort 92 | if !exists("g:slime_last_channel") 93 | call s:clear_all_buffs() 94 | return 95 | elseif len(g:slime_last_channel) == 0 96 | call s:clear_all_buffs() 97 | unlet g:slime_last_channel 98 | else 99 | let last_channel_copy = copy(g:slime_last_channel) 100 | let filtered_last_channels = filter(last_channel_copy, {_, val -> val['bufnr'] == a:buf_in}) 101 | 102 | if len(filtered_last_channels) > 0 103 | let jobid_to_clear = filtered_last_channels[0]['jobid'] 104 | call s:clear_related_bufs(jobid_to_clear) 105 | call filter(g:slime_last_channel, {_, val -> val['bufnr'] != a:buf_in}) 106 | endif 107 | 108 | endif 109 | endfunction 110 | 111 | " evaluates whether there is a terminal running; if there isn't then no config can be valid 112 | function! slime#targets#neovim#ValidEnv() abort 113 | if (!exists("g:slime_last_channel") || (len(g:slime_last_channel)) < 1) || empty(g:slime_last_channel) 114 | call slime#targets#neovim#EchoWarningMsg("Terminal not found.") 115 | return 0 116 | endif 117 | return 1 118 | endfunction 119 | 120 | " "checks that a configuration is valid 121 | " returns boolean of whether the supplied config is valid 122 | function! slime#targets#neovim#ValidConfig(config, silent) abort 123 | 124 | if !exists("g:slime_last_channel") 125 | if !a:silent 126 | call slime#targets#neovim#EchoWarningMsg("Terminal not found.") 127 | endif 128 | return 0 129 | endif 130 | 131 | " Ensure the config is a dictionary and a previous channel exists 132 | if type(a:config) != v:t_dict 133 | if !a:silent 134 | call slime#targets#neovim#EchoWarningMsg("Config type not valid.") 135 | endif 136 | return 0 137 | endif 138 | 139 | if empty(a:config) 140 | if !a:silent 141 | call slime#targets#neovim#EchoWarningMsg("Config is empty.") 142 | endif 143 | return 0 144 | endif 145 | 146 | " Ensure the correct keys exist within the configuration 147 | if !(has_key(a:config, 'jobid')) 148 | if !a:silent 149 | call slime#targets#neovim#EchoWarningMsg("Config object lacks 'jobid'.") 150 | endif 151 | return 0 152 | endif 153 | 154 | if a:config["jobid"] == -1 "the id wasn't found translate_pid_to_id 155 | if !a:silent 156 | call slime#targets#neovim#EchoWarningMsg("No matching job ID for the provided pid.") 157 | endif 158 | return 0 159 | endif 160 | 161 | if !(index( 162 | \map(copy(g:slime_last_channel), {_, val -> val["jobid"]}), 163 | \a:config['jobid']) >= 0 164 | \) 165 | if !a:silent 166 | call slime#targets#neovim#EchoWarningMsg("Invalid job ID.") 167 | endif 168 | return 0 169 | endif 170 | 171 | if s:translate_id_to_pid(a:config['jobid']) == -1 172 | if !a:silent 173 | call slime#targets#neovim#EchoWarningMsg("job ID not linked to a PID.") 174 | endif 175 | return 0 176 | endif 177 | 178 | return 1 179 | endfunction 180 | 181 | function! s:translate_pid_to_id(pid) abort 182 | for ch in g:slime_last_channel 183 | if ch['pid'] == a:pid 184 | return ch['jobid'] 185 | endif 186 | endfor 187 | return -1 188 | endfunction 189 | 190 | function! s:translate_id_to_pid(id) abort 191 | let pid_out = -1 192 | try 193 | let pid_out = jobpid(a:id) 194 | catch 195 | endtry 196 | return pid_out 197 | endfunction 198 | 199 | " Transforms a channel dictionary with job ID and pid into a newline separated string of job IDs. 200 | " for the purposes of input completion 201 | function! Last_channel_to_jobid(ArgLead, CmdLine, CursorPos) abort 202 | let jobids = map(copy(g:slime_last_channel), {_, val -> val["jobid"]}) 203 | call map(jobids, {_, val -> string(val)}) 204 | return reverse(jobids) " making correct order in menu 205 | endfunction 206 | 207 | " Transforms a channel dictionary with job ID and pid into an newline separated string of job PIDs. 208 | " for the purposes of input completion 209 | function! Last_channel_to_pid(ArgLead, CmdLine, CursorPos) abort 210 | "they will be transformed into pids so naming them by their final identity 211 | let jobpids = map(copy(g:slime_last_channel), {_, val -> val["jobid"]}) 212 | call map(jobpids, {_, val -> s:translate_id_to_pid(val)}) 213 | call filter(jobpids, {_,val -> val != -1}) 214 | call map(jobpids, {_, val -> string(val)}) 215 | return reverse(jobpids) "making most recent the first selected 216 | endfunction 217 | 218 | 219 | " clears all buffers with a certain invalid configuration 220 | function! s:clear_related_bufs(id_in) abort 221 | let related_bufs = filter(getbufinfo(), {_, val -> has_key(val['variables'], "slime_config") 222 | \ && get(val['variables']['slime_config'], 'jobid', -2) == a:id_in}) 223 | 224 | for buf in related_bufs 225 | call setbufvar(buf['bufnr'], 'slime_config', {}) 226 | endfor 227 | endfunction 228 | 229 | " clears all buffers of all configurations 230 | function! s:clear_all_buffs() abort 231 | let target_bufs = filter(getbufinfo(), {_, val -> has_key(val['variables'], "slime_config") }) 232 | 233 | for buf in target_bufs 234 | call setbufvar(buf['bufnr'], 'slime_config', {}) 235 | endfor 236 | endfunction 237 | 238 | function! s:extend_term_buffer_titles(specific_term_info, all_bufinfo) abort 239 | " add buffer name and terminal title to a dictionary that already has jobid, pid, buffer number 240 | " specific term info is a dictionary that contains jobid, pid, and bufnr 241 | " all_bufinfo is the output of getbufinfo() 242 | 243 | " important to use copy here to avoid filtering in calling environment 244 | let all_bufinfo_in = copy(a:all_bufinfo) 245 | let specific_term_info_in = copy(a:specific_term_info) 246 | 247 | " get the term info 248 | let wanted_term = filter(all_bufinfo_in, {_, val -> val['bufnr'] == specific_term_info_in['bufnr']})[0] 249 | return extend(specific_term_info_in, {'name': wanted_term['name'], 'term_title': wanted_term['variables']['term_title']}) 250 | endfunction 251 | 252 | 253 | function! s:buffer_dictionary_to_string(dict_in) abort 254 | " dict in is an array of dictionaries that has the values of the menu items 255 | 256 | " menu order is an array of dictionaries 257 | " menu entries will follow the order of menu order 258 | " the labels of each field of the menu entry will be the values of each dictionary in the array 259 | let menu_order = slime#config#resolve('neovim_menu_order') 260 | 261 | let delimiter = slime#config#resolve('neovim_menu_delimiter') 262 | 263 | let menu_string = '' 264 | 265 | for i in range(len(menu_order)) 266 | let menu_item = menu_order[i] 267 | let key = keys(menu_item)[0] 268 | let label = get(menu_item, key, "") 269 | let value = get(a:dict_in, key, "") 270 | if i != len(menu_order) - 1 271 | let menu_string = menu_string . label . value . delimiter 272 | else 273 | let menu_string = menu_string . label . value 274 | endif 275 | endfor 276 | 277 | return menu_string 278 | endfunction 279 | 280 | 281 | "get full bufinfo only of terminal buffers 282 | function! s:get_terminal_bufinfo() abort 283 | if !exists("g:slime_last_channel") || len(g:slime_last_channel) == 0 || empty(g:slime_last_channel) 284 | "there are no valid terminal buffers 285 | return [] 286 | endif 287 | 288 | let buf_info = getbufinfo() 289 | return map(copy(g:slime_last_channel), { _, val -> s:extend_term_buffer_titles(val, buf_info)}) 290 | endfunction 291 | 292 | 293 | function! s:config_with_menu() abort 294 | " get info of running terminals, array of dictionaries 295 | " reversing to make it appear in the right order in the menu 296 | let term_bufinfo = s:get_terminal_bufinfo() 297 | 298 | " turn each item into a string for the menu 299 | let menu_strings = map(copy(term_bufinfo), {_, val -> s:buffer_dictionary_to_string(val)}) 300 | 301 | for i in range(1, len(menu_strings)) 302 | let menu_strings[i - 1] = i . '. ' . menu_strings[i - 1] 303 | endfor 304 | call insert(menu_strings, "Select a terminal:") 305 | 306 | let selection = str2nr(inputlist(menu_strings)) 307 | 308 | if selection <= 0 || selection >= len(menu_strings) 309 | return {} 310 | endif 311 | 312 | let used_config = term_bufinfo[selection - 1] 313 | 314 | return {"jobid": used_config["jobid"], "pid": used_config["pid"] } 315 | endfunction 316 | 317 | 318 | "really make sure the config is cleared from the current buffer, and from all buffers with the same config 319 | function! s:sure_clear_buf_config() 320 | if exists('b:slime_config') && type(b:slime_config) == v:t_dict && !empty(b:slime_config) && has_key(b:slime_config, 'jobid') && type(b:slime_config['jobid']) == v:t_number 321 | call s:clear_related_bufs(b:slime_config['jobid']) 322 | endif 323 | endfunction 324 | 325 | 326 | 327 | function! slime#targets#neovim#EchoWarningMsg(msg) 328 | echohl WarningMsg 329 | echo a:msg 330 | echohl None 331 | endfunction 332 | -------------------------------------------------------------------------------- /autoload/slime/targets/screen.vim: -------------------------------------------------------------------------------- 1 | 2 | function! slime#targets#screen#config() abort 3 | if !exists("b:slime_config") 4 | let b:slime_config = {"sessionname": "", "windowname": "0"} 5 | end 6 | let b:slime_config["sessionname"] = input("screen session name: ", b:slime_config["sessionname"], "custom,slime#targets#screen#session_names") 7 | let b:slime_config["windowname"] = input("screen window name: ", b:slime_config["windowname"]) 8 | endfunction 9 | 10 | function! slime#targets#screen#send(config, text) 11 | call slime#common#write_paste_file(a:text) 12 | call slime#common#system('screen -S %s -p %s -X eval "readreg p %s"', [a:config["sessionname"], a:config["windowname"], slime#config#resolve("paste_file")]) 13 | call slime#common#system('screen -S %s -p %s -X paste p', [a:config["sessionname"], a:config["windowname"]]) 14 | endfunction 15 | 16 | " ------------------------------------------------- 17 | 18 | function! slime#targets#screen#session_names(A,L,P) 19 | return slime#common#system("screen -ls | awk '/Attached/ {print $1}'", []) 20 | endfunction 21 | 22 | -------------------------------------------------------------------------------- /autoload/slime/targets/tmux.vim: -------------------------------------------------------------------------------- 1 | 2 | function! slime#targets#tmux#config() abort 3 | if !exists("b:slime_config") 4 | let b:slime_config = {"socket_name": "default", "target_pane": ""} 5 | end 6 | let b:slime_config["socket_name"] = input("tmux socket name or absolute path: ", b:slime_config["socket_name"]) 7 | let b:slime_config["target_pane"] = input("tmux target pane: ", b:slime_config["target_pane"], "custom,slime#targets#tmux#pane_names") 8 | if b:slime_config["target_pane"] =~ '\s\+' 9 | let b:slime_config["target_pane"] = split(b:slime_config["target_pane"])[0] 10 | endif 11 | endfunction 12 | 13 | function! slime#targets#tmux#send(config, text) 14 | let target_cmd = s:target_cmd(a:config["socket_name"]) 15 | let [bracketed_paste, text_to_paste, has_crlf] = slime#common#bracketed_paste(a:text) 16 | 17 | if len(text_to_paste) == 0 18 | return 19 | end 20 | 21 | " only need to do this once 22 | call slime#common#system(target_cmd . " send-keys -X -t %s cancel", [a:config["target_pane"]]) 23 | 24 | " reasonable hardcode, will become config if needed 25 | let chunk_size = 1000 26 | 27 | for i in range(0, strchars(text_to_paste) / chunk_size) 28 | let chunk = strcharpart(text_to_paste, i * chunk_size, chunk_size) 29 | call slime#common#system(target_cmd . " load-buffer -", [], chunk) 30 | if bracketed_paste 31 | call slime#common#system(target_cmd . " paste-buffer -d -p -t %s", [a:config["target_pane"]]) 32 | else 33 | call slime#common#system(target_cmd . " paste-buffer -d -t %s", [a:config["target_pane"]]) 34 | end 35 | endfor 36 | 37 | " trailing newline 38 | if has_crlf 39 | call slime#common#system(target_cmd . " send-keys -t %s Enter", [a:config["target_pane"]]) 40 | end 41 | endfunction 42 | 43 | " ------------------------------------------------- 44 | 45 | function! slime#targets#tmux#pane_names(A,L,P) 46 | let target_cmd = s:target_cmd(b:slime_config["socket_name"]) 47 | let format = '#{pane_id} #{session_name}:#{window_index}.#{pane_index} #{window_name}#{?window_active, (active),}' 48 | return slime#common#system(target_cmd . " list-panes -a -F %s", [format]) 49 | endfunction 50 | 51 | function! s:target_cmd(socket_name) 52 | " socket with absolute path: use tmux -S 53 | if a:socket_name =~ "^/" 54 | return "tmux -S " . shellescape(a:socket_name) 55 | endif 56 | " socket with relative path: use tmux -L 57 | return "tmux -L " . shellescape(a:socket_name) 58 | endfunction 59 | -------------------------------------------------------------------------------- /autoload/slime/targets/vimterminal.vim: -------------------------------------------------------------------------------- 1 | 2 | function! slime#targets#vimterminal#config() abort 3 | if !exists("*term_start") 4 | echoerr "vimterminal support requires vim built with :terminal support" 5 | return 6 | endif 7 | if !exists("b:slime_config") 8 | let b:slime_config = {"bufnr": ""} 9 | end 10 | let bufs = filter(term_list(),"term_getstatus(v:val)=~'running'") 11 | let terms = map(bufs,"getbufinfo(v:val)[0]") 12 | let choices = map(copy(terms),"s:VimterminalDescription(v:key+1,v:val)") 13 | call add(choices, printf("%2d. ",len(terms)+1)) 14 | let choice = len(choices)>1 15 | \ ? inputlist(choices) 16 | \ : 1 17 | if choice > 0 18 | if choice>len(terms) 19 | if exists("b:slime_vimterminal_cmd") 20 | let cmd = b:slime_vimterminal_cmd 21 | elseif exists("g:slime_vimterminal_cmd") 22 | let cmd = g:slime_vimterminal_cmd 23 | else 24 | let cmd = input("Enter a command to run [".&shell."]:") 25 | if len(cmd)==0 26 | let cmd = &shell 27 | endif 28 | endif 29 | let winid = win_getid() 30 | if exists("g:slime_vimterminal_config") 31 | let new_bufnr = term_start(cmd, g:slime_vimterminal_config) 32 | else 33 | let new_bufnr = term_start(cmd) 34 | end 35 | call win_gotoid(winid) 36 | let b:slime_config["bufnr"] = new_bufnr 37 | else 38 | let b:slime_config["bufnr"] = terms[choice-1].bufnr 39 | endif 40 | endif 41 | endfunction 42 | 43 | function! slime#targets#vimterminal#send(config, text) 44 | let bufnr = str2nr(get(a:config,"bufnr","")) 45 | if len(term_getstatus(bufnr))==0 46 | echoerr "Invalid terminal. Use :SlimeConfig to select a terminal" 47 | return 48 | endif 49 | " send the text, translating newlines to enter keycode for Windows or any 50 | " other platforms where they are not the same 51 | call term_sendkeys(bufnr,substitute(a:text,'\n',"\r",'g')) 52 | endfunction 53 | 54 | " ------------------------------------------------- 55 | 56 | function! s:VimterminalDescription(idx,info) 57 | let title = term_gettitle(a:info.bufnr) 58 | if len(title)==0 59 | let title = term_getstatus(a:info.bufnr) 60 | endif 61 | return printf("%2d.%4d %s [%s]",a:idx,a:info.bufnr,a:info.name,title) 62 | endfunction 63 | 64 | -------------------------------------------------------------------------------- /autoload/slime/targets/wezterm.vim: -------------------------------------------------------------------------------- 1 | 2 | function! slime#targets#wezterm#config() abort 3 | if !exists("b:slime_config") 4 | let b:slime_config = {"pane_id": 1} 5 | elseif exists("b:slime_config.pane_direction") 6 | let pane_id = slime#common#system("wezterm cli get-pane-direction %s", [b:slime_config["pane_direction"]]) 7 | let pane_id = trim(pane_id) 8 | let b:slime_config = {"pane_id": pane_id} 9 | endif 10 | let b:slime_config["pane_id"] = input("wezterm pane_id: ", b:slime_config["pane_id"]) 11 | endfunction 12 | 13 | function! slime#targets#wezterm#send(config, text) 14 | let [bracketed_paste, text_to_paste, has_crlf] = slime#common#bracketed_paste(a:text) 15 | 16 | if bracketed_paste 17 | call slime#common#system("wezterm cli send-text --pane-id=%s", [a:config["pane_id"]], text_to_paste) 18 | else 19 | call slime#common#system("wezterm cli send-text --no-paste --pane-id=%s", [a:config["pane_id"]], text_to_paste) 20 | endif 21 | 22 | " trailing newline 23 | if has_crlf 24 | call slime#common#system("wezterm cli send-text --no-paste --pane-id=%s", [a:config["pane_id"]], "\n") 25 | end 26 | endfunction 27 | 28 | -------------------------------------------------------------------------------- /autoload/slime/targets/whimrepl.vim: -------------------------------------------------------------------------------- 1 | 2 | function! slime#targets#whimrepl#config() abort 3 | if !exists("b:slime_config") 4 | let b:slime_config = {"server_name": "whimrepl"} 5 | end 6 | let b:slime_config["server_name"] = input("whimrepl server name: ", b:slime_config["server_name"]) 7 | endfunction 8 | 9 | function! slime#targets#whimrepl#send(config, text) 10 | call remote_send(a:config["server_name"], a:text) 11 | endfunction 12 | 13 | -------------------------------------------------------------------------------- /autoload/slime/targets/x11.vim: -------------------------------------------------------------------------------- 1 | 2 | function! slime#targets#x11#config() abort 3 | if !exists("b:slime_config") 4 | let b:slime_config = {"window_id": ""} 5 | end 6 | let b:slime_config["window_id"] = trim(slime#common#system("xdotool selectwindow", [])) 7 | endfunction 8 | 9 | function! slime#targets#x11#send(config, text) 10 | call slime#common#system("xdotool type --delay 0 --window %s -- %s", [a:config["window_id"], a:text]) 11 | endfunction 12 | 13 | -------------------------------------------------------------------------------- /autoload/slime/targets/zellij.vim: -------------------------------------------------------------------------------- 1 | 2 | function! slime#targets#zellij#config() abort 3 | if !exists("b:slime_config") 4 | let b:slime_config = {"session_id": "current", "relative_pane": "current"} 5 | end 6 | let b:slime_config["session_id"] = input("zellij session: ", b:slime_config["session_id"]) 7 | let b:slime_config["relative_pane"] = input("target pane relative position: ", b:slime_config["relative_pane"]) 8 | if b:slime_config["relative_pane"] == "current" 9 | let b:slime_config["relative_move_back"] = "current" 10 | elseif b:slime_config["relative_pane"] == "right" 11 | let b:slime_config["relative_move_back"] = "left" 12 | elseif b:slime_config["relative_pane"] == "left" 13 | let b:slime_config["relative_move_back"] = "right" 14 | elseif b:slime_config["relative_pane"] == "up" 15 | let b:slime_config["relative_move_back"] = "down" 16 | elseif b:slime_config["relative_pane"] == "down" 17 | let b:slime_config["relative_move_back"] = "up" 18 | else 19 | echoerr "Error: Allowed values are (current, right, left, up, down)" 20 | endif 21 | endfunction 22 | 23 | function! slime#targets#zellij#send(config, text) 24 | let target_cmd = s:target_cmd(a:config["session_id"]) 25 | if a:config["relative_pane"] != "current" 26 | call slime#common#system(target_cmd . " action move-focus %s", [a:config["relative_pane"]]) 27 | end 28 | let [bracketed_paste, text_to_paste, has_crlf] = slime#common#bracketed_paste(a:text) 29 | 30 | if bracketed_paste 31 | call slime#common#system(target_cmd . " action write 27 91 50 48 48 126", []) 32 | call slime#common#system(target_cmd . " action write-chars %s", [text_to_paste]) 33 | call slime#common#system(target_cmd . " action write 27 91 50 48 49 126", []) 34 | if has_crlf 35 | call slime#common#system(target_cmd . " action write 10", []) 36 | endif 37 | else 38 | call slime#common#system(target_cmd . " action write-chars %s", [text_to_paste]) 39 | endif 40 | 41 | if a:config["relative_pane"] != "current" 42 | call slime#common#system(target_cmd . " action move-focus %s", [a:config["relative_move_back"]]) 43 | end 44 | endfunction 45 | 46 | " ------------------------------------------------- 47 | 48 | function! s:target_cmd(session_id) 49 | if a:session_id != "current" 50 | return "zellij -s " . shellescape(a:session_id) 51 | end 52 | return "zellij" 53 | endfunction 54 | 55 | -------------------------------------------------------------------------------- /doc/vim-slime.txt: -------------------------------------------------------------------------------- 1 | *slime.txt* Grab some text and "send" it to a GNU Screen / tmux / whimrepl session. 2 | 3 | Author: Jonathan Palardy *slime-author* 4 | License: Same terms as Vim itself (see |license|) 5 | 6 | This plugin is only available if 'compatible' is not set. 7 | 8 | ============================================================================== 9 | *slime* 10 | Grab some text and "send" it to a GNU Screen / tmux / whimrepl session. 11 | 12 | VIM ---(text)---> screen / tmux / whimrepl~ 13 | 14 | Presumably, your session contains a REPL, maybe Clojure, R or python. If you 15 | can type text into it, vim-slime can send text to it. 16 | 17 | The reason you're doing this? Because you want the benefits of a REPL and the 18 | benefits of using Vim (familiar environment, syntax highlighting, persistence 19 | ...). 20 | 21 | 1. Usage |slime-usage| 22 | 2. Screen Configuration |slime-screen| 23 | 3. Tmux Configuration |slime-tmux| 24 | 4. dtach Configuration |slime-dtach| 25 | 5. Neovim Configuration |slime-neovim| 26 | 6. Kitty Configuration |slime-kitty| 27 | 7. Zellij Configuration |slime-zellij| 28 | 8. Wezterm Configuration |slime-wezterm| 29 | 9. X11 Configuration |slime-x11| 30 | 10. whimrepl Configuration |slime-whimrepl| 31 | 11. vimterminal Configuration |slime-vimterminal| 32 | 12. Slime Configuration |slime-configuration| 33 | 13. Slime Requirements |slime-requirements| 34 | 35 | ============================================================================== 36 | 1. Slime Usage *slime-usage* 37 | 38 | *CTRL-C_CTRL-C* ** 39 | Send the current paragraph text to screen/tmux/whimrepl. 40 | Slime will prompt for configuration if slime is not 41 | configured for the current buffer. 42 | 43 | *v_CTRL-C_CTRL-C* *v_* 44 | {Visual} Send highlighted text to screen/tmux/whimrepl. 45 | 46 | *CTRL-C_v* *v* 47 | *:SlimeConfig* 48 | v Setup slime to use screen, tmux or whimrepl. You will 49 | :SlimeConfig be prompted for information regarding how to target 50 | screen, tmux or whimrepl. See |slime-screen|, 51 | |slime-tmux| or |slime-whimrepl| for more information. 52 | 53 | *:SlimeSend* 54 | :SlimeSend Send a [range] of lines to screen, tmux or whimrepl. 55 | If no range is provided the current line is sent. 56 | 57 | *:SlimeSend1* 58 | :SlimeSend1 {text} Send a single line of text, specified on the command 59 | line, to screen, tmux, or whimrepl. A carriage return 60 | is automatically appended. 61 | 62 | *:SlimeSend0* 63 | :SlimeSend0 {text} Send a single line of text, specified on the command 64 | line, to screen, tmux, or whimrepl. This will not 65 | automatically append a carriage return. 66 | 67 | 68 | ============================================================================== 69 | 2. Screen Configuration *slime-screen* 70 | 71 | By default, GNU Screen is assumed, you don't have to do anything. If you want 72 | to be explicit, you can add this line to your |.vimrc|: 73 | > 74 | let g:slime_target = "screen" 75 | < 76 | When you invoke vim-slime for the first time (see below), you will be prompted 77 | for more configuration. 78 | 79 | Screen session name~ 80 | 81 | This is what you put in the -S flag, or one of the line of "screen -ls". 82 | 83 | Screen window name~ 84 | 85 | This is the window number or name, zero-based. 86 | 87 | ============================================================================== 88 | 3. Tmux Configuration *slime-tmux* 89 | 90 | Tmux is not the default, to use it you will have to add this line to your 91 | |.vimrc|: 92 | > 93 | let g:slime_target = "tmux" 94 | < 95 | When you invoke vim-slime for the first time (see below), you will be prompted 96 | for more configuration. 97 | 98 | Tmux socket name~ 99 | 100 | This is what you put in the -L flag, it will be "default" if you didn't put 101 | anything. 102 | 103 | Tmux target pane~ 104 | 105 | A tmux pane can be targeted with any of the following values: 106 | - ":" means current window, current pane (a reasonable default) 107 | - ":i" means the ith window, current pane 108 | - ":i.j" means the ith window, jth pane 109 | - "h:i.j" means the tmux session where h is the session identifier 110 | (either session name or number), the ith window and the jth pane 111 | - "%i" means i refers the pane's unique id 112 | - "{token}" one of tmux's supported special tokens, like "{right-of}" 113 | 114 | To get a list of all the available pane execute the following: 115 | > 116 | tmux list-panes -a 117 | < 118 | 119 | Use bracketed-paste mode~ 120 | 121 | Sometimes REPL are too smart for their own good, e.g. autocompleting a bracket 122 | that should not be autocompleted when pasting code from a file. In this case 123 | it can be useful to rely on bracketed-paste 124 | (https://cirw.in/blog/bracketed-paste). Luckily, some targets (https://github.com/jpalardy/vim-slime#targets) know how to handle 125 | that. 126 | 127 | You can enable bracketed-paste using eiter 128 | > 129 | g:slime_bracketed_paste 130 | < 131 | or 132 | > 133 | b:slime_bracketed_paste 134 | < 135 | 136 | Note that the buffer variable takes precedence over the global's one. 137 | 138 | ============================================================================== 139 | 4. dtach Configuration *slime-dtach* 140 | 141 | dtach is not the default, to use it you will have to add this line to your 142 | |.vimrc|: 143 | > 144 | let g:slime_target = "dtach" 145 | 146 | < 147 | When you invoke vim-slime for the first time, you will be prompted for more 148 | configuration. 149 | 150 | socket_path~ 151 | 152 | The path to the Unix-domain socket that the dtach session is attached to. 153 | The default is /tmp/slime 154 | 155 | ============================================================================== 156 | 5. Neovim Configuration *slime-neovim* 157 | 158 | Neovim is not the default, to use it you will have to add this line to your 159 | |.vimrc|: 160 | > 161 | let g:slime_target = "neovim" 162 | < 163 | When you invoke vim-slime for the first time (see below), you will be prompted 164 | for more configuration. 165 | 166 | jobid~ 167 | 168 | In the neovim terminal buffer type ":echo &channel" to get the 169 | job_id for that terminal 170 | 171 | ============================================================================== 172 | 6. Kitty Configuration *slime-kitty* 173 | 174 | Kitty is not the default, to use it you will have to add this line to your 175 | |.vimrc|: 176 | > 177 | let g:slime_target = "kitty" 178 | < 179 | When you invoke vim-slime for the first time (see below), you will be prompted 180 | for more configuration. 181 | 182 | kitty target window~ 183 | 184 | See e.g. the value of $KITTY_WINDOW_ID in the target window. 185 | 186 | ============================================================================== 187 | 7. Zellij Configuration *slime-zellij* 188 | 189 | Zellij is not the default, to use it you will have to add this line to your 190 | |.vimrc|: 191 | > 192 | let g:slime_target = "zellij" 193 | < 194 | When you invoke vim-slime for the first time (see below), you will be prompted 195 | for more configuration. 196 | 197 | Zellij session id~ 198 | 199 | This is the id of the zellij session that you wish to target, the default value is "current" meaning the session containing the vim pane. 200 | See e.g. the value of "zellij list-sessions" in the target window to figure out specific session names. 201 | 202 | Zellij relative pane~ 203 | 204 | A pane relative to the currently active one in the target session 205 | - "current" for the currently active pane 206 | - "up"/"down"/"right"/"left" for the pane in that direction relative to the location of the active pane 207 | 208 | ============================================================================== 209 | 8. Wezterm Configuration *slime-wezterm* 210 | 211 | Wezterm is not the default, to use it you will have to add this line to your 212 | |.vimrc|: 213 | > 214 | let g:slime_target = "wezterm" 215 | < 216 | When you invoke vim-slime for the first time (see below), you will be prompted 217 | for more configuration. 218 | 219 | wezterm pane id~ 220 | 221 | See e.g. the value of $WEZTERM_PANE in the target pane. 222 | 223 | wezterm pane direction~ 224 | 225 | If you want the id of the pane in a relative direction to the default, see "wezterm cli get-pane-direction --help" for possible values. 226 | 227 | You can configure the defaults for these options. If you generally run vim to the left of your REPL, you could set the default pane direction to "right".~ 228 | > 229 | let g:slime_default_config = {"pane_direction": "right"} 230 | < 231 | 232 | ============================================================================== 233 | 9. X11 Configuration *slime-x11* 234 | 235 | x11 is not the default, to use it you will have to add this line to your 236 | |.vimrc|: 237 | > 238 | let g:slime_target = "x11" 239 | < 240 | When you invoke vim-slime for the first time, you will have to designate a 241 | target window by clicking on it. 242 | 243 | ============================================================================== 244 | 10. whimrepl Configuration *slime-whimrepl* 245 | 246 | whimrepl is not the default, to use it you will have to add this line to 247 | your |.vimrc|: 248 | > 249 | let g:slime_target = "whimrepl" 250 | < 251 | When you invoke vim-slime for the first time (see below), you will be prompted 252 | for more configuration. 253 | 254 | whimrepl server name~ 255 | 256 | This is the name of the whimrepl server that you wish to target. whimrepl 257 | displays that name in its banner every time you start up an instance of 258 | whimrepl. 259 | 260 | ============================================================================== 261 | 11. Vim :terminal Configuration *slime-vimterminal* 262 | 263 | Vim :terminal support targets the terminal emulator built into vim from 264 | version 8.0.0693, accessed via the :terminal command. It does not support the 265 | neovim implementation of :terminal, which is instead supported by the separate 266 | 'neovim' target. 267 | 268 | Vim :terminal is not the default, to use it you will have to add this line to 269 | your |.vimrc|: 270 | > 271 | let g:slime_target = "vimterminal" 272 | 273 | Vim terminal configuration can be set by using the following in your .vimrc: 274 | 275 | let g:slime_vimterminal_config = {options} 276 | 277 | for possible options, see help term_start() 278 | 279 | You can specify if you have frequently used commands: 280 | > 281 | let b:slime_vimterminal_cmd = "command" 282 | < 283 | or 284 | > 285 | let g:slime_vimterminal_cmd = "command" 286 | < 287 | For example, if you use Python, set the following in your |.vimrc|: 288 | > 289 | autocmd FileType python let b:slime_vimterminal_cmd='python3' 290 | < 291 | Note that the buffer variable takes precedence over the global one. 292 | 293 | When you invoke vim-slime for the first time (see below), you will be prompted 294 | to select from an existing terminal or to create a new one. 295 | 296 | ============================================================================== 297 | 12. Slime Configuration *slime-configuration* 298 | 299 | Global Variables~ 300 | *g:slime_target* 301 | g:slime_target Set to either "screen" (default), "tmux" or "whimrepl". 302 | 303 | *g:slime_no_mappings* 304 | g:slime_no_mappings Set to non zero value to disable the default mappings. 305 | 306 | *g:slime_paste_file* 307 | g:slime_paste_file Required to transfer data from vim to GNU screen. 308 | Set to "$HOME/.slime_paste" by default. Setting 309 | this explicitly can work around some occasional 310 | portability issues. whimrepl does not require or 311 | support this setting. 312 | 313 | *g:slime_preserve_curpos* 314 | g:slime_preserve_curpos Set to non zero value to preserve cursor position when 315 | sending a line or paragraph. Default is 1. 316 | 317 | 318 | *g:slime_default_config* 319 | g:slime_default_config Set to dictionary of pre-filled prompt answer. See 320 | the README for details: https://github.com/jpalardy/vim-slime 321 | 322 | *g:slime_dont_ask_default* 323 | g:slime_dont_ask_default Works with g:slime_default_config. See 324 | the README for details: https://github.com/jpalardy/vim-slime 325 | 326 | Mappings~ 327 | 328 | Slime's default mappings can be overridden by setting up mappings in your 329 | |.vimrc| like so: 330 | > 331 | xmap s SlimeRegionSend 332 | nmap s SlimeParagraphSend 333 | < 334 | 335 | The following special plugin mappings are provided by slime: 336 | 337 | Used by the default mappings: 338 | SlimeRegionSend Send {visual} text. Use |xmap|. 339 | SlimeParagraphSend Send a paragraph. Use |nmap|. 340 | SlimeConfig Call |:SlimeConfig|. Use |nmap|. 341 | 342 | Optional mappings: 343 | SlimeLineSend Send {count} line(s). Use |nmap|. 344 | SlimeMotionSend Send {motion}. Use |nmap|. 345 | 346 | Disabling a mapping is as simple as creating a mapping that does not exist, 347 | for example: 348 | > 349 | nmap NoSlimeParagraphSend SlimeParagraphSend 350 | < 351 | 352 | To use vim like mappings instead of emacs keybindings use the following: 353 | > 354 | let g:slime_no_mappings = 1 355 | xmap s SlimeRegionSend 356 | nmap s SlimeMotionSend 357 | nmap ss SlimeLineSend 358 | < 359 | 360 | ============================================================================== 361 | 13. Slime Requirements *slime-requirements* 362 | 363 | Slime requires either screen or tmux to be available and executable. Awk is 364 | used for completion of screen sessions. whimrepl does not require any 365 | executables, only that 'lein whimrepl' was used to start the Leiningen REPL. 366 | 367 | ============================================================================== 368 | 369 | vim:tw=78:ts=8:ft=help:norl: 370 | -------------------------------------------------------------------------------- /ftplugin/coffee/slime.vim: -------------------------------------------------------------------------------- 1 | 2 | function! _EscapeText_coffee(text) 3 | " \x16 is ctrl-v 4 | return ["\x16", a:text, "\x16"] 5 | endfunction 6 | 7 | -------------------------------------------------------------------------------- /ftplugin/elm/slime.vim: -------------------------------------------------------------------------------- 1 | 2 | " add newline to multi-line blocks 3 | " "\n." --> newline with text after 4 | function! _EscapeText_elm(text) 5 | if match(a:text, "\n.") > -1 6 | return [a:text, "\n"] 7 | endif 8 | return a:text 9 | endfunction 10 | 11 | -------------------------------------------------------------------------------- /ftplugin/fsharp/slime.vim: -------------------------------------------------------------------------------- 1 | 2 | function! _EscapeText_fsharp(text) 3 | let trimmed = substitute(a:text, '\_s*$', '', '') 4 | if match(trimmed,';;\n*$') > -1 5 | return [trimmed,"\n"] 6 | else 7 | return [trimmed,";;\n"] 8 | endif 9 | endfunction 10 | 11 | -------------------------------------------------------------------------------- /ftplugin/haskell/README.md: -------------------------------------------------------------------------------- 1 | 2 | ### Haskell 3 | 4 | This plugin has support for sending Haskell source code to `ghci`. 5 | 6 | #### Sending normal code 7 | 8 | To support older GHC versions, code is processed in order to comply with the 9 | syntax rules that are specific to interactive mode. For instance when sending 10 | the following snippet to `ghci`: 11 | 12 | ```haskell 13 | -- make in triplicate 14 | f :: a -> [a] 15 | f = replicate 3 16 | ``` 17 | 18 | This translates to the following: 19 | 20 | ```haskell 21 | :{ 22 | let f :: a -> [a] 23 | f = replicate 3 24 | :} 25 | ``` 26 | 27 | Some of this behavior can be selectively turned off so that what is run is more 28 | faithful to the actual code in your buffer, but requires a recent enough GHC: 29 | 30 | * `let g:slime_haskell_ghci_add_let = 0` disables the transformation of 31 | top-level bindings into a let binding; requires GHC 8.0.1 or later 32 | 33 | #### Sending GHCi scripts 34 | 35 | All of this is very nice but my workflow sometimes requires that I send the same 36 | code to `ghci` over and over, so I usually put it in a separate "script" file 37 | that holds some testing instructions so I can send them quickly. 38 | 39 | However since some of the syntax is different between interactive and normal 40 | Haskell and I write these script files as if I were writing in `ghci`, sometimes 41 | the syntax translation would get in the way. E.g. I would write a function call 42 | to test a certain function and check it's type: 43 | 44 | ```haskell 45 | (++) "This is a: " "TEST" 46 | :type (++) 47 | ``` 48 | 49 | and it get translated to: 50 | 51 | ```haskell 52 | :{ 53 | let (++) "This is a: " "TEST" 54 | :type (++) 55 | :} 56 | ``` 57 | 58 | which is not what I wanted obviously. 59 | 60 | To get around this, there is another handler that only kicks in if the filetype 61 | in vim is set to `haskell.script`. If you want access to this handler call `set 62 | ft=haskell.script` or create a new ftdetect file which does this for you for a 63 | certain file extension. For instance, I have: 64 | 65 | ```vim 66 | au BufRead,BufNewFile,BufNew *.hss setl ft=haskell.script 67 | ``` 68 | 69 | in `~/.vim/ftdetect/hss.vim`. 70 | 71 | -------------------------------------------------------------------------------- /ftplugin/haskell/slime.vim: -------------------------------------------------------------------------------- 1 | 2 | " GHC before 8.0.1 does not support top-level bindings 3 | let g:slime_config_defaults["haskell_ghci_add_let"] = 1 4 | 5 | " Remove '>' on line beginning in literate haskell 6 | function! s:Remove_initial_gt(lines) 7 | return map(copy(a:lines), "substitute(v:val, '^>[ \t]*', '', 'g')") 8 | endfunction 9 | 10 | function! s:Is_type_declaration(line) 11 | let l:isNewType = a:line =~ "newtype" 12 | let l:isTypeAlias = a:line =~ "type" 13 | let l:isData = a:line =~ "data" 14 | return l:isNewType || l:isTypeAlias || l:isData 15 | endfunction 16 | 17 | " Prepend certain statements with 'let' 18 | function! s:Perhaps_prepend_let(lines) 19 | if len(a:lines) > 0 20 | let l:lines = a:lines 21 | let l:line = l:lines[0] 22 | 23 | " Prepend let if the line is an assignment 24 | if (l:line =~ "=[^>]" || l:line =~ "::") && !s:Is_type_declaration(l:line) 25 | let l:lines[0] = "let " . l:lines[0] 26 | endif 27 | 28 | return l:lines 29 | else 30 | return a:lines 31 | endif 32 | endfunction 33 | 34 | " indent lines except for first one. 35 | " lines are indented equally, so indentation is preserved. 36 | function! s:Indent_lines(lines) 37 | let l:lines = a:lines 38 | let l:indent = slime#common#get_indent_string() 39 | let l:i = 1 40 | let l:len = len(l:lines) 41 | while l:i < l:len 42 | let l:lines[l:i] = l:indent . l:lines[l:i] 43 | let l:i += 1 44 | endwhile 45 | return l:lines 46 | endfunction 47 | 48 | " Check if line is commented out 49 | function! s:Is_comment(line) 50 | return (match(a:line, "^[ \t]*--.*") >= 0) 51 | endfunction 52 | 53 | " Remove commented out lines 54 | function! s:Remove_line_comments(lines) 55 | return filter(copy(a:lines), "!s:Is_comment(v:val)") 56 | endfunction 57 | 58 | " remove block comments 59 | function! s:Remove_block_comments(text) 60 | return substitute(a:text, "{-.*-}", "", "g") 61 | endfunction 62 | 63 | " remove line comments 64 | " todo: fix this! it only removes one occurence whilst it should remove all. 65 | " function! s:Remove_line_comments(text) 66 | " return substitute(a:text, "^[ \t]*--[^\n]*\n", "", "g") 67 | " endfunction 68 | 69 | " Wrap in :{ :} if there's more than one line 70 | function! Wrap_if_multi(lines) 71 | if len(a:lines) > 1 72 | return [":{"] + a:lines + [":}"] 73 | else 74 | return a:lines 75 | endif 76 | endfunction 77 | 78 | function! s:FilterImportLines(lines) 79 | let l:matches = [] 80 | let l:noMatches = [] 81 | for l:line in a:lines 82 | if l:line =~ "^import" 83 | call add(l:matches, l:line) 84 | else 85 | call add(l:noMatches, l:line) 86 | endif 87 | endfor 88 | return [l:matches, l:noMatches] 89 | endfunction 90 | 91 | " vim slime handler 92 | function! _EscapeText_lhaskell(text) 93 | let l:text = s:Remove_block_comments(a:text) 94 | let l:lines = slime#common#lines(slime#common#tab_to_spaces(l:text)) 95 | let l:lines = s:Remove_initial_gt(l:lines) 96 | let [l:imports, l:nonImports] = s:FilterImportLines(l:lines) 97 | let l:lines = s:Remove_line_comments(l:nonImports) 98 | 99 | if slime#config#resolve("haskell_ghci_add_let") 100 | let l:lines = s:Perhaps_prepend_let(l:lines) 101 | let l:lines = s:Indent_lines(l:lines) 102 | endif 103 | 104 | let l:lines = Wrap_if_multi(l:lines) 105 | return slime#common#unlines(l:imports + l:lines) 106 | endfunction 107 | 108 | function! _EscapeText_haskell(text) 109 | let l:text = s:Remove_block_comments(a:text) 110 | let l:lines = slime#common#lines(slime#common#tab_to_spaces(l:text)) 111 | let [l:imports, l:nonImports] = s:FilterImportLines(l:lines) 112 | let l:lines = s:Remove_line_comments(l:nonImports) 113 | 114 | if slime#config#resolve("haskell_ghci_add_let") 115 | let l:lines = s:Perhaps_prepend_let(l:lines) 116 | let l:lines = s:Indent_lines(l:lines) 117 | endif 118 | 119 | let l:lines = Wrap_if_multi(l:lines) 120 | return slime#common#unlines(l:imports + l:lines) 121 | endfunction 122 | 123 | function! _EscapeText_haskell_script(text) 124 | let l:text = s:Remove_block_comments(a:text) 125 | let l:lines = slime#common#lines(slime#common#tab_to_spaces(l:text)) 126 | let l:lines = s:Remove_line_comments(l:lines) 127 | return slime#common#unlines(l:lines) 128 | endfunction 129 | 130 | -------------------------------------------------------------------------------- /ftplugin/lhaskell/slime.vim: -------------------------------------------------------------------------------- 1 | ../haskell/slime.vim -------------------------------------------------------------------------------- /ftplugin/matlab/slime.vim: -------------------------------------------------------------------------------- 1 | 2 | " Check if line is commented out 3 | function! s:Is_comment(line) 4 | return (match(a:line, "^[ \t]*%.*") >= 0) 5 | endfunction 6 | 7 | " Remove commented out lines 8 | function! s:Remove_line_comments(lines) 9 | return filter(copy(a:lines), "!s:Is_comment(v:val)") 10 | endfunction 11 | 12 | " vim slime handler 13 | function! _EscapeText_matlab(text) 14 | let l:lines = slime#common#lines(slime#common#tab_to_spaces(a:text)) 15 | let l:lines = s:Remove_line_comments(l:lines) 16 | return slime#common#unlines(l:lines) 17 | endfunction 18 | 19 | -------------------------------------------------------------------------------- /ftplugin/ocaml/slime.vim: -------------------------------------------------------------------------------- 1 | 2 | function! _EscapeText_ocaml(text) 3 | let trimmed = substitute(a:text, '\_s*$', '', '') 4 | if match(trimmed,';;\n*$') > -1 5 | return [trimmed,"\n"] 6 | else 7 | return [trimmed . ";;","\n"] 8 | endif 9 | endfunction 10 | 11 | -------------------------------------------------------------------------------- /ftplugin/python/README.md: -------------------------------------------------------------------------------- 1 | 2 | ### Python 3 | 4 | Sending code to an interactive Python session is tricky business due to 5 | Python's indentation-sensitive nature. Perfectly valid code which executes when 6 | run from a file may fail with a `SyntaxError` when pasted into the CPython 7 | interpreter. 8 | 9 | [IPython](http://ipython.org/) has a `%cpaste` "magic function" that allows for 10 | error-free pasting. In order for vim-slime to make use of this feature for 11 | Python buffers, you need to set the corresponding variable in your .vimrc: 12 | 13 | ```vim 14 | let g:slime_python_ipython = 1 15 | ``` 16 | 17 | Note: if you're using IPython 5, you _need_ to set `g:slime_python_ipython` for 18 | pasting to work correctly. 19 | 20 | #### Note for `tmux`, `kitty`, `wezterm`, `zellij` users 21 | 22 | If your target supports [bracketed-paste](https://cirw.in/blog/bracketed-paste), that's 23 | a better option than `g:slime_python_ipython`: 24 | 25 | ```vim 26 | " in .vimrc 27 | let g:slime_bracketed_paste = 1 28 | " or, in ftplugin/python.vim 29 | let b:slime_bracketed_paste = 1 30 | ``` 31 | 32 | This lets your target deal with all the problems with indentation and avoids depending `%cpaste`, 33 | which occassionally causes issues (e.g., [#327](https://github.com/jpalardy/vim-slime/issues/327)) 34 | 35 | -------------------------------------------------------------------------------- /ftplugin/python/slime.vim: -------------------------------------------------------------------------------- 1 | 2 | let g:slime_config_defaults["python_ipython"] = 0 3 | let g:slime_config_defaults["dispatch_ipython_pause"] = 100 4 | 5 | function! _EscapeText_python(text) 6 | if slime#config#resolve("python_ipython") && len(split(a:text,"\n")) > 1 7 | return ["%cpaste -q\n", slime#config#resolve("dispatch_ipython_pause"), a:text, "--\n"] 8 | else 9 | let empty_lines_pat = '\(^\|\n\)\zs\(\s*\n\+\)\+' 10 | let no_empty_lines = substitute(a:text, empty_lines_pat, "", "g") 11 | let dedent_pat = '\(^\|\n\)\zs'.matchstr(no_empty_lines, '^\s*') 12 | let dedented_lines = substitute(no_empty_lines, dedent_pat, "", "g") 13 | let except_pat = '\(elif\|else\|except\|finally\)\@!' 14 | let add_eol_pat = '\n\s[^\n]\+\n\zs\ze\('.except_pat.'\S\|$\)' 15 | return substitute(dedented_lines, add_eol_pat, "\n", "g") 16 | end 17 | endfunction 18 | 19 | -------------------------------------------------------------------------------- /ftplugin/scala/README.md: -------------------------------------------------------------------------------- 1 | 2 | ### Scala 3 | 4 | By default, `vim-slime` expects to use the base Scala REPL 5 | (or [sbt](https://www.scala-sbt.org/) console). 6 | 7 | Scala's [Ammonite REPL](https://ammonite.io) is an improved Scala REPL, 8 | similar to IPython. It does not have a `:paste` command like the default 9 | REPL, but rather wraps multi-line expressions in curly braces. 10 | 11 | To use Ammonite with `vim-slime`, set the following variable in your `.vimrc`: 12 | 13 | ```vim 14 | let g:slime_scala_ammonite = 1 15 | ``` 16 | 17 | Note that although Scala is usually installed on a per-project basis 18 | through tools like sbt, setting `let g:slime_scala_ammonite = 1` 19 | is a global change to your Vim setup. 20 | 21 | You will need to unset the variable in your `.vimrc` before starting Vim 22 | if you decide not to use Ammonite for a particular project. 23 | -------------------------------------------------------------------------------- /ftplugin/scala/slime.vim: -------------------------------------------------------------------------------- 1 | 2 | let g:slime_config_defaults["scala_ammonite"] = 0 3 | 4 | function! _EscapeText_scala(text) 5 | if slime#config#resolve("scala_ammonite") 6 | return ["{\n", a:text, "}\n"] 7 | end 8 | " \x04 is ctrl-d 9 | return [":paste\n", a:text, "\x04"] 10 | endfunction 11 | -------------------------------------------------------------------------------- /ftplugin/sh/slime.vim: -------------------------------------------------------------------------------- 1 | " vim slime handler 2 | function! _EscapeText_sh(text) 3 | let l:lines = slime#common#lines(slime#common#tab_to_spaces(a:text)) 4 | return slime#common#unlines(l:lines) 5 | endfunction 6 | -------------------------------------------------------------------------------- /ftplugin/sml/slime.vim: -------------------------------------------------------------------------------- 1 | 2 | function! _EscapeText_sml(text) 3 | let trimmed = substitute(a:text, '\_s*$', '', '') 4 | if match(trimmed, ';\n*$') > -1 5 | return [trimmed, "\n"] 6 | else 7 | return [trimmed, ";\n"] 8 | endif 9 | endfunction 10 | 11 | -------------------------------------------------------------------------------- /ftplugin/stata/slime.vim: -------------------------------------------------------------------------------- 1 | 2 | function! _EscapeText_stata(text) 3 | let remove_comments = substitute(a:text, '///[^\n]*\n', ' ', 'g') 4 | let remove_comments = substitute(remove_comments, '\n//[^\n]*', '', 'g') 5 | let remove_comments = substitute(remove_comments, '\s//[^\n]*', '', 'g') 6 | let remove_comments = substitute(remove_comments, '/\*.\{-}\*/', '', 'g') 7 | return remove_comments 8 | endfunction 9 | 10 | -------------------------------------------------------------------------------- /plugin/slime.vim: -------------------------------------------------------------------------------- 1 | if exists('g:loaded_slime') || &cp || v:version < 700 2 | finish 3 | endif 4 | let g:loaded_slime = 1 5 | 6 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 7 | " Setup key bindings 8 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 9 | 10 | command -bar -nargs=0 SlimeConfig call slime#config() 11 | command -range -bar -nargs=0 SlimeSend call slime#send_range(, ) 12 | command -nargs=+ SlimeSend1 call slime#send( . "\r") 13 | command -nargs=+ SlimeSend0 call slime#send() 14 | command! SlimeSendCurrentLine call slime#send(getline(".") . "\r") 15 | 16 | noremap Operator :call slime#store_curpos():set opfunc=slime#send_opg@ 17 | 18 | noremap