Unable to get completion + icons working on GUIX Emacs

Hello crafters community! Nice to meet you! I come here because I require the knowledge of the elisp-fu masters. I had a semi-working Emacs configuration that had somewhat bad autocompletions but overall it kind of worked save for a few details. I now don’t know if these new packages are faster but fragile-er or if there’s anything I can do to fix the mess I have in my Emacs Lisp configurations. I would be extremely grateful if anyone can shed some light onto this skill-issue I have with Emacs Lisp myself.

I used a minimal init.el file I found on GitHub many years ago, and have jumped from helm to ivy to the newest sensation which is vertico, marginalia, corfu, cape, orderless, prescient & embark . I however have the following issues and I haven’t found a single repository, explanation or single source of truth on how this package bundle should be configured:

  1. No autocomplete happens in some programming language modes even when eglot is present
  2. For some reason <Space> doesn’t stop the autocompletion work, I have to escape first and then continue typing (I’m using evil)
  3. nerd-icons are partially working only inside consult and neotree, however marginalia, vertico & corfu are not showing any icons no matter what options are used.

I’m using a “layered” approach and no package manager outside the included (use-package) as I wanted to keep the package count as low as possible.

I could paste my configuration here but it exceeds well over 1000 lines of elisp so I’ll include the relevant parts only:

The layer responsible for nerd-icons does work since neotree and consult do show icons:

(use-package nerd-icons
  :defer t
  :custom
  (nerd-icons-scale-factor 0.9))

Here’s the ~/.config/emacs/layers/neotree-layer.el:

(use-package neotree
    :ensure t
    :init
    (global-set-key [f3] 'neotree-toggle)
    :config
    (setq neo-smart-open t)
    (setq neo-show-hidden-files t)
    (setq neo-create-file-auto-open t)
    (setq neo-window-width 55)
    (setq neo-window-fixed-size nil)
    (setq inhibit-compacting-font-caches t)
    (setq neo-theme (if (display-graphic-p) 'nerd-icons))
    (setq projectile-switch-project-action 'neotree-projectile-action))

; Neotree won't load icons inside emacsclient because `display-graphic-p` does not load
;; at server init.
(defun neo-icons-mode-hook ()
  "Custom `neo-theme` to always display icons."
  (when (display-graphic-p)
    (setq neo-theme 'nerd-icons)))

(add-hook 'neotree-mode-hook #'neo-icons-mode-hook)

I have this ~/.config/emacs/layers/completion-layer.el:

(use-package prescient
  :custom
  (prescient-filter-method '(literal initialism regexp))
  (prescient-sort-length-enable nil)
  (prescient-sort-full-matches-first t)
  (prescient-history-length 200)
  (prescient-frequency-decay 0.997)
  (prescient-frequency-threshold 0.05)
  :config
  (prescient-persist-mode 1))

(use-package corfu
    :ensure t
    :demand t
    :init
    (global-corfu-mode)
    :custom
    (corfu-auto t)
    (corfu-auto-delay 0.3)
    (corfu-cycle t)
    (corfu-preselect 'first)
    (corfu-quit-at-boundary nil) 
    (corfu-separator ?\s)
    (corfu-quit-no-match 'separator)
    (corfu-preview-current t)
    (corfu-on-exact-match nil)
    (corfu-count 14)
    (corfu-scroll-margin 4))

(use-package nerd-icons-corfu
  :ensure t
  :after corfu
  :requires nerd-icons
  :if (display-graphic-p)
  :config
  (add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))

(use-package corfu-prescient
  :demand t
  :after corfu prescient
  :custom
  (corfu-prescient-enable-sorting t)
  (corfu-prescient-override-sorting nil)
  (corfu-prescient-enable-filtering nil) 
  (corfu-prescient-completion-styles '(prescient flex))
  (corfu-prescient-completion-category-overrides
   '((file (styles partial-completion prescient))
     (eglot (styles prescient flex))))
  :config
  (corfu-prescient-mode 1))

(use-package corfu-popupinfo
  :ensure nil
  :hook (corfu-mode . corfu-popupinfo-mode)
  :bind ( :map corfu-map
          ([remap corfu-info-documentation] . corfu-popupinfo-toggle)
          ("M-l" . corfu-popupinfo-location))
  :custom
  (corfu-popupinfo-delay '(nil . 0.3))  ; Don't display initially
  (corfu-popupinfo-direction '(right left vertical))
  (corfu-popupinfo-hide t)
  (corfu-popupinfo-resize t)
  (corfu-popupinfo-max-height 70)
  (corfu-popupinfo-max-width 80)
  (corfu-popupinfo-min-height 1)
  (corfu-popupinfo-min-width 25))

(use-package emacs
  :custom
  (completion-cycle-threshold 5)
  (enable-recursive-minibuffers t)
  (read-extended-command-predicate #'command-completion-default-include-p)
  (tab-always-indent 'complete)
  (read-extended-command-predicate #'command-completion-default-include-p)
  :init
  (defun crm-indicator (args)
    (cons (format "[CRM%s] %s"
                  (replace-regexp-in-string
                   "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
                   crm-separator)
                  (car args))
          (cdr args)))
  (advice-add #'completing-read-multiple :filter-args #'crm-indicator)
  (setq minibuffer-prompt-properties
        '(read-only t cursor-intangible t face minibuffer-prompt))
  (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode))

(use-package orderless
  :custom
  (orderless-matching-styles
   '(orderless-regexp
     orderless-prefixes
     orderless-initialism))
  (orderless-component-separator 'orderless-escapable-split-on-space)
  :config
  (add-to-list 'completion-category-overrides '(eglot (styles . (orderless flex)))))

(use-package cape
  :ensure t
  :defer t
  :init
  (add-hook 'completion-at-point-functions #'cape-dabbrev)
  (add-hook 'completion-at-point-functions #'cape-file)
  (add-hook 'completion-at-point-function #'cape-history)
  (add-hook 'completion-at-point-function #'cape-elisp-symbol)
  (add-hook 'completion-at-point-function #'cape-keyword)
  (add-hook 'completion-at-point-function #'cape-sgml)
  :custom
  (cape-dabbrev-min-length 2)
  :config
  (setq-local completion-at-point-functions
            (list (cape-capf-super #'cape-dabbrev #'cape-dict #'cape-keyword))))

(use-package marginalia
  :ensure t
  :demand t
  :custom
  (marginalia-max-relative-age 0)
  (marginalia-align 'left)
  :bind (:map minibuffer-local-map
         ("M-A" . marginalia-cycle))
  :custom
  (marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil))
  :init
  (marginalia-mode))

(use-package consult
  :init
  (advice-add #'register-preview :override #'consult-register-window)
  (setq register-preview-delay 0.5)
  (setq xref-show-xrefs-function #'consult-xref
        xref-show-definitions-function #'consult-xref)
  :config
  (consult-customize
   consult-theme :preview-key '(:debounce 0.2 any)
   consult-ripgrep consult-git-grep consult-grep
   consult-bookmark consult-recent-file consult-xref
   consult--source-bookmark consult--source-file-register
   consult--source-recent-file consult--source-project-recent-file
   :preview-key '(:debounce 0.4 any))
  (setq consult-narrow-key "<") ;; "C-+")

(use-package consult-dir
  :ensure t
  :after (consult))

(use-package embark
  :ensure t
  :bind
  (("C-." . embark-act)         ;; pick some comfortable binding
   ("M-." . embark-dwim)        ;; good alternative: M-.
   ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings'
  :init
  (setq prefix-help-command #'embark-prefix-help-command)
  :config
  (add-to-list 'display-buffer-alist
               '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
                 nil
                 (window-parameters (mode-line-format . none)))))

;; Consult users will also want the embark-consult package.
(use-package embark-consult
  :ensure t ; only need to install it, embark loads it after consult if found
  :defer t
  :after (consult embark)
  :hook
  (embark-collect-mode . consult-preview-at-point-mode))

(use-package flymake :ensure nil
  :hook
  (prog-mode . flymake-mode)
  :config ; (Optional) For fix bad icon display (Only for left margin)
  (advice-add #'flymake--indicator-overlay-spec
              :filter-return
              (lambda (indicator)
                (concat indicator
                        (propertize " "
                                    'face 'default
                                    'display `((margin left-margin)
                                               (space :width 5))))))
  :custom
  (flymake-indicator-type 'margins)
  (flymake-margin-indicators-string
   `((error ,(nerd-icons-faicon "nf-fa-remove_sign") compilation-error)
     (warning ,(nerd-icons-faicon "nf-fa-warning") compilation-warning)
     (note ,(nerd-icons-faicon "nf-fa-circle_info") compilation-info))))

(use-package nerd-icons-completion
  :ensure t
  :hook (marginalia-mode . nerd-icons-completion-marginalia-setup)
  :config
  (nerd-icons-completion-mode 1))

Finally I have my ~/.config/emacs/layers/vertico-layer.el:

(use-package vertico
  :ensure t
  :demand t
  :defer t
  :bind (:map vertico-map
              ("C-j" . vertico-next)
              ("C-k" . vertico-previous)
              ("C-f" . vertico-exit)
              :map minibuffer-local-map
              ("M-h" . backward-kill-word))
  :hook
  (minibuffer-setup-hook . vertico-repeat-save)
  :custom
  (vertico-scroll-margin 0)
  (vertico-count 20)
  (vertico-resize t)
  (vertico-cycle t)
  :init
  (vertico-mode)
  (vertico-mouse-mode))

(use-package vertico-directory
  :ensure nil
  :requires vertico
  :after vertico
  :hook
  (rfn-eshadow-update-overlay-hook . vertico-directory-tidy))

(use-package vertico-flat
  :ensure nil
  :requires vertico
  :after vertico
  :custom
  (vertico-flat-annotate t)
  (vertico-flat-format
   `( :multiple #("\n{%s}" 0 2 (face minibuffer-prompt) 4 5 (face minibuffer-prompt))
      :single #("\n[%s]" 0 2 (face minibuffer-prompt) 2 4 (face success) 4 5 (face minibuffer-prompt))
      :prompt #("(%s)" 0 1 (face minibuffer-prompt) 3 4 (face minibuffer-prompt))
      :separator #("  |  " 0 5 (face minibuffer-prompt))
      :ellipsis ,(propertize "…" 'face 'minibuffer-prompt)
      :no-match ,(propertize "\n[No match]" 'face 'shadow)
      :spacer #(" " 0 1 (cursor t)))))

(use-package vertico-multiform
  :ensure nil
  :requires vertico
  :after vertico
  :defer t
  :hook
  (vertico-mode-hook . vertico-multiform-mode)
  :custom
  (vertico-multiform-categories
   '((buffer flat (vertico-sort-function . nil))
     (file grid (:keymap . vertico-directory-map))
     (project-file grid)                ; For `project-find-file'
     (command flat (vertico-flat-annotate . nil))
     (symbol-help flat)
     (kill-ring (vertico-sort-function . nil))
     (color (vertico-sort-function . vertico-sort-history-length-alpha))
     (jinx grid
           (vertico-grid-annotate . 20)
           (vertico-grid-max-columns . 12)
           (vertico-grid-separator
            . #("    |    " 4 5 (display (space :width (1)) face (:inherit shadow :inverse-video t)))))))
  (vertico-multiform-commands
   `((pdf-view-goto-label
      (vertico-sort-function . nil))
     (".+-history" (vertico-sort-function . nil))
     (,(rx bol (or (seq "recentf" (* (any alnum))) "consult-recent-file"))
      (vertico-sort-function . nil))
     (,(rx bol (literal "customize-"))
      flat)
     (,(rx bol (or (seq (zero-or-one (literal "krisb-")) (literal "find-library"))
                   (literal "load-library")))
      flat)
     (,(rx bol (literal "consult-history"))
      (vertico-sort-function . nil))
     (,(rx (or "find-function"
               "find-library"
               "find-variable"))
      flat))))

(use-package vertico-buffer
  :requires vertico
  :ensure nil
  :custom
  (vertico-buffer-hide-prompt nil)
  (vertico-buffer-display-action '(display-buffer-reuse-window)))

(use-package vertico-prescient
  :requires vertico
  :after prescient
  :custom
  ;; Sorting
  (vertico-prescient-enable-sorting t)
  (vertico-prescient-override-sorting nil)
  (vertico-prescient-enable-filtering nil)
  (vertico-prescient-completion-styles '(prescient flex))
  (vertico-prescient-completion-category-overrides
   '((file (styles partial-completion prescient))
     (eglot (styles prescient flex))))
  :config
  (vertico-prescient-mode 1))

I know for a fact I must be doing something wrong here, I just don’t know where am I having conflicting settings or if my configuration is outated :worried: I’ve googled for many Emacs configurations but I haven’t found any answers. I thank you in advance for taking the time to read this long post and for any light you can bring into this not-so-skilled lisper.

You must have wasted too much time.

I got few errors. But I removed few lines and I was able to get icons showing in marginalia (M-x find-file) and in corfu as well.

Try to update all packages or reinstall packages.
If not you can try to bisect the code and eval half by half.

I got weird errors as in, a package did not load. So I simply deleted and reinstalled it. Try them by each package in emacs -Q too

Also for specifically nerd-icons in corfu and completions, try to evaluate them again.

(nerd-icons-completion-mode 1)
(add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter)

and see if it gives error or evals fine.

LMK if you are still stuck.

After tons of trial and error, YouTube videos and the always reliable “Read The Friendly Manual”. I’m using GNU Guix, so this took longer because I had to constantly run guix home reconfigure home.scm, until I saw @zororg (thank you!) suggestion which helped me a lot to test and reconfigure once I was sure that I could reproduce from scratch combining emacs -Q and elisp copypasta :sweat_smile:

I’ve found my problem root cause, which was a poor use-package comprehension, I’ll post the process here for people with layered configurations with the following considerations:

  1. Your configuration is divided by file layers like this:
early-init.el => init.el
              =>  layers/
                 => checkers-config.el
                 => dashboard-config.el
                 => dirvish-config.el
  1. You are NOT using any external package manager like straight.el or elpaca and your use-package configuration is present inside init.el.

Here’s the complete source code written in (2025) in case any other lost LISPer finds this thread in the future:

layers/nerd-icons-layer.el

Make sure you are using the latest version of your favourite font. Or install the symbols only. On GNU/Linux systems make sure to also run fc-cache -fv to reconfigure fonts after installing/updating them.

(use-package nerd-icons
  :ensure t
  :defer t
  :custom
  (nerd-icons-scale-factor 0.9))

layers/corfu-layer.el

Special thanks to System Crafters (Crafted Emacs) & Gavin Freeborn (YouTube) for their content, I found some modern code in there.

(use-package corfu
    :ensure t
    :demand t
    :init
    (global-corfu-mode)
    (corfu-history-mode)
    :custom
    (corfu-auto t)
    (corfu-auto-delay 0.0)
    (corfu-auto-prefix 2)
    (corfu-cycle t)
    (corfu-count 14)
    (corfu-min-width 80)
    (corfu-max-width corfu-min-width) ;; lock  width
    (corfu-preselect 'first)
    (corfu-preselect-first nil)
    (corfu-preview-current 'insert)
    (corfu-quit-at-boundary 'separator)
    (corfu-scroll-margin 25)
    (corfu-separator ?\s)
    :bind (:map corfu-map
                ("M-SPC"      . corfu-insert-separator)
                ("RET"        . nil)
                ("TAB"        . corfu-next)
                ([tab]        . corfu-next)
                ("S-TAB"      . corfu-previous)
                ([backtab]    . corfu-previous)
                ("S-<return>" . corfu-insert))
    :config
    (add-hook 'eshell-mode-hook
              (lambda ()
                (setq-local corfu-quit-at-boundary t
                            corfu-quit-no-match t
                            corfu-auto nil)
                (corfu-mode))))

(use-package nerd-icons-corfu
  :ensure t
  :after corfu ;; <- If you omit this, the icons won't work!
  :config
  (add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))

;;; === OPTIONAL, NOT NEEDED, BUT NICE TO HAVE ===

;; included in corfu/extensions/corfu-echo.el
(use-package corfu-echo
  :ensure nil
  :after corfu
  :hook (corfu-mode . corfu-echo-mode))

;; included in corfu/extensions/corfu-info.el
(use-package corfu-info
  :ensure nil
  :after corfu)

;; included in corfu/extensions/corfu-popupinfo.el
(use-package corfu-popupinfo
  :ensure nil
  :after corfu
  :hook (corfu-mode . corfu-popupinfo-mode)
  :custom
  (corfu-popupinfo-delay '(0.25 . 0.1))
  (corfu-popupinfo-hide nil)
  (corfu-popupinfo-resize t)
  (corfu-popupinfo-max-height 70)
  (corfu-popupinfo-max-width 80)
  (corfu-popupinfo-min-height corfu-popupinfo-max-height)
  (corfu-popupinfo-min-width corfu-popupinfo-max-width)

layers/marginalia-layer.el

(use-package marginalia
  :ensure t
  :demand t
  :custom
  (marginalia-max-relative-age 0)
  (marginalia-align 'left)
  ;; Bind `marginalia-cycle' locally in the minibuffer.  To make the binding
  ;; available in the *Completions* buffer, add it to the
  ;; `completion-list-mode-map'.
  :bind (:map minibuffer-local-map
         ("M-A" . marginalia-cycle))

  :custom
  (marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil))

  ;; The :init section is always executed.
  :init

  ;; Marginalia must be activated in the :init section of use-package such that
  ;; the mode gets enabled right away. Note that this forces loading the
  ;; package.
  (marginalia-mode))


;; Show icons inside completion buffers
(use-package nerd-icons-completion
  :ensure t
  :after marginalia
  :requires nerd-icons ;; to avoid evaluation before nerd-icons is ready
  :hook (marginalia-mode . nerd-icons-completion-marginalia-setup)
  :config
  (nerd-icons-completion-mode 1))

For the mode-line stuff there’s little to nothing one can do except use doom-modeline unless you have unlimited patience to either understand the ancient glyphs in the default modeline. The current mode-line-format API is horrible at best, I’d rather write vimscript for ten years.