234 lines
8.0 KiB
Lua

local wezterm = require("wezterm")
local session_manager = {}
local os = wezterm.target_triple
--- Displays a notification in WezTerm.
-- @param message string: The notification message to be displayed.
local function display_notification(message)
wezterm.log_info(message)
-- Additional code to display a GUI notification can be added here if needed
end
--- Retrieves the current workspace data from the active window.
-- @return table or nil: The workspace data table or nil if no active window is found.
local function retrieve_workspace_data(window)
local workspace_name = window:active_workspace()
local workspace_data = {
name = workspace_name,
tabs = {}
}
-- Iterate over tabs in the current window
for _, tab in ipairs(window:mux_window():tabs()) do
local tab_data = {
tab_id = tostring(tab:tab_id()),
panes = {}
}
-- Iterate over panes in the current tab
for _, pane_info in ipairs(tab:panes_with_info()) do
-- Collect pane details, including layout and process information
table.insert(tab_data.panes, {
pane_id = tostring(pane_info.pane:pane_id()),
index = pane_info.index,
is_active = pane_info.is_active,
is_zoomed = pane_info.is_zoomed,
left = pane_info.left,
top = pane_info.top,
width = pane_info.width,
height = pane_info.height,
pixel_width = pane_info.pixel_width,
pixel_height = pane_info.pixel_height,
cwd = tostring(pane_info.pane:get_current_working_dir()),
tty = tostring(pane_info.pane:get_foreground_process_name())
})
end
table.insert(workspace_data.tabs, tab_data)
end
return workspace_data
end
--- Saves data to a JSON file.
-- @param data table: The workspace data to be saved.
-- @param file_path string: The file path where the JSON file will be saved.
-- @return boolean: true if saving was successful, false otherwise.
local function save_to_json_file(data, file_path)
if not data then
wezterm.log_info("No workspace data to log.")
return false
end
local file = io.open(file_path, "w")
if file then
file:write(wezterm.json_encode(data))
file:close()
return true
else
return false
end
end
--- Recreates the workspace based on the provided data.
-- @param workspace_data table: The data structure containing the saved workspace state.
local function recreate_workspace(window, workspace_data)
local function extract_path_from_dir(working_directory)
if os == "x86_64-pc-windows-msvc" then
-- On Windows, transform 'file:///C:/path/to/dir' to 'C:/path/to/dir'
return working_directory:gsub("file:///", "")
elseif os == "x86_64-unknown-linux-gnu" then
-- On Linux, transform 'file://{computer-name}/home/{user}/path/to/dir' to '/home/{user}/path/to/dir'
return working_directory:gsub("^.*(/home/)", "/home/")
else
return working_directory:gsub("^.*(/Users/)", "/Users/")
end
end
if not workspace_data or not workspace_data.tabs then
wezterm.log_info("Invalid or empty workspace data provided.")
return
end
local tabs = window:mux_window():tabs()
if #tabs ~= 1 or #tabs[1]:panes() ~= 1 then
wezterm.log_info(
"Restoration can only be performed in a window with a single tab and a single pane, to prevent accidental data loss.")
return
end
local initial_pane = window:active_pane()
local foreground_process = initial_pane:get_foreground_process_name()
-- Check if the foreground process is a shell
if foreground_process:find("sh") or foreground_process:find("cmd.exe") or foreground_process:find("powershell.exe") or foreground_process:find("pwsh.exe") or foreground_process:find("nu") then
-- Safe to close
initial_pane:send_text("exit\r")
else
wezterm.log_info("Active program detected. Skipping exit command for initial pane.")
end
-- Recreate tabs and panes from the saved state
for _, tab_data in ipairs(workspace_data.tabs) do
local cwd_uri = tab_data.panes[1].cwd
local cwd_path = extract_path_from_dir(cwd_uri)
local new_tab = window:mux_window():spawn_tab({ cwd = cwd_path })
if not new_tab then
wezterm.log_info("Failed to create a new tab.")
break
end
-- Activate the new tab before creating panes
new_tab:activate()
-- Recreate panes within this tab
for j, pane_data in ipairs(tab_data.panes) do
local new_pane
if j == 1 then
new_pane = new_tab:active_pane()
else
local direction = 'Right'
if pane_data.left == tab_data.panes[j - 1].left then
direction = 'Bottom'
end
new_pane = new_tab:active_pane():split({
direction = direction,
cwd = extract_path_from_dir(pane_data.cwd)
})
end
if not new_pane then
wezterm.log_info("Failed to create a new pane.")
break
end
-- Restore TTY for Neovim on Linux
-- NOTE: cwd is handled differently on windows. maybe extend functionality for windows later
-- This could probably be handled better in general
if not (os == "x86_64-pc-windows-msvc") then
if not (os == "x86_64-pc-windows-msvc") and pane_data.tty:sub(- #"/bin/nvim") == "/bin/nvim" then
new_pane:send_text(pane_data.tty .. " ." .. "\n")
else
-- TODO - With running npm commands (e.g a running web client) this seems to execute Node, without the arguments
new_pane:send_text(pane_data.tty .. "\n")
end
end
end
end
wezterm.log_info("Workspace recreated with new tabs and panes based on saved state.")
return true
end
--- Loads data from a JSON file.
-- @param file_path string: The file path from which the JSON data will be loaded.
-- @return table or nil: The loaded data as a Lua table, or nil if loading failed.
local function load_from_json_file(file_path)
local file = io.open(file_path, "r")
if not file then
wezterm.log_info("Failed to open file: " .. file_path)
return nil
end
local file_content = file:read("*a")
file:close()
local data = wezterm.json_parse(file_content)
if not data then
wezterm.log_info("Failed to parse JSON data from file: " .. file_path)
end
return data
end
--- Loads the saved json file matching the current workspace.
function session_manager.restore_state(window)
local workspace_name = window:active_workspace()
local file_path = wezterm.home_dir ..
"/.config/wezterm/wezterm-session-manager/wezterm_state_" .. workspace_name .. ".json"
local workspace_data = load_from_json_file(file_path)
if not workspace_data then
window:toast_notification('WezTerm',
'Workspace state file not found for workspace: ' .. workspace_name, nil, 4000)
return
end
if recreate_workspace(window, workspace_data) then
window:toast_notification('WezTerm', 'Workspace state loaded for workspace: ' .. workspace_name,
nil, 4000)
else
window:toast_notification('WezTerm', 'Workspace state loading failed for workspace: ' .. workspace_name,
nil, 4000)
end
end
--- Allows to select which workspace to load
function session_manager.load_state(window)
-- TODO: Implement
-- Placeholder for user selection logic
-- ...
-- TODO: Call the function recreate_workspace(workspace_data) to recreate the workspace
-- Placeholder for recreation logic...
end
--- Orchestrator function to save the current workspace state.
-- Collects workspace data, saves it to a JSON file, and displays a notification.
function session_manager.save_state(window)
local data = retrieve_workspace_data(window)
-- Construct the file path based on the workspace name
local file_path = wezterm.home_dir .. "/.config/wezterm/wezterm-session-manager/wezterm_state_" .. data.name .. ".json"
-- Save the workspace data to a JSON file and display the appropriate notification
if save_to_json_file(data, file_path) then
window:toast_notification('WezTerm Session Manager', 'Workspace state saved successfully', nil, 4000)
else
window:toast_notification('WezTerm Session Manager', 'Failed to save workspace state', nil, 4000)
end
end
return session_manager