Search code examples
emacsclient-server

custom-set-faces only works with non-server emacs, but not /usr/bin/emacsclient and emacs server


I've been trying to work with emacs client/server functionality to avoid the long delays when starting a new emacs session. (emacs version 27.1 on Ubuntu MATE, BTW)

When I start emacs directly (i.e. no client server settings), the following code in my .emacs.d/custom.el file are correctly applied and I get a "dark-magenta" cursor. Yay!

However, when I use emacsclient to join a preexisting daemon, I have to reload custom.el within the session using M-x load-file for my cursor to change to "dark-magenta", which is becoming a PITA.

I'd really appreciate some help understanding this behavior and how to fix it.

Background

custom.el

I load custom.el at the end of my init.el file using the following code

.
.
.
;; loading custom.el at end of init.el
;; This ensures the themes are loaded before they are modified
;; Create custom.el file if it's missing, otherwise load it.
(defconst custom-file "~/.emacs.d/custom.el")
(unless (file-exists-p custom-file)
  (with-temp-file custom-file
    (insert ";; Autogenerated Emacs custom.el file")))
(load custom-file)

(provide 'init)

;;; init.el ends here

Client/Server Scenario

Code that Starts Emacs

Here's the code I use with the emacs server/client scenario. It checks to see if the appropriate server already exists, if not it creates it, if it does it joins it and starts a new one for future use.

#!/bin/bash
# Script for controlling whether emacs uses pre-existing daemon or creates new one
#
# IF you want to kill all Rmd-* servers, try
# pkill  -fe -9  "/usr/bin/emacs \-\-daemon=Rmd-*"


declare -a args="( $@ )"; ## need to use parentheses to prevent array of strings from being contatenated into one string

echo "Passed arguments: ${args[@]}"

# "$ids[@]}" returns space separated string
if [[ "${args[@]}" =~  .*\.("Rmd") ]]; then
    daemonCategory="Rmd"
else
    daemonCategory="nonR"
fi

## Get largest id # of --daemon=Rmd-[0-9]+
lastID=$(pgrep -a -f -u "$USER"  "/usr/bin/emacs \-\-daemon=$daemonCategory-[0-9]+" | sed -e 's/.*='"$daemonCategory"'-\([0-9]\+\)/\1/' | sort -rn | head -n 1)

echo "Daemon $daemonCategory detected lastID = $lastID"

## test to see if lastID is empty
if [ -z "$lastID" ]
then
    lastID=1
    echo "starting new daemon $daemonCategory-$lastID"
    ## Start new service
    /usr/bin/emacs --daemon="$daemonCategory-$lastID" 2>/dev/null
fi

## join already started service
## -c = create frame
## -s = socket-name
echo "Joining daemon $daemonCategory-$lastID"
/usr/bin/emacsclient -s "$daemonCategory-$lastID" -c "$@" &

nextID=$((lastID + 1))
## Start new service in anticipation of future needs
echo "starting new daemon $daemonCategory-$nextID"
/usr/bin/emacs --daemon="$daemonCategory-$nextID" 2>/dev/null

Messages Output

Here's what my Messages buffer looks like when I use the client/server set up

Loading /etc/emacs/site-start.d/00debian.el (source)...done
Loading /etc/emacs/site-start.d/50auctex.el (source)...
Loading /home/mikeg/.emacs.d/elpa/auctex-13.0.15/auctex.el (source)...done
Loading /usr/share/emacs/site-lisp/preview-latex.el (source)...done
Loading /etc/emacs/site-start.d/50auctex.el (source)...done
Loading /etc/emacs/site-start.d/50autoconf.el (source)...done
Loading /etc/emacs/site-start.d/50dictionaries-common.el (source)...
Loading debian-ispell...
Loading /var/cache/dictionaries-common/emacsen-ispell-default.el (source)...done
Loading debian-ispell...done
Loading /var/cache/dictionaries-common/emacsen-ispell-dicts.el (source)...done
Loading /etc/emacs/site-start.d/50dictionaries-common.el (source)...done
Loading /etc/emacs/site-start.d/50emacs-goodies-el.el (source)...
Package emacs-goodies-el removed but not purged.  Skipping setup.
Loading /etc/emacs/site-start.d/50emacs-goodies-el.el (source)...done
Loading /etc/emacs/site-start.d/50git-core.el (source)...
git removed but not purged, skipping setup
Loading /etc/emacs/site-start.d/50git-core.el (source)...done
Loading /etc/emacs/site-start.d/50tcsh.el (source)...done
Importing gnu-elpa.gpg-keyring...done
Loading /home/mikeg/.emacs.d/elpa/ess-20201004.1522/ess-tracebug.el (source)...done
[yas] Prepared just-in-time loading of snippets successfully.
Importing package-keyring.gpg...done
Contacting host: elpa.gnu.org:443
Contacting host: melpa.org:443
Contacting host: elpa.nongnu.org:443
Package refresh done
Loading /home/mikeg/.emacs.d/lisp/personal.el (source)...
Loading /home/mikeg/.emacs.d/lisp/no-x-settings.el (source)...done
Loading /home/mikeg/.emacs.d/lisp/ssh.el (source)...done
Loading /home/mikeg/.emacs.d/lisp/personal.el (source)...done
Loading /home/mikeg/.emacs.d/lisp/beamer.el (source)...done
Loading /home/mikeg/.emacs.d/lisp/modify-other-keys-extension.el (source)...done
Loading /home/mikeg/.emacs.d/lisp/jnm-term.el (source)...done
Loading /home/mikeg/.emacs.d/lisp/ess-init.el (source)...
[yas] Prepared just-in-time loading of snippets successfully.
Loading /home/mikeg/.emacs.d/lisp/ess-init.el (source)...done
Loading /home/mikeg/.emacs.d/lisp/my-tramp.el (source)...done
Loading /home/mikeg/.emacs.d/custom.el (source)...done
Turning on magit-auto-revert-mode...done
Starting Emacs daemon.
(New file)
When done with a buffer, type C-x #

If use `M-x load-file ".emacs.d/custom.el" the cursor turns magenta and the line

Loading /home/mikeg/.emacs.d/custom.el (source)...done gets appended to Messages

Emacs Stand Alone Scenario

In the stand alone scenario, e.g. /usr/bin/emacs file.txt everything works as intended, my cursor turns magenta, it just takes a while to get things started. In this case I get pretty much the same Messages buffer as in previous scenario. Specifically, it looks like this

Loading /etc/emacs/site-start.d/00debian.el (source)...done
Loading /etc/emacs/site-start.d/50auctex.el (source)...
Loading /home/mikeg/.emacs.d/elpa/auctex-13.0.15/auctex.el (source)...done
Loading /usr/share/emacs/site-lisp/preview-latex.el (source)...done
Loading /etc/emacs/site-start.d/50auctex.el (source)...done
Loading /etc/emacs/site-start.d/50autoconf.el (source)...done
Loading /etc/emacs/site-start.d/50dictionaries-common.el (source)...
Loading debian-ispell...
Loading /var/cache/dictionaries-common/emacsen-ispell-default.el (source)...done
Loading debian-ispell...done
Loading /var/cache/dictionaries-common/emacsen-ispell-dicts.el (source)...done
Loading /etc/emacs/site-start.d/50dictionaries-common.el (source)...done
Loading /etc/emacs/site-start.d/50emacs-goodies-el.el (source)...
Package emacs-goodies-el removed but not purged.  Skipping setup.
Loading /etc/emacs/site-start.d/50emacs-goodies-el.el (source)...done
Loading /etc/emacs/site-start.d/50git-core.el (source)...
git removed but not purged, skipping setup
Loading /etc/emacs/site-start.d/50git-core.el (source)...done
Loading /etc/emacs/site-start.d/50tcsh.el (source)...done
Importing gnu-elpa.gpg-keyring...done
Loading /home/mikeg/.emacs.d/elpa/ess-20201004.1522/ess-tracebug.el (source)...done
[yas] Prepared just-in-time loading of snippets successfully.
Importing package-keyring.gpg...done
Contacting host: elpa.gnu.org:443
Contacting host: melpa.org:443
Contacting host: elpa.nongnu.org:443
Package refresh done
Loading /home/mikeg/.emacs.d/lisp/personal.el (source)...
Loading /home/mikeg/.emacs.d/lisp/no-x-settings.el (source)...done
Loading /home/mikeg/.emacs.d/lisp/ssh.el (source)...done
Loading /home/mikeg/.emacs.d/lisp/personal.el (source)...done
Loading /home/mikeg/.emacs.d/lisp/beamer.el (source)...done
Loading /home/mikeg/.emacs.d/lisp/modify-other-keys-extension.el (source)...done
Loading /home/mikeg/.emacs.d/lisp/jnm-term.el (source)...done
Loading /home/mikeg/.emacs.d/lisp/ess-init.el (source)...
[yas] Prepared just-in-time loading of snippets successfully.
Loading /home/mikeg/.emacs.d/lisp/ess-init.el (source)...done
Loading /home/mikeg/.emacs.d/lisp/my-tramp.el (source)...done
Loading /home/mikeg/.emacs.d/custom.el (source)...done
Turning on magit-auto-revert-mode...done
For information about GNU Emacs and the GNU system, type C-h C-a.
(New file)
Mark set

Final Note

The problem persists even if I have a second (load custom-file) command in my init.el file. So it seems like I've got to have an active window for things to work.

Much obliged for any help provided.


Solution

  • I think this question is as same as the question at Emacs StackExchange.

    In short, customize your Emacs faces this (or similar) way:

    ;;; UI-settings.el  -*- lexical-binding: t; -*-
    ;;
    ;; Load this file in your init file.
    
    (letrec ((face-customizer
              (lambda ()
                (custom-set-faces  ; Put your original
                 ...)              ; “(custom-set-faces ...)” here.
                (remove-hook 'server-after-make-frame-hook face-customizer))))
      (if (daemonp)
          (add-hook 'server-after-make-frame-hook face-customizer)
        (funcall face-customizer)))
    

    I guess the problem you encountered has to do with the order of actions at Emacs startup:

    When Emacs is started up, it performs the following operations (see normal-top-level in startup.el):

    1. If appropriate, it creates a graphical frame. As part of creating the graphical frame, it initializes the window system specified by initial-frame-alist and default-frame-alist for the graphical frame, by calling the window-system-initialization function for that window system. This is not done in batch (noninteractive) or daemon mode.
    2. It initializes the initial frame’s faces, and sets up the menu bar and tool bar if needed. If graphical frames are supported, it sets up the tool bar even if the current frame is not a graphical one, since a graphical frame may be created later on.