mirror of
https://github.com/pojokcodeid/nvim-lazy.git
synced 2025-06-22 17:03:36 +02:00
enc: update modal npm runner
This commit is contained in:
parent
23a8cf07bf
commit
7450625ba8
6 changed files with 588 additions and 161 deletions
|
@ -1,40 +1,43 @@
|
||||||
return {
|
return {
|
||||||
"pojokcodeid/auto-conform.nvim",
|
"pojokcodeid/auto-conform.nvim",
|
||||||
dependencies = {
|
dependencies = {
|
||||||
"williamboman/mason.nvim",
|
"williamboman/mason.nvim",
|
||||||
"stevearc/conform.nvim",
|
"stevearc/conform.nvim",
|
||||||
},
|
},
|
||||||
event = "VeryLazy",
|
event = "VeryLazy",
|
||||||
opts = function(_, opts)
|
opts = function(_, opts)
|
||||||
opts.formatters = opts.formatters or {}
|
opts.formatters = opts.formatters or {}
|
||||||
opts.formatters_by_ft = opts.formatters_by_ft or {}
|
opts.formatters_by_ft = opts.formatters_by_ft or {}
|
||||||
opts.ensure_installed = opts.ensure_installed or {}
|
opts.ensure_installed = opts.ensure_installed or {}
|
||||||
-- vim.list_extend(opts.ensure_installed, { "stylua" })
|
-- vim.list_extend(opts.ensure_installed, { "stylua" })
|
||||||
opts.lang_maps = opts.lang_maps or {}
|
opts.lang_maps = opts.lang_maps or {}
|
||||||
opts.name_maps = opts.name_maps or {}
|
opts.name_maps = opts.name_maps or {}
|
||||||
opts.add_new = opts.add_new or {}
|
opts.add_new = opts.add_new or {}
|
||||||
opts.ignore = opts.ignore or {}
|
opts.ignore = opts.ignore or {}
|
||||||
opts.format_on_save = opts.format_on_save or true
|
opts.format_on_save = opts.format_on_save or true
|
||||||
opts.format_timeout_ms = opts.format_timeout_ms or 5000
|
opts.format_timeout_ms = opts.format_timeout_ms or 5000
|
||||||
end,
|
end,
|
||||||
config = function(_, opts)
|
config = function(_, opts)
|
||||||
require("auto-conform").setup(opts)
|
require("auto-conform").setup(opts)
|
||||||
-- other conform config
|
-- other conform config
|
||||||
local conform = require("conform")
|
local conform = require("conform")
|
||||||
if opts.format_on_save then
|
if opts.format_on_save then
|
||||||
conform.setup({
|
conform.setup({
|
||||||
format_on_save = {
|
format_on_save = {
|
||||||
lsp_fallback = true,
|
lsp_fallback = true,
|
||||||
timeout_ms = opts.format_timeout_ms or 5000,
|
timeout_ms = opts.format_timeout_ms or 5000,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
vim.keymap.set({ "n", "v" }, "<leader>lF", function()
|
vim.keymap.set({ "n", "v" }, "<leader>lF", function()
|
||||||
conform.format({
|
conform.format({
|
||||||
lsp_fallback = true,
|
lsp_fallback = true,
|
||||||
async = false,
|
async = false,
|
||||||
timeout_ms = opts.format_timeout_ms or 5000,
|
timeout_ms = opts.format_timeout_ms or 5000,
|
||||||
})
|
})
|
||||||
end, { desc = "Format file or range (in visual mode)" })
|
end, { desc = "Format file or range (in visual mode)" })
|
||||||
end,
|
end,
|
||||||
|
keys = {
|
||||||
|
{ "<leader>l", "", desc = " Lsp" },
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,10 +94,29 @@ local M = {
|
||||||
end
|
end
|
||||||
|
|
||||||
require("neotest").setup(opts)
|
require("neotest").setup(opts)
|
||||||
require("pcode.user.npmrun").setup("npm run dev")
|
local command = pcode.npm_commad
|
||||||
|
or {
|
||||||
|
dev = {
|
||||||
|
start = "NpmRunDev",
|
||||||
|
stop = "NpmStopDev",
|
||||||
|
cmd = "npm run dev",
|
||||||
|
},
|
||||||
|
prod = {
|
||||||
|
start = "NpmStart",
|
||||||
|
stop = "NpmStop",
|
||||||
|
cmd = "npm start",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
require("pcode.user.npmrun").setup(command, {
|
||||||
|
show_mapping = "<leader>nm",
|
||||||
|
hide_mapping = "<leader>nh",
|
||||||
|
width = 70,
|
||||||
|
height = 20,
|
||||||
|
})
|
||||||
end,
|
end,
|
||||||
-- stylua: ignore
|
-- stylua: ignore
|
||||||
keys = {
|
keys = {
|
||||||
|
{"<leader>n","",desc=" Npm"},
|
||||||
{ "<leader>T","",desc=" Test"},
|
{ "<leader>T","",desc=" Test"},
|
||||||
{ "<leader>Tt", function() require("neotest").run.run(vim.fn.expand("%")) end, desc = "Run File" },
|
{ "<leader>Tt", function() require("neotest").run.run(vim.fn.expand("%")) end, desc = "Run File" },
|
||||||
{ "<leader>Tr", function() require("neotest").run.run() end, desc = "Run Nearest" },
|
{ "<leader>Tr", function() require("neotest").run.run() end, desc = "Run Nearest" },
|
||||||
|
|
|
@ -3,6 +3,7 @@ return {
|
||||||
lazy = true,
|
lazy = true,
|
||||||
event = "VeryLazy",
|
event = "VeryLazy",
|
||||||
keys = {
|
keys = {
|
||||||
|
{ "<leader>u", "", desc = " Notify" },
|
||||||
{
|
{
|
||||||
"<leader>uN",
|
"<leader>uN",
|
||||||
function()
|
function()
|
||||||
|
|
|
@ -1,52 +1,112 @@
|
||||||
local function show_modal(text)
|
local last_modal = nil -- global modal manager instance
|
||||||
local buf = vim.api.nvim_create_buf(false, true) -- buffer untuk modal
|
|
||||||
|
|
||||||
vim.api.nvim_buf_set_lines(buf, 0, -1, false, { text, "", "Press q to close" })
|
local function modal_manager(opts)
|
||||||
|
opts = opts or {}
|
||||||
|
local win_id = nil
|
||||||
|
local buf_id = nil
|
||||||
|
local last_content = ""
|
||||||
|
local is_shown = false
|
||||||
|
|
||||||
local width = 50
|
local function close()
|
||||||
local height = 5
|
if win_id and vim.api.nvim_win_is_valid(win_id) then
|
||||||
local row = math.floor((vim.o.lines - height) / 2)
|
vim.api.nvim_win_close(win_id, true)
|
||||||
local col = math.floor((vim.o.columns - width) / 2)
|
win_id = nil
|
||||||
|
end
|
||||||
|
is_shown = false
|
||||||
|
end
|
||||||
|
|
||||||
local win = vim.api.nvim_open_win(buf, true, {
|
local function open(content)
|
||||||
relative = "editor",
|
local lines = vim.split(content or "", "\n")
|
||||||
width = width,
|
local width = opts.width or 70
|
||||||
height = height,
|
local height = math.min(opts.height or 15, #lines + 6)
|
||||||
row = row,
|
local row = math.floor((vim.o.lines - height) / 2)
|
||||||
col = col,
|
local col = math.floor((vim.o.columns - width) / 2)
|
||||||
style = "minimal",
|
if not buf_id or not vim.api.nvim_buf_is_valid(buf_id) then
|
||||||
border = "rounded",
|
buf_id = vim.api.nvim_create_buf(false, true)
|
||||||
})
|
vim.api.nvim_buf_set_keymap(buf_id, "n", "q", "", {
|
||||||
|
noremap = true,
|
||||||
|
silent = true,
|
||||||
|
callback = function()
|
||||||
|
close()
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
-- Pastikan buffer modifiable sebelum update, abaikan jika error (buffer deleted)
|
||||||
|
pcall(vim.api.nvim_buf_set_option, buf_id, "modifiable", true)
|
||||||
|
pcall(vim.api.nvim_buf_set_lines, buf_id, 0, -1, false, lines)
|
||||||
|
pcall(vim.api.nvim_buf_set_option, buf_id, "modifiable", false)
|
||||||
|
if not win_id or not vim.api.nvim_win_is_valid(win_id) then
|
||||||
|
win_id = vim.api.nvim_open_win(buf_id, true, {
|
||||||
|
relative = "editor",
|
||||||
|
width = width,
|
||||||
|
height = height,
|
||||||
|
row = row,
|
||||||
|
col = col,
|
||||||
|
style = "minimal",
|
||||||
|
border = "rounded",
|
||||||
|
})
|
||||||
|
end
|
||||||
|
is_shown = true
|
||||||
|
end
|
||||||
|
|
||||||
-- keymap untuk menutup modal dengan 'q'
|
local function show()
|
||||||
vim.api.nvim_buf_set_keymap(buf, "n", "q", "", {
|
if is_shown then
|
||||||
noremap = true,
|
return
|
||||||
silent = true,
|
end
|
||||||
callback = function()
|
if last_content ~= "" then
|
||||||
vim.api.nvim_win_close(win, true)
|
open(last_content)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function hide()
|
||||||
|
close()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function update(content)
|
||||||
|
last_content = content
|
||||||
|
if is_shown and buf_id and vim.api.nvim_buf_is_valid(buf_id) then
|
||||||
|
local lines = vim.split(content or "", "\n")
|
||||||
|
pcall(vim.api.nvim_buf_set_option, buf_id, "modifiable", true)
|
||||||
|
pcall(vim.api.nvim_buf_set_lines, buf_id, 0, -1, false, lines)
|
||||||
|
pcall(vim.api.nvim_buf_set_option, buf_id, "modifiable", false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function set_content(content)
|
||||||
|
last_content = content
|
||||||
|
if is_shown then
|
||||||
|
update(content)
|
||||||
|
else
|
||||||
|
open(content)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
show = show,
|
||||||
|
hide = hide,
|
||||||
|
update = update,
|
||||||
|
close = close,
|
||||||
|
set_content = set_content,
|
||||||
|
is_shown = function()
|
||||||
|
return is_shown
|
||||||
end,
|
end,
|
||||||
})
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
M._last_modal = nil
|
||||||
local main_cmd = "npm run dev" -- default
|
|
||||||
|
|
||||||
local function log(message, level)
|
local function log(message, level)
|
||||||
vim.notify(string.format("npm-dev-runner: %s", message), vim.log.levels[level])
|
vim.notify(string.format("npm-dev-runner: %s", message), vim.log.levels[level])
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Cache: dir -> { job_id=..., buf=... }
|
local function find_cached_dir(dir, cache)
|
||||||
local job_cache = {}
|
|
||||||
|
|
||||||
local function find_cached_dir(dir)
|
|
||||||
if not dir then
|
if not dir then
|
||||||
vim.notify("npm-dev-runner: No directory provided to find_cached_dir()", vim.log.levels.ERROR)
|
vim.notify("npm-dev-runner: No directory provided to find_cached_dir()", vim.log.levels.ERROR)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local cur = dir
|
local cur = dir
|
||||||
while not job_cache[cur] do
|
while not cache[cur] do
|
||||||
if cur == "/" or string.match(cur, "^[A-Z]:\\$") then
|
if cur == "/" or string.match(cur, "^[A-Z]:\\$") then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -55,119 +115,131 @@ local function find_cached_dir(dir)
|
||||||
return cur
|
return cur
|
||||||
end
|
end
|
||||||
|
|
||||||
local function is_running(dir)
|
local function is_running(dir, cache)
|
||||||
local cached_dir = find_cached_dir(dir)
|
local cached_dir = find_cached_dir(dir, cache)
|
||||||
return cached_dir and job_cache[cached_dir]
|
return cached_dir and cache[cached_dir]
|
||||||
end
|
end
|
||||||
|
|
||||||
local function is_windows()
|
local function is_windows()
|
||||||
return vim.loop.os_uname().version:match("Windows")
|
return vim.loop.os_uname().version:match("Windows")
|
||||||
end
|
end
|
||||||
|
|
||||||
M.toggle = function(dir)
|
local default_opts = {
|
||||||
local running = is_running(dir)
|
show_mapping = "<leader>nm",
|
||||||
if not running then
|
hide_mapping = "<leader>nh",
|
||||||
M.start(dir)
|
}
|
||||||
return
|
|
||||||
end
|
|
||||||
M.stop(dir)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Fungsi setup menerima argumen command utama, contoh: require("npmrun").setup("pnpm dev")
|
M.setup = function(command_table, opts)
|
||||||
M.setup = function(cmd)
|
opts = vim.tbl_deep_extend("force", {}, default_opts, opts or {})
|
||||||
main_cmd = cmd or "npm run dev"
|
command_table = command_table or {}
|
||||||
if not vim.fn.executable(main_cmd:match("%S+")) then
|
|
||||||
log(main_cmd .. " is not executable. Make sure it is installed and in PATH.", "ERROR")
|
-- Keymap global, pakai modal terakhir yang aktif
|
||||||
return
|
if opts.show_mapping then
|
||||||
|
vim.keymap.set("n", opts.show_mapping, function()
|
||||||
|
if last_modal then
|
||||||
|
last_modal.show()
|
||||||
|
end
|
||||||
|
end, { desc = "Show last NPM modal output" })
|
||||||
|
end
|
||||||
|
if opts.hide_mapping then
|
||||||
|
vim.keymap.set("n", opts.hide_mapping, function()
|
||||||
|
if last_modal then
|
||||||
|
last_modal.hide()
|
||||||
|
end
|
||||||
|
end, { desc = "Hide last NPM modal output" })
|
||||||
end
|
end
|
||||||
|
|
||||||
local function find_dir(args)
|
for key, conf in pairs(command_table) do
|
||||||
local dir = args ~= "" and args or "%:p:h"
|
local start_cmd = conf.start or ("NpmRun" .. key)
|
||||||
return vim.fn.expand(vim.fn.fnamemodify(vim.fn.expand(dir), ":p"))
|
local stop_cmd = conf.stop or ("NpmStop" .. key)
|
||||||
end
|
local cmd_str = conf.cmd or "npm run dev"
|
||||||
|
local cache = {}
|
||||||
|
|
||||||
vim.api.nvim_create_user_command("DevStart", function(opts)
|
local function do_start(dir)
|
||||||
M.start(find_dir(opts.args))
|
if is_running(dir, cache) then
|
||||||
end, { nargs = "?" })
|
log(cmd_str .. " already running", "INFO")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
vim.api.nvim_create_user_command("DevStop", function(opts)
|
local all_output = {}
|
||||||
M.stop(find_dir(opts.args))
|
local modal = modal_manager(opts)
|
||||||
end, { nargs = "?" })
|
last_modal = modal
|
||||||
|
M._last_modal = modal
|
||||||
|
|
||||||
vim.api.nvim_create_user_command("DevToggle", function(opts)
|
local cmd
|
||||||
M.toggle(find_dir(opts.args))
|
if is_windows() then
|
||||||
end, { nargs = "?" })
|
cmd = { "cmd.exe", "/C" }
|
||||||
end
|
for word in cmd_str:gmatch("%S+") do
|
||||||
|
table.insert(cmd, word)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
cmd = {}
|
||||||
|
for word in cmd_str:gmatch("%S+") do
|
||||||
|
table.insert(cmd, word)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
M.start = function(dir)
|
local function process_lines(lines)
|
||||||
if is_running(dir) then
|
if not lines then
|
||||||
log(main_cmd .. " already running", "INFO")
|
return
|
||||||
return
|
end
|
||||||
end
|
for _, l in ipairs(lines) do
|
||||||
|
table.insert(all_output, tostring(l))
|
||||||
|
end
|
||||||
|
modal.set_content(table.concat(all_output, "\n"))
|
||||||
|
end
|
||||||
|
|
||||||
local cmd
|
local job_id = vim.fn.jobstart(cmd, {
|
||||||
if is_windows() then
|
cwd = dir,
|
||||||
cmd = { "cmd.exe", "/C" }
|
stdout_buffered = false,
|
||||||
for word in main_cmd:gmatch("%S+") do
|
stderr_buffered = false,
|
||||||
table.insert(cmd, word)
|
on_stdout = function(_, data)
|
||||||
end
|
process_lines(data)
|
||||||
else
|
end,
|
||||||
cmd = {}
|
on_stderr = function(_, data)
|
||||||
for word in main_cmd:gmatch("%S+") do
|
process_lines(data)
|
||||||
table.insert(cmd, word)
|
end,
|
||||||
end
|
on_exit = function(_, code)
|
||||||
end
|
table.insert(all_output, ("Process exited with code: %d"):format(code))
|
||||||
|
modal.set_content(table.concat(all_output, "\n"))
|
||||||
|
cache[dir] = nil
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
local function append_to_buffer(lines)
|
cache[dir] = { job_id = job_id, modal = modal }
|
||||||
if not lines then
|
log(cmd_str .. " started", "INFO")
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, line in ipairs(lines) do
|
local function do_stop(dir)
|
||||||
if line ~= "" then
|
local running = is_running(dir, cache)
|
||||||
line = tostring(line)
|
if running then
|
||||||
line = line:gsub("^%s*(.-)%s*$", "%1")
|
local cached_dir = find_cached_dir(dir, cache)
|
||||||
if string.find(line, "http") then
|
if cached_dir then
|
||||||
show_modal(line)
|
local job_entry = cache[cached_dir]
|
||||||
break
|
if job_entry then
|
||||||
|
vim.fn.jobstop(job_entry.job_id)
|
||||||
|
if job_entry.modal then
|
||||||
|
job_entry.modal.close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
cache[cached_dir] = nil
|
||||||
|
log(cmd_str .. " stopped", "INFO")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
local job_id = vim.fn.jobstart(cmd, {
|
local function find_dir(args)
|
||||||
cwd = dir,
|
local dir = args ~= "" and args or "%:p:h"
|
||||||
stdout_buffered = false, -- streaming mode
|
return vim.fn.expand(vim.fn.fnamemodify(vim.fn.expand(dir), ":p"))
|
||||||
stderr_buffered = false,
|
|
||||||
on_stdout = function(_, data)
|
|
||||||
append_to_buffer(data)
|
|
||||||
end,
|
|
||||||
on_stderr = function(_, data)
|
|
||||||
append_to_buffer(vim.tbl_map(function(l)
|
|
||||||
return "[ERR] " .. l
|
|
||||||
end, data))
|
|
||||||
end,
|
|
||||||
on_exit = function(_, _)
|
|
||||||
job_cache[dir] = nil
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
job_cache[dir] = { job_id = job_id }
|
|
||||||
log(main_cmd .. " started", "INFO")
|
|
||||||
end
|
|
||||||
|
|
||||||
M.stop = function(dir)
|
|
||||||
local running = is_running(dir)
|
|
||||||
if running then
|
|
||||||
local cached_dir = find_cached_dir(dir)
|
|
||||||
if cached_dir then
|
|
||||||
local job_entry = job_cache[cached_dir]
|
|
||||||
if job_entry then
|
|
||||||
vim.fn.jobstop(job_entry.job_id)
|
|
||||||
end
|
|
||||||
job_cache[cached_dir] = nil
|
|
||||||
log(main_cmd .. " stopped", "INFO")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
vim.api.nvim_create_user_command(start_cmd, function(opts)
|
||||||
|
do_start(find_dir(opts.args))
|
||||||
|
end, { nargs = "?" })
|
||||||
|
|
||||||
|
vim.api.nvim_create_user_command(stop_cmd, function(opts)
|
||||||
|
do_stop(find_dir(opts.args))
|
||||||
|
end, { nargs = "?" })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
174
lua/pcode/user/npmrun3.lua
Normal file
174
lua/pcode/user/npmrun3.lua
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
local function show_modal(text)
|
||||||
|
local buf = vim.api.nvim_create_buf(false, true) -- buffer untuk modal
|
||||||
|
|
||||||
|
vim.api.nvim_buf_set_lines(buf, 0, -1, false, { text, "", "Press q to close" })
|
||||||
|
|
||||||
|
local width = 50
|
||||||
|
local height = 5
|
||||||
|
local row = math.floor((vim.o.lines - height) / 2)
|
||||||
|
local col = math.floor((vim.o.columns - width) / 2)
|
||||||
|
|
||||||
|
local win = vim.api.nvim_open_win(buf, true, {
|
||||||
|
relative = "editor",
|
||||||
|
width = width,
|
||||||
|
height = height,
|
||||||
|
row = row,
|
||||||
|
col = col,
|
||||||
|
style = "minimal",
|
||||||
|
border = "rounded",
|
||||||
|
})
|
||||||
|
|
||||||
|
-- keymap untuk menutup modal dengan 'q'
|
||||||
|
vim.api.nvim_buf_set_keymap(buf, "n", "q", "", {
|
||||||
|
noremap = true,
|
||||||
|
silent = true,
|
||||||
|
callback = function()
|
||||||
|
vim.api.nvim_win_close(win, true)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
local main_cmd = "npm run dev" -- default
|
||||||
|
|
||||||
|
local function log(message, level)
|
||||||
|
vim.notify(string.format("npm-dev-runner: %s", message), vim.log.levels[level])
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Cache: dir -> { job_id=..., buf=... }
|
||||||
|
local job_cache = {}
|
||||||
|
|
||||||
|
local function find_cached_dir(dir)
|
||||||
|
if not dir then
|
||||||
|
vim.notify("npm-dev-runner: No directory provided to find_cached_dir()", vim.log.levels.ERROR)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local cur = dir
|
||||||
|
while not job_cache[cur] do
|
||||||
|
if cur == "/" or string.match(cur, "^[A-Z]:\\$") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
cur = vim.fn.fnamemodify(cur, ":h")
|
||||||
|
end
|
||||||
|
return cur
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_running(dir)
|
||||||
|
local cached_dir = find_cached_dir(dir)
|
||||||
|
return cached_dir and job_cache[cached_dir]
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_windows()
|
||||||
|
return vim.loop.os_uname().version:match("Windows")
|
||||||
|
end
|
||||||
|
|
||||||
|
M.toggle = function(dir)
|
||||||
|
local running = is_running(dir)
|
||||||
|
if not running then
|
||||||
|
M.start(dir)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
M.stop(dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Fungsi setup menerima argumen command utama, contoh: require("npmrun").setup("pnpm dev")
|
||||||
|
M.setup = function(cmd)
|
||||||
|
main_cmd = cmd or "npm run dev"
|
||||||
|
if not vim.fn.executable(main_cmd:match("%S+")) then
|
||||||
|
log(main_cmd .. " is not executable. Make sure it is installed and in PATH.", "ERROR")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local function find_dir(args)
|
||||||
|
local dir = args ~= "" and args or "%:p:h"
|
||||||
|
return vim.fn.expand(vim.fn.fnamemodify(vim.fn.expand(dir), ":p"))
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.api.nvim_create_user_command("DevStart", function(opts)
|
||||||
|
M.start(find_dir(opts.args))
|
||||||
|
end, { nargs = "?" })
|
||||||
|
|
||||||
|
vim.api.nvim_create_user_command("DevStop", function(opts)
|
||||||
|
M.stop(find_dir(opts.args))
|
||||||
|
end, { nargs = "?" })
|
||||||
|
|
||||||
|
vim.api.nvim_create_user_command("DevToggle", function(opts)
|
||||||
|
M.toggle(find_dir(opts.args))
|
||||||
|
end, { nargs = "?" })
|
||||||
|
end
|
||||||
|
|
||||||
|
M.start = function(dir)
|
||||||
|
if is_running(dir) then
|
||||||
|
log(main_cmd .. " already running", "INFO")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local cmd
|
||||||
|
if is_windows() then
|
||||||
|
cmd = { "cmd.exe", "/C" }
|
||||||
|
for word in main_cmd:gmatch("%S+") do
|
||||||
|
table.insert(cmd, word)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
cmd = {}
|
||||||
|
for word in main_cmd:gmatch("%S+") do
|
||||||
|
table.insert(cmd, word)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function append_to_buffer(lines)
|
||||||
|
if not lines then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, line in ipairs(lines) do
|
||||||
|
if line ~= "" then
|
||||||
|
line = tostring(line)
|
||||||
|
line = line:gsub("^%s*(.-)%s*$", "%1")
|
||||||
|
if string.find(line, "http") then
|
||||||
|
show_modal(line)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local job_id = vim.fn.jobstart(cmd, {
|
||||||
|
cwd = dir,
|
||||||
|
stdout_buffered = false, -- streaming mode
|
||||||
|
stderr_buffered = false,
|
||||||
|
on_stdout = function(_, data)
|
||||||
|
append_to_buffer(data)
|
||||||
|
end,
|
||||||
|
on_stderr = function(_, data)
|
||||||
|
append_to_buffer(vim.tbl_map(function(l)
|
||||||
|
return "[ERR] " .. l
|
||||||
|
end, data))
|
||||||
|
end,
|
||||||
|
on_exit = function(_, _)
|
||||||
|
job_cache[dir] = nil
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
job_cache[dir] = { job_id = job_id }
|
||||||
|
log(main_cmd .. " started", "INFO")
|
||||||
|
end
|
||||||
|
|
||||||
|
M.stop = function(dir)
|
||||||
|
local running = is_running(dir)
|
||||||
|
if running then
|
||||||
|
local cached_dir = find_cached_dir(dir)
|
||||||
|
if cached_dir then
|
||||||
|
local job_entry = job_cache[cached_dir]
|
||||||
|
if job_entry then
|
||||||
|
vim.fn.jobstop(job_entry.job_id)
|
||||||
|
end
|
||||||
|
job_cache[cached_dir] = nil
|
||||||
|
log(main_cmd .. " stopped", "INFO")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
158
lua/pcode/user/npmrun4.lua
Normal file
158
lua/pcode/user/npmrun4.lua
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
local function show_modal(text)
|
||||||
|
local buf = vim.api.nvim_create_buf(false, true)
|
||||||
|
vim.api.nvim_buf_set_lines(buf, 0, -1, false, { text, "", "Press q to close" })
|
||||||
|
|
||||||
|
local width = 50
|
||||||
|
local height = 5
|
||||||
|
local row = math.floor((vim.o.lines - height) / 2)
|
||||||
|
local col = math.floor((vim.o.columns - width) / 2)
|
||||||
|
|
||||||
|
local win = vim.api.nvim_open_win(buf, true, {
|
||||||
|
relative = "editor",
|
||||||
|
width = width,
|
||||||
|
height = height,
|
||||||
|
row = row,
|
||||||
|
col = col,
|
||||||
|
style = "minimal",
|
||||||
|
border = "rounded",
|
||||||
|
})
|
||||||
|
|
||||||
|
vim.api.nvim_buf_set_keymap(buf, "n", "q", "", {
|
||||||
|
noremap = true,
|
||||||
|
silent = true,
|
||||||
|
callback = function()
|
||||||
|
vim.api.nvim_win_close(win, true)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
local function log(message, level)
|
||||||
|
vim.notify(string.format("npm-dev-runner: %s", message), vim.log.levels[level])
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fungsi untuk mencari cache dir (supaya stop/start tetap per dir/project)
|
||||||
|
local function find_cached_dir(dir, cache)
|
||||||
|
if not dir then
|
||||||
|
vim.notify("npm-dev-runner: No directory provided to find_cached_dir()", vim.log.levels.ERROR)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local cur = dir
|
||||||
|
while not cache[cur] do
|
||||||
|
if cur == "/" or string.match(cur, "^[A-Z]:\\$") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
cur = vim.fn.fnamemodify(cur, ":h")
|
||||||
|
end
|
||||||
|
return cur
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fungsi cek proses running
|
||||||
|
local function is_running(dir, cache)
|
||||||
|
local cached_dir = find_cached_dir(dir, cache)
|
||||||
|
return cached_dir and cache[cached_dir]
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_windows()
|
||||||
|
return vim.loop.os_uname().version:match("Windows")
|
||||||
|
end
|
||||||
|
|
||||||
|
M.setup = function(command_table)
|
||||||
|
command_table = command_table or {}
|
||||||
|
for key, conf in pairs(command_table) do
|
||||||
|
local start_cmd = conf.start or ("NpmRun" .. key)
|
||||||
|
local stop_cmd = conf.stop or ("NpmStop" .. key)
|
||||||
|
local cmd_str = conf.cmd or "npm run dev"
|
||||||
|
|
||||||
|
-- cache khusus untuk mode ini
|
||||||
|
local cache = {}
|
||||||
|
|
||||||
|
-- Fungsi Start
|
||||||
|
local function do_start(dir)
|
||||||
|
if is_running(dir, cache) then
|
||||||
|
log(cmd_str .. " already running", "INFO")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local cmd
|
||||||
|
if is_windows() then
|
||||||
|
cmd = { "cmd.exe", "/C" }
|
||||||
|
for word in cmd_str:gmatch("%S+") do
|
||||||
|
table.insert(cmd, word)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
cmd = {}
|
||||||
|
for word in cmd_str:gmatch("%S+") do
|
||||||
|
table.insert(cmd, word)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function process_lines(lines)
|
||||||
|
if not lines then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
for _, line in ipairs(lines) do
|
||||||
|
if line ~= "" then
|
||||||
|
local str = tostring(line):gsub("^%s*(.-)%s*$", "%1")
|
||||||
|
if string.find(str, "http") then
|
||||||
|
show_modal(str)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local job_id = vim.fn.jobstart(cmd, {
|
||||||
|
cwd = dir,
|
||||||
|
stdout_buffered = false,
|
||||||
|
stderr_buffered = false,
|
||||||
|
on_stdout = function(_, data)
|
||||||
|
process_lines(data)
|
||||||
|
end,
|
||||||
|
on_stderr = function(_, data)
|
||||||
|
process_lines(vim.tbl_map(function(l)
|
||||||
|
return "[ERR] " .. l
|
||||||
|
end, data))
|
||||||
|
end,
|
||||||
|
on_exit = function(_, _)
|
||||||
|
cache[dir] = nil
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
cache[dir] = { job_id = job_id }
|
||||||
|
log(cmd_str .. " started", "INFO")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fungsi Stop
|
||||||
|
local function do_stop(dir)
|
||||||
|
local running = is_running(dir, cache)
|
||||||
|
if running then
|
||||||
|
local cached_dir = find_cached_dir(dir, cache)
|
||||||
|
if cached_dir then
|
||||||
|
local job_entry = cache[cached_dir]
|
||||||
|
if job_entry then
|
||||||
|
vim.fn.jobstop(job_entry.job_id)
|
||||||
|
end
|
||||||
|
cache[cached_dir] = nil
|
||||||
|
log(cmd_str .. " stopped", "INFO")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function find_dir(args)
|
||||||
|
local dir = args ~= "" and args or "%:p:h"
|
||||||
|
return vim.fn.expand(vim.fn.fnamemodify(vim.fn.expand(dir), ":p"))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Register Perintah
|
||||||
|
vim.api.nvim_create_user_command(start_cmd, function(opts)
|
||||||
|
do_start(find_dir(opts.args))
|
||||||
|
end, { nargs = "?" })
|
||||||
|
|
||||||
|
vim.api.nvim_create_user_command(stop_cmd, function(opts)
|
||||||
|
do_stop(find_dir(opts.args))
|
||||||
|
end, { nargs = "?" })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
Loading…
Add table
Add a link
Reference in a new issue