From patchwork Thu Jan 12 14:01:18 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: David Wilson X-Patchwork-Id: 46067 Return-Path: X-Original-To: patchwork@mira.cbaines.net Delivered-To: patchwork@mira.cbaines.net Received: by mira.cbaines.net (Postfix, from userid 113) id D5B8727BBEB; Thu, 12 Jan 2023 14:28:15 +0000 (GMT) X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on mira.cbaines.net X-Spam-Level: X-Spam-Status: No, score=-3.7 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_MSPIKE_H2,SPF_HELO_PASS, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mira.cbaines.net (Postfix) with ESMTPS id A6C8427BBE9 for ; Thu, 12 Jan 2023 14:28:13 +0000 (GMT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pFyJN-00088A-To; Thu, 12 Jan 2023 09:12:09 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pFyJH-00087D-36 for guix-patches@gnu.org; Thu, 12 Jan 2023 09:12:03 -0500 Received: from debbugs.gnu.org ([209.51.188.43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1pFyJG-0007tu-Ri for guix-patches@gnu.org; Thu, 12 Jan 2023 09:12:02 -0500 Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1pFyJG-0002Wv-Ms for guix-patches@gnu.org; Thu, 12 Jan 2023 09:12:02 -0500 X-Loop: help-debbugs@gnu.org Subject: [bug#60753] [PATCH] gnu: home: Add home-emacs-service-type. Resent-From: David Wilson Original-Sender: "Debbugs-submit" Resent-CC: guix-patches@gnu.org Resent-Date: Thu, 12 Jan 2023 14:12:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 60753 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: patch To: 60753@debbugs.gnu.org X-Debbugs-Original-To: guix-patches@gnu.org Received: via spool by submit@debbugs.gnu.org id=B.16735326879668 (code B ref -1); Thu, 12 Jan 2023 14:12:02 +0000 Received: (at submit) by debbugs.gnu.org; 12 Jan 2023 14:11:27 +0000 Received: from localhost ([127.0.0.1]:45047 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1pFyIg-0002Vp-JQ for submit@debbugs.gnu.org; Thu, 12 Jan 2023 09:11:27 -0500 Received: from lists.gnu.org ([209.51.188.17]:38814) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1pFyIe-0002VZ-8W for submit@debbugs.gnu.org; Thu, 12 Jan 2023 09:11:25 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pFyIS-0007YU-4E for guix-patches@gnu.org; Thu, 12 Jan 2023 09:11:12 -0500 Received: from wout4-smtp.messagingengine.com ([64.147.123.20]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pFyIN-0007gb-3J for guix-patches@gnu.org; Thu, 12 Jan 2023 09:11:11 -0500 Received: from compute3.internal (compute3.nyi.internal [10.202.2.43]) by mailout.west.internal (Postfix) with ESMTP id 5874F3200908 for ; Thu, 12 Jan 2023 09:02:08 -0500 (EST) Received: from mailfrontend1 ([10.202.2.162]) by compute3.internal (MEProxy); Thu, 12 Jan 2023 09:02:08 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=daviwil.com; h= cc:content-transfer-encoding:content-type:date:date:from:from :in-reply-to:message-id:mime-version:reply-to:sender:subject :subject:to:to; s=fm1; t=1673532127; x=1673618527; bh=2iVUYUp0lF Kdrz0Zg6rgAW+W9myrJCnDelp4qlsgSuQ=; b=3yBV8dh43mQ2lnVfV00rzCmxDE 0LwkKZCc4eSLF8+NgPPMDZiGROg6n90111Krod+p8JrQyVw4GBN04dbQ6lCHE6aa z7BVAF3gv9eATU4Fc82a8hXfqUHMmdKLLgKNfiQPNnzWsRn6VbNlkVrRFz6UAYQ8 SZpcrcOIuQ1eaUDftgU5XtqU9aPM9x9E4u4kiAm04ZxNzxK3hWyEs45PzrKh5850 XI2mz2Huet14K2MWUmVpC/7CSi4hHSzyX3yjJWkwN6udguEA+D/ATwJ09O83opzj ub2VMRthBU1cs7QEhhw/r0RolYkNQRlaDYgBZAOMKCxuRXx032Dzjl75XUYw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-transfer-encoding:content-type :date:date:feedback-id:feedback-id:from:from:in-reply-to :message-id:mime-version:reply-to:sender:subject:subject:to:to :x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm3; t=1673532127; x=1673618527; bh=2iVUYUp0lFKdrz0Zg6rgAW+W9myr JCnDelp4qlsgSuQ=; b=MVgBk6zV7TsX8feQT7KOUmrne+yo0po3BBcC4sJv0TS/ 87oPRYfFLXdo4SqiRxLZkT6aX4on1gisVT92FSRk65NxhR39PgWx1FmghyXp/CPS usQ/5auztfoc3wnucLPeVnLrv+hfFsFMWHph6raaurbPIYhAQs2tVUEnYTH3nyjG FG3WJwVVsnKGi2oz1/aNEG2frPyhIoKqLhrb/qZ3l98xjtaRsVjqagmIxV/HBRl3 IDoEaDJ+hSAOMmVT10CteJehMsZcZvdODPXmnClGR0AKZFAVUb8qW3NoXu7l1E4p Sx0vkt0CV1nw4qPQJibZYTc/33nf07hO2/BGPiOzAw== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvhedrleeigdehlecutefuodetggdotefrodftvf curfhrohhfihhlvgemucfhrghsthforghilhdpqfgfvfdpuffrtefokffrpgfnqfghnecu uegrihhlohhuthemuceftddtnecunecujfgurhepfgfhvffufffkgggtgfesthhqredttd erjeenucfhrhhomhepffgrvhhiugcuhghilhhsohhnuceouggrvhhiugesuggrvhhifihi lhdrtghomheqnecuggftrfgrthhtvghrnhepkeefudfhkedvueeikeekteejkedvjeejge dtfffgffejvdejueefhfejhfeutdetnecuffhomhgrihhnpehgnhhurdhorhhgnecuvehl uhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepuggrvhhiugesug grvhhifihilhdrtghomh X-ME-Proxy: Feedback-ID: iba684179:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA for ; Thu, 12 Jan 2023 09:02:06 -0500 (EST) User-agent: mu4e 1.8.11; emacs 28.2 From: David Wilson Date: Thu, 12 Jan 2023 16:01:18 +0200 Message-ID: <87edrzzw9w.fsf@daviwil.com> MIME-Version: 1.0 Received-SPF: none client-ip=64.147.123.20; envelope-from=david@daviwil.com; helo=wout4-smtp.messagingengine.com X-Spam_score_int: -27 X-Spam_score: -2.8 X-Spam_bar: -- X-Spam_report: (-2.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_PASS=-0.001, SPF_NONE=0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+patchwork=mira.cbaines.net@gnu.org Sender: guix-patches-bounces+patchwork=mira.cbaines.net@gnu.org X-getmail-retrieved-from-mailbox: Patches 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: ( --- 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 -- 2.38.1 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 ( +;;; Copyright © 2023 David Wilson +;;; +;;; 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 . + +(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 + (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 \