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.

1 Like

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.

1 Like