Home

Awesome

easysession.el - Easily persist and restore your Emacs editing sessions

MELPA Build Status License

The easysession.el Emacs package is a lightweight session manager for Emacs that can persist and restore file editing buffers, indirect buffers/clones, Dired buffers, windows/splits, the built-in tab-bar (including tabs, their buffers, and windows), and Emacs frames. It offers a convenient and effortless way to manage Emacs editing sessions and utilizes built-in Emacs functions to persist and restore frames.

<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-refresh-toc -->

Table of Contents

<!-- markdown-toc end -->

Features

Key features include:

Installation

To install easysession from MELPA:

  1. If you haven't already done so, add MELPA repository to your Emacs configuration.

  2. Add the following code to your Emacs init file to install easysession from MELPA:

(use-package easysession
  :ensure t
  :custom
  ;; Interval between automatic session saves
  (easysession-save-interval (* 10 60))
  :init
  (add-hook 'emacs-startup-hook #'easysession-load-including-geometry 102)
  (add-hook 'emacs-startup-hook #'easysession-save-mode 103))

Note that:

Usage

It is recommended to use the following functions:

To facilitate session management, consider using the following key mappings: C-c l for switching sessions with easysession-switch-to, and C-c s for saving the current session with easysession-save-as:

(global-set-key (kbd "C-c l") 'easysession-switch-to)
(global-set-key (kbd "C-c s") 'easysession-save-as)

Frequently asked questions

Configuring EasySession with Emacs daemon mode

When using Emacs in daemon mode (emacs --daemon), loading sessions needs to be triggered appropriately. If using the after-init-hook results in issues on startup, an alternative approach is to use server-after-make-frame-hook. This hook ensures that the session is loaded once the client frame is created.

Here is an example:

(use-package easysession
  :ensure t
  :config
  (defun my-setup-easy-session ()
    (easysession-load-including-geometry)
    (easysession-save-mode)
    (remove-hook 'server-after-make-frame-hook #'my-setup-easy-session))

  (add-hook 'server-after-make-frame-hook #'my-setup-easy-session))

(read this discussion for more information.)

How to only persist and restore visible buffers

By default, all file visiting buffers, dired buffers, and indirect buffers are persisted and restored.

To ensure that only visible buffers are saved and restored in your sessions, follow these steps:

Here is the Lisp code:

(defun my-easysession-visible-buffer-list ()
"Return a list of all visible buffers in the current session.
This includes buffers visible in windows or tab-bar tabs."
(let ((visible-buffers '()))
    (dolist (buffer (buffer-list))
    (when (or
            ;; Windows
            (get-buffer-window buffer 'visible)
            ;; Tab-bar windows
            (and (bound-and-true-p tab-bar-mode)
                (fboundp 'tab-bar-get-buffer-tab)
                (tab-bar-get-buffer-tab buffer t nil)))
        (push buffer visible-buffers)))
    visible-buffers))

(setq easysession-buffer-list-function #'my-easysession-visible-buffer-list)

(get-buffer-window checks if a buffer is visible in any window. tab-bar-get-buffer-tab checks if the buffer is visible in a tab-bar tab window. The function collects all visible buffers into the visible-buffers list and returns it.)

How to persist and restore global variables?

To persist and restore global variables in Emacs, you can use the built-in savehist Emacs package. This package is designed to save and restore minibuffer histories, but it can also be configured to save other global variables:

(use-package savehist
  :ensure nil
  :hook
  (after-init . savehist-mode)
  :config
  (add-to-list 'savehist-additional-variables 'kill-ring)
  (add-to-list 'savehist-additional-variables 'mark-ring)
  (add-to-list 'savehist-additional-variables 'search-ring)
  (add-to-list 'savehist-additional-variables 'regexp-search-ring))

(Each element added to savehist-additional-variables is a variable that will be persisted across Emacs sessions that use savehist.)

The easysession package can leverage savehist save the restore the current session name:

(add-to-list 'savehist-additional-variables 'easysession--current-session-name)

How to make the current session name appear in the mode-line?

You can display the current session name in the mode line by setting the following variable to t:

(setq easysession-mode-line-misc-info t)

How to create an empty session setup

To set up a minimal environment when easysession creates a new session, you can define a function that closes all other tabs, deletes all other windows, and switches to the scratch buffer. The following Emacs Lisp code demonstrates how to achieve this:

(defun my-empty-easysession ()
  "Set up a minimal environment when easysession creates a new session."
  (when (and (boundp 'tab-bar-mode) tab-bar-mode)
    (tab-bar-close-other-tabs))
  (delete-other-windows)
  (scratch-buffer))

(add-hook 'easysession-new-session-hook #'my-empty-easysession)

How to configure easysession-save-mode to automatically save only the "main" session and let me manually save others?

To set up easysession-save-mode to automatically save only the "main" session and allow you to manually save other sessions, add the following code to your configuration:

(defun my-easysession-only-main-saved ()
  "Only save the main session."
  (when (string= "main" (easysession-get-current-session-name))
    t))
(setq easysession-save-mode-predicate 'my-easysession-only-main-saved)

How to make EasySession kill all buffers before loading a session?

Here is a code snippet shared by u/capuche on Reddit that closes all buffers before loading a session:

(defun kill-old-session-buffers ()
  (save-some-buffers t)
  (mapc #'kill-buffer
        (cl-remove-if
         (lambda (buffer)
           (string= (buffer-name buffer) "*Messages*"))
         (buffer-list)))
  (delete-other-windows))
(add-hook 'easysession-before-load-hook #'kill-old-session-buffers)
(add-hook 'easysession-new-session-hook #'kill-old-session-buffers)

How to create custom load and save handlers for non-file-visiting buffers

EasySession is customizable. Users can implement their own handlers to manage non-file-editing buffers, enabling the creation of custom functions for restoring such buffers.

Here is an example:

;; NOTE: You cannot use the following reserved Easy Session keys:
;;       buffers, indirect-buffers, frameset, frameset-geo.
(setq my-easysession-buffers-key "MY-BUFFERS")

(defun my-easysession-load-handler (session-data)
  "Load handler for restoring specific buffers from SESSION-DATA."
  (let ((data (assoc-default my-easysession-buffers-key session-data)))
    (when data
      (message "[MY EASYSESSION TEST] LOAD HANDLER: Loaded data: %s" data)

      ;; TODO: Add code to process and restore session data associated
      ;; with `my-easysession-buffers-key'
      )))

(defun my-easysession-save-handler (buffers)
  "Save handler for saving specific buffers from the given BUFFERS list."
  (let ((saved-buffers '())
        (remaining-buffers buffers))
    (dolist (buf buffers)
      (with-current-buffer buf
        (let ((buffer-name (buffer-name)))
          (message "[MY EASYSESSION TEST] SAVE HANDLER: Processing buffer %s"
                   buffer-name)
          ;; TODO: Replace t with a condition like: (derived-mode-p 'MY-MODE)
          (if t
              ;; TODO: Replace "MY-BUFFER-DATA" with your data. It does not
              ;;       have to be a string; it can be a list, hash table, or
              ;;       any other data structure.
              (let ((buffer-data (cons buffer-name "MY-BUFFER-DATA")))
                (push buffer-data saved-buffers)
                (message "[MY EASYSESSION TEST] SAVE HANDLER: Buffer %s saved"
                         buffer-name))
            ;; If the buffer should not be saved, add it to remaining-buffers
            (push buf remaining-buffers)))))
    ;; Return an alist with the saved data and remaining buffers
    (list
     (cons 'key my-easysession-buffers-key)
     (cons 'buffers saved-buffers)
     (cons 'remaining-buffers remaining-buffers))))

(defun my-easysession-setup-handlers ()
  "Configure EasySession load and save handlers."
  (easysession-add-load-handler 'my-easysession-load-handler)
  (easysession-add-save-handler 'my-easysession-save-handler))

(add-hook 'easysession-before-load-hook #'my-easysession-setup-handlers)
(add-hook 'easysession-before-save-hook #'my-easysession-setup-handlers)

Replace TODO with the appropriate code and remove [MY EASYSESSION TEST] after debugging is complete.

How does the author use easysession?

The author uses easysession.el by setting up each session to represent a distinct project or a specific "view" on a particular project, including various tabs (built-in tab-bar), window splits, dired buffers, and file editing buffers. This organization allows for the creation of dedicated environments for different tasks or aspects of a project, such as development, debugging, specific issue, and documentation. The author switches between projects and views of the same projects multiple times a day, and easysession.el helps significantly by allowing quick transitions between them.

Why not use the desktop.el?

While desktop.el is a foundational session management tool for Emacs, it has several limitations:

In contrast, easysession.el offers enhanced functionality:

Why not just improve and submit patches to desktop.el?

It is preferable for EasySession to remain a third-party plugin, as this provides more flexibility for implementing new features. EasySession relies on the same built-in functions as desktop.el (e.g., frameset) but includes additional features that enhance the experience of persisting and restoring sessions. EasySession is also customizable, allowing users to implement their own handlers to persist and restore new types of non-file-visiting buffers.

How does it compare to activities.el?

Why not use one of the other third-party session packages?

There are some existing packages, such as minimal-session-saver, save-visited-files, sesman, and psession. However, these packages have the following limitations:

Easysession is lightweight and can persist and restore file editing buffers, indirect buffers/clones, Dired buffers, the tab-bar, and the Emacs frames (with or without the Emacs frames geometry). It is similar to Vim or Neovim sessions because it loads and restores your editing environment, including buffers, windows, tabs, and other settings, allowing you to resume work exactly where you left off.

License

The easysession Emacs package has been written by James Cherti and is distributed under terms of the GNU General Public License version 3, or, at your choice, any later version.

Copyright (C) 2024 James Cherti

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program.

Links

Other Emacs packages by the same author: