initial commit
This commit is contained in:
@@ -0,0 +1,92 @@
|
|||||||
|
# zterm-navigator
|
||||||
|
|
||||||
|
Seamless navigation between Neovim windows and ZTerm terminal panes.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Navigate between Neovim splits using `Alt+Arrow` keys
|
||||||
|
- When there's no Neovim window in that direction, automatically navigate to the next ZTerm pane
|
||||||
|
- Works bidirectionally - you can navigate from shell to Neovim and back
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- [ZTerm](https://github.com/yourusername/zterm) terminal emulator
|
||||||
|
- Neovim 0.7+
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Using lazy.nvim
|
||||||
|
|
||||||
|
```lua
|
||||||
|
{
|
||||||
|
"yourusername/zterm-navigator",
|
||||||
|
config = function()
|
||||||
|
require("zterm-navigator").setup()
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using packer.nvim
|
||||||
|
|
||||||
|
```lua
|
||||||
|
use {
|
||||||
|
"yourusername/zterm-navigator",
|
||||||
|
config = function()
|
||||||
|
require("zterm-navigator").setup()
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
```lua
|
||||||
|
require("zterm-navigator").setup({
|
||||||
|
-- Default keybindings (set to false to disable)
|
||||||
|
left = "<A-Left>",
|
||||||
|
right = "<A-Right>",
|
||||||
|
up = "<A-Up>",
|
||||||
|
down = "<A-Down>",
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## ZTerm Configuration
|
||||||
|
|
||||||
|
Make sure your ZTerm config (`~/.config/zterm/config.json`) has matching keybindings:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"keybindings": {
|
||||||
|
"focus_pane_up": "alt+up",
|
||||||
|
"focus_pane_down": "alt+down",
|
||||||
|
"focus_pane_left": "alt+left",
|
||||||
|
"focus_pane_right": "alt+right"
|
||||||
|
},
|
||||||
|
"pass_keys_to_programs": ["nvim", "vim"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
1. When you press `Alt+Arrow` in Neovim:
|
||||||
|
- The plugin checks if there's a Neovim window in that direction
|
||||||
|
- If yes, it navigates to that window using `wincmd`
|
||||||
|
- If no, it sends an OSC 51 escape sequence to ZTerm
|
||||||
|
|
||||||
|
2. ZTerm receives the OSC sequence and navigates to the neighboring pane
|
||||||
|
|
||||||
|
3. When you press `Alt+Arrow` in a shell (non-Neovim):
|
||||||
|
- ZTerm checks `pass_keys_to_programs` - since it's not Neovim, ZTerm handles navigation directly
|
||||||
|
|
||||||
|
## Protocol
|
||||||
|
|
||||||
|
The plugin communicates with ZTerm using OSC (Operating System Command) sequences:
|
||||||
|
|
||||||
|
```
|
||||||
|
OSC 51 ; navigate ; <direction> BEL
|
||||||
|
```
|
||||||
|
|
||||||
|
Where `<direction>` is one of: `up`, `down`, `left`, `right`.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
-- zterm-navigator: Seamless navigation between Neovim windows and ZTerm panes
|
||||||
|
--
|
||||||
|
-- When you press the navigation key (e.g., Alt+Arrow):
|
||||||
|
-- - If there's a Neovim window in that direction, navigate to it
|
||||||
|
-- - If not, send an OSC sequence to ZTerm to navigate to the next pane
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
-- Default configuration
|
||||||
|
M.config = {
|
||||||
|
-- Key mappings for navigation
|
||||||
|
-- Set to false to disable a direction
|
||||||
|
left = "<A-Left>",
|
||||||
|
right = "<A-Right>",
|
||||||
|
up = "<A-Up>",
|
||||||
|
down = "<A-Down>",
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Send OSC 51 command to ZTerm for pane navigation
|
||||||
|
local function zterm_navigate(direction)
|
||||||
|
-- OSC 51;navigate;<direction> ST
|
||||||
|
-- Using BEL (\007) as string terminator for better compatibility
|
||||||
|
local osc = string.format("\027]51;navigate;%s\007", direction)
|
||||||
|
io.write(osc)
|
||||||
|
io.flush()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get the window number in a given direction, or 0 if none exists
|
||||||
|
local function get_window_in_direction(direction)
|
||||||
|
local dir_char = ({
|
||||||
|
left = "h",
|
||||||
|
right = "l",
|
||||||
|
up = "k",
|
||||||
|
down = "j",
|
||||||
|
})[direction]
|
||||||
|
|
||||||
|
if not dir_char then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get current window number
|
||||||
|
local current_winnr = vim.fn.winnr()
|
||||||
|
-- Get window number in the specified direction
|
||||||
|
local target_winnr = vim.fn.winnr(dir_char)
|
||||||
|
|
||||||
|
-- If winnr() returns the same window, there's no window in that direction
|
||||||
|
if target_winnr == current_winnr then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
return target_winnr
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Navigate in the given direction
|
||||||
|
-- First tries to move within Neovim, falls back to ZTerm pane navigation
|
||||||
|
local function navigate(direction)
|
||||||
|
local target_win = get_window_in_direction(direction)
|
||||||
|
|
||||||
|
if target_win ~= 0 then
|
||||||
|
-- There's a Neovim window in that direction, navigate to it
|
||||||
|
vim.cmd("wincmd " .. ({
|
||||||
|
left = "h",
|
||||||
|
right = "l",
|
||||||
|
up = "k",
|
||||||
|
down = "j",
|
||||||
|
})[direction])
|
||||||
|
else
|
||||||
|
-- No Neovim window, ask ZTerm to navigate
|
||||||
|
zterm_navigate(direction)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Public navigation functions
|
||||||
|
function M.navigate_left()
|
||||||
|
navigate("left")
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.navigate_right()
|
||||||
|
navigate("right")
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.navigate_up()
|
||||||
|
navigate("up")
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.navigate_down()
|
||||||
|
navigate("down")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Setup function to configure keymaps
|
||||||
|
function M.setup(opts)
|
||||||
|
-- Merge user options with defaults
|
||||||
|
opts = opts or {}
|
||||||
|
M.config = vim.tbl_deep_extend("force", M.config, opts)
|
||||||
|
|
||||||
|
-- Set up keymaps
|
||||||
|
local keymap_opts = { noremap = true, silent = true }
|
||||||
|
|
||||||
|
if M.config.left then
|
||||||
|
vim.keymap.set({"n", "t"}, M.config.left, M.navigate_left,
|
||||||
|
vim.tbl_extend("force", keymap_opts, { desc = "Navigate left (window/pane)" }))
|
||||||
|
end
|
||||||
|
|
||||||
|
if M.config.right then
|
||||||
|
vim.keymap.set({"n", "t"}, M.config.right, M.navigate_right,
|
||||||
|
vim.tbl_extend("force", keymap_opts, { desc = "Navigate right (window/pane)" }))
|
||||||
|
end
|
||||||
|
|
||||||
|
if M.config.up then
|
||||||
|
vim.keymap.set({"n", "t"}, M.config.up, M.navigate_up,
|
||||||
|
vim.tbl_extend("force", keymap_opts, { desc = "Navigate up (window/pane)" }))
|
||||||
|
end
|
||||||
|
|
||||||
|
if M.config.down then
|
||||||
|
vim.keymap.set({"n", "t"}, M.config.down, M.navigate_down,
|
||||||
|
vim.tbl_extend("force", keymap_opts, { desc = "Navigate down (window/pane)" }))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
Reference in New Issue
Block a user