@@ -107,7 +107,7 @@ Copyright @copyright{} 2022 Karl Hallsby@*
Copyright @copyright{} 2022 Justin Veilleux@*
Copyright @copyright{} 2022 Reily Siegel@*
Copyright @copyright{} 2022 Simon Streit@*
-Copyright @copyright{} 2022 (@*
+Copyright @copyright{} 2022-2023 (@*
Copyright @copyright{} 2022 John Kehayias@*
Copyright @copyright{} 2022–2023 Bruno Victal@*
Copyright @copyright{} 2022 Ivan Vilata-i-Balaguer@*
@@ -41793,6 +41793,7 @@ services)}.
* Sound: Sound Home Services. Dealing with audio.
* Messaging: Messaging Home Services. Services for managing messaging.
* Media: Media Home Services. Services for managing media.
+* Emacs: Emacs Home Services. Manage the Emacs daemon.
@end menu
@c In addition to that Home Services can provide
@@ -42981,6 +42982,98 @@ kodi} for more information.
@end table
@end deftp
+@node Emacs Home Services
+@subsection Emacs Home Services
+
+@cindex emacs
+The @uref{https://gnu.org/s/emacs, GNU Emacs} extensible text editor can
+be run as a daemon, allowing you to connect multiple Emacs windows to a
+single core process using @command{emacsclient}. The
+@code{home-emacs-service-type}, exported from
+@code{(gnu home services emacs)}, allows you to launch this daemon on
+login using Guix Home.
+
+Here is a very basic example of an Emacs service configuration,
+including a single package and enabling native compilation support:
+
+@lisp
+(service home-emacs-service-type
+ (home-emacs-configuration
+ (packages (list emacs-magit))
+ (native-compile? #t)))
+@end lisp
+
+The service supports running multiple differently-named Emacs daemons
+simultaneously, which can be connected to using the @code{-s NAME} flag
+of @command{emacsclient}; here is a more complex service configuration
+utilising this feature:
+
+@lisp
+(service home-emacs-service-type
+ (home-emacs-configuration
+ (servers (list "server"
+ "mail"))
+ (emacs emacs-next-pgtk)
+ (packages (list emacs-magit))
+ (native-compile? #t)
+ (debug? #t)))
+@end lisp
+
+As well as adding a @code{"mail"} server, this configuration changes the
+version of Emacs to the latest experimental version available in Guix,
+with the pure-GTK UI enabled, and enables debug log output. Make sure
+you include @code{"server"} in the @code{servers} list, as it provides
+the default server.
+
+@defvar home-emacs-service-type
+Type of the service available from @code{(gnu home services emacs)},
+taking as its configuration value a @code{home-emacs-configuration}
+object.
+
+Logs for this service may be found at
+@file{@var{XDG_STATE_HOME}/log/emacs/SERVER-NAME.log}; the value of
+@var{XDG_STATE_HOME} is often either @file{~/.local/state} or
+@file{~/.local/var/lib}.
+@end defvar
+
+@deftp {Data Type} home-emacs-configuration
+Record for configuring @code{home-emacs-service-type}. Available fields
+are:
+
+@table @asis
+@item @code{services} (default: @code{(list "server")}) (type: string)
+List of strings naming Emacs servers to run. To write ELisp conditional
+on the name of the server it is run under, use the @code{server-name}
+variable.
+
+@item @code{emacs} (default: @code{emacs}) (type: file-like)
+Package providing @file{/bin/emacs}.
+
+@item @code{packages} (default: @code{'()}) (type: list-of-file-likes)
+Packages to give Emacs access to.
+
+@item @code{native-compile?} (default: @code{#f}) (type: boolean)
+Whether to enable native-compilation of Emacs packages by building them
+with @code{emacs} rather than @code{emacs-minimal}. Has no effect if
+@code{compilation-emacs} is specified.
+
+@item @code{compilation-emacs} (default: @code{#f}) (type: file-like-or-#f)
+Emacs to use to compile Emacs packages. If @code{#f}, default to
+@code{emacs} if @code{native-compile?} is @code{#t}, or
+@code{emacs-minimal} otherwise.
+
+@item @code{init-file} (default: @code{(plain-file "init.el" "")} (type: file-like)
+File-like to use as the Emacs initialisation script.
+
+@item @code{early-init-file} (default: @code{(plain-file "early-init.el" "")} (type: file-like)
+File-like to use as the Emacs early-initialisation script, run before
+the UI is set up.
+
+@item @code{debug?} (default: @code{#f}) (type: boolean)
+Whether Emacs will print debug output to the log file.
+@end table
+@end deftp
+
@node Invoking guix home
@section Invoking @command{guix home}
new file mode 100644
@@ -0,0 +1,211 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2022, 2023 ( <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 emacs-minimal)
+ #:use-module (gnu services configuration)
+ #:use-module (guix derivations)
+ #:use-module (guix gexp)
+ #:use-module (guix monads)
+ #:use-module (guix profiles)
+ #:use-module (guix records)
+ #:use-module (guix search-paths)
+ #:use-module (guix store)
+ #:use-module (ice-9 match)
+ #:export (home-emacs-configuration
+ home-emacs-service-type))
+
+(define list-of-string?
+ (list-of string?))
+
+(define list-of-file-likes?
+ (list-of file-like?))
+
+(define file-like-or-#f?
+ (match-lambda
+ ((or #f (? file-like?)) #t)
+ (_ #f)))
+
+(define-configuration/no-serialization home-emacs-configuration
+ (servers
+ (list-of-string (list "server"))
+ "List of strings which name Emacs servers to run.")
+ (emacs
+ (file-like emacs)
+ "The package providing @file{/bin/emacs}.")
+ (packages
+ (list-of-file-likes '())
+ "Packages to add to the Emacs load path.")
+ (native-compile?
+ (boolean #f)
+ "Whether to enable native-compilation of Emacs packages by building them with
+@code{emacs} rather than @code{emacs-minimal}. Has no effect if
+@code{compilation-emacs} is not set to @code{#f}.")
+ (compilation-emacs
+ (file-like-or-#f #f)
+ "The Emacs to use to compile Emacs packages. If @code{#f}, default to
+@code{emacs-minimal}.")
+ (init-file
+ (file-like (plain-file "init.el" ""))
+ "File-like object to use as the Emacs initialisation script.")
+ (early-init-file
+ (file-like (plain-file "early-init.el" ""))
+ "File-like object to use as the Emacs early-initialisation script.")
+ (debug?
+ (boolean #f)
+ "Whether to enable debug output from Emacs."))
+
+(define (emacs-for-compile config)
+ "Using CONFIG, figure out and return the Emacs package to use in the compilation of
+Emacs plugins."
+ (match-record config <home-emacs-configuration> (native-compile?
+ compilation-emacs)
+ (cond
+ (compilation-emacs compilation-emacs)
+ (native-compile? emacs)
+ (else emacs-minimal))))
+
+(define (transformed-emacs-packages config)
+ "Return the PACKAGES of CONFIG, transformed to use the result of EMACS-FOR-COMPILE
+rather than EMACS-MINIMAL for compilation."
+ (match-record config <home-emacs-configuration> (packages)
+ (map (package-input-rewriting
+ `((,emacs-minimal . ,(emacs-for-compile config))))
+ packages)))
+
+(define (make-emacs-profile config)
+ "Return a declarative profile value called ``emacs-profile'' corresponding to
+CONFIG."
+ (match-record config <home-emacs-configuration> (packages)
+ (profile
+ (name "emacs-profile")
+ (content (packages->manifest (cons (emacs-for-compile config)
+ packages))))))
+
+;; XXX: It would be preferable to just ungexp the profile object within the
+;; EMACS-ENVIRONMENT gexp, and do the PROFILE-SEARCH-PATH dance within it, but
+;; both MATCH-RECORD and record field accessors don't seem to work in gexps.
+(define (build-emacs-profile config)
+ "Return the path to a newly-built store item containing a profile corresponding to
+CONFIG."
+ (with-store store
+ (run-with-store store
+ (mlet %store-monad
+ ((profile-drv (lower-object
+ (make-emacs-profile config))))
+ (mbegin %store-monad
+ (built-derivations (list profile-drv))
+ (return (derivation-output-path
+ (assoc-ref (derivation-outputs profile-drv)
+ "out"))))))))
+
+(define (emacs-environment config)
+ "Return a list of ``VARIABLE=VALUE'' strings corresponding to variables to be set
+in the Emacs daemon's environment."
+ #~(map (lambda (env-var)
+ ;; XXX: Not only does it seem that MATCH-RECORD blows up in gexps, but
+ ;; MATCH-LAMBDA too... Stick with this, admittedly ugly, solution for
+ ;; now.
+ (let ((variable (list-ref env-var 0))
+ (separator (list-ref env-var 1))
+ (value (list-ref env-var 2)))
+ (string-append variable "="
+ (or (and=> (getenv variable)
+ (lambda (original)
+ (string-append original
+ separator)))
+ "")
+ value)))
+ '(#$@(map (match-lambda
+ ((spec . value)
+ (match-record spec <search-path-specification>
+ (variable separator)
+ (list variable separator value))))
+ (profile-search-paths
+ (build-emacs-profile config))))))
+
+(define (home-emacs-profile-packages config)
+ (match-record config <home-emacs-configuration> (emacs)
+ (list emacs)))
+
+(define (home-emacs-shepherd-services config)
+ (match-record config <home-emacs-configuration> (servers emacs debug?)
+ (map (lambda (server)
+ (shepherd-service
+ (provision (list (string->symbol
+ (string-append "emacs-" server))))
+ (documentation
+ (string-append "Start the Emacs server called "
+ server "."))
+ (start
+ #~(make-forkexec-constructor
+ (list #$(file-append emacs "/bin/emacs")
+ (string-append "--fg-daemon=" #$server)
+ "--eval"
+ (format #f "~s"
+ `(progn
+ (setq custom-file
+ ,(format #f "~a/emacs/custom.el"
+ (or (getenv "XDG_CONFIG_HOME")
+ (format #f "~a/.config"
+ (getenv "HOME")))))
+ (load custom-file)))
+ #$@(if debug?
+ (list "--debug-init")
+ '()))
+ #:log-file
+ (format #f "~a/log/emacs/~a.log"
+ (or (getenv "XDG_STATE_HOME")
+ (format #f "~a/.local/state"
+ (getenv "HOME")))
+ #$server)
+ #:environment-variables
+ (append (default-environment-variables)
+ #$(emacs-environment config))))
+ (stop
+ #~(make-forkexec-constructor
+ (list #$(file-append emacs "/bin/emacsclient")
+ "-s" #$server "--eval" "(kill-emacs)")))))
+ servers)))
+
+(define (home-emacs-xdg-configuration-files config)
+ (match-record config <home-emacs-configuration> (early-init-file
+ init-file)
+ `(("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.")))
@@ -54,7 +54,7 @@
# Copyright © 2022 muradm <mail@muradm.net>
# Copyright © 2022 Hilton Chain <hako@ultrarare.space>
# Copyright © 2022 Alex Griffin <a@ajgrf.com>
-# Copyright © 2022 ( <paren@disroot.org>
+# Copyright © 2022, 2023 ( <paren@disroot.org>
# Copyright © 2022 jgart <jgart@dismail.de>
# Copyright © 2023 Zheng Junjie <873216071@qq.com>
#
@@ -89,9 +89,10 @@ GNU_SYSTEM_MODULES = \
%D%/home.scm \
%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/mcron.scm \
%D%/home/services/media.scm \
%D%/home/services/messaging.scm \
%D%/home/services/pm.scm \
@@ -99,7 +100,7 @@ GNU_SYSTEM_MODULES = \
%D%/home/services/shepherd.scm \
%D%/home/services/sound.scm \
%D%/home/services/ssh.scm \
- %D%/home/services/mcron.scm \
+ %D%/home/services/symlink-manager.scm \
%D%/home/services/utils.scm \
%D%/home/services/xdg.scm \
%D%/image.scm \