diff mbox series

[bug#58693,1/1] gnu: home: Add home-emacs-service-type.

Message ID 20221021192458.4956-1-paren@disroot.org
State New
Headers show
Series gnu: home: Add home-emacs-service-type. | expand

Checks

Context Check Description
cbaines/comparison success View comparision
cbaines/git-branch success View Git branch
cbaines/applying patch success
cbaines/issue success View issue
cbaines/comparison success View comparision
cbaines/git-branch success View Git branch
cbaines/applying patch success View Laminar job
cbaines/issue success View issue

Commit Message

\( Oct. 21, 2022, 7:24 p.m. UTC
* gnu/local.mk (GNU_SYSTEM_MODULES): Add gnu/home/services/emacs.scm.
* gnu/home/services/emacs.scm (home-emacs-configuration): New
  record type.
(home-emacs-service-type): New variable.
* doc/guix.texi: Document them.
---
 doc/guix.texi               |  51 ++++++++++++
 gnu/home/services/emacs.scm | 162 ++++++++++++++++++++++++++++++++++++
 gnu/local.mk                |   1 +
 3 files changed, 214 insertions(+)
 create mode 100644 gnu/home/services/emacs.scm

Comments

Liliana Marie Prikler Oct. 22, 2022, 9:41 a.m. UTC | #1
merge 58693 58652
thanks

Am Freitag, dem 21.10.2022 um 20:24 +0100 schrieb (:
> +(define-configuration/no-serialization home-emacs-configuration
> +  (emacs
> +   (file-like emacs)
> +   "The package providing @file{/bin/emacs}.")
> +  (packages
> +   (list-of-file-likes '())
> +   "Packages to add to the Emacs plugin load path.")
> +  (native-compile?
> +   (boolean #f)
> +   "Whether to compile the @code{packages} using the Emacs package
> +provided as the value of the @code{emacs} field, which will enable
> +native compilation if the @code{emacs} package supports it.")
> +  (init-file
> +   (file-like (plain-file "init.el" ""))
> +   "File-like to use as the initialisation Lisp file.")
> +  (early-init-file
> +   (file-like (plain-file "early-init.el" ""))
> +   "File-like to use as the pre-initialisation Lisp file.")
> +  (debug?
> +   (boolean #f)
> +   "Whether to enable debugging."))
> +
> +(define (home-emacs-profile-packages config)
> +  (list (home-emacs-configuration-emacs config)))
> +
> +(define (home-emacs-transformed-packages config)
> +  (map (if (home-emacs-configuration-native-compile? config)
> +           (package-input-rewriting
> +            `((,emacs-minimal
> +              . ,(home-emacs-configuration-emacs config))))
> +           identity)
> +       (let ((packages (home-emacs-configuration-packages config)))
> +         (concatenate
> +          (cons packages
> +                (map (compose (cute map second <>)
> +                              package-transitive-propagated-inputs)
> +                     packages))))))
> +
> +(define (home-emacs-shepherd-services config)
> +  (list (shepherd-service
> +         (provision '(emacs))
> +         (documentation "Start the Emacs daemon.")
> +         (modules '((ice-9 ftw)
> +                    (srfi srfi-1)
> +                    (srfi srfi-26)))
> +         (start
> +          #~(make-forkexec-constructor
> +             (list #$(file-append
> +                      (home-emacs-configuration-emacs config)
> +                      "/bin/emacs")
> +                   "--fg-daemon" "--eval"
You should probably use a systemd-style constructor/destructor pair.

> +                   (format #f "~s"
> +                           `(progn
> +                             (setq custom-file
> +                                   (concat (or (getenv
> "XDG_CONFIG_HOME")
> +                                               (concat (getenv
> "HOME")
> +                                                       "/.config"))
> +                                           "/emacs/custom.el"))
> +                             (load custom-file)))
This one should be customizable by the user using init.el or early-
init.el -- alternatively, you could set up a custom-file parameter and
use that *if given*.

> +                   #$@(if (home-emacs-configuration-debug? config)
> +                          (list "--debug-init")
> +                          '()))
> +             #:log-file
> +             (format #f "~a/emacs.log"
> +                     (or (getenv "XDG_LOG_HOME")
> +                         (format #f "~a/.local/var/log"
> +                                 (getenv "HOME"))))
XDG_LOG_HOME and ~/.local/var are guix home idiosyncrasies that ought
to be removed.  The XDG-supported variable/value pair is XDG_STATE_HOME
and ~/.local/state.

> +             #:environment-variables
> +             (let ((env-var
> +                    (lambda (name path)
> +                      (define (regular-directory? directory)
> +                        (not (member directory (list "." ".."))))
> +
> +                      (define (package-paths package)
> +                        (let ((directory (string-append package "/"
> path)))
> +                          (if (file-exists? directory)
> +                              (cons directory
> +                                    (map (cute string-append
> directory "/" <>)
> +                                         (scandir directory regular-
> directory?)))
> +                              '())))
> +
> +                      (let ((old-value (getenv name)))
> +                        (string-append
> +                         name "="
> +                         (string-join
> +                          (append-map
> +                           package-paths
> +                           (list #$@(home-emacs-transformed-packages
> config)))
> +                          ":" (if old-value
> +                                  'suffix
> +                                  'infix))
> +                         (or old-value ""))))))
> +               (append (default-environment-variables)
> +                       (list (env-var "EMACSLOADPATH"
> +                                      "share/emacs/site-lisp")
> +                             (env-var "EMACSNATIVELOADPATH"
> +                                      "lib/emacs/native-site-
> lisp"))))))
You should collect the emacs package plus lisp packages into a profile.
This will make it easier to set emacs-related variables. 
Alternatively, you could use (guix search-paths) directly.

> +         (stop
> +          #~(make-forkexec-constructor
> +             (list #$(file-append
> +                      (home-emacs-configuration-emacs config)
> +                      "/bin/emacsclient")
> +                   "--eval" "(kill-emacs)"))))))
> +
> +(define (home-emacs-xdg-configuration-files config)
> +  `(("emacs/early-init.el"
> +     ,(home-emacs-configuration-early-init-file config))
> +    ("emacs/init.el"
> +     ,(home-emacs-configuration-init-file config))))
You're missing an escape hatch for additional elisp files like
custom.el

Cheers
Ludovic Courtès Nov. 8, 2022, 12:59 p.m. UTC | #2
Hi!

"(" <paren@disroot.org> skribis:

> +@defvr {Scheme Variable} home-emacs-service-type
> +This is the service type for configuring the Emacs text editor and
> +running its daemon.

I would happily use some Guix Home service to configure Emacs, but I
don’t want to start it as a daemon (currently I start it from
~/.xsession as I use EXWM).

Could we somehow decouple the daemon aspect from the rest?

Anyhow, I’m glad to see this work—thank you!

Ludo’.
\( Nov. 8, 2022, 4:30 p.m. UTC | #3
Heya,

On Tue Nov 8, 2022 at 12:59 PM GMT, Ludovic Courtès wrote:
> I would happily use some Guix Home service to configure Emacs, but I
> don’t want to start it as a daemon (currently I start it from
> ~/.xsession as I use EXWM).
>
> Could we somehow decouple the daemon aspect from the rest?

There's virtually no point in doing so. This service doesn't generate ELisp from
a Scheme config, as I think Emacs is too flexible for that; instead, it lets you
choose the packages, init file, etc to pass to the daemon (which is what is meant
by "configure", I suppose).

    -- (
diff mbox series

Patch

diff --git a/doc/guix.texi b/doc/guix.texi
index 3bf2dee752..dc27b7437e 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -40517,6 +40517,7 @@  services)}.
 * Shepherd: Shepherd Home Service.                     Managing User's Daemons.
 * SSH: Secure Shell.                                   Setting up the secure shell client.
 * Desktop: Desktop Home Services.                      Services for graphical environments.
+* Emacs: Emacs Home Service.                           Service for running Emacs.
 * Guix: Guix Home Services.                            Services for Guix.
 @end menu
 @c In addition to that Home Services can provide
@@ -41348,6 +41349,56 @@  The package providing the @code{/bin/dbus-daemon} command.
 @end table
 @end deftp
 
+@node Emacs Home Service
+@subsection Emacs Home Service
+
+@defvr {Scheme Variable} home-emacs-service-type
+This is the service type for configuring the Emacs text editor and
+running its daemon.
+
+Note that if you have an existing @file{~/.emacs} and/or
+@file{~/.emacs.d}, the configuration aspect of this service will
+not function, as they take precedence over @file{~/.config/emacs},
+which this service uses in the interest of cleanliness.  To migrate
+to the XDG directory, run these commands:
+
+@example
+$ cp ~/.emacs.d $XDG_CONFIG_HOME/emacs
+$ cp ~/.emacs $XDG_CONFIG_HOME/emacs/init.el
+@end example
+@end defvr
+
+@deftp {Data Type} home-emacs-configuration
+The configuration record for @code{home-emacs-service-type}.
+
+@table @asis
+@item @code{emacs} (default: @code{emacs})
+The package providing the @file{/bin/emacs} and @file{/bin/emacsclient}
+commands.
+
+@item @code{packages} (default: @code{'()})
+Packages providing Emacs plugins to add to the daemon's load paths.
+
+@item @code{native-compile?} (default: @code{#f})
+Whether to recompile all @code{packages}, using the provided @code{emacs}
+package in place of @code{emacs-minimal}, which will enable native
+compilation if the @code{emacs} package supports it.  All
+non-@code{-minimal} Emacs packages at version 28 or above should support
+native compilation.
+
+@item @code{init-file} (default: @code{(plain-file "init.el" "")})
+The file-like to use as @file{init.el}, the main Emacs configuration
+file.
+
+@item @code{early-init-file} (default: @code{(plain-file "early-init.el" "")})
+The file-like to use as @file{early-init.el}, which is loaded early in
+the initialisation process, before the GUI is started.
+
+@item @code{debug?} (default: @code{#f})
+Whether to enable detailed debug log output with backtraces.
+@end table
+@end deftp
+
 @node Guix Home Services
 @subsection Guix Home Services
 
diff --git a/gnu/home/services/emacs.scm b/gnu/home/services/emacs.scm
new file mode 100644
index 0000000000..a727a2f246
--- /dev/null
+++ b/gnu/home/services/emacs.scm
@@ -0,0 +1,162 @@ 
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2022 ( <paren@disroot.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix 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.
+;;;
+;;; GNU Guix 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 GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu home services emacs)
+  #:use-module (gnu home services)
+  #:use-module (gnu home services shepherd)
+  #:autoload   (gnu packages emacs) (emacs-minimal
+                                     emacs)
+  #:use-module (gnu services configuration)
+  #:use-module (guix gexp)
+  #:use-module (guix packages)
+  #:use-module (guix records)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-26)
+
+  #:export (home-emacs-configuration
+            home-emacs-service-type))
+
+(define list-of-file-likes?
+  (list-of file-like?))
+
+(define-configuration/no-serialization home-emacs-configuration
+  (emacs
+   (file-like emacs)
+   "The package providing @file{/bin/emacs}.")
+  (packages
+   (list-of-file-likes '())
+   "Packages to add to the Emacs plugin load path.")
+  (native-compile?
+   (boolean #f)
+   "Whether to compile the @code{packages} using the Emacs package
+provided as the value of the @code{emacs} field, which will enable
+native compilation if the @code{emacs} package supports it.")
+  (init-file
+   (file-like (plain-file "init.el" ""))
+   "File-like to use as the initialisation Lisp file.")
+  (early-init-file
+   (file-like (plain-file "early-init.el" ""))
+   "File-like to use as the pre-initialisation Lisp file.")
+  (debug?
+   (boolean #f)
+   "Whether to enable debugging."))
+
+(define (home-emacs-profile-packages config)
+  (list (home-emacs-configuration-emacs config)))
+
+(define (home-emacs-transformed-packages config)
+  (map (if (home-emacs-configuration-native-compile? config)
+           (package-input-rewriting
+            `((,emacs-minimal
+              . ,(home-emacs-configuration-emacs config))))
+           identity)
+       (let ((packages (home-emacs-configuration-packages config)))
+         (concatenate
+          (cons packages
+                (map (compose (cute map second <>)
+                              package-transitive-propagated-inputs)
+                     packages))))))
+
+(define (home-emacs-shepherd-services config)
+  (list (shepherd-service
+         (provision '(emacs))
+         (documentation "Start the Emacs daemon.")
+         (modules '((ice-9 ftw)
+                    (srfi srfi-1)
+                    (srfi srfi-26)))
+         (start
+          #~(make-forkexec-constructor
+             (list #$(file-append
+                      (home-emacs-configuration-emacs config)
+                      "/bin/emacs")
+                   "--fg-daemon" "--eval"
+                   (format #f "~s"
+                           `(progn
+                             (setq custom-file
+                                   (concat (or (getenv "XDG_CONFIG_HOME")
+                                               (concat (getenv "HOME")
+                                                       "/.config"))
+                                           "/emacs/custom.el"))
+                             (load custom-file)))
+                   #$@(if (home-emacs-configuration-debug? config)
+                          (list "--debug-init")
+                          '()))
+             #:log-file
+             (format #f "~a/emacs.log"
+                     (or (getenv "XDG_LOG_HOME")
+                         (format #f "~a/.local/var/log"
+                                 (getenv "HOME"))))
+             #:environment-variables
+             (let ((env-var
+                    (lambda (name path)
+                      (define (regular-directory? directory)
+                        (not (member directory (list "." ".."))))
+
+                      (define (package-paths package)
+                        (let ((directory (string-append package "/" path)))
+                          (if (file-exists? directory)
+                              (cons directory
+                                    (map (cute string-append directory "/" <>)
+                                         (scandir directory regular-directory?)))
+                              '())))
+
+                      (let ((old-value (getenv name)))
+                        (string-append
+                         name "="
+                         (string-join
+                          (append-map
+                           package-paths
+                           (list #$@(home-emacs-transformed-packages config)))
+                          ":" (if old-value
+                                  'suffix
+                                  'infix))
+                         (or old-value ""))))))
+               (append (default-environment-variables)
+                       (list (env-var "EMACSLOADPATH"
+                                      "share/emacs/site-lisp")
+                             (env-var "EMACSNATIVELOADPATH"
+                                      "lib/emacs/native-site-lisp"))))))
+         (stop
+          #~(make-forkexec-constructor
+             (list #$(file-append
+                      (home-emacs-configuration-emacs config)
+                      "/bin/emacsclient")
+                   "--eval" "(kill-emacs)"))))))
+
+(define (home-emacs-xdg-configuration-files config)
+  `(("emacs/early-init.el"
+     ,(home-emacs-configuration-early-init-file config))
+    ("emacs/init.el"
+     ,(home-emacs-configuration-init-file config))))
+
+(define home-emacs-service-type
+  (service-type
+   (name 'home-emacs)
+   (extensions
+    (list (service-extension
+           home-profile-service-type
+           home-emacs-profile-packages)
+          (service-extension
+           home-shepherd-service-type
+           home-emacs-shepherd-services)
+          (service-extension
+           home-xdg-configuration-files-service-type
+           home-emacs-xdg-configuration-files)))
+   (default-value (home-emacs-configuration))
+   (description
+    "Configure and run the GNU Emacs extensible text editor.")))
diff --git a/gnu/local.mk b/gnu/local.mk
index 8247180bef..3431c771b8 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -88,6 +88,7 @@  GNU_SYSTEM_MODULES =				\
   %D%/home/services.scm			\
   %D%/home/services/desktop.scm			\
   %D%/home/services/symlink-manager.scm		\
+  %D%/home/services/emacs.scm			\
   %D%/home/services/fontutils.scm		\
   %D%/home/services/guix.scm			\
   %D%/home/services/pm.scm			\