-- recentf.lua -- Track and access recently opened files (like Emacs recentf-mode) -- ============================================================================ local recentf = {} -- Configuration recentf.config = { enabled = true, max_saved = 20, -- Maximum number of recent files to track exclude_patterns = { -- Patterns to exclude from recent files "^/tmp/", "%.git/", "COMMIT_EDITMSG$", }, } -- Recent files list (most recent first) recentf.list = {} -- Check if a file should be excluded local function should_exclude(filepath) if not filepath or filepath == "" then return true end -- Exclude *scratch* and other special buffers if filepath:match("^%*") then return true end for _, pattern in ipairs(recentf.config.exclude_patterns) do if filepath:match(pattern) then return true end end return false end -- Add a file to the recent list function recentf.add(filepath) if should_exclude(filepath) then return end -- Remove if already in list (to move to front) for i, f in ipairs(recentf.list) do if f == filepath then table.remove(recentf.list, i) break end end -- Add to front table.insert(recentf.list, 1, filepath) -- Trim to max size while #recentf.list > recentf.config.max_saved do table.remove(recentf.list) end end -- Get the recent files list function recentf.get_list() return recentf.list end -- Clear the recent files list function recentf.clear() recentf.list = {} end -- Open a recent file by index (1-based) function recentf.open(index) local idx = tonumber(index) if not idx or idx < 1 or idx > #recentf.list then return {success = false, message = "Invalid index: " .. tostring(index)} end local filepath = recentf.list[idx] return editor:execute_command("find-file", {filepath}) end -- Format the recent files list for display function recentf.format_list() local lines = {} for i, filepath in ipairs(recentf.list) do -- Shorten home directory local display = filepath:gsub("^" .. os.getenv("HOME"), "~") table.insert(lines, string.format("%2d: %s", i, display)) end return lines end -- Setup function function recentf.setup(opts) opts = opts or {} for k, v in pairs(opts) do recentf.config[k] = v end if not recentf.config.enabled then return end -- Register commands editor:register_command("recentf-open", "Open a recent file by number", function(args) if #args == 0 then -- List recent files local list = recentf.format_list() if #list == 0 then return {success = true, message = "No recent files"} end -- Show in echo area (up to 5 files) local preview = {} for i = 1, math.min(5, #list) do table.insert(preview, list[i]) end local msg = table.concat(preview, " | ") if #list > 5 then msg = msg .. " ..." end return {success = true, message = msg} end return recentf.open(args[1]) end, {}, true, "n") editor:register_command("recentf-list", "List all recent files", function(args) local list = recentf.format_list() if #list == 0 then return {success = true, message = "No recent files"} end -- Use echo area for multi-line display editor:set_echo_area(list) return {success = true, message = ""} end) editor:register_command("recentf-clear", "Clear the recent files list", function(args) recentf.clear() return {success = true, message = "Recent files list cleared"} end) -- Key binding (similar to Emacs) editor:bind_key("C-x C-r", "recentf-open", "Open recent file") -- Hook into find-file to track opens -- We'll expose a global function that the C++ can call lumacs.recentf_track = function(filepath) recentf.add(filepath) end print("[recentf] Package loaded") end -- Auto-setup with defaults recentf.setup() return recentf