initial commit
This commit is contained in:
parent
69ab1efcff
commit
5bc3a78955
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"diagnostics.globals": [
|
||||
"vim"
|
||||
],
|
||||
"diagnostics.disable": [
|
||||
"duplicate-doc-field",
|
||||
"duplicate-set-field"
|
||||
]
|
||||
}
|
83
lua/lf.lua
83
lua/lf.lua
|
@ -1,85 +1,8 @@
|
|||
local M = {}
|
||||
|
||||
local Config = require("lf.config")
|
||||
local utils = require("lf.utils")
|
||||
|
||||
local uv = vim.loop
|
||||
local api = vim.api
|
||||
local fn = vim.fn
|
||||
|
||||
---Check Neovim version before setting mappings
|
||||
---@param cfg Lf.Config
|
||||
local function has_feature(cfg)
|
||||
if not vim.keymap or not vim.keymap.set then
|
||||
utils.err("lf.nvim mappings require Neovim 0.7.0 or higher", true)
|
||||
cfg.mappings = false
|
||||
end
|
||||
end
|
||||
|
||||
---Make `Lf` become the file manager that opens whenever a directory buffer is loaded
|
||||
---@param bufnr integer
|
||||
---@return boolean
|
||||
local function become_dir_fman(bufnr)
|
||||
local bufname = api.nvim_buf_get_name(bufnr)
|
||||
if bufname == "" then
|
||||
return false
|
||||
end
|
||||
local stat = uv.fs_stat(bufname)
|
||||
if type(stat) ~= "table" or (type(stat) == "table" and stat.type ~= "directory") then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function setup_autocmds()
|
||||
api.nvim_create_user_command("Lf", function(tbl)
|
||||
require("lf").start(tbl.args)
|
||||
end, {nargs = "*", complete = "file"})
|
||||
|
||||
if Config.data.default_file_manager or vim.g.lf_netrw then
|
||||
local group = api.nvim_create_augroup("Lf_ReplaceNetrw", {clear = true})
|
||||
|
||||
if vim.g.loaded_netrwPlugin ~= 1 and not Config.data.disable_netrw_warning then
|
||||
api.nvim_create_autocmd("FileType", {
|
||||
desc = "Display message about Lf not being default file manager",
|
||||
group = group,
|
||||
pattern = "netrw",
|
||||
once = true,
|
||||
callback = function()
|
||||
utils.warn([[Lf cannot be the default file manager with netrw enabled.]] ..
|
||||
[[Put `vim.g.loaded_netrwPlugin` in your configuration.]])
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
api.nvim_create_autocmd("VimEnter", {
|
||||
desc = "Override the default file manager (i.e., netrw)",
|
||||
group = group,
|
||||
pattern = "*",
|
||||
nested = true,
|
||||
callback = function(a)
|
||||
if fn.exists("#FileExplorer") then
|
||||
api.nvim_create_augroup("FileExplorer", {clear = true})
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
api.nvim_create_autocmd("BufEnter", {
|
||||
desc = "After overriding default file manager, open Lf",
|
||||
group = group,
|
||||
pattern = "*",
|
||||
once = true,
|
||||
callback = function(a)
|
||||
if become_dir_fman(a.buf) then
|
||||
vim.defer_fn(function()
|
||||
require("lf").start(a.file)
|
||||
end, 1)
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
---Setup the Lf plugin
|
||||
---@param cfg Lf.Config
|
||||
|
@ -89,10 +12,8 @@ function M.setup(cfg)
|
|||
end
|
||||
|
||||
cfg = cfg or {}
|
||||
has_feature(cfg)
|
||||
M.__conf = cfg
|
||||
Config.init()
|
||||
setup_autocmds()
|
||||
end
|
||||
|
||||
---Start the file manager
|
||||
|
@ -115,11 +36,11 @@ function M.start(path, cfg)
|
|||
else
|
||||
-- Strict nil checks are needed because `nil` can be given as an argument
|
||||
if path ~= nil and path_t ~= "string" then
|
||||
utils.err("first argument must be a string")
|
||||
api.nvim_err_writeln("first argument must be a string")
|
||||
return
|
||||
end
|
||||
if cfg ~= nil and type(cfg) ~= "table" then
|
||||
utils.err("second argument must be a table")
|
||||
api.nvim_err_writeln("second argument must be a table")
|
||||
return
|
||||
end
|
||||
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
local fn = vim.fn
|
||||
local o = vim.o
|
||||
|
||||
local utils = require("lf.utils")
|
||||
local api = vim.api
|
||||
|
||||
---@class Lf.Container
|
||||
---@field data Lf.Config
|
||||
|
@ -16,82 +13,23 @@ local Config = {
|
|||
local default = {
|
||||
default_cmd = "lf",
|
||||
default_action = "drop",
|
||||
default_actions = {
|
||||
["<C-t>"] = "tabedit",
|
||||
["<C-x>"] = "split",
|
||||
["<C-v>"] = "vsplit",
|
||||
["<C-o>"] = "tab drop",
|
||||
["<C-e>"] = "edit",
|
||||
["<C-g>"] = "argedit",
|
||||
},
|
||||
winblend = 10,
|
||||
dir = "",
|
||||
direction = "float",
|
||||
border = "double",
|
||||
height = fn.float2nr(fn.round(0.75 * o.lines)),
|
||||
width = fn.float2nr(fn.round(0.75 * o.columns)),
|
||||
dir = "gwd",
|
||||
escape_quit = false,
|
||||
focus_on_open = true,
|
||||
mappings = true,
|
||||
tmux = false,
|
||||
default_file_manager = false,
|
||||
disable_netrw_warning = true,
|
||||
highlights = {
|
||||
Normal = {link = "Normal"},
|
||||
FloatBorder = {link = "FloatBorder"},
|
||||
},
|
||||
count = nil,
|
||||
env = {
|
||||
clear = false,
|
||||
vars = {}, -- NOTE: this doesn't work for now
|
||||
},
|
||||
-- Layout configurations
|
||||
layout_mapping = "<A-u>",
|
||||
views = {
|
||||
{width = 0.800, height = 0.800},
|
||||
{width = 0.600, height = 0.600},
|
||||
{width = 0.950, height = 0.950},
|
||||
{width = 0.500, height = 0.500, col = 0, row = 0},
|
||||
{width = 0.500, height = 0.500, col = 0, row = 0.5},
|
||||
{width = 0.500, height = 0.500, col = 0.5, row = 0},
|
||||
{width = 0.500, height = 0.500, col = 0.5, row = 0.5},
|
||||
},
|
||||
}
|
||||
|
||||
---Validate configuration values
|
||||
---@param cfg Lf.Config existing configuration options
|
||||
---@return Lf.Config
|
||||
local function validate(cfg)
|
||||
vim.validate({
|
||||
default_cmd = {cfg.default_cmd, "s", false},
|
||||
default_action = {cfg.default_action, "s", false},
|
||||
default_actions = {cfg.default_actions, "t", false},
|
||||
winblend = {cfg.winblend, {"n", "s"}, false},
|
||||
dir = {cfg.dir, "s", false},
|
||||
direction = {cfg.direction, "s", false},
|
||||
border = {cfg.border, {"s", "t"}, false},
|
||||
height = {cfg.height, {"n", "s"}, false},
|
||||
width = {cfg.width, {"n", "s"}, false},
|
||||
escape_quit = {cfg.escape_quit, "b", false},
|
||||
focus_on_open = {cfg.focus_on_open, "b", false},
|
||||
mappings = {cfg.mappings, "b", false},
|
||||
tmux = {cfg.tmux, "b", false},
|
||||
default_file_manager = {cfg.default_file_manager, "b", false},
|
||||
disable_netrw_warning = {cfg.disable_netrw_warning, "b", false},
|
||||
highlights = {cfg.highlights, "t", false},
|
||||
count = {cfg.count, "n", true},
|
||||
env = {cfg.env, "t", false},
|
||||
env_vars = {cfg.env.vars, "t", false},
|
||||
env_clear = {cfg.env.clear, "b", false},
|
||||
-- Layout configurations
|
||||
layout_mapping = {cfg.layout_mapping, "s", false},
|
||||
views = {cfg.views, "t", false},
|
||||
})
|
||||
|
||||
cfg.winblend = tonumber(cfg.winblend) --[[@as number]]
|
||||
cfg.height = tonumber(cfg.height) --[[@as number]]
|
||||
cfg.width = tonumber(cfg.width) --[[@as number]]
|
||||
return cfg
|
||||
end
|
||||
|
||||
---Set a configuration passed as a function argument (not through `setup`)
|
||||
|
@ -100,7 +38,7 @@ end
|
|||
function Config:override(cfg)
|
||||
if type(cfg) == "table" then
|
||||
self.data = vim.tbl_deep_extend("force", self.data, cfg) --[[@as Lf.Config]]
|
||||
self.data = validate(self.data)
|
||||
validate(self.data)
|
||||
end
|
||||
return self.data
|
||||
end
|
||||
|
@ -124,7 +62,7 @@ function Config.init()
|
|||
local lf = require("lf")
|
||||
-- Keep options from the `lf.setup()` call
|
||||
Config.data = vim.tbl_deep_extend("keep", lf.__conf or {}, default) --[[@as Lf.Config]]
|
||||
Config.data = validate(Config.data)
|
||||
validate(Config.data)
|
||||
lf.__conf = nil
|
||||
Config.__loaded = true
|
||||
end
|
||||
|
@ -133,55 +71,17 @@ return setmetatable(Config, {
|
|||
__index = function(self, key)
|
||||
return rawget(self, key)
|
||||
end,
|
||||
__newindex = function(_self, key, val)
|
||||
utils.warn(("do not set invalid config values: %s => %s"):format(key, val))
|
||||
---@diagnostic disable-next-line: unused-local
|
||||
__newindex = function(self, key, val)
|
||||
api.nvim_err_writeln(("do not set invalid config values: %s => %s"):format(key, val))
|
||||
end,
|
||||
})
|
||||
|
||||
---@alias Lf.border.generic {[1]:string,[2]:string,[3]:string,[4]:string,[5]:string,[6]:string,[7]:string,[8]:string}
|
||||
---@alias Lf.border "'none'"|"'single'"|"'double'"|"'rounded'"|"'solid'"|"'shadow'"|Lf.border.generic
|
||||
---@alias Lf.direction "'vertical'"|"'horizontal'"|"'tab'"|"'float'"
|
||||
---@alias Lf.directory "'gwd'"|"''"|nil|string
|
||||
|
||||
---@class Lf.views
|
||||
---@field width number
|
||||
---@field height number
|
||||
---@field relative? "'editor'"|"'win'"|"'cursor'"|"'mouse'"
|
||||
---@field win? integer For `relative='win'`
|
||||
---@field anchor? "'NW'"|"'NE'"|"'SW'"|"'SE'" Which corner of float to place `(row, col)`
|
||||
---@field bufpos? {row: number, col: number}
|
||||
---@field row? integer|float
|
||||
---@field col? integer|float
|
||||
---@field focusable? boolean
|
||||
---@field zindex? number
|
||||
---@field style? "'minimal'"
|
||||
---@field border? Lf.border Border kind
|
||||
---@field title? string|{[1]: string, [2]: string}[] Can be a string or an array of tuples
|
||||
---@field title_pos? "'left'"|"'center'"|"'right'"
|
||||
---@field noautocmd? boolean
|
||||
|
||||
---@class Lf.env
|
||||
---@field clear boolean Should environment variables be cleared?
|
||||
---@field vars table<string, string|number> Hash of variables to be set on startup
|
||||
|
||||
---@class Lf.Config
|
||||
---@field default_cmd? string Default `lf` command
|
||||
---@field default_action? string Default action when `Lf` opens a file
|
||||
---@field default_actions? table<string, string> Default action keybindings
|
||||
---@field winblend? number Psuedotransparency level
|
||||
---@field dir? Lf.directory Directory where `lf` starts ('gwd' is git-working-directory, ""/nil is CWD)
|
||||
---@field direction? Lf.direction Window layout
|
||||
---@field border? Lf.border Border kind
|
||||
---@field width? integer Width of the *floating* window
|
||||
---@field height? integer Height of the *floating* window
|
||||
---@field escape_quit? boolean Whether escape should be mapped to quit
|
||||
---@field focus_on_open? boolean Whether Lf should open focused on current file
|
||||
---@field mappings? boolean Whether terminal buffer mappings should be set
|
||||
---@field tmux? boolean Whether `tmux` statusline should be changed by this plugin
|
||||
---@field default_file_manager? boolean Make lf the default file manager for neovim
|
||||
---@field disable_netrw_warning? boolean Don't display a message when opening a directory with `default_file_manager` as true
|
||||
---@field highlights? table<string, table<string, string>> Highlight table passed to `toggleterm`
|
||||
---@field layout_mapping? string Keybinding to rotate through the window layouts
|
||||
---@field views? Lf.views[] Table of layouts to be applied to `nvim_win_set_config`
|
||||
---@field env? Lf.env Environment variables
|
||||
---@field count? integer A number that triggers that specific terminal
|
||||
|
|
177
lua/lf/main.lua
177
lua/lf/main.lua
|
@ -1,24 +1,77 @@
|
|||
local utils = require("lf.utils")
|
||||
|
||||
local ok, terminal = pcall(require, "toggleterm")
|
||||
if not ok then
|
||||
utils.err("toggleterm.nvim must be installed to use this program")
|
||||
return
|
||||
end
|
||||
local fs = utils.fs
|
||||
|
||||
local cmd = vim.cmd
|
||||
local api = vim.api
|
||||
local fn = vim.fn
|
||||
local uv = vim.loop
|
||||
local o = vim.o
|
||||
local fs = utils.fs
|
||||
local map = utils.map
|
||||
|
||||
local ok, terminal = pcall(require, "toggleterm")
|
||||
if not ok then
|
||||
api.nvim_err_writeln("toggleterm.nvim must be installed to use this program")
|
||||
return
|
||||
end
|
||||
|
||||
local Config = require("lf.config")
|
||||
-- local promise = require("promise")
|
||||
-- local async = require("async")
|
||||
|
||||
---@class Terminal
|
||||
---@alias Mode "n" | "i" | "?"
|
||||
|
||||
--- @class TerminalState
|
||||
--- @field mode Mode
|
||||
|
||||
--- @class TermCreateArgs
|
||||
--- @field newline_chr? string user specified newline chararacter
|
||||
--- @field cmd? string a custom command to run
|
||||
--- @field direction? string the layout style for the terminal
|
||||
--- @field id number?
|
||||
--- @field highlights table<string, table<string, string>>?
|
||||
--- @field dir string? the directory for the terminal
|
||||
--- @field count number? the count that triggers that specific terminal
|
||||
--- @field display_name string?
|
||||
--- @field hidden boolean? whether or not to include this terminal in the terminals list
|
||||
--- @field close_on_exit boolean? whether or not to close the terminal window when the process exits
|
||||
--- @field auto_scroll boolean? whether or not to scroll down on terminal output
|
||||
--- @field float_opts table<string, any>?
|
||||
--- @field on_stdout fun(t: Terminal, job: number, data: string[]?, name: string?)?
|
||||
--- @field on_stderr fun(t: Terminal, job: number, data: string[], name: string)?
|
||||
--- @field on_exit fun(t: Terminal, job: number, exit_code: number?, name: string?)?
|
||||
--- @field on_create fun(term:Terminal)?
|
||||
--- @field on_open fun(term:Terminal)?
|
||||
--- @field on_close fun(term:Terminal)?
|
||||
|
||||
--- @class Terminal
|
||||
--- @field newline_chr string
|
||||
--- @field cmd string
|
||||
--- @field direction string the layout style for the terminal
|
||||
--- @field id number
|
||||
--- @field bufnr number
|
||||
--- @field window number
|
||||
--- @field job_id number
|
||||
--- @field highlights table<string, table<string, string>>
|
||||
--- @field dir string the directory for the terminal
|
||||
--- @field name string the name of the terminal
|
||||
--- @field count number the count that triggers that specific terminal
|
||||
--- @field hidden boolean whether or not to include this terminal in the terminals list
|
||||
--- @field close_on_exit boolean? whether or not to close the terminal window when the process exits
|
||||
--- @field auto_scroll boolean? whether or not to scroll down on terminal output
|
||||
--- @field float_opts table<string, any>?
|
||||
--- @field display_name string?
|
||||
--- @field env table<string, string> environmental variables passed to jobstart()
|
||||
--- @field clear_env boolean use clean job environment, passed to jobstart()
|
||||
--- @field on_stdout fun(t: Terminal, job: number, data: string[]?, name: string?)?
|
||||
--- @field on_stderr fun(t: Terminal, job: number, data: string[], name: string)?
|
||||
--- @field on_exit fun(t: Terminal, job: number, exit_code: number?, name: string?)?
|
||||
--- @field on_create fun(term:Terminal)?
|
||||
--- @field on_open fun(term:Terminal)?
|
||||
--- @field on_close fun(term:Terminal)?
|
||||
--- @field _display_name fun(term: Terminal): string
|
||||
--- @field __state TerminalState
|
||||
--- @field new fun(self, term: TermCreateArgs?): Terminal
|
||||
--- @field close fun(self)
|
||||
--- @field open fun(self, size: number?, direction: string?)
|
||||
local Terminal = require("toggleterm.terminal").Terminal
|
||||
|
||||
---@class Lf
|
||||
|
@ -75,7 +128,6 @@ function Lf:new(config)
|
|||
self:__set_argv()
|
||||
self.bufnr = 0
|
||||
self.view_idx = 1
|
||||
self.action = self.cfg.default_action
|
||||
-- Needs to be grabbed here before the terminal buffer is created
|
||||
self.signcolumn = o.signcolumn
|
||||
|
||||
|
@ -91,20 +143,10 @@ 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,
|
||||
hidden = false,
|
||||
clear_env = self.cfg.env.clear,
|
||||
highlights = self.cfg.highlights,
|
||||
display_name = "Lf",
|
||||
count = self.cfg.count,
|
||||
float_opts = {
|
||||
border = self.cfg.border,
|
||||
width = self.cfg.width,
|
||||
height = self.cfg.height,
|
||||
winblend = self.cfg.winblend,
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
|
@ -136,22 +178,26 @@ function Lf:__open_in(path)
|
|||
if path == "gwd" or path == "git_dir" then
|
||||
path = utils.git_dir()
|
||||
end
|
||||
path = fn.expand((path == "" or path == nil) and "%:p:h" or path)
|
||||
|
||||
local built = path
|
||||
if type(path) == "string" then
|
||||
path = fn.expand(path)
|
||||
else
|
||||
path = fn.expand("%:p:h")
|
||||
end
|
||||
|
||||
local stat = uv.fs_stat(path)
|
||||
if not type(stat) == "table" then
|
||||
local cwd = uv.cwd()
|
||||
stat = uv.fs_stat(cwd)
|
||||
built = cwd
|
||||
path = cwd --[[@as string]]
|
||||
end
|
||||
|
||||
-- Should be fine, but just checking
|
||||
if stat and not stat.type == "directory" then
|
||||
built = fs.dirname(built)
|
||||
path = fs.dirname(path)
|
||||
end
|
||||
|
||||
self.term.dir = built
|
||||
self.term.dir = path
|
||||
self.curfile = fn.expand("%:p")
|
||||
|
||||
return self
|
||||
|
@ -193,63 +239,26 @@ function Lf:__on_open(term)
|
|||
self.bufnr = term.bufnr
|
||||
self.winid = term.window
|
||||
|
||||
cmd("silent! doautocmd User LfTermEnter")
|
||||
-- cmd("silent! doautocmd User LfTermEnter")
|
||||
|
||||
-- Wrap needs to be set, otherwise the window isn't aligned on resize
|
||||
api.nvim_win_call(self.winid, function()
|
||||
vim.wo.showbreak = "NONE"
|
||||
vim.wo.wrap = true
|
||||
vim.wo.sidescrolloff = 0
|
||||
vim.wo.scrolloff = 0
|
||||
vim.wo.scrollbind = false
|
||||
end)
|
||||
|
||||
if self.cfg.tmux then
|
||||
utils.tmux(true)
|
||||
end
|
||||
|
||||
-- Not sure if this works
|
||||
if self.cfg.mappings then
|
||||
if self.cfg.escape_quit then
|
||||
map(
|
||||
"t",
|
||||
"<Esc>",
|
||||
"<Cmd>q<CR>",
|
||||
{buffer = self.bufnr, desc = "Exit Lf"}
|
||||
)
|
||||
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.action = mapping
|
||||
|
||||
local res = utils.read_file(self.tmp_id)
|
||||
self.id = tonumber(res)
|
||||
|
||||
fn.system({"lf", "-remote", ("send %d open"):format(self.id)})
|
||||
end, {noremap = true, buffer = self.bufnr, desc = ("Lf %s"):format(mapping)})
|
||||
end
|
||||
|
||||
if self.cfg.layout_mapping then
|
||||
map("t", self.cfg.layout_mapping, function()
|
||||
api.nvim_win_set_config(self.winid, utils.get_view(
|
||||
self.cfg.views[self.view_idx],
|
||||
self.bufnr,
|
||||
self.signcolumn
|
||||
))
|
||||
self.view_idx = self.view_idx < #self.cfg.views
|
||||
and self.view_idx + 1
|
||||
or 1
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
-- Don't know why whenever wrap is set in the terminal, a weird resize happens.
|
||||
-- Because of that, this is needed here.
|
||||
vim.defer_fn(function()
|
||||
cmd("silent! doautoall VimResized")
|
||||
end, 800)
|
||||
-- -- Wrap needs to be set, otherwise the window isn't aligned on resize
|
||||
-- api.nvim_win_call(self.winid, function()
|
||||
-- vim.wo.showbreak = "NONE"
|
||||
-- vim.wo.wrap = true
|
||||
-- vim.wo.sidescrolloff = 0
|
||||
-- vim.wo.scrolloff = 0
|
||||
-- vim.wo.scrollbind = false
|
||||
-- end)
|
||||
--
|
||||
-- if self.cfg.tmux then
|
||||
-- utils.tmux(true)
|
||||
-- end
|
||||
--
|
||||
-- -- Don't know why whenever wrap is set in the terminal, a weird resize happens.
|
||||
-- -- Because of that, this is needed here.
|
||||
-- vim.defer_fn(function()
|
||||
-- cmd("silent! doautoall VimResized")
|
||||
-- end, 800)
|
||||
end
|
||||
|
||||
---@private
|
||||
|
@ -257,16 +266,12 @@ end
|
|||
---
|
||||
---@param term Terminal
|
||||
function Lf:__callback(term)
|
||||
if self.cfg.tmux then
|
||||
utils.tmux(false)
|
||||
end
|
||||
|
||||
if
|
||||
(self.action == "cd" or self.action == "lcd")
|
||||
and uv.fs_stat(self.tmp_lastdir)
|
||||
then
|
||||
local last_dir = utils.read_file(self.tmp_lastdir)
|
||||
if last_dir ~= nil and last_dir ~= uv.cwd() then
|
||||
if type(last_dir) == "string" then
|
||||
cmd(("%s %s"):format(self.action, fn.fnameescape(last_dir)))
|
||||
return
|
||||
end
|
||||
|
|
399
lua/lf/utils.lua
399
lua/lf/utils.lua
|
@ -1,66 +1,68 @@
|
|||
local M = {}
|
||||
|
||||
--
|
||||
local uv = vim.loop
|
||||
local fn = vim.fn
|
||||
local api = vim.api
|
||||
local levels = vim.log.levels
|
||||
local o = vim.o
|
||||
|
||||
---Echo a message with `nvim_echo`
|
||||
---@param msg string message
|
||||
---@param hl string highlight group
|
||||
function M.echomsg(msg, hl)
|
||||
hl = hl or "Title"
|
||||
api.nvim_echo({{msg, hl}}, true, {})
|
||||
end
|
||||
|
||||
---Display notification message
|
||||
---@param msg string
|
||||
---@param level number
|
||||
---@param opts table?
|
||||
function M.notify(msg, level, opts)
|
||||
opts = vim.tbl_extend("force", opts or {}, {title = "lf.nvim"})
|
||||
vim.notify(msg, level, opts)
|
||||
end
|
||||
|
||||
---INFO message
|
||||
---@param msg string
|
||||
---@param print? boolean
|
||||
---@param opts table?
|
||||
function M.info(msg, print, opts)
|
||||
if print then
|
||||
M.echomsg(("[INFO]: %s"):format(msg), "Directory")
|
||||
else
|
||||
M.notify(msg, levels.INFO, opts)
|
||||
end
|
||||
end
|
||||
|
||||
---WARN message
|
||||
---@param msg string
|
||||
---@param print? boolean
|
||||
---@param opts table?
|
||||
function M.warn(msg, print, opts)
|
||||
if print then
|
||||
M.echomsg(("[WARN]: %s"):format(msg), "WarningMsg")
|
||||
else
|
||||
M.notify(msg, levels.WARN, opts)
|
||||
end
|
||||
end
|
||||
|
||||
---ERROR message
|
||||
---@param msg string
|
||||
---@param print? boolean
|
||||
---@param opts table?
|
||||
function M.err(msg, print, opts)
|
||||
if print then
|
||||
M.echomsg(("[ERROR]: %s"):format(msg), "ErrorMsg")
|
||||
else
|
||||
M.notify(msg, levels.ERROR, opts)
|
||||
end
|
||||
end
|
||||
|
||||
-- local api = vim.api
|
||||
-- local levels = vim.log.levels
|
||||
-- local o = vim.o
|
||||
--
|
||||
-- ---Echo a message with `nvim_echo`
|
||||
-- ---@param msg string message
|
||||
-- ---@param hl string highlight group
|
||||
-- function M.echomsg(msg, hl)
|
||||
-- hl = hl or "Title"
|
||||
-- api.nvim_echo({{msg, hl}}, true, {})
|
||||
-- end
|
||||
--
|
||||
-- ---Display notification message
|
||||
-- ---@param msg string
|
||||
-- ---@param level number
|
||||
-- ---@param opts table?
|
||||
-- function M.notify(msg, level, opts)
|
||||
-- opts = vim.tbl_extend("force", opts or {}, {title = "lf.nvim"})
|
||||
-- vim.notify(msg, level, opts)
|
||||
-- end
|
||||
--
|
||||
-- ---INFO message
|
||||
-- ---@param msg string
|
||||
-- ---@param print? boolean
|
||||
-- ---@param opts table?
|
||||
-- function M.info(msg, print, opts)
|
||||
-- if print then
|
||||
-- M.echomsg(("[INFO]: %s"):format(msg), "Directory")
|
||||
-- else
|
||||
-- M.notify(msg, levels.INFO, opts)
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
-- ---WARN message
|
||||
-- ---@param msg string
|
||||
-- ---@param print? boolean
|
||||
-- ---@param opts table?
|
||||
-- function M.warn(msg, print, opts)
|
||||
-- if print then
|
||||
-- M.echomsg(("[WARN]: %s"):format(msg), "WarningMsg")
|
||||
-- else
|
||||
-- M.notify(msg, levels.WARN, opts)
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
-- ---ERROR message
|
||||
-- ---@param msg string
|
||||
-- ---@param print? boolean
|
||||
-- ---@param opts table?
|
||||
-- function M.err(msg, print, opts)
|
||||
-- if print then
|
||||
-- M.echomsg(("[ERROR]: %s"):format(msg), "ErrorMsg")
|
||||
-- else
|
||||
-- M.notify(msg, levels.ERROR, opts)
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
---@param path string
|
||||
---@diagnostic disable-next-line: undefined-doc-name
|
||||
---@return uv_fs_t|string
|
||||
---@diagnostic disable-next-line: undefined-doc-name
|
||||
---@return uv.aliases.fs_stat_table?
|
||||
function M.read_file(path)
|
||||
-- tonumber(444, 8) == 292
|
||||
|
@ -70,153 +72,150 @@ function M.read_file(path)
|
|||
uv.fs_close(fd)
|
||||
return buffer, stat
|
||||
end
|
||||
|
||||
---Create a neovim keybinding
|
||||
---@param mode string vim mode in a single letter
|
||||
---@param lhs string keys that are bound
|
||||
---@param rhs string|function string or lua function that is mapped to the keys
|
||||
---@param opts table? options set for the mapping
|
||||
function M.map(mode, lhs, rhs, opts)
|
||||
opts = opts or {}
|
||||
opts.noremap = opts.noremap == nil and true or opts.noremap
|
||||
vim.keymap.set(mode, lhs, rhs, opts)
|
||||
end
|
||||
|
||||
---Get Neovim window height
|
||||
---@return number
|
||||
function M.height()
|
||||
return o.lines - o.cmdheight
|
||||
end
|
||||
|
||||
---Get neovim window width (minus signcolumn)
|
||||
---@param bufnr number: Buffer number from the file that Lf is opened from
|
||||
---@param signcolumn string: Signcolumn option set by the user, not the terminal buffer
|
||||
---@return number
|
||||
function M.width(bufnr, signcolumn)
|
||||
if signcolumn:match("no") then
|
||||
return o.columns
|
||||
end
|
||||
|
||||
-- This is a rough estimate of the signcolumn
|
||||
local width = #tostring(api.nvim_buf_line_count(bufnr))
|
||||
local col = vim.split(signcolumn, ":")
|
||||
if #col == 2 then
|
||||
width = width + tonumber(col[2])
|
||||
end
|
||||
|
||||
return o.columns - width
|
||||
end
|
||||
|
||||
---Get the table that is passed to `api.nvim_win_set_config`
|
||||
---@param opts table
|
||||
---@param bufnr number Buffer number from the file that Lf is opened from
|
||||
---@param signcolumn string Signcolumn option set by the user, not the terminal buffer
|
||||
---@return table
|
||||
function M.get_view(opts, bufnr, signcolumn)
|
||||
opts = opts or {}
|
||||
|
||||
local width = opts.width
|
||||
if width > o.columns then
|
||||
M.err(("width (%d) cannot be greater than columns (%d)"):format(width, o.columns))
|
||||
width = nil
|
||||
end
|
||||
width = width or M.width(bufnr, signcolumn)
|
||||
if width < 0 then
|
||||
if width > -1 then
|
||||
width = fn.float2nr(o.columns + fn.round(width * o.columns))
|
||||
else
|
||||
width = o.columns + width
|
||||
end
|
||||
else
|
||||
if width < 1 then
|
||||
width = fn.float2nr(fn.round(width * o.columns))
|
||||
end
|
||||
end
|
||||
|
||||
local height = opts.height
|
||||
if height > o.lines then
|
||||
M.err(("height (%d) cannot be greater than lines (%d)"):format(height, o.lines))
|
||||
height = nil
|
||||
end
|
||||
height = height or M.height()
|
||||
if height < 0 then
|
||||
if height > -1 then
|
||||
height = fn.float2nr(o.lines + fn.round(height * o.lines))
|
||||
else
|
||||
height = o.lines + height
|
||||
end
|
||||
else
|
||||
if height < 1 then
|
||||
height = fn.float2nr(fn.round(height * o.lines))
|
||||
end
|
||||
end
|
||||
|
||||
local col = opts.col
|
||||
and fn.float2nr(fn.round(opts.col * o.columns))
|
||||
or math.ceil(o.columns - width) * 0.5 - 1
|
||||
local row = opts.row
|
||||
and fn.float2nr(fn.round(opts.row * o.lines))
|
||||
or math.ceil(o.lines - height) * 0.5 - 1
|
||||
|
||||
return {
|
||||
col = col, -- fractional allowed
|
||||
row = row, -- fractional allowed
|
||||
width = width, -- minimum of 1
|
||||
height = height, -- minimum of 1
|
||||
relative = "editor",
|
||||
style = "minimal",
|
||||
}
|
||||
end
|
||||
|
||||
---@return string|nil
|
||||
function M.git_dir()
|
||||
---@diagnostic disable-next-line: missing-parameter
|
||||
local gitdir = fn.system(("git -C %s rev-parse --show-toplevel"):format(fn.expand("%:p:h")))
|
||||
|
||||
if gitdir:match("^fatal:.*") then
|
||||
M.info("Failed to get git directory")
|
||||
return
|
||||
end
|
||||
return vim.trim(gitdir)
|
||||
end
|
||||
|
||||
---Set the tmux statusline when opening/closing `Lf`
|
||||
---@param disable boolean: whether the statusline is being enabled or disabled
|
||||
function M.tmux(disable)
|
||||
if not vim.env.TMUX then
|
||||
return
|
||||
end
|
||||
if disable then
|
||||
fn.system([[tmux set status off]])
|
||||
fn.system([[tmux list-panes -F '\#F' | grep -q Z || tmux resize-pane -Z]])
|
||||
else
|
||||
fn.system([[tmux set status on]])
|
||||
fn.system([[tmux list-panes -F '\#F' | grep -q Z && tmux resize-pane -Z]])
|
||||
end
|
||||
end
|
||||
|
||||
-- ╭──────────────────────────────────────────────────────────╮
|
||||
-- │ vim.fs replacement │
|
||||
-- ╰──────────────────────────────────────────────────────────╯
|
||||
|
||||
M.fs = {}
|
||||
|
||||
---Return basename of the given file entry
|
||||
---
|
||||
---@param file string: File or directory
|
||||
---@return string: Basename of `file`
|
||||
function M.fs.basename(file)
|
||||
return fn.fnamemodify(file, ":t")
|
||||
end
|
||||
--
|
||||
-- ---Create a neovim keybinding
|
||||
-- ---@param mode string vim mode in a single letter
|
||||
-- ---@param lhs string keys that are bound
|
||||
-- ---@param rhs string|function string or lua function that is mapped to the keys
|
||||
-- ---@param opts table? options set for the mapping
|
||||
-- function M.map(mode, lhs, rhs, opts)
|
||||
-- opts = opts or {}
|
||||
-- opts.noremap = opts.noremap == nil and true or opts.noremap
|
||||
-- vim.keymap.set(mode, lhs, rhs, opts)
|
||||
-- end
|
||||
--
|
||||
-- ---Get Neovim window height
|
||||
-- ---@return number
|
||||
-- function M.height()
|
||||
-- return o.lines - o.cmdheight
|
||||
-- end
|
||||
--
|
||||
-- ---Get neovim window width (minus signcolumn)
|
||||
-- ---@param bufnr number: Buffer number from the file that Lf is opened from
|
||||
-- ---@param signcolumn string: Signcolumn option set by the user, not the terminal buffer
|
||||
-- ---@return number
|
||||
-- function M.width(bufnr, signcolumn)
|
||||
-- if signcolumn:match("no") then
|
||||
-- return o.columns
|
||||
-- end
|
||||
--
|
||||
-- -- This is a rough estimate of the signcolumn
|
||||
-- local width = #tostring(api.nvim_buf_line_count(bufnr))
|
||||
-- local col = vim.split(signcolumn, ":")
|
||||
-- if #col == 2 then
|
||||
-- width = width + tonumber(col[2])
|
||||
-- end
|
||||
--
|
||||
-- return o.columns - width
|
||||
-- end
|
||||
--
|
||||
-- ---Get the table that is passed to `api.nvim_win_set_config`
|
||||
-- ---@param opts table
|
||||
-- ---@param bufnr number Buffer number from the file that Lf is opened from
|
||||
-- ---@param signcolumn string Signcolumn option set by the user, not the terminal buffer
|
||||
-- ---@return table
|
||||
-- function M.get_view(opts, bufnr, signcolumn)
|
||||
-- opts = opts or {}
|
||||
--
|
||||
-- local width = opts.width
|
||||
-- if width > o.columns then
|
||||
-- M.err(("width (%d) cannot be greater than columns (%d)"):format(width, o.columns))
|
||||
-- width = nil
|
||||
-- end
|
||||
-- width = width or M.width(bufnr, signcolumn)
|
||||
-- if width < 0 then
|
||||
-- if width > -1 then
|
||||
-- width = fn.float2nr(o.columns + fn.round(width * o.columns))
|
||||
-- else
|
||||
-- width = o.columns + width
|
||||
-- end
|
||||
-- else
|
||||
-- if width < 1 then
|
||||
-- width = fn.float2nr(fn.round(width * o.columns))
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
-- local height = opts.height
|
||||
-- if height > o.lines then
|
||||
-- M.err(("height (%d) cannot be greater than lines (%d)"):format(height, o.lines))
|
||||
-- height = nil
|
||||
-- end
|
||||
-- height = height or M.height()
|
||||
-- if height < 0 then
|
||||
-- if height > -1 then
|
||||
-- height = fn.float2nr(o.lines + fn.round(height * o.lines))
|
||||
-- else
|
||||
-- height = o.lines + height
|
||||
-- end
|
||||
-- else
|
||||
-- if height < 1 then
|
||||
-- height = fn.float2nr(fn.round(height * o.lines))
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
-- local col = opts.col
|
||||
-- and fn.float2nr(fn.round(opts.col * o.columns))
|
||||
-- or math.ceil(o.columns - width) * 0.5 - 1
|
||||
-- local row = opts.row
|
||||
-- and fn.float2nr(fn.round(opts.row * o.lines))
|
||||
-- or math.ceil(o.lines - height) * 0.5 - 1
|
||||
--
|
||||
-- return {
|
||||
-- col = col, -- fractional allowed
|
||||
-- row = row, -- fractional allowed
|
||||
-- width = width, -- minimum of 1
|
||||
-- height = height, -- minimum of 1
|
||||
-- relative = "editor",
|
||||
-- style = "minimal",
|
||||
-- }
|
||||
-- end
|
||||
--
|
||||
-- ---@return string|nil
|
||||
-- function M.git_dir()
|
||||
-- ---@diagnostic disable-next-line: missing-parameter
|
||||
-- local gitdir = fn.system(("git -C %s rev-parse --show-toplevel"):format(fn.expand("%:p:h")))
|
||||
--
|
||||
-- if gitdir:match("^fatal:.*") then
|
||||
-- M.info("Failed to get git directory")
|
||||
-- return
|
||||
-- end
|
||||
-- return vim.trim(gitdir)
|
||||
-- end
|
||||
--
|
||||
-- ---Set the tmux statusline when opening/closing `Lf`
|
||||
-- ---@param disable boolean: whether the statusline is being enabled or disabled
|
||||
-- function M.tmux(disable)
|
||||
-- if not vim.env.TMUX then
|
||||
-- return
|
||||
-- end
|
||||
-- if disable then
|
||||
-- fn.system([[tmux set status off]])
|
||||
-- fn.system([[tmux list-panes -F '\#F' | grep -q Z || tmux resize-pane -Z]])
|
||||
-- else
|
||||
-- fn.system([[tmux set status on]])
|
||||
-- fn.system([[tmux list-panes -F '\#F' | grep -q Z && tmux resize-pane -Z]])
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
-- -- ╭──────────────────────────────────────────────────────────╮
|
||||
-- -- │ vim.fs replacement │
|
||||
-- -- ╰──────────────────────────────────────────────────────────╯
|
||||
--
|
||||
-- M.fs = {}
|
||||
--
|
||||
-- ---Return basename of the given file entry
|
||||
-- ---
|
||||
-- ---@param file string: File or directory
|
||||
-- ---@return string: Basename of `file`
|
||||
-- function M.fs.basename(file)
|
||||
-- return fn.fnamemodify(file, ":t")
|
||||
-- end
|
||||
|
||||
---Return parent directory of the given file entry
|
||||
---
|
||||
---@param file string: File or directory
|
||||
---@return string?: Parent directory of file
|
||||
---@return string: Parent directory of file
|
||||
function M.fs.dirname(file)
|
||||
if file == nil then
|
||||
return nil
|
||||
end
|
||||
return fn.fnamemodify(file, ":h")
|
||||
end
|
||||
|
||||
|
|
Reference in New Issue