All Projects â†’ alhassy â†’ Emacs.d

alhassy / Emacs.d

My Emacs configuration, literately 😄

Projects that are alternatives of or similar to Emacs.d

Dotfiles
🏠
Stars: ✭ 60 (-76.38%)
Mutual labels:  dotfiles, emacs
Sensible Defaults.el
A simple, modular collection of better Emacs default settings.
Stars: ✭ 81 (-68.11%)
Mutual labels:  dotfiles, emacs
Dotfiles
well-tailored NixOS & nix-darwin dotfiles
Stars: ✭ 63 (-75.2%)
Mutual labels:  dotfiles, emacs
Dotfiles
🐲 My Arch Linux config [i3-gaps + i3blocks + Zsh + Spacemacs + Rofi + Alacritty + Neofetch]
Stars: ✭ 725 (+185.43%)
Mutual labels:  dotfiles, emacs
Dots
💾 — Dumb & Opinionated Configurations
Stars: ✭ 144 (-43.31%)
Mutual labels:  dotfiles, emacs
Cfgs
My personal configs
Stars: ✭ 14 (-94.49%)
Mutual labels:  dotfiles, emacs
.emacs.d
My emacs configuration
Stars: ✭ 76 (-70.08%)
Mutual labels:  dotfiles, emacs
Dotfiles
●📄 Ryan McGeary's configuration shiznit that makes him productive
Stars: ✭ 278 (+9.45%)
Mutual labels:  dotfiles, emacs
Dotfiles
Configuration files for XMonad, Emacs, NixOS, Taffybar and more.
Stars: ✭ 127 (-50%)
Mutual labels:  dotfiles, emacs
Dotfiles
If there is a shell, there is a way!
Stars: ✭ 112 (-55.91%)
Mutual labels:  dotfiles, emacs
Dotfiles
Let's be honest: mostly Emacs.
Stars: ✭ 536 (+111.02%)
Mutual labels:  dotfiles, emacs
Dotfiles
Jichao Ouyang's awesome dotfiles
Stars: ✭ 182 (-28.35%)
Mutual labels:  dotfiles, emacs
Awesome Dotfiles
Dotfiles for awesome people using the awesomewm linux environment
Stars: ✭ 409 (+61.02%)
Mutual labels:  dotfiles, emacs
.dot Org Files
Dotfiles, Emacs + Org-mode with babel and Literate programming.
Stars: ✭ 57 (-77.56%)
Mutual labels:  dotfiles, emacs
Rogue
Personal Emacs config
Stars: ✭ 286 (+12.6%)
Mutual labels:  dotfiles, emacs
Dotfiles
💻 Dotfiles for zsh, vim, emacs, tmux, and bash. Tested on mac os.
Stars: ✭ 70 (-72.44%)
Mutual labels:  dotfiles, emacs
Dotfiles
👾 ~/
Stars: ✭ 91 (-64.17%)
Mutual labels:  dotfiles, emacs
Emacs Bootstrap
Your on-the-fly Emacs development environment.
Stars: ✭ 147 (-42.13%)
Mutual labels:  dotfiles, emacs
Dotfiles
Ninrod's sharpened dotfiles for emacs, vim, zsh, tmux. Since '15.
Stars: ✭ 208 (-18.11%)
Mutual labels:  dotfiles, emacs
DotThemes
Collection of my Themes (GTK, Xfce, Openbox)
Stars: ✭ 31 (-87.8%)
Mutual labels:  dotfiles

Updated, /pretty/, version: https://alhassy.github.io/emacs.d/index.html


Created 2020-09-16 Wed 10:04

#+OPTIONS: html-postamble:nil toc:nil d:nil num:nil :results nil #+TITLE: A Life Configuring Emacs #+DATE: 2018-07-25 #+AUTHOR: Musa Al-hassy #+export_file_name: README.org

#+html:

I enjoy reading others' /literate/ configuration files and incorporating what I learn into my own. The result is a sufficiently well-documented and accessible read that yields a stylish and functional system (•̀ᴗ•́)و

This README.org has been automatically generated from my configuration and its contents below are accessible in (outdated) blog format, with /colour/, or as colourful PDF, [[https://alhassy.github.io/init/][here]]. Enjoy 😄

#+description: My Emacs Initialisation File, Written in Org-mode. #+startup: indent lognoteclock-out #+property: header-args :tangle init.el :comments link :results none

#+sourcefile: https://github.com/alhassy/emacs.d/blob/master/init.org #+image: ../assets/img/emacs_logo.png #+categories: Emacs Lisp

#+begin_src emacs-lisp :exports none :tangle no (defun go ()) #+end_src

  • Abstract :ignore: #+html:

    #+begin_center Abstract #+end_center #+html:

Herein I document the configurations I utilise with [[https://gnu.org/s/emacs][Emacs]].

As a [[https://www.offerzen.com/blog/literate-programming-empower-your-writing-with-emacs-org-mode][literate program]] file with [[http://orgmode.org/][Org-mode]], I am ensured optimal navigation through my ever growing configuration files, ease of usability and reference for peers, and, most importantly, better maintainability for myself!

Dear reader, when encountering a foregin command X I encourage you to execute (describe-symbol 'X), or press C-h o with the cursor on X. An elementary Elisp Cheat Sheet can be found [[https://github.com/alhassy/ElispCheatSheet][here]] and here is a 2-page 3-column [[https://github.com/alhassy/emacs.d/blob/master/CheatSheet.pdf][Emacs Cheat Sheet]] of the bindings in “this” configuration.

  • C-h o ⇒ What's this thing?
  • C-h e ⇒ What'd /Emacs/ do?
  • C-h l ⇒ What'd /I/ do?
  • C-h ? ⇒ What're the help topics? ---gives possible completions to “C-h ⋯”.
  • “I accidentally hit a key, which one and what did it do!?” ⇒ C-h e and C-h l, then use C-h o to get more details on the action. ;-)

Finally, C-h d asks nicely what ‘d’ocumentation you're interested in. After providing a few keywords, the =apropos= tool yields possible functions and variables that may accomplish my goal.

  • Table of Contents :Github:TOC_4:
  • [[#abstract][Abstract]]
  • [[#booting-up][Booting Up]]
    • [[#emacs-vs-initorg][=~/.emacs= vs. =init.org=]]
      • [[#adventure-time-honey-wheres-my-init][/Adventure time!/ “Honey, where's my init?”]]
      • [[#adventure-time-using-emacs-easy-customisation-interface][/Adventure time!/ Using Emacs' Easy Customisation Interface]]
      • [[#support-for-custom][Support for ‘Custom’]]
    • [[#use-package----the-start-of-initel][=use-package= ---The start of =init.el=]]
    • [[#readme----from-initorg-to-initel][=README= ---From =init.org= to =init.el=]]
      • [[#the-mymake-init-el-and-readme-function][The =my/make-init-el-and-README= function]]
      • [[#the-org-block-named-make-readme][The Org-block named =make-readme=]]
      • [[#table-of-contents-for-org-vs-github][‘Table of Contents’ for Org vs. Github]]
      • [[#alternate-approaches-to-generating-a-readme][Alternate approaches to generating a README]]
    • [[#installing-emacs-packages-directly-from-source][Installing Emacs packages directly from source]]
    • [[#magit----emacs-porcelain-interface-to-gitq][=magit= ---Emacs' porcelain interface to gitq]]
    • [[#syncing-to-the-systems-path][Syncing to the System's =$PATH=]]
    • [[#installing-os-packages-and-automatically-keeping-my-system-up-to-data-from-within-emacs][Installing OS packages, and automatically keeping my system up to data, from within Emacs]]
    • [[#being-at-the-helm----completion--narrowing-framework][“Being at the Helm” ---Completion & Narrowing Framework]]
    • [[#having-a-workspace-manager-in-emacs][Having a workspace manager in Emacs]]
    • [[#excellent-pdf-viewer][Excellent PDF Viewer]]
    • [[#who-am-i----using-gnus-for-gmail][Who am I? ---Using Gnus for Gmail]]
      • [[#prettifications][Prettifications]]
      • [[#super-terse-tutorial][Super Terse Tutorial]]
      • [[#capturing-mail-as-todonotes][Capturing Mail as Todo/Notes]]
      • [[#auto-completing-mail-addresses][Auto-completing mail addresses]]
      • [[#feeds-to-blogs][Feeds to Blogs]]
    • [[#jumping-to-extreme-semantic-units][Jumping to extreme semantic units]]
    • [[#quickly-pop-up-a-terminal-run-a-command-close-it----and-zsh][Quickly pop-up a terminal, run a command, close it ---and zsh]]
    • [[#restarting-emacs----keeping-buffers-open-across-sessions][Restarting Emacs ---Keeping buffers open across sessions?]]
    • [[#automatic-backups][Automatic Backups]]
    • [[#screencapturing-the-current-emacs-frame][Screencapturing the Current Emacs Frame]]
    • [[#editor-documentation-with-contextual-information][Editor Documentation with Contextual Information]]
  • Booting Up Let's decide on where we want to setup our declarations for personalising Emacs to our needs. Then, let's bootstrap Emacs' primitive packaging mechanism with a slick interface ---which not only installs Emacs packages but also programs at the operating system level, all from inside Emacs! Finally, let's declare who we are and use that to setup Emacs email service.

** =/.emacs= vs. =init.org= /Emacs is extenible/: When Emacs is started, it tried to load a user's Lisp program known as a initialisation file which specfies how Emacs should look and behave for you. Emacs looks for the init file using the filenames =/.emacs.el, /.emacs,= or =/.emacs.d/init.el= ---it looks for the first one that exists, in that order; at least it does so on my machine. Below we'll avoid any confusion by /ensuring/ that only one of them is in our system. Regardless, execute =C-h o user-init-file= to see the name of the init file loaded. Having no init file is tantamount to have an empty init file.

  • One can read about the various Emacs initialisation files [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Init-File.html#Init-File][online]] or within Emacs by the sequence C-h i m emacs RET i init file RET.
  • A /friendly/ tutorial on ‘beginning a =.emacs= file’ can be read [[https://www.gnu.org/software/emacs/manual/html_node/eintr/Beginning-init-File.html#Beginning-init-File][online]] or within Emacs by C-h i m emacs lisp intro RET i .emacs RET.
  • After inserting some lisp code and saving, such as (set-background-color "salmon"), one can load the changes with M-x eval-buffer.
  • In a terminal, use emacs -Q to open emacs without any initialisation files.

Besides writing Lisp in an init file, one may use Emacs' customisation interface, M-x customize: Point and click to change Emacs to your needs. The resulting customisations are, by default, automatically thrown into your init file ---=~/.emacs= is created for you if you have no init file. This interface is great for beginners, but one major drawback is that it's a bit difficult to share settings since it's not amicable to copy-pasting.

We shall use =/.emacs.d/init.el= as the initialisation file so that /all/ of our Emacs related files live in the /same/ directory: =/.emacs.d/=.

A raw code file is difficult to maintain, especially for a /large/ system such as Emacs. Instead, we're going with a ‘literate programming’ approach: The intialisation configuration is presented in an essay format, along with headings and subheadings, intended for consumption by humans such as myself, that, incidentally, can be ‘tangled’ into a raw code file that is comprehensible by a machine. We achieve this goal using [[#Life-within-Org-mode][org-mode]] ---/Emacs' killer app/--- which is discussed in great detail later on.

*** /Adventure time!/ “Honey, where's my init?” Let's use the three possible locations for the initialisation files to explore how Emacs finds them. Make the following three files.

~/.emacs.el #+begin_src emacs-lisp :tangle no ;; Emacs looks for this first; (set-background-color "chocolate3") (message-box ".emacs.el says hello") #+end_src ~/.emacs #+begin_src emacs-lisp :tangle no ;; else; looks for this one; (set-background-color "plum4") (message-box ".emacs says hello") #+end_src ~/.emacs.d/init.el #+begin_src emacs-lisp :tangle no ;; Finally, if neither are found; it looks for this one. (set-background-color "salmon") (message-box ".emacs.d/init.el says hello") #+end_src

Now restart your Emacs to see how there super tiny initilaisation files affect your editor. Delete some of these files in-order for others to take effect!

*** /Adventure time!/ Using Emacs' Easy Customisation Interface We have chosen not to keep configurations in /.emacs since Emacs may explicitly add, or alter, code in it.

Let's see this in action!

Execute the following to see additions to the /.emacs have been added by ‘custom’.

  1. =M-x customize-variable RET line-number-mode RET=
  2. Then press: toggle, state, then 1.
  3. Now take a look: =C-x C-f ~/.emacs=

*** Support for ‘Custom’ Let the Emacs customisation GUI insert configurations into its own file, not touching or altering my initialisation file. For example, I tend to have local variables to produce README.org's and other matters, so Emacs' Custom utility will remember to not prompt me each time for the safety of such local variables. #+begin_src emacs-lisp (setq custom-file "~/.emacs.d/custom.el") (ignore-errors (load custom-file)) ;; It may not yet exist. #+end_src

Speaking of local variables, let's always ones we've already marked as safe ---see the bottom of the source of this file for an example of local variables. ( At one point, all my files had locals! ) #+begin_src emacs-lisp (setq enable-local-variables :safe) #+end_src

** =use-package= ---The start of =init.el= There are a few ways to install packages ---run C-h C-e for a short overview. The easiest, for a beginner, is to use the command package-list-packages then find the desired package, press i to mark it for installation, then install all marked packages by pressing x.

  • /Interactively/: M-x list-packages to see all melpa packages that can install
    • Press Enter on a package to see its description.
  • Or more quickly, to install, say, the haskell mode: M-x package-install RET unicode-fonts RET.

“From rags to riches”: Recently I switched to Mac ---first time trying the OS. I had to do a few package-install's and it was annoying. I'm looking for the best way to package my Emacs installation ---including my installed packages and configuration--- so that I can quickly install it anywhere, say if I go to another machine. It seems use-package allows me to configure and auto install packages. On a new machine, when I clone my .emacs.d and start Emacs, on the first start it should automatically install and compile all of my packages through use-package when it detects they're missing.

First we load package, the built-in package manager. It is by default only connected to the GNU ELPA (Emacs Lisp Package Archive) repository, so we extended it with other popular repositories; such as the much larger [[https://melpa.org/#/][MELPA]] (Milkypostman's ELPA) ---it builds packages [[https://github.com/melpa/melpa][directly from the source-code reposistories of developers]], rather than having all packages in one repository. #+begin_src emacs-lisp ;; Make all commands of the “package” module present. (require 'package)

;; Internet repositories for new packages. (setq package-archives '(("org" . "http://orgmode.org/elpa/") ("gnu" . "http://elpa.gnu.org/packages/") ("melpa" . "http://melpa.org/packages/")))

;; Actually get “package” to work. (package-initialize) (package-refresh-contents) #+end_src

  • All installed packages are placed, by default, in =~/.emacs.d/elpa=.
  • Neato: /If one module requires others to run, they will be installed automatically./

The declarative configuration tool [[https://github.com/jwiegley/use-package/][use-package]] is a macro/interface that manages other packages and the way they interact.

  • It allows us to tersely organise a package's configuration.

    • By default, (use-package foo) only loads a package, if it's on our system.
      • Use the standalone keyword :disabled to turn off loading a module that, say, you're not using anymore.
  • It is /not/ a package manger, but we can make it one by having it automatically install modules, via Emacs packing mechanism, when they're not in our system.

    We achieve this by using the keyword option :ensure t.

  • Here are common keywords we will use, in super simplified terms.

    • :init f₁ … fₙ /Always/ executes code forms fᾢ /before/ loading a package.

    • :diminish str Uses /optional/ string str in the modeline to indicate this module is active. Things we use often needn't take real-estate down there and so no we provide no str.

    • :config f₁ … fₙ /Only/ executes code forms fᾢ /after/ loading a package.

      The remaining keywords only take affect /after/ a module loads.

    • :bind ((k₁ . f₁) … (kₙ . fₙ) Lets us bind keys kᾢ, such as "M-s o", to functions, such as =occur=.

      • When /n = 1/, the extra outer parenthesis are not necessary.
    • :hook ((m₁ … mₙ) . f) Enables functionality f whenever we're in one of the modes mᾢ, such as org-mode. The . f, along with the outermost parenthesis, is optional and defaults to the name of the package ---Warning: Erroneous behaviour happens if the package's name is not a function provided by the package; a common case is when package's name does /not/ end in -mode, leading to the invocation ((m₁ … mₙ) . -mode) instead.

      Additionally, when /n = 1/, the extra outer parenthesis are not necessary.

      Outside of =use-package=, one normally uses a add-hook clause. Likewise, an ‘advice’ can be given to a function to make it behave differently ---this is known as ‘decoration’ or an ‘attribute’ in other languages.

    • :custom (k₁ v₁ d₁) … (kₙ vₙ dₙ) Sets a package's custom variables kᾢ to have values vᾢ, along with /optional/ user documentation dᾢ to explain to yourself, in the future, why you've made this decision.

      This is essentially setq within :config.

We now bootstrap use-package. #+begin_src emacs-lisp (unless (package-installed-p 'use-package) (package-install 'use-package)) (require 'use-package) #+end_src

We can now invoke (use-package XYZ :ensure t) which should check for the XYZ package and make sure it is accessible. If not, the :ensure t part tells use-package to download it ---using the built-in package manager--- and place it somewhere accessible, in =~/.emacs.d/elpa/= by default. By default we would like to download packages, since I do not plan on installing them manually by downloading Lisp files and placing them in the correct places on my system. #+begin_src emacs-lisp (setq use-package-always-ensure t) #+end_src The use of :ensure t only installs absent modules, but it does no updating. Let's set up [[https://github.com/rranelli/auto-package-update.el][an auto-update mechanism]]. #+begin_src emacs-lisp (use-package auto-package-update :defer 10 :config ;; Delete residual old versions (setq auto-package-update-delete-old-versions t) ;; Do not bother me when updates have taken place. (setq auto-package-update-hide-results t) ;; Update installed packages at startup if there is an update pending. (auto-package-update-maybe)) #+end_src

Here's another example use of use-package. Later on, I have a “show recent files pop-up” command set to C-x C-r; but what if I forget? This mode shows me all key completions when I type C-x, for example. Moreover, I will be shown other commands I did not know about! Neato :-) #+begin_src emacs-lisp ;; Making it easier to discover Emacs key presses. (use-package which-key :diminish :defer 5 :config (which-key-mode) (which-key-setup-side-window-bottom) (setq which-key-idle-delay 0.05)) #+end_src ⟨ Honestly, I seldom even acknowledge this pop-up; but it's always nice to show to people when I'm promoting Emacs. ⟩

Above, the :diminish keyword indicates that we do not want the mode's name to be shown to us in the modeline ---the area near the bottom of Emacs. It does so by using the diminish package, so let's install that. #+begin_src emacs-lisp (use-package diminish :defer 5 :config ;; Let's hide some markers. (diminish 'org-indent-mode)) #+end_src

Here are other packages that I want to be installed onto my machine. #+begin_src emacs-lisp ;; Efficient version control. ;; ;; Bottom of Emacs will show what branch you're on ;; and whether the local file is modified or not. (use-package magit :config (global-set-key (kbd "C-x g") 'magit-status))

(use-package htmlize :defer t) ;; Main use: Org produced htmls are coloured. ;; Can be used to export a file into a coloured html.

;; Get org-headers to look pretty! E.g., * → ⊙, ** ↦ ◯, *** ↦ ★ ;; https://github.com/emacsorphanage/org-bullets (use-package org-bullets :hook (org-mode . org-bullets-mode))

;; Haskell's cool (use-package haskell-mode :defer t)

;; Lisp libraries with Haskell-like naming. (use-package dash) ;; “A modern list library for Emacs” (use-package s ) ;; “The long lost Emacs string manipulation library”.

;; Library for working with system files; ;; e.g., f-delete, f-mkdir, f-move, f-exists?, f-hidden? (use-package f) #+end_src

Note:

  • [[https://github.com/magnars/dash.el][dash]]: “A modern list library for Emacs”
    • E.g., (--filter (> it 10) (list 8 9 10 11 12))
  • [[https://github.com/magnars/s.el][s]]: “The long lost Emacs string manipulation library”.
    • E.g., s-trim, s-replace, s-join.

Remember that snippet for undo-tree in the introductory section? Let's activate it now, after use-package has been setup. #+begin_src emacs-lisp :noweb yes <> #+end_src

Finally, let's try our best to have a [[https://chris.beams.io/posts/git-commit/][useful & consistent commit log]]: #+begin_src emacs-lisp (defun my/git-commit-reminder () (insert "\n\n# The commit subject line ought to finish the phrase:

“If applied, this commit will ⟪your subject line here⟫.” ")

(beginning-of-buffer))

(add-hook 'git-commit-setup-hook 'my/git-commit-reminder) #+end_src

Super neat stuff!

** =README= ---From =init.org= to =init.el= Rather than manually extracting the Lisp code from this literate document each time we alter it, let's instead add a ‘hook’ ---a method that is invoked on a particular event, in this case when we save the file. More precisely, in this case, C-x C-s is a normal save whereas C-u C-x C-s is a save after forming init.elc and README.md.

*** The =my/make-init-el-and-README= function We ‘hook on’ the following function to the usual save method that is associated with this file only.

#+name: startup-code #+begin_src emacs-lisp :eval never-export (defun my/make-init-el-and-README () "Tangle an el and a github README from my init.org." (interactive "P") ;; Places value of universal argument into: current-prefix-arg (when current-prefix-arg (let* ((time (current-time)) (date (format-time-string "%Y-%m-%d")) (.emacs "/.emacs") (.emacs.el "/.emacs.el")) ;; Make README.org (save-excursion (org-babel-goto-named-src-block "make-readme") ;; See next subsubsection. (org-babel-execute-src-block))

      ;; remove any other initialisation file candidates
      (ignore-errors
        (f-move .emacs    (concat .emacs _date))
        (f-move .emacs.el (concat .emacs.el _date)))

      ;; Make init.el
      (org-babel-tangle)
      ;; (byte-compile-file "~/.emacs.d/init.el")
      (load-file "~/.emacs.d/init.el")

      ;; Acknowledgement
      (message "Tangled, compiled, and loaded init.el; and made README.md … %.06f seconds"
               (float-time (time-since time))))))

(add-hook 'after-save-hook 'my/make-init-el-and-README nil 'local-to-this-file-please) #+end_src

*** The Org-block named =make-readme= Where the following block has #+NAME: make-readme before it. This source block generates the README for the associated Github repository. #+name: make-readme #+begin_src emacs-lisp :tangle no :export_never t (save-buffer) (with-temp-buffer (insert "#+EXPORT_FILE_NAME: README.org

   # Logos and birthday present painting
   ,#+HTML:" (s-collapse-whitespace (concat
  " <p align=\"center\">
     <img src=\"images/emacs-logo.png\" width=150 height=150/>
   </p>

   <p align=\"center\">
      <a href=\"https://www.gnu.org/software/emacs/\">
           <img src=\"https://img.shields.io/badge/GNU%20Emacs-" emacs-version "-b48ead.svg?style=plastic\"/></a>
      <a href=\"https://orgmode.org/\"><img src=\"https://img.shields.io/badge/org--mode-" org-version "-489a9f.svg?style=plastic\"/></a>
   </p>

   <p align=\"center\">
     <img src=\"images/emacs-birthday-present.png\" width=250 height=250/>
   </p>
  "))

 ;; My Literate Setup; need the empty new lines for the export
 "

   I enjoy reading others' /literate/ configuration files and
   incorporating what I learn into my own. The result is a
   sufficiently well-documented and accessible read that yields
   a stylish and functional system (•̀ᴗ•́)و

   This ~README.org~ has been automatically generated from my
   configuration and its contents below are accessible
   in (outdated) blog format, with /colour/, or as colourful
   PDF, [[https://alhassy.github.io/init/][here]]. Enjoy
   😄

   ,#+INCLUDE: init.org
  ")

  ;; No code execution on export
  ;; ⟪ For a particular block, we use “:eval never-export”. ⟫
  (let ((org-export-use-babel nil))
    (org-mode)
    (org-org-export-to-org)))

#+end_src Alternatively, evaluate the above source block with C-c C-c to produce a README file.

For the ‘badges’, see https://shields.io/. The syntax above is structured: #+begin_example org https://img.shields.io/badge/--.svg #+end_example

*** ‘Table of Contents’ for Org vs. Github The above mentioned package [[https://github.com/snosov1/toc-org][toc-org]], which creates an up-to-date table of contents in an org file, at any heading tagged :TOC:. It's useful primarily for README files on Github. There is also [[https://github.com/alphapapa/org-make-toc][org-make-toc]], which is more flexible: The former provides only a top-level TOC; whereas this package allows TOCs at the sibling level, say, to produce a TOC of only the subsections of a particular heading, and other TOC features. Unlike toc-org, org-make-toc uses property drawers to designate TOC matter. #+begin_src emacs-lisp (use-package toc-org ;; Automatically update toc when saving an Org file. :hook (org-mode . toc-org-mode) ;; Use both “:ignore_N:” and ":export_N:” to exlude headings from the TOC. :custom (toc-org-noexport-regexp "\(^+\)\s+.:\(ignore\|noexport\)\([@_][0-9]\)?:\($\|[^ ]*?:$\)")) #+end_src

However, [[https://github.com/snosov1/toc-org/issues/54#issuecomment-363710561][toc-org produces broken links for numbered sections]]. That is, if we use =#+OPTIONS: num:t= then a section, say ** =/.emacs= vs. =init.org=~ as the first subheading of the third heading, then it renders with the text preceeded by =3.1=. On the left-most part of the heading, Github provides a a link option; clicking provides a link to this exact location in the README, changing the current URL to something like =https://github.com/alhassy/emacs.d#31-emacs-vs-initorg=. Now, toc-org produces Github-style anchors from Org headings, but does not account for numbers, and so gives us =https://github.com/alhassy/emacs.d#emacs-vs-initorg=, which is so close but missing the translated number, 31.

I've experimented with using toc-org links using org-style, instead of the default Github style, but it seems that the org-style completely breaks rendering the resulting readme. Likewise, [[https://github.com/snosov1/toc-org/issues/3][it seems]] that headings that are links break the TOC link; whence my section on the Reveal slide-deck system has a broken link to it. Perhaps org-make-toc solves these issues ---something to look into.

I'm not sure how I feel about actually having the Github-serving TOC in my source file. It's nice to have around, from an essay-perspecive, but it breaks HTML export since its links are /not/ well-behaved; e.g., :ignore:-ed headlines appear in the toc, but do not link to any visible heading in the HTML; likewise, headings with URLS in their names break. As such, below I've developed a way to erase it altogether ---alternatively, one could mark the toc as :noexport:, but this would then, in my current approach, not result in a toc in the resulting README. #+begin_src emacs-lisp (cl-defun my/org-replace-tree-contents (heading &key (with "") (offset 0)) "Replace the contents of org tree HEADING with WITH, starting at OFFSET.

Clear a subtree leaving first 3 lines untouched ⇐ :offset 3 Deleting a tree & its contents ⇐ :offset -1, or any negative number. Do nothing to a tree of 123456789 lines ⇐ :offset 123456789

Precondition: offset < most-positive-fixnum; else we wrap to a negative number." (interactive) (save-excursion (beginning-of-buffer) (re-search-forward (format "^\*+ %s" (regexp-quote heading))) ;; To avoid ‘forward-line’ from spilling onto other trees. (org-narrow-to-subtree) (org-mark-subtree) ;; The 1+ is to avoid the heading. (dotimes (_ (1+ offset)) (forward-line)) (delete-region (region-beginning) (region-end)) (insert with) (widen)))

;; Erase :TOC: body ---provided we're using toc-org. ;; (my/org-replace-tree-contents "Table of Contents") #+end_src *** Alternate approaches to generating a README Github supports several markup languages, one of which is Org-mode.

  • It seems that Github uses [[https://github.com/bdewey/org-ruby][org-ruby]] to convert org-mode to html.
  • [[https://github.com/novoid/github-orgmode-tests][Here]] is a repo demonstrating how Github interprets Org-mode files.
  • org-ruby supports inline #+HTML but [[https://github.com/wallyqs/org-ruby/issues/51][not html blocks]].

It seems coloured HTML does not render well: #+begin_example emacs-lisp (org-html-export-to-html) (shell-command "mv README.html README.md") #+end_example

[[https://orgmode.org/manual/JavaScript-support.html][JavaScript supported display of web pages]] with: #+begin_example org ,#+INFOJS_OPT: view:info toc:t buttons:t #+end_example This looks nice for standalone pages, but doesn't incorporate nicely with github README.org.

Usually, Github readme files are in markdown, which we may obtain from an Org file with =M-x org-md-export-to-markdown=.

  • [ ] By default, this approach results in grey-coloured source blocks ---eek!

  • [X] It allows strategic placement of a table of contents.

    Declare ~#+options: toc:nil~ at the top of the Org file, then have =#+TOC:
    

    headlines 2= in a strategic position for a table of contents, say after a brief explanation of what the readme is for.

  • [X] It allows us to preview the readme locally before comitting, using [[https://github.com/joeyespo/grip][grip]].

#+begin_src emacs-lisp :tangle no ;; grip looks for README.md (system-packages-ensure "grip") ;; Next: (async-shell-command "cd ~/.emacs.d/; grip") #+end_src

We can approximate this behaviour for the other approaches:

  1. Export to markdown.
  2. =COMMENT=-out any =:TOC:=-tagged sections ---their links are not valid markdown links, since they don't refer to any markdown labels.
  3. Rename the exported file to =README.md=.
  4. Run grip.

** Installing Emacs packages directly from source [[https://github.com/quelpa/quelpa-use-package][Quelpa]] allows us to build Emacs packages directly from source repositories. It derives its name from the German word /Quelle/, for /souce/ [code], adjoined to ELPA. Its use-package interface allows us to use use-package like normal but when we want to install a file from souce we use the keyword :quelpa.

#+begin_src emacs-lisp (use-package quelpa :defer 5 :custom (quelpa-upgrade-p t "Always try to update packages") :config ;; Get ‘quelpa-use-package’ via ‘quelpa’ (quelpa '(quelpa-use-package :fetcher git :url "https://github.com/quelpa/quelpa-use-package.git")) (require 'quelpa-use-package)) #+end_src

Let's use this to obtain an improved info-mode from the EmacsWiki. [Disabled for now] #+begin_src emacs-lisp :tangle no (use-package info+ :disabled :quelpa (info+ :fetcher wiki :url "https://www.emacswiki.org/emacs/info%2b.el")) #+end_src

** =magit= ---Emacs' porcelain interface to gitq Let's setup an Emacs ‘porcelain’ interface to git ---it makes working with version control tremendously convenient. Moreover, I add a little pop-up so that I don't forget to commit often!

Why use magit as the interface to the git version control system? In magit buffer nearly everything can be acted upon: Press =return=, or =space=, to see details and =tab= to see children items, usually.

First, let's setup our git credentials. #+begin_src emacs-lisp ;; See here for a short & useful tutorial: ;; https://alvinalexander.com/git/git-show-change-username-email-address (when (equal "" (shell-command-to-string "git config user.name")) (shell-command "git config --global user.name "Musa Al-hassy"") (shell-command "git config --global user.email "[email protected]"")) #+end_src

Below is my personal quick guide to working with magit ---for a full tutorial see [[http://jr0cket.co.uk/2012/12/driving-git-with-emacs-pure-magic-with.html.html][jr0cket's blog]].

  • dired :: See the contents of a particular directory.

  • magit-init :: Put a project under version control. The mini-buffer will prompt you for the top level folder version. A .git folder will be created there.

  • magit-status , C-x g :: See status in another buffer. Press ? to see options, including:

    • g :: Refresh the status buffer.

    • TAB :: See collapsed items, such as what text has been changed.

    • q :: Quit magit, or go to previous magit screen.

    • s :: Stage, i.e., add, a file to version control. Add all untracked files by selecting the /Untracked files/ title.

      [[https://softwareengineering.stackexchange.com/a/119807/185815][The staging area is akin to a pet store; commiting is taking the pet home.]]

    • k :: Kill, i.e., delete a file locally.

    • K :: This' (magit-file-untrack) which does git rm --cached.

    • i :: Add a file to the project .gitignore file. Nice stuff =)

    • u :: Unstage a specfif staged change highlighed by cursor. C-u s stages everything --tracked or not.

    • c :: Commit a change.

      • A new buffer for the commit message appears, you write it then commit with C-c C-c or otherwise cancel with C-c C-k. These commands are mentioned to you in the minibuffer when you go to commit.
      • You can provide a commit to /each/ altered chunk of text! This is super neat, you make a series of local such commits rather than one nebulous global commit for the file. The magit interface makes this far more accessible than a standard terminal approach!
      • You can look at the unstaged changes, select a /region/, using C-SPC as usual, and commit only that if you want!
      • When looking over a commit, M-p/n to efficiently go to previous or next altered sections.
      • Amend a commit by pressing a on HEAD.
    • d :: Show differences, another d or another option.

      • This is magit! Each hunk can be acted upon; e.g., s or c or k ;-)
    • v :: Revert a commit.

    • x :: Undo last commit. Tantamount to git reset HEAD when cursor is on most recent commit; otherwise resets to whatever commit is under the cursor.

    • l :: Show the log, another l for current branch; other options will be displayed.

      • Here space shows details in another buffer while cursour remains in current buffer and, moreover, continuing to press space scrolls through the other buffer! Neato.
    • P :: Push.

    • F :: Pull.

    • : :: Execute a raw git command; e.g., enter whatchanged.

Notice that every time you press one of these commands, a ‘pop-up’ of realted git options appears! Thus not only is there no need to memorise many of them, but this approach makes /discovering/ other commands easier.

Below are the git repos I'd like to clone ---along with a function to do so quickly. #+begin_src emacs-lisp (use-package magit :defer t :custom ;; Do not ask about this variable when cloning. (magit-clone-set-remote.pushDefault t))

(cl-defun maybe-clone (remote &optional (local (concat "~/" (file-name-base remote)))) "Clone a REMOTE repository if the LOCAL directory does not exist.

Yields ‘repo-already-exists’ when no cloning transpires, otherwise yields ‘cloned-repo’.

LOCAL is optional and defaults to the base name; e.g., if REMOTE is https://github.com/X/Y then LOCAL becomes ~/Y." (if (file-directory-p local) 'repo-already-exists (async-shell-command (concat "git clone " remote " " local)) (add-to-list 'magit-repository-directories `(,local . 0)) 'cloned-repo))

(maybe-clone "https://github.com/alhassy/emacs.d" "~/.emacs.d") (maybe-clone "https://github.com/alhassy/alhassy.github.io") (maybe-clone "https://github.com/alhassy/CheatSheet") (maybe-clone "https://github.com/alhassy/ElispCheatSheet") (maybe-clone "https://github.com/alhassy/CatsCheatSheet") (maybe-clone "https://github.com/alhassy/islam")

;; For brevity, many more ‘maybe-clone’ clauses are hidden in the source file. #+end_src Let's always notify ourselves of a file that has [[https://tpapp.github.io/post/check-uncommitted/][uncommited changes]] ---we might have had to step away from the computer and forgotten to commit. #+begin_src emacs-lisp (require 'magit-git)

(defun my/magit-check-file-and-popup () "If the file is version controlled with git and has uncommitted changes, open the magit status popup." (let ((file (buffer-file-name))) (when (and file (magit-anything-modified-p t file)) (message "This file has uncommited changes!") (when nil ;; Became annyoying after some time. (split-window-below) (other-window 1) (magit-status)))))

;; I usually have local variables, so I want the message to show ;; after the locals have been loaded. (add-hook 'find-file-hook '(lambda () (add-hook 'hack-local-variables-hook 'my/magit-check-file-and-popup))) #+end_src

Finally, one of the main points for using version control is to have access to historic versions of a file. The following utility allows us to M-x git-timemachine on a file and use p/n/g/q to look at previous, next, goto arbitrary historic versions, or quit. #+begin_src emacs-lisp (use-package git-timemachine :defer t) #+end_src If we want to roll back to a previous version, we just write-file or C-x C-s as usual! The power of text!

** Syncing to the System's =$PATH= For one reason or another, on OS X it seems that an Emacs instance begun from the terminal may not inherit the terminal's environment variables, thus making it difficult to use utilities like pdflatex when Org-mode attempts to produce a PDF.

#+begin_src emacs-lisp (use-package exec-path-from-shell :init (when (memq window-system '(mac ns x)) (exec-path-from-shell-initialize))) #+end_src

See the [[https://github.com/purcell/exec-path-from-shell][exec-path-from-shell]] documentation for setting other environment variables. ** Installing OS packages, and automatically keeping my system up to data, from within Emacs Sometimes Emacs packages depend on existing system binaries, use-package let's us ensure these exist using the :ensure-system-package keyword extension.

  • This is like :ensure t but operates at the OS level and uses your default OS package manager.

Let's obtain the extension. #+begin_src emacs-lisp ;; Auto installing OS system packages (use-package use-package-ensure-system-package :defer 5 :config (system-packages-update))

;; Ensure our operating system is always up to date. ;; This is run whenever we open Emacs & so wont take long if we're up to date. ;; It happens in the background ^_^ ;; ;; After 5 seconds of being idle, after starting up. #+end_src

After an update to Mac OS, one may need to [[https://emacs.stackexchange.com/questions/53026/how-to-restore-file-system-access-in-macos-catalina][restore file system access privileges to Emacs]].

Here's an example use for Emacs packages that require OS packages: #+begin_src emacs-lisp :tangle no (shell-command-to-string "type rg") ;; ⇒ rg not found (use-package rg :ensure-system-package rg) ;; ⇒ There's a buffer system-packages ;; installing this tool at the OS level! #+end_src If you look at the Messages buffer, via C-h e, on my machine it says brew install rg: finished ---it uses brew which is my OS package manager!

  • The [[https://github.com/jwiegley/use-package#use-package-ensure-system-package][use-package-ensure-system-package]] documentation for a flurry of use cases.

The extension makes use of [[https://gitlab.com/jabranham/system-packages][system-packages]]; see its documentation to learn more about managing installed OS packages from within Emacs. This is itself a powerful tool, however it's interface M-x system-packages-install leaves much to be desired ---namely, tab-compleition listing all available packages, seeing their descriptions, and visiting their webpages. This is remedied by [[https://github.com/emacs-helm/helm-system-packages][M-x helm-system-packages]] then RET to see a system package's description, or TAB for the other features! /This is so cool!/

#+begin_src emacs-lisp ;; An Emacs-based interface to the package manager of your operating system. (use-package helm-system-packages :defer t) #+end_src

The Helm counterpart is great for /discovarability/, whereas the plain system-packages is great for /programmability/.

It is tedious to arrange my program windows manually, and as such I love tiling window managers, which automatically arrange them. I had been using [[https://xmonad.org][xmonad]] until recently when I obtained a Mac machine and now use [[https://ianyh.com/amethyst/][Amethyst]] ---“Tiling window manager for macOS along the lines of xmonad.”

#+begin_src emacs-lisp ;; Unlike the Helm variant, we need to specify our OS pacman. (setq system-packages-package-manager 'brew) #+end_src #+begin_src emacs-lisp :tangle no ;; Use “brew cask install” instead of “brew install” for installing programs. (setf (nth 2 (assoc 'brew system-packages-supported-package-managers)) '(install . "brew cask install"))

;; If the given system package doesn't exist; install it. (system-packages-ensure "amethyst") #+end_src Neato! Now I can live in Emacs even more ^_^

** “Being at the Helm” ---Completion & Narrowing Framework Whenever we have a choice to make from a list, [[http://tuhdo.github.io/helm-intro.html][Helm]] provides possible completions and narrows the list of choices as we type. This is extremely helpful for when switching between buffers, =C-x b=, and discovering & learning about other commands! E.g., press M-x to see recently executed commands and other possible commands! Press M-x and just start typing, methods mentioning what you've typed are suddenly listed!

| Remembrance comes with time, until then /ask/ Emacs! |

/Try and be grateful!/ #+begin_src emacs-lisp (use-package helm :diminish :init (helm-mode t) :bind (("M-x" . helm-M-x) ("C-x C-f" . helm-find-files) ("C-x b" . helm-mini) ;; See buffers & recent files; more useful. ("C-x r b" . helm-filtered-bookmarks) ("C-x C-r" . helm-recentf) ;; Search for recently edited files ("C-c i" . helm-imenu) ("C-h a" . helm-apropos) ;; Look at what was cut recently & paste it in. ("M-y" . helm-show-kill-ring)

      :map helm-map
      ;; We can list ‘actions’ on the currently selected item by C-z.
      ("C-z" . helm-select-action)
      ;; Let's keep tab-completetion anyhow.
      ("TAB"   . helm-execute-persistent-action)
      ("<tab>" . helm-execute-persistent-action)))

#+end_src

Helm provides generic functions for completions to replace tab-completion in Emacs with no loss of functionality.

  • The =execute-extended-command=, the default “M-x”, is replaced with helm-M-x which shows possible command completions.

    Likewise with apropos, which is helpful for looking up commands. It shows all meaningful Lisp symbols whose names match a given pattern.

  • The ‘Helm-mini’, C-x b, shows all buffers, recently opened files, bookmarks, and allows us to create new bookmarks and buffers!

  • The ‘Helm-imenu’, C-c i, yields a a menu of all “top-level items” in a file; e.g., functions and constants in source code or headers in an org-mode file.

    ⟳ Nifty way to familarise yourself with a new code base, or one from a while ago.

  • When Helm is active, C-x lists possible course of actions on the currently selected item.

When helm-mode is enabled, even help commands make use of it. E.g., C-h o runs describe-symbol for the symbol at point, and C-h w runs where-is to find the key binding of the symbol at point. Both show a pop-up of other possible commands.

   Here's a nifty tutorial:

[[http://tuhdo.github.io/helm-intro.html][A package in a league of its own: Helm]]

Let's ensure C-x b shows us: Current buffers, recent files, and bookmarks ---as well as the ability to create bookmarks, which is via C-x r b manually. For example, I press C-x b then type any string and will have the option of making that a bookmark referring to the current location I'm working in, or jump to it if it's an existing bookmark, or make a buffer with that name, or find a file with that name. #+begin_src emacs-lisp (setq helm-mini-default-sources '(helm-source-buffers-list helm-source-recentf helm-source-bookmarks helm-source-bookmark-set helm-source-buffer-not-found)) #+end_src

Incidentally, Helm even provides an [[http://tuhdo.github.io/helm-intro.html#orgheadline24][interface]] for the top program via helm-top. It also serves as an interface to popular search engines and over 100 websites such as google, stackoverflow, ctan, and arxiv. #+begin_src emacs-lisp (system-packages-ensure "surfraw") ; ⇒ “M-x helm-surfraw” or “C-x c s” #+end_src If we want to perform a google search, with interactive suggestions, then invoke helm-google-suggest ---which can be acted for other serves, such as Wikipedia or Youtube by C-z. For more google specific options, there is the google-this package.

Let's switch to a powerful searching mechanism -- [[https://github.com/ShingoFukuyama/helm-swoop][helm-swoop]]. It allows us to not only search the current buffer but also the other buffers and to make live edits by pressing C-c C-e when a search buffer exists. Incidentally, executing C-s on a word, region, will search for that particular word, region; then make changes with C-c C-e and apply them by C-x C-s. #+begin_src emacs-lisp (use-package helm-swoop :bind (("C-s" . 'helm-swoop) ;; search current buffer ("C-M-s" . 'helm-multi-swoop-all) ;; Search all buffer ;; Go back to last position where ‘helm-swoop’ was called ("C-S-s" . 'helm-swoop-back-to-last-point) ;; swoop doesn't work with PDFs, use Emacs' default isearch instead. :map pdf-view-mode-map ("C-s" . isearch-forward)) :custom (helm-swoop-speed-or-color nil "Give up colour for speed.") (helm-swoop-split-with-multiple-windows nil "Do not split window inside the current window.")) #+end_src

  • C-u 𝓃 C-s does a search but showing 𝓃 contextual lines!
  • helm-multi-swoop-all, C-M-s, lets us grep files anywhere!

Finally, note that there is now a M-x helm-info command to show documentation, possibly with examples, of the packages installed. For example, M-x helm-info RET dash RET -parition RET to see how the parition function from the dash library works via examples ;-) ** Having a workspace manager in Emacs I've loved using XMonad as a window tiling manager. I've enjoyed the ability to segregate my tasks according to what ‘project’ I'm working on; such as research, marking, Emacs play, etc. With [[https://github.com/nex3/perspective-el][perspective]], I can do the same thing :-)

That is, I can have a million buffers, but only those that belong to a workspace will be visible when I'm switching between buffers, for example. ( The awesome-tab and centaur-tab, mentioned elsewhere here, can be used to achieve the same thing by ‘grouping buffers together’. )

#+begin_src emacs-lisp (use-package perspective :defer t :config ;; Activate it. (persp-mode) ;; In the modeline, tell me which workspace I'm in. (persp-turn-on-modestring)) #+end_src

All commands are prefixed by C-x x; main commands:

  • s, n/→, p/← :: ‘S’elect a workspace to go to or create it, or go to ‘n’ext one, or go to ‘p’revious one.
  • c :: Query a perspective to kill.
  • r :: Rename a perspective.
  • A :: Add buffer to current perspective & remove it from all others.

As always, since we've installed which-key, it suffices to press C-x x then look at the resulting menu 😃 ** Excellent PDF Viewer Let's install the [[https://github.com/politza/pdf-tools][pdf-tools]] library for viewing PDFs in Emacs. #+begin_src emacs-lisp (use-package pdf-tools :defer t ; :init (system-packages-ensure "pdf-tools") :custom (pdf-tools-handle-upgrades nil) (pdf-info-epdfinfo-program "/usr/local/bin/epdfinfo") :config (pdf-tools-install))

;; Now PDFs opened in Emacs are in pdfview-mode. #+end_src

Besides the expected PDF viewing utilities, such as search, annotation, and continuous scrolling; with a simple mouse right-click, we can even select a ‘midnight’ rendering mode which may be easier on the eyes. For more, see the brief [[https://www.dailymotion.com/video/x2bc1is][pdf-tools-tourdeforce]] demo.

** Who am I? ---Using Gnus for Gmail Let's set the following personal Emacs-wide variables ---to be used in other locations besides email. #+begin_src emacs-lisp (setq user-full-name "Musa Al-hassy" user-mail-address "[email protected]") #+end_src

For some fun, run this cute method. #+begin_src emacs-lisp :tangle no (animate-birthday-present user-full-name) #+end_src

By default, in Emacs, we may send mail: Write it in Emacs with C-x m, then press C-c C-c to have it sent via your OS's default mailing system ---mine appears to be Gmail via the browser. Or cancel sending mail with C-c C-k ---the same commands for org-capturing, discussed below (•̀ᴗ•́)و

To send and read email in Emacs we use [[https://en.wikipedia.org/wiki/Gnus][GNUS]], which, like GNU itself, is a recursive acronym: GNUS Network User Service.

  1. Execute, rather place in your init: #+begin_src emacs-lisp (setq message-send-mail-function 'smtpmail-send-it) #+end_src Revert to the default OS mailing method by setting this variable to mailclient-send-it.

  2. Follow only the [[https://www.emacswiki.org/emacs/GnusGmail#toc1][quickstart here]]; namely, make a file named /.gnus containing: #+begin_src emacs-lisp :tangle ~/.gnus ;; user-full-name and user-mail-address should be defined

    (setq gnus-select-method '(nnimap "gmail" (nnimap-address "imap.gmail.com") (nnimap-server-port "imaps") (nnimap-stream ssl)))

    (setq smtpmail-smtp-server "smtp.gmail.com" smtpmail-smtp-service 587 gnus-ignored-newsgroups "^to\.\|^[0-9. ]+\( \|$\)\|^["]"[#'()]") #+end_src

  3. Enable “2 step authentication” for Gmail following [[https://emacs.stackexchange.com/a/33309/10352][these]] instructions.

  4. You will then obtain a secret password, the x marks below, which you insert in a file named /.authinfo as follows ---using your email address. #+begin_src shell :tangle no machine imap.gmail.com login [email protected] password xxxxxxxxxxxxxxxx port imaps machine smtp.gmail.com login [email protected] password xxxxxxxxxxxxxxxx port 587 #+end_src

  5. In Emacs, M-x gnus to see what's there.

    Or compose mail with C-x m then send it with C-c C-c.

    • Press C-h m to learn more about message mode for mail composition; or read the [[https://www.gnus.org/manual/message.pdf][Message Manual]].

#+begin_src emacs-lisp ;; After startup, if Emacs is idle for 10 seconds, then start Gnus. ;; Gnus is slow upon startup since it fetches all mails upon startup. ;(run-with-idle-timer 10 nil #'gnus) #+end_src

Learn more by reading [[https://www.gnu.org/software/emacs/manual/html_mono/gnus.html#Top][The Gnus Newsreader Manual]]; also available within Emacs by C-h i m gnus (•̀ᴗ•́)و

  • Or look at the [[https://www.gnu.org/software/emacs/refcards/pdf/gnus-refcard.pdf][Gnus Reference Card]].
  • Or, less comprehensively, this [[https://github.com/redguardtoo/mastering-emacs-in-one-year-guide/blob/master/gnus-guide-en.org#subscribe-groups][outline]].

[[https://www.emacswiki.org/emacs/GnusTutorial][EmacsWiki]] has a less technical and more user friendly tutorial.

*** Prettifications

Let's add the icon  near my mail groups ^_^ #+begin_src emacs-lisp ;; Fancy icons for Emacs ;; Only do this once: (use-package all-the-icons :defer t) ; :config (all-the-icons-install-fonts 'install-without-asking)

;; Make mail look pretty (use-package all-the-icons-gnus :defer t :config (all-the-icons-gnus-setup))

;; While we're at it: Make dired, ‘dir’ectory ‘ed’itor, look pretty (use-package all-the-icons-dired :hook (dired-mode . all-the-icons-dired-mode)) #+end_src

Next, let's paste in some [[http://groups.google.com/group/gnu.emacs.gnus/browse_thread/thread/a673a74356e7141f][eye-candy for Gnus]]: #+begin_src emacs-lisp (setq gnus-sum-thread-tree-vertical "│" gnus-sum-thread-tree-leaf-with-other "├─► " gnus-sum-thread-tree-single-leaf "╰─► " gnus-summary-line-format (concat "%0{%U%R%z%}" "%3{│%}" "%1{%d%}" "%3{│%}" " " "%4{%-20,20f%}" " " "%3{│%}" " " "%1{%B%}" "%s\n")) #+end_src

*** Super Terse Tutorial ⟨ See the [[https://www.gnu.org/software/emacs/refcards/pdf/gnus-refcard.pdf][GNUS Reference Card]]! ⟩

In gnus, by default items you've looked at disappear ---i.e., are archived. They can still be viewed in, say, your online browser if you like. In the Group view, R resets gnus, possibly retriving mail or alterations from other mail clients. q exits gnus in Group mode, q exits the particular view to go back to summary mode. Only after pressing q from within a group do changes take effect on articles ---such as moves, reads, deletes, etc.

  • Expected keys: RET enter/open an item, q quit and return to previous view, g refresh view ---i.e., ‘g’et new articles.

  • =RET=: Enter a group by pressing, well, the enter key.

    • Use SPC to open a group and automatically one first article there.
    • Use C-u RET to see all mail in a folder instead of just unread mail.
  • Only groups/folders with unread mail will be shown, use L/l to toggle between listing all groups.

  • SPC, DEL to scroll forward and backward; or C-v, M-v as always.

  • =G G=: Search mail at server side in the group buffer.

    • Limit search to particular folders/groups by marking them with #, or unmarking them with M-#.
  • / /,a: Filter mail according to subject or author; there are many other options, see [[https://www.gnu.org/software/emacs/manual/html_mono/gnus.html#Limiting][§3.8 Limiting]].

  • =d=: Mark an article as done, i.e., read it and it can be archived.

  • =!=: Mark an article as read, but to be kept around ---e.g., you have not replied to it, or it requires more reading at a later time.

    This lets us read mail offline; cached mail is found at =~/News/cache/=.

    #+begin_src emacs-lisp :tangle "~/.gnus" (setq gnus-use-cache 'use-as-much-cache-as-possible) #+end_src

  • =B m=: Move an article, in its current state, to another group ---i.e., ‘label’ using Gmail parlance.

    • Something to consider doing when finished with an article.

    To delete an article, simply move it to ‘trash’ ---of course this will delete it in other mail clients as well. There is no return from trash.

    Emails can always be archieved ---never delete, maybe?

    Anyhow, B m Trash is too verbose, let's just use t for “trash”: #+begin_src emacs-lisp (with-eval-after-load 'gnus (bind-key "t" (lambda (N) (interactive "P") (gnus-summary-move-article N "[Gmail]/Trash")) gnus-summary-mode-map))

    ;; Orginally: t ⇒ gnus-summary-toggle-header #+end_src

    • Select and deselect many articles before moving them by pressing # and M-#, respectively, anywhere on the entry.

    • As usual, you can mark a region, =C-SPC=, then move all entries therein.

  • =R, r=: Reply with sender's quoted text in place, or without but still visible in an adjacent buffer.

    • Likewise S W or S w to reply all, ‘wide reply’, with or without quoted text.
    • C-c C-z Delete everything from current position till the end.
    • C-c C-e Replace selected region with ‘[...]’; when omitting parts of quoted text.
  • Press m to compose mail; or C-x m from anywhere in Emacs to do so.

    • C-c C-c to send the mail.
    • S D e to resend an article as new mail: Alter body, subject, etc, before
    • C-c C-f to forward mail. sending.
  • C-c C-a to attach a file; it'll be embedded in the mail body as plaintext.

    • Press o on an attachment to save it locally.

*** Capturing Mail as Todo/Notes Sometime mail contains useful reference material or may be a self-contained task. Rather than using our inbox as a todo-list, we can copy the content of the mail and store it away in our todos/notes files. [[#Capturing-ideas-notes-without-interrupting-the-current-workflow][Capturing]], below, is a way to, well, capture ideas and notes /without/ interrupting the current workflow. Below, in the section on capturing, we define my/org-capture-buffer which quickly captures the contents of the current buffer as notes to store away. We use that method in the article view of mail so that c captures mail content with the option to provide additional remarks, and C to silently do so without additional remarks.

#+begin_src emacs-lisp (with-eval-after-load 'gnus

(bind-key "c" #'my/org-capture-buffer gnus-article-mode-map) ;; Orginally: c ⇒ gnus-summary-catchup-and-exit

(bind-key "C" (lambda (&optional keys) (interactive "P") (my/org-capture-buffer keys 'no-additional-remarks)) gnus-article-mode-map)) ;; Orginally: C ⇒ gnus-summary-cancel-article #+end_src

Gnus’ default =c= only enables a bad habit: Subscribing to stuff that you don't read, since you can mark all entries as read with one key. We now replace it with a ‘c’apturing mechanism that captures the current message as a todo or note for further processing. Likewise, the default =C= is to cancel posting an article; we replace it to be a silent capture: Squirrel away informative mail content without adding additional remarks.

*** Auto-completing mail addresses In order to get going quickly, using [[https://github.com/redguardtoo/gmail2bbdb][gmail2bbdb]], let's convert our Gmail contacts into a BBDB file ---the [[http://bbdb.sourceforge.net/][Insidious Big Brother Database]] is an address-book application that we'll use for E-mail; if you want to use it as a address-book application to keep track of contacts, notes, their organisation, etc, then consider additionally installing [[https://github.com/emacs-helm/helm-bbdb][helm-bbdb]] which gives a nice menu interface.

  • From the [[https://www.google.com/contacts][Gmail Contacts page]], obtain a =contacts.vcf= file by clicking “More -> Export -> vCard format -> Export”.
  • Run command =M-x gmail2bbdb-import-file= and select =contacts.vcf=; a bbdb file will be created in my Dropbox folder.
  • Press C-x m then begin typing a contact's name and you'll be queried about setting up BBDB, say yes.

#+begin_src emacs-lisp (use-package gmail2bbdb :defer t :custom (gmail2bbdb-bbdb-file "~/Dropbox/bbdb"))

(use-package bbdb :after company ;; The “com”plete “any”thig mode is set below in §Prose :hook (message-mode . bbdb-insinuate-gnus) (gnus-startup-hook . bbdb-insinuate-gnus) :custom (bbdb-file gmail2bbdb-bbdb-file) (bbdb-use-pop-up t) ;; allow popups for addresses :config (add-to-list 'company-backends 'company-bbdb)) #+end_src

Here is an [[http://emacs-fu.blogspot.com/2009/08/managing-e-mail-addresses-with-bbdb.html][emacs-fu]] article on managing e-mail addressed with bbdb.

*** Feeds to Blogs :Disabled: One can easily subscribe to an RSS feed in Gnus: Just press G R in the group buffer view, then follow the prompts. However, doing so programmatically is much harder. Below is my heartfelt attempt at doing so ---if you want a feed reader in Emacs that “just works”, then [[https://github.com/skeeto/elfeed][elfeed]] is the way to go. When all is said and done, the code below had me reading Gnus implementations and led me to conclude that /Gnus has a great key-based interface but a /poor programming interface/ ---or maybe I need to actually read the manual instead of frantically consulting source code.

My homemade hack to getting tagged feeds programmatically into Gnus. #+begin_src emacs-lisp :tangle no ;; Always show Gnus items organised by topic. (add-hook 'gnus-group-mode-hook 'gnus-topic-mode)

;; From Group view, press ^, then SPC on Gwene, then look for the site you want to follow. ;; If it's not there, add it via the web interface http://gwene.org/ (add-to-list 'gnus-secondary-select-methods '(nntp "news.gwene.org")) ;; ;; E.g., http://nullprogram.com/feed/ uses an Atom feed which Gnus does not ;; support natively. But it can be found on Gwene.

(setq my/gnus-feeds ;; topic title url '(Emacs "C‘est La 𝒵" https://cestlaz.github.io/rss.xml Emacs "Marcin Borkowski's Blog" http://mbork.pl?action=rss Emacs "Howardism" http://www.howardism.org/rss.xml Islam "Shia Islam Blogspot" http://welcometoshiaislam.blogspot.com/feeds/posts/default?alt=rss Cats "Hedonistic Learning" http://www.hedonisticlearning.com/rss.xml Cats "Functorial Blog" https://blog.functorial.com/feed.rss Programming "Joel on Software" http://www.joelonsoftware.com/rss.xml Haskell "Lysxia's Blog" https://blog.poisson.chat/rss.xml))

;; If fubared, then: ;; (ignore-errors (f-delete "/News/" 'force) (f-delete "/.newsrc.eld"))

;; Execute this after a Gnus buffer has been opened. (progn (use-package with-simulated-input) (cl-loop for (topic title url) in (-partition 3 my/gnus-feeds) ;; url & topic are symbols, make them strings. for url′ = (symbol-name url) for topic′ = (symbol-name topic) ;; Avoid spacing issues by using a Unicode ghost space “ â€. for title′ = (gnus-newsgroup-savable-name (s-replace " " " " title)) for input = (format "C-SPC C-a %s RET RET" title′) do ; cl-letf* (((symbol-function 'insert) (lambda (x) nil))) ;; see the (undo) below. ;; Add the group (with-simulated-input input (gnus-group-make-rss-group url′)) ;; Ensure it lives in the right topic category. (if (equal 'no-such-topic (alist-get topic gnus-topic-alist 'no-such-topic nil #'string=)) (push (list topic′ title′) gnus-topic-alist) ;; make topic if it doesnt exist (setf (alist-get topic′ gnus-topic-alist 'no-such-topic nil #'string=) (cons title′ (alist-get topic gnus-topic-alist 'no-such-topic nil #'string=))))) ;; Acknowledgement (message "Now switch into the GNUS group buffer, and refresh the topics; i.e., t t."))

    ;; The previous command performs an insert, since it's intended to be interactively
    ;; used; let's undo the insert.
    ; (undo-only)

;; (setq gnus-permanently-visible-groups ".*") ;; ;; Show topic alphabetically? The topics list is rendered in reverse order. ;; (reverse (cl-sort gnus-topic-alist 'string-lessp :key 'car)) #+end_src

Ironically, I've decide that “no, I do not want to see my blogs in Emacs” for the same reasons I do not activelly use M-x eww to browse the web in Emacs: I like seeing the colours, fonts, and math symbols that the authours have labored over to producing quality content. Apparently, I'm shallow and I'm okay with it ---but not that shallow, since I'm constantly pushing Emacs which looks ugly by default but it's unreasonably powerful. ** Jumping to extreme semantic units [[https://github.com/DamienCassou/beginend][Sometimes it's unreasonable]] for M-< to take us to the actual start of a buffer; instead it'd be preferable to go to the first “semantic unit” in the buffer. For example, when directory editing with dired we should jump to the first file, with version control with magit we should jump to the first section, when composing mail we should jump to the first body line, and in the agenda we should jump to the first entry. #+begin_src emacs-lisp ;; M-< and M-> jump to first and final semantic units. ;; If pressed twice, they go to physical first and last positions. (use-package beginend :diminish 'beginend-global-mode :config (beginend-global-mode) (cl-loop for (_ . m) in beginend-modes do (diminish m))) #+end_src

** Quickly pop-up a terminal, run a command, close it ---and zsh /Pop up a terminal, do some work, then close it using the same command./

[[https://github.com/kyagi/shell-pop-el][Shell-pop]] uses only one key action to work: If the buffer exists, and we're in it, then hide it; else jump to it; otherwise create it if it doesn't exit. Use universal arguments, e.g., C-u 5 C-t, to have multiple shells and the same universal arguments to pop those shells up, but C-t to pop them away.

#+begin_src emacs-lisp (use-package shell-pop :defer t :custom ;; This binding toggles popping up a shell, or moving cursour to the shell pop-up. (shell-pop-universal-key "C-t")

  ;; Percentage for shell-buffer window size.
  (shell-pop-window-size 30)

  ;; Position of the popped buffer: top, bottom, left, right, full.
  (shell-pop-window-position "bottom")

  ;; Please use an awesome shell.
  (shell-pop-term-shell "/bin/zsh"))

#+end_src

Now that we have access to quick pop-up for a shell, let's get a pretty and practical shell: [[https://www.howtogeek.com/362409/what-is-zsh-and-why-should-you-use-it-instead-of-bash/][zsh]] along with the [[https://ohmyz.sh/][Oh My Zsh]] community configurations give us:

  1. brew install zsh

  2. sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"

    This installs everything ^_^

#+begin_src emacs-lisp ;; Be default, Emacs please use zsh ;; E.g., M-x shell (setq shell-file-name "/bin/zsh") #+end_src

Out of the box, zsh comes with

  • git support; the left side indicates which branch we're on and whether the repo is dirty, ✗.
  • Recursive path expansion; e.g., /u/lo/b TAB expands to /usr/local/bin/
  • Over [[https://github.com/ohmyzsh/ohmyzsh/wiki/Plugins#apache2-macports][250+ Plugins]] and [[https://github.com/ohmyzsh/ohmyzsh/wiki/Themes][125+ Themes]] that are enabled by simply mentioning their name in the .zshrc file.

The defaults have been good enough for me, for now ---as all else is achieved via Emacs ;-)

Also, there's [[https://tldr.sh/][tldr]] tool which aims to be like terse manuals for commandline-tools in the style of practical example uses cases: tldr 𝒳 yields a number of ways you'd actually use 𝒳. #+begin_src emacs-lisp :tangle no (system-packages-ensure "tldr") #+end_src

** Restarting Emacs ---Keeping buffers open across sessions? Sometimes I wish to close then reopen Emacs; unsurprisingly someone's thought of implementing that. #+begin_src emacs-lisp ;; Provides only the command “restart-emacs”. (use-package restart-emacs ;; If I ever close Emacs, it's likely because I want to restart it. :bind ("C-x C-c" . restart-emacs) ;; Let's define an alias so there's no need to remember the order. :config (defalias 'emacs-restart #'restart-emacs)) #+end_src

The following is disabled. I found it a nuisance to have my files open across sessions ---If I'm closing Emacs, it's for a good reason. #+begin_example emacs-lisp ;; Keep open files open across sessions. (desktop-save-mode 1) (setq desktop-restore-eager 10) #+end_example

Instead, let's try the following: When you visit a file, point goes to the last place where it was when you previously visited the same file. #+begin_src emacs-lisp (setq-default save-place t) (setq save-place-file "~/.emacs.d/etc/saveplace") #+end_src

** Automatic Backups By default, Emacs saves backup files ---those ending in =~=--- in the current directory, thereby cluttering it up. Let's place them in /.emacs.d/backups, in case we need to look for a backup; moreover, let's keep old versions since there's disk space to go around ---what am I going to do with 500gigs when nearly all my ‘software’ is textfiles interpreted within Emacs 😼

#+begin_src emacs-lisp ;; New location for backups. (setq backup-directory-alist '(("." . "~/.emacs.d/backups")))

;; Silently delete execess backup versions (setq delete-old-versions t)

;; Only keep the last 1000 backups of a file. (setq kept-old-versions 1000)

;; Even version controlled files get to be backed up. (setq vc-make-backup-files t)

;; Use version numbers for backup files. (setq version-control t) #+end_src

Why backups? Sometimes I may forget to submit a file, or edit, to my version control system, and it'd be nice to be able to see a local automatic backup. Whenever ‘I need space,’ then I simply empty the backup directory, if ever. That the backups are numbered is so sweet ^_^

Like package installations, my backups are not kept in any version control system, like git; only locally.

Let's use an elementary diff system for backups. #+begin_src emacs-lisp (use-package backup-walker :commands backup-walker-start) #+end_src

In a buffer that corresponds to a file, invoke backup-walker-start to see a visual diff of changes /between/ versions. By default, you see the changes ‘backwards’: Red means delete these things to get to the older version; i.e., the red ‘-’ are newer items.

Emacs only makes a backup the very first time a buffer is saved; I'd prefer Emacs makes backups everytime I save! ---If I saved, that means I'm at an important checkpoint, so please check what I have so far as a backup! #+begin_src emacs-lisp ;; Make Emacs backup everytime I save

(defun my/force-backup-of-buffer () "Lie to Emacs, telling it the curent buffer has yet to be backed up." (setq buffer-backed-up nil))

(add-hook 'before-save-hook 'my/force-backup-of-buffer) #+end_src

It is intestesting to note that the above snippet could be modified to [[https://stackoverflow.com/a/6918217/3550444][make our own backup system]], were Emacs lacked one, by having our function simply save copies of our file ---on each save--- where the filename is augmented with a timestamp.

  • =diff-backup= compares a file with its backup or vice versa.

** Screencapturing the Current Emacs Frame

Sometimes an image can be tremendously convincing, or at least sufficiently inviting. The following incantation is written for MacOS and uses it's native =screencapture= utility, as well as =magick=. #+begin_src emacs-lisp (defun my/capture-emacs-frame (&optional prefix output) "Insert a link to a screenshot of the current Emacs frame.

Unless the name of the OUTPUT file is provided, read it from the user. If PREFIX is provided, let the user select a portion of the screen." (interactive "p") (defvar my/emacs-window-id (s-collapse-whitespace (shell-command-to-string "osascript -e 'tell app "Emacs" to id of window 1'")) "The window ID of the current Emacs frame.

  Takes a second to compute, whence a defvar.")

(let* ((screen (if prefix "-i" (concat "-l" my/emacs-window-id))) (temp (format "emacs_temp_%s.png" (random))) (default (format-time-string "emacs-%m-%d-%Y-%H:%M:%S.png"))) ;; Get output file name (unless output (setq output (read-string (format "Emacs screenshot filename (%s): " default))) (when (s-blank-p output) (setq output default))) ;; Clear minibuffer before capturing screen or prompt user (message (if prefix "Please select region for capture …" "♥‿♥")) ;; Capture current screen and resize (thread-first (format "screencapture -T 2 %s %s" screen temp) (concat "; magick convert -resize 60% " temp " " output) (shell-command)) (f-delete temp) ;; Insert a link to the image and reload inline images. (insert (concat "[[file:" output "]]"))) (org-display-inline-images nil t))

(bind-key* "C-c M-s" #'my/capture-emacs-frame) #+end_src

Why this way? On MacOS, ImageMagick's =import= doesn't seem to work ---not at all for me! Also, I dislike how large the resulting image is. As such, I'm using MacOS's =screencapture= utility, which in-turn requires me to somehow obtain frame IDs. Hence, the amount of work needed to make this happen on my system was most simple if I just wrote it out myself rather than tweaking an existing system.

  • C-c C-x C-v ⇒ Toggle inline images!

** Editor Documentation with Contextual Information /Emacs is an extensible self-documenting editor!/

Let's use a helpful Emacs /documentation/ system that cleanly shows a lot of contextual information ---then let's /extend/ that to work as we want it to: C-h o to describe the symbol at point. #+begin_src emacs-lisp (use-package helpful :defer t)

(defun my/describe-symbol (symbol) "A “C-h o” replacement using “helpful”: If there's a thing at point, offer that as default search item.

 If a prefix is provided, i.e., “C-u C-h o” then the built-in
 “describe-symbol” command is used.

 ⇨ Pretty docstrings, with links and highlighting.
 ⇨ Source code of symbol.
 ⇨ Callers of function symbol.
 ⇨ Key bindings for function symbol.
 ⇨ Aliases.
 ⇨ Options to enable tracing, dissable, and forget/unbind the symbol!
"
(interactive "p")
(let* ((thing (symbol-at-point))
       (val (completing-read
             (format "Describe symbol (default %s): " thing)
             (vconcat (list thing) obarray)
             (lambda (vv)
               (cl-some (lambda (x) (funcall (nth 1 x) vv))
                        describe-symbol-backends))
             t nil nil))
       (it (intern val)))
  (cond
   (current-prefix-arg (funcall #'describe-symbol it))
   ((or (functionp it) (macrop it) (commandp it)) (helpful-callable it))
   (t (helpful-symbol it)))))

;; Keybindings. (global-set-key (kbd "C-h o") #'my/describe-symbol) (global-set-key (kbd "C-h k") #'helpful-key) #+end_src

I like [[https://github.com/Wilfred/helpful][helpful]] and wanted it to have the same behaviour as C-h o, which helpful-at-point does not achieve. The incantation above makes C-h o use helpful in that if the cursor is on a symbol, then it is offered to the user as a default search item for help, otherwise a plain search box for help appears. Using a universal argument lets us drop to the built-in help command.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].