guix-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Creating an Emacs Home Configuration Service


From: jbranso
Subject: Re: Creating an Emacs Home Configuration Service
Date: Tue, 18 Oct 2022 15:41:38 +0000

October 17, 2022 7:12 PM, "Zain Jabbar" <zaijab2000@gmail.com> wrote:

> Aloha Guix Development Team,
> 
> Thank you for this email. Your advice was directed very kindly and is
> very helpful. I have tried to revise the code based on your email. I
> also checked the setting for plaintext mode in GMail; I hope this
> makes the email easier to read.
> 
> First, I define a configuration (without serialization currently).
> 
> #+BEGIN_SRC scheme
> (define file-likes? (list-of file-like?))
> 
> (define-configuration/no-serialization emacs-configuration
> (emacs-packages
> (file-likes (list (specification->package "emacs-next"))) "Files")
> (early-init
> (list '()) "Early-Init")
> (init
> (list '()) "Init"))
> #+END_SRC
> 
> Then, I define an =emacs-configuration-service= that takes in a
> configuration. This service will add packages in the =emacs-packages=
> to the profile, and append the S-Expressions in =early-init= and
> =init= to $XDG_CONFIG_HOME/emacs/early-init.el and
> $XDG_CONFIG_HOME/emacs/init.el respectively. The service has
> definition,
> 
> #+BEGIN_SRC scheme
> (define-public emacs-configuration-service
> (service-type (name (symbol-append 'emacs-configuration))
> (extensions
> (list (service-extension
> home-profile-service-type
> (lambda (config) (emacs-configuration-emacs-packages config)))
> (service-extension
> home-xdg-configuration-files-service-type
> (lambda (config)
> (list
> `("emacs/init.el" ,(scheme-file "init.el"
> (emacs-configuration-init config)
> #:splice? #:t))
> `("emacs/early-init.el" ,(scheme-file "early-init.el"
> (emacs-configuration-early-init config)
> #:splice? #:t)))))))
> (default-value (emacs-configuration))
> (description "Configures Emacs init.el")))
> #+END_SRC
> 
> This version of the service is one big service that only takes in one
> configuration file. So in order to configure bits and pieces of Emacs,
> for example evil-mode and vertico we can append emacs-configurations
> into one big configuration. I do this as follows.
> 
> #+BEGIN_SRC scheme
> (define evil-configuration
> (emacs-configuration
> (emacs-packages (list (specification->package "emacs-evil")))
> (init '((evil-mode 1)))))
> 
> (define vertico-configuration
> (emacs-configuration
> (emacs-packages (list (specification->package "emacs-vertico")))
> (init '((vertico-mode 1)))))
> 
> (define-public total-emacs-configuration
> (fold (lambda (config-1 config-2) (emacs-configuration
> (init (append (emacs-configuration-init config-1)
> (emacs-configuration-init config-2)))
> (early-init (append (emacs-configuration-early-init config-1)
> (emacs-configuration-early-init config-2)))
> (emacs-packages (append (emacs-configuration-emacs-packages config-1)
> (emacs-configuration-emacs-packages config-2)))))
> (emacs-configuration)
> (list evil-configuration vertico-configuration)))
> #+END_SRC
> 
> We can actually go crazy with this idea. The next source block is a
> generalization of the last one. Rather than declaring the list of
> configurations, we have Guile figure out all of the bound
> =emacs-configurations= in the current module and append them that way.
> 
> #+BEGIN_SRC scheme
> (define-public total-emacs-configuration
> (fold (lambda (config-1 config-2) (emacs-configuration
> (init (append (emacs-configuration-init config-1)
> (emacs-configuration-init config-2)))
> (early-init (append (emacs-configuration-early-init config-1)
> (emacs-configuration-early-init config-2)))
> (emacs-packages (append (emacs-configuration-emacs-packages config-1)
> (emacs-configuration-emacs-packages config-2)))))
> (emacs-configuration)
> 
> (filter emacs-configuration?
> (map variable-ref
> (filter variable-bound?
> (hash-map->list (lambda (x y) y) (struct-ref (current-module) 0)))))))
> #+END_SRC
> 
> What further improvements could I add to this system? The end goal
> (hopefully) is to help add another home service to Guix. I was
> inspired by David Wilson's call to action during his Guix Home talk at
> the 10 year anniversary event.
> 
> On Mon, Oct 17, 2022 at 12:09 PM <jbranso@dismail.de> wrote:
> 
>> October 17, 2022 2:38 AM, "Zain Jabbar" <zaijab2000@gmail.com> wrote:
>> 
>> Aloha Guix Development Team,
>> 
>> Running =guix home search emacs= returns nothing. I also could not find an 
>> email using =C-u M-x
>> debbugs-gnu= about an Emacs configuration service.
>> 
>> This is my first email to this mailing address. Please give me pointers on 
>> formatting and further
>> improvements.
>> 
>> I think you sent an html email. Generally you want to send plain text 
>> emails. :)
>> 
>> I have attempted to make an =emacs-home-service-type= so that it is possible 
>> to configure Emacs
>> using Guix home. This code is extremely preliminary hence I don't even think 
>> it is worth sending as
>> a patch. Also I have never worked on a multi person Git project before and 
>> do not know how to solve
>> the keyring error I get when using guix pull. I will outline what my code 
>> does and what features I
>> would like to add.
>> 
>> #+BEGIN_SRC scheme
>> (define* (emacs-configuration-service name #:key (init '()) (early-init '()) 
>> (emacs-packages '()))
>> (service-type (name (symbol-append 'emacs- name '-configuration))
>> (extensions
>> (list (service-extension
>> home-profile-service-type
>> (lambda (config) emacs-packages))
>> (service-extension
>> home-files-service-type
>> (lambda (config)
>> (list
>> `(,(string-append
>> ".config/emacs/services/" (symbol->string name) ".el")
>> ,(scheme-file (string-append (symbol->string name) ".el")
>> init #:splice? #t))
>> `(,(string-append
>> ".config/emacs/early-services/" (symbol->string name) ".el")
>> ,(scheme-file (string-append "early-" (symbol->string name) ".el")
>> early-init #:splice? #t)))))))
>> (default-value #f)
>> (description "Configures Emacs init.el")))
>> 
>> (define-public emacs-init-service-type
>> (service-type (name 'home-emacs)
>> (extensions
>> (list (service-extension
>> home-profile-service-type
>> (lambda (config) (list emacs-next)))
>> (service-extension
>> home-files-service-type
>> (lambda (config)
>> (list
>> `(".config/emacs/early-init.el"
>> ,(scheme-file
>> "early-init.el"
>> '((mapc
>> 'load (file-expand-wildcards
>> "~/.config/emacs/early-services/*.el")))
>> #:splice? #t))
>> `(".config/emacs/init.el"
>> ,(scheme-file
>> "init.el"
>> '((mapc
>> 'load (file-expand-wildcards
>> "~/.config/emacs/services/*.el")))
>> #:splice? #t)))))))
>> (default-value #f)
>> (description "Configures Emacs init.el")))
>> #+END_SRC
>> 
>> I define a general configuration service generator which takes in four 
>> things:
>> 1. The =name= of the service
>> 2. The configuration to be ran in =init.el=
>> 3. The configuration to be ran in =early-init.el=
>> 4. The packages in Guix to be added to the =home-profile=.
>> 
>> After giving the =name=, =packages=, and =config.el= files we get a new 
>> service type that we can
>> add to our home declaration. This service will then add a file in
>> =~/.config/emacs/services/emacs-{NAME}-configuration.el=. I then have 
>> another service that places
>> an =init.el= which loads everything in the service directory.
>> 
>> If we want to install and configure =evil-mode= using this =home-service= we 
>> may define the
>> following somewhere.
>> 
>> #+BEGIN_SRC scheme
>> (define-public emacs-evil-service-type
>> (emacs-configuration-service
>> 'evil #:emacs-packages (list emacs-evil)
>> #:init '((evil-mode 1))))
>> #+END_SRC
>> 
>> Within our =home-environment= we may add the service using:
>> 
>> #+BEGIN_SRC scheme
>> (home-environment
>> ;; ...Things in the home-environment...
>> (services
>> (list
>> ;; ...Other Services...
>> (service emacs-evil-service-type))))
>> #+END_SRC
>> 
>> There are some missing features I want to add.
>> 
>> 1. Have the =home-emacs-*-service-type= service-types add to the =init.el= 
>> directly rather than
>> within a folder to be loaded. I couldn't add two files with the same name to 
>> the store. So I have
>> emacs-evil.el in the store to be placed separately later rather than 
>> appending to the existing
>> init.el file.
>> 
>> 2. Have Emacs update whenever the =home-environment= is updated. Meaning, if 
>> I did not add
>> =(service emacs-evil-service-type)= in my =home-environment= then obviously 
>> =M-x evil-mode= should
>> not work. But after adding the service then I want =M-x evil-mode= to work 
>> without having to
>> restart Emacs. I do not understand the Emacs loading system on Guix well 
>> enough to know why it does
>> not work. Skipping all of the =home-service= stuff, running =guix install 
>> emacs-evil-mode= then
>> =(guix-emacs-autoload-packages)= does not let emacs know that =evil-mode= is 
>> installed. I would
>> need to close Emacs and start Emacs again for Emacs to know about 
>> =evil-mode= being installed.
>> 
>> 3. Use configurations somehow. I have completely neglected this feature in 
>> my system. I do not know
>> what would be useful there.
>> 
>> I believe that you are referring to using scheme records to configure the 
>> emacs service. :)
>> 
>> I would recommend using (define-configuration ...) procedure.
>> 
>> (There is a define-record-type* as well, but I think the consensus is that
>> define-configuration* is a little easier to use. Does better error handling
>> and can help you generate documentation from the code).
>> 
>> You can find examples of that here:
>> 
>> https://git.savannah.gnu.org/cgit/guix.git/tree/gnu/services/mail.scm
>> 
>> --
>> Thank you,
>> Zain Jabbar
> 
> --
> Thank you,
> Zain Jabbar
> 
> On Mon, Oct 17, 2022 at 12:09 PM <jbranso@dismail.de> wrote:
> 
>> October 17, 2022 2:38 AM, "Zain Jabbar" <zaijab2000@gmail.com> wrote:
>> 
>> Aloha Guix Development Team,
>> 
>> Running =guix home search emacs= returns nothing. I also could not find an 
>> email using =C-u M-x
>> debbugs-gnu= about an Emacs configuration service.
>> 
>> This is my first email to this mailing address. Please give me pointers on 
>> formatting and further
>> improvements.
>> 
>> I think you sent an html email. Generally you want to send plain text 
>> emails. :)
>> 
>> I have attempted to make an =emacs-home-service-type= so that it is possible 
>> to configure Emacs
>> using Guix home. This code is extremely preliminary hence I don't even think 
>> it is worth sending as
>> a patch. Also I have never worked on a multi person Git project before and 
>> do not know how to solve
>> the keyring error I get when using guix pull. I will outline what my code 
>> does and what features I
>> would like to add.
>> 
>> #+BEGIN_SRC scheme
>> (define* (emacs-configuration-service name #:key (init '()) (early-init '()) 
>> (emacs-packages '()))
>> (service-type (name (symbol-append 'emacs- name '-configuration))
>> (extensions
>> (list (service-extension
>> home-profile-service-type
>> (lambda (config) emacs-packages))
>> (service-extension
>> home-files-service-type
>> (lambda (config)
>> (list
>> `(,(string-append
>> ".config/emacs/services/" (symbol->string name) ".el")
>> ,(scheme-file (string-append (symbol->string name) ".el")
>> init #:splice? #t))
>> `(,(string-append
>> ".config/emacs/early-services/" (symbol->string name) ".el")
>> ,(scheme-file (string-append "early-" (symbol->string name) ".el")
>> early-init #:splice? #t)))))))
>> (default-value #f)
>> (description "Configures Emacs init.el")))
>> 
>> (define-public emacs-init-service-type
>> (service-type (name 'home-emacs)
>> (extensions
>> (list (service-extension
>> home-profile-service-type
>> (lambda (config) (list emacs-next)))
>> (service-extension
>> home-files-service-type
>> (lambda (config)
>> (list
>> `(".config/emacs/early-init.el"
>> ,(scheme-file
>> "early-init.el"
>> '((mapc
>> 'load (file-expand-wildcards
>> "~/.config/emacs/early-services/*.el")))
>> #:splice? #t))
>> `(".config/emacs/init.el"
>> ,(scheme-file
>> "init.el"
>> '((mapc
>> 'load (file-expand-wildcards
>> "~/.config/emacs/services/*.el")))
>> #:splice? #t)))))))
>> (default-value #f)
>> (description "Configures Emacs init.el")))
>> #+END_SRC
>> 
>> I define a general configuration service generator which takes in four 
>> things:
>> 1. The =name= of the service
>> 2. The configuration to be ran in =init.el=
>> 3. The configuration to be ran in =early-init.el=
>> 4. The packages in Guix to be added to the =home-profile=.
>> 
>> After giving the =name=, =packages=, and =config.el= files we get a new 
>> service type that we can
>> add to our home declaration. This service will then add a file in
>> =~/.config/emacs/services/emacs-{NAME}-configuration.el=. I then have 
>> another service that places
>> an =init.el= which loads everything in the service directory.
>> 
>> If we want to install and configure =evil-mode= using this =home-service= we 
>> may define the
>> following somewhere.
>> 
>> #+BEGIN_SRC scheme
>> (define-public emacs-evil-service-type
>> (emacs-configuration-service
>> 'evil #:emacs-packages (list emacs-evil)
>> #:init '((evil-mode 1))))
>> #+END_SRC
>> 
>> Within our =home-environment= we may add the service using:
>> 
>> #+BEGIN_SRC scheme
>> (home-environment
>> ;; ...Things in the home-environment...
>> (services
>> (list
>> ;; ...Other Services...
>> (service emacs-evil-service-type))))
>> #+END_SRC
>> 
>> There are some missing features I want to add.
>> 
>> 1. Have the =home-emacs-*-service-type= service-types add to the =init.el= 
>> directly rather than
>> within a folder to be loaded. I couldn't add two files with the same name to 
>> the store. So I have
>> emacs-evil.el in the store to be placed separately later rather than 
>> appending to the existing
>> init.el file.
>> 
>> 2. Have Emacs update whenever the =home-environment= is updated. Meaning, if 
>> I did not add
>> =(service emacs-evil-service-type)= in my =home-environment= then obviously 
>> =M-x evil-mode= should
>> not work. But after adding the service then I want =M-x evil-mode= to work 
>> without having to
>> restart Emacs. I do not understand the Emacs loading system on Guix well 
>> enough to know why it does
>> not work. Skipping all of the =home-service= stuff, running =guix install 
>> emacs-evil-mode= then
>> =(guix-emacs-autoload-packages)= does not let emacs know that =evil-mode= is 
>> installed. I would
>> need to close Emacs and start Emacs again for Emacs to know about 
>> =evil-mode= being installed.
>> 
>> 3. Use configurations somehow. I have completely neglected this feature in 
>> my system. I do not know
>> what would be useful there.
>> 
>> I believe that you are referring to using scheme records to configure the 
>> emacs service. :)
>> 
>> I would recommend using (define-configuration ...) procedure.
>> 
>> (There is a define-record-type* as well, but I think the consensus is that
>> define-configuration* is a little easier to use. Does better error handling
>> and can help you generate documentation from the code).
>> 
>> You can find examples of that here:
>> 
>> https://git.savannah.gnu.org/cgit/guix.git/tree/gnu/services/mail.scm

I'm impressed with how quickly you made your code work!  That's awesome!

Just some food for thought, if you have your emacs packages declared in your
service...would emacs still be able to look up the info documentation
for each emacs package?  Are the emacs packages added to the user's 
package store?  

Thanks,

Joshua


>> 
>> --
>> Thank you,
>> Zain Jabbar
> 
> --
> Thank you,
> Zain Jabbar



reply via email to

[Prev in Thread] Current Thread [Next in Thread]