Start Screen

Table of Contents

A literate programming file for configuring Emacs to show a startup screen.

Left Side

Dad Jokes!

The critical part of my dashboard, is the Dad Joke function, a curl call to a web service:

curl -sH "Accept: text/plain" https://icanhazdadjoke.com/

For this, I use the request package (and I’ll use this elsewhere too) and the dashboard project (defined below) will incorporate it:

(use-package request
  :init
  (defvar ha-dad-joke nil "Holds the latest dad joke.")

  :config
  (defun ha-dad-joke ()
    "Display a random dad joke."
    (interactive)
    (message (ha--dad-joke)))

  (defun ha--dad-joke ()
    "Return string containing a dad joke from www.icanhazdadjoke.com."
    (setq ha-dad-joke nil)  ; Clear out old joke
    (ha--dad-joke-request)
    (ha--dad-joke-wait))

  (defun ha--dad-joke-wait ()
    (while (not ha-dad-joke)
      (sit-for 1))
    (unless ha-dad-joke
      (ha--dad-joke-wait))
    ha-dad-joke)

  (defun ha--dad-joke-request ()
    (request "https://icanhazdadjoke.com"
      :sync t
      :complete (cl-function
                 (lambda (&key data &allow-other-keys)
                   (setq ha-dad-joke data))))))

Features

I would appreciate seeing if my Emacs installation has the features that I expect:

(defun ha-hamacs-features (&optional non-iconic)
  "Simple display of features I'm most keen about.
  If NON-ICONIC is non-nil, return a string of text only."
  (interactive)

  (defun feature-combo (icon title)
    (if (or non-iconic (null icon))
        title
      (format "%s—%s" icon title)))

  (defun all-images ()
    (s-join "·"
            (-remove 'null
                     (list
                      (when (image-type-available-p 'gif)  "GIF")
                      (when (image-type-available-p 'svg)  "SVG")
                      (when (image-type-available-p 'jpeg) "JPG")
                      (when (image-type-available-p 'tiff) "TIFF")
                      (when (image-type-available-p 'webp) "WEBP")
                      (when (image-type-available-p 'png)  "PNG")))))

  (let* ((features
          (list (when (and (fboundp 'native-comp-available-p)
                           (native-comp-available-p))
                  (feature-combo (all-the-icons-faicon "cog")     "Native Compilation"))
                (when (eq (window-system) 'ns)
                  (feature-combo (all-the-icons-faicon "apple")   "MacOS"))
                (when (eq (window-system) 'pgtk)
                  (feature-combo (all-the-icons-faicon "xing")    "Gnome"))
                (when (treesit-available-p)
                  (feature-combo (all-the-icons-faicon "tree")    "Tree Sitter"))
                (when (sqlite-available-p)
                  (feature-combo (all-the-icons-faicon "database") "Sqlite"))
                (when (gnutls-available-p)
                  (feature-combo (all-the-icons-faicon "expeditedssl") "TLS"))
                (when (or (string-search "with-mailutils" system-configuration-options)
                          (string-search "without-pop" system-configuration-options))
                  (feature-combo (all-the-icons-material "mail") "GNU Mail"))
                (when (fboundp 'make-xwidget)
                  (feature-combo (all-the-icons-material "widgets") "XWidgets"))
                (when module-file-suffix  ; or (fboundp 'module-load)
                  (feature-combo (all-the-icons-faicon "th")      "Modules"))
                (when (json-available-p)
                  (feature-combo (all-the-icons-fileicon "config-js") "JSON"))
                (when (string-search "HARFBUZZ" system-configuration-features)
                  (feature-combo (all-the-icons-faicon "font")    "HARFBUZZ"))
                (when (string-search "DBUS" system-configuration-features)
                  (feature-combo (all-the-icons-faicon "bus")     "DBUS"))
                (feature-combo (all-the-icons-faicon "picture-o") (all-images))
                (when (fboundp 'imagemagick-types)
                  (feature-combo (all-the-icons-faicon "magic")   "ImageMagick"))))
         (results (s-join "  " (-remove 'null features))))

    (if (called-interactively-p)
        (message "Enabled features: %s" results)
      results)))

Dashboard

The emacs-dashboard project makes a nicer startup screen.

(use-package dashboard
  :init
  (defun ha-dashboard-version ()
    (let ((smaller-version (replace-regexp-in-string
                            (rx " (" (zero-or-more any) eol) "" (emacs-version))))
      (string-replace "\n" "" smaller-version)))

  (setq dashboard-banner-logo-title
        (format "Emacs %s — %s"
                (if (and (fboundp 'native-comp-available-p)
                         (native-comp-available-p))
                    "with Native Compilation" "")
                (ha-dashboard-version))
        dashboard-startup-banner "~/src/hamacs/support/levitating-gnu.png"
        dashboard-center-content t
        dashboard-set-init-info t
        dashboard-projects-switch-function 'project-switch-project
        dashboard-items '(;; (projects . 5)
                          ;; (agenda . 5)
                          ;; (bookmarks . 5)
                         )
        dashboard-set-heading-icons t
        dashboard-footer-messages (list (ha--dad-joke)))

  :config
  (dashboard-setup-startup-hook)

  ;; Real shame that :config is incompatible with :hook, otherwise:
  ;; :hook (dashboard-after-initialize . ha-dashboard)
  (add-hook 'dashboard-after-initialize-hook 'ha-dashboard))

This dashboard project requires page-break-lines (which is a nice project):

(use-package page-break-lines)

Right Side

On the right side should show a list of keybindings or other hints that I want to work on memorizing.

Cheatsheet

Lots of things to learn and keep straight. Let’s try the cheatsheet.el project.

(use-package cheatsheet
  :config
  (cheatsheet-add-group 'Text-Objects
                        '(:key "w" :description "word")
                        '(:key "s" :description "sentence")
                        '(:key "p" :description "paragraph")
                        '(:key "l" :description "line / list")
                        '(:key "o" :description "symbol")
                        '(:key "a" :description "argument")
                        '(:key "x" :description "s-exp")
                        '(:key "'" :description "string")
                        '(:key "d" :description "function")
                        '(:key "f" :description "function ... tree-sitter")
                        '(:key "b" :description "loop ... tree-sitter")
                        '(:key "u" :description "condition ... tree-sitter")
                        '(:key "j" :description "smaller indent block")
                        '(:key "k" :description "larger indent block")
                        '(:key "i" :description "indented block")
                        '(:key "c" :description "comment"))

  (cheatsheet-add-group 'Symbols
                        '(:key "(" :description "h d ... jump start s-expression")
                        '(:key ")" :description "h f ... jump to end s-expression")
                        '(:key "{" :description "h e ... forward expression")
                        '(:key "}" :description "h r ... backward expression")
                        '(:key "[" :description "h c ... backward expression")
                        '(:key "]" :description "h v ... forward expression")
                        '(:key "@" :description "h w ... play macro")
                        '(:key "!" :description "h q ... shell command")
                        '(:key "#" :description "h a ... reverse search (not `n')")
                        '(:key "^" :description "h x ... start of line")
                        '(:key "$" :description "h s ... end of line")
                        '(:key "%" :description "h z ... jump paren start/end")
                        '(:key "~" :description "h b ... change case")
                        '(:key "`" :description "h g ... jump to mark. See `m'")
                        '(:key "|" :description "h t ... goto column. Number prefix")
                        )
  (cheatsheet-add-group 'G
                        '(:key "g ;" :description "goto last change")
                        '(:key "g ," :description "return from last change")

                        '(:key "g ." :description "goto definition")
                        '(:key "g >" :description "goto definition other window")
                        '(:key "g ," :description "return definition stack")
                        '(:key "g <" :description "go forward (like definition)")
                        '(:key "g /" :description "find references")
                        '(:key "g ?" :description "find references and replace")
                        '(:key "g h" :description "find apropos with LSP")

                        '(:key "g d" :description "goto definition ... g b to go back")
                        '(:key "g w" :description "fill to object, g q to fill and move")
                        '(:key "g c" :description "comment line")
                        '(:key "g e" :description "go backward word end")
                        '(:key "g s" :description "visual search for line")
                        '(:key "g r" :description "visual search/replace")))

Learn This

Simple function to display a file in the top-right corner (if the file exists):

(defun ha-show-learn-this ()
  ""
  (interactive)
  (let ((filename "~/src/hamacs/learn-this.org")
        (curr-win (get-buffer-window (buffer-name))))
    (when (file-exists-p filename)
      (split-window-below 15)
      (select-window curr-win)
      (find-file filename))))

Altogether

The dashboard project hooks to emacs-startup-hook and this ha-dashboard function hooks to dashboard’s dashboard-after-initialize-hook:

(defun ha-dashboard ()
  "Shows the extra stuff with the dashboard."
  (interactive)
  (switch-to-buffer "*dashboard*")
  (delete-other-windows)
  (split-window-horizontally)
  (other-window 1)
  (switch-to-buffer "*cheatsheet*")
  (ignore-errors
    (cheatsheet-mode)
    (erase-buffer)
    (insert (cheatsheet--format))
    (setq buffer-read-only t))
  ;; (shrink-window-horizontally (- (window-size nil t) 50))
  (shrink-window-horizontally 40)
  (goto-char (point-min))
  (add-hook 'after-init (lambda ()
                 (call-interactively 'ha-hamacs-features))))