equalsraf / neovim-qt

Neovim client library and GUI, in Qt5.
https://github.com/equalsraf/neovim-qt/wiki
ISC License
1.85k stars 171 forks source link

Application sessions: restoring application state #1026

Open hinell opened 1 year ago

hinell commented 1 year ago

Please read about QT Sessions here and here. This is more broad topic on user sessions in X11 systems on Linux platforms.

I request to make it possible for neovim-qt to automatically save the application state when the system is shut down and replicate it when it starts up so I don't have to run everytime the same commands.

Related issue:

jgehrig commented 1 year ago

I don't have any plans to implement this feature soon...

However, it looks like you've put some thought and research into this; feel free to open a Pull Request!

If you can provide a clean and simple implementation, I will gladly provide guidance, review code, and merge it.

My potential hesitance/concerns:

hinell commented 1 year ago

feel free to open a Pull Request!

@jgehrig Hi, thanks! I will consider to do this at some point for sure. We have had this feature in QT apps running under KDE DE for many years. It's been default X11-builtin for ages, take a look: X session manager. KDE folks even have implemented X.org's session server namely ksmserver. It's very useful feature as it makes devs more productive. When system is started up I just continue where I left.

We would need a mechanism to restore the nvim state too

I think this could be as simple as a few commands restoring opened files. It doesn't require anything sofisticated.

If this requires X11/Linux specific code

Qt provides a guideline on sessions implementation and they have this feature built-in into their framework. It doesn't provide it for Windows systems though because Windows doesn't have session manager like X11 or KDE do so it's going to be excluded anyway.

Upd: see list of plugins for session management below

equalsraf commented 1 year ago

Instead of trying to capture all details about what state needs to be saved, why not defer to the neovim configuration to handle the details. After all neovim has mechanisms for storing sessions (mksession) and preserve work data (swap-file, shada). And there are probably too many corner cases to address on our side (should you call :wa?).

From the Qt docs these are the supported signals,

commitDataRequest

this means saving all open files, after getting permission from the user. Furthermore you may want to provide a means by which the user can cancel the shutdown.

I think this is usually implemented as a popup dialog for the user to confirm/cancel shutdown.

saveStateStateRequest

For example, a text editor would create a temporary file that includes the current contents of its edit buffers, the location of the cursor and other aspects of the current editing session.

sounds a lot like swap-file.

QGuiApplication::isSessionRestored()/sessionId()

The session manager also provides a unique id so the application can, for example, save unique filenames on exit and restore them on startup.

RFC

Instead of trying to determine which context should be saved (or not), it is better to passthrough these calls to user callback functions (vimscript/lua) and allow those to handle the events.

For implementation purposes:

As an uninspired example, the user could

  1. on saveStateStateRequest call mksession, using the session id to save a unique session file, and write to swap/shada files
  2. on commitDataRequest, prompt the user for confirmation - using input() or some other method and save files (see caveats)
  3. at startup, if this is a restore session, the UI calls a callback with the session id and the callback decides what to do

Caveats

  1. Initialization order, need to call isSessionRestored and the user callback after ginit?
  2. The semantics of commitDataRequest are not clear to me ATM. It warns that no user interaction may be possible. If the manager does not allow it, we may be unable to redraw on screen.
  3. nvim-qt could provide the session manager with its argv at startup, so that it gets restarted on the new session (setRestartCommand) but i see two issues here
    1. our argv gets transformed because nvim-qt calls itself
    2. I don't know how the session manager expects the process to behave (should it fork, or not)
  4. Data cleanup. I'm not sure what is the correct way to implement DiscardCommand - I am tempted to just pass the responsibility to the user i.e. they may just want to call something like rm sessionfile.vim. However neovim itself could be running in some other machine. Conversely starting up nvim-qt just to clean session state sounds like a bad experience for the user
  5. at least for X11 the session manager supports other options, such as current directory and environment - current directory is probably important if nvim-qt was called from the command line. Based on the X11 docs, I'm not sure if the Qt api can set these (the types don't seem to match).

A better place to read on session management is probably X11 docs https://www.x.org/releases/X11R7.7/doc/libSM/xsmp.html. In addition Vim has an implementation for X11 but I think it does not implement restart/discard commands.

hinell commented 1 year ago

@equalsraf I agree, the native mksession or any other command may be utilized by users via init.lua configuration to manage session. This command saves Session.vim file.

... Instead of trying to determine which context should be saved (or not)

Yeah, nvim's native autocmds can also be used as hook (listener) into nvim session lifecycle from plugins.

Plugins list to manage sessions

A few weeks ago I've found there are plugins to manage sessions for neovim, some of them hook into neovim events and save sessions upon exit (i.e. olimorris/persisted), checkout:

equalsraf commented 1 year ago

An autocmd would work for saveStateStateRequest. But not for commitDataRequest because it may need to return data (cancelation). But maybe it is best to skip implementing commitDataRequest for starters.

For saveStateStateRequest

m_nvim->api0()->vim_command("doautocmd User GuiSessionSaveRequest");

and another for restoring the session startup GuiSessionRestore.

Also need a way to expose the session id, like a global var g:GuiSessionId. This can be set at startup from QGuiApplication::sessionId().

hinell commented 1 year ago

@equalsraf Yup. Passing sid (session Id) is important. The system may keep track of different sessions of different users.

upd: currently, for xterm in KDE I've session restored automatically. I think xterm have implemented some kind of session functionality that runs xterm with previous command.