#+TITLE:  Start Screen
#+AUTHOR: Howard Abrams
#+DATE:   2022-11-02
#+TAGS: emacs

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

#+begin_src emacs-lisp :exports none
  ;;; ha-dashboard --- show a startup screen. -*- lexical-binding: t; -*-
  ;;
  ;; © 2022-2023 Howard Abrams
  ;;   Licensed under a Creative Commons Attribution 4.0 International License.
  ;;   See http://creativecommons.org/licenses/by/4.0/
  ;;
  ;; Author: Howard Abrams <http://gitlab.com/howardabrams>
  ;; Maintainer: Howard Abrams
  ;; Created: November  2, 2022
  ;;
  ;; While obvious, GNU Emacs does not include this file or project.
  ;;
  ;; *NB:* Do not edit this file. Instead, edit the original literate file at:
  ;;            ~/src/hamacs/ha-dashboard.org
  ;;       And tangle the file to recreate this one.
  ;;
  ;;; Code:
  #+end_src
* Left Side
** Dad Jokes!
The /critical part/ of my dashboard, is the [[https://icanhazdadjoke.com/][Dad Joke]] function, a =curl= call to a web service:
#+begin_src sh
  curl -sH "Accept: text/plain" https://icanhazdadjoke.com/
#+end_src

For this, I use the [[https://github.com/tkf/emacs-request][request]] package (and I’ll use this elsewhere too) and the =dashboard= project (defined below) will incorporate it:
#+begin_src emacs-lisp
  (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))))))
#+end_src
** Features
I would appreciate seeing if my Emacs installation has the features that I expect:
#+begin_src emacs-lisp
  (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 (container icon title)
      (font-icons container icon :title title :height 1.3 :v-adjust -0.1))

    (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
             ;; An either or ... at least for me:
             (when (eq (window-system) 'ns)
               (feature-combo  'faicon "apple"   "MacOS"))
             (when (eq (window-system) 'pgtk)
               (feature-combo  'faicon "xing"    "Gnome"))

             ;; Important to know if I have it:
             (when (and (fboundp 'native-comp-available-p)
                        (native-comp-available-p))
               (feature-combo  'faicon "cog"     "Native Compilation"))
             (when (treesit-available-p)
               (feature-combo  'faicon "tree"    "Tree Sitter"))
             (when (sqlite-available-p)
               (feature-combo  'faicon "database" "Sqlite"))
             (when (gnutls-available-p)
               (feature-combo  'faicon "expeditedssl" "TLS"))

             ;; Did we build with mail utilities? Check the command line
             ;; options we used to build Emacs:
             (when (or (string-search "with-mailutils" system-configuration-options)
                       (string-search "without-pop" system-configuration-options))
               (feature-combo  'octicon "mail" "GNU Mail"))

             (when (fboundp 'make-xwidget)
               (feature-combo  'mdicon "widgets" "XWidgets"))

             ;; This suffix of loadable module files will be nil if
             ;; modules are not supported:
             (when module-file-suffix  ; or (fboundp 'module-load)
               (feature-combo 'faicon "th"       "Modules"))

             (when (json-available-p)
               (feature-combo  'codicon "json"   "JSON"))

             (when (string-search "HARFBUZZ" system-configuration-features)
               (feature-combo  'faicon "font"    "HARFBUZZ"))
             (when (string-search "DBUS" system-configuration-features)
               (feature-combo  'faicon "bus"     "DBUS"))
             (feature-combo  'faicon "picture-o" (all-images))
             (when (fboundp 'imagemagick-types)
               (feature-combo  'faicon "magic"   "ImageMagick"))))
           (results (s-join "  " (-remove 'null features))))

      (if (called-interactively-p)
          (message "Enabled features: %s" results)
        results)))
#+end_src
** Dashboard
The [[https://github.com/emacs-dashboard/emacs-dashboard][emacs-dashboard]] project makes a nicer startup screen.
#+begin_src emacs-lisp
  (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-startup-banner
          ;; Choose a random image from my collection of startup images:
          (thread-first "~/src/hamacs/support/dashboard"
                        (directory-files t (rx ".png"))
                        (seq-random-elt)))

    (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-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)))

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

    :config
    (tab-bar-switch-to-tab "main")
    (dashboard-setup-startup-hook))
#+end_src

 This dashboard project requires [[https://github.com/purcell/page-break-lines][page-break-lines]] (which is a nice project):
#+begin_src emacs-lisp
  (use-package page-break-lines)
#+end_src

* 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 [[https://github.com/mykyta-shyrin/cheatsheet][cheatsheet.el]] project.
#+begin_src emacs-lisp
  (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")))
                          #+end_src
** Learn This
Simple function to display a file in the top-right corner (if the file exists):
#+begin_src emacs-lisp
  (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))))
#+end_src

* Altogether
The =dashboard= project hooks to [[help:emacs-startup-hook][emacs-startup-hook]] and this =ha-dashboard= function hooks to dashboard’s [[help:dashboard-after-initialize-hook][dashboard-after-initialize-hook]]:
#+begin_src emacs-lisp
  (defun ha-dashboard ()
    "Shows the extra stuff with the dashboard."
    (interactive)
    (tab-bar-switch-to-tab "main")
    (switch-to-buffer "*dashboard*")
    (setq-local mode-line-format nil)
    (delete-other-windows)
    (split-window-horizontally)
    (other-window 1)
    (switch-to-buffer "*cheatsheet*")
    (ignore-errors
      (cheatsheet-mode)
      (setq-local mode-line-format nil)
      (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))))
#+end_src
* Technical Artifacts                                :noexport:

Let's =provide= a name so we can =require= this file:

#+begin_src emacs-lisp :exports none
  (provide 'ha-dashboard)
  ;;; ha-dashboard.el ends here
#+end_src

#+description: configuring Emacs to show a startup screen.

#+property:    header-args:sh :tangle no
#+property:    header-args:emacs-lisp  :tangle yes
#+property:    header-args    :results none :eval no-export :comments no mkdirp yes

#+options:     num:nil toc:t todo:nil tasks:nil tags:nil date:nil
#+options:     skip:nil author:nil email:nil creator:nil timestamp:nil
#+infojs_opt:  view:nil toc:t ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js
