-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from jam1015/pid_input
enhanced terminal tracking/Pid input
- Loading branch information
Showing
3 changed files
with
251 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,103 @@ | ||
# vim-slime-ext-neovim | ||
An experiment for an external neovim plugin for vim-slime | ||
|
||
## Example of configuration using packer | ||
A plugin to send code from a neovim Neovim buffer to a running Neovim terminal, enhancing your development workflow. This plugin uses Neovim's built-in terminal and extends [vim-slime-ext-plugins](https://github.com/jpalardy/vim-slime-ext-plugins/). | ||
|
||
|
||
## Example of installation and configuration using lazy.nvim | ||
|
||
### Lua installation Configuration | ||
|
||
```lua | ||
use { | ||
'Klafyvel/vim-slime-ext-neovim', | ||
config=function () | ||
vim.g.slime_target_send = "slime_neovim#send" | ||
vim.g.slime_target_config = "slime_neovim#config" | ||
end | ||
} | ||
{ | ||
'Klafyvel/vim-slime-ext-neovim', | ||
dependencies = { "jpalardy/vim-slime-ext-plugins" }, | ||
config = function() | ||
vim.g.slime_target_send = "slime_neovim#send" | ||
vim.g.slime_target_config = "slime_neovim#config" | ||
|
||
-- allows use of PID rather than internal job_id for config see note below this codeblock | ||
vim.g.slime_input_pid = 1 | ||
|
||
-- optional but useful keymaps: | ||
---- send text using gz as operator before motion or text object | ||
vim.keymap.set("n", "gz", "<Plug>SlimeMotionSend", { remap = true }) | ||
---- send line of text | ||
vim.keymap.set("n", "gzz", "<Plug>SlimeLineSend", { remap = true }) | ||
---- send visual selection | ||
vim.keymap.set("x", "gz", "<Plug>SlimeRegionSend", { remap = true }) | ||
end | ||
} | ||
|
||
``` | ||
|
||
#### Note on `g:slime_input_pid` | ||
|
||
Used to send text using the external PID rather than Neovim's internal job id. Setting this to a nonzero value (evaluated as `true` in vimscript), as is done here, is recommended because the PID is the number displayed on the status line of a terminal buffer, making it easier to select the desired terminal. This recommended setting is not the default because neovim uses it's internal job id to send text to a terminal; the plugin has a function that translates the PID to the inernal job id. | ||
|
||
##### Side Note | ||
|
||
Recall that when configuring neovim in lua, variables in the global `g:` namespace are set with `vim.g.foo = bar`. | ||
|
||
### vimscript configuration | ||
|
||
```vim | ||
let g:slime_target_send = "slime_neovim#send" | ||
let g:slime_target_config = "slime_neovim#config" | ||
" Use external PID instead of Neovim's internal job id | ||
let g:slime_input_pid = 1 | ||
" Key mappings: | ||
" Send text using gz as operator before motion or text object | ||
nmap gz <Plug>SlimeMotionSend | ||
" Send line of text | ||
nmap gzz <Plug>SlimeLineSend | ||
" Send visual selection | ||
xmap gz <Plug>SlimeRegionSend | ||
``` | ||
|
||
|
||
|
||
## What This Is | ||
Say you are writing code in, for example, python. One way of quickly testing code is to have a terminal where you repeatedly source commands from the terminal. For example if your file is `hello.py` you might have an editor open in one window, and a shell open in another where you input `python hello.py` after you save changes. Another way might be to copy and paste your code to an open python session in the terminal. | ||
|
||
The [vim-slime](https://github.com/jpalardy/vim-slime) plugin allows the user to set keybindings to send text directly from a Vim or Neovim buffer to a running shell or window. Configuration code for each target is included in that repository. | ||
|
||
[vim-slime-ext-plugins](https://github.com/jpalardy/vim-slime-ext-plugins/) in contrast provides infrastructure for sending text to a target, and leaves the community to develop plugins for each target. This plugin extends `vim-slime-ext-plugins` and targets the Neovim terminal. | ||
|
||
## How to Use | ||
|
||
See `:h vim-slime.txt` for default keybindings to send text to a target. I repeat the suggested keymappings from the config section above: | ||
|
||
- `gz[operator/motion]`: send text using an operator or motion. | ||
- In visual mode `gz` can send visually selected text to the target. | ||
- `gzz` sends the current line to the target. | ||
|
||
Of course these are optional and you can do what you want. | ||
|
||
When you use one of these motions, the plugin will try to find the most recently opened terminal and select it as the target. You are prompted with the identification number (`terminal_job_id`) or (`terminal_job_pid` if `g:sline_input_pid` is set to a nonzero value). `terminal_job_pid` is easier to use because that number is displayed on the status line of each terminal buffer. `terminal_job_id` is used by default because that is that Neovim internally uses to send text to the terminals. | ||
|
||
Call the `:SlimeConfig` function from an open buffer to reconfigure the terminal connection of that buffer. | ||
|
||
## Capabilities Summary | ||
|
||
At the risk of repetition, this plugin: | ||
|
||
### Keeps Track of Multiple Terminals | ||
|
||
It does this using the `g:slime_last_channel` variable which is an array of vimscript dictionaries containing the PIDs (external identifier) and job ids (Neovim internal identifier) of open Neovim terminals. If a connected terminal is closed, upon trying to send text again the user is prompted to pick another terminal, with the next-most recently opened terminal selected by default. If no terminals are available, or if there is misconfiguration, a helpful message telling you to open a new terminal is displayed. If you find the messages aren't helpful enogh please leave feedback witha repo maintainer. | ||
|
||
|
||
### Can Use PID or internal job id for configuration | ||
|
||
Under the hood Neovim sends text to a running a terminal using the `terminal_job_id`, which are typically low numbers. Neovim also keeps track of the `terminal_job_pid` which is the system's identifier, and importantly *is displayed on the status line of the terinal buffer*. The default settings are that the user if prompted with a `terminal_job_id` value, because this is what is used by neovim internally to send text to a terminal. However, because it is readily displayed for each running terminal, `terminal_job_pid` is much easier to manually configure, and that is why `vim.g.slime_input_pid=1` is included in the example configuration (the vimscript equivalent of this is `let g:slime_input_pid=1`. | ||
|
||
|
||
## Glossary | ||
|
||
- `PID`: In Linux, PID stands for "Process IDentifier". A PID is a unique number that is automatically assigned to each process when it is created on a Unix-like operating system. A process is an executing (i.e., running) instance of a program. Applies to Macos as well. Represented as `terminal_job_pid` in under the `variables` field of the `getbufinfo()` Neovim vimscript command. | ||
|
||
|
||
- `job id`: the internal identifier that Neovim attaches to a running terminal process. `termnal_job_id` is the corresponding field in the `variables` section of `getbufinfo()`. | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,155 @@ | ||
" Public API for vim-slime to use. | ||
|
||
function! slime_neovim#config(config) | ||
if !exists("a:config['neovim']") | ||
let a:config["neovim"] = {"jobid": get(g:, "slime_last_channel", "")} | ||
end | ||
if exists("g:slime_get_jobid") | ||
let a:config["neovim"]["jobid"] = g:slime_get_jobid() | ||
else | ||
let a:config["neovim"]["jobid"] = input("jobid: ", a:config["neovim"]["jobid"]) | ||
end | ||
return a:config | ||
function! slime_neovim#config(config, ...) | ||
let internal = a:0 > 0 && a:1 == "internal" "testing if we called the function internally, or if slime_neovim_ext_plugins called it | ||
let config_in = a:config | ||
if s:NotExistsLastChannel() | ||
if internal | ||
throw "Terminal not detected." | ||
else | ||
return {} | ||
endif | ||
endif | ||
if s:NotValidConfig(config_in) | ||
let config_in = {} | ||
let config_in["neovim"]= {"jobid": str2nr(get(g:slime_last_channel, -1, "")['jobid'])} | ||
endif | ||
let id_in = 0 | ||
if get(g:, "slime_input_pid", 0) | ||
let pid_in = input("pid: ", str2nr(slime_neovim#translate_id_to_pid(config_in["neovim"]["jobid"]))) | ||
let id_in = slime_neovim#translate_pid_to_id(pid_in) | ||
else | ||
if exists("g:slime_get_jobid") | ||
let id_in = g:slime_get_jobid() | ||
else | ||
let id_in = input("jobid: ", str2nr(config_in["neovim"]["jobid"])) | ||
let id_in = str2nr(id_in) | ||
endif | ||
endif | ||
if id_in == -1 | ||
if internal | ||
throw "No matching job id for the provided pid." | ||
else | ||
return {} | ||
endif | ||
endif | ||
let config_in["neovim"]["jobid"] = id_in | ||
if s:NotValidConfig(config_in) | ||
if internal | ||
throw "Channel id not valid." | ||
else | ||
return {} | ||
endif | ||
endif | ||
return config_in | ||
endfunction | ||
|
||
|
||
function! s:NotExistsConfig() abort | ||
return exists("b:slime_config") | ||
endfunction | ||
|
||
function! s:NotValidConfig(config) abort | ||
"checks if the current configuration refers to an actual running terminal | ||
let not_valid = 1 | ||
|
||
if type(a:config) != v:t_dict || !exists("g:slime_last_channel") | ||
return not_valid | ||
endif | ||
|
||
if has_key(a:config, 'neovim') && has_key(a:config['neovim'], 'jobid') && index( slime_neovim#channel_to_array(g:slime_last_channel), a:config['neovim']['jobid']) >= 0 | ||
let not_valid = 0 | ||
return not_valid | ||
endif | ||
|
||
|
||
|
||
return not_valid | ||
|
||
endfunction | ||
|
||
|
||
function! slime_neovim#SlimeAddChannel() | ||
if !exists("g:slime_last_channel") | ||
let g:slime_last_channel = [{'jobid': &channel, 'pid': b:terminal_job_pid}] | ||
else | ||
call add(g:slime_last_channel, {'jobid': &channel, 'pid': b:terminal_job_pid}) | ||
endif | ||
endfunction | ||
|
||
function slime_neovim#SlimeClearChannel() | ||
if !exists("g:slime_last_channel") | ||
return | ||
elseif len(g:slime_last_channel) == 1 | ||
unlet g:slime_last_channel | ||
else | ||
let bufinfo = getbufinfo() | ||
call filter(bufinfo, {_, val -> has_key(val['variables'], "terminal_job_id") && has_key(val['variables'], "terminal_job_pid")}) | ||
call map(bufinfo, {_, val -> val["variables"]["terminal_job_id"] }) | ||
call filter(g:slime_last_channel, {_, val -> index(bufinfo, val["jobid"]) >= 0}) | ||
endif | ||
endfunction | ||
|
||
function! slime_neovim#send(config, text) | ||
call chansend(str2nr(a:config["neovim"]["jobid"]), split(a:text, "\n", 1)) | ||
let config_in = a:config | ||
let not_valid = s:NotValidConfig(config_in) | ||
|
||
if not_valid | ||
|
||
try | ||
let b:slime_config = slime_neovim#config(config_in, "internal") | ||
let config_in = b:slime_config | ||
|
||
catch /No matching job id for the provided pid/ | ||
echo "No matching job id for the provided pid. Try again. " | ||
return | ||
catch /Terminal not detected./ | ||
echo "Terminal not detected: Open a neovim terminal and try again. " | ||
return | ||
catch /Channel id not valid./ | ||
echo "Channel id not valid: Open a neovim terminal and try again. " | ||
return | ||
finally | ||
endtry | ||
endif | ||
|
||
call chansend(str2nr(config_in["neovim"]["jobid"]), split(a:text, "\n", 1)) | ||
endfunction | ||
|
||
|
||
function! slime_neovim#translate_pid_to_id(pid) | ||
for ch in g:slime_last_channel | ||
if ch['pid'] == a:pid | ||
return ch['jobid'] | ||
endif | ||
endfor | ||
return -1 | ||
endfunction | ||
|
||
function! slime_neovim#translate_id_to_pid(jobid) | ||
for ch in g:slime_last_channel | ||
if ch['jobid'] == a:jobid | ||
return ch['pid'] | ||
endif | ||
endfor | ||
return -1 | ||
endfunction | ||
|
||
function! s:NotExistsLastChannel() abort " | ||
" check if slime_last_channel variable exists | ||
let not_exists = 1 | ||
|
||
if !exists("g:slime_last_channel") || (len(g:slime_last_channel)) < 1 | ||
return not_exists | ||
endif | ||
|
||
|
||
let not_exists = 0 | ||
return not_exists | ||
endfunction | ||
|
||
|
||
|
||
function! slime_neovim#channel_to_array(channel_dict) | ||
return map(copy(a:channel_dict), {_, val -> val["jobid"]}) | ||
endfunction |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
" autocmds to keep of terminal identification numbers whenever a terminal is opened or closed | ||
augroup nvim_slime | ||
autocmd! | ||
autocmd TermOpen * call slime_neovim#SlimeAddChannel() | ||
autocmd TermClose * call slime_neovim#SlimeClearChannel() | ||
augroup END |