snailed
/
taolf
Archived
2
0
Fork 0

fix: open keys - now keys directly open file

This commit is contained in:
Lucas Burns 2022-04-13 18:59:44 -05:00
parent ba62cbc7a2
commit dd04d5f0e9
No known key found for this signature in database
GPG Key ID: C011CBEF6628B679
4 changed files with 269 additions and 268 deletions

View File

@ -103,23 +103,12 @@ require('lf').start("~/.config", { border = "rounded" }) -- opens in `~/.config`
```
### Default Actions
The goal is to allow for these keybindings to be hijacked by `Lf` and make them execute the command
as soon as the keybinding is pressed; however, I am unsure of a way to do this at the moment. If `lf` had a more
programmable API that was similar to `ranger`'s, then something like [`rnvimr`](https://github.com/kevinhwang91/rnvimr)
would be possible, which allows this.
For the moment, these bindings are hijacked on the startup of `Lf`, and when they are pressed, a notification is sent
that your default action has changed. When you go to open a file as you normally would, this command is ran instead
of your `default_action`.
These are various ways to open the wanted file(s). The process works by creating a Neovim mapping to send
`lf` a command to manually open the file. The available commands is anything that can open a file.
### Resizing Window
The configuration option `layout_mapping` is the keymapping that will cycle through the window `views`.
The way it is setup now seems to work by resizing the window; however, the file manager is not center focused on each resize.
If `lf` is used (i.e., `j` or `k`, etc. is pressed to move around) before the resize, the file manager seems to stay focused,
until the last view is reached and the cycle starts back over. Once the cycle restarts the file manager is not center
focused anymore.
If anyone can get this to work, I would very much appreciate it.
The configuration option `layout_mapping` is the key-mapping that will cycle through the window `views`.
Once the last view is reached, the cycle is restarted.
### Neovim 0.7.0
If you do not have the nightly version of `nvim`, then the `mappings` field can be set to false.
@ -134,8 +123,7 @@ The only configurable environment variable is `g:lf_replace_netrw`, which can be
### TODO
- [ ] `:LfToggle` command
- [ ] Find a way for `lf` to hijack keybindings
- This may have to be done by writing to the user's configuration file
- It would involve mapping each `default_actions` key to the `open` command, which closes the file manager
- Once the file manager was closed, these would need to be removed
- [x] Find a way for `lf` to hijack keybindings
- [x] Cycling through various sizes of the terminal (similar to `rnvimr`)
- [ ] Save previous size when terminal is closed, so it is restored on open
- [ ] Set `tmux` title of ToggleTerm

View File

@ -18,43 +18,40 @@ local o = vim.o
--- Initialize the default configuration
local function init()
local lf = require("lf")
vim.validate({ Config = { lf._cfg, "table", true } })
local lf = require("lf")
vim.validate({Config = {lf._cfg, "table", true}})
local opts = {
default_cmd = "lf",
default_action = "edit",
default_actions = {
["<C-t>"] = "tabedit",
["<C-x>"] = "split",
["<C-v>"] = "vsplit",
["<C-o>"] = "tab drop",
},
local opts = {
default_cmd = "lf",
default_action = "edit",
default_actions = {
["<C-t>"] = "tabedit",
["<C-x>"] = "split",
["<C-v>"] = "vsplit",
["<C-o>"] = "tab drop"
},
winblend = 10,
dir = "",
direction = "float",
border = "double",
height = 0.80,
width = 0.85,
mappings = true,
-- Layout configurations
layout_mapping = "<A-u>",
views = {
{width = 0.600, height = 0.600},
{
width = 1.0 * fn.float2nr(fn.round(0.7 * o.columns)) / o.columns,
height = 1.0 * fn.float2nr(fn.round(0.7 * o.lines)) / o.lines
},
{width = 0.800, height = 0.800},
{width = 0.950, height = 0.950}
}
}
winblend = 10,
dir = "",
direction = "float",
border = "double",
height = 0.80,
width = 0.85,
mappings = true,
-- Layout configurations
layout_mapping = "<A-u>",
views = {
{ width = 0.600, height = 0.600 },
{
width = 1.0 * fn.float2nr(fn.round(0.7 * o.columns)) / o.columns,
height = 1.0 * fn.float2nr(fn.round(0.7 * o.lines)) / o.lines,
},
{ width = 0.800, height = 0.800 },
{ width = 0.950, height = 0.950 },
},
}
Config = vim.tbl_deep_extend("keep", lf._cfg or {}, opts)
lf._cfg = nil
Config = vim.tbl_deep_extend("keep", lf._cfg or {}, opts)
lf._cfg = nil
end
init()
@ -64,50 +61,50 @@ local notify = require("lf.utils").notify
---Verify that configuration options that are numbers are numbers or can be converted to numbers
---@param field string `Config` field to check
function Config:__check_number(field)
if type(field) == "string" then
local res = tonumber(field)
if res == nil then
notify(("invalid option for winblend: %s"):format(field))
return self.winblend
else
return res
if type(field) == "string" then
local res = tonumber(field)
if res == nil then
notify(("invalid option for winblend: %s"):format(field))
return self.winblend
else
return res
end
end
end
end
---Set a configuration passed as a function argument (not through `setup`)
---@param cfg table configuration options
---@return Config
function Config:set(cfg)
if cfg and type(cfg) == "table" then
if cfg and type(cfg) == "table" then
cfg.winblend = self:__check_number(cfg.winblend)
cfg.height = self:__check_number(cfg.height)
cfg.width = self:__check_number(cfg.width)
cfg.winblend = self:__check_number(cfg.winblend)
cfg.height = self:__check_number(cfg.height)
cfg.width = self:__check_number(cfg.width)
self = vim.tbl_deep_extend("force", self, cfg or {})
end
self = vim.tbl_deep_extend("force", self, cfg or {})
end
return self
return self
end
---Get the entire configuration if empty, else get the given key
---@param key string option to get
---@return Config
function Config:get(key)
if key then
return self[key]
end
return self
if key then
return self[key]
end
return self
end
return setmetatable(
Config, {
__index = function(this, k)
return this[k]
end,
__newindex = function(this, k, v)
this[k] = v
end,
Config,
{
__index = function(this, k)
return this[k]
end,
__newindex = function(this, k, v)
this[k] = v
end
}
)

View File

@ -7,14 +7,14 @@ local notify = utils.notify
local res, terminal = pcall(require, "toggleterm")
if not res then
notify("toggleterm.nvim must be installed to use this program", "error")
return
notify("toggleterm.nvim must be installed to use this program", "error")
return
end
local res, Path = pcall(require, "plenary.path")
if not res then
notify("plenary must be installed to use this program", "error")
return
notify("plenary must be installed to use this program", "error")
return
end
local api = vim.api
@ -25,42 +25,45 @@ local map = utils.map
---Error for this program
M.error = nil
---Is `Lf` configured?
M.loaded = nil
local Job = require("plenary.job")
local Config = require("lf.config")
local with = require("plenary.context_manager").with
local open = require("plenary.context_manager").open
--- @class Terminal
local Terminal = require("toggleterm.terminal").Terminal
--- @class Lf
--- @field cfg Config Configuration options
--- @field cwd string Current working directory
--- @field term Terminal Toggle terminal
--- @field view_idx number Current index of configuration `views`
--- @field winid number `Terminal` window id
--- @field lf_tmp string File path with the files to open with `lf`
--- @field lastdir_tmp string File path with the last directory `lf` was in
--- @field id_tmp string File path to a file containing `lf`'s id
local Lf = {}
local function setup_term()
terminal.setup(
{
size = function(term)
if term.direction == "horizontal" then
return vim.o.lines * 0.4
elseif term.direction == "vertical" then
return vim.o.columns * 0.5
end
end,
hide_numbers = true,
shade_filetypes = {},
shade_terminals = true,
shading_factor = "1",
start_in_insert = true,
insert_mappings = true,
persist_size = true,
-- open_mapping = [[<c-\>]],
}
)
terminal.setup(
{
size = function(term)
if term.direction == "horizontal" then
return vim.o.lines * 0.4
elseif term.direction == "vertical" then
return vim.o.columns * 0.5
end
end,
hide_numbers = true,
shade_filetypes = {},
shade_terminals = true,
shading_factor = "1",
start_in_insert = true,
insert_mappings = true,
persist_size = true
-- open_mapping = [[<c-\>]],
}
)
end
---Setup a new instance of `Lf`
@ -70,84 +73,78 @@ end
---@param config 'table'
---@return Lf
function Lf:new(config)
self.__index = self
self.__index = self
if config then
self.cfg = Config:set(config):get()
else
self.cfg = Config
end
if config then
self.cfg = Config:set(config):get()
else
self.cfg = Config
end
self.view_idx = 1
self.winid = nil
self.view_idx = 1
self.winid = nil
self.id_tmp = nil
-- TODO: use or del
self.cwd = uv.cwd()
setup_term()
self:__create_term()
setup_term()
self:__create_term()
return self
return self
end
---Create the toggle terminal
function Lf:__create_term()
self.term = Terminal:new(
{
cmd = self.cfg.default_cmd,
dir = self.cfg.dir,
direction = self.cfg.direction,
winblend = self.cfg.winblend,
close_on_exit = true,
float_opts = {
border = self.cfg.border,
width = math.floor(vim.o.columns * self.cfg.width),
height = math.floor(vim.o.lines * self.cfg.height),
winblend = self.cfg.winblend,
highlights = { border = "Normal", background = "Normal" },
},
}
)
self.term =
Terminal:new(
{
cmd = self.cfg.default_cmd,
dir = self.cfg.dir,
direction = self.cfg.direction,
winblend = self.cfg.winblend,
close_on_exit = true,
float_opts = {
border = self.cfg.border,
width = math.floor(vim.o.columns * self.cfg.width),
height = math.floor(vim.o.lines * self.cfg.height),
winblend = self.cfg.winblend,
highlights = {border = "Normal", background = "Normal"}
}
}
)
end
---Start the underlying terminal
---@param path string path where lf starts (reads from config if none, else CWD)
function Lf:start(path)
self:__open_in(path or self.cfg.dir)
if M.error ~= nil then
notify(M.error, "error")
return
end
self:__wrapper()
if self.cfg.mappings then
self.term.on_open = function(term)
self:__on_open(term)
self:__open_in(path or self.cfg.dir)
if M.error ~= nil then
notify(M.error, "error")
return
end
else
self.term.on_open = function(_)
self.winid = api.nvim_get_current_win()
api.nvim_win_set_option(self.winid, "wrap", true)
self:__wrapper()
if self.cfg.mappings then
self.term.on_open = function(term)
self:__on_open(term)
end
else
self.term.on_open = function(_)
self.winid = api.nvim_get_current_win()
api.nvim_win_set_option(self.winid, "wrap", true)
end
end
end
self.term.on_exit = function(term, _, _, _)
self:__callback(term)
end
self.term.on_exit = function(term, _, _, _)
self:__callback(term)
end
-- NOTE: Maybe pcall here?
self.term:toggle()
end
function M.print_active()
p(active)
-- NOTE: Maybe pcall here?
self.term:toggle()
end
---Toggle `Lf` on and off
---@param path string
function Lf:toggle(path)
-- TODO:
-- TODO:
end
---@private
@ -156,35 +153,36 @@ end
---@param path string
---@return Lf
function Lf:__open_in(path)
path = Path:new(
(function(dir)
if dir == "gwd" then
dir = require("lf.utils").git_dir()
end
path =
Path:new(
(function(dir)
if dir == "gwd" then
dir = require("lf.utils").git_dir()
end
if dir ~= "" then
return fn.expand(dir)
else
-- `uv` lib doesn't switch directories
-- Expanding the filename works instead
return fn.expand("%:p")
end
end)(path)
)
if dir ~= "" then
return fn.expand(dir)
else
-- `uv` lib doesn't switch directories
-- Expanding the filename works instead
return fn.expand("%:p")
end
end)(path)
)
if not path:exists() then
M.error = ("directory doesn't exist: %s"):format(path)
return
end
if not path:exists() then
M.error = ("directory doesn't exist: %s"):format(path)
return
end
-- Should be fine, but just checking
if not path:is_dir() then
path = path:parent()
end
-- Should be fine, but just checking
if not path:is_dir() then
path = path:parent()
end
self.term.dir = path:absolute()
self.term.dir = path:absolute()
return self
return self
end
---@private
@ -192,45 +190,72 @@ end
---
---@return Lf
function Lf:__wrapper()
self.lf_tmp = os.tmpname()
self.lastdir_tmp = os.tmpname()
self.lf_tmp = os.tmpname()
self.lastdir_tmp = os.tmpname()
self.id_tmp = os.tmpname()
self.term.cmd = ([[%s -last-dir-path='%s' -selection-path='%s' %s]]):format(
self.term.cmd, self.lastdir_tmp, self.lf_tmp, self.term.dir
)
return self
-- command lf -command '$printf $id > '"$fid"'' -last-dir-path="$tmp" "$@"
self.term.cmd =
([[%s -command='$printf $id > %s' -last-dir-path='%s' -selection-path='%s' %s]]):format(
self.term.cmd,
self.id_tmp,
self.lastdir_tmp,
self.lf_tmp,
self.term.dir
)
return self
end
-- TODO: Figure out a way to open the file with these commands
---On open closure to run in the `Terminal`
---@param term Terminal
function Lf:__on_open(term)
-- api.nvim_command("setlocal filetype=lf")
M.loaded = true
-- api.nvim_command("setlocal filetype=lf")
for key, mapping in pairs(self.cfg.default_actions) do
map(
"t", key, function()
self.cfg.default_action = mapping
notify(("Default action changed: %s"):format(mapping))
end, { noremap = true, buffer = term.bufnr }
)
end
for key, mapping in pairs(self.cfg.default_actions) do
map(
"t",
key,
function()
-- Change default_action for easier reading in the callback
self.cfg.default_action = mapping
-- Set it to a `self` variable in case this is to ever be used again
self.id =
with(
open(self.id_tmp),
function(r)
return r:read()
end
)
-- self.id_tmp = nil
if self.cfg.layout_mapping then
self.winid = api.nvim_get_current_win()
api.nvim_win_set_option(self.winid, "wrap", true)
-- Manually tell `lf` to open the current file
-- since Neovim has hijacked the binding
Job:new(
{
command = "lf",
args = {"-remote", ("send %d open"):format(self.id)}
}
):sync()
end,
{noremap = true, buffer = term.bufnr}
)
end
map(
"t", self.cfg.layout_mapping, function()
api.nvim_win_set_config(
self.winid, M.get_view(self.cfg.views[self.view_idx])
)
self.view_idx = self.view_idx < #self.cfg.views and self.view_idx + 1 or
1
end
)
end
if self.cfg.layout_mapping then
self.winid = api.nvim_get_current_win()
api.nvim_win_set_option(self.winid, "wrap", true)
map(
"t",
self.cfg.layout_mapping,
function()
api.nvim_win_set_config(self.winid, M.get_view(self.cfg.views[self.view_idx]))
self.view_idx = self.view_idx < #self.cfg.views and self.view_idx + 1 or 1
end
)
end
end
---@private
@ -238,67 +263,58 @@ end
---
---@param term Terminal
function Lf:__callback(term)
if (self.cfg.default_action == "cd" or self.cfg.default_action == "lcd") and
uv.fs_stat(self.lastdir_tmp) then
local with = require("plenary.context_manager").with
local open = require("plenary.context_manager").open
-- Since plenary is already being used, this is used instead of `io`
local last_dir = with(
open(self.lastdir_tmp), function(r)
return r:read()
end
)
if last_dir ~= uv.cwd() then
api.nvim_exec(("%s %s"):format(self.cfg.default_action, last_dir), true)
return
end
elseif uv.fs_stat(self.lf_tmp) then
local contents = {}
for line in io.lines(self.lf_tmp) do
contents[#contents + 1] = line
end
if not vim.tbl_isempty(contents) then
term:close()
for _, fname in pairs(contents) do
api.nvim_exec(
("%s %s"):format(
self.cfg.default_action, Path:new(fname):absolute()
), true
if (self.cfg.default_action == "cd" or self.cfg.default_action == "lcd") and uv.fs_stat(self.lastdir_tmp) then
-- Since plenary is already being used, this is used instead of `io`
local last_dir =
with(
open(self.lastdir_tmp),
function(r)
return r:read()
end
)
end
if last_dir ~= uv.cwd() then
api.nvim_exec(("%s %s"):format(self.cfg.default_action, last_dir), true)
return
end
elseif uv.fs_stat(self.lf_tmp) then
local contents = {}
for line in io.lines(self.lf_tmp) do
contents[#contents + 1] = line
end
if not vim.tbl_isempty(contents) then
term:close()
for _, fname in pairs(contents) do
api.nvim_exec(("%s %s"):format(self.cfg.default_action, Path:new(fname):absolute()), true)
end
end
end
end
end
---Get the table that is passed to `api.nvim_win_set_config`
---@param opts table
---@return table
function M.get_view(opts)
opts = opts or {}
local width = opts.width or
math.ceil(math.min(o.columns, math.max(80, o.columns - 20)))
local height = opts.height or
math.ceil(math.min(o.lines, math.max(20, o.lines - 10)))
opts = opts or {}
local width = opts.width or math.ceil(math.min(o.columns, math.max(80, o.columns - 20)))
local height = opts.height or math.ceil(math.min(o.lines, math.max(20, o.lines - 10)))
width = fn.float2nr(width * o.columns)
height = fn.float2nr(fn.round(height * o.lines))
local col = fn.float2nr(fn.round((o.columns - width) / 2))
local row = fn.float2nr(fn.round((o.lines - height) / 2))
width = fn.float2nr(width * o.columns)
height = fn.float2nr(fn.round(height * o.lines))
local col = fn.float2nr(fn.round((o.columns - width) / 2))
local row = fn.float2nr(fn.round((o.lines - height) / 2))
return {
col = col,
row = row,
relative = "editor",
style = "minimal",
width = width,
height = height,
}
return {
col = col,
row = row,
relative = "editor",
style = "minimal",
width = width,
height = height
}
end
M.Lf = Lf

View File

@ -1,4 +1,4 @@
command! -nargs=* -complete=file Lf lua require('lf').start(<f-args>)
command! -nargs=* -complete=file Lfnvim lua require('lf').start(<f-args>)
" TODO: Finish this command
" command! -nargs=* -complete=file LfToggle lua require('lf').setup():toggle(<f-args>)