refactor(ui): removed the preview and search popups. Refactored project specific code be faster and more maintained
This commit is contained in:
@@ -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,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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user