commit 268995357965a1bebc0bc10a29b6c8be6767fb29 Author: BZ Date: Sun Mar 22 23:19:52 2020 +0100 initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..0536e91 --- /dev/null +++ b/README.md @@ -0,0 +1,53 @@ +AwesomeWM - Vim - Tmux Navigator +================== + +This plugin lets you navigate seamlessly between system windows, vim splits and tmux panes using a consisent set of hotkeys. +Based on [christoomey/vim-tmux-navigator](https://github.com/christoomey/vim-tmux-navigator) and [fogine/vim-i3wm-tmux-navigator](https://github.com/fogine/vim-i3wm-tmux-navigator). + +Installation +------------ + +### AwesomeWM +Clone the repo. +``` +cd ~/.config/awesome +git clone https://github.com/intrntbrn/awesomewm-vim-tmux-navigator +``` +This path is hardcoded in some configuration files. + +Add your preferred navigation keybinds to `rc.lua` (e.g. Mod4+arrow or Mod4+hjkl) + +``` +require("awesomewm-vim-tmux-navigator"){ + up = {"Up", "k"}, + down = {"Down", "j"}, + left = {"Left", "h"}, + right = {"Right", "l"}, + } +``` +Remove conflicting keybinds from your `rc.lua`. + +### Vim + + +```vim +Plug 'intrntbrn/awesomewm-vim-tmux-navigator' +``` + +Remove similar plugins (like `christoomey/vim-tmux-navigator`). + +### Tmux +Add the following to your `tmux.conf`. +```tmux +# Set Terminal titles where possible +set-option -g set-titles on +set-option -g set-titles-string '#S: #W - TMUX' + +# Smart pane switching with awareness of vim splits and system windows +is_vim="ps -o state= -o comm= -t '#{pane_tty}' \ + | grep -iqE '^[^TXZ ]+ +(\\S+\\/)?g?(view|n?vim?x?)(diff)?$'" +bind -n C-Left if-shell "$is_vim" "send-keys C-h" "run-shell 'sh ~/.config/awesome/awesomewm-vim-tmux-navigator/tmux_focus.sh left'" +bind -n C-Down if-shell "$is_vim" "send-keys C-j" "run-shell 'sh ~/.config/awesome/awesomewm-vim-tmux-navigator/tmux_focus.sh down'" +bind -n C-Up if-shell "$is_vim" "send-keys C-k" "run-shell 'sh ~/.config/awesome/awesomewm-vim-tmux-navigator/tmux_focus.sh up'" +bind -n C-Right if-shell "$is_vim" "send-keys C-l" "run-shell 'sh ~/.config/awesome/awesomewm-vim-tmux-navigator/tmux_focus.sh right'" +``` diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..e4de437 --- /dev/null +++ b/init.lua @@ -0,0 +1,126 @@ +local capi = { + root = root, + screen = screen, + client = client, + keygrabber = keygrabber +} + +local awful = require("awful") +local glib = require("lgi").GLib +local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1) +local module = {} + +local keys = { + up = { "k" }, + down = { "j" }, + left = { "h" }, + right = { "l" } +} + +local function new(ks) + keys = ks or keys + local aw = {} + + glib.idle_add(glib.PRIORITY_DEFAULT_IDLE, function() + for k, v in pairs(keys) do + for _, key_name in ipairs(v) do + aw[#aw + 1] = awful.key( + { "Mod4" }, + key_name, + function() + module.focus(k) + end, + { + description = "Change focus to the " .. key_name .. " window", + group = "Navigator" + } + ) + end + end + capi.root.keys(awful.util.table.join(capi.root.keys(), unpack(aw))) + end) + return module +end + +function module.focus(dir) + local c = client.focus + + local client_name = c.name or "" + + if string.find(client_name, "- N?VIM$") then + keygrabber.stop() + root.fake_input("key_release", "Super_L") + root.fake_input("key_release", "Control_L") + root.fake_input("key_press", "Control_L") + + if dir == "left" then + root.fake_input("key_release", "h") + root.fake_input("key_press", "h") + root.fake_input("key_release", "h") + else + if dir == "right" then + root.fake_input("key_release", "l") + root.fake_input("key_press", "l") + root.fake_input("key_release", "l") + else + if dir == "up" then + root.fake_input("key_release", "k") + root.fake_input("key_press", "k") + root.fake_input("key_release", "k") + else + if dir == "down" then + root.fake_input("key_release", "j") + root.fake_input("key_press", "j") + root.fake_input("key_release", "j") + end + end + end + end + root.fake_input("key_release", "Control_L") + root.fake_input("key_press", "Super_L") + return + else + if string.find(client_name, "- TMUX$") then + keygrabber.stop() + root.fake_input("key_release", "Super_L") + root.fake_input("key_press", "Control_L") + + if dir == "left" then + root.fake_input("key_release", "Left") + root.fake_input("key_press", "Left") + root.fake_input("key_release", "Left") + else + if dir == "right" then + root.fake_input("key_release", "Right") + root.fake_input("key_press", "Right") + root.fake_input("key_release", "Right") + else + if dir == "up" then + root.fake_input("key_release", "Up") + root.fake_input("key_press", "Up") + root.fake_input("key_release", "Up") + else + if dir == "down" then + root.fake_input("key_release", "Down") + root.fake_input("key_press", "Down") + root.fake_input("key_release", "Down") + end + end + end + end + root.fake_input("key_release", "Control_L") + root.fake_input("key_press", "Super_L") + return + else + if collision then + collision.focus(dir, c) + else + awful.client.focus.global_bydirection(dir) + end + end + end +end + +return setmetatable(module, { __call = function(_, ...) + return new(...) +end }) diff --git a/plugin/awesomewm_vim_tmux_navigator.vim b/plugin/awesomewm_vim_tmux_navigator.vim new file mode 100644 index 0000000..8bb19f7 --- /dev/null +++ b/plugin/awesomewm_vim_tmux_navigator.vim @@ -0,0 +1,154 @@ +if has('nvim') + set title titlestring=%(%{expand(\"%:~:.:h\")}%)/%t\ -\ NVIM +else + set title titlestring=%(%{expand(\"%:~:.:h\")}%)/%t\ -\ VIM +endif + +" Maps to switch vim splits in the given direction. If there are +" no more windows in that direction, forwards the operation to tmux. +" Additionally, toggles between last active vim splits/tmux panes. + +if exists("g:loaded_tmux_navigator") || &cp || v:version < 700 + finish +endif +let g:loaded_tmux_navigator = 1 + +if !exists("g:tmux_navigator_save_on_switch") + let g:tmux_navigator_save_on_switch = 0 +endif + +function! s:UseTmuxNavigatorMappings() + return !exists("g:tmux_navigator_no_mappings") || !g:tmux_navigator_no_mappings +endfunction + +function! s:InTmuxSession() + return $TMUX != '' +endfunction + +function! s:TmuxPaneCurrentCommand() + echo system("tmux display-message -p '#{pane_current_command}'") +endfunction +command! TmuxPaneCurrentCommand call TmuxPaneCurrentCommand() + +let s:tmux_is_last_pane = 0 +au WinEnter * let s:tmux_is_last_pane = 0 + +" Like `wincmd` but also change tmux panes instead of vim windows when needed. +function! s:TmuxWinCmd(direction) + if s:InTmuxSession() + call s:TmuxAwareNavigate(a:direction) + else + let oldw = winnr() + call s:VimNavigate(a:direction) + + if oldw == winnr() + call s:SystemWindowNavigate(a:direction) + endif + endif +endfunction + +function! s:NeedsVitalityRedraw() + return exists('g:loaded_vitality') && v:version < 704 && !has("patch481") +endfunction + +function! s:TmuxGetActivePaneId() + let cmd = "tmux list-panes -F '#P #{?pane_active,active,}'" + let list = split(system(cmd), '\n') + let paneID = '' + + for pane in list + if match(pane, 'active') != -1 + let paneID = pane + endif + endfor + return paneID +endfunction + +function! s:TmuxAwareNavigate(direction) + + let nr = winnr() + "let tmux_pane = '' + "let tmux_pane = s:TmuxGetActivePaneId() + let tmux_last_pane = (a:direction == 'p' && s:tmux_is_last_pane) + if !tmux_last_pane + call s:VimNavigate(a:direction) + endif + + + " Forward the switch panes command to tmux if: + " a) we're toggling between the last tmux pane; + " b) we tried switching windows in vim but it didn't have effect. + if tmux_last_pane || nr == winnr() + if g:tmux_navigator_save_on_switch + update + endif + + if a:direction == 'p' + finish + endif + + let dir = s:CmdToDir(a:direction) + + let cmd = 'sh ~/.bin/tmux_smart.sh '. dir + silent call system(cmd) + let output= system("tmux run-shell 'tmux rename-window #{pane_current_command}'") + + "if tmux_pane == s:TmuxGetActivePaneId() + "call s:SystemWindowNavigate(a:direction) + "endif + + if s:NeedsVitalityRedraw() + redraw! + endif + let s:tmux_is_last_pane = 1 + else + let s:tmux_is_last_pane = 0 + endif +endfunction + +function! s:VimNavigate(direction) + try + execute 'wincmd ' . a:direction + catch + echohl ErrorMsg | echo 'E11: Invalid in command-line window; executes, CTRL-C quits: wincmd k' | echohl None + endtry +endfunction + +func! s:SystemWindowNavigate(cmd) + if a:cmd == 'p' + finish + endif + + let dir = s:CmdToDir(a:cmd) + + call system('awesome-client ''require("awful.client").focus.global_bydirection("' . dir . '") '' ') + if !has("gui_running") + redraw! + endif +endfunction + +func! s:CmdToDir(cmd) + if a:cmd == 'h' + return "left" + elseif a:cmd == 'j' + return "down" + elseif a:cmd == 'k' + return "up" + elseif a:cmd == 'l' + return "right" + endif +endfunction + +command! TmuxNavigateLeft call TmuxWinCmd('h') +command! TmuxNavigateDown call TmuxWinCmd('j') +command! TmuxNavigateUp call TmuxWinCmd('k') +command! TmuxNavigateRight call TmuxWinCmd('l') +command! TmuxNavigatePrevious call TmuxWinCmd('p') + +if s:UseTmuxNavigatorMappings() + nnoremap :TmuxNavigateLeft + nnoremap :TmuxNavigateDown + nnoremap :TmuxNavigateUp + nnoremap :TmuxNavigateRight + nnoremap :TmuxNavigatePrevious +endif diff --git a/tmux_focus.sh b/tmux_focus.sh new file mode 100755 index 0000000..5eff402 --- /dev/null +++ b/tmux_focus.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +dir=$1 + +wm_focus() { + awesome-client 'require("awful.client").focus.global_bydirection("'"$dir"'")' +} + +case "$dir" in + "left") + if [ "$(tmux display-message -p '#{pane_at_left}')" -ne 1 ]; then tmux select-pane -L; else wm_focus; fi ;; + "right") + if [ "$(tmux display-message -p '#{pane_at_right}')" -ne 1 ]; then tmux select-pane -R; else wm_focus; fi ;; + "up") + if [ "$(tmux display-message -p '#{pane_at_top}')" -ne 1 ]; then tmux select-pane -U; else wm_focus; fi ;; + "down") + if [ "$(tmux display-message -p '#{pane_at_bottom}')" -ne 1 ]; then tmux select-pane -D; else wm_focus; fi ;; +esac