├── .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 | 
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 | 
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