* [bug#60753] [PATCH] gnu: home: Add home-emacs-service-type. @ 2023-01-12 14:01 David Wilson 2023-01-12 17:24 ` ( via Guix-patches via ` (2 more replies) 0 siblings, 3 replies; 19+ messages in thread From: David Wilson @ 2023-01-12 14:01 UTC (permalink / raw) To: 60753 This commit continues work by ( to add a home-emacs-service-type for the purpose of configuring Emacs. The goal here is not to configure Emacs with Scheme forms, but to make it possible to assemble init.el and early-init.el using files from one's own personal configuration and other Emacs Lisp snippets for further customization. The simplest usage of the service would be to use simple strings or local files to build init.el and early-init.el: (home-environment (services (list (service home-emacs-service-type (home-emacs-configuration (init-file (list (local-file "init.el"))) (early-init-file '(";; This is early-init.el!\n"))))))) The `emacs-variable' function can be used to generate a g-expression which will produce a `setq' form inside of an init file: (home-emacs-configuration (init-file (list (emacs-variables '((my/font-size . 24) (my/tab-width . 2) (inhibit-startup-message . #t))) (local-file "init.el")))) If you have an existing folder of Emacs Lisp files that you `require' into your configuration, you can use the `load-paths' field to pull in the entire folder or reference the existing files on your system: (home-emacs-configuration (init-file (list "(require 'my-init)")) (load-paths (list (local-file "emacs-modules" #:recursive #t)))) Since Emacs loves to write files to `user-emacs-directory', we must provide a writeable path for it so that Emacs doesn't try to write files into the read-only store. The `user-emacs-directory' field can be used to customize that; it defaults to `~/.cache/emacs'. Other services can extend home-emacs-service-type with the home-emacs-extension type to add further configuration snippets, packages, and load paths: (home-environment (services (list (service home-emacs-service-type (home-emacs-configuration (init-file (list "(require 'my-module)\n")))) (simple-service 'home-emacs-extension-one home-emacs-service-type (home-emacs-extension (packages (list (specification->package "emacs-evil"))) (init-file (list "(evil-mode +1)\n"))))))) Co-authored-by: ( <paren@disroot.org> --- doc/guix.texi | 77 +++++++++++++++ gnu/home/services/emacs.scm | 188 ++++++++++++++++++++++++++++++++++++ gnu/local.mk | 1 + 3 files changed, 266 insertions(+) create mode 100644 gnu/home/services/emacs.scm diff --git a/doc/guix.texi b/doc/guix.texi index 751d0957d8..62fefde1ea 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -111,6 +111,7 @@ Copyright @copyright{} 2022 (@* Copyright @copyright{} 2022 John Kehayias@* Copyright @copyright{} 2022 Ivan Vilata-i-Balaguer@* Copyright @copyright{} 2023 Giacomo Leidi@* +Copyright @copyright{} 2023 David Wilson@* Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or @@ -41061,6 +41062,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 Services. Services for configuring Emacs. * Guix: Guix Home Services. Services for Guix. @end menu @c In addition to that Home Services can provide @@ -41914,6 +41916,81 @@ The package providing the @code{/bin/dbus-daemon} command. @end table @end deftp +@node Emacs Home Services +@subsection Emacs Home Services + +@defvr {Scheme Variable} home-emacs-service-type +This is the service type for configuring the Emacs text editor. It +enables you to assemble @file{init.el} and @file{early-init.el} files +from snippets in your home configuration and other Emacs Lisp files you +have in your personal configuration folder. + +This service can be extended using the @code{home-emacs-extension} type. + +Note that if you have an existing @file{~/.emacs} and/or +@file{~/.emacs.d}, the configuration aspect of this service will not be +loaded, as the former location takes precedence over +@file{~/.config/emacs}. This service uses the latter path 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} command. + +@item @code{packages} (default: @code{'()}) +Additional packages required by the Emacs configuration. + +@item @code{user-emacs-directory} (default: @file{~/.cache/emacs}) +The directory beneath which additional per-user Emacs-specific files are placed. + +@item @code{init-file} (default: @code{'()}) +Configuration text or files to include in @file{init.el}. + +@item @code{early-init-file} (default: @code{'()}) +Configuration text or files to include in @file{early-init.el}. + +@item @code{load-paths} (default: @code{'()}) +Additional load paths to add to Emacs' @code{load-path} variable. Lines +will be inserted at the beginning of @file{early-init.el}. + +@item @code{native-compile?} (default: @code{#f}) +Whether to compile 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. +@end table +@end deftp + +@deftp {Data Type} home-emacs-extension +The extension record for @code{home-emacs-service-type}. + +@table @asis +@item @code{packages} (default: @code{'()}) +Additional packages required by the Emacs configuration. + +@item @code{init-file} (default: @code{'()}) +Configuration text or files to include in @file{init.el}. + +@item @code{early-init-file} (default: @code{'()}) +Configuration text or files to include in @file{early-init.el}. + +@item @code{load-paths} (default: @code{'()}) +Additional load paths to add to Emacs' @code{load-path} variable. Lines +will be inserted at the beginning of @file{early-init.el}. +@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..45b4f2a5d6 --- /dev/null +++ b/gnu/home/services/emacs.scm @@ -0,0 +1,188 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2022 ( <paren@disroot.org> +;;; Copyright © 2023 David Wilson <david@daviwil.com> +;;; +;;; 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) + #: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 (ice-9 match) + #:use-module (srfi srfi-1) + #:use-module (srfi srfi-26) + + #:export (emacs-variables + home-emacs-configuration + home-emacs-extension + home-emacs-service-type)) + +(define list-of-file-likes? + (list-of file-like?)) + +(define (string-or-file-like? val) + (or (string? val) + (file-like? val))) + +(define list-of-string-or-file-likes? + (list-of string-or-file-like?)) + +(define-configuration/no-serialization home-emacs-configuration + (emacs + (file-like emacs) + "The package providing the @file{/bin/emacs} command.") + (packages + (list-of-file-likes '()) + "Additional packages required by the Emacs configuration.") + (user-emacs-directory + (string "~/.cache/emacs") + "Directory beneath which additional per-user Emacs-specific files are placed.") + (init-file + (text-config '()) + "Configuration text or files to include in init.el.") + (early-init-file + (text-config '()) + "Configuration text or files to include in early-init.el.") + (load-paths + (list-of-string-or-file-likes '()) + "Additional load paths to add to Emacs' @code{load-path} variable. Lines +will be inserted at the beginning of early-init.el.") + (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.")) + +(define (home-emacs-profile-packages config) + (cons (home-emacs-configuration-emacs config) + (home-emacs-configuration-packages 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 (serialize-emacs-load-paths config) + #~(string-append + ";; Additional load paths\n" + #$@(map (lambda (load-path) + #~(format #f "(add-to-list 'load-path \"~a\")" #$load-path)) + (home-emacs-configuration-load-paths config)) + "\n\n")) + +(define (serialize-emacs-user-directory config) + (format #f + ";; Set the `user-emacs-directory` to a writeable path\n(setq user-emacs-directory \"~a\")\n\n" + (home-emacs-configuration-user-emacs-directory config))) + +(define (home-emacs-xdg-configuration-files config) + `(("emacs/early-init.el" + ,(apply mixed-text-file + (cons* "early-init.el" + (serialize-emacs-load-paths config) + (serialize-emacs-user-directory config) + (home-emacs-configuration-early-init-file config)))) + ("emacs/init.el" + ,(apply mixed-text-file + (cons "init.el" + (home-emacs-configuration-init-file config)))))) + +(define-configuration/no-serialization home-emacs-extension + (packages + (list-of-file-likes '()) + "Additional packages required by the Emacs configuration.") + (init-file + (text-config '()) + "Configuration text or files to include in init.el.") + (early-init-file + (text-config '()) + "Configuration text or files to include in early-init.el.") + (load-paths + (list-of-string-or-file-likes '()) + "Additional load paths to add to Emacs' @code{load-path} variable. Lines +will be inserted at the beginning of early-init.el.")) + +(define (home-emacs-extensions original-config extension-configs) + (match-record original-config <home-emacs-configuration> + (packages load-paths init-file early-init-file) + (home-emacs-configuration + (inherit original-config) + (packages + (append packages + (append-map + home-emacs-extension-packages extension-configs))) + (init-file + (append init-file + (append-map + home-emacs-extension-init-file extension-configs))) + (early-init-file + (append early-init-file + (append-map + home-emacs-extension-early-init-file extension-configs))) + (load-paths + (append load-paths + (append-map + home-emacs-extension-load-paths extension-configs)))))) + +(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)) + (compose identity) + (extend home-emacs-extensions) + (description + "Configure the GNU Emacs extensible text editor."))) + +(define scheme-value->emacs-value + (match-lambda (#t (quote 't)) + (#f (quote 'nil)) + (val val))) + +(define (emacs-variables var-alist) + "Converts an alist of variable names and values into a @code{setq} +expression that can be used in an Emacs configuration. Scheme values +@code{#t} and @code{#f} will be converted into @code{t} and @code{nil}, +respectively." + #~(string-append + "(setq" + #$@(map (lambda (var) + #~(format #f "\n ~a ~s" + (quote #$(car var)) + #$(scheme-value->emacs-value (cdr var)))) + var-alist) + ")\n\n")) diff --git a/gnu/local.mk b/gnu/local.mk index 184f43e753..35d88b4dd6 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -89,6 +89,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 \ -- 2.38.1 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* [bug#60753] [PATCH] gnu: home: Add home-emacs-service-type. 2023-01-12 14:01 [bug#60753] [PATCH] gnu: home: Add home-emacs-service-type David Wilson @ 2023-01-12 17:24 ` ( via Guix-patches via 2023-01-12 17:27 ` David Wilson 2023-01-15 0:21 ` [bug#60753] file like parameters not working benoit 2023-01-15 2:07 ` [bug#60753] issue with file-like init file benoit 2 siblings, 1 reply; 19+ messages in thread From: ( via Guix-patches via @ 2023-01-12 17:24 UTC (permalink / raw) To: David Wilson, 60753 [-- Attachment #1: Type: text/plain, Size: 388 bytes --] On Thu Jan 12, 2023 at 2:01 PM GMT, David Wilson wrote: > + (service-extension > + home-shepherd-service-type > + home-emacs-shepherd-services) home-emacs-shepherd-services isn't defined here :( Doesn't this cause an unbound-variable error? (Also, most of the reason I wrote this service was to support auto-starting emacs --daemon :)) -- ( [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 659 bytes --] ^ permalink raw reply [flat|nested] 19+ messages in thread
* [bug#60753] [PATCH] gnu: home: Add home-emacs-service-type. 2023-01-12 17:24 ` ( via Guix-patches via @ 2023-01-12 17:27 ` David Wilson 2023-01-14 18:00 ` Ludovic Courtès 0 siblings, 1 reply; 19+ messages in thread From: David Wilson @ 2023-01-12 17:27 UTC (permalink / raw) To: (; +Cc: 60753 Hey (! "(" <paren@disroot.org> writes: > home-emacs-shepherd-services isn't defined here :( Doesn't this cause an unbound-variable > error? (Also, most of the reason I wrote this service was to support auto-starting emacs > --daemon :)) Whoops, in my haste to send out the patch I forgot to take that out! Yes, I saw that you meant to run Emacs as a daemon and I think it should be added to this service (or another daemon-specific service) in a future patch. I figured it would be easier to get a patch accepted without the daemon functionality just yet since there seemed to be more feedback about that aspect in your patch thread. Here's an update to my patch that actually works (sorry if I'm doing this wrong, it's been a while since I worked on a patch thread!) -- Co-authored-by: ( <paren@disroot.org> --- doc/guix.texi | 77 +++++++++++++++ gnu/home/services/emacs.scm | 185 ++++++++++++++++++++++++++++++++++++ gnu/local.mk | 1 + 3 files changed, 263 insertions(+) create mode 100644 gnu/home/services/emacs.scm diff --git a/doc/guix.texi b/doc/guix.texi index 751d0957d8..62fefde1ea 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -111,6 +111,7 @@ Copyright @copyright{} 2022 (@* Copyright @copyright{} 2022 John Kehayias@* Copyright @copyright{} 2022 Ivan Vilata-i-Balaguer@* Copyright @copyright{} 2023 Giacomo Leidi@* +Copyright @copyright{} 2023 David Wilson@* Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or @@ -41061,6 +41062,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 Services. Services for configuring Emacs. * Guix: Guix Home Services. Services for Guix. @end menu @c In addition to that Home Services can provide @@ -41914,6 +41916,81 @@ The package providing the @code{/bin/dbus-daemon} command. @end table @end deftp +@node Emacs Home Services +@subsection Emacs Home Services + +@defvr {Scheme Variable} home-emacs-service-type +This is the service type for configuring the Emacs text editor. It +enables you to assemble @file{init.el} and @file{early-init.el} files +from snippets in your home configuration and other Emacs Lisp files you +have in your personal configuration folder. + +This service can be extended using the @code{home-emacs-extension} type. + +Note that if you have an existing @file{~/.emacs} and/or +@file{~/.emacs.d}, the configuration aspect of this service will not be +loaded, as the former location takes precedence over +@file{~/.config/emacs}. This service uses the latter path 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} command. + +@item @code{packages} (default: @code{'()}) +Additional packages required by the Emacs configuration. + +@item @code{user-emacs-directory} (default: @file{~/.cache/emacs}) +The directory beneath which additional per-user Emacs-specific files are placed. + +@item @code{init-file} (default: @code{'()}) +Configuration text or files to include in @file{init.el}. + +@item @code{early-init-file} (default: @code{'()}) +Configuration text or files to include in @file{early-init.el}. + +@item @code{load-paths} (default: @code{'()}) +Additional load paths to add to Emacs' @code{load-path} variable. Lines +will be inserted at the beginning of @file{early-init.el}. + +@item @code{native-compile?} (default: @code{#f}) +Whether to compile 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. +@end table +@end deftp + +@deftp {Data Type} home-emacs-extension +The extension record for @code{home-emacs-service-type}. + +@table @asis +@item @code{packages} (default: @code{'()}) +Additional packages required by the Emacs configuration. + +@item @code{init-file} (default: @code{'()}) +Configuration text or files to include in @file{init.el}. + +@item @code{early-init-file} (default: @code{'()}) +Configuration text or files to include in @file{early-init.el}. + +@item @code{load-paths} (default: @code{'()}) +Additional load paths to add to Emacs' @code{load-path} variable. Lines +will be inserted at the beginning of @file{early-init.el}. +@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..7a821fed8a --- /dev/null +++ b/gnu/home/services/emacs.scm @@ -0,0 +1,185 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2022 ( <paren@disroot.org> +;;; Copyright © 2023 David Wilson <david@daviwil.com> +;;; +;;; 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) + #: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 (ice-9 match) + #:use-module (srfi srfi-1) + #:use-module (srfi srfi-26) + + #:export (emacs-variables + home-emacs-configuration + home-emacs-extension + home-emacs-service-type)) + +(define list-of-file-likes? + (list-of file-like?)) + +(define (string-or-file-like? val) + (or (string? val) + (file-like? val))) + +(define list-of-string-or-file-likes? + (list-of string-or-file-like?)) + +(define-configuration/no-serialization home-emacs-configuration + (emacs + (file-like emacs) + "The package providing the @file{/bin/emacs} command.") + (packages + (list-of-file-likes '()) + "Additional packages required by the Emacs configuration.") + (user-emacs-directory + (string "~/.cache/emacs") + "Directory beneath which additional per-user Emacs-specific files are placed.") + (init-file + (text-config '()) + "Configuration text or files to include in init.el.") + (early-init-file + (text-config '()) + "Configuration text or files to include in early-init.el.") + (load-paths + (list-of-string-or-file-likes '()) + "Additional load paths to add to Emacs' @code{load-path} variable. Lines +will be inserted at the beginning of early-init.el.") + (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.")) + +(define (home-emacs-profile-packages config) + (cons (home-emacs-configuration-emacs config) + (home-emacs-configuration-packages 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 (serialize-emacs-load-paths config) + #~(string-append + ";; Additional load paths\n" + #$@(map (lambda (load-path) + #~(format #f "(add-to-list 'load-path \"~a\")" #$load-path)) + (home-emacs-configuration-load-paths config)) + "\n\n")) + +(define (serialize-emacs-user-directory config) + (format #f + ";; Set the `user-emacs-directory` to a writeable path\n(setq user-emacs-directory \"~a\")\n\n" + (home-emacs-configuration-user-emacs-directory config))) + +(define (home-emacs-xdg-configuration-files config) + `(("emacs/early-init.el" + ,(apply mixed-text-file + (cons* "early-init.el" + (serialize-emacs-load-paths config) + (serialize-emacs-user-directory config) + (home-emacs-configuration-early-init-file config)))) + ("emacs/init.el" + ,(apply mixed-text-file + (cons "init.el" + (home-emacs-configuration-init-file config)))))) + +(define-configuration/no-serialization home-emacs-extension + (packages + (list-of-file-likes '()) + "Additional packages required by the Emacs configuration.") + (init-file + (text-config '()) + "Configuration text or files to include in init.el.") + (early-init-file + (text-config '()) + "Configuration text or files to include in early-init.el.") + (load-paths + (list-of-string-or-file-likes '()) + "Additional load paths to add to Emacs' @code{load-path} variable. Lines +will be inserted at the beginning of early-init.el.")) + +(define (home-emacs-extensions original-config extension-configs) + (match-record original-config <home-emacs-configuration> + (packages load-paths init-file early-init-file) + (home-emacs-configuration + (inherit original-config) + (packages + (append packages + (append-map + home-emacs-extension-packages extension-configs))) + (init-file + (append init-file + (append-map + home-emacs-extension-init-file extension-configs))) + (early-init-file + (append early-init-file + (append-map + home-emacs-extension-early-init-file extension-configs))) + (load-paths + (append load-paths + (append-map + home-emacs-extension-load-paths extension-configs)))))) + +(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-xdg-configuration-files-service-type + home-emacs-xdg-configuration-files))) + (default-value (home-emacs-configuration)) + (compose identity) + (extend home-emacs-extensions) + (description + "Configure the GNU Emacs extensible text editor."))) + +(define scheme-value->emacs-value + (match-lambda (#t (quote 't)) + (#f (quote 'nil)) + (val val))) + +(define (emacs-variables var-alist) + "Converts an alist of variable names and values into a @code{setq} +expression that can be used in an Emacs configuration. Scheme values +@code{#t} and @code{#f} will be converted into @code{t} and @code{nil}, +respectively." + #~(string-append + "(setq" + #$@(map (lambda (var) + #~(format #f "\n ~a ~s" + (quote #$(car var)) + #$(scheme-value->emacs-value (cdr var)))) + var-alist) + ")\n\n")) diff --git a/gnu/local.mk b/gnu/local.mk index 184f43e753..35d88b4dd6 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -89,6 +89,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 \ -- 2.38.1 ^ permalink raw reply related [flat|nested] 19+ messages in thread
* [bug#60753] [PATCH] gnu: home: Add home-emacs-service-type. 2023-01-12 17:27 ` David Wilson @ 2023-01-14 18:00 ` Ludovic Courtès 2023-01-15 8:02 ` Andrew Tropin 2023-01-16 9:25 ` David Wilson 0 siblings, 2 replies; 19+ messages in thread From: Ludovic Courtès @ 2023-01-14 18:00 UTC (permalink / raw) To: David Wilson; +Cc: (, 60753 Hello! David Wilson <david@daviwil.com> skribis: > Yes, I saw that you meant to run Emacs as a daemon and I think it should > be added to this service (or another daemon-specific service) in a > future patch. I figured it would be easier to get a patch accepted > without the daemon functionality just yet since there seemed to be more > feedback about that aspect in your patch thread. Agree, I think we should add the daemon functionality in a subsequent patch. Overall it looks nice to me! Some comments and suggestions: > +@node Emacs Home Services > +@subsection Emacs Home Services > + > +@defvr {Scheme Variable} home-emacs-service-type It would be nice if you could start the section with a few sentences explaining the rationale and what’s being described here. > +This is the service type for configuring the Emacs text editor. It > +enables you to assemble @file{init.el} and @file{early-init.el} files > +from snippets in your home configuration and other Emacs Lisp files you > +have in your personal configuration folder. Maybe like so: … to assemble the @file{init.el} (@pxref{Init File,,, emacs, GNU Emacs Manual}) and @file{early-init.el} (@pxref{Early Init File,,, emacs, GNU Emacs Manual}) files … > +@example > +$ cp ~/.emacs.d $XDG_CONFIG_HOME/emacs > +$ cp ~/.emacs $XDG_CONFIG_HOME/emacs/init.el > +@end example I’d drop the prompt. > +@item @code{init-file} (default: @code{'()}) > +Configuration text or files to include in @file{init.el}. > + > +@item @code{early-init-file} (default: @code{'()}) > +Configuration text or files to include in @file{early-init.el}. What about accepting sexps (or gexps) instead of strings? As in: (init-file '((require 'whatever) (setq something t))) Also I find it confusing that it’s either text or files. In the code it has type ‘text-config’, which means list of file-like objects IIUC, no? > +@item @code{load-paths} (default: @code{'()}) > +Additional load paths to add to Emacs' @code{load-path} variable. Lines > +will be inserted at the beginning of @file{early-init.el}. Nitpick: I think this should be ‘load-path’ (singular), because it’s one search path (i.e., a list of directories). And: s/Additional load paths/Additional directories/ > +@end table > +@end deftp Would be nice to have a couple of commented examples here, like you had in the first message in this thread. :-) > +@deftp {Data Type} home-emacs-extension > +The extension record for @code{home-emacs-service-type}. Would be nice to have a sentence above, like “This service type can be extended with @code{home-emacs-extension} records, described below:”. > +@table @asis > +@item @code{packages} (default: @code{'()}) > +Additional packages required by the Emacs configuration. > + > +@item @code{init-file} (default: @code{'()}) > +Configuration text or files to include in @file{init.el}. > + > +@item @code{early-init-file} (default: @code{'()}) > +Configuration text or files to include in @file{early-init.el}. > + > +@item @code{load-paths} (default: @code{'()}) Ditto. > +@end deftp Would be great to have an example of that too. :-) Thank you! Ludo’. ^ permalink raw reply [flat|nested] 19+ messages in thread
* [bug#60753] [PATCH] gnu: home: Add home-emacs-service-type. 2023-01-14 18:00 ` Ludovic Courtès @ 2023-01-15 8:02 ` Andrew Tropin 2023-01-17 9:02 ` Ludovic Courtès 2023-01-16 9:25 ` David Wilson 1 sibling, 1 reply; 19+ messages in thread From: Andrew Tropin @ 2023-01-15 8:02 UTC (permalink / raw) To: Ludovic Courtès, David Wilson; +Cc: (, 60753 [-- Attachment #1: Type: text/plain, Size: 3872 bytes --] On 2023-01-14 19:00, Ludovic Courtès wrote: > Hello! > > David Wilson <david@daviwil.com> skribis: > >> Yes, I saw that you meant to run Emacs as a daemon and I think it should >> be added to this service (or another daemon-specific service) in a >> future patch. I figured it would be easier to get a patch accepted >> without the daemon functionality just yet since there seemed to be more >> feedback about that aspect in your patch thread. > > Agree, I think we should add the daemon functionality in a subsequent > patch. > > Overall it looks nice to me! Some comments and suggestions: > >> +@node Emacs Home Services >> +@subsection Emacs Home Services >> + >> +@defvr {Scheme Variable} home-emacs-service-type > > It would be nice if you could start the section with a few sentences > explaining the rationale and what’s being described here. > >> +This is the service type for configuring the Emacs text editor. It >> +enables you to assemble @file{init.el} and @file{early-init.el} files >> +from snippets in your home configuration and other Emacs Lisp files you >> +have in your personal configuration folder. > > Maybe like so: > > … to assemble the @file{init.el} (@pxref{Init File,,, emacs, GNU Emacs > Manual}) and @file{early-init.el} (@pxref{Early Init File,,, emacs, > GNU Emacs Manual}) files … > >> +@example >> +$ cp ~/.emacs.d $XDG_CONFIG_HOME/emacs >> +$ cp ~/.emacs $XDG_CONFIG_HOME/emacs/init.el >> +@end example > > I’d drop the prompt. > >> +@item @code{init-file} (default: @code{'()}) >> +Configuration text or files to include in @file{init.el}. >> + >> +@item @code{early-init-file} (default: @code{'()}) >> +Configuration text or files to include in @file{early-init.el}. > > What about accepting sexps (or gexps) instead of strings? As in: > > (init-file '((require 'whatever) (setq something t))) A quick minor note on this approach: it won't be possible to use #'elisp-function inside such configuration because it will be interpreted by guile reader, but actually rde lives without this functionality completely ok. Do we want something like this possible? (init-file `((require 'whatever) (setq something t) (load ,(local-file "old-init.el"))) > > Also I find it confusing that it’s either text or files. In the code it > has type ‘text-config’, which means list of file-like objects IIUC, no? > >> +@item @code{load-paths} (default: @code{'()}) >> +Additional load paths to add to Emacs' @code{load-path} variable. Lines >> +will be inserted at the beginning of @file{early-init.el}. > > Nitpick: I think this should be ‘load-path’ (singular), because it’s one > search path (i.e., a list of directories). > > And: s/Additional load paths/Additional directories/ > >> +@end table >> +@end deftp > > Would be nice to have a couple of commented examples here, like you had > in the first message in this thread. :-) > >> +@deftp {Data Type} home-emacs-extension >> +The extension record for @code{home-emacs-service-type}. > > Would be nice to have a sentence above, like “This service type can be > extended with @code{home-emacs-extension} records, described below:”. > >> +@table @asis >> +@item @code{packages} (default: @code{'()}) >> +Additional packages required by the Emacs configuration. >> + >> +@item @code{init-file} (default: @code{'()}) >> +Configuration text or files to include in @file{init.el}. >> + >> +@item @code{early-init-file} (default: @code{'()}) >> +Configuration text or files to include in @file{early-init.el}. >> + >> +@item @code{load-paths} (default: @code{'()}) > > Ditto. > >> +@end deftp > > Would be great to have an example of that too. :-) > > Thank you! > > Ludo’. > > > -- Best regards, Andrew Tropin [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 832 bytes --] ^ permalink raw reply [flat|nested] 19+ messages in thread
* [bug#60753] [PATCH] gnu: home: Add home-emacs-service-type. 2023-01-15 8:02 ` Andrew Tropin @ 2023-01-17 9:02 ` Ludovic Courtès 2023-01-17 14:46 ` Andrew Tropin 2023-01-26 18:50 ` Reily Siegel 0 siblings, 2 replies; 19+ messages in thread From: Ludovic Courtès @ 2023-01-17 9:02 UTC (permalink / raw) To: Andrew Tropin; +Cc: (, David Wilson, 60753 Hi, Andrew Tropin <andrew@trop.in> skribis: >> What about accepting sexps (or gexps) instead of strings? As in: >> >> (init-file '((require 'whatever) (setq something t))) > > A quick minor note on this approach: it won't be possible to use > #'elisp-function inside such configuration because it will be > interpreted by guile reader, but actually rde lives without this > functionality completely ok. Specifically: (write '#'x) |= (syntax x) But we can use (guix read-print) and ensure that it prints #'. > Do we want something like this possible? > > (init-file `((require 'whatever) > (setq something t) > (load ,(local-file "old-init.el"))) It’d be nice. In that case, we’ll want it to be a gexp though: #~((require 'whatever) (load #$(local-file …))) Thanks, Ludo’. ^ permalink raw reply [flat|nested] 19+ messages in thread
* [bug#60753] [PATCH] gnu: home: Add home-emacs-service-type. 2023-01-17 9:02 ` Ludovic Courtès @ 2023-01-17 14:46 ` Andrew Tropin 2023-01-23 10:18 ` Ludovic Courtès 2023-01-26 18:50 ` Reily Siegel 1 sibling, 1 reply; 19+ messages in thread From: Andrew Tropin @ 2023-01-17 14:46 UTC (permalink / raw) To: Ludovic Courtès; +Cc: (, David Wilson, 60753 [-- Attachment #1: Type: text/plain, Size: 1434 bytes --] On 2023-01-17 10:02, Ludovic Courtès wrote: > Hi, > > Andrew Tropin <andrew@trop.in> skribis: > >>> What about accepting sexps (or gexps) instead of strings? As in: >>> >>> (init-file '((require 'whatever) (setq something t))) >> >> A quick minor note on this approach: it won't be possible to use >> #'elisp-function inside such configuration because it will be >> interpreted by guile reader, but actually rde lives without this >> functionality completely ok. > > Specifically: > > (write '#'x) > |= (syntax x) > > But we can use (guix read-print) and ensure that it prints #'. > Do you have any links to docs/sample implementations on the topic of extending guile reader, so we have an example to start with? Does guix workflow language do something like that? I think it will be cool to hook up a custom reader, ideally comment preserving, for emacs lisp inside scheme files. >> Do we want something like this possible? >> >> (init-file `((require 'whatever) >> (setq something t) >> (load ,(local-file "old-init.el"))) > > It’d be nice. In that case, we’ll want it to be a gexp though: > > #~((require 'whatever) (load #$(local-file …))) > gexps are nice, but do we really need/want them here? Do you have any thoughts on what are the benifits over quasiquotes in this case? Maybe some examples? -- Best regards, Andrew Tropin [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 832 bytes --] ^ permalink raw reply [flat|nested] 19+ messages in thread
* [bug#60753] [PATCH] gnu: home: Add home-emacs-service-type. 2023-01-17 14:46 ` Andrew Tropin @ 2023-01-23 10:18 ` Ludovic Courtès 2023-01-26 5:06 ` Andrew Tropin 0 siblings, 1 reply; 19+ messages in thread From: Ludovic Courtès @ 2023-01-23 10:18 UTC (permalink / raw) To: Andrew Tropin; +Cc: (, David Wilson, 60753 Hi, Andrew Tropin <andrew@trop.in> skribis: > On 2023-01-17 10:02, Ludovic Courtès wrote: > >> Hi, >> >> Andrew Tropin <andrew@trop.in> skribis: >> >>>> What about accepting sexps (or gexps) instead of strings? As in: >>>> >>>> (init-file '((require 'whatever) (setq something t))) >>> >>> A quick minor note on this approach: it won't be possible to use >>> #'elisp-function inside such configuration because it will be >>> interpreted by guile reader, but actually rde lives without this >>> functionality completely ok. >> >> Specifically: >> >> (write '#'x) >> |= (syntax x) >> >> But we can use (guix read-print) and ensure that it prints #'. >> > > Do you have any links to docs/sample implementations on the topic of > extending guile reader, so we have an example to start with? It’s not the reader but rather the writer that we’d want to tweak. In (guix read-print), ‘pretty-print-with-comments’ already special cases quasiquote etc. so that it prints ‘`’ (backtick) and not ‘quasiquote'. We’d add clauses for ‘syntax’ and ‘quasisyntax’. > I think it will be cool to hook up a custom reader, ideally comment > preserving, for emacs lisp inside scheme files. (guix read-print) is what you want. :-) >>> Do we want something like this possible? >>> >>> (init-file `((require 'whatever) >>> (setq something t) >>> (load ,(local-file "old-init.el"))) >> >> It’d be nice. In that case, we’ll want it to be a gexp though: >> >> #~((require 'whatever) (load #$(local-file …))) >> > > gexps are nice, but do we really need/want them here? Do you have any > thoughts on what are the benifits over quasiquotes in this case? Maybe > some examples? The benefit in the example above is that the gexp would actually work whereas the sexp wouldn’t :-), unless there’s code somewhere to manually traverse the sexp adn replace the <local-file> record with its store item (which is what gexps are about). I hope that makes sense! Ludo’. ^ permalink raw reply [flat|nested] 19+ messages in thread
* [bug#60753] [PATCH] gnu: home: Add home-emacs-service-type. 2023-01-23 10:18 ` Ludovic Courtès @ 2023-01-26 5:06 ` Andrew Tropin 2023-01-31 16:26 ` Ludovic Courtès 2023-02-01 12:59 ` Jelle Licht 0 siblings, 2 replies; 19+ messages in thread From: Andrew Tropin @ 2023-01-26 5:06 UTC (permalink / raw) To: Ludovic Courtès; +Cc: (, David Wilson, 60753 [-- Attachment #1: Type: text/plain, Size: 4273 bytes --] On 2023-01-23 11:18, Ludovic Courtès wrote: > Hi, > > Andrew Tropin <andrew@trop.in> skribis: > >> On 2023-01-17 10:02, Ludovic Courtès wrote: >> >>> Hi, >>> >>> Andrew Tropin <andrew@trop.in> skribis: >>> >>>>> What about accepting sexps (or gexps) instead of strings? As in: >>>>> >>>>> (init-file '((require 'whatever) (setq something t))) >>>> >>>> A quick minor note on this approach: it won't be possible to use >>>> #'elisp-function inside such configuration because it will be >>>> interpreted by guile reader, but actually rde lives without this >>>> functionality completely ok. >>> >>> Specifically: >>> >>> (write '#'x) >>> |= (syntax x) >>> >>> But we can use (guix read-print) and ensure that it prints #'. >>> >> >> Do you have any links to docs/sample implementations on the topic of >> extending guile reader, so we have an example to start with? > > It’s not the reader but rather the writer that we’d want to tweak. Right, it already can read #'x as (syntax x) and we can print it properly later, but AFAIK comments are ignored by the default reader. So I would expect to do something (very roughly) like this: --8<---------------cut here---------------start------------->8--- (parameterize (((@@ (guix gexp) read-procedure) read-with-comments)) #~(list 'hello ; Comment I would like to preserve during serialization 'guix)) --8<---------------cut here---------------end--------------->8--- Of course it doesn't work, but I hope demonstrates the idea. > > In (guix read-print), ‘pretty-print-with-comments’ already special > cases quasiquote etc. so that it prints ‘`’ (backtick) and not > ‘quasiquote'. We’d add clauses for ‘syntax’ and ‘quasisyntax’. > It seems ice-9 pretty-print also preserves backticks, but I see that pretty-print-with-comments also preserves gexps, which is cool. Adding syntax will make it even cooler. >> I think it will be cool to hook up a custom reader, ideally comment >> preserving, for emacs lisp inside scheme files. > > (guix read-print) is what you want. :-) > Can you give a hint on how to use it for preserving comments, please? >>>> Do we want something like this possible? >>>> >>>> (init-file `((require 'whatever) >>>> (setq something t) >>>> (load ,(local-file "old-init.el"))) >>> >>> It’d be nice. In that case, we’ll want it to be a gexp though: >>> >>> #~((require 'whatever) (load #$(local-file …))) >>> >> >> gexps are nice, but do we really need/want them here? Do you have any >> thoughts on what are the benifits over quasiquotes in this case? Maybe >> some examples? > > The benefit in the example above is that the gexp would actually work > whereas the sexp wouldn’t :-), unless there’s code somewhere to manually > traverse the sexp adn replace the <local-file> record with its store > item (which is what gexps are about). > > I hope that makes sense! With this simple serializer we already achieved quite good results: https://git.sr.ht/~abcdw/rde/tree/388d3ad95e8607543df3dcdf26d058b610e77389/src/rde/serializers/lisp.scm#L35 For this input --8<---------------cut here---------------start------------->8--- `((load ,(local-file "./feature-lists.scm")) ,#~(format #f "hello") ; top level gexps are evaluated (list ,#~(format #f "hello")) ; nested gexps are not ,#~";; hacky comment" ;; comment, which is not preserved #'hi-fn ; incorrectly serialized, but fixable by alternative ; pretty-print ) --8<---------------cut here---------------end--------------->8--- it provides quite satisfying results: --8<---------------cut here---------------start------------->8--- (load "/gnu/store/xb6ma0mcgg1zzq645s63arvy3qskmbiz-feature-lists.scm") hello (list (format #f "hello")) ;; hacky comment (syntax hi-fn) --8<---------------cut here---------------end--------------->8--- It's a little incosistent (top level gexp are evaluated, but nested are not), comments are not preserved and #' serialized incorrectly, but other than that it works very good. WDYT about overall approach used here? or we can do it radically better? -- Best regards, Andrew Tropin [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 832 bytes --] ^ permalink raw reply [flat|nested] 19+ messages in thread
* [bug#60753] [PATCH] gnu: home: Add home-emacs-service-type. 2023-01-26 5:06 ` Andrew Tropin @ 2023-01-31 16:26 ` Ludovic Courtès 2023-02-01 14:06 ` Andrew Tropin 2023-02-10 7:50 ` David Wilson 2023-02-01 12:59 ` Jelle Licht 1 sibling, 2 replies; 19+ messages in thread From: Ludovic Courtès @ 2023-01-31 16:26 UTC (permalink / raw) To: Andrew Tropin; +Cc: (, David Wilson, 60753 Hi Andrew, Andrew Tropin <andrew@trop.in> skribis: >>> I think it will be cool to hook up a custom reader, ideally comment >>> preserving, for emacs lisp inside scheme files. >> >> (guix read-print) is what you want. :-) >> > > Can you give a hint on how to use it for preserving comments, please? It can be used like this: --8<---------------cut here---------------start------------->8--- scheme@(guile-user)> ,use(guix read-print) scheme@(guile-user)> (pretty-print-with-comments (current-output-port) `(list foo ,(comment ";ooh!\n" #t) bar)) (list foo ;ooh! bar)$5 = 10 scheme@(guile-user)> (call-with-input-string "(list foo ;oh!\nbar)" read-with-comments) $6 = (list foo #<<comment> str: ";oh!\n" margin?: #t> bar) --8<---------------cut here---------------end--------------->8--- There’s a <comment> record type. Now let’s see perhaps what we need to get ‘home-emacs-service-type’ merged, and what we can keep as future work. Thoughts? Ludo’. ^ permalink raw reply [flat|nested] 19+ messages in thread
* [bug#60753] [PATCH] gnu: home: Add home-emacs-service-type. 2023-01-31 16:26 ` Ludovic Courtès @ 2023-02-01 14:06 ` Andrew Tropin 2023-02-10 7:50 ` David Wilson 1 sibling, 0 replies; 19+ messages in thread From: Andrew Tropin @ 2023-02-01 14:06 UTC (permalink / raw) To: Ludovic Courtès; +Cc: (, David Wilson, 60753 [-- Attachment #1: Type: text/plain, Size: 2247 bytes --] On 2023-01-31 17:26, Ludovic Courtès wrote: > Hi Andrew, > > Andrew Tropin <andrew@trop.in> skribis: > >>>> I think it will be cool to hook up a custom reader, ideally comment >>>> preserving, for emacs lisp inside scheme files. >>> >>> (guix read-print) is what you want. :-) >>> >> >> Can you give a hint on how to use it for preserving comments, please? > > It can be used like this: > > --8<---------------cut here---------------start------------->8--- > scheme@(guile-user)> ,use(guix read-print) > scheme@(guile-user)> (pretty-print-with-comments (current-output-port) `(list foo ,(comment ";ooh!\n" #t) bar)) > (list foo ;ooh! > bar)$5 = 10 > scheme@(guile-user)> (call-with-input-string "(list foo ;oh!\nbar)" read-with-comments) > $6 = (list foo #<<comment> str: ";oh!\n" margin?: #t> bar) > --8<---------------cut here---------------end--------------->8--- > > There’s a <comment> record type. Yep, I already experimented with it, but it's not exactly what I'm looking for, Jelle gave a few ideas and code snippets which looks closer to what I searching. Pretty printer and read function are useful, but not directly related to my question about reader. > > Now let’s see perhaps what we need to get ‘home-emacs-service-type’ > merged, and what we can keep as future work. Thoughts? We have not perfect, but working quite good implementation of lisp serializer in rde, which we use in rde's home-emacs-service-type: https://git.sr.ht/~abcdw/rde/tree/d0ea604282c9aeb0b121f51979373b4aa40f9bcb/item/tests/rde/serializers/lisp-test.scm https://git.sr.ht/~abcdw/rde/tree/d0ea604282c9aeb0b121f51979373b4aa40f9bcb/item/src/rde/home/services/emacs.scm#L93 But I'm not completely satisfyied with it and don't want to upstream it in current state, I plan to experiment with Jelle's code and get a feeling of how his approach works, also want to dive into reader extensions topic. If I get satisfying solution I will share it. It can take quite a while, so don't expect it to happen anytime soon; it can, but unlikely :) If anyone want to work in parallel and propose alternative implementation, I would be glad to give a feedback on it. -- Best regards, Andrew Tropin [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 832 bytes --] ^ permalink raw reply [flat|nested] 19+ messages in thread
* [bug#60753] [PATCH] gnu: home: Add home-emacs-service-type. 2023-01-31 16:26 ` Ludovic Courtès 2023-02-01 14:06 ` Andrew Tropin @ 2023-02-10 7:50 ` David Wilson 2023-02-20 11:10 ` Ludovic Courtès 1 sibling, 1 reply; 19+ messages in thread From: David Wilson @ 2023-02-10 7:50 UTC (permalink / raw) To: Ludovic Courtès; +Cc: (, 60753, Andrew Tropin Ludovic Courtès <ludo@gnu.org> writes: > Now let’s see perhaps what we need to get ‘home-emacs-service-type’ > merged, and what we can keep as future work. Thoughts? Sorry for the long delay in getting back to this! To summarize what we've discussed so far: - It's possibly best to use g-expressions for the init.el file contents because it gives ultimate flexibility on how one can describe their Emacs configuration in Scheme while pulling in files from their local configuration folder. Are we concerned whether this may be harder for users to understand and adopt? - We'll postpone any work to ensure that comments and #'function references can be properly represented so that we can get the basic `home-emacs-service' merged for now. Future patches will improve on that situation. Is that accurate? Either way, I'll spend some time today responding to the feedback so that we can get this merged. Thanks! David ^ permalink raw reply [flat|nested] 19+ messages in thread
* [bug#60753] [PATCH] gnu: home: Add home-emacs-service-type. 2023-02-10 7:50 ` David Wilson @ 2023-02-20 11:10 ` Ludovic Courtès 0 siblings, 0 replies; 19+ messages in thread From: Ludovic Courtès @ 2023-02-20 11:10 UTC (permalink / raw) To: David Wilson; +Cc: (, 60753, Andrew Tropin Hi David, David Wilson <david@daviwil.com> skribis: > Ludovic Courtès <ludo@gnu.org> writes: > >> Now let’s see perhaps what we need to get ‘home-emacs-service-type’ >> merged, and what we can keep as future work. Thoughts? > > Sorry for the long delay in getting back to this! To summarize what > we've discussed so far: > > - It's possibly best to use g-expressions for the init.el file contents > because it gives ultimate flexibility on how one can describe their > Emacs configuration in Scheme while pulling in files from their local > configuration folder. Are we concerned whether this may be harder for > users to understand and adopt? Ease of use should always be a concern IMO. I’d expect that writing gexps will feel more convenient than in sexps-in-Scheme-strings for the target Elisp audience. > - We'll postpone any work to ensure that comments and #'function > references can be properly represented so that we can get the basic > `home-emacs-service' merged for now. Future patches will improve on > that situation. > > Is that accurate? Either way, I'll spend some time today responding to > the feedback so that we can get this merged. Sounds good to me! Ludo’. ^ permalink raw reply [flat|nested] 19+ messages in thread
* [bug#60753] [PATCH] gnu: home: Add home-emacs-service-type. 2023-01-26 5:06 ` Andrew Tropin 2023-01-31 16:26 ` Ludovic Courtès @ 2023-02-01 12:59 ` Jelle Licht 2023-02-01 13:46 ` Andrew Tropin 1 sibling, 1 reply; 19+ messages in thread From: Jelle Licht @ 2023-02-01 12:59 UTC (permalink / raw) To: Andrew Tropin, Ludovic Courtès; +Cc: (, David Wilson, 60753 Andrew Tropin <andrew@trop.in> writes: > On 2023-01-23 11:18, Ludovic Courtès wrote: > >> Hi, >> >> Andrew Tropin <andrew@trop.in> skribis: >> >>> On 2023-01-17 10:02, Ludovic Courtès wrote: >>> >>>> Hi, >>>> >>>> Andrew Tropin <andrew@trop.in> skribis: >>>> >>>>>> What about accepting sexps (or gexps) instead of strings? As in: >>>>>> >>>>>> (init-file '((require 'whatever) (setq something t))) >>>>> >>>>> A quick minor note on this approach: it won't be possible to use >>>>> #'elisp-function inside such configuration because it will be >>>>> interpreted by guile reader, but actually rde lives without this >>>>> functionality completely ok. >>>> >>>> Specifically: >>>> >>>> (write '#'x) >>>> |= (syntax x) >>>> >>>> But we can use (guix read-print) and ensure that it prints #'. >>>> >>> >>> Do you have any links to docs/sample implementations on the topic of >>> extending guile reader, so we have an example to start with? >> >> It’s not the reader but rather the writer that we’d want to tweak. > > Right, it already can read #'x as (syntax x) and we can print it > properly later, but AFAIK comments are ignored by the default reader. > So I would expect to do something (very roughly) like this: > > --8<---------------cut here---------------start------------->8--- > (parameterize (((@@ (guix gexp) read-procedure) read-with-comments)) > #~(list 'hello ; Comment I would like to preserve during serialization > 'guix)) > --8<---------------cut here---------------end--------------->8--- > > Of course it doesn't work, but I hope demonstrates the idea. > >> >> In (guix read-print), ‘pretty-print-with-comments’ already special >> cases quasiquote etc. so that it prints ‘`’ (backtick) and not >> ‘quasiquote'. We’d add clauses for ‘syntax’ and ‘quasisyntax’. >> > > It seems ice-9 pretty-print also preserves backticks, but I see that > pretty-print-with-comments also preserves gexps, which is cool. Adding > syntax will make it even cooler. > >>> I think it will be cool to hook up a custom reader, ideally comment >>> preserving, for emacs lisp inside scheme files. >> >> (guix read-print) is what you want. :-) >> > > Can you give a hint on how to use it for preserving comments, please? > >>>>> Do we want something like this possible? >>>>> >>>>> (init-file `((require 'whatever) >>>>> (setq something t) >>>>> (load ,(local-file "old-init.el"))) >>>> >>>> It’d be nice. In that case, we’ll want it to be a gexp though: >>>> >>>> #~((require 'whatever) (load #$(local-file …))) >>>> >>> >>> gexps are nice, but do we really need/want them here? Do you have any >>> thoughts on what are the benifits over quasiquotes in this case? Maybe >>> some examples? >> >> The benefit in the example above is that the gexp would actually work >> whereas the sexp wouldn’t :-), unless there’s code somewhere to manually >> traverse the sexp adn replace the <local-file> record with its store >> item (which is what gexps are about). >> >> I hope that makes sense! > > With this simple serializer we already achieved quite good results: > https://git.sr.ht/~abcdw/rde/tree/388d3ad95e8607543df3dcdf26d058b610e77389/src/rde/serializers/lisp.scm#L35 > > For this input > --8<---------------cut here---------------start------------->8--- > `((load ,(local-file "./feature-lists.scm")) > ,#~(format #f "hello") ; top level gexps are evaluated > (list ,#~(format #f "hello")) ; nested gexps are not > ,#~";; hacky comment" > ;; comment, which is not preserved > #'hi-fn ; incorrectly serialized, but fixable by alternative > ; pretty-print > ) > --8<---------------cut here---------------end--------------->8--- > > it provides quite satisfying results: > --8<---------------cut here---------------start------------->8--- > (load "/gnu/store/xb6ma0mcgg1zzq645s63arvy3qskmbiz-feature-lists.scm") > hello > (list (format #f "hello")) > ;; hacky comment > (syntax hi-fn) > --8<---------------cut here---------------end--------------->8--- > > It's a little incosistent (top level gexp are evaluated, but nested are > not), comments are not preserved and #' serialized incorrectly, but > other than that it works very good. > > WDYT about overall approach used here? or we can do it radically > better? Not saying it's better in any particular way, but I have had this locally for all my elisp-read-by-guile-written-back-to-elisp needs: --8<---------------cut here---------------start------------->8--- (define-module (jlicht build elisp-write) #:use-module (ice-9 match) #:use-module (srfi srfi-1) #:export (elisp-write)) (define (elisp-write in-list? exp port) "Stack-blowing implementation that writes guile's internal elisp representation to something that can be parsed by Emacs." ;; Definitions from (language elisp parser)'s quotation-symbols: (define symbol-strings '((#{`}# . "`") (#{,}# . ",") (#{,@}# . ",@"))) (define (elisp-symbol? sym) (assq sym symbol-strings)) (define (write-elisp-symbol sym port) (format port "~A" (assq-ref symbol-strings sym))) (match exp (((? elisp-symbol? sym) rest) (write-elisp-symbol sym port) (elisp-write in-list? rest port)) ;; Vector expression (#(vs ...) (format port "[") (elisp-write #t vs port) (format port "]")) ;; Guile elisp implementation detail ('(%set-lexical-binding-mode #f) 'skip) ;; List walker ((e ...) (when (not in-list?) (format port "(")) (unless (null? e) (elisp-write #f (car e) port) (for-each (lambda (v) (format port " ") (elisp-write #f v port)) (cdr e))) (when (not in-list?) (format port ")"))) ;; dotted pair ((and (? pair?) (? dotted-list? l)) (format port "(") (elisp-write #t (drop-right l 0) port) (format port " . ") (elisp-write #t (take-right l 0) port) (format port ")")) ;; Print simple primitives (_ (write exp port)))) --8<---------------cut here---------------end--------------->8--- On the reader side I just use guile's elisp reader: --8<---------------cut here---------------start------------->8--- (define-module (jlicht test elisp) #:use-module (language elisp parser) #:use-module (jlicht build elisp-write) #:use-module (srfi srfi-26) #:use-module (srfi srfi-64)) (eval-when (expand load eval) (read-hash-extend #\e (lambda (chr port) (read-elisp port)))) (set! test-log-to-file #f) (define (roundtrip expr) (let ((written (call-with-output-string (cut elisp-write #f expr <>)))) (call-with-input-string written read-elisp))) (define-syntax test-roundtrip-equals (syntax-rules () ((_ expr) (let ((e1 (roundtrip expr))) (test-equal e1 (roundtrip e1)))))) (define runner (test-runner-simple)) (test-with-runner runner (test-begin "roundtrip-elisp-fixed-point") (test-roundtrip-equals 12) (test-roundtrip-equals "hello") (test-roundtrip-equals '#e#'my-fn) (test-roundtrip-equals '#e[a b c]) (test-roundtrip-equals '#e`(+ 1 2 ,@(a b) ,c)) (test-end "roundtrip-elisp-fixed-point")) (exit (test-runner-fail-count runner)) --8<---------------cut here---------------end--------------->8--- I've also hooked it up in combination with a sequence of calls to `scheme-file' -> `computed-file' called `elisp-file', but that's a bit more hacky and less relevant to the current discussion. - Jelle ^ permalink raw reply [flat|nested] 19+ messages in thread
* [bug#60753] [PATCH] gnu: home: Add home-emacs-service-type. 2023-02-01 12:59 ` Jelle Licht @ 2023-02-01 13:46 ` Andrew Tropin 0 siblings, 0 replies; 19+ messages in thread From: Andrew Tropin @ 2023-02-01 13:46 UTC (permalink / raw) To: Jelle Licht, Ludovic Courtès; +Cc: (, David Wilson, 60753 [-- Attachment #1: Type: text/plain, Size: 8584 bytes --] On 2023-02-01 12:59, Jelle Licht wrote: > Andrew Tropin <andrew@trop.in> writes: > >> On 2023-01-23 11:18, Ludovic Courtès wrote: >> >>> Hi, >>> >>> Andrew Tropin <andrew@trop.in> skribis: >>> >>>> On 2023-01-17 10:02, Ludovic Courtès wrote: >>>> >>>>> Hi, >>>>> >>>>> Andrew Tropin <andrew@trop.in> skribis: >>>>> >>>>>>> What about accepting sexps (or gexps) instead of strings? As in: >>>>>>> >>>>>>> (init-file '((require 'whatever) (setq something t))) >>>>>> >>>>>> A quick minor note on this approach: it won't be possible to use >>>>>> #'elisp-function inside such configuration because it will be >>>>>> interpreted by guile reader, but actually rde lives without this >>>>>> functionality completely ok. >>>>> >>>>> Specifically: >>>>> >>>>> (write '#'x) >>>>> |= (syntax x) >>>>> >>>>> But we can use (guix read-print) and ensure that it prints #'. >>>>> >>>> >>>> Do you have any links to docs/sample implementations on the topic of >>>> extending guile reader, so we have an example to start with? >>> >>> It’s not the reader but rather the writer that we’d want to tweak. >> >> Right, it already can read #'x as (syntax x) and we can print it >> properly later, but AFAIK comments are ignored by the default reader. >> So I would expect to do something (very roughly) like this: >> >> --8<---------------cut here---------------start------------->8--- >> (parameterize (((@@ (guix gexp) read-procedure) read-with-comments)) >> #~(list 'hello ; Comment I would like to preserve during serialization >> 'guix)) >> --8<---------------cut here---------------end--------------->8--- >> >> Of course it doesn't work, but I hope demonstrates the idea. >> >>> >>> In (guix read-print), ‘pretty-print-with-comments’ already special >>> cases quasiquote etc. so that it prints ‘`’ (backtick) and not >>> ‘quasiquote'. We’d add clauses for ‘syntax’ and ‘quasisyntax’. >>> >> >> It seems ice-9 pretty-print also preserves backticks, but I see that >> pretty-print-with-comments also preserves gexps, which is cool. Adding >> syntax will make it even cooler. >> >>>> I think it will be cool to hook up a custom reader, ideally comment >>>> preserving, for emacs lisp inside scheme files. >>> >>> (guix read-print) is what you want. :-) >>> >> >> Can you give a hint on how to use it for preserving comments, please? >> >>>>>> Do we want something like this possible? >>>>>> >>>>>> (init-file `((require 'whatever) >>>>>> (setq something t) >>>>>> (load ,(local-file "old-init.el"))) >>>>> >>>>> It’d be nice. In that case, we’ll want it to be a gexp though: >>>>> >>>>> #~((require 'whatever) (load #$(local-file …))) >>>>> >>>> >>>> gexps are nice, but do we really need/want them here? Do you have any >>>> thoughts on what are the benifits over quasiquotes in this case? Maybe >>>> some examples? >>> >>> The benefit in the example above is that the gexp would actually work >>> whereas the sexp wouldn’t :-), unless there’s code somewhere to manually >>> traverse the sexp adn replace the <local-file> record with its store >>> item (which is what gexps are about). >>> >>> I hope that makes sense! >> >> With this simple serializer we already achieved quite good results: >> https://git.sr.ht/~abcdw/rde/tree/388d3ad95e8607543df3dcdf26d058b610e77389/src/rde/serializers/lisp.scm#L35 >> >> For this input >> --8<---------------cut here---------------start------------->8--- >> `((load ,(local-file "./feature-lists.scm")) >> ,#~(format #f "hello") ; top level gexps are evaluated >> (list ,#~(format #f "hello")) ; nested gexps are not >> ,#~";; hacky comment" >> ;; comment, which is not preserved >> #'hi-fn ; incorrectly serialized, but fixable by alternative >> ; pretty-print >> ) >> --8<---------------cut here---------------end--------------->8--- >> >> it provides quite satisfying results: >> --8<---------------cut here---------------start------------->8--- >> (load "/gnu/store/xb6ma0mcgg1zzq645s63arvy3qskmbiz-feature-lists.scm") >> hello >> (list (format #f "hello")) >> ;; hacky comment >> (syntax hi-fn) >> --8<---------------cut here---------------end--------------->8--- >> >> It's a little incosistent (top level gexp are evaluated, but nested are >> not), comments are not preserved and #' serialized incorrectly, but >> other than that it works very good. >> >> WDYT about overall approach used here? or we can do it radically >> better? > > Not saying it's better in any particular way, but I have had this locally > for all my elisp-read-by-guile-written-back-to-elisp needs: I saw it in guix-home-manager and probably you've made the thread on rde-devel too. I tried some parts of this code, but didn't succeed to get a complete working out of it, now I have a little more knowledge and hope will get better results :) > > --8<---------------cut here---------------start------------->8--- > (define-module (jlicht build elisp-write) > #:use-module (ice-9 match) > #:use-module (srfi srfi-1) > #:export (elisp-write)) > > (define (elisp-write in-list? exp port) > "Stack-blowing implementation that writes guile's internal elisp > representation to something that can be parsed by Emacs." > ;; Definitions from (language elisp parser)'s quotation-symbols: > (define symbol-strings > '((#{`}# . "`") > (#{,}# . ",") > (#{,@}# . ",@"))) > (define (elisp-symbol? sym) > (assq sym symbol-strings)) > (define (write-elisp-symbol sym port) > (format port "~A" (assq-ref symbol-strings sym))) > > (match exp > (((? elisp-symbol? sym) rest) > (write-elisp-symbol sym port) > (elisp-write in-list? rest port)) > ;; Vector expression > (#(vs ...) > (format port "[") > (elisp-write #t vs port) > (format port "]")) > ;; Guile elisp implementation detail > ('(%set-lexical-binding-mode #f) 'skip) > ;; List walker > ((e ...) > (when (not in-list?) (format port "(")) > (unless (null? e) > (elisp-write #f (car e) port) > (for-each (lambda (v) > (format port " ") > (elisp-write #f v port)) (cdr e))) > (when (not in-list?) (format port ")"))) > ;; dotted pair > ((and (? pair?) (? dotted-list? l)) > (format port "(") > (elisp-write #t (drop-right l 0) port) > (format port " . ") > (elisp-write #t (take-right l 0) port) > (format port ")")) > ;; Print simple primitives > (_ (write exp port)))) > --8<---------------cut here---------------end--------------->8--- > > On the reader side I just use guile's elisp reader: > > --8<---------------cut here---------------start------------->8--- > (define-module (jlicht test elisp) > #:use-module (language elisp parser) > #:use-module (jlicht build elisp-write) > #:use-module (srfi srfi-26) > #:use-module (srfi srfi-64)) > > (eval-when (expand load eval) > (read-hash-extend #\e (lambda (chr port) (read-elisp port)))) That's what I was looking for. If you have any links related to the topic of the reader extension, please let me know. > > (set! test-log-to-file #f) > > (define (roundtrip expr) > (let ((written (call-with-output-string (cut elisp-write #f expr <>)))) > (call-with-input-string written read-elisp))) > > (define-syntax test-roundtrip-equals > (syntax-rules () > ((_ expr) > (let ((e1 (roundtrip expr))) > (test-equal e1 (roundtrip e1)))))) > > (define runner (test-runner-simple)) > > (test-with-runner runner > (test-begin "roundtrip-elisp-fixed-point") > (test-roundtrip-equals 12) > (test-roundtrip-equals "hello") > (test-roundtrip-equals '#e#'my-fn) > (test-roundtrip-equals '#e[a b c]) > (test-roundtrip-equals '#e`(+ 1 2 ,@(a b) ,c)) It would be cool to make elisp-unquote for #e, but I think I can take a look at ungexp to understand how to implement it. > (test-end "roundtrip-elisp-fixed-point")) > > (exit (test-runner-fail-count runner)) > --8<---------------cut here---------------end--------------->8--- > > I've also hooked it up in combination with a sequence of calls to > `scheme-file' -> `computed-file' called `elisp-file', but that's a bit > more hacky and less relevant to the current discussion. > > - Jelle Thank you very much! -- Best regards, Andrew Tropin [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 832 bytes --] ^ permalink raw reply [flat|nested] 19+ messages in thread
* [bug#60753] [PATCH] gnu: home: Add home-emacs-service-type. 2023-01-17 9:02 ` Ludovic Courtès 2023-01-17 14:46 ` Andrew Tropin @ 2023-01-26 18:50 ` Reily Siegel 1 sibling, 0 replies; 19+ messages in thread From: Reily Siegel @ 2023-01-26 18:50 UTC (permalink / raw) To: Ludovic Courtès, Andrew Tropin; +Cc: (, David Wilson, 60753 Ludovic Courtès <ludo@gnu.org> writes: > Specifically: > > (write '#'x) > |= (syntax x) > > But we can use (guix read-print) and ensure that it prints #'. The way I get around this in my config is adding #+begin_source emacs-lisp ;; #' exports a scheme (syntax ...) form. Treat this as a ;; (function ...) form. (defalias 'syntax 'function) #+end_source This is a very hacky solution, but if you wanted to not modify the reader, you could add this to the beginning of init.el with the service. -- Reily Siegel ^ permalink raw reply [flat|nested] 19+ messages in thread
* [bug#60753] [PATCH] gnu: home: Add home-emacs-service-type. 2023-01-14 18:00 ` Ludovic Courtès 2023-01-15 8:02 ` Andrew Tropin @ 2023-01-16 9:25 ` David Wilson 1 sibling, 0 replies; 19+ messages in thread From: David Wilson @ 2023-01-16 9:25 UTC (permalink / raw) To: Ludovic Courtès; +Cc: (, 60753 Thanks for the feedback, Ludo! I'll make the requested changes and send them later today. I may also try adding some tests to ensure that the output is what we expect. There seems to be a `tests/services` folder, should I add a `tests/home-services` folder to match? Ludovic Courtès <ludo@gnu.org> writes: > It would be nice if you could start the section with a few sentences > explaining the rationale and what’s being described here. Will do, I'll look at other services to get an idea of the best way to do this. > What about accepting sexps (or gexps) instead of strings? As in: > > (init-file '((require 'whatever) (setq something t))) > > Also I find it confusing that it’s either text or files. In the code it > has type ‘text-config’, which means list of file-like objects IIUC, no? Yes, this was a misunderstanding on my part! Regarding using s-expressions for this, it would certainly look cleaner. Andrew raises a good concern about it in his response, I'll take a look at what RDE does for this and see if I can adopt a similar approach. > Nitpick: I think this should be ‘load-path’ (singular), because it’s one > search path (i.e., a list of directories). > > And: s/Additional load paths/Additional directories/ Will change both of these, thanks! > Would be nice to have a couple of commented examples here, like you had > in the first message in this thread. :-) Yep, I'll add examples for all the use cases here. > Would be nice to have a sentence above, like “This service type can be > extended with @code{home-emacs-extension} records, described below:”. Will describe and give an example of how to extend the service! David ^ permalink raw reply [flat|nested] 19+ messages in thread
* [bug#60753] file like parameters not working 2023-01-12 14:01 [bug#60753] [PATCH] gnu: home: Add home-emacs-service-type David Wilson 2023-01-12 17:24 ` ( via Guix-patches via @ 2023-01-15 0:21 ` benoit 2023-01-15 2:07 ` [bug#60753] issue with file-like init file benoit 2 siblings, 0 replies; 19+ messages in thread From: benoit @ 2023-01-15 0:21 UTC (permalink / raw) To: 60753 Hi, the last patch fixed the undefined issue, now the configuration does not work with file-like input. this example: (init-file (list (local-file "init.el"))) the resulting ~/.config/emacs/init.el has the path of my init.el file instead of it's content. I'm suspecting the serialization code consider init-file list to always be string. If I put a string instead of a local-file, I get the proper behavior. thanks for doing this! I'll see if I can troubleshoot the issue later tonight. Benoit ^ permalink raw reply [flat|nested] 19+ messages in thread
* [bug#60753] issue with file-like init file 2023-01-12 14:01 [bug#60753] [PATCH] gnu: home: Add home-emacs-service-type David Wilson 2023-01-12 17:24 ` ( via Guix-patches via 2023-01-15 0:21 ` [bug#60753] file like parameters not working benoit @ 2023-01-15 2:07 ` benoit 2 siblings, 0 replies; 19+ messages in thread From: benoit @ 2023-01-15 2:07 UTC (permalink / raw) To: 60753 Hi David, I think I found the issue. It looks like the mixed-text-file function does not replace file-like object with their content, but instead with their path. if you look at the example in the documentation: https://guix.gnu.org/manual/en/html_node/G_002dExpressions.html you'll see that it's being used to replace the file-like with it's path in an "export" clause. So it totally explain what I see in my generated init file. When compared with home-bash-service-type in shells.scm, it calls "serialize-configuration" on the file-like object. This is found in add-bash-configuration. Hope this helps. thanks! Benoit ^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2023-02-20 11:11 UTC | newest] Thread overview: 19+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2023-01-12 14:01 [bug#60753] [PATCH] gnu: home: Add home-emacs-service-type David Wilson 2023-01-12 17:24 ` ( via Guix-patches via 2023-01-12 17:27 ` David Wilson 2023-01-14 18:00 ` Ludovic Courtès 2023-01-15 8:02 ` Andrew Tropin 2023-01-17 9:02 ` Ludovic Courtès 2023-01-17 14:46 ` Andrew Tropin 2023-01-23 10:18 ` Ludovic Courtès 2023-01-26 5:06 ` Andrew Tropin 2023-01-31 16:26 ` Ludovic Courtès 2023-02-01 14:06 ` Andrew Tropin 2023-02-10 7:50 ` David Wilson 2023-02-20 11:10 ` Ludovic Courtès 2023-02-01 12:59 ` Jelle Licht 2023-02-01 13:46 ` Andrew Tropin 2023-01-26 18:50 ` Reily Siegel 2023-01-16 9:25 ` David Wilson 2023-01-15 0:21 ` [bug#60753] file like parameters not working benoit 2023-01-15 2:07 ` [bug#60753] issue with file-like init file benoit
Code repositories for project(s) associated with this public inbox https://git.savannah.gnu.org/cgit/guix.git This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).