Motion plugin you don't know you've been missing.
telepathy:
thought-transference.
telepath:
a person who uses telepathy.
telepath.nvim
is a Leap extension that allows you to operate on remote textobjects like built-in ones, from treesitter and any others.
telepath.nvim.mp4
- Jump: jump directly to a place you plan to operate from
- Cursor restoration: return to your initial position after performing operations
- Recursion: support for recursive operations
- Search everywhere:
telepath.nvim
uses bidirectional search in all windows by default - Textobjects:
telepath.nvim
doesn't create any textobjects. Instead, you can use all of yours. - Window restoration: you can turn on/off window restoration for a source window, other windows or for all of them.
Why creating a new plugin when we already have leap-spooky.nvim
or flash.nvim
?
First of all, telepath
is for people who use Leap
as their main jump engine instead of flash
, but prefer flash's way to operate on remote textobjects. I personally also prefer flash's way
just because you don't need to create custom mappings on every textobject you plan to operate remotely on. Most importantly, using this way of performing remote actions means that all of the textobjects (including ones from plugins or custom) you have are supported. It also feels more natural and intuitive to type dr
for delete remote
, so your final combination will look like this: dr{search}iw
to delete a word and return back to your initial cursor position.
In short, the main idea is to take the best of 2 worlds, for me that's:
- Leap's jump engine
- Flash's way of operating on remote textobjects
With lazy.nvim:
To use default mappings:
{
'rasulomaroff/telepath.nvim',
dependencies = 'ggandor/leap.nvim',
-- there's no sence in using lazy loading since telepath won't load the main module
-- until you actually use mappings
lazy = false,
config = function()
require('telepath').use_default_mappings()
end
}
To use custom mappings:
{
'rasulomaroff/telepath.nvim',
dependencies = 'ggandor/leap.nvim',
keys = {
-- you can use your own keys
{ 'r', function() require('telepath').remote { restore = true } end, mode = 'o' },
{ 'R', function() require('telepath').remote { restore = true, recursive = true } end, mode = 'o' },
{ 'm', function() require('telepath').remote() end, mode = 'o' },
{ 'M', function() require('telepath').remote { recursive = true } end, mode = 'o' }
}
}
I would recommend reading Leap's documentation as well to have a full understanding of it, but together with telepath
your usage will look like that:
dr{search}{textobject}
d
- this is an operator. In this case it'sdelete
, but you can use any, even custom ones.r
- stands forremote
. You can configure this key as well as you can configure the way it behaves. It must always go after an operator key.{search}
- this is a 2 or 3 key pattern to jump to the specific location you want to operate on. More about it in Leap's documentation.{textobject}
- this is a specific textobject you want to apply your operator on. For examplei(
- inside parentheses. You can use any textobject available in your configuration.
Curly brackets in search
and textobject
are meant to emphasize semantic meaning, you don't need to use them.
There're 6 options you can pass to the remote
method:
restore
- will restore your cursor to the original position after an operation.Default: false
recursive
- will trigger leap mode with the same operator after every operation.Default: false
jumplist
- will set jump points on every jump.Default: true
window_restore
- will restore windows when leaving them or after a remote action. You can pass either a string to specify a restore strategy for all windows, you can passfalse
to disable restoration in all windows, or a table{ source = 'cursor' | 'view' | false, rest = 'cursor' | 'view' | false }
wheresource
means a strategy for a source window andrest
- for all windows except a source one.Default: 'view'
. Possible strategies:
view
- restore a window view together with a cursor statecursor
- restore only a cursor state
remap
- will use your own mapping instead of a neovim's default one. For example, if you have your own mapping fory
key, which uses neovim's defaulty
operator and does some additional things or maybe you use this operator provided by a plugin. Not used by default.hooks
- a table of hooks that will be called during the flow. You can also use user commands. Possible hooks are the same as user commands, but with snake case instead:enter
,leave
,jump_pre
,jump
,window_restore_pre
,window_restore
,restore_pre
,restore
. All rules that are applied to user commands also applied for hooks, meaning that you can cancel restoration or use the data that's passed as a first argument.
Example:
require('telepath').remote {
restore = false,
recursive = false,
jumplist = true,
-- only restore other windows, but not the source one
window_restore = { source = false, rest = 'view' },
remap = {
y = true, -- just put `true` to use a remapped version
d = 'x' -- you can remap it to use another key that should do the same operation, but with additional side-effects
},
hooks = {
restore_pre = function(data)
if true then
data.fn.cancel_restoration()
end
end
}
}
telepath
has a set of user commands that will be executed during the flow.
TelepathEnter
- called as soon as you evoketelepath
.TelepathLeave
- called after all the operations.TelepathJumpPre
- called before jumping. Will be called on each jump when therecursive
option is passed as true. Not called when returning a cursor back to the initial position.TelepathJump
- called right after jumping. Will be called on each jump when therecursive
option is passed as true. Not called when returning a cursor back to the initial position.TelepathWindowRestorePre
- called before a window is restored. You can cancel window restoration here. Not called when restoring a source window.TelepathWindowRestore
- called after a window was restored. Not called when restoring a source window.TelepathRestorePre
- called beforetelepath
restores the cursor to the initial position (only whenrestore=true
in theremote()
method). You can cancel restoration here.TelepathRestore
- called aftertelepath
restores the cursor to the inital position.
Example:
vim.api.nvim_create_autocmd('User', {
pattern = 'TelepathEnter',
callback = function(args)
-- here's the data that's passed to each user command/hook
vim.print(args.data)
end
})
To each user command (as well as to each hook) this data is passed:
{
opts: {
action: {
count: number,
register: string,
operator: string,
regtype: string,
remap?: string
},
restore: boolean,
window_restore: {
source: 'view' | 'cursor' | false,
rest: 'view' | 'cursor' | false
},
recursive: boolean,
jumplist: boolean
},
-- this field is only passed to `TelepathWindowRestorePre` and `TelepathRestorePre` commands
fn: {
-- only passed to `TelepathWindowRestorePre` command
cancel_window_restoration: fun(),
-- only passed to `TelepathRestorePre` command
cancel_restoration: fun()
}
}
All mappings are operator-pending mode only and listed below:
r
- stands forrestore
orreturn
. Operates on remote textobject and return you back to the initial position.m
- stands formagnet
. The same asr
, but won't return you back to the initial position.R
- stands forrestore recursive
. After performing any action, Leap'ssearch
mode will be triggered again with the same operator. You can exit this state by pressing escape and you'll be returned to your initial cursor position.M
- the same asR
, but won't return you to the initial position.
To use default mappings, simply call use_default_mappings
method:
require('telepath').use_default_mappings()
By default, telepath
won't overwrite any existing mappings, if you want it to do so, pass overwrite
boolean field:
require('telepath').use_default_mappings { overwrite = true }
If you only want to use certain default mappings, you can do it by passing keys
field:
-- you can pass the `overwrite` property here as well
require('telepath').use_default_mappings { keys = { 'r', 'm' } }
To create a custom mapping, you need to use the remote
method of the module, where you can pass optional params.
After that, set that function to your preferred key:
vim.keymap.set('o', 'r', function()
require('telepath').remote {
-- options
}
end, { desc = 'Remote action' })