├── lua ├── lf.lua └── floatTerm.lua ├── plugin └── floafLf.vim ├── README.md └── autoload └── floatLf.vim /lua/lf.lua: -------------------------------------------------------------------------------- 1 | local term = require 'floatTerm' 2 | local api = vim.api 3 | local M = {} 4 | 5 | function M.toggleLf(bufpath) 6 | if term.buf_handle == nil or not api.nvim_buf_is_valid(term.buf_handle) then 7 | term.createFloatingWindow() 8 | api.nvim_command("startinsert") 9 | M.jobID = api.nvim_call_function("floatLf#wrap_term_open", {bufpath}) 10 | else 11 | api.nvim_call_function("floatLf#delete_lf_buffer", {}) 12 | end 13 | end 14 | 15 | function M.toggleLf_current_buf() 16 | if term.buf_handle == nil or not api.nvim_buf_is_valid(term.buf_handle) then 17 | local buf_path 18 | if api.nvim_get_var('floatLf_exec') == 'lf' then 19 | buf_path = api.nvim_call_function('expand', {'%:p'}) 20 | else 21 | buf_path = api.nvim_call_function('expand', {'%:p:h'}) 22 | end 23 | term.createFloatingWindow() 24 | api.nvim_command("startinsert") 25 | M.jobID = api.nvim_call_function("floatLf#wrap_term_open_current_buf", {buf_path}) 26 | else 27 | api.nvim_call_function("floatLf#delete_lf_buffer", {}) 28 | end 29 | end 30 | 31 | function M.lfOpenFile() 32 | api.nvim_call_function("floatLf#wrap_open", {M.jobID}) 33 | end 34 | 35 | function M.lfSplitFile() 36 | api.nvim_call_function("floatLf#wrap_split", {M.jobID}) 37 | end 38 | 39 | function M.lfVsplitFile() 40 | api.nvim_call_function("floatLf#wrap_vsplit", {M.jobID}) 41 | end 42 | 43 | function M.lfTabFile() 44 | api.nvim_call_function("floatLf#wrap_tab", {M.jobID}) 45 | end 46 | 47 | 48 | return M 49 | -------------------------------------------------------------------------------- /plugin/floafLf.vim: -------------------------------------------------------------------------------- 1 | if exists('g:loaded_floatLf') | finish | endif 2 | 3 | let s:save_cpo = &cpo 4 | set cpo&vim 5 | 6 | 7 | if ! exists('g:floatLf_autoclose') 8 | " disable autoclose by defualt 9 | let g:floatLf_autoclose = 0 10 | endif 11 | 12 | if ! exists('g:floatLf_border') 13 | " disable border by default 14 | let g:floatLf_border = 0 15 | endif 16 | 17 | if ! exists('g:floatLf_topleft_border') 18 | let g:floatLf_topleft_border = "╔" 19 | endif 20 | 21 | if ! exists('g:floatLf_topright_border') 22 | let g:floatLf_topright_border = "╗" 23 | endif 24 | 25 | if ! exists('g:floatLf_botleft_border') 26 | let g:floatLf_botleft_border = "╚" 27 | endif 28 | 29 | if ! exists('g:floatLf_botright_border') 30 | let g:floatLf_botright_border = "╝" 31 | endif 32 | 33 | if ! exists('g:floatLf_vertical_border') 34 | let g:floatLf_vertical_border = "║" 35 | endif 36 | 37 | if ! exists('g:floatLf_horizontal_border') 38 | let g:floatLf_horizontal_border = "═" 39 | endif 40 | 41 | if ! exists('g:floatLf_lf_close') 42 | " mapping of closing lf 43 | let g:floatLf_lf_close = 'q' 44 | endif 45 | 46 | if ! exists('g:floatLf_lf_open') 47 | " mapping of closing lf 48 | let g:floatLf_lf_open = '' 49 | endif 50 | 51 | if ! exists('g:floatLf_lf_split') 52 | " mapping of open file in split in lf 53 | let g:floatLf_lf_split = '' 54 | endif 55 | 56 | if ! exists('g:floatLf_lf_vsplit') 57 | " mapping of open file in vsplit in lf 58 | let g:floatLf_lf_vsplit = '' 59 | endif 60 | 61 | if ! exists('g:floatLf_lf_tab') 62 | " mapping of open file in tab in lf 63 | let g:floatLf_lf_tab = '' 64 | endif 65 | 66 | if ! exists('g:floatLf_exec') 67 | let g:floatLf_exec = 'lf' 68 | endif 69 | 70 | lua lf = require "lf" 71 | lua term = require "floatTerm" 72 | 73 | command! -nargs=0 LfRefocus lua require'floatTerm'.refocusFloatingWindow() 74 | command! -nargs=0 LfFocusPrev lua require'floatTerm'.focusPrevWindow() 75 | command! -nargs=* LfToggle call luaeval('lf.toggleLf(_A)', expand('')) 76 | command! -nargs=0 LfToggleCurrentBuf lua require'lf'.toggleLf_current_buf() 77 | " command! -nargs=0 LfOpen lua require'lf'.lfOpenFile() 78 | " command! -nargs=0 LfSplit lua require'lf'.lfSplitFile() 79 | " command! -nargs=0 LfVsplit lua require'lf'.lfVsplitFile() 80 | " command! -nargs=0 LfTab lua require'lf'.lfTabFile() 81 | 82 | 83 | let &cpo = s:save_cpo 84 | unlet s:save_cpo 85 | 86 | let g:loaded_floatLf = 1 87 | 88 | 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # floatLf-nvim 2 | 3 | A simple neovim plugin that make you use the amazing file manager [lf](https://github.com/gokcehan/lf) in neovim as you expected. 4 | 5 | - Open lf in neovim floating window with all your setting available. 6 | - Open files without nested in split or tab. 7 | 8 | ## Demo 9 | 10 | ![floatLf](https://user-images.githubusercontent.com/35623968/75210925-70bb9b00-57bd-11ea-82b9-b5043bb4c3df.gif) 11 | 12 | 13 | ## Why lf? 14 | 15 | - lf is a very fast and light-weighted file manager. 16 | - Use lf in neovim can be better than using other file explorer plugins because it simply has all your bindings and also can integrate with other programs very well. 17 | - lf can accept shell command, which makes it very easy to combine with neovim-remote to prevent opening file in nested session in neovim. 18 | 19 | ## Prerequisite 20 | 21 | - Only support neovim(because the main control part of this plugin is written in lua) 22 | - requires [neovim-remote](https://github.com/mhinz/neovim-remote) and make sure you can find the `nvr` executable. Install it easily with: 23 | ```bash 24 | pip3 install neovim-remote 25 | ``` 26 | 27 | ## Install 28 | 29 | - Install with any plugin manager by using the path on GitHub. 30 | ```Plug 'haorenW1025/floatLf-nvim'``` 31 | 32 | ## Usage 33 | 34 | - Use `:LfToggle` to open lf window in floating window. 35 | - Use `:LfToggleCurrentBuf` if you want to open lf and focus to the current buffer. 36 | - Just use your lf binding to go around and manipulate files. 37 | - ``, ``, `` and `` in lf can open file in current window, split, vertical split and tab. 38 | - Press `q` to quit out of lf window. 39 | 40 | ## Configuration 41 | 42 | - By default, lf will still be open if you open any files, you can change that by 43 | ``` 44 | let g:floatLf_autoclose = 1 45 | ``` 46 | 47 | - You can change the key mapping in lf by 48 | ``` 49 | let g:floatLf_lf_close = 'q' 50 | let g:floatLf_lf_open = '' 51 | let g:floatLf_lf_split = '' 52 | let g:floatLf_lf_vsplit = '' 53 | let g:floatLf_lf_tab = '' 54 | ``` 55 | - Note that these mapping will only be mapped locally to lf buffer so it won't affect other terminal buffer. 56 | 57 | - The floating window has no border by default, enable it by 58 | ``` 59 | let g:floatLf_border = 1 60 | ``` 61 | 62 | - Modify your border type by 63 | ``` 64 | let g:floatLf_topleft_border = "╔" 65 | let g:floatLf_topright_border = "╗" 66 | let g:floatLf_botleft_border = "╚" 67 | let g:floatLf_botright_border = "╝" 68 | let g:floatLf_vertical_border = "║" 69 | let g:floatLf_horizontal_border = "═" 70 | ``` 71 | 72 | ## Todo 73 | - Support terminal size option. 74 | - Maybe support other file manager? 75 | -------------------------------------------------------------------------------- /autoload/floatLf.vim: -------------------------------------------------------------------------------- 1 | function! floatLf#on_exit(job_id, code, event) dict 2 | if a:code == 0 3 | call floatLf#delete_lf_buffer() 4 | endif 5 | endfunction 6 | 7 | function! floatLf#delete_lf_buffer() 8 | for n in nvim_list_bufs() 9 | if ! buflisted(n) 10 | let name = bufname(n) 11 | if name == '[Scratch]' || 12 | \ matchend(name, ":vifm") 13 | call CleanupBuffer(n) 14 | endif 15 | endif 16 | endfor 17 | endfunction 18 | 19 | function! CleanupBuffer(buf) 20 | if bufexists(a:buf) 21 | silent execute 'bwipeout! '.a:buf 22 | endif 23 | endfunction 24 | 25 | function! floatLf#wrap_term_open(path) 26 | let jobID = termopen(g:floatLf_exec.' '.(a:path), {'on_exit': function('floatLf#on_exit')}) 27 | return jobID 28 | endfunction 29 | 30 | function! floatLf#wrap_term_open_current_buf(path) 31 | let jobID = termopen(g:floatLf_exec.' '.(a:path), {'on_exit': function('floatLf#on_exit')}) 32 | return jobID 33 | endfunction 34 | 35 | function! floatLf#get_callback() 36 | if g:floatLf_autoclose == 1 37 | return "-c 'call floatLf#delete_lf_buffer()'" 38 | endif 39 | return " -c 'LfRefocus' " 40 | endfunction 41 | 42 | " HACK use two to prevent unexpected behavior 43 | function! floatLf#wrap_open(job_id) 44 | let callback = floatLf#get_callback() 45 | if g:floatLf_exec == "vifm" 46 | call chansend(a:job_id, ":!nvr --servername $(nvr --serverlist) -cc 'LfFocusPrev' " . callback . " %c \") 47 | else 48 | call chansend(a:job_id, "%%nvr --servername $(nvr --serverlist) -cc 'LfFocusPrev' " . callback . " $f \") 49 | endif 50 | endfunction 51 | 52 | function! floatLf#wrap_split(job_id) 53 | let callback = floatLf#get_callback() 54 | if g:floatLf_exec == "vifm" 55 | call chansend(a:job_id, ":!nvr --servername $(nvr --serverlist) -cc 'LfFocusPrev' " . callback . " -o %c \") 56 | else 57 | call chansend(a:job_id, "%%nvr --servername $(nvr --serverlist) -cc 'LfFocusPrev' " . callback . " -o $f \") 58 | endif 59 | endfunction 60 | 61 | function! floatLf#wrap_vsplit(job_id) 62 | let callback = floatLf#get_callback() 63 | if g:floatLf_exec == "vifm" 64 | call chansend(a:job_id, ":!nvr --servername $(nvr --serverlist) -cc 'LfFocusPrev' " . callback . " -O %c \") 65 | else 66 | call chansend(a:job_id, "%%nvr --servername $(nvr --serverlist) -cc 'LfFocusPrev' " . callback . " -O $f \") 67 | endif 68 | endfunction 69 | 70 | function! floatLf#wrap_tab(job_id) 71 | let callback = floatLf#get_callback() 72 | if g:floatLf_exec == "vifm" 73 | call chansend(a:job_id, ":!nvr --servername $(nvr --serverlist) -cc 'LfFocusPrev' " . callback . " -p %c \") 74 | else 75 | call chansend(a:job_id, "%%nvr --servername $(nvr --serverlist) -cc 'LfFocusPrev' " . callback . " -p $f \") 76 | endif 77 | endfunction 78 | -------------------------------------------------------------------------------- /lua/floatTerm.lua: -------------------------------------------------------------------------------- 1 | local api = vim.api 2 | local M = {} 3 | 4 | function M.createFloatingWindow() 5 | M.win_prev = api.nvim_tabpage_get_win(0) 6 | local column = api.nvim_get_option("columns") 7 | local line = api.nvim_get_option("lines") 8 | 9 | -- TODO make this user adjustable 10 | local width = column * 0.9 11 | local height = line * 0.9 12 | local opts = { 13 | relative = "editor", 14 | width = math.ceil(width), 15 | height = math.ceil(height), 16 | row = math.ceil((line - height) / 2)-1 , 17 | col = math.ceil((column - width) / 2), 18 | style = "minimal" 19 | } 20 | 21 | if api.nvim_get_var("floatLf_border") == 1 then 22 | local top_left = api.nvim_get_var("floatLf_topleft_border") 23 | local top_right = api.nvim_get_var("floatLf_topright_border") 24 | local horizontal = api.nvim_get_var("floatLf_horizontal_border") 25 | local vertical = api.nvim_get_var("floatLf_vertical_border") 26 | local bot_left = api.nvim_get_var("floatLf_botleft_border") 27 | local bot_right = api.nvim_get_var("floatLf_botright_border") 28 | 29 | local top_border = top_left..string.rep(horizontal, width - 2)..top_right 30 | local mid_border = vertical..string.rep(" ", width - 2)..vertical 31 | local bot_border = bot_left..string.rep(horizontal, width - 2)..bot_right 32 | local lines = {top_border} 33 | for _=1, math.ceil(height)-2, 1 do 34 | table.insert(lines, mid_border) 35 | end 36 | table.insert(lines, bot_border) 37 | M.buf_handle = api.nvim_create_buf(false, true) 38 | api.nvim_buf_set_lines(M.buf_handle, 0, -1, true, lines) 39 | M.win_handle = api.nvim_open_win(M.buf_handle, true, opts) 40 | api.nvim_win_set_option(M.win_handle, 'winhl', 'Normal:Floating') 41 | end 42 | 43 | opts['width'] = opts['width'] - 4 44 | opts['height'] = opts['height'] -2 45 | opts['row'] = opts['row'] + 1 46 | opts['col'] = opts['col'] + 2 47 | M.buf_handle = api.nvim_create_buf(false, true) 48 | M.setMapping() 49 | M.win_handle = api.nvim_open_win(M.buf_handle, true, opts) 50 | api.nvim_win_set_option(M.win_handle, 'winhl', 'Normal:Floating') 51 | end 52 | 53 | function M.setMapping() 54 | -- TODO make this user adjustable 55 | local close = api.nvim_get_var("floatLf_lf_close") 56 | local open = api.nvim_get_var("floatLf_lf_open") 57 | local split = api.nvim_get_var("floatLf_lf_split") 58 | local vsplit = api.nvim_get_var("floatLf_lf_vsplit") 59 | local tab = api.nvim_get_var("floatLf_lf_tab") 60 | local opts = {silent = true} 61 | api.nvim_buf_set_keymap(M.buf_handle, 't', close, 'call floatLf#delete_lf_buffer()', opts) 62 | api.nvim_buf_set_keymap(M.buf_handle, 't', open, 'lua lf.lfOpenFile()', opts) 63 | api.nvim_buf_set_keymap(M.buf_handle, 't', split, 'lua lf.lfSplitFile()', opts) 64 | api.nvim_buf_set_keymap(M.buf_handle, 't', vsplit, 'lua lf.lfVsplitFile()', opts) 65 | api.nvim_buf_set_keymap(M.buf_handle, 't', tab, 'lua lf.lfTabFile()', opts) 66 | end 67 | 68 | function M.focusPrevWindow() 69 | api.nvim_set_current_win(M.win_prev) 70 | end 71 | 72 | function M.refocusFloatingWindow() 73 | api.nvim_set_current_win(M.win_handle) 74 | api.nvim_command("startinsert") 75 | end 76 | 77 | M.setMapping() 78 | 79 | return M 80 | --------------------------------------------------------------------------------