unofficial mirror of guix-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: jbranso@dismail.de
To: "Zain Jabbar" <zaijab2000@gmail.com>
Cc: guix-devel@gnu.org
Subject: Re: Creating an Emacs Home Configuration Service
Date: Tue, 18 Oct 2022 15:41:38 +0000	[thread overview]
Message-ID: <8b00165ff49030fdd11708cb1fd4a5b9@dismail.de> (raw)
In-Reply-To: <CAH+UbWRZLZc1oKtYsj-j--7MME2iSQFrNAurTsgxzxYyEzkgjQ@mail.gmail.com>

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


  parent reply	other threads:[~2022-10-18 15:44 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <CAH+UbWQ1JZCZf0BEOzT1zcTeW5sDekOsxbGYgKn8rjdQ5e0kUw@mail.gmail.com>
2022-10-17  2:34 ` Creating an Emacs Home Configuration Service Zain Jabbar
2022-10-17 22:09   ` jbranso
2022-10-17 23:12     ` Zain Jabbar
2022-10-18 15:41     ` jbranso [this message]
     [not found]       ` <CAH+UbWR8xWoCK9wouhsizEzTAO3CksNpkebu5we_75yQq++yUg@mail.gmail.com>
     [not found]         ` <a8142b002799224845f8efdddb28e518@dismail.de>
2022-10-19 18:18           ` jbranso
2022-10-19 20:25             ` Zain Jabbar
2022-10-19 20:56             ` jbranso
2022-10-19 21:09               ` Zain Jabbar
2022-10-19 15:36   ` Ludovic Courtès
2022-10-19 20:53     ` Zain Jabbar
2022-10-20  7:22       ` Andrew Tropin
2022-10-20 13:17       ` Ludovic Courtès
2022-10-20 18:35   ` Liliana Marie Prikler

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://guix.gnu.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=8b00165ff49030fdd11708cb1fd4a5b9@dismail.de \
    --to=jbranso@dismail.de \
    --cc=guix-devel@gnu.org \
    --cc=zaijab2000@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).