refactor(ui): removed the preview and search popups. Refactored project specific code be faster and more maintained

This commit is contained in:
DarthMooMancer
2025-05-27 13:50:50 -04:00
parent 23bc6c2bfc
commit 60e5d9ea2f
3 changed files with 89 additions and 265 deletions
+1 -2
View File
@@ -1,6 +1,5 @@
local M = {} local M = {}
---@param project_root string ---@param project_root string
---@return table ---@return table
local function default_languages(project_root) local function default_languages(project_root)
@@ -54,7 +53,7 @@ M.user_config = {}
function M.setup(user_opts) function M.setup(user_opts)
local defaults = { local defaults = {
globals = { globals = {
project_root = "~/Projects", project_root = "~/personal/Projects",
keybinds = { keybinds = {
["<leader>po"] = "PolydevManager", ["<leader>po"] = "PolydevManager",
}, },
-1
View File
@@ -1,7 +1,6 @@
local M = {} local M = {}
function M.setup_highlights() function M.setup_highlights()
-- Only set if not already defined by colorscheme
local function define(name, opts) local function define(name, opts)
if vim.fn.hlexists(name) == 0 then if vim.fn.hlexists(name) == 0 then
vim.api.nvim_set_hl(0, name, opts) vim.api.nvim_set_hl(0, name, opts)
+78 -252
View File
@@ -9,27 +9,13 @@ local function get_entries(path)
while true do while true do
local name, type = vim.uv.fs_scandir_next(handle) local name, type = vim.uv.fs_scandir_next(handle)
if not name then break end if not name then break end
-- Check if this is a project file
local is_project = false
if type == "file" then
local ext = name:match("%.([^%.]+)$")
if ext == "project" then
is_project = true
type = "project"
end
end
table.insert(result, { table.insert(result, {
name = name .. (type == "directory" and "/" or ""), name = name .. (type == "directory" and "/" or ""),
type = type, type = type,
full_path = path .. "/" .. name, full_path = path .. "/" .. name,
is_project = is_project
}) })
end end
table.sort(result, function(a, b) table.sort(result, function(a, b)
-- Sort projects first, then directories, then files
if a.is_project ~= b.is_project then
return a.is_project
end
if a.type == "directory" and b.type ~= "directory" then if a.type == "directory" and b.type ~= "directory" then
return true return true
end end
@@ -41,32 +27,16 @@ local function get_entries(path)
return result return result
end end
---@param list table
---@param query string
---@return table
local function fuzzy_filter(list, query)
local result = {}
query = query:lower()
for _, entry in ipairs(list) do
if entry.name:lower():find(query) then
table.insert(result, entry)
end
end
return result
end
---@param project_root string ---@param project_root string
function M.manager(project_root) function M.manager(project_root)
local templates = require("Polydev.templates") local templates = require("Polydev.templates")
local languages = require("Polydev.languages")
local Popup = require("nui.popup") local Popup = require("nui.popup")
local Layout = require("nui.layout") local Layout = require("nui.layout")
local event = require("nui.utils.autocmd").event
local cwd = vim.fn.expand(vim.fn.getcwd()) local cwd = vim.fn.expand(vim.fn.getcwd())
local entries = get_entries(cwd) local entries = get_entries(cwd)
local current_view = vim.deepcopy(entries) local current_view = vim.deepcopy(entries)
local popup, search, preview = Popup({ local popup = Popup({
enter = true, enter = true,
focusable = true, focusable = true,
border = { border = {
@@ -78,103 +48,45 @@ function M.manager(project_root)
win_options = { win_options = {
winhighlight = "Normal:PolydevNormal,FloatBorder:PolydevBorder,FloatTitle:PolydevTitle", winhighlight = "Normal:PolydevNormal,FloatBorder:PolydevBorder,FloatTitle:PolydevTitle",
} }
}), Popup({
enter = false,
focusable = true,
border = {
style = "rounded", -- "rounded", "single" or "solid".
text = { top = " Find Files ", top_align = "center" },
},
buf_options = { modifiable = true, readonly = false },
win_options = {
winhighlight = "Normal:PolydevNormal,FloatBorder:PolydevBorder,FloatTitle:PolydevTitle",
}
}), Popup({
enter = false,
focusable = false,
border = {
style = "rounded",
text = { top = " Grep Preview ", top_align = "center" },
},
position = "50%",
buf_options = { modifiable = true, readonly = false },
win_options = {
winhighlight = "Normal:PolydevNormal,FloatBorder:PolydevBorder,FloatTitle:PolydevTitle",
}
}) })
local layout = Layout({ local layout = Layout(
position = "55%", {
size = { width = "81%", height = "90%" }, position = "50%",
size = { width = "80%", height = "90%" },
}, },
Layout.Box({ Layout.Box({ Layout.Box(popup, { grow = 1 }) }, { dir = "row" })
Layout.Box({
Layout.Box(search, { size = "10%" }),
Layout.Box(popup, { size = "93%" }),
}, { dir = "col", size = "48%" }),
Layout.Box(preview, { size = "51.5%" }),
}, { dir = "row" })
) )
layout:mount() layout:mount()
---@return table, integer
local function get_selected_entry() local function get_selected_entry()
local row = vim.api.nvim_win_get_cursor(popup.winid)[1] - 1 local row = vim.api.nvim_win_get_cursor(popup.winid)[1] - 1
return current_view[row], row return current_view[row], row
end end
local function update_preview()
if not preview.bufnr then return end
vim.api.nvim_buf_set_lines(preview.bufnr, 0, -1, false, {}) -- Clear
local entry = get_selected_entry() ---@return string, string
if not entry then return end local function get_inputs()
local l, n
if entry.type == "file" or entry.type == "project" then vim.ui.input({ prompt = "Enter language for new file: " }, function(lang)
-- Preview file contents (first 100 lines) vim.ui.input({ prompt = "Enter file name: " }, function(name)
local lines = {} if not lang or lang == "" then return end
local fd = io.open(entry.full_path, "r") if not name or name == "" then return end
if fd then l, n = lang, name
for _ = 1, 100 do end)
local line = fd:read("*line") end)
if not line then break end return l, n
table.insert(lines, line)
end
fd:close()
else
table.insert(lines, "[Failed to read file]")
end
vim.api.nvim_buf_set_lines(preview.bufnr, 0, -1, false, lines)
elseif entry.type == "directory" then
-- List contents of directory
local contents = get_entries(entry.full_path)
local lines = {}
for _, item in ipairs(contents) do
table.insert(lines, string.format("- %s", item.name))
end
if #lines == 0 then
table.insert(lines, "[Empty folder]")
end
vim.api.nvim_buf_set_lines(preview.bufnr, 0, -1, false, lines)
end
end end
vim.api.nvim_create_autocmd("CursorMoved", { local function update_screen()
buffer = popup.bufnr,
callback = function()
vim.schedule(update_preview)
end,
})
local function refresh()
entries = get_entries(cwd) entries = get_entries(cwd)
local root = vim.fn.expand(project_root) local root = vim.fn.expand(project_root)
-- If we're not in the Projects folder, redirect to Projects
if not cwd:find(root, 1, true) then if not cwd:find(root, 1, true) then
cwd = root cwd = root
entries = get_entries(cwd) entries = get_entries(cwd)
end end
-- Only add ".." if we're inside Projects and not at the root
if cwd ~= root then if cwd ~= root then
table.insert(entries, 1, { table.insert(entries, 1, {
name = "../", name = "../",
@@ -184,193 +96,107 @@ function M.manager(project_root)
}) })
end end
current_view = vim.deepcopy(entries) current_view = vim.deepcopy(entries)
end
local function render()
local hints = {
"",
"/ → Find a → New Directory % → New File R → Rename",
"x → New Helper File d → New Project D → Delete",
}
if not popup.bufnr then return end if not popup.bufnr then return end
vim.api.nvim_buf_set_lines(popup.bufnr, 0, -1, false, {}) vim.api.nvim_buf_set_lines(popup.bufnr, 0, -1, false, {})
for i, item in ipairs(current_view) do for i, item in ipairs(current_view) do
local line = string.format("%-2d | %-27s | %-6s", i, item.name, item.type) local line = string.format("%-2d | %-27s | %-6s", i, item.name, item.type)
vim.api.nvim_buf_set_lines(popup.bufnr, -1, -1, false, { line }) vim.api.nvim_buf_set_lines(popup.bufnr, -1, -1, false, { line })
end end
vim.api.nvim_buf_set_lines(popup.bufnr, -1, -1, false, hints)
update_preview()
end end
local function prompt_filter()
vim.api.nvim_buf_set_lines(search.bufnr, 0, -1, false, { "" })
vim.api.nvim_set_option_value("modifiable", true, { win = search.bufnr })
if vim.api.nvim_win_is_valid(search.winid) then
vim.api.nvim_set_current_win(search.winid)
end
vim.cmd("startinsert")
vim.keymap.set("i", "<CR>", function()
local input = vim.api.nvim_buf_get_lines(search.bufnr, 0, 1, false)[1]
if input ~= "" then
current_view = fuzzy_filter(entries, input)
render()
end
vim.api.nvim_buf_set_lines(search.bufnr, 0, -1, false, { "" })
vim.api.nvim_set_current_win(popup.winid)
vim.cmd("stopinsert") -- exit insert mode
end, { buffer = search.bufnr, noremap = true })
vim.keymap.set("i", "<Esc>", function()
vim.api.nvim_set_current_win(popup.winid)
vim.cmd("stopinsert") -- ensure we are in normal mode
end, { buffer = search.bufnr, noremap = true })
end
popup:map("n", "/", function() vim.schedule(prompt_filter) end)
-- Mappings
popup:map("n", "<ESC>", function() popup:unmount() end) popup:map("n", "<ESC>", function() popup:unmount() end)
-- Creates new file
popup:map("n", "%", function()
vim.ui.input({ prompt = "Enter language for new file: " }, function(lang)
if not lang or lang == "" then return print("File creation canceled") end
if languages.load_language_module(lang) and templates.files[lang] and templates.files[lang].run then
popup:unmount()
vim.schedule(function()
vim.ui.input({ prompt = "Enter file name: " }, function(file_name)
if not file_name or file_name == "" then return print("File creation canceled") end
templates.files[lang].run(file_name)
end)
end)
else
print("Error: No File creation method for " .. lang)
end
end)
end)
-- Creates extra file
popup:map("n", "x", function()
vim.ui.input({ prompt = "Enter language for new auxilary file: "}, function(lang)
if not lang or lang == "" then return print("File create canceled") end
if languages.load_language_module(lang) and templates.extras.run then
popup:unmount()
vim.schedule(function()
vim.ui.input({ prompt = "Enter file name: "}, function(file_name)
if not file_name or file_name == "" then return print("File creation canceled") end
templates.extras.run(lang, file_name)
end)
end)
else
print("Error: No File creation method for " .. lang)
end
end)
end)
-- Creates a new folder
popup:map("n", "a", function() popup:map("n", "a", function()
vim.ui.input({ prompt = "New folder name:" }, function(input) vim.ui.input({ prompt = "New folder name: " }, function(input)
if input then if input then
vim.fn.mkdir(cwd .. "/" .. input, "p") vim.fn.mkdir(cwd .. "/" .. input, "p")
refresh() update_screen()
render()
end end
end) end)
end) end)
-- Creates new project popup:map("n", "R", function()
popup:map("n", "d", function() local entry = get_selected_entry()
local opts = {} if not entry.is_up then
vim.ui.input({ prompt = "Enter language for project: " }, function(lang) vim.ui.input({ prompt = "Rename: ", default = entry.name }, function(input)
if not lang or lang == "" then return print("Project creation canceled") end if input then
opts = vim.tbl_deep_extend("force", {}, require("Polydev.configs").get(lang), opts or {}) local new_path = cwd .. "/" .. input
if languages.load_language_module(lang) and templates.projects[lang] and templates.projects[lang].run then vim.fn.rename(entry.full_path, new_path)
-- if templates.projects[lang] and templates.projects[lang].run then update_screen()
end
end)
end
end)
popup:map("n", "<CR>", function()
local entry = get_selected_entry()
if entry.type == "directory" then
cwd = entry.full_path
vim.cmd("cd " .. cwd)
update_screen()
end
if entry.type == "file" then
popup:unmount() popup:unmount()
vim.schedule(function() vim.cmd("edit " .. vim.fn.fnameescape(entry.full_path))
vim.ui.input({ prompt = "Enter project name: " }, function(project_name)
if not project_name or project_name == "" then return print("Project creation canceled") end
templates.projects[lang].run(project_name, opts.project_root)
end)
end)
else
print("Error: No project creation method for " .. lang)
end end
end) end)
end)
-- Deletes Folder and confirms deletion
popup:map("n", "D", function() popup:map("n", "D", function()
local entry = get_selected_entry() local entry = get_selected_entry()
if not entry.is_up then if not entry.is_up then
vim.ui.input({ prompt = "Deletion of " .. entry.name .. ": (y/N) Defaults to N: " }, function(input) vim.ui.input({ prompt = "Deletion of " .. entry.name .. ": (y/N) Defaults to N: " }, function(input)
if input == "y" or input == "Y" then if input == "y" or input == "Y" then
vim.fn.delete(entry.full_path, "rf") vim.fn.delete(entry.full_path, "rf")
refresh() update_screen()
render()
end end
end) end)
end end
end) end)
-- Renames Files and Folders popup:map("n", "%", function()
popup:map("n", "R", function() local lang, name = get_inputs()
local entry = get_selected_entry() if lang == nil or lang == "" then return print("Error: Can't have a blank name for lang") end
if not entry.is_up then if templates.files[lang].run then
vim.ui.input({ prompt = "Rename:", default = entry.name }, function(input)
if input then
local new_path = cwd .. "/" .. input
vim.fn.rename(entry.full_path, new_path)
refresh()
render()
end
end)
end
end)
-- Changes working directory or edits a file
popup:map("n", "<CR>", function()
local entry = get_selected_entry()
if entry.type == "directory" then
cwd = entry.full_path
vim.cmd("cd " .. cwd)
refresh()
render()
end
if entry.type == "file" then
popup:unmount() popup:unmount()
vim.schedule(function() if name ~= nil and name ~= "" then
vim.cmd("edit " .. vim.fn.fnameescape(entry.full_path)) templates.files[lang].run(name)
end) end
else
print("Error: No File creation method for " .. lang)
end end
end) end)
popup:map("n", "<BS>", function() popup:map("n", "x", function()
local parent = vim.fn.fnamemodify(cwd, ":h") local lang, name = get_inputs()
if parent ~= cwd then if lang == nil or lang == "" then return print("Error: Can't have a blank name for lang") end
cwd = parent if templates.extras.run then
refresh()
render()
end
end)
popup:on(event.BufLeave, function()
vim.defer_fn(function()
local function is_valid_win(winid)
return type(winid) == "number" and vim.api.nvim_win_is_valid(winid)
end
if not is_valid_win(popup.winid) and not is_valid_win(search.winid) then
popup:unmount() popup:unmount()
if name ~= nil and name ~= "" then
templates.extras.run(lang, name)
end
else
print("Error: No File creation method for " .. lang)
end
end)
popup:map("n", "d", function()
local lang, name = get_inputs()
local opts = {}
opts = vim.tbl_deep_extend("force", {}, require("Polydev.configs").get(lang), opts or {})
if lang == nil or lang == "" then return print("Error: Can't have a blank name for lang") end
if templates.projects[lang].run then
popup:unmount()
if name ~= nil and name ~= "" then
templates.projects[lang].run(name, opts.project_root)
end
else
print("Error: No project creation method for " .. lang)
end end
end, 50)
end) end)
popup:mount() popup:mount()
refresh() update_screen()
render()
end end
return M return M