Skip to content

Commit 9f208af

Browse files
committed
Creating EcaLogs cmd
1 parent a980b02 commit 9f208af

File tree

4 files changed

+384
-220
lines changed

4 files changed

+384
-220
lines changed

lua/eca/api.lua

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
local Utils = require("eca.utils")
22

3+
-- Load nui.nvim components for floating windows
4+
local Popup = require("nui.popup")
5+
36
---@class eca.Api
47
local M = {}
58

@@ -388,4 +391,125 @@ function M.clear_todos()
388391
end
389392
end
390393

394+
-- Keep reference to logs popup globally to reuse it
395+
local logs_popup = nil
396+
397+
function M.show_logs()
398+
local eca = require("eca")
399+
400+
if not eca.server then
401+
Utils.warn("ECA server not initialized")
402+
return
403+
end
404+
405+
local logs = eca.server:get_logs()
406+
407+
if #logs == 0 then
408+
Utils.info("No ECA server logs available")
409+
return
410+
end
411+
412+
-- Format logs
413+
local lines = {}
414+
415+
for _, log_entry in ipairs(logs) do
416+
-- Split message by newlines to handle multi-line log entries
417+
local message_lines = vim.split(log_entry.message, "\n", { plain = true })
418+
419+
for i, message_line in ipairs(message_lines) do
420+
if i == 1 then
421+
-- First line gets full timestamp and level
422+
local formatted_line = string.format("[%s] %s: %s",
423+
log_entry.timestamp,
424+
log_entry.level,
425+
message_line)
426+
table.insert(lines, formatted_line)
427+
else
428+
-- Continuation lines are indented
429+
table.insert(lines, " " .. message_line)
430+
end
431+
end
432+
end
433+
434+
if #logs > 0 then
435+
table.insert(lines, "")
436+
table.insert(lines, string.format("--- %d log entries ---", #logs))
437+
end
438+
439+
-- If popup already exists and is mounted, update content
440+
if logs_popup and logs_popup.winid and vim.api.nvim_win_is_valid(logs_popup.winid) then
441+
-- Update existing popup content
442+
vim.api.nvim_set_option_value("modifiable", true, { buf = logs_popup.bufnr })
443+
vim.api.nvim_buf_set_lines(logs_popup.bufnr, 0, -1, false, lines)
444+
vim.api.nvim_set_option_value("modifiable", false, { buf = logs_popup.bufnr })
445+
446+
-- Jump to end to show latest logs
447+
vim.api.nvim_win_set_cursor(logs_popup.winid, {#lines, 0})
448+
Utils.info("Updated ECA server logs (" .. #logs .. " entries)")
449+
return
450+
end
451+
452+
-- Calculate popup size (responsive)
453+
local width = math.floor(vim.o.columns * 0.8)
454+
local height = math.floor(vim.o.lines * 0.7)
455+
456+
-- Create new floating popup
457+
logs_popup = Popup({
458+
enter = true,
459+
focusable = true,
460+
border = {
461+
style = "rounded",
462+
text = {
463+
top = " 📋 ECA Server Logs ",
464+
top_align = "center",
465+
},
466+
},
467+
position = "50%",
468+
size = {
469+
width = width,
470+
height = height,
471+
},
472+
buf_options = {
473+
buftype = "nofile",
474+
bufhidden = "hide",
475+
swapfile = false,
476+
modifiable = true,
477+
filetype = "log",
478+
},
479+
win_options = {
480+
wrap = false,
481+
number = false,
482+
relativenumber = false,
483+
signcolumn = "no",
484+
cursorline = true,
485+
},
486+
})
487+
488+
-- Mount the popup
489+
logs_popup:mount()
490+
491+
-- Set content
492+
vim.api.nvim_buf_set_lines(logs_popup.bufnr, 0, -1, false, lines)
493+
vim.api.nvim_set_option_value("modifiable", false, { buf = logs_popup.bufnr })
494+
495+
-- Jump to end to show latest logs
496+
vim.api.nvim_win_set_cursor(logs_popup.winid, {#lines, 0})
497+
498+
-- Setup close keymaps
499+
logs_popup:map("n", "q", function()
500+
logs_popup:unmount()
501+
end, { noremap = true, silent = true })
502+
503+
logs_popup:map("n", "<Esc>", function()
504+
logs_popup:unmount()
505+
end, { noremap = true, silent = true })
506+
507+
-- Clean up reference when popup is closed
508+
logs_popup:on("BufWinLeave", function()
509+
logs_popup = nil
510+
end)
511+
512+
Utils.info("Opened ECA server logs (" .. #logs .. " entries) - Press 'q' or <Esc> to close")
513+
end
514+
391515
return M

lua/eca/commands.lua

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,12 @@ function M.setup()
183183
desc = "Show ECA server status with details",
184184
})
185185

186+
vim.api.nvim_create_user_command("EcaLogs", function()
187+
require("eca.api").show_logs()
188+
end, {
189+
desc = "Open ECA server logs in a new buffer",
190+
})
191+
186192
vim.api.nvim_create_user_command("EcaSend", function(opts)
187193
if opts.args and opts.args ~= "" then
188194
require("eca.api").send_message(opts.args)

lua/eca/server.lua

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ local PathFinder = require("eca.path_finder")
1212
---@field private _server_capabilities? table Server capabilities
1313
---@field private _chat_id? string Current chat ID
1414
---@field private _path_finder eca.PathFinder Server path finder
15+
---@field private _logs table Array of log entries
1516
local M = {}
1617
M.__index = M
1718

@@ -32,6 +33,7 @@ function M:new(opts)
3233
instance._on_started = opts.on_started
3334
instance._on_status_changed = opts.on_status_changed
3435
instance._path_finder = PathFinder:new()
36+
instance._logs = {}
3537
return instance
3638
end
3739

@@ -53,6 +55,29 @@ function M:is_running()
5355
return self._status == ServerStatus.Running
5456
end
5557

58+
---@param level string Log level (INFO, WARN, ERROR, DEBUG)
59+
---@param message string Log message
60+
function M:_add_log(level, message)
61+
local timestamp = os.date("%Y-%m-%d %H:%M:%S")
62+
local log_entry = {
63+
timestamp = timestamp,
64+
level = level,
65+
message = message
66+
}
67+
68+
table.insert(self._logs, log_entry)
69+
70+
-- Keep only last 1000 log entries to prevent memory issues
71+
if #self._logs > 1000 then
72+
table.remove(self._logs, 1)
73+
end
74+
end
75+
76+
---@return table Array of log entries
77+
function M:get_logs()
78+
return vim.deepcopy(self._logs)
79+
end
80+
5681
---@return table?
5782
function M:connection()
5883
return self._rpc
@@ -84,11 +109,14 @@ function M:start()
84109

85110
if not ok or not server_path then
86111
self:_change_status(ServerStatus.Failed)
87-
Utils.error("Could not find or download ECA server: " .. tostring(err))
112+
local error_msg = "Could not find or download ECA server: " .. tostring(err)
113+
Utils.error(error_msg)
114+
self:_add_log("ERROR", error_msg)
88115
return
89116
end
90117

91118
Utils.debug("Starting ECA server: " .. server_path)
119+
self:_add_log("INFO", "Starting ECA server: " .. server_path)
92120

93121
local args = { server_path, "server" }
94122
if Config.server_args and Config.server_args ~= "" then
@@ -109,9 +137,20 @@ function M:start()
109137
end,
110138
on_stderr = function(_, data, _)
111139
if data then
112-
local error_output = table.concat(data, "\n")
113-
if error_output and error_output ~= "" and error_output ~= "\n" then
140+
-- Filter out empty lines and join meaningful content
141+
local meaningful_lines = {}
142+
for _, line in ipairs(data) do
143+
local trimmed = line:match("^%s*(.-)%s*$") -- Trim whitespace
144+
if trimmed and trimmed ~= "" then
145+
table.insert(meaningful_lines, trimmed)
146+
end
147+
end
148+
149+
if #meaningful_lines > 0 then
150+
local error_output = table.concat(meaningful_lines, "\n")
114151
Utils.debug("ECA server stderr: " .. error_output)
152+
-- Capture logs for the logs buffer
153+
self:_add_log("SERVER", error_output)
115154
end
116155
end
117156
end,
@@ -134,11 +173,14 @@ function M:start()
134173

135174
if self._proc <= 0 then
136175
self:_change_status(ServerStatus.Failed)
137-
Utils.error("Failed to start ECA server process. Job ID: " .. tostring(self._proc))
176+
local error_msg = "Failed to start ECA server process. Job ID: " .. tostring(self._proc)
177+
Utils.error(error_msg)
178+
self:_add_log("ERROR", error_msg)
138179
return
139180
end
140181

141182
Utils.debug("ECA server started with job ID: " .. self._proc)
183+
self:_add_log("INFO", "ECA server process started successfully (Job ID: " .. self._proc .. ")")
142184

143185
-- Create RPC connection with the job ID
144186
self._rpc = RPC:new(self._proc)
@@ -200,18 +242,22 @@ function M:_initialize_server()
200242
}, function(err, result)
201243
if err then
202244
self:_change_status(ServerStatus.Failed)
203-
Utils.error("Failed to initialize ECA server: " .. tostring(err))
245+
local error_msg = "Failed to initialize ECA server: " .. tostring(err)
246+
Utils.error(error_msg)
247+
self:_add_log("ERROR", error_msg)
204248
return
205249
end
206250

207251
-- Store server capabilities
208252
if result then
209253
self._server_capabilities = result
210254
Utils.debug("Server capabilities: " .. vim.inspect(result))
255+
self:_add_log("INFO", "Server capabilities received and stored")
211256
end
212257

213258
self:_change_status(ServerStatus.Running)
214259
Utils.info("ECA server started successfully")
260+
self:_add_log("INFO", "ECA server initialized and running successfully")
215261

216262
-- Send initialized notification
217263
self._rpc:send_notification("initialized", {})
@@ -227,6 +273,8 @@ function M:stop()
227273
return
228274
end
229275

276+
self:_add_log("INFO", "Stopping ECA server...")
277+
230278
if self._rpc then
231279
self._rpc:send_request("shutdown", {}, function()
232280
self._rpc:send_notification("exit", {})
@@ -252,6 +300,7 @@ function M:stop()
252300
self._chat_id = nil
253301
self:_change_status(ServerStatus.Stopped)
254302
Utils.info("ECA server stopped")
303+
self:_add_log("INFO", "ECA server stopped successfully")
255304
end
256305

257306
---@param method string

0 commit comments

Comments
 (0)