Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide a way to save the current layout #3237

Open
jason0x43 opened this issue Mar 13, 2023 · 8 comments
Open

Provide a way to save the current layout #3237

jason0x43 opened this issue Mar 13, 2023 · 8 comments
Labels
enhancement New feature or request

Comments

@jason0x43
Copy link

jason0x43 commented Mar 13, 2023

Is your feature request related to a problem? Please describe.

One of the features I miss most from tmux when using Wezterm's native tabs and panes is the ability to save and later restore the active layout. This is useful when updating the terminal client, when restarting my laptop, or just when I want to close whatever I was doing for work on Friday and restore it on Monday.

This is related to #1949.

Describe the solution you'd like

Wezterm currently provides APIs to initialize a layout at startup. As far as I'm aware, though, there isn't straightforward way to save the current layout. That would be ideal -- a function that could be called, such as from a keybinding or some other event, that would save a snapshot of the domain to a file, and a complementary function that could restore such a snapshot.

Ideally, the snapshot would save at least the layout within each window (tabs and panes), tab and pane titles, and CWD in each pane. The ability to optionally save and restore the scroll buffers of all the panes would also be worthwhile.

I'm less concerned with saving window positions and sizes. Other tools make snapping windows into position easy enough; the worthwhile part would be restoring the general context of whatever I was working on when I saved the layout.

Describe alternatives you've considered

Use a terminal multiplexer than can save layouts, such as tmux with the resurrect plugin.

Additional context

@RazaGR
Copy link

RazaGR commented May 3, 2023

I want to add that it would be also great to have the ability to also save workspaces and what it was running

@wez any idea if this will be implemented? Thanks

@wez
Copy link
Owner

wez commented May 4, 2023

No one is working on this at this time. I have no plans to work on this any time soon. If you search for other issues around saving/restoring state you will discover the set of tricky concerns that need to be considered before attempting to implement this feature.

@vvzen
Copy link

vvzen commented Jun 7, 2023

+1, since this would be lovely and quite useful!

I have recently switched from iTerm2+tmux to just using wezterm on macOS and it has been a beautiful journey so far. The only missing piece right now is the fact that now I have to shut down my laptop to do an OS update and I'm gonna lose all of my carefully crafted tabs and workspaces, unless I find I way to serialize everything down and restore it later.

When in tmux-land, I was using https://github.com/tmux-plugins/tmux-resurrect .

Even if this is not being planned right now, do you think there could be a way to achieve something similar (hacking it away) by creating the required workspaces and tabs when the gui-startup event fires (https://wezfurlong.org/wezterm/config/lua/gui-events/gui-startup.html) ? I'm asking since I'm still kinda new and if you think it's not worth the effort I'll avoid even going in this rabbit hole :D

Thanks!

@wez
Copy link
Owner

wez commented Jun 7, 2023

That's exactly what the gui-startup event is for

@jason0x43
Copy link
Author

If you search for other issues around saving/restoring state you will discover the set of tricky concerns that need to be considered before attempting to implement this feature.

I did look at some existing issues, particularly #256, but I think those are asking for more than I'm looking for. My overall goal is just to do what tux-resurrect does -- save a layout, including scrollback text and CWDs, and then restore that later. I'm not concerned about window positions or sizes, just relative pane sizes and a few other things (tab titles, scrollback text, CWD). This functionality is very useful if a user needs to do something that would disrupt their terminal session, like rebooting or updating Wezterm.

The API already includes most of the necessary functionality -- you can walk through the list of windows, tabs, and panes, and save all that to a file, and create new windows, tabs, and panes. I think the main thing that's missing (or at least that I don't know how to do yet) is a way to save the scrollback buffer.

@jorgebef
Copy link

For whoever might be interested, I'm currently preparing just a couple of session management functions that are rather basic but fill my current needs:

local function getTableLn(tbl)
  local getN = 0
  for _ in pairs(tbl) do
    getN = getN + 1
  end
  return getN
end

-- Save JSON data a file
local function save_json(data, json_session_file)
  local f = assert(io.open(json_session_file, "w"))
  -- f:write(wezterm.json_encode(deepcopy(data)))
  -- f:write(table.tostring(data))
  f:write(JSON:encode(data))
  -- f:write("this should be the session info")
  f:close()
end

function M.save_session()
  return action_callback(function()
    -- os.execute([[wezterm cli list --format json > /Users/jbef/.config/wezterm/session2.json]])
    local sessions = {}
    for _, win in ipairs(mux.all_windows()) do
      local workspace = win:get_workspace()
      if sessions[workspace] == nil then
        sessions[workspace] = {}
      end
      wezterm.log_info(win:get_workspace())
      for _, tab in ipairs(win:tabs()) do
        local tabId = tostring(tab:tab_id())
        sessions[workspace][tabId] = {}
        for _, pane in ipairs(tab:panes_with_info()) do
          local paneId = tostring(pane.pane:pane_id())
          sessions[workspace][tabId][paneId] = {
            cwd = tostring(pane.pane:get_current_working_dir()):gsub(".*/Users", "/Users"),
            tty = tostring(pane.pane:get_foreground_process_name()),
          }
        end
      end
    end
    save_json(sessions, file2)
    -- wezterm.log_info(sessions)
  end)
end

function M.restore()
  -- ==========================
  -- check if file exists
  local openFile = io.open(file2, "r")
  if openFile == nil then
    return
  end
  --==========================

  local dump = openFile:read("*a")
  openFile:close()
  ---@type table
  local decoded_dump = JSON:decode(dump)

  -- Loop through Workspaces =====================================
  for workspace, wTable in pairs(decoded_dump) do
    -- initialize Tab counter for each workspace ===============
    local tCount = 0
    local _, _, window = mux.spawn_window({
      workspace = workspace,
      cwd = "~/",
    })
    -- Loop through Tabs ======================================
    for _, tTable in pairs(wTable) do
      local pCount = 0
      -- if getTableLn(wTable) > 1 and wTab:tab_id() ~= window:active_tab():tab_id() then
      if getTableLn(wTable) > 1 and tCount > 0 then
        local tab, pane, _ = window:spawn_tab({})
        tab:activate()
        pane:activate()
        tCount = tCount + 1
      else
        tCount = tCount + 1
      end

      -- initialize Pane counter for each tab ===============
      -- Loop through Panes ======================================
      for _, pTable in pairs(tTable) do
        -- -- Check if it's not the first tab ======================================
        if getTableLn(tTable) > 1 and pCount > 0 then
          window:active_pane():split({ cwd = pTable["cwd"] })
          pCount = pCount + 1
        else
          window:active_pane():send_text("cd " .. pTable["cwd"] .. "\r")
          pCount = pCount + 1
        end
      end
    end
  end
end

The save_session function produces a json that is stored in the provided path, and the restore function takes said json and reads it to produce an initial layout when called on 'gui-startup' as follows:

local session_manager = require("user.session-manager")
...
...
wezterm.on("gui-startup", function()
  session_manager.restore()
end)

The only thing I can't seem to put my finger on is that seemingly randomly, sometimes the initial tab created when calling mux.spawn_window() is placed in the first spot and sometimes not. This coincides with the times that it spawns with the default cwd or when it is called after the spawn_tab command, which I think should never happen provided they are nested for loops.

I have basic lua knowledge, so I might be completely missing something.
If anyone has a working solution or a way to improve the above mentioned one, I am very happy to seek alternatives!

Also, thank you Wez for your work, I seem to keep finding more and more utilities for Wezterm and replace many parts of my workflow that were slowing me or the performance down.

@danielcopper
Copy link

If anyone is interested i created something very similar. I'm open to collab on this.
https://github.com/danielcopper/wezterm-session-manager

@Destroy666x
Copy link
Contributor

Does anyone know a way to make any of these workarounds work automatically with window closing? I see no event for that, so impossible? I guess the closest would be to have a periodic function that saves the session ever few seconds.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

7 participants