282 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Org Mode
		
	
	
	
	
	
			
		
		
	
	
			282 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Org Mode
		
	
	
	
	
	
| #+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:
 | ||
|   ;;            /Users/howard/other/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 (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)))
 | ||
| #+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-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 "~/other/hamacs/support/levitating-gnu.png"
 | ||
|           dashboard-center-content t
 | ||
|           dashboard-set-init-info t
 | ||
|           dashboard-projects-switch-function 'projectile-persp-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))
 | ||
| #+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 "~/other/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)
 | ||
|     (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))))
 | ||
| #+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
 |