Package: guix-patches;
Reported by: fernseed <at> fernseed.me
Date: Fri, 14 Jul 2023 15:50:02 UTC
Severity: normal
Tags: patch
View this message in rfc822 format
From: Kierin Bell <fernseed <at> fernseed.me> To: Ludovic Courtès <ludo <at> gnu.org> Cc: 64620 <at> debbugs.gnu.org Subject: [bug#64620] [PATCH] gnu: home: Add home-emacs-service-type. Date: Wed, 23 Aug 2023 12:14:57 -0400
Hi Ludo’, Ludovic Courtès <ludo <at> gnu.org> writes: > Hi Kierin, > > This is a truly impressive piece of work! > > fernseed <at> fernseed.me skribis: > >> This patch builds on patches from ( and David Wilson for a >> `home-emacs-service-type' (https://issues.guix.gnu.org/58693, >> https://issues.guix.gnu.org/60753, https://issues.guix.gnu.org/62549). >> >> Many of the features of the prior patches have been included, but the >> major focus here is to configure Emacs in Scheme rather than symlinking >> to existing configuration files. > > OK, interesting. This seems to be one of the main questions: how far > should we go on the Scheme side? > > In <https://issues.guix.gnu.org/62549>, unmatched-paren chose to not > generate elisp at all from Scheme. The advantage is that the > implementation is simpler; as a user, the model one has to have in mind > is also simpler: you’re still configuring most things the traditional > way in elisp. That’s also its downside: you have to do plumbing on the > elisp side, when Guix Home in some cases would know what to do. > > I don’t have the answer and I’m not sure what I’d prefer, but I’m trying > to see the tradeoffs and to map out the design space. My philosophy here, I think, is that we can do both. ('s approach likely provides all of the integration between Guix and Emacs that many would want. David Wilson's patch goes a step further by providing a configuration option to change the Emacs user directory, which I think is a reasonable option to have. (Testing this, though, it turns out that changing the Emacs user directory is not so simple, so the implementation in my patch is more involved). In any case, as we saw, Guix Home does need to serialize some Elisp to do this (or to do anything similar). So, ideally, we would provide some useful mechanisms for serializing Elisp, but not go overboard. It's Scheme, so users can build on things later if reasonable foundations are there. We may want to work on simplifying the implementation here by removing features that add too much complexity. For example, maybe all of the fined-tuned controls over how Emacs servers "inherit" configuration aren't justified given the complexity of the implementation --- especially given that configuration is done via Scheme records, so users can duplicate configuration themselves by just reusing records. (But I do think it makes sense to at least include a subset of those features.) >> Here are some of the broad strokes: >> >> * The following record types have been introduced to encapsulate >> configuration for Emacs: `emacs-configuration' (for general >> configuration), `emacs-package' (for package-specific configuration), >> `emacs-keymap' (for configuration of local keymaps), and >> `emacs-server' (for configuration of Emacs servers). > > Why special-case keymaps, of all the things one might one to configure > in Emacs? I understand it’s one of the first things one may want to > tweak, but then why not add <emacs-theme> as well, etc.; IOW, where do > we draw the line? > For `emacs-keymap', I created a record type to avoid having nested alists like: '((foo-map . (("C-c a" . foo) ...)) (bar-map . ...) ...) Also, the `emacs-keymap' record has a `repeat?' field, so it can serve the purpose of something like use-package's `:repeat-map' keyword. ...I do like the idea of `<emacs-theme>', though... Just kidding. >> * Most configuration fields are either flat lists or alists that are >> considerably abstracted from their final serialized Elisp >> representation, but escape hatches are provided for both pulling in >> existing configuration files and specifying s-expressions directly. > > Are seasoned Emacsers not going to be frustrated because of this? :-) > > They might prefer to have full access to elisp. > I think it probably would be frustrating if it wasn't clear that users do have access to Elisp via the escape hatch fields. Maybe these should be mentioned more prominently in the documentation. They can also specify Elisp directly by using "Elisp expressions" as values in some of the alists --- for example, those that set variables (examples given in the configuration snippets). >> * All serialized Elisp is pretty-printed much how we would expect to see >> it in Emacs (for example, with proper indentation according to the >> `lisp-indent-function' symbol property, etc.). This has been >> accomplished by adding a new keyword argument to >> `pretty-print-with-comments' from `(guix read-print)', among other >> improvements. > > Fun. I’d like to see how we can avoid spreading elisp conditionals in > (guix read-print). > I think the Elisp reader extension could be implemented completely on its own, outside of (guix read-print). The (guix read-print) pretty printer is very nice, though, and it seems relatively simple to adapt it to print Elisp. Though I'm open to better ideas. Most of the changes to `pretty-print-with-comments' are actually fixes that are not Elisp-specific, like preventing newlines where they definitely don't belong. Maybe that could go in a separate commit? >> * Emacs package configuration can either be serialized as `use-package' >> forms or as equivalent, more minimalist s-expressions. Users can >> define their own package serializers, too. >> >> * For specifying s-expressions, an "Elisp expression" syntax has been >> implemented that is essentially a lighter-weight version G-expressions. >> (I try to explain why this is helpful in the documentation.) >> >> * A reader extension has been implemented that allows for "Elisp >> expressions" to be specified directly with Elisp read syntax, and >> Scheme values (including file-like objects or G-expressions) can in >> turn be "unquoted" within that Elisp code. Also, comments and >> whitespace can be included within the Elisp code via the `#;' >> (comment), `#>' (newline), and `;^L' (page break) forms. > > Great that you’re putting (language elisp parser) to good use! > >> * Each Emacs server has its own user init and early init files, which >> can optionally inherit configuration from the init files used by >> non-server Emacsen. Each server can also inherit the "main" >> `user-emacs-directory', or it can use its own subdirectory. >> >> * The `home-emacs-service-type' can be extended, with subordinate >> configuration records being merged intelligently when possible. > > Very nice. > >> * A utility function has been provided for generating the aforementioned >> Scheme records from an existing Emacs init file: >> `elisp-file->home-emacs-configuration'. > > Neat; perhaps ‘guix home import’ could use it? > I looked into modifying `guix home import', but didn't have time to figure out exactly how to make that work. >> (define %gnus-init-file >> (elisp-file "gnus.el" >> (list >> (elisp (setq gnus-select-method '(nnnil ""))) >> (elisp (setq gnus-secondary-select-methods >> '((nnml "") >> (nntp "news.gmane.io")))) >> (elisp (setq mail-sources >> '((imap :server "mail.example.net" >> :user "user <at> example.net" >> :port 993 >> :stream tls)))) >> ;; Elisp reader extension >> #%(define-key global-map [remap compose-mail] #;comment >> '#$%my-function-name nil)))) > > Could I write: > > #%(progn > (setq x …) > (setq y …) > (define-key …)) > > ? That would seem nicer. > I'm thinking about a way to create a "splicing" version of the `elisp' macro, or something similar, so you could do something like that without serializing the actual `progn'. > #%(body …) is short for (elisp body …) right? > > > [...] > Yes, they are equivalent. Most users would probably use the reader extension, unless they want to use Scheme-specific syntax (like `#(...)' for vectors versus `[...]'). >> (configured-packages >> (list >> (emacs-package >> (name 'windmove) >> ;; Autoload a function used by `my--display-buffer-down'. >> (autoloads '(windmove-display-in-direction)) >> (keys-override >> '(("C-M-<left>" . windmove-left) >> ("C-M-<right>" . windmove-right) >> ("C-M-<up>" . windmove-up) >> ("C-M-<down>" . windmove-down) >> ("C-x <down>" >> . my--display-buffer-down))) >> (keys-local >> (list >> (emacs-keymap >> (name 'windmove-repeat-map) >> (repeat? #t) >> (keys '(("<left>" . windmove-left) >> ("<right>" . windmove-right) >> ("<up>" . windmove-up) >> ("<down>" . windmove-down)))))) > > My first reaction is that I don’t see myself my 2K lines (or a subset > thereof) of .emacs and .gnus in that style. I can foresee potential > benefits in terms of composability, but the barrier to entry looks too > high. WDYT? > I have about 2K lines of it (a lot of it auto-generated by the import function). As for the barrier to entry, I think we should hear more from others. In my opinion, the benefits of configuring Emacs with Scheme records like this go beyond composability. I think that it is cognitively easier to manage configuration when it is uniform, and the structure forces us to be more deliberate about what we include and why. But maybe that's more of a philosophical debate. >> Finally, unit tests have been added for the new `(guix read-print)' >> functionality, and for the "Elisp expression" syntax. I couldn't make >> unit tests for anything that builds derivations serializing Elisp, >> because '%bootstrap-guile' is apparently too old to load `(guix >> read-print)' on the derivation side. But most of this has gotten quite >> a bit of testing, as all of my personal Emacs config is now generated >> from Scheme. > > I think you could write tests using ‘guix home container’ and the host’s > store, similar to what ‘tests/guix-home.sh’ is doing. We don’t have a > testing strategy for Home services yet, but we should definitely work on > it. > Will look into it. Testing the `elisp-file' function is important, because it does all of the serialization. > That’s it for my initial feedback. I hope others in the Home and Emacs > teams will chime in! > > Thanks, > Ludo’. > > Thanks, I appreciate the feedback! I have a second version of the patch in the works that fixes 3 cosmetic issues, in case people noticed: 2 comments are confusing and shouldn't be there (code changed but comments didn't), and one line of the documentation in `guix.texi' wasn't properly filled. The actual code has been unchanged for 2 months, even with regular use. Also, the example snippet I gave in the original patch message has an error: --8<---------------cut here---------------start------------->8--- ;; ... (emacs-server (name "sandbox") ;; Server gets its own subdirectory of `user-emacs-directory' ;; when inheritance is disabled. (inherit-directory? #f) ;; Server still inherits configuration from non-server Emacsen ;; unless inheritance is explicitly disabled. (inherit-init? #f) ;; ... (default-init (emacs-configuration (variables `(;; ... ;; Individualized `user-emacs-directory' gets symlinks ;; to all `extra-files' from the `emacs-configuration' ;; used by other Emacsen, so the files can still be ;; referenced. (mail-signature-file . ,(elisp (locate-user-emacs-file "signature"))))) ;; ... )) ;; ... ) --8<---------------cut here---------------end--------------->8--- `inherit-init?' should be set to true. When `inherit-init?' is false, the `signature' file (which was previously created for the non-server Emacsen) will not be created automatically in the Emacs server's user directory. When it is true, all of the files that Guix Home manages in the main Emacs user directory are duplicated in the server's user directory, to ensure that any references to those files in the inherited configuration are still valid. ...Again, maybe this is one of the confusing features that could be simplified. -- Kierin Bell GPG Key: FCF2 5F08 EA4F 2E3D C7C3 0D41 D14A 8CD3 2D97 0B36
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.