diff --git a/README.md b/README.md index 52e7839..221b81a 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,8 @@ require("lf").setup({ focus_on_open = true, -- focus the current file when opening Lf (experimental) mappings = true, -- whether terminal buffer mapping is enabled tmux = false, -- tmux statusline can be disabled on opening of Lf + default_file_manager = false, -- make lf default file manager + disable_netrw_warning = true, -- don't display a message when opening a directory with `default_file_manager` as true highlights = { -- highlights passed to toggleterm Normal = {link = "Normal"}, NormalFloat = {link = 'Normal'}, diff --git a/doc/lf.txt b/doc/lf.txt index aa178b5..36f6b8b 100644 --- a/doc/lf.txt +++ b/doc/lf.txt @@ -119,6 +119,8 @@ Defaults ~ focus_on_open = true, -- focus the current file when opening Lf (experimental) mappings = true, -- whether terminal buffer mapping is enabled tmux = false, -- tmux statusline can be disabled on opening of Lf + default_file_manager = false, -- make lf default file manager + disable_netrw_warning = true, -- don't display a message when opening a directory with `default_file_manager` as true highlights = { -- highlights passed to toggleterm Normal = {link = "Normal"}, NormalFloat = {link = 'Normal'}, diff --git a/lua/lf.lua b/lua/lf.lua index cd3b47c..43efabf 100644 --- a/lua/lf.lua +++ b/lua/lf.lua @@ -1,8 +1,13 @@ local M = {} -local loaded = false +local Config = require("lf.config") local utils = require("lf.utils") +local uv = vim.loop +local api = vim.api +local fn = vim.fn +local cmd = vim.cmd + ---Check Neovim version before setting mappings ---@param cfg Lf.Config local function has_feature(cfg) @@ -12,16 +17,83 @@ local function has_feature(cfg) 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 function M.setup(cfg) - if loaded then + if Config.__loaded then return end + cfg = cfg or {} has_feature(cfg) - M.__conf = cfg or {} - loaded = true + M.__conf = cfg + Config.init() + setup_autocmds() end ---Start the file manager diff --git a/lua/lf/config.lua b/lua/lf/config.lua index 60cf511..97765e6 100644 --- a/lua/lf/config.lua +++ b/lua/lf/config.lua @@ -1,10 +1,19 @@ local fn = vim.fn local o = vim.o -local Config = {} +local utils = require("lf.utils") + +---@class Lf.Container +---@field data Lf.Config +---@field group integer Autocmd id +---@field __loaded boolean +local Config = { + data = {}, + __loaded = false, +} ---@type Lf.Config -local opts = { +local default = { default_cmd = "lf", default_action = "drop", default_actions = { @@ -12,6 +21,8 @@ local opts = { [""] = "split", [""] = "vsplit", [""] = "tab drop", + [""] = "edit", + [""] = "argedit", }, winblend = 10, dir = "", @@ -23,6 +34,8 @@ local opts = { focus_on_open = true, mappings = true, tmux = false, + default_file_manager = false, + disable_netrw_warning = true, highlights = { Normal = {link = "Normal"}, FloatBorder = {link = "FloatBorder"}, @@ -63,7 +76,11 @@ local function validate(cfg) 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}, -- Layout configurations layout_mapping = {cfg.layout_mapping, "s", false}, views = {cfg.views, "t", false}, @@ -75,34 +92,49 @@ local function validate(cfg) return cfg end ----@private ----Initialize the default configuration -local function init() - local lf = require("lf") - -- Keep options from the `lf.setup()` call - Config = vim.tbl_deep_extend("keep", lf.__conf or {}, opts) --[[@as Lf.Config]] - Config = validate(Config) - lf.__conf = nil -end - -init() - ---Set a configuration passed as a function argument (not through `setup`) ---@param cfg? Lf.Config configuration options ---@return Lf.Config function Config:override(cfg) if type(cfg) == "table" then - self = vim.tbl_deep_extend("force", self, cfg) --[[@as Lf.Config]] - self = validate(self) + self.data = vim.tbl_deep_extend("force", self.data, cfg) --[[@as Lf.Config]] + self.data = validate(self.data) + -- self = vim.tbl_deep_extend("force", self, cfg) --[[@as Lf.Config]] + -- self = validate(self) end return self end +---Return the configuration +---@param key? string +---@return Lf.Config +function Config:get(key) + if key then + return self.data[key] + end + return self.data +end + +---Initialize the default configuration +function Config.init() + if Config.__loaded then + return + end + + 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) + lf.__conf = nil + Config.__loaded = true +end + return setmetatable(Config, { __index = function(self, key) return rawget(self, key) end, - __newindex = function(_self, _key, _val) + __newindex = function(_self, key, val) + utils.warn(("do not set invalid config values: %s => %s"):format(key, val)) end, }) @@ -112,42 +144,44 @@ return setmetatable(Config, { ---@alias Lf.directory "'gwd'"|"''"|nil|string ---@class Lf.views ----@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 width number ---@field height number ----@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 +---@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 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 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 env Lf.env Environment variables ----@field highlights table> 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 default_cmd? string Default `lf` command +---@field default_action? string Default action when `Lf` opens a file +---@field default_actions? table 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> 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 diff --git a/lua/lf/main.lua b/lua/lf/main.lua index 9ad14d3..f4692fe 100644 --- a/lua/lf/main.lua +++ b/lua/lf/main.lua @@ -68,7 +68,7 @@ function Lf:new(config) if config then self.cfg = Config:override(config) else - self.cfg = Config + self.cfg = Config.data end self.bufnr = 0 @@ -107,7 +107,7 @@ function Lf:__create_term() end ---Start the underlying terminal ----@param path? string path where Lf starts (reads from Config if none, else CWD) +---@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) self:__set_cmd_wrapper() @@ -238,6 +238,12 @@ function Lf:__on_open(term) 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) end ---@private diff --git a/plugin/lf.lua b/plugin/lf.lua deleted file mode 100644 index 97f4d82..0000000 --- a/plugin/lf.lua +++ /dev/null @@ -1,54 +0,0 @@ -if vim.g.loaded_lf == 1 then - return -end - -vim.g.loaded_lf = 1 - -local M = {} - -local uv = vim.loop -local api = vim.api -local fn = vim.fn -local cmd = vim.cmd - -api.nvim_create_user_command("Lf", function(tbl) - ---@diagnostic disable-next-line: missing-parameter - require("lf").start(tbl.args) -end, {nargs = "*", complete = "file"}) - -if vim.g.lf_netrw == 1 or vim.g.lf_netrw then - local group = api.nvim_create_augroup("ReplaceNetrwWithLf", {clear = true}) - - api.nvim_create_autocmd("VimEnter", { - pattern = "*", - group = group, - once = true, - callback = function() - if fn.exists("#FileExplorer") then - cmd("sil! au! FileExplorer") - end - end, - }) - - api.nvim_create_autocmd("BufEnter", { - pattern = "*", - group = group, - once = true, - callback = function() - local bufnr = api.nvim_get_current_buf() - local fname = fn.expand("%:p") - local stat = uv.fs_stat(fname) - if type(stat) == "table" then - if stat.type == "directory" and fn.argc() ~= 0 then - cmd(("sil! bwipeout! %s"):format(bufnr)) - - vim.defer_fn(function() - require("lf").start(fname) - end, 1) - end - end - end, - }) -end - -return M