# السّلام عليكم ─“Hello, and welcome” in Arabic (العربيّة) # # Over 13k line org file, with over 300 source code blocks. #+title: A Life Configuring Emacs #+author: Musa Al-hassy #+email: alhassy@gmail.com #+date: 2018-07-25 #+description: My Emacs Initialisation File, Written in Org-mode. #+startup: indent lognoteclock-out #+options: html-postamble:nil toc:2 d:nil num:t broken-links:auto # +property: header-args :tangle deferred-init.el :comments link :results none #+property: header-args :tangle init.el :comments link :results none #+export_file_name: index * Abstract :ignore: :PROPERTIES: :CUSTOM_ID: Abstract :END: #+begin_center badge:A_Life|Configuring_Emacs|success|https://github.com/alhassy/emacs.d|gnu-emacs badge:A_Life_Configuring|Emacs|success|https://alhassy.github.io/emacs.d/|gnu badge:Currently_Reorganising|Not_Polished|yellow #+end_center #+html:

#+begin_center text *Abstract* #+end_center #+html:

[[doc:Hello][Hello!]] Herein I document the configurations I utilise with [[https://gnu.org/s/emacs][Emacs]]. # After cloning the file, many packages are automatically installed; usually with # little or no trouble. 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 kbd:C-h_o with the cursor on ~X~. An elementary Elisp Cheat Sheet can be found at [[badge:Elisp|CheatSheet|success|https://alhassy.github.io/ElispCheatSheet/CheatSheet.pdf|Gnu-Emacs][Elisp cheat sheet]] and badge:Lifemacs|CheatSheet|informational|https://alhassy.github.io/emacs.d/CheatSheet.pdf|Gnu-Emacs is a 2-page 3-column PDF of the bindings in /this/ configuration. + kbd:C-h_e ⇒ *What'd /Emacs/ do?* + kbd:C-h_o ⇒ *What's this thing?* + kbd:C-h_l ⇒ *What'd /I/ do?* + [[kbd: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!?” ⇒ kbd:C-h_e and kbd:C-h_l, then use kbd:C-h_o to get more details on the action. ;-) Finally, kbd: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. #+begin_center badge:Emacs|27|green|https://www.gnu.org/software/emacs|gnu-emacs badge:Org|9.4|blue|https://orgmode.org|gnu [[badge:license|GNU_3|informational|https://www.gnu.org/licenses/gpl-3.0.en.html|read-the-docs][gnu 3 license badge]] tweet:https://github.com/alhassy/org-special-block-extras badge:author|musa_al-hassy|purple|https://alhassy.github.io/|nintendo-3ds badge:|buy_me_a coffee|gray|https://www.buymeacoffee.com/alhassy|buy-me-a-coffee # badge:Hire|me|success|https://alhassy.github.io/about #+end_center This article is about /how I like/ to do things ---/I'm not insisting others should/ do things this way. #+begin_box /Always remember that to argue, and win, is to break down the reality of the person you are arguing against. It is painful to lose your reality, so be kind,/ /even if you are right./ - Haruki Murakami /Life is too short to not read the very best book you know of right now./ - Patrick Collison /Inspiration is for amateurs. The rest of us just show up and get to work./ - Chuck Close #+end_box * Meta-setup :ignore: :PROPERTIES: :CUSTOM_ID: Meta-setup :END: ** Blog/HTML Setup :ignore: :PROPERTIES: :CUSTOM_ID: Blog-HTML-Setup :END: # ─AlBasmala keywords─ # DRAFT: yes #+SOURCEFILE: https://github.com/alhassy/emacs.d/blob/master/init.org #+IMAGE: ../assets/img/emacs_logo.png #+CATEGORIES: Emacs Lisp #+html_head: #+html_head: #+html_head: # The last one has the styling for lists. ** Github Actions :noexport: :PROPERTIES: :CUSTOM_ID: Github-Actions :END: The following creates the “Github Actions Workflow” file; this way, Github will run your tests every time you commit ^_^ Below I'm using =main= as the /name/ of the main branch; if you use =master= as the name, then change that or otherwise the tests will not trigger automatically after push! #+begin_src shell :tangle .github/workflows/main.yml :mkdirp yes # This workflow will do a clean install of dependencies and run tests # For more information see: https://help.github.com/actions/language-and-framework-guides/ name: Lifemacs Loads Successfully # Controls when the action will run. on: # Triggers the workflow on push or pull request events but only for the main branch push: branches: [ master ] pull_request: branches: [ master ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: # This workflow contains a single job called "build" build: # The type of runner that the job will run on runs-on: ubuntu-latest # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v2 - name: Set up Emacs uses: purcell/setup-emacs@v3.0 with: # The version of Emacs to install, e.g. "24.3", or "snapshot" for a recent development version. version: 27.1 # optional # Runs a single command using the runners shell # - name: Run a one-line script # run: echo Hello, world! # Runs a set of commands using the runners shell # - name: Run a multi-line script # run: | # echo Add other actions to build, # echo test, and deploy your project. - name: where am I and what is here run: | pwd ls - name: Attempting startup & basic test ... run: time emacs -nw --batch --eval='(let ( (user-emacs-directory default-directory)) (message "Default directory" ) (message default-directory) (setq url-show-status nil) (package-initialize) (load-file "init.el") (message "\n 🤤 Startup Successful! 🤩") (when nil "It seems this results in a non-existent Agenda issue when run by GHA; ignoring for now." (message "\nCheck we can do something basic, like getting pretty HTML from an Org file\n") (find-file "init.org") (org-html-export-to-html) (message "\n 🤤 HTML file generated successfully! 🤩\n")))' #+end_src TODO: Build HTML export as a minimal test that things work as expected. (find-file "init.org") (org-html-export-to-html) (message "\n 🤤 HTML Export Successful! 🤩") (unless noninteractive ...) ;; Use this to omit stuff from the Github Actions test ** Personal instructions for a new machine :ignore: :PROPERTIES: :CUSTOM_ID: Personal-instructions-for-a-new-machine :END: #+begin_details "“Personal instructions for a new machine”" These steps must be performed at the terminal /since/ they are required to get /my/ Emacs, which then installs everything else /when it's first opened/. 1. Install a package manager: https://brew.sh/ : #+begin_src shell :tangle no /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" #+end_src Also: Change to the conventional scrolling direction: /If I pull my scroll down, I want to go down./ - Apple menu → System Preferences → Mouse → Tick the scroll direction option. 2. [[https://www.emacswiki.org/emacs/EmacsForMacOS#h5o-14][Use brew to get Emacs]]: # #+begin_src shell :tangle no # brew install --cask emacs # #+end_src # #+begin_src shell :tangle no # https://github.com/d12frosted/homebrew-emacs-plus $ brew tap d12frosted/emacs-plus $ brew install emacs-plus@30 --with-xwidgets --with-imagemagick #+end_src # $ /usr/local/Cellar/emacs-plus@29/29.0.50/bin/emacs-29.0.50 & # # In ~/.bashrc, or ~/.zshrc, put the following at the end: # alias emacs="/opt/homebrew/Cellar/emacs-plus@29/29.0.90/bin/emacs-29.0.90" # # # If that fails, try this to [[https://github.com/daviderestivo/homebrew-emacs-head#gnu-emacs-27-bottle-or-head][install Emacs:]] # #+BEGIN_SRC shell :tangle no # brew tap daviderestivo/emacs-head # brew install emacs-head # #+END_SRC # 3. [[https://emacs.stackexchange.com/a/50405/10352][Then]]    [[https://www.emacswiki.org/emacs/EmacsForMacOS#h5o-14][make]]   the command ~emacs~ available via the terminal ---required if doing any melpa development. #+begin_src shell :tangle no ln -s /opt/homebrew/opt/emacs-plus@30/Emacs.app /Applications #+end_src # sudo ln -s /usr/local/opt/emacs-head@27/Emacs.app/Contents/MacOS/Emacs /usr/local/bin/emacs 4. Why ~--with-imagemagick~? ;; This lets us change the size of images when shown in Org-mode. #+begin_src emacs-lisp (setq org-image-actual-width nil) #+end_src #+begin_org-demo #+ATTR_HTML: :alt musa in a pink shirt :title The author of this article :align center #+ATTR_HTML: :width 50% :height 50% [[~/blog/images/musa_pink.jpg]] #+end_org-demo Note that only the =:width= option is used for in-Org image preview. 5. Why =--with-xwidgets=? We get a full-fledged Internet browser /within/ Emacs. #+begin_src emacs-lisp ;; Clicking on a URL, or running M-x browse-url, should open the URL *within* Emacs. (setq browse-url-browser-function #'xwidget-webkit-browse-url) ;; (use-package xwwp) ;; Enhance the Emacs xwidget-webkit browser #+end_src Related: doc:goto-address-mode is useful whenever you have a buffer full of "http..." URLs (e.g.., a JSON file): It makes them into clickable buttons or via =C-c RET=. #+begin_details "How about EAF or nyxt?" An alternative to xwidget-webkit is EAF, sadly this does not work well with MacOS. EAF essentially makes Emacs a window manager that runs other GUI apps ---as such, EAF buffers are not classic Emacs buffers (and so your favourite text commands are useless). There is also: | nyxt  ≅  the web running common lisp instead of JS  ≅  an Emacs backed-by common lisp | #+end_details #+begin_details "Tell me more about xwidget-webkit" I’ve found that the only two applications I regularly have open are Emacs and a browser ---and Emacs has a modern browser, so might as well use that in Emacs. - Downsides of Emacs as a browser: Some webpages, such as Slack, try to be an editor and so I'm using a Slack editor insider a web browser inside an text editor (Emacs). As such, sometimes the lines between editor and browser need to be explicitly demarcated; e.g., via doc:xwidget-webkit-edit-mode. - *“xwidget ≈ eXternal widget”* lets us embed GTK widgets inside an Emacs window; e.g., we can insert fancy buttons via doc:xwidget-insert, and a browser using doc:xwidget-webkit-browse-url. - For history and info on xwidget, see [[https://github.com/jave/xwidget-emacs][the original patch]]; See also: https://webkitgtk.org/ #+end_details 6. Install git: =brew install git= 7. Get my Emacs setup: =rm -rf ~/.emacs.d; git clone https://github.com/alhassy/emacs.d.git ~/.emacs.d= Open Emacs and watch download and set up many other things ... ^_^ *This may take ~15 minutes ---we install a massive LaTeX setup.* We get: Spell checker, dictionary, LaTeX + pygements, Dropbox, AG (for fast system-wide searching of a string with doc:helm-do-grep-ag, useful for finding definitions), Amethyst window manager. Amethyst requires some more setup: Open its preferences, then... - Then select: =Mouse: Focus follows mouse=. - Also: =Shortcuts=, then disable ‘increase/decrease main pane count’ bindings since they override the beloved Emacs =M-<,>= keys. For convenience, on a Mac, add the home (=~/=) directory to the default file navigator: Finder → Preferences → Sidebar, then select home 🏠. If you notice any “file system access” concerns, give Emacs permissions to read your files: General Settings → Security & Privacy → Full Disk Access → ~⌘-M-g~ (to search) then enter =/usr/bin/ruby= ---Emacs is launched via a Ruby script in MacOS. -------------------------------------------------------------------------------- /See the Prose/Unicode section, we need to manually install the Symbol font for subscripts./ #+end_details * Why Emacs? :PROPERTIES: :CUSTOM_ID: Why-Emacs :header-args: :tangle init.el :END: 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]] ---which is /Emacs' killer app/. ** Mini-tutorial on Org-mode :ignore: :PROPERTIES: :CUSTOM_ID: Mini-tutorial-on-Org-mode :header-args: :tangle no :END: # To include this mini-tutorial elsewhere: # #+include: ~/.emacs.d/init.org::#Mini-tutorial-on-Org-mode #+begin_details Super Simple Intro to Emacs’ Org-mode link-here:Super-Simple-Intro-to-Emacs-Org-mode Emacs’ Org-mode is an outliner, a rich markup language, spreadsheet tool, literate programming system, and so much more. It is an impressive reason to use Emacs (•̀ᴗ•́)و # badge:Emacs|27|green|https://www.gnu.org/software/emacs|gnu-emacs # badge:Org|9.4|blue|https://orgmode.org|gnu Org-mode syntax is very /natural/; e.g., the following is Org-mode! ( [[https://karl-voit.at/2017/09/23/orgmode-as-markup-only/][Org Mode Is One of the Most Reasonable Markup Languages to Use for Text]] ) #+begin_src org :noeval + Numbered and bulleted lists are as expected. - Do the things: 1. This first 2. This second 44. [@44] This forty-fourth - =[@𝓃]= at the beginning of an iterm forces list numbering to start at 𝓃 - =[ ]= or =[X]= at the beginning for checkbox lists - Use =Alt ↑, ↓= to move items up and down lists; renumbering happens automatically. + Definitions lists: ~- term :: def~ + Use a comment, such as =# separator=, between two lists to communicate that these are two lists that /happen/ to be one after the other. Or use any /non-indented/ text to split a list into two. ,* My top heading, section words ,** Child heading, subsection more words ,*** Grandchild heading, subsubsection even more! #+END_SRC *Export* In Emacs, press kbd:C-c_C-e_h_o to obtain an HTML webpage ---/like this one!/--- of the Org-mode markup; use kbd:C-c_C-e_l_o to obtain a PDF rendition. You can try Org-mode notation and see how it renders live at: http://mooz.github.io/org-js/ -------------------------------------------------------------------------------- You make a heading by writing =* heading= at the start of a line, then you can kbd:TAB to fold/unfold its contents. A table of contents, figures, tables can be requested as follows: #+BEGIN_SRC org # figures not implemented in the HTML backend # The 𝓃 is optional and denotes headline depth ,#+toc: headlines 𝓃 ,#+toc: figures ,#+toc: tables #+END_SRC -------------------------------------------------------------------------------- + *Markup* elements can be nested. | Syntax | Result | |------------------------------------+------------------| | ~/Emphasise/~, italics | /Emphasise/ | | ~*Strong*~, bold | *Strong* | | ~*/very strongly/*~, bold italics | */very strongly/* | | ~=verbatim=~, monospaced typewriter | =verbatim= | | ~+deleted+~ | +deleted+ | | ~_inserted_~ | _inserted_ | | ~super^{script}ed~ | super^{script}ed | | ~sub_{scripted}ed~ | sub_{scripted}ed | * Markup can span across multiple lines, by default no more than 2. * In general, markup cannot be ‘in the middle’ of a word. + New lines demarcate paragraphs + Use =\\= to force line breaks without starting a new paragraph + Use /at least/ 5 dashes, =-----=, to form a horizontal rule badge:org-special-block-extras|2.0|informational|https://github.com/alhassy/org-special-block-extras|Gnu-Emacs provides support for numerous other kinds of markup elements, such as ~red:hello~ which becomes “ red:hello ”. -------------------------------------------------------------------------------- *Working with tables* #+BEGIN_SRC org #+ATTR_HTML: :width 100% #+name: my-tbl #+caption: Example table | Who? | What? | |------+-------| | me | Emacs | | you | Org | #+END_SRC Note the horizontal rule makes a header row and is formed by typing [[kbd:doit][| -]] then pressing kbd:TAB. You can kbd:TAB between cells. + You can make an empty table with ~C-c |~, which is just doc:org-table-create-or-convert-from-region, then give it row×column dimensions. + Any lines with comma-separated-values (CSV) can be turned into an Org table by selecting the region and pressing ~C-u C-c |~. (Any CSV file can thus be visualised nicely as an Org table). + Use ~C-u C-u C-u C-c |~ to make a table from values that are speared by a certain regular expression. -------------------------------------------------------------------------------- *Working with links* Link syntax is =[[source url][description]]=; e.g., we can refer to the above table with =[[my-tbl][woah]]=. Likewise for images: =file:path-to-image.= # The HTML has Up/Home on the right now ;-) # +HTML_LINK_HOME: http://www.google.com # +HTML_LINK_UP: http://www.bing.com -------------------------------------------------------------------------------- *Mathematics* #+BEGIN_org-demo \[ \sin^2 x + \cos^2 x = \int_\pi^{\pi + 1} 1 dx = {3 \over 3} \] #+END_org-demo - Instead of ~\[...\]~, which displays a formula on its own line, centred, use ~$...$~ to show a formula inline. - Captioned equations are numbered and can be referenced via links, as shown below. #+BEGIN_org-demo :source-color green :result-color green #+name: euler \begin{equation} e ^ {i \pi} + 1 = 0 \end{equation} See equation [[euler]]. #+END_org-demo -------------------------------------------------------------------------------- *Source code* #+begin_org-demo :source-color custard :result-color custard #+begin_src C -n int tot = 1; (ref:start) for (int i = 0; i != 10; i++) (ref:loop) tot *= i; (ref:next) printf("The factorial of 10 is %d", tot); #+end_src #+end_org-demo The labels =(ref:name)= refer to the lines in the source code and can be referenced with link syntax: ~[[(name)]]~. Hovering over the link, in the HTML export, will dynamically highlight the corresponding line of code. To strip-out the labels from the displayed block, use ~-r -n~ in the header so it becomes =#+begin_src C -r -n=, now the references become line numbers. -------------------------------------------------------------------------------- Another reason to use Org: If you use =:results raw=, you obtain *dynamic templates* that may use Org-markup: #+begin_org-demo :source-color brown :result-color brown #+BEGIN_SRC C :results raw replace printf("*bold* +%d+ (strikethrough) /slanted/", 12345); #+END_SRC ♯+RESULTS: *bold* +12345+ (strikethrough) /slanted/ #+end_org-demo The ~#+RESULTS:~ is obtained by pressing kbd:C-c_C-c on the ~src~ block, to execute it and obtain its result. Also: Notice that a C program can be run without a =main= ;-) That is, we can write code /in between/ prose that is intended to be read like an essay: # This should be a URL, so that any includes will show the PNG. # It does work locally too; but just in case... # [[file:images/literate-programming.png]] [[file:https://alhassy.github.io/emacs.d/images/literate-programming.png]] -------------------------------------------------------------------------------- + badge:Lifemacs|CheatSheet|informational|https://alhassy.github.io/emacs.d/CheatSheet.pdf|Gnu-Emacs ⇒ A brief reference of Emacs keybindings; 2 pages + [[badge:Elisp|CheatSheet|success|https://alhassy.github.io/ElispCheatSheet/CheatSheet.pdf|Gnu-Emacs][Elisp cheat sheet]] ⇒ A /compact/ Emacs Lisp Reference; 7 pages -------------------------------------------------------------------------------- *Single source of truth:* This mini-tutorial can be included into other Org files by declaring | ~#+include: ~/.emacs.d/init.org::#Mini-tutorial-on-Org-mode~ | -------------------------------------------------------------------------------- For more, see https://orgmode.org/features.html. #+end_details ** Intro to why Emacs :ignore: :PROPERTIES: :CUSTOM_ID: Intro-to-why-Emacs :END: /Emacs is a flexible platform for developing end-user applications/ ---unfortunately it is generally perceived as merely a text editor. Some people use it specifically for one or two applications. For example, [[https://www.youtube.com/watch?v=FtieBc3KptU][writers]] use it as an interface for Org-mode and others use it as an interface for version control with Magit. [[https://orgmode.org/index.html#sec-4][Org]] is an organisation tool that can be used for typesetting which subsumes LaTeX, generating many different formats --html, latex, pdf, etc-- from a single source, keeping track of [[https://orgmode.org/worg/org-tutorials/index.html#orgff7b885][schedules]] & task management, blogging, habit tracking, personal information management tool, and [[http://orgmode.org/worg/org-contrib/][much more]]. Moreover, its syntax is so [[https://karl-voit.at/2017/09/23/orgmode-as-markup-only/][natural]] that most people use it without even knowing! For me, Org allows me to do literate programming: I can program and document at the same time, with no need to seperate the two tasks and with the ability to generate multiple formats and files from a single file. #+begin_details A list of programs that can be replaced by Emacs /Pieces of (disparate) software can generally be replaced by (applications written on the) Emacs (text processing Lisp platform)./ From the table below, of non-editing things you can do with Emacs, it's reasonable to think of Emacs as an operating system ---and Vim/Evil is one of its text editors. # Examples: |----------------------------------------------+---+--------------------------------------------------------------------| | Application | ≈ | Emacs Package | |----------------------------------------------+---+--------------------------------------------------------------------| | Habit Tracker / TODO-list | | Org mode | | Agenda / Calendar / Time Tracker | | Org mode | | Literate Programming (like Jupyter) | | Org mode | | Blogging Software | | Org mode | | Reference Information Platform | | Org mode with [[https://orgmode.org/manual/Refile-and-Copy.html][refile]] and doc:my/reference | |----------------------------------------------+---+--------------------------------------------------------------------| | Word Processing / PDFs / Slidedeck tool | | Org mode | | Spell checker & dictionary & grammar checker | | doc:ispell & langtool | | Reference and citation manager | | org-ref | | PDF Viewer | | PDF View mode | | Powerful Calculator | | Calc-mode ([[https://hungyi.net/posts/solve-system-of-equations-literate-calc-mode/][Nice article on literate calc mode]]) | | Fillable Forms / Data Entry | | [[https://www.gnu.org/software/emacs/manual/html_mono/widget.html][Widgets]] | | Ebook Reader | | [[https://depp.brause.cc/nov.el/][nov.el]] and [[https://github.com/chenyanming/calibredb.el][calibredb.el]] | |----------------------------------------------+---+--------------------------------------------------------------------| | Git / Version control | | Magit or doc:vc-mode | | Shells | | doc:eshell or doc:shell | | Terminal emulators | | doc:term, doc:ansi-term, and [[https://github.com/akermu/emacs-libvterm][vterm]] | | Package Manager | | doc:helm-system-packages | | File Manager | | doc:dired | | IDE / debugger | | LSP / Dap | | Scripting Language | | Emacs Lisp | | Web client / server | | [[https://github.com/pashky/restclient.el][Restclient]] / [[https://github.com/skeeto/emacs-web-server][emacs-web-server]] | |----------------------------------------------+---+--------------------------------------------------------------------| | [Neo]Vim / Modal text editor | | EVIL mode | | RSS Newsreader | | ElFeed | | Email | | Gnus / [[https://gist.github.com/rougier/009e7d13a816d053d8f319b56836e1c9?permalink_comment_id=3738945#gistcomment-3738945][Mu4e]] [very pretty!] / notmuch | | Spredsheet tool | | [[https://orgmode.org/manual/The-Spreadsheet.html][Org Table]] / [[https://www.reddit.com/r/emacs/comments/t8k1cw/simple_emacs_spreadsheet/][Simple Emacs Spreadsheet]] / spreadsheet-mode / csv-mode | | Automatic file backups | | ⟨Built-in⟩ & backup-walker | | seemless GPG tool | | ⟨Built-in⟩ | | Lisp interpreter | | Anywhere press kbd:C-x_C-e to run a Lisp expression | | Documentation viewer | | tldr-mode; kbd:C-h_o / doc:describe-symbol | | Diff / Merge tool | | doc:ediff, doc:diff | |----------------------------------------------+---+--------------------------------------------------------------------| | Games | | doc:tetris, pacman, mario, etc | | Psychologist | | doc:doctor | | Weather Web Service | | [[https://github.com/bcbcarl/emacs-wttrin][wttrin.el]] or [[https://github.com/aaronbieber/sunshine.el][sunshine.el]] | | Typing tutor | | typing-of-emacs | | Modern Internet Browser | | doc:xwidget-webkit-browse-url | | Street map viewer | | [[https://github.com/minad/osm][osm.el - OpenStreetMap viewer for Emacs]] | | everything else | | [[https://github.com/emacs-eaf/emacs-application-framework][EAF]] | |----------------------------------------------+---+--------------------------------------------------------------------| I’m down to essentially Emacs and Chrome for almost all my work ---I like using Chrome; I like the integration of all things Google. - The [[https://nyxt.atlas.engineer/][Nyxt browser]] is an eerily Emacs-like browser ;-) Were I “only coding”, then I'd use a popular Integrated Development Environment that requires minimal setup and Just Worksᵀᴹ; but I blog, make cheat sheets, run background services, etc, and so I need an /Integrated Computing Environment:/ Emacs. #+end_details #+begin_quote If you are a professional writer…Emacs outshines all other editing software in approximately the same way that the noonday sun does the stars. It is not just bigger and brighter; it simply makes everything else vanish. —[[http://project.cyberpunk.ru/lib/in_the_beginning_was_the_command_line/][Neal Stephenson]], /In the beginning was the command line/ #+end_quote + Extensible ⇒ IDEs are generally optimised for one framework, unlike Emacs! # Emacs is a live interpreter for ELisp. - You can /program/ Emacs to /automate/ anything you want. # Even arrow keys and characters can be customised, via self-insert-command! - Hence, it's an /environment/, not just an editor. - ⇒ Unified keybinding across all tools in your environment. Users are given a high-level full-featured programming language, not just a small configuration language. For the non-programmers, there is Custom, a friendly point-and-click customisation interface. # with support for a large portion of Common Lisp + Self Documented ⇒ Simply [[kbd:M-x info-apropos]] or kbd:C-h_d to search all manuals or look up any function provided by Emacs! + [[https://en.wikipedia.org/wiki/Emacs#History][Mature]] ⇒ tool with over 40 years of user created features - Plugins for nearly everything! - No distinction between built-ins and user-defined features! (Lisp!) - You can alter others' code [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Advising-Functions.html][without even touching the source]]. * Advising functions and ‘hooking’ functionality onto events. + [[https://www.gnu.org/philosophy/free-sw.html][Free software]] ⇒ It will never die! - Emacs is one of the oldest open source projects still under developement. # - Unlike other certain editors, Emacs' source is completely open. Of course Emacs comes with the basic features of a text editor, but it is much more; for example, it comes with a powerful notion of ‘undo’: Basic text editors have a single stream of undo, yet in Emacs, we have a /tree/ ---when we undo and make new edits, we branch off in our editing stream as if our text was being version controlled as we type! We can even switch between such branches! /That is, while other editors have a single-item clipboard, Emacs has an infinite clipboard that allows undoing to any historical state./ #+begin_src emacs-lisp :tangle no :noweb-ref undo-tree-setup ;; Allow tree-semantics for undo operations. (use-package undo-tree :defer nil :bind ("C-x u" . undo-tree-visualize) :hook (org-mode . undo-tree-mode) ;; For some reason, I need this. FIXME. :config ;; Always have it on (global-undo-tree-mode) ;; Each node in the undo tree should have a timestamp. (setq undo-tree-visualizer-timestamps t) ;; Show a diff window displaying changes between undo nodes. (setq undo-tree-visualizer-diff t) ;; Prevent undo tree files from polluting your git repo (setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo")))) ;; Execute (undo-tree-visualize) then navigate along the tree to witness ;; changes being made to your file live! #+end_src ( The above snippet has a ~noweb-ref~: It is presented here in a natural position, but is only executable once ~use-package~ is setup and so it is weaved there! We can /present/ code in any order and /tangle/ it to the order the compilers need it to be! ) /Emacs is an extensible editor: You can make it into the editor of your dreams!/ You can make it suited to your personal needs. If there's a feature you would like, a behaviour your desire, you can simply code that into Emacs with a bit of Lisp. As a programming language enthusiast, for me Emacs is my default Lisp interpreter and a customisable IDE that I use for other programming languages --such as C, Haskell, Agda, Lisp, and Prolog. Moreover, being a Lisp interpreter, we can alter the look and feel of Emacs live, without having to restart it --e.g., press kbd:C-x_C-e after the final parenthesis of ~(scroll-bar-mode 0)~ to run the code that removes the scroll-bar. #+begin_quote /I use Emacs every day. I rarely notice it. But when I do, it usually brings me joy./ ─[[https://so.nwalsh.com/2019/03/01/emacs][Norman Walsh]] #+end_quote I have used Emacs as an interface for developing [[https://github.com/alhassy/CheatSheet#cheatsheet-examples][cheat sheets]], for making my blog, and as an application for ‘interactively learning C’. If anything Emacs is more like an OS than just a text editor --“living within Emacs” provides an abstraction over whatever operating system my machine has: [[https://www.fugue.co/blog/2015-11-11-guide-to-emacs.html][It's so easy to take everything with me.]] Moreover, the desire to mould Emacs to my needs has made me a better programmer: I am now a more literate programmer and, due to Elisp's documentation-oriented nature, I actually take the time and effort to make meaningful documentation --even when the project is private and will likely only be seen by me. #+begin_quote /Seeing Emacs as an editor is like seeing a car as a seating-accommodation./ -- [[https://karl-voit.at/2015/10/23/Emacs-is-not-just-an-editor/][Karl Voit]] #+end_quote # Comparing Emacs to an editor is like comparing GNU/Linux to a word processor. -- [[https://karl-voit.at/2015/10/23/Emacs-is-not-just-an-editor/][Karl Voit]] **   /Emacs is a flexible platform for developing end-user applications/ :PROPERTIES: :CUSTOM_ID: Emacs-is-a-flexible-platform-for-developing-end-user-applications :END: Just as a web browser is utilised as a platform for deploying applications, or ‘extensions’, written in JavaScript that act on HTML documents, Emacs is a platform for deploying applications written in Emacs Lisp that act on buffers of text. In the same vein, people who say Emacs having Tetris is bloat are akin to non-coders who think their browser has bloat since it has a “view page source” feature ---which nearly all browsers have, yet it's only useful to web developers. Unlike a web browser in which the user must get accustomed to its features, Emacs is customised to meet the needs of its user. ( Incidentally, Emacs comes bundled with a web browser. ) #+begin_quote In the case of Emacs the boundary between user and programmer is blurred as adapting the environment to one’s needs is [[https://www.gnu.org/software/emacs/emacs-paper.html][already an act of programming with a very low barrier to entry.]] ---[[https://elephly.net/posts/2016-02-14-ilovefs-emacs.html][rekado]] #+END_quote #+begin_box /Don't just get used to your tool, make it get used to you!/ #+end_box Emacs is not just an editor, but a host for running Lisp applications! For example, Emacs is shipped as a language-specific IDE to a number of communities ---e.g., Oz, Common Lisp, and, most notably, Agda. Emacs is a great IDE for a language ---one just needs to provide a ‘major mode’ and will then have syntax highlighting, code compleition, jumping to definitions, etc. There is no need to make an IDE from scratch. ** The Power of Text Manipulation :PROPERTIES: :CUSTOM_ID: The-Power-of-Text-Manipulation :END: Emacs has ways to represent all kinds of information as text. E.g., if want to make a regular expression rename of files in a directory, there's no need to learn about a batch renaming tool: [[kbd:M-x dired ⟨RET⟩ M-x wdired-change-to-wdired-mode]] now simply perform a /usual/ find-and-replace, then save with the /usual/ kbd:C-x_C-s to effect the changes! Likewise for other system utilities and services (•̀ᴗ•́)و Moreover, as will be shown below, you can literally use [[https://github.com/zachcurry/emacs-anywhere/#usage][Emacs anywhere]] for textually input in your operating system --no copy-paste required. ** COMMENT It will change how you think about programming :PROPERTIES: :CUSTOM_ID: COMMENT-It-will-change-how-you-think-about-programming :END: Emacs is an incremental programming environment: You run snippets of code immediately after writing them ---there is no formal edit-run cycle. /The editor is the interpreter./ In my personal experience, Emacs introduced me to Lisp. - Since Lisp has no concrete syntax, everything is written using abstract syntax trees (and macros introduce concrete, domain-specific, syntax), we can see Lisp everywhere and so see Lisp as “building material” for other programming languages. - Likewise, Emacs is building material for a computing environment. Whereas others might use a mixture of bash scripts to accomplish their goals, I can use Lisp to produce applications with radically distinct uses; e.g., using the same template application to produce email snippets and code snippets. - [ ] Generally speaking, applications provide configurations via checkboxes that can be ticked off (i.e., a JSON file). - [ ] What if you want such a feature enabled only under specific settings? - [ ] What if you want the value of the checkbox setting to be the result of an arbitrary expression evaluated according to a file? Emacs provides a full fledged programming language for the purposes of configuration: Press ~C- h k~ then any key sequence to find out what (well-documented) code is run, then /advise/ that code with your desired configuration. # This is the power of full introspectivity! That is, general applications are configured using a /passive/ JSON *files* (i.e., checkboxes) whereas Emacs is configured using an /executable/ Lisp *program*. I have fallen head over heels for Lisp. ** Keyboard Navigation and Alteration :PROPERTIES: :CUSTOM_ID: Keyboard-Navigation-and-Alteration :END: Suppose you wrote a paragraph of text, and wanted to ‘border’ it up for emphasies in hypens. Using the mouse to navigate along with a copy-paste of the hypens is vastely inferior to the incantation [[kbd:M-{ C-u 80 - RET M-} C-u 80 - RET]]. If we want to border up the previous 𝓃-many paragraphs, we simply prefix kbd:M-{,} above with kbd:C-u_𝓃 ---a manual approach would have us count 𝓃 and slowly scroll. ( Exercise: What incantation of keys ‘underlines’ the current line with /only/ the necessary amount of dashes? ---Solution in the source file. ) # =C-a C-k C-y RET C-y C-SPC C-a C-M-% . RET - RET != ⇒ [[https://support.apple.com/en-ca/HT201236][MacOS supports]] many Emacs shortcuts, system-wide, such as kbd:C-a/e, kbd:C-d, kbd:C-k/y, kbd:C-o, kbd:C-p/n and even kbd:C-t for transposing two characters. ⇐ ** Emacs Proverbs as Koan :PROPERTIES: :CUSTOM_ID: Emacs-Proverbs-as-Koan :END: Below is an extract from William Cobb's “Reflections on the Game of Go”, with minor personalised adjustements for Emacs. Enjoy! The Japanese term /satori/ refers to the experience of enlightenment, the realisation of how things really are that is the primary aim of practice and meditation. However, the Zen tradition is famous for claiming that one cannot say what it is that one realises, that is, one cannot articulate the content of the enlightenment experience. Although it makes everything clear, it is an experience beyond words. Instead of being given an explanation of how things are, the student of Zen hears sayings called /koan/, often somewhat paradoxical in character, that come from those who are enlightened: + “There are no CTRL and META.” + “If you meet an Emacs you dislike, kill it.” + “No one knows Emacs.” + “One can only learn Emacs by living within it.” + “To know Org mode is to know oneself.” It is important to realise that /koan/ are intended to move you off of one path and onto another. They are not just attempts to mystify you. For example, the first proverb is in regards to newcomers complaining about too many keybinings ---eventually it's muscle memory---, whereas the second is about using the right tool for the right task ---Emacs is not for everyone. The fourth is, well, Emacs is an operating system. ** Possibly Interesting Reads :PROPERTIES: :CUSTOM_ID: Possibly-Interesting-Reads :END: + [[https://www.gnu.org/software/emacs/tour/][The Emacs Tour]] + [[https://sachachua.com/blog/series/a-visual-guide-to-emacs/][How to Learn Emacs: A Hand-drawn One-pager for Beginners / A visual tutorial]] + [[http://emacsrocks.com/][Video Series on Why Emacs Rocks]] ---catch the enthusiasm! + [[https://www.gnu.org/software/emacs/emacs-paper.html][EMACS: The Extensible, Customizable Display Editor]] # - This paper was written by Richard Stallman in 1981 and delivered in the # ACM Conference on Text Processing. “The programmable editor is an outstanding opportunity to learn to program!” + [[https://www.gnu.org/philosophy/free-sw.html][What is free software?]] # + Link to emacs main site, [[https://www.gnu.org/software/emacs/][Emacs]] . + [[http://ehneilsen.net/notebook/orgExamples/org-examples.html#sec-18][Emacs org-mode examples and cookbook]] + [[https://m00natic.github.io/emacs/emacs-wiki.html][An Opinionated Emacs guide for newbies and beyond]] + [[https://tuhdo.github.io/emacs-tutor.html][Emacs Mini-Manual, Part I of III]] # + The [[http://tuhdo.github.io/emacs-tutor.html#orgheadline63][Emacs Mini Manual]], or + [[https://github.com/erikriverson/org-mode-R-tutorial/blob/master/org-mode-R-tutorial.org][Org and R Programming]] ---a tutorial on literate programming, e.g., evaluating code within ~src~ bloc. + Reference cards for [[https://www.gnu.org/software/emacs/refcards/pdf/refcard.pdf][GNU Emacs]], [[https://www.gnu.org/software/emacs/refcards/pdf/orgcard.pdf][Org-mode]], and [[https://github.com/alhassy/ElispCheatSheet/blob/master/CheatSheet.pdf][Elisp]]. + [[https://www.reddit.com/r/emacs/comments/6fytr5/when_did_you_start_using_emacs/][“When did you start using Emacs” discussion on Reddit]] + [[https://david.rothlis.net/emacs/howtolearn.html][“How to Learn Emacs”]] + [[https://orgmode.org/index.html#sec-4][The Org-mode Reference Manual]] or [[https://orgmode.org/worg/][Worg: Community-Written Docs]] which includes a [[https://orgmode.org/worg/org-tutorials/index.html][meta-tutorial]]. + [[https://github.com/emacs-tw/awesome-emacs][Awesome Emacs]]: A community driven list of useful Emacs packages, libraries and others. + [[https://github.com/caisah/emacs.dz][A list of people's nice emacs config files]] # + [[https://emacs.stackexchange.com/questions/3143/can-i-use-org-mode-to-structure-my-emacs-or-other-el-configuration-file][Stackexchange: Using org-mode to structure config files]] + [[http://emacslife.com/how-to-read-emacs-lisp.html][Read Lisp, Tweak Emacs: How to read Emacs Lisp so that you can customize Emacs]] + [[https://practicaltypography.com/why-racket-why-lisp.html][Why Racket? Why Lisp?]] ---If eye-candy, a sleek and beautiful GUI, would entice you then consider starting with [[http://spacemacs.org/][spacemacs]]. Here's a helpful [[https://www.youtube.com/watch?v=hCNOB5jjtmc][installation video]], after which you may want to watch [[https://www.youtube.com/watch?v=PVsSOmUB7ic][Org-mode in Spacemacs]] tutorial--- Remember: Emacs is a flexible platform for developing end-user applications; e.g., this configuration file is at its core an Emacs Lisp program that yields the editor of my dreams --it encourages me to grow and to be creative, and I hope the same for all who use it; moreover, it reflects my personality such as what I value and what I neglect in my workflow. # why emacs ---not marching to someone-else's tune! #+begin_quote org I’m stunned that you, as a professional software engineer, would eschew inferior computer languages that hinder your ability to craft code, but you put up with editors that bind your fingers to someone else’s accepted practice. ---[[http://www.howardism.org/Technical/Emacs/why-emacs.html][Howard Abrams]] #+end_quote ** Fun commands to try out :PROPERTIES: :CUSTOM_ID: Fun-commands-to-try-out :END: Finally, here's some fun commands to try out: + ~M-x doctor~ ---generalising the idea of rubber ducks + ~M-x tetris~ or ~M-x gomoku~ or ~M-x snake~---a break with a classic - ~C-u 𝓃 M-x hanoi~ for the 𝓃-towers of Hanoi + ~M-x butterfly~ ---in reference to [[https://xkcd.com/378/][“real programmers”]] # Then, ~M-x help-with-tutorial~ or ~C-h t~ to start the ~30 min tutorial. A neat way to get started with Emacs is to solve a problem you have, such as taking notes or maintaining an agenda ---both with Org-mode. Before we get started… ** What Does Literate Programming Look Like? :PROPERTIES: :CUSTOM_ID: What-Does-Literate-Programming-Look-Like :END: Briefly put, literate programming in Emacs allows us to evaluate source code within our text files, then using the results as values in other source blocks. When presenting an algorithm, we can talk it out, with a full commentary thereby providing ‘reproducible research’: Explorations and resulting algorithms are presented together in a natural style. #+html:

:Src: #+begin_src plantuml :file images/literate-programming.png :tangle no :exports results :eval never-export :results (progn (org-display-inline-images t t) "replace") skinparam defaultTextAlignment center /' Text alignment '/ skinparam titleBorderRoundCorner 15 skinparam titleBorderThickness 2 skinparam titleBorderColor red skinparam titleBackgroundColor Aqua-CadetBlue title Literate Programming with Org-Mode actor You You --> (Code) : Ideas You --> (Text) : Ideas [**Org Mode**] as Org (Text) --> Org : Writing (Code) --> Org : Writing Org --[#green]> (Document) : ‘Weaving’ Org --[#green]> (RawCode) : ‘Tangeling’ database Computer as "**Computer** --- interpreter --- compiler" cloud People { :jasim: :kathy: } (Document) --[#green]> People : PDF, HTML, Text (RawCode) --[#green]> (Computer) : tex, java, c, py center footer ♥‿♥ Write once, generate many (•̀ᴗ•́)و #+end_src :End: [[file:images/literate-programming.png]] # # (org-display-inline-images t t) ⟨ This image was created in org-mode; details [[#Workflow-States][below]] or by looking at the source file 😉 ⟩ #+html:

Here's an example of showing code in a natural style, but having the resulting code appear in a style amicable to a machine. *Here's what you type:* #+BEGIN_src org :tangle no It's natural to decompose large problems, ,#+begin_src haskell :noweb-ref defn-of-f :tangle no f = h ∘ g ,#+end_src But we need to define $g$ and $h$ /beforehand/ before we can use them. Yet it's natural to “motivate” their definitions ---rather than pull a rabbit out of hat. Org lets us do that! Here's one definition, ,#+begin_src haskell :noweb-ref code-from-other-places :tangle no g = ⋯ ,#+end_src then the other. ,#+begin_src haskell :noweb-ref code-from-other-places :tangle no h = ⋯ ,#+end_src Of-course, we might also want a preamble: ,#+BEGIN_SRC haskell :tangle myprogram.hs import ⋯ ,#+END_SRC We can now tangle together the tagged code blocks in the order we want. ,#+BEGIN_SRC haskell :tangle myprogram.hs :comments none :noweb yes <> <> ,#+END_SRC ( You can press “C-c C-v C-v” to see what this block expands into! ) #+END_src Now kbd:C-c_C-v_C-t (doc:org-babel-tangle) yields a file named ~myprogram.hs~ with contents in an order amicable to a machine. #+BEGIN_SRC haskell :tangle no import ⋯ g = ⋯ h = ⋯ f = h ∘ g #+END_SRC Interestingly, unlike certain languages, Haskell doesn't care too much about declaration order. *Warning!* If we have different language blocks tangled to the same file, then they are tangled alphabetically ---e.g., if one of the blocks is marked ~emacs-lisp~ then its contents will be the very first one in the resulting source file, since ~emacs-lisp~ begins with ~e~ which is alphabetically before ~h~ of ~haskell~. + [[http://www.howardism.org/Technical/Emacs/literate-programming-tutorial.html][Introduction to Literate Programming with Org-mode]] + [[http://ehneilsen.net/notebook/orgExamples/org-examples.html][Emacs org-mode examples and cookbook]] + [[https://leanpub.com/lit-config/read][Literate Config]] ---Online booklet ** Why a monolithic configuration? :PROPERTIES: :CUSTOM_ID: Why-a-monolithic-configuration :END: Why am I keeping my entire configuration ---from those involving cosmetics & prose to those of agendas & programming--- in one file? Being monolithic ---“a large, mountain-sized, indivisible block of stone”--- is generally not ideal in nearly any project: E.g., a book is split into chapters and a piece of software is partitioned into modules. Using Org-mode, we can still partition our setup while remaining in one file. An Emacs configuration is a personal, leisurely project, and one file is a simple architecture: I don't have to worry about many files and the troubles of moving content between them; instead, I have headings and move content almost instantaneously ---org-refile by pressing ~w~ at the start of the reader. Moreover, being one file, it is easy to distribute and to extract artefacts from it ---such as the README for Github, the HTML for my blog, the colourful PDF rendition, and the all-important Emacs Lisp raw code file. Moreover, with a single ~#~ I can quickly comment out whole sections, thereby momentarily disabling features. There's no point in being modular if there's nothing explaining what's going on, so I document. The [[#Conclusion---Why-Configuration-Files-Should-be-Literate][concluding]] section of this read further argues the benefits of maintaining literate, and monolithic, configuration files. As a convention, I will try to motivate the features I set up and I will prefix my local functions with, well, ~my/~ ---this way it's easy to see all my defined functions, and this way I cannot accidentally shadow existing utilities. Moreover, besides browsing the web, I do nearly everything in Emacs and so the start-up time is unimportant to me: Once begun, I have no intention of spawning another instance nor closing the current one. ( Upon an initial startup using this configuration, it takes a total of 121 seconds to install all the packages featured here. ) Enjoy! * Booting Up :PROPERTIES: :CUSTOM_ID: Booting-Up :header-args: :tangle init.el :END: Let's decide on where we want to setup our declarations for personalising Emacs to our needs. Then, let's bootstrap Emacs' primintive 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= :PROPERTIES: :CUSTOM_ID: emacs-vs-init-org :END: /Emacs is extensible/: When Emacs is started, it tries to load a user's Lisp program known as an *initialisation (‘init’) file* which specifies 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 [[kbd: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 [[kbd: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 [[kbd:C-h i m emacs lisp intro RET i .emacs RET]]. + After inserting some lisp code, such as ~(set-background-color "salmon")~, and saving, one can load the changes with [[kbd:M-x eval-buffer]], doc:eval-buffer. + In a terminal, use ~emacs -Q~ to open emacs without any initialisation files. # Emacs is a stateful Lisp-based machine! Besides writing Lisp in an init file, one may use Emacs' customisation interface, [[kbd: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. # # Unless suggested otherwise, Emacs writes stuff to =~.emacs= automatically. 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. #+begin_details "/Adventure time!/ “Honey, where's my init?”" link-here: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! #+end_details #+begin_details Adventure time! Using Emacs’ Easy Customisation Interface link-here: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. [[kbd:M-x customize-variable RET line-number-mode RET]] 2. Then press: kbd:toggle, kbd:state, then [[kbd:1]]. 3. Now take a look: [[kbd:C-x C-f ~/.emacs]] #+end_details #+begin_details Support for ‘Custom’ link-here: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 :No_longer_true: Speaking of local variables, let's always load 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 :tangle no (setq enable-local-variables :safe) #+END_SRC :End: #+end_details ** Who am I? :PROPERTIES: :CUSTOM_ID: Who-am-I :END: Let's set the following personal Emacs-wide variables ---to be used locations such as email. #+begin_src emacs-lisp (setq user-full-name "Musa Al-hassy" user-mail-address "alhassy@gmail.com") #+end_src For some fun, run this cute method. #+BEGIN_SRC emacs-lisp :tangle no (animate-birthday-present user-full-name) #+END_SRC ** Emacs Package Manager :PROPERTIES: :CUSTOM_ID: Emacs-Package-Manager :END: # Installing Emacs packages directly from source There are a few ways to install packages ---run kbd:C-h_C-e for a short overview. The easiest, for a beginner, is to use the command doc:package-list-packages then find the desired package, press [[kbd:i]] to mark it for installation, then install all marked packages by pressing [[kbd:x]]. + /Interactively/: [[kbd:M-x list-packages]] to see all melpa packages that can install - Press kbd:Enter on a package to see its description. + Or more quickly, to install, say, unicode fonts: [[kbd: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 doc: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]] ([[https://github.com/melpa/melpa][Milkypostman's ELPA]]) ---it builds packages directly from the source-code repositories of developers rather than having all packages in one repository. #+BEGIN_SRC emacs-lisp :tangle init.el ;; Make all commands of the “package” module present. (require 'package) ;; Internet repositories for new packages. (setq package-archives '(("gnu" . "http://elpa.gnu.org/packages/") ("nongnu" . "https://elpa.nongnu.org/nongnu/") ("melpa" . "http://melpa.org/packages/"))) #+END_SRC #+BEGIN_SRC emacs-lisp :tangle init.el ;; Update local list of available packages: ;; Get descriptions of all configured ELPA packages, ;; and make them available for download. (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./ :Faq: If there are issues with loading the archives, say, "Failed to download ‘gnu’ archive." then ensure you can both read and write, recursively, to your .emacs.d/ E.g., within emacs try to execute (package-refresh-contents) and you'll observe a permissions error. :End: The declarative configuration tool [[https://github.com/jwiegley/use-package/][use-package]] is a macro/interface that manages our packages and the way they interact. #+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 makes sure it is accessible. If the file is not on our system, 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 Notice that doc:use-package /allows us to tersely organise a package's configuration/ ---and that it is /not/ a package manger, but we can make it one by having it automatically install modules, when needed, using ~:ensure t~. #+begin_details Super Simple ‘use-package’ Mini-tutorial link-here:Super-Simple-‘use-package’-Mini-tutorial 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. # More generally, it let's us hook functions fᵢ, which may depend on the # current mode, to modules mᵢ. 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~. - Use the standalone keyword ~:disabled~ to turn off loading a module that, say, you're not using anymore. #+end_details We now bootstrap ~use-package~. 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 :tangle init.el (use-package auto-package-update :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 :tangle init.el ;; Making it easier to discover Emacs key presses. (use-package which-key :defer nil :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. ⟩ Here are other packages that I want to be installed onto my machine. #+BEGIN_SRC emacs-lisp :tangle init.el ;; Haskell's cool (use-package haskell-mode ) ;; 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”. ;; Let's use the “s” library. (defvar my/personal-machine? (equal "Musa’s MacBook Air " (s-collapse-whitespace (shell-command-to-string "scutil --get ComputerName"))) "Is this my personal machine, or my work machine? At one point, on my work machine I run the following command to give the machine a sensible name. sudo scutil --set ComputerName work-machine dscacheutil -flushcache") (defvar my/work-machine? (not my/personal-machine?)) ;; 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 :tangle init.el <> #+END_SRC #+begin_box DRY: Don't Repeat Yourself! In the HTML export, above it /looks/ like I just copy-pasted the undo tree setup from earlier, but that is not the case! All I did was *pink:declare* to Org that I'd like that /named snippet/ to be tangled now, here in the resulting code file. #+begin_src org :tangle no ,#+begin_src emacs-lisp :noweb yes <> ,#+end_src #+end_src You can press kbd:C-c_C-v_C-v, doc:org-babel-expand-src-block, to see what this block expands into ---which is what was shown above. #+end_box -------------------------------------------------------------------------------- link-here:Quelpa [[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~. # Example invocation. # (quelpa '(discover-my-major :fetcher git :url "https://framagit.org/steckerhalter/discover-my-major.git")) #+BEGIN_SRC emacs-lisp :tangle init.el (use-package quelpa :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+ :quelpa (info+ :fetcher wiki :url "https://www.emacswiki.org/emacs/info%2b.el")) #+END_SRC ** Installing OS packages, and automatically keeping my system up to data, from within Emacs :PROPERTIES: :CUSTOM_ID: Installing-OS-packages-and-automatically-keeping-my-system-up-to-data-from-within-Emacs :END: 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. - It has [[https://github.com/jwiegley/use-package#use-package-ensure-system-package][multiple features]]. Let's obtain the extension. #+BEGIN_SRC emacs-lisp :tangle init.el ;; Auto installing OS system packages (use-package system-packages :config (system-packages-update)) ;; Install OS packages using `use-package`. (use-package use-package-ensure-system-package) ;; Caching the installed pkgs list makes system-package-ensure return nearly immediately for things already installed! (setq my/installed-packages (shell-command-to-string "brew list")) (defun system-packages-ensure (pkg) (unless (s-contains-p pkg my/installed-packages) (shell-command-to-string (format "brew list %s || brew install %s --force" pkg pkg)))) ;; Please don't bother me when shell buffer names are in use, just make a new ;; buffer. (setq async-shell-command-buffer 'new-buffer) ;; Display the output buffer for asynchronous shell commands only when the ;; command generates output. (setq async-shell-command-display-buffer nil) ;; Don't ask me if I want to kill a buffer with a live process attached to it; ;; just kill it please. (setq kill-buffer-query-functions (remq 'process-kill-buffer-query-function kill-buffer-query-functions)) ;; 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. (defvar my/installed-packages (shell-command-to-string "brew list") "What is on my machine already? Sometimes when I install a GUI based application and do not have access to it everywhere in my path, it may seem that I do not have that application installed. For instance, (system-packages-package-installed-p \"google-chrome\") returns nil, even though Google Chrome is on my machine. As such, we advise the `system-packages-ensure' installtion method to only do installs of pacakges that are not in our `my/installed-packages' listing. ") (advice-add 'system-packages-ensure :before-until (lambda (pkg) (s-contains-p pkg my/installed-packages))) #+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 :tangle init.el ;; An Emacs-based interface to the package manager of your operating system. (use-package helm-system-packages) #+END_SRC The Helm counterpart is great for /discovarability/, whereas the plain ~system-packages~ is great for /programmability/. #+begin_src emacs-lisp :tangle init.el (setq system-packages-noconfirm :do-not-prompt-me-about-confirms) ;; After 1 minute after startup, kill all buffers created by ensuring system ;; packages are present. (run-with-timer 60 nil (lambda () (kill-matching-buffers ".*system-packages.*" t :kill-without-confirmation))) #+end_src 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 :tangle init.el ;; Unlike the Helm variant, we need to specify our OS pacman. (when (eq system-type 'darwin) (setq system-packages-package-manager 'brew)) ;; If the given system package doesn't exist; install it. (when (eq system-type 'darwin) (system-packages-ensure "amethyst")) ;; This is a MacOS specific package. (ignore-errors (system-packages-ensure "google-chrome")) ;; My choice of web browser (system-packages-ensure "microsoft-teams") ;; For remote work meetings ;; Gif maker; needs privileges to capture screen. ;; ;; ⇒ Move the screen capture frame while recording. ;; ⇒ Pause and restart recording, with optional inserted text messages. ;; ⇒ Global hotkey (shift+space) to toggle pausing while recording (system-packages-ensure "licecap") ;; Use: ⌘-SPACE licecap ;; Pack, ship and run any application as a lightweight container (system-packages-ensure "docker") ;; Free universal database tool and SQL client (system-packages-ensure "dbeaver-community") ;; Kubernetes IDE (system-packages-ensure "lens") ;; Platform built on V8 to build network applications ;; Also known as: node.js, node@16, nodejs, npm (system-packages-ensure "node") ;; https://nodejs.org/ ;; Nice: https://nodesource.com/blog/an-absolute-beginners-guide-to-using-npm/ ;; Manage multiple Node.js versions (shell-command "curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash") ;; According to https://github.com/nvm-sh/nvm, nvm shouldn't be installed via brew. ;; ;; 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")) #+end_src # For instance, let's install a tiling window manager: https://ianyh.com/amethyst/ # brew install --cask amethyst Amethyst requires some more setup: Open its preferences, then... - Then select: =Mouse: Focus follows mouse=. - Also: =Shortcuts=, then disable ‘increase/decrease main pane count’ bindings since they override the beloved Emacs =M-<,>= keys. Neato! Now I can live in Emacs even more ^_^ -------------------------------------------------------------------------------- (*Open Scripting Architecture (OSA) Scripts*) Amethyst is great, but it has a problem of randomly not working. Unfortunatley it has no command line interface, so let's make one in Emacs: Now kbd:⌘-a_r relaunches Amethyst. #+begin_details #+begin_src emacs-lisp :tangle init.el (defun ⌘-quit (app) "Kill application APP; e.g., “amethyst” or “Safari”" (shell-command (format "osascript -e 'quit app \"%s\"'" app))) (defun ⌘-open (app) "Open application APP; e.g., “amethyst” or “Safari”" (async-shell-command (format "osascript -e 'launch app \"%s\"'" app))) ;; (bind-key "???-a r" #'my/relaunch-amethyst) (defun my/relaunch-amethyst () (interactive) (⌘-quit "amethyst") (⌘-open "amethyst")) #+end_src We use the ~osascript~ command to ~tell~ the [[https://en.wikibooks.org/wiki/AppleScript_Programming/System_Events][System Events]] ~application~ to issue keystrokes to other applications. I found out about by Googling “How to send keystrokes from terminal”. #+begin_src emacs-lisp :tangle init.el ;; (bind-key "???-a c" #'amethyst/cycle-layout) (defun amethyst/cycle-layout () (interactive) (shell-command "osascript -e 'tell application \"System Events\" to keystroke space using {shift down, option down}'")) #+end_src If you get: #+begin_src shell :tangle no 36:51: execution error: System Events got an error: osascript is not allowed to send keystrokes. (1002) #+end_src Then: Go to Security & Privacy -> Privacy tab -> Accessibility -> Add osascript (/usr/bin/osascript) You may need to restart Emacs. Reads: + [[https://eastmanreference.com/complete-list-of-applescript-key-codes][Complete list of AppleScript key codes]] + [[https://eastmanreference.com/how-to-automate-your-keyboard-in-mac-os-x-with-applescript][How to automate your keyboard in Mac OS X with AppleScript]] #+end_details I enter “⌘” using a TeX input method setup below (called “Agda”). ** Syncing to the System's =$PATH= :PROPERTIES: :CUSTOM_ID: Syncing-to-the-System's-PATH :END: 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 :tangle init.el (use-package exec-path-from-shell :defer nil :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. :Explicit_solution: #+BEGIN_SRC emacs-lisp :tangle no ;; https://emacs.stackexchange.com/questions/4090/org-mode-cannot-find-pdflatex-using-mac-os (defun set-exec-path-from-shell-PATH () "Sets the exec-path to the same value used by the user shell" (let ((path-from-shell (replace-regexp-in-string "[[:space:]\n]*$" "" (shell-command-to-string "$SHELL -l -c 'echo $PATH'")))) (setenv "PATH" path-from-shell) (setq exec-path (split-string path-from-shell path-separator)))) ;; call function now (set-exec-path-from-shell-PATH) #+END_SRC :End: ** Restarting Emacs ---Keeping buffers open across sessions? :PROPERTIES: :CUSTOM_ID: Restarting-Emacs-Keeping-buffers-open-across-sessions :END: Sometimes I wish to close then reopen Emacs; unsurprisingly someone's thought of implementing that. #+BEGIN_SRC emacs-lisp :tangle init.el ;; Provides only the command “restart-emacs”. (use-package restart-emacs :defer nil ;; 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 :tangle init.el ;; 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 :tangle init.el (setq-default save-place t) (setq save-place-file "~/.emacs.d/etc/saveplace") #+END_SRC **  “Being at the Helm” ---Completion & Narrowing Framework :PROPERTIES: :CUSTOM_ID: Being-at-the-Helm-Completion-Narrowing-Framework :END: 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! “Fuzzy finding”: Press ~M-x~ and just start typing, methods mentioning what you've typed are suddenly listed! Moreover, ~C-c i~ (doc:helm-imenu) will show you the headers in an Org file /or/ the top-level variables/functions/types when programming. Finally, whenever a Helm session has started, toggle follow-mode with ~C-c C-f~ to obtain contextual-awareness; e.g., ~C-c i RET C-c C-f~ will change your screen as you scroll through the menu. /(A killer feature is doc:helm-do-grep-ag which will do a search in your whole project, file tree)./ | Remembrance comes with time, until then /ask/ Emacs! | /Try and be grateful!/ #+BEGIN_SRC emacs-lisp :tangle init.el (use-package helm :defer nil :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.f. “C-x t m” (imenu-list) ;; ("C-u C-c i" . imenu-list) ;; TODO FIXME Key sequence C-u C-c i starts with non-prefix key C-u ("C-h a" . helm-apropos) ;; Look at what was cut recently & paste it in. ("M-y" . helm-show-kill-ring) ("C-x C-x" . helm-all-mark-rings) :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) ("" . helm-execute-persistent-action))) ;; Show me nice file icons when using, say, “C-x C-f” or “C-x b” ;; (use-package helm-icons ;; :defer nil ;; :custom (helm-icons-provider 'all-the-icons) ;; :config (helm-icons-enable)) ;; When I want to see the TOC of an Org file, show me down to 3 subheadings. (setq org-imenu-depth 3) #+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. - If we want the ~M-x~ minibuffer to appear at the top of the screen, or middle, we can use [[https://github.com/muffinmad/emacs-mini-frame][emacs-mini-frame]] as [[https://gist.github.com/rougier/126e358464e12aa28fac5b4f3dd5eb9c?permalink_comment_id=4110106#gistcomment-4110106][shown beautifully here.]] I like ~helm-M-x~, for now. 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 :tangle init.el (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 :tangle init.el (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 :tangle init.el (use-package helm-swoop :defer nil :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! :ThisBecameMoreAnnoyingThanUseful: #+BEGIN_SRC emacs-lisp :tangle no ;; If there is no symbol at the cursor, use the last used words instead. (setq helm-swoop-pre-input-function (lambda () (let (($pre-input (thing-at-point 'symbol))) (if (eq (length $pre-input) 0) helm-swoop-pattern ;; this variable keeps the last used words $pre-input)))) #+END_SRC Press ~M-i~ after a search has executed to enable it for all buffers. We can also limit our search to org files, or buffers of the same mode, or buffers belonging to the same project! Note that on the Mac, I can still perform default Emacs search using /Cmd+f/. :End: :Old_ivy_counsel_swiper_shenanigans: + There is a super duper neato search capability we can utilise -- ~swiper~. + We also use the ~counsel~, a collection of completion, ~ivy~, enhanced versions of common Emacs commands. #+begin_src emacs-lisp :tangle no (use-package ivy ;; More powerful but ugly looking completetion framework. ;; Helm is prettier. ;; Needed for Counsel, below. :config ;; add ‘recentf-mode’ and bookmarks to ‘ivy-switch-buffer’. (setq ivy-use-virtual-buffers t) ;; number of result lines to display (setq ivy-height 10) ;; does not count candidates (setq ivy-count-format "") ;; no regexp by default (setq ivy-initial-inputs-alist nil)) (use-package counsel :bind* ; load when pressed (("C-s" . swiper) ;; ("C-x C-r" . counsel-recentf) ; search for recently edited (" l" . counsel-find-library) ; find an Emacs Lisp library (" u" . counsel-unicode-char))) ; insert a unicode symbol using a pop-up #+end_src ~swiper~ is an alternative to Emacs' default incremental search. It shows an overview of all matches --navigate using arrow keys. There is also a helm based version of swiper. :end: Lets also use doc:helm-do-grep-ag ([[kbd:C-x c M-g a]]) *search all files in the current directory for a particular (regexp) string* - Shows matches live as you type - Very helpful when looking for a definition of something #+begin_src emacs-lisp :tangle init.el (system-packages-ensure "ag") #+end_src *Marking my place when I jump around* Let's use ~M-m~ to get a nice menu of places we've been recently. #+begin_src emacs-lisp :tangle init.el ;; Save/mark a location with “C-u M-m”, jump back to it with “M-m”. (bind-key* "M-m" (lambda () (interactive) (if (not current-prefix-arg) (helm-mark-ring) (push-mark) (message "[To return to this location, press M-m] ∷ %s" (s-trim (substring-no-properties (thing-at-point 'line))))))) #+end_src 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 ;-) #+begin_src emacs-lisp :tangle init.el ;; Make `links' from elisp symbols (quoted functions, variables and fonts) in Gnu-Emacs Info viewer to their help documentation. (use-package inform :defer nil :config (require 'inform)) #+end_src ** Org-Mode Administrivia :PROPERTIES: :CUSTOM_ID: Org-Mode-Administrivia :END: :Updating_FAQ: #+BEGIN_SRC emacs-lisp :tangle no (shell-command "rm ~/.emacs.d/elpa/org-9.3/*.elc") (byte-recompile-directory "~/.emacs.d/elpa/org-9.3/") #+END_SRC Easiest way get latest version of org-mode: ~M-x package-list-packages~, then select ~org~, then ~install~. https://stackoverflow.com/questions/31855904/emacs-sees-the-directory-with-the-new-org-mode-version-but-loads-the-old-versio https://emacs.stackexchange.com/questions/27597/how-to-update-org-to-latest-version-using-package-repos-git-clone-in-ubuntu :End: # Let's setup Org-mode so # that we can quickly move between headings and org-blocks ---~n,p~ on heading # starts and ~s-n,p~ on blocks---, then let's prettify the leading stars of # headings, Org's formatting delimiters, and even its blocks delimiters by making # them less intrusive thereby ‘fading into the background’ and drawing minimal # attention. This has been useful when promoting Org-mode by sharing my screen # with others. # Let's conclude this ‘boot-up’ by getting /Emacs' killer app/, Org-mode, setup; along with the extras that allow us to ignore heading names, but still utilise their contents ---e.g., such as a heading named ‘preamble’ that contains org-mode setup for a file. #+begin_src emacs-lisp :tangle init.el (use-package emacs :defer nil :ensure org-contrib :config (require 'ox-extra) (ox-extras-activate '(ignore-headlines))) #+end_src ~org-plus-contrib~ contain the files that are included with Emacs plus all contributions from the [[https://code.orgmode.org/bzg/org-mode/src/master/contrib][org-mode repository]]. + Use the ~:ignore:~ tag on headlines you'd like to have ignored, while not ignoring their content. + Use the ~:noexport:~ tag to omit a headline /and/ its contents. #+begin_src emacs-lisp :tangle init.el ;; Replace the content marker, “⋯”, with a nice unicode arrow. (setq org-ellipsis " ⤵") ;; Fold all source blocks on startup. (setq org-hide-block-startup t) ;; Lists may be labelled with letters. (setq org-list-allow-alphabetical t) ;; Avoid accidentally editing folded regions, say by adding text after an Org “⋯”. (setq org-catch-invisible-edits 'show) ;; I use indentation-sensitive programming languages. ;; Tangling should preserve my indentation. (setq org-src-preserve-indentation t) ;; Tab should do indent in code blocks (setq org-src-tab-acts-natively t) ;; Give quote and verse blocks a nice look. (setq org-fontify-quote-and-verse-blocks t) ;; Pressing ENTER on a link should follow it. (setq org-return-follows-link t) #+END_SRC I rarely use tables, but here is a useful [[http://notesyoujustmightwanttosave.blogspot.com/][Org-Mode Table Editing Cheatsheet]] and a [[http://www.howardism.org/Technical/Emacs/spreadsheet.html][friendly tutorial]]. Moreover, since I end up using org-mode most of the time, let's make that the default mode. #+BEGIN_SRC emacs-lisp :tangle init.el (setq initial-major-mode 'org-mode) #+END_SRC Finally, let's get some extra Org-mode mark-up goodies, such as ~kbd:C-c_C-e~ which renders as kbd:C-c_C-e. Documentations and screenshots at: https://alhassy.github.io/org-special-block-extras/ #+BEGIN_SRC emacs-lisp :tangle init.el (defun org-special-block-extras-short-names ()) ;; ;; org-special-block-extras.el:681:1:Error: Symbol’s value as variable is void: o--supported-blocks (setq o--supported-blocks nil) ;; TODO org-special-block-extras.el:681:1:Error: Symbol’s value as variable is void: o--supported-blocks ;; (use-package org-special-block-extras :defer nil :hook (org-mode . org-special-block-extras-mode) :custom ;; The places where I keep my ‘#+documentation’ (org-special-block-extras--docs-libraries '("~/org-special-block-extras/documentation.org")) ;; Disable the in-Emacs fancy-links feature? (org-special-block-extras-fancy-links '(elisp badge kbd link-here doc tweet)) ;; Details heading “flash pink” whenever the user hovers over them? (org-html-head-extra (concat org-html-head-extra "")) ;; The message prefixing a ‘tweet:url’ badge (org-special-block-extras-link-twitter-excitement "This looks super neat (•̀ᴗ•́)و:") :config ;; Use short names like ‘defblock’ instead of the fully qualified name ;; ‘org-special-block-extras--defblock’ (org-special-block-extras-short-names)) ;; Let's execute Lisp code with links, as in “elisp:view-hello-file”. (setq org-confirm-elisp-link-function nil) #+END_SRC ** Password-locking files ---“encryption” :Disabled: :PROPERTIES: :CUSTOM_ID: Password-locking-files-encryption :header-args: :tangle no :END: With the following incantation, we name our files ~𝒳.𝒴.gpg~ where 𝒳 is the file name and 𝒴 is the usual extension, then upon save we will be prompted for an encryption method, we can press kbd:Enter on ~OK~ to just provide a password for that file. You can open that file /without/ the passphrase for a limited amount of time ---i.e., it's cached, saved, for your current computing session until logout--- or force authentication by invoking ~gpgconf --kill gpg-agent~. #+begin_src emacs-lisp (system-packages-ensure "gnupg") ;; i.e., brew install gnupg ;; “epa” ≈ EasyPG Assistant ;; Need the following in init to have gpg working fine: ;; force Emacs to use its own internal password prompt instead of an external pin entry program. (setq epa-pinentry-mode 'loopback) ;; https://emacs.stackexchange.com/questions/12212/how-to-type-the-password-of-a-gpg-file-only-when-opening-it (setq epa-file-cache-passphrase-for-symmetric-encryption t) ;; No more needing to enter passphrase at each save ^_^ ;; ;; Caches passphrase for the current emacs session? #+end_src /The purpose of encrypting a file is so that an adversary/ ---e.g., an immoral computer administrator or a thief who stole your computer--- /will have to spend so much decrypting the data than the data is actually worth./ As such, one uses GPG keys...! #+begin_details GPG Details Trivia: “gpg” stands for GnuPG, which abbreviates GNU Privacy Guard. To obtain encrypted messages from others, you will need a “GPG key”: They use /your/ “public key” (which others can see) to encrypt a file, which only /you/ can open since you have the /associated/ “private key” (which only you see). Possibly interesting reads: + [[https://www.bytedude.com/gpg-in-emacs/][GPG In Emacs | Bytedude]] + [[https://softwareengineering.stackexchange.com/questions/212192/what-are-the-advantages-and-disadvantages-of-cryptographically-signing-commits-a][What are the advantages and disadvantages of cryptographically signing commits and tags in Git? | Software Engineering Stack Exchange]] #+end_details ** Hydra: Supply a prefix only once :PROPERTIES: :CUSTOM_ID: Hydra-Supply-a-prefix-only-once :END: | /[[http://rski.github.io/emacs/hydra/2017/04/08/a-case-for-hydra.html][Hydras let us do “super temporary modal editing”]]/ | Sometimes we have keybindings that share a common prefix, say ~C-c j~ and ~C-c k~, and we invoke them in an arbitrary sequence, it would be nice to invoke the shared prefix /only once/ thereby having: | ~C-c j C-c j C-c k C-c k M-3 C-c j M-5 C-c k~ | ≈ | ~C-c jjkk3j5k~ | - The [[https://github.com/abo-abo/hydra#the-one-with-the-least-amount-of-code][“hydra-zoom”]] example from the documentation really showcases this utility. - After the prefix is supplied, all extensions are shown in a minibuffer. #+BEGIN_SRC emacs-lisp :tangle init.el ;; Invoke all possible key extensions having a common prefix by ;; supplying the prefix only once. (use-package hydra :defer nil) #+END_SRC # Outdated image. # Center image # +ATTR_HTML: :style margin-left: auto; margin-right: auto; # [[file:images/hydra.png]] From the [[https://github.com/abo-abo/hydra][Hydra]] repository is a ‘description for poets’: #+begin_quote Once you summon the Hydra through the prefixed binding (the body + any one head), all heads can be called in succession with only a short extension. The Hydra is vanquished once Hercules, any binding that isn't the Hydra's head, arrives. Note that Hercules, besides vanquishing the Hydra, will still serve his original purpose, calling his proper command. This makes the Hydra very seamless, it's like a minor mode that disables itself auto-magically. #+end_quote ⇒ The [[https://github.com/abo-abo/hydra/wiki][Hydra Wiki]] has many example hydras for common uses cases ⇐ Below are two examples; one to simplify textual navigation and another for window navigation. Yet another possible hydra would be to avoid remembering word operations, such as copying a word, upcasing it, killing a word from anywhere within it ---in contrast ~kill-word~ kills /to the end/ of the word---, etc. Likewise for line operations, such as copying a line from anywhere in it. See [[#Taking-a-tour-of-one's-edits][Taking a tour of one's edits]] below for another small and useful example. When there are multiple actions, it's nice to see such a menu displayed in the middle of the frame; so we use [[https://github.com/Ladicle/hydra-posframe#hydra-posframe][hydra-posframe]]. Moreover, it can be useful to group related actions under a common heading ---e.g., textual navigation may occur at the line level or word level or screen level--- we obtain a nice interface by declaraing hydras using [[https://github.com/jerrypnz/major-mode-hydra.el#get-started][pretty-hydra-define]] ---this saves us the trouble of [[https://github.com/abo-abo/hydra#the-impressive-looking-one][formating docstrings using classic hydra]]. #+begin_src emacs-lisp :tangle init.el ;; Show hydras overlayed in the middle of the frame (use-package hydra-posframe :disabled "TODO Fix me, breaking Github Actions test setup" :quelpa (hydra-posframe :fetcher git :url "https://github.com/Ladicle/hydra-posframe.git") :hook (after-init . hydra-posframe-mode) :custom (hydra-posframe-border-width 5)) ;; Neato doc strings for hydras (use-package pretty-hydra :defer nil) #+end_src To /actually/ define hydras, we make a helper function: doc:my/defhydra ---which combines doc:defhydra and doc:pretty-hydra-define. #+begin_details Implementation #+begin_src emacs-lisp :tangle init.el ;; TODO convert my existing defhydras to my/defhydra. (defmacro my/defhydra (key title icon-name &rest body) "Make a hydra whose heads appear in a pretty pop-up window. Heads are signalled by keywords and the hydra has an icon in its title. KEY [String]: Global keybinding for the new hydra. TITLE [String]: Either a string or a plist, as specified for pretty-hydra-define. The underlying Lisp function's name is derived from the TITLE; which is intentional since hydra's are for interactive, pretty, use. One uses a plist TITLE to specify what a hydra should do *before* any options, or to specify an alternate quit key (:q by default). ICON-NAME [Symbol]: Possible FontAwesome icon-types: C-h v `all-the-icons-data/fa-icon-alist'. BODY: A list of columns and entries. Keywords indicate the title of a column; 3-lists (triples) indicate an entry key and the associated operation to perform and, optionally, a name to be shown in the pop-up. See DEFHYDRA for more details. For instance, the verbose mess: ;; Use ijkl to denote ↑←↓→ arrows. (global-set-key (kbd \"C-c w\") (pretty-hydra-define my/hydra/\\t\\tWindow\\ Adjustment ;; Omitting extra work to get an icon into the title. (:title \"\t\tWindow Adjustment\" :quit-key \"q\") (\"Both\" ((\"b\" balance-windows \"balance\") (\"s\" switch-window-then-swap-buffer \"swap\")) \"Vertical adjustment\" ((\"h\" enlarge-window \"heighten\") (\"l\" shrink-window \"lower\")) \"Horizontal adjustment\" ((\"n\" shrink-window-horizontally \"narrow\") (\"w\" enlarge-window-horizontally \"widen\" ))))) Is replaced by: ;; Use ijkl to denote ↑←↓→ arrows. (my/defhydra \"C-c w\" \"\t\tWindow Adjustment\" windows :Both (\"b\" balance-windows \"balance\") (\"s\" switch-window-then-swap-buffer \"swap\") :Vertical_adjustment (\"h\" enlarge-window \"heighten\") (\"l\" shrink-window \"lower\") :Horizontal_adjustment (\"n\" shrink-window-horizontally \"narrow\") (\"w\" enlarge-window-horizontally \"widen\"))" (let* ((name (intern (concat "my/hydra/" (if (stringp title) title (plist-get title :title))))) (icon-face `(:foreground ,(face-background 'highlight))) (iconised-title (concat (when icon-name (require 'all-the-icons) (concat (all-the-icons-faicon (format "%s" icon-name) :face icon-face :height 1.0 :v-adjust -0.1) " ")) (propertize title 'face icon-face)))) `(global-set-key (kbd ,key) (pretty-hydra-define ,name ,(if (stringp title) (list :title iconised-title :quit-key "q") title) ,(thread-last body (-partition-by-header #'keywordp) (--map (cons (s-replace "_" " " (s-chop-prefix ":" (symbol-name (car it)))) (list (cdr it)))) (-flatten-n 1)))))) #+end_src #+end_details *** Textual Navigation ---“Look Ma, no CTRL key!” :PROPERTIES: :CUSTOM_ID: Textual-Navigation-Look-Ma-no-CTRL-key :END: Basic movement commands ---without having to hold the control key! =C-n= moves us to the next line, as it should; but it now /also/ let's us press =n,p,f,…= for other movement commands. Unlisted keys insert themselves, whereas ~q~ close the pop-up menu. #+begin_src emacs-lisp :tangle init.el (my/defhydra "C-n" "\t\t\t\t\tTextual Navigation" arrows :Line ("n" next-line) ("p" previous-line) ("a" beginning-of-line) ("e" move-end-of-line) ("g" goto-line) :Word ("f" forward-word "Next") ("b" backward-word "Previous") ("{" org-backward-element "Next Element") ("}" org-forward-element "Previous Element") :Screen ("v" scroll-up-command "Scroll Down") ("V" scroll-down-command "Scroll Up") ("l" recenter-top-bottom "Center Page") ("r" move-to-window-line-top-bottom "Relocate Point") ("m" helm-imenu "Textual Menu")) #+end_src # (:body-pre (next-line) :title "\t\t\t\t\tTextual Navigation" :quit-key "q") /Along with the “pop-up window”, this is a useful way to (re)learn about Emacs' features./ For “key-based navigation”, consider ‘EVIL-mode’ or ‘Spacemacs’, or [[#Letter-based-Navigation][ace-jump-mode]] (below). Also … #+BEGIN_SRC emacs-lisp :tangle init.el ;; C-n, next line, inserts newlines when at the end of the buffer (setq next-line-add-newlines t) #+END_SRC *** Window Navigation :PROPERTIES: :CUSTOM_ID: Window-Navigation :END: It can be difficult to remember the incantations to adjust windows, so we can make a hydra to alleviate the trouble. #+begin_src emacs-lisp :tangle init.el ;; Use ijkl to denote ↑←↓→ arrows. (my/defhydra "C-c w" "\t\tWindow Adjustment" windows :Both ("b" balance-windows "balance") ("s" switch-window-then-swap-buffer "swap") :Vertical_adjustment ("h" enlarge-window "heighten") ("l" shrink-window "lower") :Horizontal_adjustment ("n" shrink-window-horizontally "narrow") ("w" enlarge-window-horizontally "widen")) ;; Provides a *visual* way to choose a window to switch to. ;; (use-package switch-window ) ;; :bind (("C-x o" . switch-window) ;; ("C-x w" . switch-window-then-swap-buffer)) ;; Have a thick ruler between vertical windows (window-divider-mode) #+end_src ** Helpful Utilities & Shortcuts :PROPERTIES: :CUSTOM_ID: Helpful-Utilities-Shortcuts :END: Let's save a few precious seconds, #+begin_src emacs-lisp :tangle init.el ;; change all prompts to y or n (fset 'yes-or-no-p 'y-or-n-p) ;; Make RETURN key act the same way as “y” key for “y-or-n” prompts. ;; E.g., (y-or-n-p "Happy?") accepts RETURN as “yes”. (define-key y-or-n-p-map [return] 'act) ;; Enable all ‘possibly confusing commands’ such as helpful but ;; initially-worrisome “narrow-to-region”, C-x n n. (setq-default disabled-command-function nil) #+end_src ** Quickly pop-up a terminal, run a command, close it ---and zsh :PROPERTIES: :CUSTOM_ID: Quickly-pop-up-a-terminal-run-a-command-close-it-and-zsh :END: /Pop up a terminal, do some work, then close it using the same command./ #+begin_src emacs-lisp (use-package vterm) ;; Shell with a nearly universal compatibility with terminal applications 💝 ;; "Intelligent" switching to vterm; eg creates it if it's not open, non-intrusive windowing, saves window setup, etc. (use-package vterm-toggle :bind* ("C-t" . vterm-toggle)) #+end_src *** Shell-pop :Disabled:details: [[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 :tangle no (use-package shell-pop :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 # There are also a number of hooks for shell-pop. E.g., to unconditionally kill the buffer afterwards. *** Oh My Zsh :details: 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 (unless noninteractive (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 ;-) *** tldr Also, there's the [[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 𝒳. ( In Emacs, [[kbd:C-t tldr 𝒳 ⟨return⟩]]. ) #+BEGIN_SRC emacs-lisp (system-packages-ensure "tldr") #+END_SRC * Literate Programming :PROPERTIES: :CUSTOM_ID: Literate-Programming :END: Org-mode lets us run chunks of code anywhere, then feed their outputs to other chunks of code in /possibly different/ programming languages: Org is a meta-(programming language). Importantly, this means we can write text and whenever we need the result of some computation, we can place it there and then and only request its result appear in PDF/HTML export. The result is a single document. ( There is the [[https://github.com/minad/org-modern][org-modern]] package, which provides a modern look-and-feel: It makes Org look less like a markup and more like a word editor. Nice stuff. ) ** High Speed Literate Programming :PROPERTIES: :CUSTOM_ID: High-Speed-Literate-Programming :END: *** Manipulating Sections :PROPERTIES: :CUSTOM_ID: Manipulating-Sections :END: #+BEGIN_SRC emacs-lisp (setq org-use-speed-commands t) #+END_SRC This enables the [[http://notesyoujustmightwanttosave.blogspot.com/2011/12/org-speed-keys.html][Org Speed Keys]] so that when the cursor is at the beginning of a headline, we can perform fast manipulation & navigation using the standard Emacs movement controls, such as: + [[kbd:#]] toggle ~COMMENT~-ing for an org-header. + [[kbd:s]] toggles [[doc:narrow-to-region][“narrowing”]] to a subtree; i.e., hide the rest of the document. If you narrow to a subtree then any export, kbd:C-c_C-e, will joyously only consider the narrowed detail. + [[kbd:u]] for going to upwards to parent heading * [[kbd:i]] insert a new same-level heading below current heading. + [[kbd:c]] for cycling structure below current heading, or ~C~ for cycling global structure. + [[kbd:w]] refile current heading; options list pops-up to select which heading to move it to. Neato! - [[kbd:g]] to go to another heading, without refiling anything. #+begin_src emacs-lisp ;; When refiling, only show me top level headings [Default]. Sometimes 2 is useful. ;; When I'm refiling my TODOS, then give me all the freedom. (setq org-refile-targets '((nil :maxlevel . 1) (org-agenda-files :maxlevel . 9))) ;; Maybe I want to refile into a new heading; confirm with me. (setq org-refile-allow-creating-parent-nodes 'confirm) ;; Use full outline paths for refile targets ;; When refiling, using Helm, show me the hierarchy paths (setq org-outline-path-complete-in-steps nil) (setq org-refile-use-outline-path 'file-path) #+end_src + kbd:n/p for next/previous /visible/ heading. + kbd:f/b for jumping forward/backward to the next/previous /same-level/ heading. + kbd:D/U move a heading down/up. + kbd:L/R recursively promote (move leftwards) or demote (more rightwards) a heading. + kbd:I/O clock In/Out to the task defined by the current heading. - Keep track of your work times! - [[kbd:v]] view agenda. + kbd:t/,/:/e to add a TODO state, priority level, tag, or effort estimate * kbd:1/2/3 to mark a heading with priority, highest to lowest. + [[kbd:^]] sort children of current subtree; brings up a list of sorting options. + kbd:k/@/a to kill or mark or archive the current subtree + [[kbd:o]] to open a link mentioned in the subtree then go to the link; a pop-up of links appears. We can add our own speed keys by altering the doc:org-speed-commands association list variable; e.g., #+begin_src emacs-lisp ;; TODO FIXME Crashes upon startup. (when nil (add-to-list 'org-speed-commands (cons "P" #'org-set-property))) ;; Use ‘:’ and ‘e’ to set tags and effort, respectively. #+end_src # To see the commands available, execute ~M-x org-speed-command-help~. | ⇒ Moreover, [[kbd:?]] to see a complete list of keys available. ⇐ | *** Seamless Navigation Between Source Blocks :PROPERTIES: :CUSTOM_ID: Seamless-Navigation-Between-Source-Blocks :END: The “super key” ---aka the command or windows key--- can be used to jump to the previous, next, or toggle editing org-mode source blocks. #+begin_src emacs-lisp ;; Overriding keys for printing buffer, duplicating gui frame, and isearch-yank-kill. ;; (require 'org) (use-package emacs :bind (:map org-mode-map ("s-p" . org-babel-previous-src-block) ("s-n" . org-babel-next-src-block) ("s-e" . org-edit-special) :map org-src-mode-map ("s-e" . org-edit-src-exit))) #+end_src # Interestingly, ~s-l~ is “goto line”. Note that we could have bound kbd:⌘+e to doc:org-edit-src-code / doc:org-edit-src-exit, but instead chose the more general doc:org-edit-special since, well, look at the tooltip documentation: This allows us to use kbd:⌘+e to ‘e’dit all kinds of Org entities ---including footnotes and export blocks. ( Footnotes can be quickly produced with doc:org-footnote-new. ) *** Modifying [[kbd:⟨return⟩]] :PROPERTIES: :CUSTOM_ID: Modifying-return :END: + [[kbd:C-⟨return⟩]] , [[kbd:C-S-⟨return⟩]] make a new heading where the latter marks it as a ~TODO~. + By default [[kbd:M-⟨return⟩]] makes it easy to work with existing list items, headings, tables, etc by creating a new item, heading, etc. Usually we want a newline then we indent, let's make that the default. #+BEGIN_SRC emacs-lisp (add-hook 'org-mode-hook '(lambda () (local-set-key (kbd "") 'org-return-indent)) (local-set-key (kbd "C-M-") 'electric-indent-just-newline)) #+END_SRC Notice that I've also added another kind of return, for when I want to break-out of the indentation approach and start working at the beginning of the line. In summary: | key | method | behaviour | |------------------+---------------------------------------------+-----------------------------------| | [[kbd:⟨return⟩]] | doc:org-return-indent | Newline with indentation | | [[kbd:M-⟨return⟩]] | doc:org-meta-return | Newline with new org item | | [[kbd:C-M-⟨return⟩]] | doc:electric-indent-just-newline | Newline, cursor at start | | [[kbd:C-⟨return⟩]] | doc:org-insert-heading-respect-content | New heading /after/ current content | | [[kbd:C-S-⟨return⟩]] | doc:org-insert-todo-heading-respect-content | Ditto, but with a ~TODO~ marker | *** COMMENT   ~C-a,e,k~ and Yanking of sections :Does_not_work: :PROPERTIES: :CUSTOM_ID: C-a-e-k-and-Yanking-of-sections :END: On an org-heading, kbd:C-a goes to /after/ the star, heading markers. To use speed keys, run kbd:C-a_C-a to get to the star markers. kbd:C-e goes to the end of the heading, not including the tags. #+begin_src emacs-lisp (setq org-special-ctrl-a/e t) #+end_src kbd:C-k no longer removes tags, if activated in the middle of a heading's name. #+begin_src emacs-lisp (setq org-special-ctrl-k t) ;; MA: Does not work …! #+end_src When you yank a subtree and paste it alongside a subtree of depth /‘d’/, then the yanked tree's depth is adjusted to become depth /‘d’/ as well. If you don't want this, then refile instead of copy-pasting. #+begin_src emacs-lisp (setq org-yank-adjusted-subtrees t) #+end_src ** Executing code from ~src~ blocks :PROPERTIES: :CUSTOM_ID: Executing-code-from-src-blocks :END: For example, to execute a shell command in Emacs, write a ~src~ with a shell command, then ~C-c c-c~ to see the results. Emacs will generally query you to ensure you're confident about executing the (possibly dangerous) code block; let's stop that: #+BEGIN_SRC emacs-lisp :tangle init.el ;; Seamless use of babel: No confirmation upon execution. ;; Downside: Could accidentally evaluate harmful code. (setq org-confirm-babel-evaluate nil) ;; Never evaluate code blocks upon export and replace results when evaluation does occur. ;; For a particular language 𝑳, alter ‘org-babel-default-header-args:𝑳’. (setq org-babel-default-header-args '((:results . "replace") (:session . "none") (:exports . "both") (:cache . "no") (:noweb . "no") (:hlines . "no") (:tangle . "no") (:eval . "never-export"))) #+END_SRC # A worked out example can be obtained as follows: ~" . company-complete-selection) ;; Use C-n,p for navigation in addition to M-n,p ("C-n" . (lambda () (interactive) (company-complete-common-or-cycle 1))) ("C-p" . (lambda () (interactive) (company-complete-common-or-cycle -1))))) ;; It's so fast that we don't need a key-binding to start it! #+end_src Note that ~M-/~ goes through a sequence of completions ---and ~C-/~ manually begins company mode at point. Besides the arrow keys, we can also use ~M-~ with ~n, p~ to navigate the options /or/ use ~C-s~ to search the list of suggestions. + Company backends are available as separate packages. + Note that [[https://github.com/company-mode/company-mode/issues/360][by default]] company mode does not support completion for phrases containing hyphens ---this can be altered, if desired. Let's add a test for some basic features of completition: #+begin_src emacs-lisp :tangle init-test.el (ert-deftest company-works-as-expected-in-text-mode () :tags '(company) (switch-to-buffer "*TESTING COMPANY MODE ~ Text*") ;; Ensure we have a clear buffer, and enter some text. (Namely, Python code). (erase-buffer) (insert "\n def first(x): pass") (insert "\n def fierce(a, b): pass") ;; Completion anything mode is enabled by default. (should company-mode) ;; There are 2 completion candidates: The two we wrote. (insert "\n fi") (company-manual-begin) ;; i.e., C-/ (should (equal company-candidates '("fierce" "first"))) ;; [fi ↦ fierce] Default option is the most recently matching written word. (insert "\n fi") (execute-kbd-macro (kbd "C-/ ")) (should (looking-back "fierce")) ;; [fi ↦ first] We can use M-2 to select the candidate “first” (insert "\n fi") (execute-kbd-macro (kbd "C-/ M-2")) (should (looking-back "first")) (kill-buffer)) ;; Let's enter Python mode and see how things change (ert-deftest company-shows-keywords-alongside-completions-alphabetically () :tags '(company) (switch-to-buffer "*TESTING COMPANY MODE ~ Python*") (python-mode) ;; Ensure we have a clear buffer, and enter some text. (Namely, Python code). (erase-buffer) (insert "\n def first(x): pass") (insert "\n def fierce(a, b): pass") ;; There are 3 completion candidates: The two we wrote, & the third being a Python keyword. (insert "\n fi") (company-manual-begin) (should (equal company-candidates '("fierce" "first" #("finally" 0 7 (company-backend company-keywords))))) ;; Candidates are shown alphabetically: M-2 yields “finally”. (execute-kbd-macro (kbd "C-g C-/ M-2")) ;; Quit and restart the completion, to get to starting position, then M-2. (should (looking-back "finally")) (kill-buffer)) #+end_src We do ~M-: (ert '(tag company))~ to run these specific tests; better would be to call these (and all) tests with a nifty name: #+begin_src emacs-lisp (defun my/ensure-machine-works-as-expected () "Run all my personal tests to ensure Emacs behaves as I expect it to." (interactive) (load-file "init-test.el") (ert t) (ert-results-pop-to-timings)) #+end_src Anyhow, besides boring word completion, let's add support for [[https://github.com/dunn/company-emoji][emojis]]. #+begin_src emacs-lisp (use-package company-emoji :config (add-to-list 'company-backends 'company-emoji)) #+end_src For example: 🥞 💻 🐵 ✉️😉 🐬 🌵. ➡️On a new line, write ~:~ then any letter to have a tool-tip appear. All emoji names are lowercase. ◀ + On MacOS, ~C-⌘-SPC~ brings up an emoji picker, where one drags desired emojis to textual areas. + [[https://gist.github.com/rxaviers/7360908][Here]] is a list of emoji ---all supported by Github. The libraries ~emojify~ and ~emojify-logos~ provides cool items like =:haskell: :emacs: :org: :ruby: :python:=. Unfortunately they do not easily export to html with org-mode, so I'm not using them. :Alternatives: #+BEGIN_SRC emacs-lisp (use-package emojify :config (setq emojify-display-style 'image) :init (global-emojify-mode 1)) ;; Will install missing images, if need be. #+END_SRC (use-package emojify-logos :after emojify) For example, #+begin_src emacs-lisp :tangle no :emacs: :haskell: :org: :c: :wink: :ruby: :python: :c: :html5: #+end_src Yields :emacs: :haskell: :org: :c: :wink: :ruby: :python: :c: :html5:. Run ~(emojify-insert-emoji)~ to see more. Rather than remember all of these, let's install [[https://github.com/syohex/emacs-ac-emoji][ac-emoji]] for completion of emoji names. (use-package ac-emoji :init (ac-emoji-setup) ) :End: **** Documentation Pop-Ups :PROPERTIES: :CUSTOM_ID: Documentation-Pop-Ups :END: Let [[https://github.com/expez/company-quickhelp][documentation pop-up]] when we pause on a completion. + For example, open an ELisp buffer and enter =s-j= and see a list of completions, including =s-join=, pause for a sec and a documentation tooltip appears. This is very useful when editing in a particular coding language, say via ~C-c '~ for org-src blocks. Or when working in a language-specific buffer. #+BEGIN_SRC emacs-lisp (use-package company-quickhelp :config (setq company-quickhelp-delay 0.1) (company-quickhelp-mode) ;; Especially when learning a new language, looking up its definition/docstring can be helpful. ;; Note: I use “M-!” everywhere else to mean “define word at point”. ) ;; TODO: Consider adding a hook to company mode to immediately call (company-quickhelp--show), in prog-modes #+END_SRC This ~M-!~ is from plain ~company~, but I wanted to define it here beside the quickhelp setup since they're related utilities. *** Intro to Snippets :PROPERTIES: :CUSTOM_ID: Intro-to-Snippets :END: A *snippet, template, mechanism* is a tool that when you press some keystrokes inserts some text, possibly with some fields (‘blanks’) to fill in. Possibly interesting read: + [[https://cupfullofcode.com/blog/2013/02/26/snippet-expansion-with-yasnippet/index.html][Snippet Expansion With Yasnippet: Save Yourself Keystrokes and Headaches]] ---a nice before introduction to Yasnippet (“Yet another snippet”) + [[https://jpace.wordpress.com/2012/10/20/tweaking-emacs-snippets/][Tweaking Emacs: Snippets]] ---a brief article on using snippets for uniformity across languages and to mitigate verbosity of weak languages (i.e., those without macros). -------------------------------------------------------------------------------- [[http://joaotavora.github.io/yasnippet/snippet-development.html][Yasnippet]] is a pleasant utility for template expansion with the alluring feature to allow arbitrary Lisp code to be executed during expansion. The declaration of templates is verbose, requiring a particular file hierarchy, as such I utilise [[https://github.com/Kungsgeten/yankpad][Yankpad]] which allows me to employ an Org-mode approach: Each template corresponds to an org heading of the form ~Key:Words:For:Expansion:Here: name of snippet here~ and the template body is then the body of the org heading. Any of ~Key, Words, For, Expansion, Here~ will rewrite into the body of the org tree. This is much more terse, and I even don't bother with that; instead preferring to tangle my templates using yankpad as a mere interface. It is important to note that Yankpad also provides features that are not in Yassnippet, such as allowing arbitrary language code to be executed ---one simply uses an org-src block! | [[https://www.philnewton.net/blog/exploring-emacs-yasnippet/][Here]] is a nice self-contained tutorial. | | Also, [[https://www.reddit.com/r/emacs/comments/o282fq/yasnippet_snippetstemplating_introductiontutorial/][YASnippet snippets/templating Introduction/Tutorial — Straightforward Emacs: emacs]] | There can only be one major completion backend for any mode, but other backends can serve as secondary ones. Here's a function to make ~company-yankpad~ a secondary of all existing backends. #+begin_src emacs-lisp ;; Add yasnippet support for all company backends ;; (cl-defun my/company-backend-with-yankpad (backend) "There can only be one main completition backend, so let's enable yasnippet/yankpad as a secondary for all completion backends. Src: https://emacs.stackexchange.com/a/10520/10352" (if (and (listp backend) (member 'company-yankpad backend)) backend (append (if (consp backend) backend (list backend)) '(:with company-yankpad)))) #+end_src #+begin_src emacs-lisp ;; Yet another snippet extension program (use-package yasnippet :config (yas-global-mode 1) ;; Always have this on for when using yasnippet syntax within yankpad ;; respect the spacing in my snippet declarations (setq yas-indent-line 'fixed)) ;; Alternative, Org-based extension program (use-package yankpad :config ;; Location of templates (setq yankpad-file "~/.emacs.d/yankpad.org") ;; Ignore major mode, always use defaults. ;; Yankpad will freeze if no org heading has the name of the given category. (setq yankpad-category "Default") ;; Load the snippet templates ---useful after yankpad is altered (yankpad-reload) ;; Set company-backend as a secondary completion backend to all existing backends. (setq company-backends (mapcar #'my/company-backend-with-yankpad company-backends))) #+end_src With these settings, along with the ~company~ backend, I may type a keyword then kbd:TAB it into expansion. Yankpad requires we have an org file that contains our templates, so we /tangle/ such a file ~~/.emacs.d/yankpad.org~, and have all of our templates be globally accessible. Here is the start of my file: #+BEGIN_SRC org :tangle "~/.emacs.d/yankpad.org" :comments none ,#+Description: This is file is generated from my init.org; do not edit. ,* Default :global: #+end_src #+begin_details Fully discussed example: Using the clipboard for Org-links Here's an example of a common template I perform by hand ---no more! I have the expected habit of /copying (to clipboard)/ a URL from someplace then forming a link to it by writing ~[[URL] [description]]~, since the URL & syntax are already known, let's expand those and place the cursour at the only unknown ---the description. #+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none ,** my_org_insert_link: cleverly insert a link copied to clipboard [[${1:`(clipboard-yank)`}][$2]] $0 #+end_src What's going on here? ( The above, verbatim: ~[[${1:`(clipboard-yank)`}][$2]] $0~. ) 0. This template is expanded with the keyword ~my-org-insert-link~, then kbd:TAB. 1. The cursour lands at position ~$1~, which has default text being the result of evaluating ~(clipboard-yank)~. # I've ‘documented’ this default as being the url. # If I leave out the ~$(clipboard-yank)~ part, the default would simply be ~url~ pasted in. We may evaluate Lisp code anywhere by enclosing it in backticks. # `(file-name-nondirectory (file-name-sans-extension (buffer-file-name)))` 2. If we're satisfied with the current field, we simply tab to the next field. Otherwise, we simply write text ---which overwrites the default text. 3. After enough tabbing we complete the template and the cursour lands at position ~$0~. ⟪ Having default or mirrored text for ~$2~ would not allow me to see the URL field, lest I wish to change it or at least confirm it's what I want. Hence, the ~$2~ field has no default. ⟫ Let's overwrite the usual way to insert such links, via ~C-c C-l~. #+BEGIN_SRC emacs-lisp (cl-defun org-insert-link () "Makes an org link by inserting the URL copied to clipboard and prompting for the link description only. Type over the shown link to change it, or tab to move to the description field. This overrides Org-mode's built-in ‘org-insert-link’ utility; whence C-c C-l uses the snippet." (interactive) (insert "my_org_insert_link") (yankpad-expand)) #+END_SRC #+end_details /Warning!/ Snippet names cannot have hypens in them ---in this setup at least. The [[http://joaotavora.github.io/yasnippet/snippet-development.html][Yasnippet manual]] is an accessible read, as is the [[https://github.com/Kungsgeten/yankpad][Yankpad manual]], and showcases many other utilities; such as having certain snippets being enabled only in particular modes or on demand. Of note is that field ~$n~ can be accessed in code with the invocation ~(yas-field-value n)~. Incidentally, I used this snippet setup to [[https://www.youtube.com/watch?v=NYOOF9xKBz8&feature=youtu.be][demo]] the idea of repetitious code in grouping constructs within dependently-typed languages, which was accepted and led to my doctoral research on a [[https://alhassy.github.io/next-700-module-systems/][‘do it yourself module system’]]. The rest of this section is other templates, not much for now, concluding with actually loading this snippet mechanism globally. The remaining subsections discuss contents of my yankpad file. *** Org-mode Templates ---A reason I “generate” templates ;) :PROPERTIES: :CUSTOM_ID: Org-mode-Templates-A-reason-I-generate-templates :END: This produces a pop-up list of org-mode block types, if ~src~ is selected, then a list of my commonly used languages pops-up. Alternatively, ignore the pop-up menu and write any block or language name. #+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none ,** begin: produce an org-mode block ,#+begin_${1:environment$(let* ((block '("src" "example" "quote" "verse" "center" "latex" "html" "ascii")) (langs '("c" "emacs-lisp" "lisp" "latex" "python" "sh" "haskell" "plantuml" "prolog")) (type (yas-choose-value block))) (concat type (when (equal type "src") (concat " " (yas-choose-value langs)))))} $0 ,#+end_${1:$(car (split-string yas-text))} #+end_src In this case, ~yas-text~ is equivalent to (~yas-field-value 1)~; it generally refers to the value of the field being mirrored with ~${n: ⋯yas-text⋯}~. However, going through pop-ups takes precious time ---besides being slightly annyoing. Let's introduce a template for my most utilised kind of language blocks. #+begin_example ,** s_org: src block for org ,#+begin_src org $0 ,#+end_src #+end_example However, doing this for each language I want is a waste of time and textual space. Why? *The purpose of templates is to reduce repetition,* yet the above block would be repeated with only 3 parts ‘unknown’: The expansion keyword, the description, and the org-mode source block name. Whence, the template /text/ is generated by the following basic loop ---whose source block is named ~my-org-lang-templates~. #+name: my-org-lang-templates #+begin_src emacs-lisp :tangle no :wrap "src org :tangle ~/.emacs.d/yankpad.org" :exports code :results replace drawer ;; We make an org BLOCK snippet template for each LANG the user has declared. ;; (cl-loop for (shortcut block takes-language-argument? default-text) in '(("s_" "src" t) ("is_" "inline source" t) ;; Treated specially below ("e_" "example" t) ("q_" "quote") ("v_" "verse") ("c_" "center") ("ex_" "export") ;; only HTML and LATEX ;; https://alhassy.github.io/org-special-block-extras/#Summary ("p_" "parallel" nil "\n$0\n#+columnbreak:\n") ("d_" "details" nil "${1:title}\n$0") ("ed_" "edcomm" nil "${1:editor}\n$0") ("doc_" "documentation" nil "${1: mandatory entry name}\n$0") ("def_" "latex-definitions")) for languages = (if takes-language-argument? (-cons* "org" "agda2" "any" ;; Extra ‘languages’ ;; Also include whatever languages we've loaded for literate programming. (--map (symbol-name (car it)) org-babel-load-languages)) '("")) ;; The “empty language” concat (cl-loop for lang in languages for key = (concat shortcut (if (s-blank? lang) block lang)) for description = (if (s-blank? lang) block (concat block " for " lang)) concat (if (equal "is_" shortcut) (concat "\n** " key ": " description "\nsrc_" lang "[:exports code]{$1} $0") (concat "\n** " key ": " description "\n#+begin_" block " " lang (or default-text "\n$0") "\n#+end_" block "\n")))) #+end_src The /resulting text/ of this block, generated below, is tangled to our yankpad by utilising a [[https://www.gnu.org/software/emacs/manual/html_node/org/Noweb-reference-syntax.html][noweb]] source block invocation. An example of the resulting text is the above ~s_org~ block. The result is (last I checked) *83* template expansions ---that would have been a bit much to write by hand. #+begin_example org ,#+begin_src org :tangle "~/.emacs.d/yankpad.org" :noweb yes <> ,#+end_src #+end_example # ActuallyDoIt #+begin_src org :tangle "~/.emacs.d/yankpad.org" :noweb yes :comments none <> #+end_src # The “:eval never-export” means that this block is never tangled on document # export, C-c C-e. #+begin_box Now ~s_~, due to company mode, brings up a list of languages that I can then scroll down through, then “enter” upon to expand. Moreover, the prefix ~s_~ means that the key is mostly irrelevant, since I needn't remember it because company-mode immediately lists possible completions /along/ with the /descriptions/ for the snippets. Likewise for examples with ~e_~ or quotes with ~q_~. Super neat stuff :-) Ain't this reminiscent of meta-programming ;-) #+end_box Using =noweb= invocations, any time the tangling is performed, the yankpad is kept up to date ---no personal intervention from myself. # Neat, but not what I want. # https://github.com/abo-abo/auto-yasnippet :Fun_albeit_useless_exercise: Let's push this frontier a bit more … In expressive languages like Agda, one can not only be type polymorphic but also ‘level polymorphic’ ---since types constitute a hierarchy where a ‘type’ is uninterestingly an ‘element’ of a higher ‘type’, ad infinitum. For example, the type of a level polymorphic ′choice’ function would be ~{a : Level} (A : Set a) → A~ ---note that such a choice function cannot exist since for any type ~A~ it returns an element of ~A~, then what of the empty type. Anyhow, the template ~{${1:a} : Level} → (${2:A} : Set $1) → $0~ would suffice to make this happen. Yet, what if we wanted /n/-many sets? Make a function that takes /n ≤ 26/ as input, produces a list of levels, then uses each level to produce a list of type names ;-) :End: With the advent of org-special-block-extras, I've made increased usage of links --such as ~green:hello~ which yields green:hello and ~[[kbd:][green]]~ which yields [[kbd:][green]]. #+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none ,** ll_make_a_link: insert a link template ${1:`(let* ((τ (read-string "Link type: ")) (δ (read-string "Link Description: ")) (⊤ (if (s-contains? ":" τ) τ (s-concat τ ":")))) (format "[[%s][%s]]" ⊤ δ))`} $0 #+END_SRC *** Operating System Keyboard Symbols :PROPERTIES: :CUSTOM_ID: Operating-System-Keyboard-Symbols :END: Write ~os_~ then see a bunch of completions ;-) #+name: my-snippets-os-symbols #+begin_src emacs-lisp :tangle no :wrap "src org :tangle ~/.emacs.d/yankpad.org" :exports code :results replace drawer (cl-loop for (name expansion) in '((os-command ⌘) (os_option ⌥) (os_alt ⌥) (os_control ⌃) (os_shift ⇧) (os_backspace ⌫) (os_delete ⌫) (os_delete_forward ⌦) (os_enter ⏎) (os_return ⏎) (os_escape ⎋) (os_tab_right ⇥) (os_tab_left ⇤) (os_caps_lock ⇪) (os_eject ⏏)) concat (format "** %s: %s Operating System Keyboard Symbol\n%s\n" name expansion expansion)) #+end_src #+begin_src org :tangle "~/.emacs.d/yankpad.org" :noweb yes :comments none <> #+end_src *** Work Templates :PROPERTIES: :CUSTOM_ID: Work-Templates :END: #+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none ,** ll_console_log: Log some JS variables console.log("%c ******* LOOK HERE *******", "color: green; font-weight: bold;"); console.log({ ${1:List the variables here whose values you want to log} }); $0 ,** uuidgen: Insert the result of “uuidgen” and copy it to the clipboard ${1:`(-let [it (shell-command-to-string "uuidgen | tr '[:upper:]' '[:lower:]' | pbcopy; pbpaste")] (message "Copied to clipboard, uuid: %s" it) it)`} #+end_src *** Elisp Templates :PROPERTIES: :CUSTOM_ID: Elisp-Templates :END: The following snippets were rather useful as I began learning Lisp to construct my editor of choice ---I love Emacs so much. Admittedly, I still need the first one below and usually beat around the bush by using ~(cl-loop for ⋯ do ⋯)~, (doc:cl-loop), which is ‘noisier’ but easier to remember and to read for non-Lispers. #+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none ,** loop: Elisp's for each loop (dolist (${1:var} ${2:list-form}) ${3:body}) ,** defun: Lisp functions (cl-defun ${1:fun-name} (${2:arguments}) "${3:documentation}" $0) ,** cond: Elisp conditionals (cond (${1:scenario₁} ${2:response₁}) (${3:scenario₂} ${4:response₂})) #+end_src *** Equational Templates :PROPERTIES: :CUSTOM_ID: Equational-Templates :END: To show ~ℒ = ℛ~, one starts at the complicated side, say /ℒ/, then, with the aim of simplification, tries to end at the simpler side, /𝓡/. Along the way, one justifies each step of the calculation. This approach is popular in the proof assistant Agda; [[https://alhassy.github.io/PathCat/][Examples]]. Read more about [[http://www.mathmeth.com/][informal calculational proofs]]. #+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none ,** fun: Function declaration with type signature ${1:fun-name} : ${2:arguments} $1 ${3:args} = ?$0 ,** eqn_begin: Start a ≡-Reasoning block in Agda begin ${1:complicated-side} $0≡⟨ ${3:reason-for-the-equality} ⟩ ${2:simpler-side} ∎ ,** eqn_step: Insert a step in a ≡-Reasoning block in Agda ≡⟨ ${2:reason-for-the-equality} ⟩ ${1:new-expression} $0 #+end_src One expands ~eqn_begin~, tabs to fill in the three main locations, then /immediately/ types ~eqn_step~ to produce a new step in a calculational proof. *** Fixed replies :PROPERTIES: :CUSTOM_ID: Fixed-replies :END: Here are some replies that I sometimes need to produce; e.g., to people who insist their way is the right way. #+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none ,** reply_opinionated_pantomath: What to say to, e.g., an arrogant academic Your certainty inspires me to continuing exploring, and I may arrive at your point of view, but I'm going to need more evidence first. ,** reply_em_dashes: Why use em dashes for parenthetical remarks? According to the “Canadian Style Guide” (CSG): The em is an expansive, attention-seeking dash. It supplies much stronger emphasis than the comma, colon or semicolon it often replaces. Positioned around interrupting elements, em dashes have the opposite effect of parentheses—em dashes emphasize; parentheses minimize. From “A Logical Approach to Discrete Math” (LADM), page ix: We place a space on one side of an em dash ---here are examples--- in order to help the reader determine whether the em dash begins or ends a parenthetical remark. In effect, we are creating two symbols from one. In longer sentences---and we do write long sentences from time to time---the lack of space can make it difficult to see the sentence structure---especially if the em dash is used too often in one sentence. Parenthetical remarks delimited by parentheses (like this one) have a space on one side of each parenthesis, so why not parenthetical remarks delimited by em dashes? Interestingly, according to the CSG, there should be no space before or after an em dash. As such, it appears that the spacing is mostly stylistic; e.g., some people surround em-s with spaces on both sides. In particular, when em-s are unmatched, I make no use of additional space ---indeed this form of one-sided parentheses without a space is how LADM is written, as can be seen at the top of page 3. #+end_src *** Misc Templates :PROPERTIES: :CUSTOM_ID: Misc-Templates :END: #+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none ,** remark: top-level literate comment {{{remark(${1:thoughts})}}} $0 #+end_src Where my local use contains ~#+MACRO: remark @@latex: \fbox{\textbf{Comment: $1 }}@@~. *** Emojis :Disabled: :PROPERTIES: :CUSTOM_ID: emojis :END: #+NAME: my-emoji-templates #+begin_src emacs-lisp :tangle no :wrap "src org :tangle ~/.emacs.d/yankpad.org" :exports code :results replace drawer ;; ;; https://emojipedia.org/people/ (cl-loop for (emoji name description) in '((😀 "Grinning Face" "Often conveys general pleasure and good cheer or humor.") (😃 "Grinning Face with Big Eyes" "Often conveys general happiness and good-natured amusement. Similar to 😀 Grinning Face but with taller, more excited eyes.") (😄 "Grinning Face with Smiling Eyes" "Often conveys general happiness and good-natured amusement. Similar to 😀 Grinning Face and 😃 Grinning Face With Big Eyes, but with warmer, less excited eyes.") (😁 "Beaming Face with Smiling Eyes" "Often expresses a radiant, gratified happiness. Tone varies, including warm, silly, amused, or proud.") (😆 "Grinning Squinting Face" "Often conveys excitement or hearty laughter. Similar to 😀 Grinning Face but with eyes that might say ‘Squee!’ or ‘Awesome!’ An emoji form of the >< or xD emoticons.") (😅 "Grinning Face with Sweat" "Intended to depict nerves or discomfort but commonly used to express a close call, as if saying ‘Whew!’ and wiping sweat from the forehead. ") (🤣 "Rolling on the Floor Laughing" "Often conveys hysterical laughter more intense than 😂 Face With Tears of Joy.") (😂 "Face with Tears of Joy") (🙂 "Slightly Smiling Face") (🙃 "Upside-Down Face") (😉 "Winking Face") (😊 "Smiling Face with Smiling Eyes") (😇 "Smiling Face with Halo") (🥰 "Smiling Face with Hearts") (😍 "Smiling Face with Heart-Eyes") (🤩 "Star-Struck") (😘 "Face Blowing a Kiss") (😗 "Kissing Face") (☺️ "Smiling Face") (😚 "Kissing Face with Closed Eyes") (😙 "Kissing Face with Smiling Eyes") (🥲 "Smiling Face with Tear") (😋 "Face Savoring Food") (😛 "Face with Tongue") (😜 "Winking Face with Tongue") (🤪 "Zany Face") (😝 "Squinting Face with Tongue") (🤑 "Money-Mouth Face") (🤗 "Hugging Face") (🤭 "Face with Hand Over Mouth") (🤫 "Shushing Face") (🤔 "Thinking Face") (🤐 "Zipper-Mouth Face") (🤨 "Face with Raised Eyebrow") (😐 "Neutral Face") (😑 "Expressionless Face") (😶 "Face Without Mouth") (😏 "Smirking Face") (😒 "Unamused Face") (🙄 "Face with Rolling Eyes") (😬 "Grimacing Face") (🤥 "Lying Face") (😌 "Relieved Face") (😔 "Pensive Face") (😪 "Sleepy Face") (🤤 "Drooling Face") ("Sleeping Face") (😷 "Face with Medical Mask") (🤒 "Face with Thermometer") (🤕 "Face with Head-Bandage") (🤢 "Nauseated Face") (🤮 "Face Vomiting") (🤧 "Sneezing Face") (🥵 "Hot Face") (🥶 "Cold Face") (🥴 "Woozy Face") (😵 "Dizzy Face") (🤯 "Exploding Head") (🤠 "Cowboy Hat Face") (🥳 "Partying Face") (🥸 "Disguised Face") (😎 "Smiling Face with Sunglasses") (🤓 "Nerd Face") (🧐 "Face with Monocle") (😕 "Confused Face") (😟 "Worried Face") (🙁 "Slightly Frowning Face") (☹️ "Frowning Face") (😮 "Face with Open Mouth") (😯 "Hushed Face") (😲 "Astonished Face") (😳 "Flushed Face") (🥺 "Pleading Face") (😦 "Frowning Face with Open Mouth") (😧 "Anguished Face") (😨 "Fearful Face") (😰 "Anxious Face with Sweat") (😥 "Sad but Relieved Face") (😢 "Crying Face") (😭 "Loudly Crying Face") (😱 "Face Screaming in Fear") (😖 "Confounded Face") (😣 "Persevering Face") (😞 "Disappointed Face") (😓 "Downcast Face with Sweat") (😩 "Weary Face") (😫 "Tired Face") (🥱 "Yawning Face") (😤 "Face with Steam From Nose") (😡 "Pouting Face") (😠 "Angry Face") (🤬 "Face with Symbols on Mouth") ) for nom = (s-replace " " "_" name) for desc = (s-collapse-whitespace (or description "")) concat (concat ;; f_… ⇒ get emoji from company menu showing only name & emoji (format "\n** f_%s: %s %s \n%s" nom emoji "" emoji) ;; fd_… ⇒ get emoji from company menu showing name, emoji, & ‘d’escription (format "\n** fd_%s: %s %s \n%s" nom emoji desc emoji))) #+end_src #+begin_src emacs-lisp ;; Get all unicode emojis to appear within Emacs ;; See also: https://emacs.stackexchange.com/questions/5689/force-a-single-font-for-all-unicode-glyphs?rq=1 (unless noninteractive (set-fontset-font t nil "Apple Color Emoji")) #+end_src ## ## #+begin_src org :tangle "~/.emacs.d/yankpad.org" :noweb yes :exports none ## <> ## #+end_src ## ***   =my_⋯= Templates to obtain User Information :PROPERTIES: :CUSTOM_ID: my-Templates-to-obtain-User-Information :END: Let's add templates for links to common user information ^_^ #+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none ,** my_name: User's name `user-full-name` ,** my_email: User's email address `user-mail-address` ,** my_github: User's Github repoistory link https://github.com/alhassy/ ,** my_emacsdrepo: User's version controlled Emacs init file https://github.com/alhassy/emacs.d ,** my_blog: User's blog website https://alhassy.github.io/ ,** my_webpage: User's organisation website http://www.cas.mcmaster.ca/~alhassm/ ,** my_twitter: User's Twitter profile https://twitter.com/musa314 ,** my_masters_thesis A Mechanisation of Internal Galois Connections In Order Theory Formalised Without Meets https://macsphere.mcmaster.ca/bitstream/11375/17276/2/thesis.pdf #+end_src It may be useful to also have Org-link variants of these … *** Templates from other places in my init :PROPERTIES: :CUSTOM_ID: Activate-templates-from-other-places-in-my-init :END: In this setup, I have some templates appear elsewhere, tagged with =:noweb-ref templates-from-other-places-in-my-init=. They are presented in natural positions, but can only occur to the machine after template expansion is setup. Using org-mode, we are able to /present/ code in any order and /tangle/ it to the order the compilers need it to be! Let's activate all such templates, now after template expansion has been setup. #+begin_src org :tangle no ,#+begin_src org :noweb yes :tangle "~/.emacs.d/yankpad.org" :comments none <> ,#+end_src #+end_src You can press kbd:C-c_C-v_C-v, doc:org-babel-expand-src-block, to see what this block expands into... #+begin_details Expansion # This is also live. #+begin_src org :noweb yes :tangle "~/.emacs.d/yankpad.org" :comments none <> #+end_src #+end_details Note: Since I've insisted that Org blocks are space sensative, any whitespace before the ~<<⋯>>~ will propogate to the resulting extracted code. #+begin_box Warning! :background-color red This section had #+begin_src org :tangle no :PROPERTIES: :CUSTOM_ID: Templates-from-other-places-in-my-init :END: #+end_src Which, as of Org 9.4, led to the entire section being tangled: This is what the above incantation requested, but I thought it only worked on src blocks, having the specified ~:noweb-ref~, not on ~:CUSTOM_ID:~ incidentally having the same name. #+end_box ** Prettify inline source code :PROPERTIES: :CUSTOM_ID: Prettify-inline-source-code :END: #+begin_src emacs-lisp ;; Show “ src_emacs-lisp[:exports results]{ 𝒳 } ” as “ ℰ𝓁𝒾𝓈𝓅﴾ 𝒳 ﴿ ”. ;; (font-lock-add-keywords 'org-mode '(("\\(src_emacs-lisp\\[.*]{\\)\\([^}]*\\)\\(}\\)" (1 '(face (:inherit (bold) :foreground "gray65") display "ℰ𝓁𝒾𝓈𝓅﴾")) (2 '(face (:foreground "blue"))) (3 '(face (:inherit (bold) :foreground "gray65") display "﴿")) ))) ;; ;; Let's do this for all my languages: ;; Show “ src_LANGUAGE[…]{ ⋯ } ” as “ ﴾ ⋯ ﴿ ”. (cl-loop for lang in my/programming-languages do (font-lock-add-keywords 'org-mode `(( ,(format "\\(src_%s\\[.*]{\\)\\([^}]*\\)\\(}\\)" lang) (1 '(face (:inherit (bold) :foreground "gray65") display "﴾")) (2 '(face (:foreground "blue"))) (3 '(face (:inherit (bold) :foreground "gray65") display "﴿")) )))) ;; (defun my/toggle-line-fontification () "Toggle the fontification of the current line" (interactive) (defvar my/toggle-fontify/current-line -1) (defvar my/toggle-fontify/on? nil) (add-to-list 'font-lock-extra-managed-props 'display) (let ((start (line-beginning-position)) (end (line-end-position))) (cond ;; Are we toggling the current line? ((= (line-number-at-pos) my/toggle-fontify/current-line) (if my/toggle-fontify/on? (font-lock-fontify-region start end) (font-lock-unfontify-region start end)) (setq my/toggle-fontify/on? (not my/toggle-fontify/on?))) ;; Nope, we've moved on to another line. (:otherwise (setq my/toggle-fontify/current-line (line-number-at-pos) my/toggle-fontify/on? :yes_please_fontify) (font-lock-unfontify-region start end))))) ;; TODO FIXME; maybe ignore: Wasted too much time here already. ;; (add-hook 'post-command-hook #'my/toggle-line-fontification nil t) ;; (font-lock-add-keywords nil '((my/toggle-line-fontification)) t) #+end_src ** Unfold Org Headings when I perform a search #+begin_src emacs-lisp (setq org-fold-core-style 'overlays) #+end_src * Staying Sane :PROPERTIES: :CUSTOM_ID: Staying-Safe :END: # See [[https://jeremykun.com/2020/01/14/the-communicative-value-of-using-git-well/][The Communicative Value of Using Git Well]] ** Dropbox & Mega :Disabled: :PROPERTIES: :CUSTOM_ID: COMMENT-Dropbox-Mega-Not-really-in-use :header-args: :tangle no :END: Install quick file sharing utility ---[[https://www.dropbox.com/install][Dropbox]]--- as well as a backup service, [[https://mega.nz/fm][Mega]] ---I use this for my =backups= directory: Keep reading. # +begin_src shell # brew install --cask dropbox # brew install --cask megasync # +end_src #+begin_src emacs-lisp (system-packages-ensure "dropbox") (system-packages-ensure "megasync") #+end_src ** Undo-tree: Very Local Version Control :PROPERTIES: :CUSTOM_ID: Undo-tree-Very-Local-Version-Control :END: doc:undo-tree-visualize, kbd:C-x_u, gives a /visual/ representation of the current buffer's edit history. #+BEGIN_SRC emacs-lisp :noweb yes :tangle no <> #+END_SRC ( We're just showing the ~<>~ from earlier since this is a good place for such a setup. More importantly, we are /not/ copy-pasting the setup: /It is written only once; in a single source of truth!/ ) #+begin_src emacs-lisp ;; By default C-z is suspend-frame, i.e., minimise, which I seldom use. (global-set-key (kbd "C-z") (lambda () (interactive) (undo-tree-mode) ;; Ensure the mode is on (undo-tree-visualize))) #+end_src ** Automatic Backups :PROPERTIES: :CUSTOM_ID: Automatic-Backups :END: 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. Finally, let's not create ~.#~ files (crashes 'npm') nor bother confirming killing processes. #+begin_src emacs-lisp (setq confirm-kill-processes nil create-lockfiles nil) #+end_src *** What changed? ---Walking through backups :PROPERTIES: :CUSTOM_ID: What-changed :END: Let's use an elementary diff system for backups: ~backup-walker~ essentially makes all our backups behave as if they were (implicitly) version controlled. #+BEGIN_SRC emacs-lisp (use-package backup-walker :commands backup-walker-start) #+END_SRC In a buffer that corresponds to a file, invoke doc:backup-walker-start to see a visual diff of changes /between/ versions; then ~n~ and ~p~ to move between diffs. 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. There is also doc:diff-backup for comparing a file with its backup. # To create backups explicitly use save-buffer (C-x C-s) with prefix arguments. # # =diff-backup= compares a file with its backup or vice versa. # But there is no function to restore backup files. *** Save ≈ Backup :PROPERTIES: :CUSTOM_ID: Save-Backup :END: 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) ;; [Default settings] ;; Autosave when idle for 30sec or 300 input events performed (setq auto-save-timeout 30 auto-save-interval 300) #+END_SRC # See the name of the latest backup, which is appeneded by a number for each save ;-) # (file-newest-backup "~/.emacs.d/init.org") 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. **   =magit= ---Emacs' porcelain interface to git :PROPERTIES: :CUSTOM_ID: magit-Emacs'-porcelain-interface-to-gitq :END: *** Intro :ignore: 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! (Personal reminder: If using 2FA [two factor authentication], then when you do git operations, such as ~git push~, [[https://webkul.com/blog/github-push-with-two-factor-authentication/][you must use your PAT]] [personal access token] /instead/ of your password! Also: Install [[https://github.com/sindresorhus/refined-github][refined-github: Browser extension that simplifies the GitHub interface and adds useful features]]!) #+BEGIN_SRC emacs-lisp ;; Bottom of Emacs will show what branch you're on ;; and whether the local file is modified or not. (use-package magit :init (require 'magit-files) :bind (("C-c M-g" . magit-file-dispatch)) :custom ;; Do not ask about this variable when cloning. (magit-clone-set-remote.pushDefault t)) #+END_SRC # :config (global-set-key (kbd "C-x g") 'magit-status) Why use ~magit~ as the interface to the git version control system? In a ~magit~ buffer nearly everything can be acted upon: Press =return=, or =space=, to see details and =tab= to see children items, usually. + kbd:C-x_g, doc:magit-status, gives you a nice buffer with an overview of the Git repo that you're buffer is currently visiting. + kbd:C-c_M-g, doc:magit-file-dispatch, lets you invoke Git actions on the current file directly; e.g., following up with [[kbd:b]]lame, [[kbd:l]]og, [[kbd:d]]iff, [[kbd:s]]tage, or [[kbd:c]]ommit the current file. For ease, above, we have also bound this to kbd:C-c_g ---reminiscent of kbd:C-x_g :smile: #+begin_src emacs-lisp ;; When we invoke magit-status, show green/red the altered lines, with extra ;; green/red on the subparts of a line that got alerted. (system-packages-ensure "git-delta") (use-package magit-delta :hook (magit-mode . magit-delta-mode)) ;; Don't forget to copy/paste the delta config into the global ~/.gitconfig file. ;; Copy/paste this: https://github.com/dandavison/delta#get-started #+end_src + Blame, doc:magit-blame, is super nice: The buffer gets annotations for each chunk of text, regarding who authoured it, when, and their commit title. Then [[kbd:q]] to quit the blame. Likewise, doc:magit-log-buffer-file is super neat! #+begin_details Super Simple ‘magit’ Mini-tutorial link-here:Super-Simple-‘magit’-Mini-tutorial 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. # The status buffer may be refereshed using ~g~, and all magit buffers by ~G~. - 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. # Use ~M-x magit-list-repositories RET~ to list local repositories: #+end_details #+begin_details [Disabled] Homemade ‘uncomitted changes’ Notification 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 :tangle no (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 :Lets_try_this_out: #+BEGIN_EXAMPLE emacs-lisp (progn (eshell-command "echo change-here >> ~/dotfiles/.emacs") (find-file "~/dotfiles/.emacs")) #+END_EXAMPLE :End: # In doubt, execute ~C-h e~ to jump to the ~*Messages*~ buffer. #+end_details *** Credentials: I am who I am :PROPERTIES: :CUSTOM_ID: Credentials-I-am-who-I-am :END: First, let's setup our git credentials. #+BEGIN_SRC emacs-lisp ;; Only set these creds up if there is no Git email set up ---ie at work I have an email set up, so don't ;; override it with my personal creds. ;; ;; 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.email ")) (shell-command (format "git config --global user.name \"%s\"" user-full-name)) (shell-command (format "git config --global user.email \"%s\"" user-mail-address))) #+END_SRC If we ever need to use Git in the terminal, it should be done with Emacs as the underlying editor #+BEGIN_SRC emacs-lisp ;; We want to reuse an existing Emacs process from the command line ;; E.g., emacsclient --eval '(+ 1 2)' # ⇒ 3 (server-start) ;; Or use it whenever we are editing a git message from the terminal (shell-command "git config --global core.editor 'emacsclient -t -a=\\\"\\\"'") #+END_SRC This will start a daemon if there is not already one running ---The ~-a~ option--- and opens a new Emacs frame on the current terminal ---The ~-t~ option. *** Encouraging useful commit messages :PROPERTIES: :CUSTOM_ID: Encouraging-useful-commit-messages :END: 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! *** Maybe clone ... everything? :PROPERTIES: :CUSTOM_ID: Maybe-clone-everything :END: Below are the git repos I'd like to clone ---along with a function to do so quickly. #+begin_src emacs-lisp ;; Clone git repo from clipboard (cl-defun maybe-clone (remote &optional local) "Clone a REMOTE repository [from clipboard] if the LOCAL directory does not exist. If called interactively, clone URL in clipboard into ~/Downloads then open in dired. 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." (interactive "P") (when (interactive-p) (setq remote (substring-no-properties (current-kill 0))) (cl-assert (string-match-p "^\\(http\\|https\\|ssh\\)://" remote) nil "No URL in clipboard")) (unless local (setq local (concat "~/" (if (interactive-p) "Downloads/" "") (file-name-base remote)))) ;; (require 'magit-repos) ;; Gets us the magit-repository-directories variable. ;; (add-to-list 'magit-repository-directories `(,local . 0)) (if (file-directory-p local) 'repo-already-exists (shell-command (concat "git clone " remote " " local)) (dired local) 'cloned-repo)) (maybe-clone "https://github.com/alhassy/emacs.d" "~/.emacs.d") (maybe-clone "https://github.com/alhassy/alhassy.github.io" "~/blog") (maybe-clone "https://github.com/alhassy/holy-books") #+end_src #+begin_details Many more repos to clone #+begin_src emacs-lisp (maybe-clone "https://github.com/alhassy/melpa") (maybe-clone "https://github.com/alhassy/org-special-block-extras") ;; (maybe-clone "https://github.com/alhassy/next-700-module-systems-proposal.git" "~/thesis-proposal") ;; (maybe-clone "https://github.com/JacquesCarette/MathScheme") ;; (maybe-clone "https://github.com/alhassy/gentle-intro-to-reflection" "~/reflection/") ;; (maybe-clone "https://github.com/alhassy/org-agda-mode") ;; (maybe-clone "https://github.com/JacquesCarette/TheoriesAndDataStructures") ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/RATH/RATH-Agda" "~/RATH-Agda") ;; (maybe-clone "https://github.com/alhassy/MyUnicodeSymbols") ;; Deleted? (maybe-clone "https://github.com/alhassy/islam") (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/OCamlCheatSheet") ;; (maybe-clone "https://github.com/alhassy/AgdaCheatSheet") (maybe-clone "https://github.com/alhassy/RubyCheatSheet") ;; (maybe-clone "https://github.com/alhassy/PrologCheatSheet") ;; (maybe-clone "https://github.com/alhassy/FSharpCheatSheet") ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/armstmp/cs3mi3.git" "~/3mi3") ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/alhassm/CAS781" "~/cas781") ;; cat adventures ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/carette/cs3fp3.git" "~/3fp3") ;; (maybe-clone "https://github.com/alhassy/interactive-way-to-c") ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/assignment-distribution.git" "~/3ea3/assignment-distribution") ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/notes.git" "~/3ea3/notes") ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/assignment-development.git" "~/3ea3/assignment-development") ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/kandeeps.git" "~/3ea3/sujan") ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/horsmane.git" "~/3ea3/emily") ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/anderj12.git" "~/3ea3/jacob") ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/alhassm/3EA3.git" "~/3ea3/_2018") ;; (maybe-clone "https://gitlab.cas.mcmaster.ca/2DM3/LectureNotes.git" "~/2dm3") #+end_src #+end_details This doc:maybe-clone utility has genuinely made it easier for me to learn about new projects and codebases from Github: I type it in with the repo's address, then kbd:C-x_C-e ---doc:eval-last-sexp--- and then I can view it in my beloved Emacs ~(─‿‿─)~. Moreover, this handy tool makes it so that you can list your Git repositories with doc:magit-list-repositories: It marks modified repos as “red:dirty”. It may be useful to know that ~(magit-anything-modified-p t file)~ can be used to check if ~file~ has been modified (doc:magit-anything-modified-p), whereas ~(magit-status repo)~ checks the status of a repository (doc:magit-status). # (magit-anything-modified-p t "~/ElispCheatSheet/CheatSheet.org") # (magit-status "~/ElispCheatSheet") *** Gotta love that time machine :PROPERTIES: :CUSTOM_ID: Gotta-love-that-time-machine :END: 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 kbd:M-x_git-timemachine on a file and use kbd:p/n/g/q to look at previous, next, goto arbitrary historic versions, or quit. #+BEGIN_SRC emacs-lisp (use-package git-timemachine ) #+END_SRC If we want to roll back to a previous version, we just doc:write-file or kbd:C-x_C-s as usual! The power of text! -------------------------------------------------------------------------------- doc:vc-annotate is also very useful to go through history and work out when things went wrong. *** Jump to a (ma)git repository with ~C-u C-x g~ :PROPERTIES: :CUSTOM_ID: Jump-to-a-ma-git-repository-with-C-u-C-x-g :END: #+begin_src emacs-lisp ;; Jump to a (ma)git repository with C-u C-x g. ;; ;; To get a selection of repositories (that have been visited at least once), ;; call with “C-u M-x magit-status” or “C-u C-x g”; use “C-u C-u C-x g” to ;; manually enter a path to a repository. ;; ;; We use projectile's record of known projects, and keep only projects with ;; .git directory. (with-eval-after-load 'projectile (setq magit-repository-directories (thread-last (projectile-relevant-known-projects) (--filter (unless (file-remote-p it) (file-directory-p (concat it "/.git/")))) (--map (list (substring it 0 -1) 0))))) ;; Follow-up utility (defun my/update-repos () "Update (git checkout main & pull) recently visited repositories." (interactive) (cl-loop for (repo _depth) in magit-repository-directories ;; Is it “main” or “master” for trunk = (s-trim (shell-command-to-string (format "cd %s; git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@'" repo))) do (message (format "🤖 %s ∷ Checking out & pulling main" repo)) (shell-command (format "cd %s; git checkout %s; git pull" repo trunk))) (message "🥳 Happy coding!")) #+end_src ** Github /within/ Emacs :Disabled: :PROPERTIES: :CUSTOM_ID: Github-within-Emacs :header-args: :tangle no :END: We can work with Github/Gitlab/etc from the comfort of Emacs Magit using the [[https://github.com/magit/forge][forge]] package. + In particular, it adds two new “topics” to the Magit status page: =Issues= and =Pull Requests=. - We can tab on these to see their “posts” ---the contributions to the conversations and /new/ commits. - [[https://magit.vc/manual/forge/Editing-Topics-and-Posts.html][Here]] is a list of actions that can be done on topics and posts. #+begin_src emacs-lisp (use-package forge :after magit) #+end_src 1. Make a file =~/.authinfo= whose top-most line is =machine api.github.com login ⟪𝑼𝑺𝑬𝑹𝑵𝑨𝑴𝑬⟫^forge password ⟪𝑻𝑶𝑲𝑬𝑵⟫= where ⟪⋯⟫ should be replaced by your Github username and a token ---which can be [[https://github.com/settings/tokens][created from this page]] with [[https://magit.vc/manual/forge/Token-Creation.html][permissions]] for ~repo, user, read:org~ checked-off. - For more detailed information, see [[https://magit.vc/manual/ghub/Creating-and-Storing-a-Token.html][this Forge manual page]]. 2. Now open a Magit status buffer for a repository and run ~M-x forge-pull~, after which you should be able to see all issues and pull requests. - Alternatively, press ~N~ to see the forge menu. - Press ~N f f~ to pull latest information from the repository. + Note how pulling data from a forge’s API works the same way as pulling Git data does; you do it explicitly when you want to see the work done by others. + We can also “c”reate a new “p”ull request or a new “i”ssue with ~N c p~ or ~N c i~, respectively. + [[https://www.youtube.com/watch?v=fFuf3hExF5w&ab_channel=ZaisteProgramming][~5 min video]] To *review a pull request* press tab on ~Pull Requests~ then press ~b y~ to checkout that branch locally. ** Pretty Magit Commit Leaders :PROPERTIES: :CUSTOM_ID: Pretty-Magit-Commit-Leaders :END: ⟨ Following [[http://www.modernemacs.com/post/pretty-magit/][Pretty Magit - Integrating commit leaders | Modern Emacs]] ⨾⨾ Code comes from there as well. Notable alteration: Helm compleition shows description of leaders. ⟩ Add faces to Magit to achieve icon and colored commit leaders. I also integrate Helm to prompt a leader when committing so there's no need to remember or type out completely every leader we choose. - /It's not just aesthetics. It's about visual clarity./ - [[https://www.reddit.com/r/emacs/comments/6jegis/comment/djeo89w/?utm_source=share&utm_medium=web2x&context=3][Here]] is an alternate approach: Add icons based on words mentioned in commit titles ---no leaders required. #+begin_details "Boring details ~ See linked article instead" #+begin_src emacs-lisp (cl-defmacro pretty-magit (WORD ICON PROPS &optional (description "") NO-PROMPT?) "Replace sanitized WORD with ICON, PROPS and by default add to prompts." `(prog1 (add-to-list 'pretty-magit-alist (list (rx bow (group ,WORD (eval (if ,NO-PROMPT? "" ":")))) ,ICON ',PROPS)) (unless ,NO-PROMPT? (add-to-list 'pretty-magit-prompt (cons (concat ,WORD ": ") ,description))))) (setq pretty-magit-alist nil) (setq pretty-magit-prompt nil) #+end_src #+end_details My personal choices for leaders are: #+begin_src emacs-lisp (pretty-magit "Add" ?➕ (:foreground "#375E97" :height 1.2) "✅ Create a capability e.g. feature, test, dependency.") (pretty-magit "Delete" ?❌ (:foreground "#375E97" :height 1.2) "❌ Remove a capability e.g. feature, test, dependency.") (pretty-magit "Fix" ?🔨 (:foreground "#FB6542" :height 1.2) "🐛 Fix an issue e.g. bug, typo, accident, misstatement.") (pretty-magit "Clean" ?🧹 (:foreground "#FFBB00" :height 1.2) "✂ Refactor code; reformat say by altering whitespace; refactor performance.") (pretty-magit "Document" ?📚 (:foreground "#3F681C" :height 1.2) "ℹ Refactor of documentation, e.g. help files.") (pretty-magit "Feature" ?⛲ (:foreground "slate gray" :height 1.2) "⛳ 🇮🇶🇨🇦 A milestone commit - flagpost") (pretty-magit "Generate" ?🔭 (:foreground "slate gray" :height 1.2) "Export PDF/HTML or tangle raw code from a literate program") ;; Generating artefacts (pretty-magit "master" ? (:box t :height 1.2) "" t) (pretty-magit "origin" ?🐙 (:box t :height 1.2) "" t) ;; Commit leader examples: https://news.ycombinator.com/item?id=13889155. ;; ;; Cut ~ Remove a capability e.g. feature, test, dependency. ;; Bump ~ Increase the version of something e.g. dependency. ;; Make ~ Change the build process, or tooling, or infra. ;; Start ~ Begin doing something; e.g. create a feature flag. ;; Stop ~ End doing something; e.g. remove a feature flag. #+end_src #+begin_details "Boring details ~ See linked article instead" #+begin_src emacs-lisp (defun add-magit-faces () "Add face properties and compose symbols for buffer from pretty-magit." (interactive) (with-silent-modifications (--each pretty-magit-alist (-let (((rgx icon props) it)) (save-excursion (goto-char (point-min)) (while (search-forward-regexp rgx nil t) (compose-region (match-beginning 1) (match-end 1) icon) (when props (add-face-text-property (match-beginning 1) (match-end 1) props)))))))) (advice-add 'magit-status :after 'add-magit-faces) (advice-add 'magit-refresh-buffer :after 'add-magit-faces) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (setq use-magit-commit-prompt-p nil) (defun use-magit-commit-prompt (&rest args) (setq use-magit-commit-prompt-p t)) (defun magit-commit-prompt () "Magit prompt and insert commit header with faces." (interactive) (when use-magit-commit-prompt-p (setq use-magit-commit-prompt-p nil) (thread-last (--map (format "%s %s" (car it) (cdr it)) pretty-magit-prompt) (completing-read "Insert commit leader ∷ ") ;; My “Generate:” commit type has one use case, for now; so let's insert it filled-in. (funcall (lambda (it) (if (s-starts-with? "Generate:" it) it (car (s-split " " it))))) (insert) (end-of-line)) (add-magit-faces))) (remove-hook 'git-commit-setup-hook 'with-editor-usage-message) (add-hook 'git-commit-setup-hook 'magit-commit-prompt) (advice-add 'magit-commit :after 'use-magit-commit-prompt) #+end_src #+end_details ** Version Control with SVN ---Using Magit! :Disabled: :PROPERTIES: :CUSTOM_ID: Version-Control-with-SVN-Using-Magit :header-args: :tangle no :END: | /Disabled: I seldom work with SVN anymore./ | Let's use git as an interface to subversion repositories so that we can continue to use =magit= as our version control interface. The utility to do so is called =git svn= ---note =git 𝒳= on a MacOS is the same as =git-𝒳= on other systems. #+begin_src emacs-lisp :tangle no (use-package magit-svn :hook (magit-mode . magit-svn-mode)) #+end_src Here's an example. The following command checksout an SVN repo; afterwhich we may open a file there and do =M-x magit-status= to get the expected porcelain git interface ^_^ #+begin_src emacs-lisp :tangle no (async-shell-command "mkdir ~/2fa3; git svn clone --username alhassm https://websvn.mcmaster.ca/csse2fa3/2019-2020_Term2 ~/2fa3/") #+end_src In the magit buffer, we may now use the ~N~ key which wraps the =git svn= subcommands =fetch, rebase, dcommit, branch, tag=. For example: 1. Make changes to a file. 2. ‘Stage’ them with ~s~ and ‘commit’ them with ~c~. 3. ‘Push’ changes with ~N c~. We get to pretend we're using ~git~ even though the underlying mechanism is ~svn~! # Indeed, in a terminal ~git svn log~ will show an ~svn~ log with single pushed commits. For move on =git svn=, see [[https://gist.github.com/rickyah/7bc2de953ce42ba07116][A simple guide to git-svn]] or [[https://www.viget.com/articles/effectively-using-git-with-subversion/][Effectively using Git with Subversion]]. (If I need to work with svn repos often enough, I'd extend my ~maybe-clone~ utility above to account for them.) ** Highlighting TODO-s & Showing them in Magit :PROPERTIES: :CUSTOM_ID: Highlighting-TODO-s-Showing-them-in-Magit :END: Sometimes it's nice to flag a chunk of text by its author, such as ‘ [[color:#dc8cc3][MA]] ’ for ‘M’usa ‘A’l-hassy, or ‘ [[color:#d0bf8f][HACK]] ’ for text that needs to be improved. Such flags stand out from other text by being coloured and bold. #+BEGIN_SRC emacs-lisp ;; NOTE that the highlighting works even in comments. (use-package hl-todo ;; I want todo-words highlighted in prose, not just in code fragements. :hook (org-mode . hl-todo-mode) :config ;; Adding new keywords (cl-loop for kw in '("TEST" "MA" "WK" "JC") do (add-to-list 'hl-todo-keyword-faces (cons kw "#dc8cc3"))) ;; Enable it everywhere. (global-hl-todo-mode)) #+END_SRC We've added few to the default flag keywords so that in total we have the following flags ---where any sequence of at least 3 [[color:#cc9393][XXX]] are considered flags. #+BEGIN_SRC emacs-lisp :results replace raw value :exports results :eval no-export :tangle no (cl-loop for (k . f) in hl-todo-keyword-faces collect (format "[[color:%s][%s]]" f k)) #+END_SRC #+RESULTS: ([[color:#dc8cc3][JC]] [[color:#dc8cc3][WK]] [[color:#dc8cc3][MA]] [[color:#dc8cc3][TEST]] [[color:#d0bf8f][HOLD]] [[color:#cc9393][TODO]] [[color:#dca3a3][NEXT]] [[color:#dc8cc3][THEM]] [[color:#7cb8bb][PROG]] [[color:#7cb8bb][OKAY]] [[color:#5f7f5f][DONT]] [[color:#8c5353][FAIL]] [[color:#afd8af][DONE]] [[color:#d0bf8f][NOTE]] [[color:#d0bf8f][KLUDGE]] [[color:#d0bf8f][HACK]] [[color:#d0bf8f][TEMP]] [[color:#cc9393][FIXME]] [[color:#cc9393][XXX+]]) :Alternate_approach_using_font_lock: #+begin_src emacs-lisp (defun add-watchwords () "Add TODO: words to font-lock keywords." (font-lock-add-keywords nil '(("\\(\\ jump to first and final semantic units. ;; If pressed twice, they go to physical first and last positions. (use-package beginend :config (beginend-global-mode)) #+END_SRC * COMMENT Init.el misc #+begin_src emacs-lisp + +;; HACK: The system-packages-ensure pkg is neat and all, but I know I'm using brew. So I'll just define the utility myself, rather than loading an entire pkg. +;; Moreover, my custom form has the added benefit of only installed packages that are not already installed, thereby foregoing any error messages which crash my init.el +(defun system-packages-ensure (pkg) + (unless (s-contains-p pkg my/installed-packages) + (message "Installing ... %s" pkg) + ;; to call async shell command without a buffer you need to run: + ;; (shell-command "something&" (universal-argument)) + (ignore-errors (shell-command-to-string (format "brew install %s &" pkg) (universal-argument))))) (load (shell-command-to-string "agda-mode locate")) +;; Prevent undo tree files from polluting your git repo +(setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo"))) +;; That will persist the undo history in ~/.emacs.d/undo instead of locally. + +(set-face-font 'default "Source Code Pro Light 14") #+end_src #+begin_src emacs-lisp +;; Use “M-x set-face-font RET default RET”, or... +;; (set-face-font 'default "Source Code Pro Light14") + +;; See ~2232 fonts +;; (append (fontset-list) (x-list-fonts "*" nil)) + + (cl-defun my/toggle-font (&optional (new-font (pop my/fonts))) + "Load NEW-FONT, which defaults from ‘my/fonts’. + +When a universal prefix is given, “C-u C-c F”, we load a random +font from all possible themes. Nice way to learn about more +fonts (•̀ᴗ•́)و" + (interactive) + (let* ((all-fonts (append (fontset-list) (x-list-fonts "*" nil))) + (font (if current-prefix-arg + (nth (random (length all-fonts)) all-fonts) + new-font))) + (set-face-font 'default font) + (message "Font: %s" font))) + + (global-set-key "\C-c\ F" 'my/toggle-font) + + ;; Default font; the “ignore-⋯” is for users who may not have the font. + (ignore-errors (my/toggle-font "Fantasque Sans Mono 12")) + (ignore-errors (my/toggle-font "Source Code Pro Light 14")) + +(unless noninteractive + (my/toggle-font "Roboto Mono Light 14") + (my/toggle-theme 'solarized-gruvbox-light)) + (unless noninteractive (load-file (let ((coding-system-for-read 'utf-8)) (shell-command-to-string "/opt/homebrew/Cellar/agda/2.6.2.2_3/bin/agda-mode locate")))) + +;; HACK: The system-packages-ensure pkg is neat and all, but I know I'm using brew. So I'll just define the utility myself, rather than loading an entire pkg. +;; Moreover, my custom form has the added benefit of only installed packages that are not already installed, thereby foregoing any error messages which crash my init.el +(defun system-packages-ensure (pkg) + (unless (s-contains-p pkg my/installed-packages) + (message "Installing ... %s" pkg) + ;; to call async shell command without a buffer you need to run: + ;; (shell-command "something&" (universal-argument)) + (ignore-errors (shell-command-to-string (format "brew install %s &" pkg) (universal-argument))))) #+end_src * Cosmetics :PROPERTIES: :CUSTOM_ID: Cosmetics :header-args: :tangle init.el :END: Upon startup, we want to be greeted with a useful, yet unobtrusive, message briefly detailing major system details. Moreover, the bottom-most area of the screen should display battery life, data, & time. Likewise, we may have a casual file explorer ---primarily to show-off to newcomers, since great functionality is found with ~M-x dired~ ---doc:dired. #+BEGIN_SRC emacs-lisp :tangle init.el ;; Get org-headers to look pretty! E.g., * → ⊙, ** ↦ ◯, *** ↦ ★ ;; https://github.com/emacsorphanage/org-bullets (use-package org-bullets :defer nil :hook (org-mode . org-bullets-mode)) #+END_SRC ** Startup message: Emacs & Org versions :PROPERTIES: :CUSTOM_ID: Startup-message-Emacs-Org-versions :END: Let's always welcome ourselves when Emacs begins with a helpful message. For example, which user account is running and what are the version numbers of our primary tools. #+begin_src emacs-lisp ;; Silence the usual message: Get more info using the about page via C-h C-a. (setq inhibit-startup-message t) (defun display-startup-echo-area-message () "The message that is shown after ‘user-init-file’ is loaded." (message (concat "Welcome " user-full-name "! Emacs " emacs-version "; Org-mode " org-version "; System " (symbol-name system-type) "/" (system-name) "; Time " (emacs-init-time)))) #+end_src Now my startup message is, #+begin_example Welcome Musa Al-hassy! Emacs 27.1; Org-mode 9.4.4; System darwin/Musas-MacBook-Air.local; Time 13.331914 seconds #+end_example :Manually_Computing_Init_Time: #+BEGIN_SRC emacs-lisp :tangle no (format "; Time %.3fs" (float-time (time-subtract (current-time) before-init-time))) #+END_SRC :End: Let's change the Emacs frame to mention the name of the buffer in focus, as well as a nice ‘motto’: #+begin_src emacs-lisp ;; Keep self motivated! (setq frame-title-format '("" "%b - Living The Dream (•̀ᴗ•́)و")) #+end_src ** My to-do list: The initial buffer when Emacs opens up :PROPERTIES: :CUSTOM_ID: My-to-do-list-The-initial-buffer-when-Emacs-opens-up :END: I almost always have Emacs open; I don't need a dashboard, but would like to see my to-do list and my init file, side-by-side. #+BEGIN_SRC emacs-lisp ;; I have symlinks for various things, just follow them, do not ask me. (setq vc-follow-symlinks t) ;; After my settings have been loaded, e.g., fancy priorities ;; and cosmetics, then open my notes files. (add-hook 'emacs-startup-hook (lambda () (find-file "~/Documents/notes.org"))) ;; If work machine, then show notes; otherwise show my todos & init side-by-side. (when nil (unless noninteractive ;; Only run the following when we're in GUI mode; ;; i.e., don't run it in Github Actions when testing. (if (not my/personal-machine?) (find-file "~/Documents/notes.org") (find-file "~/Dropbox/todo.org") ;; After startup, if Emacs is idle for 10 seconds, then open my work file; ;; which is a GPG file and so requires passphrase before other things can load. ;; (run-with-idle-timer 10 nil (lambda () (find-file "~/Desktop/work.org.gpg"))) (split-window-right) ;; C-x 3 (other-window 1) ;; C-x 0 (let ((enable-local-variables :all) ;; Load *all* locals. (org-confirm-babel-evaluate nil)) ;; Eval *all* blocks. (ignore-errors (find-file "~/.emacs.d/init.org")))))) #+END_SRC There is the neat-looking [[https://github.com/emacs-dashboard/emacs-dashboard][emacs-dashboard]] package that provides an extensbile yet minimalist splash screen showing recent files, projects, and bookmarks. ** A sleek, informative, & fancy mode line :PROPERTIES: :CUSTOM_ID: A-sleek-informative-and-fancy-mode-line :END: The ‘modeline’ is a part near the bottom of Emacs that gives information about the current buffer, such as its file-type/‘major-mode’ and enabled extensions/‘minor-modes’. Let's use the [[https://github.com/seagle0128/doom-modeline][doom-modeline]], which is a /sleek & minimalistic, yet fancy/ setup with the following notable perks: + Gives each buffer a nice icon in the modeline (denoting its major mode; e.g., Lisp/JavaScript/Org/etc each get a cool icon). + Name of file becomes red when unsaved/modified. + Nice version control icon, with branch name. + Name of file is of the shape is shown as “project/file.ext”, when a project is detected using ~projectile.el~. + Flycheck error reporting is ugly by default, and one would consider using flycheck-status-emojis to make things look better in a simple modeline, but Doom-modeline gives a nice status indicators for Flycheck. + Shows “+2” when the text scale is two above usual. For fine-grained control on what/how things appear, there is doc:doom-modeline-def-modeline and doc:doom-modeline-set-modeline. NOTE: We need to run =M-x= doc:nerd-icons-install-fonts for doom-modeline to use pretty icons ---then restart Emacs. #+begin_src emacs-lisp :tangle init.el ;; The modeline looks really nice with doom-themes, e.g., doom-solarised-light. (use-package doom-modeline :defer nil :config (doom-modeline-mode)) ;; Use minimal height so icons still fit; modeline gets slightly larger when ;; buffer is modified since the "save icon" shows up. Let's disable the icon. ;; Let's also essentially disable the hud bar, a sort of progress-bar on where we are in the buffer. (setq doom-modeline-height 1) (setq doom-modeline-buffer-state-icon nil) (setq doom-modeline-hud t) (setq doom-modeline-bar-width 1) ;; Show 3 Flycheck numbers: “red-error / yellow-warning / green-info”, which ;; we can click to see a listing. ;; If not for doom-modeline, we'd need to use flycheck-status-emoji.el. (setq doom-modeline-checker-simple-format nil) ;; Don't display the buffer encoding, E.g., “UTF-8”. (setq doom-modeline-buffer-encoding nil) ;; Inactive buffers' modeline is greyed out. ;; (let ((it "Source Code Pro Light" )) ;; (set-face-attribute 'mode-line nil :family it :height 100) ;; (set-face-attribute 'mode-line-inactive nil :family it :height 100)) ;; A quick hacky way to add stuff to doom-modeline is to add to the mode-line-process list. ;; E.g.: (add-to-list 'mode-line-process '(:eval (format "%s" (count-words (point-min) (point-max))))) ;; We likely want to add this locally, to hooks on major modes. #+end_src :Disabled__spaceline_modeline: [[https://github.com/TheBB/spaceline][Spaceline]] # I may not use the spacemacs [[https://www.emacswiki.org/emacs/StarterKits][starter kit]], since I find spacemacs to “hide things” # from me ---whereas Emacs “encourages” me to learn more---, however it is a # configuration and I enjoy reading Emacs configs in order to improve my own # setup. From Spacemacs I've adopted Helm for list completion, its sleek light & # dark themes, and its modified powerline setup. #+begin_src emacs-lisp :tangle no ;; When using helm & info & default, mode line looks prettier. (use-package spaceline :custom (spaceline-buffer-encoding-abbrev-p nil) ;; Use an arrow to seperate modeline information (powerline-default-separator 'arrow) ;; Show “line-number : column-number” in modeline. (spaceline-line-column-p t) ;; Use two colours to indicate whether a buffer is modified or not. (spaceline-highlight-face-func 'spaceline-highlight-face-modified) :config (custom-set-faces '(spaceline-unmodified ((t (:foreground "black" :background "gold"))))) (custom-set-faces '(spaceline-modified ((t (:foreground "black" :background "cyan"))))) (require 'spaceline-config) (spaceline-helm-mode) (spaceline-info-mode) (spaceline-emacs-theme)) (spaceline-toggle-buffer-size-off) ;; Not interested in how large a buffer is. (spaceline-toggle-input-method-off) ;; Usually a “Π” symbol in the modeline, since I use Unicode input with Agda, denoting the current language environment, buffer coding system, and current input method. (spaceline-toggle-buffer-encoding-abbrev-off) ;; buffer-encoding-abbrev: the line ending convention used in the current buffer (unix, dos or mac). (spaceline-toggle-hud-off) ;; A tiny “progress bar”; shows the currently visible part of the buffer. (spaceline-toggle-helm-buffer-id-off) ;; I don't need to see “Helm M-x” whenever I press “M-x” or other Helm-powered commands. #+end_src Other separators ---of modeline information--- that I've considered include ~'brace~ instead of an arrow, and ~'contour, 'chamfer, 'wave, 'zigzag~ which look like browser tabs that are curved, boxed, wavy, or in the style of driftwood. :End: *** Menu to Toggle Minor Modes: A quick way to see all of my modes, and which are enabled :PROPERTIES: :CUSTOM_ID: Menu-to-Toggle-Minor-Modes-A-quick-way-to-see-all-of-my-modes-and-which-are-enabled :END: Enabled minor modes clutter up the modeline with their names, albeit some have useful status information shown. We can either selectively pick which names/status are shown using diminish.el, possibly forgetting which minor modes are enabled or we can use minions.el to “gather up” all enabled minor modes, and recently enabled ones, under a single menu which doom-modeline shows as a simple configurations gear icon. ⚙. :gear: #+begin_src emacs-lisp (setq doom-modeline-minor-modes t) (use-package minions :init (minions-mode 1)) #+end_src *** Nice battery icon alongside with percentage, in doom-modeline :PROPERTIES: :CUSTOM_ID: Nice-battery-icon-alongside-with-percentage-in-doom-modeline :END: #+begin_src emacs-lisp :tangle init.el ;; If not for doom-modeline, we'd need to use fancy-battery-mode.el. (display-battery-mode +1) #+end_src #+begin_details [Posterity / Disabled] Fancy Battery Setup Let's have it also show remaining battery life, coloured green if charging and coloured yellow otherwise. It is important to note that this package is no longer maintained. It works on my machine. #+BEGIN_SRC emacs-lisp :tangle no ;; Let's use a fancy indicator … (use-package fancy-battery :custom (fancy-battery-show-percentage t) (battery-update-interval 15) :config (fancy-battery-mode)) #+END_SRC #+end_details *** Time & date :PROPERTIES: :CUSTOM_ID: Time-date :END: Let’s display the current time, with updates every second. #+begin_src emacs-lisp :tangle init.el ;; Show date and time as well. ;; [Simple Approach] ;; (setq display-time-day-and-date t) ;; (display-time) ;; [More Controlled Approach: Set date&time format] ;; a ≈ weekday; b ≈ month; d ≈ numeric day, R ≈ 24hr:minute. (setq display-time-format "%a %b %d ╱ %r") ;; E.g.,: Fri Mar 04 ╱ 03:42:08 pm (setq display-time-interval 1) ;; Please update the time every second. (display-time-mode) #+end_src But doc:display-time-mode shows me a bit more info that I actually don't care for; so let's disable those. #+begin_src emacs-lisp :tangle init.el ;; I don't need the system load average in the modeline. (setq display-time-default-load-average nil) (setq display-time-load-average nil) #+end_src *** No Line, nor Column, Numbers ---and no buffer percentage Likewise, let's have the modeline display column numbers, but not line numbers. Instead, let's have line numbers on the side of the buffer; moreover let's have a uniform width for displaying line numbers, rather than having the width grow as necessary. #+BEGIN_SRC emacs-lisp :tangle init.el ;; ;; Do not show me line numbers, nor column numbers, in the modeline (column-number-mode -1) (line-number-mode -1) ;; Likewise, no need to show me “Top∣Mid∣Bot” in the modeline. (setq-default mode-line-percent-position nil) #+END_SRC Line numbers are a /conventionally expected/ part of a user interface, but I've realised that I seldom /need to see/ them. I can still jump to a line number provided by a compilation error with kbd:M-g_g; and toggle line numbers on when I'm pair programming with doc:display-line-numbers-mode. - In Emacs, there are /buffer/ which exist and contain textual data, but to actually see them one requires a /window/. In the same vein, there are line numbers but I don't need to always see them. - If I need an indication of ‘progress’, the modeline contains a percentage of how far I am in a buffer. #+BEGIN_SRC emacs-lisp :tangle init.el ;; (setq display-line-numbers-width-start t) ;; (global-display-line-numbers-mode t) #+END_SRC *** Buffer names are necessarily injective :No_longer_needed_due_to_doom:Disabled: :PROPERTIES: :CUSTOM_ID: Buffer-names-are-necessarily-injective :header-args: :tangle no :END: By default when multiple files sharing the same name are opened, say for comparison from different directories, their buffers are named uniquely by having the format “⟨file-name⟩ <𝓃>”, for numbers 𝓃. It'd be more helpful to have the buffer names reflect their location. #+BEGIN_SRC emacs-lisp ;; Note that ‘uniquify’ is builtin. (require 'uniquify) (setq uniquify-separator "/" ;; The separator in buffer names. uniquify-buffer-name-style 'forward) ;; names/in/this/style #+END_SRC Note that this does not affect cloning buffers, kbd:C-x_4_c. ( A function /f/ is /injective/ precisely when it's /distinction-preserving/; i.e., /x ≠ y ≡ f x ≠ f y/. We can tell whether two things are the same or not, by ‘zooming in’ on their particular property ‘f’, which may be easier to compare. E.g., object IDs, hashcodes, unique keys in database tables. ) ( Why am I bringing this up? I like math and seldom get to use it; so why not! ) ** Exquisite Fonts and Themes :PROPERTIES: :CUSTOM_ID: Exquisite-Fonts-and-Themes :END: Emacs' default theme leaves much to be desired: It does not look sleek and shiny, which usually leaves first-timers with a poor, shallow, impression of the system. Below we install a few themes that make Emacs look exquisite. We cycle between the chosen themes with kbd:C-c_t, doc:my/toggle-theme. #+begin_details “my/toggle-theme” Implementation + ~M-x load-theme RET TAB~ shows all themes, including built-in ones, that may be loaded. + Loading multiple themes results in their pallets mixed. - ~M-x disable-theme~ to remove a theme from the current pallet. -------------------------------------------------------------------------------- #+begin_src emacs-lisp :tangle init.el ;; Treat all themes as safe; no query before use. (setf custom-safe-themes t) ;; Nice looking themes ^_^ (use-package solarized-theme ) (use-package doom-themes ) (use-package spacemacs-common :ensure spacemacs-theme) (use-package stimmung-themes ) (use-package shanty-themes ) #+end_src + The [[https://github.com/hlissner/emacs-doom-themes/tree/screenshots][Doom Themes]] also look rather appealing. + A showcase of many themes can be found [[https://emacsthemes.com/][here]]. -------------------------------------------------------------------------------- #+BEGIN_SRC emacs-lisp :tangle init.el ;; Infinite list of my commonly used themes. (setq my/themes '(doom-laserwave shanty-themes-light stimmung-themes-light stimmung-themes-dark doom-solarized-light doom-vibrant spacemacs-light solarized-gruvbox-dark solarized-gruvbox-light)) (setcdr (last my/themes) my/themes) #+END_SRC kbd:C-c_t to toggle between the personal themes. #+BEGIN_SRC emacs-lisp :tangle init.el (cl-defun my/load-theme (&optional (new-theme (completing-read "Theme: " (custom-available-themes)))) "Disable all themes and load the given one ---read from user when called interactively." (interactive) (mapc #'disable-theme custom-enabled-themes) (load-theme new-theme) (message "Theme %s" new-theme)) (cl-defun my/toggle-theme (&optional (new-theme (pop my/themes))) "Disable all themes and load NEW-THEME, which defaults from ‘my/themes’. When a universal prefix is given, “C-u C-c t”, we load a random theme from all possible themes. Nice way to learn about more themes (•̀ᴗ•́)و" (interactive) (-let [theme (if current-prefix-arg (nth (random (length (custom-available-themes))) (custom-available-themes)) new-theme)] (my/load-theme theme))) (global-set-key "\C-c\ t" 'my/toggle-theme) ;; (my/toggle-theme) (my/toggle-theme 'doom-laserwave) #+END_SRC Apparently, there's already a package that accomplishes these goals and more: [[https://github.com/myTerminal/theme-looper][theme-looper]]. I may switch to it, but for now my simple function above is slightly informative, to me at least, about how themes work and it does what I want. ...Actually, the above learning adventure has made it easy to provide a similar setup for fonts 😁 #+end_details Likewise, kbd:C-c_F, doc:my/toggle-font, to quickly change fonts (according to mood 😸). [I already use kbd:C-c_f, doc:my/org-mode-format, for the more likely operation of formatting text.] #+begin_details “my/toggle-font” Implementation #+begin_src emacs-lisp :tangle init.el (unless nil ;; my/work-machine? ;; Infinite list of my commonly used fonts (setq my/fonts '(;; NOPE: Breaks Gerrit! "Roboto Mono Light 14" ;; Sleek "Input Mono 14" "Source Code Pro Light 14" ;; thin, similar to Inconsolata Light "Papyrus 14" "Bradley Hand Light 12" ;; "Chalkduster 14" ;; Laggy! "Courier Light 12" "Noteworthy 9" "Savoye LET 14" "Fantasque Sans Mono 16" )) (setcdr (last my/fonts) my/fonts) ;; Let's ensure they're on our system ;; brew search "/font-/" # List all fonts (shell-command "brew tap homebrew/cask-fonts") (system-packages-ensure "svn") ;; Required for the following font installs ;; No thanks! (system-packages-ensure "font-roboto-mono") ;; Makes Gerrit in Chrome look like Gibberish! (system-packages-ensure "font-input") (system-packages-ensure "font-source-code-pro") (system-packages-ensure "font-fira-mono") (system-packages-ensure "font-mononoki") (system-packages-ensure "font-monoid") (system-packages-ensure "font-menlo-for-powerline") (system-packages-ensure "font-fantasque-sans-mono") ;; Use “M-x set-face-font RET default RET”, or... ;; (set-face-font 'default "Source Code Pro Light14") ;; See ~2232 fonts ;; (append (fontset-list) (x-list-fonts "*" nil)) (cl-defun my/toggle-font (&optional (new-font (pop my/fonts))) "Load NEW-FONT, which defaults from ‘my/fonts’. When a universal prefix is given, “C-u C-c F”, we load a random font from all possible themes. Nice way to learn about more fonts (•̀ᴗ•́)و" (interactive) (let* ((all-fonts (append (fontset-list) (x-list-fonts "*" nil))) (font (if current-prefix-arg (nth (random (length all-fonts)) all-fonts) new-font))) (set-face-font 'default font) (message "Font: %s" font))) (global-set-key "\C-c\ F" 'my/toggle-font) ;; Default font; the “ignore-⋯” is for users who may not have the font. (ignore-errors (my/toggle-font "Fantasque Sans Mono 12")) (ignore-errors (my/toggle-font "Source Code Pro Light 14"))) #+end_src #+end_details In any Org file, type ~elisp:menu-set-font~; then you can click on this link to get a nice font selection menu ---this can be useful for your own ‘personal startup buffer’. # Finally, for fun, let's colour all source blocks, in Org mode, by the background colour pink. # (set-face-attribute 'org-block nil :background "pink") # MA: This does not work well with dark themes; should use a theme-based setting. Let's use the following theme and font, upon startup. #+begin_src emacs-lisp :tangle init.el (unless noninteractive ;; Breaks Gerrit: (my/toggle-font "Roboto Mono Light 14") (my/toggle-theme 'solarized-gruvbox-light)) #+end_src ** Never lose the cursor :PROPERTIES: :CUSTOM_ID: Never-lose-the-cursor :END: Let's have the entire line containing the cursour be slightly highlighted. #+BEGIN_SRC emacs-lisp ;; Make it very easy to see the line with the cursor. (global-hl-line-mode t) #+END_SRC Moreover, we reduce the mental strain of locating the cursour when navigation happens: When we switch windows or scroll, for example, we get a wave of light near the cursor. #+begin_src emacs-lisp (use-package beacon :defer nil :config (setq beacon-color "#666600") :hook ((org-mode text-mode) . beacon-mode)) #+end_src ** Dimming Unused Windows :PROPERTIES: :CUSTOM_ID: Dimming-Unused-Windows :END: Let's dim windows, and even the whole Emacs frame, when not in use. #+BEGIN_SRC emacs-lisp (use-package dimmer :defer nil :config (dimmer-mode)) #+END_SRC A more ‘fine-grained’ [[https://github.com/larstvei/Focus][tool]] dims all text except the ‘paragraph’ you're working on. It's nifty, but not for me. ** Flashing when something goes wrong :PROPERTIES: :CUSTOM_ID: Flashing-when-something-goes-wrong :END: Enable flashing mode-line on errors. E.g., ~C-g~, or calling an unbound key sequence, or misspelling a word. #+BEGIN_SRC emacs-lisp ;; (setq visible-bell 1) ;; On MacOS, this shows a caution symbol ^_^ ;; The doom themes package comes with a function to make the mode line flash on error. (use-package doom-themes :defer nil) (require 'doom-themes-ext-visual-bell) (doom-themes-visual-bell-config) #+END_SRC -------------------------------------------------------------------------------- +A blinking cursor rushes me to type; let's slow down.+ Recently I'm thinking that a blinking cursours prompts me to continue upwards and onwards. #+BEGIN_SRC emacs-lisp (blink-cursor-mode 1) #+END_SRC ** Hiding Scrollbar, tool bar, and menu :PROPERTIES: :CUSTOM_ID: Hiding-Scrollbar-tool-bar-and-menu :END: As a laptop user, screen space is important, so let's remove rarely used visual items. #+BEGIN_SRC emacs-lisp :tangle init.el (unless noninteractive (tool-bar-mode -1) ;; No large icons please (scroll-bar-mode -1)) ;; No visual indicator please ;; (menu-bar-mode -1) ;; The Mac OS top pane has menu options #+END_SRC ** Highlight & complete parenthesis pair when cursor is near ;-) :PROPERTIES: :CUSTOM_ID: Highlight-complete-parenthesis-pair-when-cursor-is-near :END: Highlight matching ‘parenthesis’ when near one of them. #+begin_src emacs-lisp :tangle init.el (setq show-paren-delay 0) (setq show-paren-style 'mixed) (show-paren-mode) #+end_src Colour parens, and other delimiters, depending on their depth. Very useful for parens heavy languages like Lisp. #+begin_src emacs-lisp :tangle init.el (use-package rainbow-delimiters :disabled :hook ((org-mode prog-mode text-mode) . rainbow-delimiters-mode)) #+end_src For example: #+begin_src emacs-lisp :tangle no (blue (purple (forest (green (yellow (blue)))))) #+end_src There is a powerful package called ‘smartparens’ for working with pair-able characters, but I've found it to be too much for my uses. Instead I'll utilise the lightweight package ~electric~, which Emacs provides out of the box. #+BEGIN_SRC emacs-lisp :tangle init.el (electric-pair-mode 1) #+END_SRC It supports, by default, ACSII pairs ~{}, [], ()~ and Unicode ~‘’, “”, ⟪⟫, ⟨⟩~. When writing Lisp, it is annoyong to have ‘<’ and ‘>’ be completed /and/ considered as pairs. Let's disassociate them from both notions. #+BEGIN_SRC emacs-lisp :tangle init.el ;; The ‘<’ and ‘>’ are not ‘parenthesis’, so give them no compleition. (setq electric-pair-inhibit-predicate (lambda (c) (or (member c '(?< ?> ?~)) (electric-pair-default-inhibit c)))) ;; Treat ‘<’ and ‘>’ as if they were words, instead of ‘parenthesis’. (modify-syntax-entry ?< "w<") (modify-syntax-entry ?> "w>") #+END_SRC :Rainbow_delims: #+BEGIN_SRC emacs-lisp :tangle no ;; Act as usual unless a ‘<’ or ‘>’ is encountered. ;; ( char-at is really “character at poisition”; C-h o! ) (setq rainbow-delimiters-pick-face-function (lambda (depth match loc) (unless (member (char-after loc) '(?< ?>)) (rainbow-delimiters-default-pick-face depth match loc)))) #+END_SRC :End: *Adding Org-emphasise markers for pair completion ---Disabled.* Let's add the org-emphasises markers: If we select a word then press =*=, it becomes bold; likewise for ~/~ for emphasise. #+BEGIN_SRC emacs-lisp :tangle no (setq electric-pair-pairs '((?~ . ?~) (?* . ?*) (?/ . ?/))) ;; Let's also, for example, avoid obtaining double ‘~’ and ‘/’ when searching for a file. ;; Disable pairs when entering minibuffer (add-hook 'minibuffer-setup-hook (lambda () (electric-pair-mode 0))) ;; Renable pairs when existing minibuffer (add-hook 'minibuffer-exit-hook (lambda () (electric-pair-mode 1))) #+END_SRC I use ‘~’ and ‘/’ too much during file navigation, and ‘*’ when marking numerous Org headers, for which the ‘completed closing pair’ must tiresomely be deleted. ** Proportional fonts for Headlines :PROPERTIES: :CUSTOM_ID: Proportional-fonts-for-Headlines :END: Let's have headings stick out a bit. + The larger headings are cute and reminicint of word processors, but having headings coloured is enough ---the larger size is too much. #+BEGIN_SRC emacs-lisp (set-face-attribute 'org-document-title nil :height 2.0) ;; (set-face-attribute 'org-level-1 nil :height 1.0) ;; Remaining org-level-𝒾 have default height 1.0, for 𝒾 : 1..8. ;; ;; E.g., reset org-level-1 to default. ;; (custom-set-faces '(org-level-1 nil)) #+END_SRC Remember you can always use Emacs' Custom utility to get Lisp incantations ;-) ---See notes on Custom above. ** Making Block Delimiters Less Intrusive :PROPERTIES: :CUSTOM_ID: Making-Block-Delimiters-Less-Intrusive :END: Let us render Org-mode's ~#+begin_src~ and ~#+end_src~ less obtrusively by, e.g., having the former render as a pencil marker ~✎~ and the latter as a tombstone ~□~ ---reminiscent of Halmos' QED end-of-proof marker. # His setup also accounts for quotes. #+begin_details Rasmus’ Incantation This is from [[https://pank.eu/blog/pretty-babel-src-blocks.html#coderef-symbol][Rasmus Roulund]]. #+begin_src emacs-lisp (defvar-local rasmus/org-at-src-begin -1 "Variable that holds whether last position was a ") (defvar rasmus/ob-header-symbol ?☰ "Symbol used for babel headers") (defun rasmus/org-prettify-src--update () (let ((case-fold-search t) (re "^[ \t]*#\\+begin_src[ \t]+[^ \f\t\n\r\v]+[ \t]*") found) (save-excursion (goto-char (point-min)) (while (re-search-forward re nil t) (goto-char (match-end 0)) (let ((args (org-trim (buffer-substring-no-properties (point) (line-end-position))))) (when (org-string-nw-p args) (let ((new-cell (cons args rasmus/ob-header-symbol))) (cl-pushnew new-cell prettify-symbols-alist :test #'equal) (cl-pushnew new-cell found :test #'equal))))) (setq prettify-symbols-alist (cl-set-difference prettify-symbols-alist (cl-set-difference (cl-remove-if-not (lambda (elm) (eq (cdr elm) rasmus/ob-header-symbol)) prettify-symbols-alist) found :test #'equal))) ;; Clean up old font-lock-keywords. (font-lock-remove-keywords nil prettify-symbols--keywords) (setq prettify-symbols--keywords (prettify-symbols--make-keywords)) (font-lock-add-keywords nil prettify-symbols--keywords) (while (re-search-forward re nil t) (font-lock-flush (line-beginning-position) (line-end-position)))))) (defun rasmus/org-prettify-src () "Hide src options via `prettify-symbols-mode'. `prettify-symbols-mode' is used because it has uncollpasing. It's may not be efficient." (let* ((case-fold-search t) (at-src-block (save-excursion (beginning-of-line) (looking-at "^[ \t]*#\\+begin_src[ \t]+[^ \f\t\n\r\v]+[ \t]*")))) ;; Test if we moved out of a block. (when (or (and rasmus/org-at-src-begin (not at-src-block)) ;; File was just opened. (eq rasmus/org-at-src-begin -1)) (rasmus/org-prettify-src--update)) ;; Remove composition if at line; doesn't work properly. ;; (when at-src-block ;; (with-silent-modifications ;; (remove-text-properties (match-end 0) ;; (1+ (line-end-position)) ;; '(composition)))) (setq rasmus/org-at-src-begin at-src-block))) (defun rasmus/org-prettify-symbols () (mapc (apply-partially 'add-to-list 'prettify-symbols-alist) (cl-reduce 'append (mapcar (lambda (x) (list x (cons (upcase (car x)) (cdr x)))) `(("#+begin_src" . ?✎) ;; ➤ 🖝 ➟ ➤ ✎ ("#+end_src" . ?□) ;; ⏹ ("#+header:" . ,rasmus/ob-header-symbol) ("#+begin_quote" . ?») ("#+end_quote" . ?«))))) (turn-on-prettify-symbols-mode) (add-hook 'post-command-hook 'rasmus/org-prettify-src t t)) ;; Last up­dated: 2019-06-09 #+end_src #+end_details #+BEGIN_SRC emacs-lisp (add-hook 'org-mode-hook #'rasmus/org-prettify-symbols) (org-mode-restart) #+END_SRC His development relies on built-in prettify-symbols-mode, which disguises strings in a buffer for the sake of readability or aesthetics. Following the example in the documentation, ~C-h f prettify-symbols-mode~, we can quickly approximate his efforts for ~example~ blocks as follows, however a main issue is that source blocks have busybodied headers which his setup disguises as ‘≡’. #+begin_src emacs-lisp (global-prettify-symbols-mode) (defvar my/prettify-alist nil "Musa's personal prettifications.") (cl-loop for pair in '(;; Example of how pairs like this to beautify org block delimiters ("#+begin_example" . (?ℰ (Br . Bl) ?⇒)) ;; ℰ⇒ ("#+end_example" . ?⇐) ;; ⇐ ;; Actuall beautifications ("==" . ?≈) ("===" . ?≈) ;; ("=" . ?≔) ;; Programming specific prettifications ("i32" . ?ℤ) ("u32" . ?ℕ) ("f64" . ?ℝ) ;; Rust specific ("bool" . ?𝔹) ;; ("\"\"\"\n" . ?“) ("\"\"\"" . ?”) ("\"\"\"" . ?“) ("fn" . ?λ) ("<=" . ?≤) (">=" . ?≥) ("->" . ?→) ("-->". ?⟶) ;; threading operators ("[ ]" . ?□) ("[X]" . ?☑) ("[-]" . ?◐)) ;; Org checkbox symbols do (push pair my/prettify-alist)) ;; Replace all Org [metadata]keywords with the “▷” symbol; e.g., “#+title: Hello” looks like “▷ Hello”. (cl-loop for keyword in '(title author email date description options property startup export_file_name html_head fileimage filetags) do (push (cons (format "#+%s:" keyword) ?▷) my/prettify-alist)) (cl-loop for hk in '(text-mode-hook prog-mode-hook org-mode-hook) do (add-hook hk (lambda () (setq prettify-symbols-alist (append my/prettify-alist prettify-symbols-alist))))) (add-hook 'org-mode-hook (lambda () (push '("# " . (?🎶 (Br . Bl) ?\ )) prettify-symbols-alist))) #+end_src For more on prettify-symbols-mode, read the informative [[https://tony-zorman.com/posts/pretty-latex.html][Prettifying LaTeX Buffers]]. #+RESULTS: :Did_it_work: #+begin_example lisp (<= (+ 1 1) (--> 2)) 1 = 2 == 3 === #+end_example :End: See [[http://www.modernemacs.com/post/prettify-mode/][“Mathematical Notation in Emacs”]] for how such prettifications can make verbose (Python) scripts much more readable by employing more economical disguises. A nice sanity: #+BEGIN_SRC emacs-lisp ;; Un-disguise a symbol when cursour is inside it or at the right-edge of it. (setq prettify-symbols-unprettify-at-point 'right-edge) #+END_SRC ** Hiding Emphasise Markers, Inlining Images, and LaTeX-as-PNG :PROPERTIES: :CUSTOM_ID: Hiding-Emphasise-Markers-Inlining-Images-and-LaTeX-as-PNG :END: :yay_it_worked: $e^x = \sum_{n = 0}^\infty \frac{x^n}{n!}$ ~awkward~ or $not$ :end: Let's make some things prettier than they appear by default. #+BEGIN_SRC emacs-lisp ;; org-mode math is now highlighted ;-) (setq org-highlight-latex-and-related '(latex)) ;; Extra space between text and underline line (setq x-underline-at-descent-line t) ;; Hide the *,=,/ markers (setq org-hide-emphasis-markers t) ;; Let’s limit the width of images inlined in org buffers to 400px. (setq org-image-actual-width 400) ;; Visually, I prefer to hide the markers of macros, so let’s do that: ;; {{{go(here)}}} is shown in Emacs as go(here) (setq org-hide-macro-markers t) ;; On HTML exports, Org-mode tries to include a validation link for the exported HTML. Let’s disable that since I never use it. ;; (setq org-html-validation-link nil) ;; Musa: This is super annoying, in practice. (setq org-pretty-entities nil) ;; Also makes subscripts (x_{sub script}) and superscripts (x^{super script}) appear in org in a WYSIWYG fashion. ;; to have \alpha, \to and others display as utf8 ;; http://orgmode.org/manual/Special-symbols.html ;; ;; Be default, any consectuive string after “_” or “^” will be shown in WYSIWYG fashion; the following requires “^{⋯}” instead. ;; (setq org-use-sub-superscripts (quote {})) #+END_SRC Org pretty entities seems rather impressive ---=M-x org-entities-help= to see all possibilities, or add your own. I'm already using the Agda input method, so I wont use Org's ---Agda's gives me a tiny menu narrowing possibilities as I type. However, it does make subscripts (x_{sub script}) and superscripts (x^{super script}) appear in Org in a WYSIWYG fashion. -------------------------------------------------------------------------------- Automatically display emphasis markers and links when the cursor is on them. (c.f. ~fragtog~ below) #+begin_src emacs-lisp (use-package org-appear :disabled t :hook (org-mode . org-appear-mode) :init (setq org-appear-autoemphasis t org-appear-autolinks nil org-appear-autosubmarkers nil)) #+end_src -------------------------------------------------------------------------------- The following is now disabled (yet again, as of Dec/31/2020) ---it makes my system slower than I'd like. #+BEGIN_SRC emacs-lisp :tangle no ;; Show inline images when loading a new Org file. (setq org-startup-with-inline-images t) ;; Whenever a src block is run, redisplay images so they're up-to-date. ;; Very useful when using ‘ob-latex-as-png’, below. (add-hook 'org-babel-after-execute-hook #'org-redisplay-inline-images) ;; Automatically convert LaTeX fragments to inline images. (setq org-startup-with-latex-preview t) #+END_SRC -------------------------------------------------------------------------------- # latex-preview-in-org Org mode supports inline image previews of LaTeX fragments; e.g., $e^{i \cdot \pi} - 1 = 0$ or $\substack{𝔹 \\ ↓ \\ 𝒜}$. These can be toggled with kbd:C-c_C-x_C-l. [[https://github.com/io12/org-fragtog][Org-fragtog]] automates this, so fragment previews are disabled for editing when your cursor steps onto them, and re-enabled when the cursor leaves. #+BEGIN_SRC emacs-lisp ;; Automatically toggle LaTeX previews when cursour enters/leaves them (use-package org-fragtog :disabled t :hook (org-mode . org-fragtog-mode)) #+END_SRC doc:org-latex-preview, kbd:C-c_C-x_C-l, renders ~$e^{i \pi} + 1 = 0$~ into a really nice inline image: $e^{i \pi} + 1 = 0$. It also works for LaTeX environments ---for personal environments, just ~(add-to-list 'org-latex-packages-alist "LaTeX definitions here")~. #+begin_src emacs-lisp ;; Make previews a bit larger (setq org-format-latex-options (plist-put org-format-latex-options :scale 1.5)) ;; I use a lot of Unicode, so let's always include a unicode header. (maybe-clone "https://armkeh.github.io/unicode-sty/") (setq org-format-latex-header (concat org-format-latex-header "\n\\usepackage{\\string~\"/unicode-sty/unicode\"}")) ;; ;; Now this looks nice too! ;; $\substack{𝔹 \\ ↓ \\ 𝒜}$ and $\mathbb{B}$. ;; Always support unicode upon LaTeX export ;; No need to explicitly import armkeh's unicode-sty in each org file. (add-to-list 'org-latex-packages-alist "\n\\usepackage{\\string~\"/unicode-sty/unicode\"}") #+end_src This approach does not work well for forming diagrams; I've tried to make tikzcd work this way and failed. Using ~ob-latex-as-png~ as a substitute. # work this way and failed. Using [doc : org-babel-execute:latex-as-png][ob-latex-as-png] as a substitute. :calc: #+begin_src emacs-lisp :tangle no ;; \step should be local to \begin{calc}⋯\end{calc}! (add-to-list 'org-latex-packages-alist "\\def\\BEGINstep{ \\{ } \\def\\ENDstep{ \\} } \\newcommand{\\step}[2][=]{ \\\\ #1 \\;\\; & \\qquad \\color{maroon}{\\BEGINstep \\text{ #2 } \\ENDstep} \\\\ & } \\newenvironment{calc}{\\begin{align*} & }{\\end{align*}}") ; (pop org-latex-packages-alist) ;; See also org-format-latex-header #+end_src :End: # LaTeX Rendering: Support “latex-as-png” src blocks, which show LaTeX as PNGs # LaTeX-Rendering-Support-latex-as-png-src-blocks-which-show-LaTeX-as-PNGs #+BEGIN_SRC emacs-lisp ;; Support “latex-as-png” src blocks, which show LaTeX as PNGs (use-package ob-latex-as-png :disabled t) #+END_SRC -------------------------------------------------------------------------------- Use ~ref:my-stuff~ to refer to an Org entity with ~#+name: my-stuff~; which must have a ~#+caption: ⋯~ as well. Example entities include tables and source blocks; as well as figure blocks. For equation blocks, you must use a ~\label{⋯}~ directly. #+begin_src emacs-lisp ;; Use the “#+name” the user provides, instead of generating label identifiers. (setq org-latex-prefer-user-labels t) #+end_src ** Show off-screen heading at the top of the window :PROPERTIES: :CUSTOM_ID: Show-off-screen-heading-at-the-top-of-the-window :END: In case we forgot which heading we're under, let's keep the current heading stuck at the top of the window. #+BEGIN_SRC emacs-lisp (use-package org-sticky-header :defer nil :hook (org-mode . org-sticky-header-mode) :config (setq-default org-sticky-header-full-path 'full ;; Child and parent headings are seperated by a /. org-sticky-header-outline-path-separator " / ")) #+END_SRC ** Powerful Directory Editing with ~dired :Disabled:~ :PROPERTIES: :CUSTOM_ID: Powerful-Directory-Editing-with-dired :header-args: :tangle no :END: ⟨ ~C-x C-v~ to open a file or directory in dired, using the current buffer. ⟩ As mentioned earlier, ~dired~ is Emacs' built-in directory editor; it's opened with ~C-x d~. /Dired let's us treat directories as textual objects!/ In dired, press ~h~ to see the many actions available. Here's a few... #+begin_details Super Terse ‘dired’ Tutorial + ~(~ toggles hiding entry details, such as modification date and ownership + ~s~ sort entries; modeline will display “Dired by date” or “Dired by name”. + ~o~ to open entry in anOther window; or ~RET~ to open in place. + ~+~ to create a new directory; or ~M-x make-directory~. + ~/~ to filter entries; with ~which-key~, possible completions pop-up. - E.g., ~/ f~ shows only files or ~/ . png~ to obtain all entries with extension ~png~. - ~/ i g~ to hide git-ignored items ^_^ - ~/ /~ to remove all filters. + ~TAB~ to navigate between different groupings of entries. - ~RET~ on a drawer heading toggles folding it ^_^ #+end_details The [[https://github.com/Fuco1/dired-hacks#dired-hacks-utils][dired-hacks]] family of packages lets us, say, get a dired buffer out of a shell incantation that lists files, or use dired to open files with external tools. Below we use three of its packages. Pressing ~i~ inserts a directory's children under it, indented, in the current buffer. Useful to see what's there. #+BEGIN_SRC emacs-lisp (use-package dired-subtree :defer nil :bind (:map dired-mode-map ("i" . dired-subtree-toggle))) #+END_SRC When directory ~𝒳~ has only one child ~𝒴~, then in dired, instead of ~𝒳~, show me ~𝒳/𝒴~ with ~𝒳~ greyed out. #+BEGIN_SRC emacs-lisp (use-package dired-collapse :defer nil :hook (dired-mode . dired-collapse-mode)) #+END_SRC Begin dired with certain entries grouped together, according to some filtering requirement; and with “garbage” files not shown ---i.e., those ending in ~.aux, .out~, etc. #+BEGIN_SRC emacs-lisp (use-package dired-filter :defer nil :hook (dired-mode . (lambda () (dired-filter-group-mode) (dired-filter-by-garbage))) :custom (dired-garbage-files-regexp "\\(?:\\.\\(?:aux\\|bak\\|dvi\\|log\\|orig\\|rej\\|toc\\|out\\)\\)\\'") (dired-filter-group-saved-groups '(("default" ("Org" (extension "org")) ("Executables" (exexutable)) ("Directories" (directory)) ("PDF" (extension "pdf")) ("LaTeX" (extension "tex" "bib")) ("Images" (extension "png")) ("Code" (extension "hs" "agda" "lagda")) ("Archives"(extension "zip" "rar" "gz" "bz2" "tar")))))) #+END_SRC #+begin_details [Disabled] Neotree: Traditional Directory Tree Navigation link-here:Neotree-Traditional-Directory-Tree-Navigation We open a nifty file manager upon startup. #+BEGIN_SRC emacs-lisp :tangle no ;; Sidebar for project file navigation (use-package neotree :disabled :config (global-set-key "\C-x\ d" 'neotree-toggle) (setq neo-theme 'icons)) ;; Uses all-the-icons from § Booting Up ;; Open it up upon startup. ;; (neotree-toggle) #+END_SRC By default ~C-x d~ invokes ~dired~, but I prefer ~neotree~ for file management. ⟨ Edit: As a naive user, this is what I thought; yet a year later, I've almost never used neotree. ⟩ Useful navigational commands include + ~U~ to go up a directory. + ~C-c C-c~ to change directory focus; ~C-C c~ to type the directory out. + ~?~ or ~h~ to get help and ~q~ to quit. As always, to go to the neotree pane when it's the only other window, execute ~C-x o~. I /rarely/ make use of this feature; company mode & Helm together quickly provide an automatic replacement for nearly all of my uses. + Reminiscent of GUI file managers is [[https://github.com/ralesi/ranger.el#features][ranger]]; e.g., it has multi-column display of parent directories along with a file preview mechanism. #+end_details ** Persistent Scratch Buffer :PROPERTIES: :CUSTOM_ID: Persistent-Scratch-Buffer :END: The ~*scratch*~ buffer is a nice playground for temporary data or experiments. However, by default its contents are not saved --which may be an issue if we have not relocated our playthings to their appropriate files. Whence let's save & restore the scratch buffer by default. #+BEGIN_SRC emacs-lisp (use-package persistent-scratch ;; In this mode, the usual save key saves to the underlying persistent file. :bind (:map persistent-scratch-mode-map ("C-x C-s" . persistent-scratch-save))) #+END_SRC We might accidentally close this buffer, so we could utilise the following. #+BEGIN_SRC emacs-lisp (defun scratch () "Recreate the scratch buffer, loading any persistent state." (interactive) (switch-to-buffer-other-window (get-buffer-create "*scratch*")) (condition-case nil (persistent-scratch-restore) (insert initial-scratch-message)) (org-mode) (persistent-scratch-mode) (persistent-scratch-autosave-mode 1)) ;; This doubles as a quick way to avoid the common formula: C-x b RET *scratch* ;; Upon startup, close the default scratch buffer and open one as specfied above (ignore-errors (kill-buffer "*scratch*") (scratch)) #+END_SRC I use Org-mode often, so that's how I want things to appear. #+BEGIN_SRC emacs-lisp (setq initial-scratch-message (concat "#+title: Persistent Scratch Buffer" "\n#\n# Welcome! This’ a place for trying things out." "\n#\n# ⟨ ‘C-x C-s’ here saves to ~/.emacs.d/.persistent-scratch ⟩ \n\n")) #+END_SRC ** Tabs :Disabled: :PROPERTIES: :CUSTOM_ID: Tabs :header-args: :tangle no :END: I really like my Helm-supported ~C-x b~, but the visial appeal of a [[https://github.com/manateelazycat/awesome-tab][tab bar]] for Emacs is interesting. Let's try it out and see how long this lasts ---it may be like Neotree: Something cute to show to others, but not as fast as the keyboard. #+BEGIN_SRC emacs-lisp :tangle no (use-package awesome-tab :disabled t :quelpa (awesome-tab :fetcher git :url "https://github.com/manateelazycat/awesome-tab.git") :config (awesome-tab-mode t)) ;; Show me /all/ the tabs at once, in one group. (defun awesome-tab-buffer-groups () (list (awesome-tab-get-group-name (current-buffer)))) #+END_SRC It's been less than three days and I've found this utility to be unhelpful, to me anyhow. An alternative is [[https://github.com/ema2159/centaur-tabs][centaur-tabs.]] ** Window resizing using the golden ratio :Disabled: :PROPERTIES: :CUSTOM_ID: Window-resizing-using-the-golden-ratio :header-args: :tangle no :END: Let's load the following package, which automatically resizes windows so that the window containing the cursor is the largest, according to the golden ratio. Consequently, the window we're working with is nice and large yet the other windows are still readable. #+begin_src emacs-lisp :tangle no (use-package golden-ratio :disabled t :init (golden-ratio-mode 1)) #+end_src After some time this got a bit annoying and I'm no longer using this. ** Org-Emphasise for Parts of Words :Disabled: :PROPERTIES: :CUSTOM_ID: Org-Emphasise-for-Parts-of-Words :header-args: :tangle no :END: From [[https://stackoverflow.com/a/24540651/3550444][stackoverflow]], the following incantation allows us to have parts of works emphasied with org-mode; e.g., /half/ed, ~half~ed, and right in the m*idd*le! Super cool stuff! #+BEGIN_SRC emacs-lisp :tangle no (setcar org-emphasis-regexp-components " \t('\"{[:alpha:]") (setcar (nthcdr 1 org-emphasis-regexp-components) "[:alpha:]- \t.,:!?;'\")}\\") (org-set-emph-re 'org-emphasis-regexp-components org-emphasis-regexp-components) #+END_SRC I've disabled this feature since multiple occurrences of an emphasise marker are sometimes treated as one lengthy phrase being emphasised. ** Preview link under cursor :Disabled: :PROPERTIES: :CUSTOM_ID: Preview-link-under-cursor :header-args: :tangle no :END: When cursor sits on a URL/Image/File link, try to preview it in a tooltip. - Useful to quickly preview files and images. - See also: https://github.com/jcs-elpa/preview-it #+begin_src emacs-lisp (quelpa '(preview-it :repo "jcs-elpa/preview-it" :fetcher github)) ;; (global-preview-it-mode) #+end_src This also works nicely when I'm looking to open a file; e.g., ~C-x C-f ~/.emacs.d/.as TAB~ to preview my ~.aspell.en.pws~ (Emacs personal dictionary) file in a tooltip. # (Sadly, the preview closes my ~M-x~ session when previewing an org file.) # Not entirely true; but it's happened with some org files. Likewise, I'd like to preview line when executing the doc:goto-line / kbd:M-g_M-g command. #+begin_src emacs-lisp (quelpa '(goto-line-preview :repo "jcs-elpa/goto-line-preview" :fetcher github)) (global-set-key [remap goto-line] 'goto-line-preview) #+end_src ** Replace phrases with nice SVG labels :Disabled:Causes_Org_rendering_issues: :PROPERTIES: :CUSTOM_ID: Replace-phrases-with-nice-SVG-labels :header-args: :tangle no :END: :TODO_What_kind_of_Org_rendering_issues_are_caused: Some org headings are not fontified, and Org links are not fontified; e.g., 📆 [[https://calendar.google.com/calendar/u/0/r][Calendar]] ✉️ + I have to copy-paste it to get it to fontify. + Likewise, Org headings need to be modified (e.g. pressing `t` to make them todo) to get them fontified. :End: [[ https://github.com/rougier/svg-tag-mode][SVG tags mode]] let's us replase arbitrary regular expressions with beautiful SVG images that can be /clicked/ to produce an action, and may have a tooltip to provide contextual information. Essentially an alternative to the built-in doc:font-lock-mode, which performs arbitrary syntax highlighting. - For more power, use the ~svg-lib~ package. - The docs have nice examples. [[https://github.com/rougier/svg-tag-mode/blob/main/examples/example-2.el][Here]] are more useful examples. # Below I setup a function, doc : my/svg-tag-declare-badge to /declaratively/ produce SVG badges. #+begin_src emacs-lisp (use-package svg-tag-mode :defer nil :hook (org-mode prog-mode) ;; :config (global-svg-tag-mode) ;; Nope: Breaks xwidget-webkit-browse-url, issue#28. :config (cl-defun my/svg-tag-declare-badge (template face &optional tooltip-message-upon-hover) ;; Example faces: 'org-level-1 'org-todo 'font-lock-doc-face "Given a TEMPLATE of the shape \"𝑿❙𝒀\", make SVG badge whose tag is 𝑿 and label is 𝒀. When `svg-tags-mode' is enabled, every occurence of \"\\(𝑿\\)\\(𝒀\\)\" is replaced by an SVG image essentially displaying “[𝑿∣𝒀]” using the given FACE. This badge can be clicked to show all instances in the buffer. You can see the badges documentation / intentions / help-message when you hover over it; to see TOOLTIP-MESSAGE-UPON-HOVER. Both 𝑿 and 𝒀 are regeular expressions; “❙” serves as the SVG tag-label delimiter ---i.e., it saves as from writing \"\\(𝑿\\)\\(𝒀\\)\". Moreover, the SVG is only active when regexp \"\\(𝑿\\)\\(𝒀\\)\" matches an instance." ;; Append tooltip message with a notice on what happens upon click. (--> "Click on me to all see occurrences of this badge, in the current buffer!" (if tooltip-message-upon-hover (concat tooltip-message-upon-hover "\n\n" it) it) (setq tooltip-message-upon-hover it)) (-let [(tag label) (s-split "❙" template)] (-let [click-to-show-all-buffer-occurrences `(lambda () (interactive) (occur (concat ,tag ,label)))] ;; Make an SVG for the tag. (push (cons (format "\\(%s\\)%s" tag label) `((lambda (tag) (svg-tag-make (s-chop-suffix ":" (s-chop-prefixes '("[" "<" "/*") tag)) :face (quote ,face) :inverse t :margin 0 :crop-right t :crop-left nil)) ,click-to-show-all-buffer-occurrences ,tooltip-message-upon-hover)) svg-tag-tags) ;; Make an SVG for the label. (push (cons (format "%s\\(%s\\)" tag label) `((lambda (label) (svg-tag-make (s-chop-suffixes '("]" ">" "*/") label) :face (quote ,face) :crop-left t)) ,click-to-show-all-buffer-occurrences ,tooltip-message-upon-hover)) svg-tag-tags)))) ;; Let's start off empty; then declare badges below. (setq svg-tag-tags nil) ;; Using caps so that these stick-out somewhat even when svg-tags-mode is not present. (my/svg-tag-declare-badge "TODO:❙.*" 'org-todo "This is something I would like to do, in the future.") (my/svg-tag-declare-badge "SILLY:❙.*" 'error "I’m experimenting; don't forget to clean-up when you’re done!") (my/svg-tag-declare-badge "HACK:❙.*" 'error "This works, but it’s far from ideal. Plan to clean this in the future.") (my/svg-tag-declare-badge "FIXME:❙.*" 'org-todo "This is busted! Plan to fix this in the future.") (my/svg-tag-declare-badge "NOTE:❙.*" 'org-done "Something to be aware of; to keep in mind.") ;; [In]Active Time stamps --- M-x org-time-stamp (my/svg-tag-declare-badge "\\[2022-.* ❙.*]" 'org-done "This is an inactive time stamp. It does not trigger the parent entry to appear in the agenda.") (my/svg-tag-declare-badge "<2022-.* ❙.*>" 'org-todo "This is an active time stamp. It causes the parent Org entry to appear in the agenda.") ;; JavaScript Lint Rules: \* eslint (.*) */ (my/svg-tag-declare-badge "/\\* eslint ❙.* \\*/" 'org-done "It looks like you’ deviating from common conventions: Tread cautiously!") ;; TODO: Make SVG tags for other interesting “2-part” pieces of textual information ) ;; If everything is setup, the following examples should look like SVGs. ;; NOTE: Do something ;; TODO: fix me later ;; HACK: hiya ;; FIXME: this thing is busted 🎭 ;; SILLY: start ;; SILLY: end ;; [2022-04-20 Sun 16:30] ;; <2022-04-20 Sun 16:30> ;; /* eslint eqeqeq: 0, curly: 2 */ ;; NOTE: Toggle svg-tags-mode; useful when experimenting with new tags. ;; (progn (svg-tag-mode-off) (svg-tag-mode-on)) ;; NOTE: (my/toggle-line-fontification) works fine with svg-tag-mode :-) #+end_src ** Now C-c C-x C-v shows remote images inline, neato! #+begin_src emacs-lisp :tangle init.el (quelpa '(org-remoteimg :fetcher github :repo "gaoDean/org-remoteimg")) (require 'org-remoteimg) (setq url-cache-directory "~/emacs.d/.cache/") (setq org-display-remote-inline-images 'cache) #+end_src Example: [[https://www.javacodegeeks.com/wp-content/uploads/2017/09/lexer-parser-center.png][lexing vs parsing]] [[https://miro.medium.com/v2/resize:fit:4800/1*bpG6TxN-nS6enHkp-KIZgg.jpeg][1% better everyday, push-ups!]] * TODO Life within Org-mode :Posterity: :PROPERTIES: :CUSTOM_ID: Life-within-Org-mode :header-args: :tangle yes :END: It's hard to estimate how long a task takes if you don't keep track of time spent by ‘clocking-in and clocking-out’ of tasks. We can ‘capture’ todos right in the middle of a task /without/ context-switching; e.g., no opening a todos file! After some reflection on the relative importance of the tasks, we can schedule them into our ‘agenda’. Let's do this! ** Using Org-Mode as a Day Planner :PROPERTIES: :CUSTOM_ID: Using-Org-Mode-as-a-Day-Planner :END: ⟪ This section is based on a dated, yet delightful, tutorial of the same title by [[http://newartisans.com/2007/08/using-org-mode-as-a-day-planner/][John Wiegley]]. ⟫ We want a day-planner with the following use: 0. “Mindlessly” & rapidly create new tasks. 1. Schedule and archive tasks at the end, or start, of the work day. 2. Glance at a week's tasks, shuffle if need be. 3. Prioritise the day's tasks. Aim for ≤15 tasks. 4. Progress towards completion of ~A~ tasks by documenting work completed. 5. Repeat! During the day, if anything comes up, capture it and intentionally forget about it. #+begin_box :background-color orange #+begin_center badge:Workflow|CheatSheet|informational|https://alhassy.github.io/emacs.d/Workflow.pdf A 1-page, 3-column, PDF summarising the ideas and keybindings of this section ~(งಠ_ಠ)ง~ #+end_center #+end_box *** Capturing ideas & notes without interrupting the current workflow :Disabled: :PROPERTIES: :CUSTOM_ID: Capturing-ideas-notes-without-interrupting-the-current-workflow :header-args: :tangle no :END: [[https://orgmode.org/org.html#Setting-up-capture][Capture]] lets me quickly make notes & capture ideas, with associated reference material, without any interruption to the current work flow. Without losing focus on what you're doing, quickly jot down a note of something important that just came up. #+begin_details ‘my/org-capture’ Implementation #+begin_src emacs-lisp (cl-defun my/org-capture-buffer (&optional keys no-additional-remarks (heading-regexp "Subject: \\(.*\\)")) "Capture the current [narrowed] buffer as a todo/note. This is mostly intended for capturing mail as todo tasks ^_^ When NO-ADDITIONAL-REMARKS is provided, and a heading is found, then make and store the note without showing a pop-up. This is useful for when we capture self-contained mail. The HEADING-REGEXP must have a regexp parenthesis construction which is used to obtain a suitable heading for the resulting todo/note." (interactive "P") (let* ((current-content (substring-no-properties (buffer-string))) (heading (progn (string-match heading-regexp current-content) (or (match-string 1 current-content) "")))) (org-capture keys) (insert heading "\n\n\n\n" (s-repeat 80 "-") "\n\n\n" current-content) ;; The overtly verbose conditions are for the sake of clarity. ;; Moreover, even though the final could have “t”, being explicit ;; communicates exactly the necessary conditions. ;; Being so verbose leads to mutual exclusive clauses, whence order is irrelevant. (cond ((s-blank? heading) (beginning-of-buffer) (end-of-line)) ((and no-additional-remarks (not (s-blank? heading))) (org-capture-finalize)) ((not (or no-additional-remarks (s-blank? heading))) (beginning-of-buffer) (forward-line 2) (indent-for-tab-command))))) #+end_src With that in-hand, we use a wrapper to ~org-capture~ to make use of it. #+begin_src emacs-lisp (defun my/org-capture (&optional prefix keys) "Capture something! C-c c ⇒ Capture something C-u C-c c ⇒ Capture current [narrowed] buffer. C-u 5 C-c c ⇒ Capture current [narrowed] buffer without adding additional remarks. C-u C-u C-c c ⇒ Goto last note stored. At work, ‘C-c c’ just captures notes under ‘Tasks’; no menu used." (interactive "p") (pcase prefix (4 (my/org-capture-buffer keys)) (5 (my/org-capture-buffer keys :no-additional-remarks)) (t (if my/personal-machine? (org-capture prefix keys) (org-capture prefix "t"))))) #+end_src #+end_details #+begin_src emacs-lisp :results raw replace :exports results :wrap box (s-join "\n" (--map (concat "+ [[kbd:" (s-replace "⇒" "]]" it)) (cddr (s-split "\n" (documentation #'my/org-capture))))) #+end_src #+RESULTS: #+begin_box + [[kbd: C-c c ]] Capture something + [[kbd:C-u C-c c ]] Capture current [narrowed] buffer. + [[kbd:C-u 5 C-c c ]] Capture current [narrowed] buffer without adding additional remarks. + [[kbd:C-u C-u C-c c ]] Goto last note stored. #+end_box E.g., I have a task, or something I wish to note down, rather than opening some file, then making a heading, then writing it; instead, I press kbd:C-c_c_t and a pop-up appears, I make my note, and it disappears ---with my notes file(s) now being altered! Moreover, by default it provides a timestamp and a link to the file location where I made the note ---helpful for tasks, tickets, to be tackled later on. #+begin_src emacs-lisp ;; Location of my todos / captured notes file (unless noninteractive (setq org-default-notes-file (if my/personal-machine? "~/Dropbox/todo.org" "~/Desktop/Work-2022-01-01.org"))) ;; “C-c c” to quickly capture a task/note (define-key global-map "\C-cc" #'my/org-capture) ;; See above. #+end_src #+begin_details "By default we only get a ‘tasks’ form of capture, let's add some more." #+begin_src emacs-lisp (cl-defun my/make-org-capture-template (shortcut heading &optional (no-todo nil) (description heading) (scheduled nil)) "Quickly produce an org-capture-template. After adding the result of this function to ‘org-capture-templates’, we will be able perform a capture with “C-c c ‘shortcut’” which will have description ‘description’. It will be added to the tasks file under heading ‘heading’. ‘no-todo’ omits the ‘TODO’ tag from the resulting item; e.g., when it's merely an interesting note that needn't be acted upon. Default for ‘description’ is ‘heading’. Default for ‘no-todo’ is ‘nil’. Scheduled items appear in the agenda; true by default. The target is ‘file+headline’ and the type is ‘entry’; to see other possibilities invoke: C-h o RET org-capture-templates. The “%?” indicates the location of the Cursor, in the template, when forming the entry. " `(,shortcut ,description entry (file+headline org-default-notes-file ,heading) ,(concat "*" (unless no-todo " TODO") " %?\n" (when nil ;; this turned out to be a teribble idea. ":PROPERTIES:\n:" (if scheduled "SCHEDULED: %^{Any time ≈ no time! Please schedule this task!}t" "CREATED: %U") "\n:END:") "\n\n ") :empty-lines 1 :time-prompt t)) #+end_src #+begin_src emacs-lisp (setq org-capture-templates (cl-loop for (shortcut heading) in (-partition 2 '("t" "Tasks, Getting Things Done" "r" "Reference Material" "m" "Email" "e" "Emacs (•̀ᴗ•́)و" "i" "Islam" "b" "Blog" "a" "Arbitrary Reading and Learning" "l" "Programming Languages" "p" "Personal Matters")) collect (my/make-org-capture-template shortcut heading))) #+end_src Rather than adding notes to particular Org headings in my =todo.org= file, I could defer such a choice by having only one template and have ~C-c a~ automatically use it. Then I could ‘refile’ tasks to their appropriate parent headings with ~w~. This allows us to seperate the concerns of capturing ideas from doing any form of processing. Something to consider. #+begin_src emacs-lisp ;; Update: Let's schedule tasks during the GTD processing phase. ;; ;; For now, let's automatically schedule items a week in advance. ;; TODO: FIXME: This overwrites any scheduling I may have performed. ;; (defun my/org-capture-schedule () ;; (org-schedule nil "+7d")) ;; ;; (add-hook 'org-capture-before-finalize-hook 'my/org-capture-schedule) #+end_src #+end_details For now I capture everything into a single file. One would ideally keep separate client, project, information in its own org file. + ⇒ Org capture actually lets us add /any/ type of entry, ‘programmable template’, to /any/ type of file! ⇐ * Look at doc:my/make-org-capture-template, above, to notice that capture actually lets you add /any/ type of item to /any/ file. + ( For now, I'm only using it to add entries to my tasks lists. ) + Org-protocol is a way to create capture notes in org-mode from other applications. Let's also ensure *[[orange:TODO]]*-s respect hierarchical structure. #+BEGIN_SRC emacs-lisp ;; Cannot mark an item DONE if it has a TODO child. ;; Conversely, all children must be DONE in-order for a parent to be DONE. (setq org-enforce-todo-dependencies t) #+END_SRC *Where am I currently capturing?* + During meetings, when a nifty idea pops into my mind, I quickly capture it. - I've found taking my laptop to meetings makes me an active listener and I get much more out of my meetings since I'm taking notes. + Through out the day, as I browse the web, read, and work; random ideas pop-up, and I capture them indiscriminately. + I envision that for a phone call, I would open up a capture to make note of what the call entailed so I can review it later. + Yet another place to capture content is from mail, such as for reference material, or self-contained tasks. # See [[#Capturing-Mail-as-Todo-Notes][above]] for this discussion. + Anywhere you simply want to make a note, for the current heading, just press kbd:C-c_C-z. The notes are just your remarks along with a timestamp; they are collected at the top of the tree, under the heading. #+begin_src emacs-lisp ;; Ensure notes are stored at the top of a tree. (setq org-reverse-note-order nil) #+end_src Quickly look up some reference material... #+begin_src emacs-lisp (cl-defun my/reference (&optional (file org-default-notes-file)) "Look up some reference material super quick. By default we look in user's Org TODOs file. FILE should be an ORG file with a top-level heading that starts with ‘Reference’. We show its subheadings in a completing-read menu, then narrow to that entry." (interactive) (find-file file) (widen) (goto-char (point-min)) (re-search-forward "^* Reference") ;; Start of line. (org-narrow-to-subtree) (org-cycle) (org-cycle) (let* ((headings (org-map-entries (lambda () (org-element-property :title (org-element-at-point)) ) "LEVEL=2")) (topic (completing-read "What to review? " headings))) (search-forward (concat "** " topic)) (org-narrow-to-subtree) (org-cycle))) (defalias 'my/review-reference-notes 'my/reference) (defalias 'w-reference 'my/reference) ;; “w”ork #+end_src **** COMMENT Emacs as a reference platform: Instead of manually finding files, looking for :Place_beside_w_reference_function: :PROPERTIES: :CUSTOM_ID: COMMENT-Emacs-as-a-reference-platform-Instead-of-manually-finding-files-looking-for :END: info, then closing them; we can press a key combination to see some reference matter _quickly_ then press _q_ to dismiss it just as quickly. We can do this with a combination of (local-set-key (kbd "q") 'kill-buffer-and-window) and (read-only-mode); but better would be to open a file and put it in special-mode which has this _q_ behaviour [see simple.el special-mode-map for other bindinfs] or to use the built-in with-electric-help macro, or make-help-screen & (require 'help-macro).. Moreover, for lots of reference matter, a hydra can be used to obtain a nice menu. My setup is to use doc:w-reference. Anyhow… *** Step 1: When new tasks come up :PROPERTIES: :CUSTOM_ID: Step-1-When-new-tasks-come-up :END: Isn't it great that we can squirrel away info into some default location then immediately return to what we were doing before ---with speed & minimal distraction! ♥‿♥ Indeed, if our system for task management were slow then we may not produce tasks and so forget them altogether! ~щ(゜ロ゜щ)~ + Entering tasks is a desirably impulsive act; do not make any further scheduling considerations. The next step, the review stage occurring at the end or the start of the workday, is for processing. #+begin_quote The reason for this is that entering new tasks should be impulsive, not reasoned. Your reasoning skills are required for the task at hand, not every new tidbit. You may even find that during the few hours that transpire between creating a task and categorizing it, you’ve either already done it or discovered it doesn’t need to be done at all! ---[[http://newartisans.com/2007/08/using-org-mode-as-a-day-planner/][John Wiegley]] #+end_quote When my computer isn't handy, I'll make a note on my phone then transfer it later. *** Step 2: Filing your tasks :PROPERTIES: :CUSTOM_ID: Step-2-Filing-your-tasks :END: At a later time, a time of reflection, we go to our tasks list and actually schedule time to get them done by kbd:C-c_C-s, doc:org-schedule, then pick a date by entering a number in the form ~+𝓃~ to mean that task is due ~𝓃~ days from now. + Tasks with no due date are ones that “could happen anytime”, most likely no time at all. + At least schedule tasks reasonably far off in the future, then reassess when the time comes. + An uncompleted task is by default rescheduled to the current day, each day, along with how overdue it is. - Aim to consciously reschedule such tasks! Let's keep track of how many times, and when, we have pushed events to other dates. #+BEGIN_SRC emacs-lisp ;; Add a note whenever a task's deadline or scheduled date is changed. (setq org-log-redeadline 'time) (setq org-log-reschedule 'time) #+END_SRC #+begin_box custard With time, it will become clear what is an unreasonable day verses what is an achievable day. #+end_box [[https://orgmode.org/manual/Repeated-tasks.html][Repeat tasks]] by a repeater such as ‘+1m’ or ‘+7d’ in their timestamps; e.g., ~:SCHEDULED: <2005-10-01 Sat +1m>.~ Likewise, to schedule an event (to repeat) for multiple days, just use a bunch of timestamps (with repeaters): ~:SCHEDULED: <2022-01-04 Tues 9:00 +1w><2022-01-06 Thu +1w><2022-01-07 Fri 9:00 +1w>~. (Notice that we have ~:~ on each side of the keyword; and we have a time of day for the event.) -------------------------------------------------------------------------------- A ‘project’ is a task that has multiple steps, each as a checkbox item. It can be given a percentage marker to show progress: Place ~[%]~ after its name, then press [[kbd:C-c_#]] ---doc:org-update-statistics-cookies--- on the name to see a completion percentage ---press kbd:C-c_C-c on a checkbox item to toggle its completion state. *** Step 3: Quickly review the upcoming week :PROPERTIES: :CUSTOM_ID: Step-3-Quickly-review-the-upcoming-week :END: The next day we begin our work, we press kbd:C-c_a_a to see the scheduled tasks for this week ---kbd:C-c_C-s to re-schedule the task under the cursor and [[kbd:r]] to refresh the agenda. #+begin_src emacs-lisp (define-key global-map "\C-ca" 'org-agenda) #+end_src + Show the next 𝓃 days schedule ⇐ =C-u 𝓃 C-c a a=. # The next section, [[Super Agenda]], will discuss acting on entries in the agenda # buffer. -------------------------------------------------------------------------------- Org agenda is an interactive tool for generating summary reports from Org data ---e.g., commonly, the weekly task list is generated from todo tasks. The agenda dispatch menu, ~C-c a~, has options for displaying tasks ---e.g., ~C-c a m~ generates a list of entries having the same tags. new ways to view tasks by altering the ~org-agenda-custom-commands~ variable ---e.g., above we added two, one for completed tasks and one for unscheduled tasks. # Moreover, we can overlay due dates and priorities to tasks in a non-intrusive # way that is easy to edit by hand. #+begin_details "Let's setup the basics of our agenda." #+begin_src emacs-lisp ;; List of all the files & directories where todo items can be found. Only one ;; for now: My default notes file. (setq org-agenda-files (list org-default-notes-file)) ;; Display tags really close to their tasks. (setq org-agenda-tags-column -10) ;; How many days ahead the default agenda view should look (setq org-agenda-span 'day) ;; May be any number; the larger the slower it takes to generate the view. ;; One day is thus the fastest ^_^ ;; How many days early a deadline item will begin showing up in your agenda list. (setq org-deadline-warning-days 14) ;; In the agenda view, days that have no associated tasks will still have a line showing the date. (setq org-agenda-show-all-dates t) ;; Scheduled items marked as complete will not show up in your agenda view. (setq org-agenda-skip-scheduled-if-done t) (setq org-agenda-skip-deadline-if-done t) #+end_src #+end_details #+begin_details Super Simple ‘agenda’ Mini-tutorial link-here:Super-Simple-‘agenda’-Mini-tutorial The agenda view, like nearly all Emacs entities, is interactive: + =𝓃 f,b= ⇒ Look forward at next week's agenda, or backward to a previous week. - The optional $𝓃$ means do the action =𝓃=-many times; it defaults to 1. + =w, d= ⇒ toggle week view, or day view; use ~v~ to see possible views. - E.g., ~C-u 2017 v y~ shows us the specific year 2017. + =𝓃 n,p= to navigate to next and previous entries. + =t= ⇒ cycle TODO state of the current entry. + =±= ⇒ cycle priority state. + =𝓃 S-⇆= ⇒ Shift date time by $𝓃$ days; 1 day by default. + =C-c C-s= ⇒ Reschedule an entry; prefix it with ~C-u~ to remove a scheduled entry. - [[https://orgmode.org/manual/Repeated-tasks.html][Repeat tasks]] by a repeater such as ‘+1m’ or ‘+7d’ in their timestamps; e.g., ~DEADLINE: <2005-10-01 Sat +1m>.~ + =s= ⇒ save all agenda buffers; i.e., save the org-files where the agenda items live. + =g= ⇒ Rebuild agenda according to any changes made thus far. + =F= ⇒ Toggle ‘follow mode’: As you go up/down entries, you can see their details in an adjacent window. - =SPC= ⇒ Show details of a single entry in other window; stay in Agenda. + =RET, TAB= ⇒ Go to the current entry in the current window or in a new adjacent window, so as to alter task details. # ;; Easy way to capture tasks when in agenda view. # (define-key org-agenda-mode-map "c" #'org-capture) #+end_details The agenda view –--even in the 7-days-at-a-time view--– will always begin on the current day. This is important, since while using org-mode as a day planner, you never want to think of days gone past. That’s something you do in other ways, such as when reviewing completed tasks. #+begin_src emacs-lisp (setq org-agenda-start-on-weekday nil) ;; Start each agenda item with ‘○’, then show me it's %timestamp and how many ;; times it's been re-%scheduled. (setq org-agenda-prefix-format " ○ %t%s") #+end_src #+begin_details Grouping agenda entries together Instead of having the day's tasks all in one field, org-super-agenda allows us to use predicates to group entries together; e.g., by considering an entry's =:tags:= or its priority level. Since I'm placing all my tasks in a single file, under appropriate parent headings, I want entries to be shown according to their parent heading. Of-course, the top-most grouping, the important tasks, should be pulled out of their group and placed at the top. #+BEGIN_SRC emacs-lisp (use-package origami ) (use-package org-super-agenda :hook (org-agenda-mode . origami-mode) ;; Easily fold groups via TAB. :bind (:map org-super-agenda-header-map ("" . origami-toggle-node)) :config (org-super-agenda-mode) (setq org-super-agenda-groups '((:name "Important" :priority "A") (:name "Personal" :habit t) ;; For everything else, nicely display their heading hierarchy list. (:auto-map (lambda (e) (org-format-outline-path (org-get-outline-path))))))) ;; MA: No noticable effect when using org-super-agenda :/ ;; ;; Leave new line at the end of an entry. ;; (setq org-blank-before-new-entry '((heading . t) (plain-list-item . t))) #+END_SRC :OlderSetup: #+BEGIN_SRC emacs-lisp :tangle no ;; Default order is 0, first come first serve. ;; Items are “or”-ed by default. '((:name "Important" :tag "PackageFormer" :tag "PF" :tag "research" :and (:tag "JC" :priority "A") :and (:tag "WK" :priority "A") :priority "A") ;; Groups supply their own section names when none are given (:name "Living the dream!" :tag "personal") (:name "Discrete Mathematics" :tag "2fa3") (:name "Emacs Init" :tag "init") ;; (:priority<= "B" :order 1) ;; Everything else automatically gets its own group (:auto-category t) ))) #+END_SRC :End: # + Origami mode ---see below in [[Text Folding with Origami-mode]]--- # works well with super-agenda. Just ~M-x origami-mode~ then ~C-c f~ to # enable the folding hydra. # The [[https://github.com/alphapapa/org-super-agenda][org-super-agenda]] homepage shows complex configurations and pleasant screenshots contrasting with and without the system. E.g., you can change how entries in particular headings are displayed and coloured. #+end_details *** Step 4: Getting ready for the day :PROPERTIES: :CUSTOM_ID: Step-4-Getting-ready-for-the-day :END: After having seen our tasks for the week, we press [[kbd:d]] to enter daily view for the current day. Now we decide whether the items for today are ~A~: of high urgency & important; ~B~: of moderate urgency & importance; or ~C~: Pretty much optional, or very quick or fun to do. + ~A~ tasks should be both important /and/ urgently done on the day they were scheduled. - Such tasks should be relatively rare! - If you have too many, you're anxious about priorities and rendering priorities useless. + ~C~ tasks can always be scheduled for another day without much worry. - Act! If the thought of rescheduling causes you to worry, upgrade it to a ~B~ or ~A~. + As such, most tasks will generally be priority ~B~: Tasks that need to be done, but the exact day isn't as critical as with an ~A~ task. These are the “bread and butter” tasks that make up your day to day life. On a task item, or any org-heading, press [[kbd:,]] then one of [[kbd:A/B/C]] to set its priority. Then [[kbd:r]] to refresh. #+begin_details Pretty Prioritisation Markers link-here:Pretty-Prioritisation-Markers Let's set four priority levels and their colours: The more intense colours are for more urgent tasks. #+begin_src emacs-lisp (setq org-lowest-priority ?C) ;; Now org-speed-eky ‘,’ gives 3 options (setq org-priority-faces '((?A :foreground "red" :weight bold) ;; :background "LightCyan1") (?B :foreground "orange" :weight bold) (?C :foreground "green" :weight bold))) ;; See all colours with: M-x list-colors-display #+end_src + ~C-c ,~ anywhere to set the priority of the current heading. - We may press ~A-D~ or ~SPC~ to an remove existing priority. Priority markers are of the form ~[#𝒳]~, the [[https://github.com/harrybournis/org-fancy-priorities][fancy priorities]] package visually renders them as words or icons. #+begin_src emacs-lisp (use-package org-fancy-priorities :hook (org-mode . org-fancy-priorities-mode) :custom (org-fancy-priorities-list '("High" "MID" "LOW")) ;; "OPTIONAL" ;; Let's use the “Eisenhower map of priority”… ;; :custom (org-fancy-priorities-list '("Urgent and Important" ;; Do now! ;; "Not Urgent But Important" ;; Do schedule this. ;; "Urgent But Not Important" ;; Delegate? ;; "Not Urgent and Not Important")) ;; Don't do / Optional ) #+end_src #+end_details At work, my tasks list is massive and constantly growing; so to avoid stressing myself out, I try to add /time effort estimates/ to my tasks with doc:org-set-effort then I open my Org agenda and run ~d~ (for day view) then doc:org-agenda-columns (~C-c C-x C-c~) to see a column-view of my time estimates /along/ with *a total time estimate for the day.* Since there are unexpected pair-programming calls, or meetings go longer than expected, I only schedule for 7 hours each day (in a usual 8-hour work day); i.e., if my time estimates exceed 7h then I reschedule or cancel some things. #+begin_src emacs-lisp (require 'org-agenda) ;; How should the columns view look? (setq org-columns-default-format "%60ITEM(Task) %6Effort(Estim){:} %3PRIORITY %TAGS") ;; Press “c” in Org agenda to see the columns view; (default binding C-c C-x C-c is too long!) (org-defkey org-agenda-mode-map "c" #'org-agenda-columns) (org-defkey org-agenda-mode-map "C" #'org-agenda-goto-calendar) ;; Press “e” in columns view to alter “e”ffort “e”stimates. (require 'org-colview) (org-defkey org-columns-map "e" ;; Refresh after making an effort estimate. (lambda () (interactive) (org-agenda-set-effort) (org-agenda-columns))) #+end_src Near the top of my TODOS file, I have the following clickable link: It looks pretty and when I click it, takes me where I need to go. # ;; Show sum of efforts for a day in Org-Agenda day title #+begin_src org :tangle no [[elisp:(progn (org-agenda nil "a") (org-agenda-columns) (delete-other-windows))][Show agenda with time estimates]] #+end_src *** Step 5: Doing the work :PROPERTIES: :CUSTOM_ID: Step-5-Doing-the-work :END: Since ~A~ tasks are the important and urgent ones, if you do all of the ~A~ tasks and nothing else today, no one would suffer. It's a good day (─‿‿─). There should be no scheduling nor prioritising at this stage. You should not be touching your tasks file until your next review session: Either at the end of the day or the start of the next. + Leverage priorities! E.g., When a full day has several ~C~ tasks, reschedule them for later in the week without a second thought. - You've already provided consideration when assigning priorities. *** Step 6: Moving a task toward completion :PROPERTIES: :CUSTOM_ID: Step-6-Moving-a-task-toward-completion :END: My workflow states are described in the section [[Workflow States]] and contain states: ~TODO, STARTED, WAITING, ON_HOLD, CANCELLED, DONE~. + Tasks marked ~WAITING~ are ones for which we are awaiting some event, like someone to reply to our query. As such, these tasks can be rescheduled until I give up or the awaited event happens ---in which case I go to ~STARTED~ and document the reply to my query. + The task may be put off indefinitely with ~ON_HOLD~, or I may choose never to do it with ~CANCELLED~. Along with ~DONE~, these three mark a task as completed and so it needn't appear in any agenda view. I personally clock-in and clock-out of tasks ---keep reading---, where upon clocking-out I'm prompted for a note about what I've accomplished so far. Entering a comment about what I've done, even if it's very little, feels like I'm getting something done. It's an explicit marker of progress. In the past, I would make a “captain's log” at the end of the day, but that's like commenting code after it's written, I didn't always feel like doing it and it wasn't that important after the fact. The continuous approach of noting after every clock-out is much more practical, for me at least. *** Step 7: Archiving Tasks :Disabled: :PROPERTIES: :CUSTOM_ID: Step-7-Archiving-Tasks :header-args: :tangle no :END: During the review state, when a task is completed, ‘archive’ it with doc:org-archive-subtree or kbd:C-c_C-x_C-s: This marks it as done, adds a time stamp, and moves it to a local ~*.org_archive~ file. What was our *[[red:‘to do’]]* list becomes a *[[green:‘ta da’]]* list showcasing all we have done (•̀ᴗ•́)و Archiving keeps task lists clutter free, but unlike deletion it allows us, possibly rarely, to look up details of a task or what tasks were completed in a certain time frame ---which may be a motivational act, to see that you have actually completed more than you thought, provided you make and archive tasks regularly. We can use [[doc:org-search-view][~M-x org-search-view~]] to search an org file /and/ the archive file too, if we enable it so. #+BEGIN_SRC emacs-lisp ;; C-c a s ➩ Search feature also looks into archived files. ;; Helpful when need to dig stuff up from the past. (setq org-agenda-text-search-extra-files '(agenda-archives)) #+END_SRC #+begin_src emacs-lisp ;; enables the org-agenda variables. (require 'org-agenda) ;; Need this to have “org-agenda-custom-commands” defined. (unless noninteractive ;; ➩ Show my agenda upon Emacs startup. (org-agenda "a" "a")) #+end_src Let's install some helpful views for our agenda. + ~C-c a c~: See completed tasks at the end of the day and archive them. #+begin_src emacs-lisp ;; Pressing ‘c’ in the org-agenda view shows all completed tasks, ;; which should be archived. (add-to-list 'org-agenda-custom-commands '("c" todo "DONE|ON_HOLD|CANCELLED" nil)) #+end_src + ~C-c a u~: See unscheduled, undeadlined, and undated tasks in my todo files. Which should then be scheduled or archived. #+begin_src emacs-lisp (add-to-list 'org-agenda-custom-commands '("u" alltodo "" ((org-agenda-skip-function (lambda () (org-agenda-skip-entry-if 'scheduled 'deadline 'regexp "\n]+>"))) (org-agenda-overriding-header "Unscheduled TODO entries: ")))) #+end_src /At the end of the day, let's schedule at least 3 things that must be done the next day; i.e., have priority =A=./ ** Tag! You're it! :PROPERTIES: :CUSTOM_ID: Tag-You're-it :END: Even when items are categorised under their own parent headings, they may be related in some way and that can made explicit by adding a ~:tag:~ to their headings; e.g., two entries both have the ~:jasim:@work:~ tags, then looking for the ~:@work:~ tag shows me all entries that are tagged as “at work”. | Tags provide a cross-section of one's entries. | Tags let us find related stuff quickly, even though they're differently categorised. /After calling ~org-agenda~, we may select ~m~ to match for tags, or use ~org-tags-view~ to search for tags./ *What to tag?* Common tags are =:@laptop:, :@work:, :@home:= to identify the location where tasks take place ---Use: When I'm at a particular place, I need only consider tasks that apply to that place ;-) Other tags I use are =:𝑭𝑳:= to identify remarks or email or request from person 𝑭irstname 𝑳astname; or something that might be interesting to that person. I also use ~:video:, :book:, :paper:~; which let me quickly find all videos! Finally, I also use =:project_name:= to identify notes that may be of interest to a particular project, but are more appropriately categorised elsewhere ---e.g., when learning about an Emacs feature, I may tag my notes with another project's name to consider whether that feature could be useful there. *How to tag?* You can just add a ~:tag₁:⋯:tagₙ:~ after a heading. If you press space, before the tags, then they are automatically indented flushright to column 77; postive numbers do not flushright but use exact column number. #+BEGIN_SRC emacs-lisp (setq org-tags-column -77) ;; the default #+END_SRC Use ~C-c C-q~, or ~org-set-tags-command~, on a heading or just the speed key ~:~ on the asterisks of a heading to set the tags of an item ---as usual, with Helm we obtain a window of all existing tags to select from. Unfortunatley, this only supports having one tag; for more, you can add them in manually or … #+begin_src emacs-lisp ;; Press ‘:’ on a heading to add a tag, press TAB to see all tags, press RETURN on a tag to add it, press TAB again to add more tags, when all done press RETURN twice. (use-package helm-org ) ;; Helm for org headlines and keywords completion. (add-to-list 'helm-completing-read-handlers-alist '(org-set-tags-command . helm-org-completing-read-tags)) ;; Also provides: helm-org-capture-templates #+end_src Now ~:~ or ~C-c C-q~ will show existing tags for the current heading, press ~TAB~ to obtain a list of all exisiting tags, press ~C-SPC~ to select the desired tags, then =TAB= or =RET= to confirm the resulting tag list, and ~RET~ to finish or ~TAB~ to select more tags. - Press “:” on a heading to interactively select tags for it, please ENTER when done selecting tags. - Press “C-u :” to align all tags. Let's render tags by Unicode symbols. #+begin_src emacs-lisp (setq org-tag-alist '( ("Work" . ?w) ("Personal" . ?p) ;; Instead of a “:meeting:” tag, I prefer to call it “social credit” (✿◠‿◠) ("SocialCredit" . ?s) ;; “Out Of Office” ("OOO" . ?o) ("Family" . ?f) ("Errand" . ?e) ("Reading" . ?r) ("BlogRoll" . ?b) ("Urgent" . ?u))) ;; I can't see the entire tags menu when using doom-modeline, so let's redisplay ;; when the tags menu appears. ;; See https://emacs.stackexchange.com/a/70856 (advice-add #'fit-window-to-buffer :before (lambda (&rest _) (redisplay t))) (use-package org-pretty-tags :config (setq org-pretty-tags-surrogate-strings '(("Neato" . "💡") ("Blog" . "✍") ("Audio" . "♬") ("Video" . "📺") ("Book" . "📚") ("Running" . "🏃") ("Question" . "❓") ("Wife" . "💕") ("Text" . "💬") ; 📨 📧 ("Friends" . "👪") ("Self" . "🍂") ("Finances" . "💰") ("Car" . "🚗") ; 🚙 🚗 🚘 ("Urgent" . "🔥"))) ;; 📥 📤 📬 (org-pretty-tags-global-mode 1)) #+end_src ** Automating [[https://en.wikipedia.org/wiki/Pomodoro_Technique][Pomodoro]] ---“Commit for only 25 minutes!” :book: :PROPERTIES: :CUSTOM_ID: Automating-https-en-wikipedia-org-wiki-Pomodoro-Technique-Pomodoro-Commit-for-only-25-minutes :END: Effort estimates are for an entire task. Yet, sometimes it's hard to even get started on some tasks. + The code below ensures a 25 minute timer is started whenever clocking in happens. - The timer is in the lower right of the modeline. + When the timer runs out, we get a notification. + We may have the momentum to continue on the difficult task, or clock-out and take a break after documenting what was accomplished. #+BEGIN_SRC emacs-lisp ;; Tasks get a 25 minute count down timer (setq org-timer-default-timer 25) ;; Use the timer we set when clocking in happens. (add-hook 'org-clock-in-hook (lambda () (org-timer-set-timer '(16)))) ;; unless we clocked-out with less than a minute left, ;; show disappointment message. (add-hook 'org-clock-out-hook (lambda () (unless (s-prefix? "0:00" (org-timer-value-string)) (message-box "The basic 25 minutes on this difficult task are not up; it's a shame to see you leave.")) (org-timer-stop))) #+END_SRC Note that this does not conflict with the total effort estimate for the task. (I'm told there's a package already made for this ---maybe I need to stop writing code, and do more searches; then again, I've learned a lot by writing code.) ** Journaling :PROPERTIES: :CUSTOM_ID: Journaling :header-args: :tangle no :END: *** Intro to why even journal :ignore: :PROPERTIES: :CUSTOM_ID: Intro-to-why-even-journal :END: Thus far I've made it easy to quickly capture ideas and tasks, not so much on the analysis phase: - What was accomplished today? - What are some notably bad habits? Good habits? - What are some future steps? Rather than overloading the capture mechanism for such thoughts, let's employ ~org-journal~ ---journal entries are stored in files such as ~journal/20190407~, where the file name is simply the date, or only one file per year as I've set it up below. Each entry is the week day, along with the date, then each child tree is an actual entry with a personal title preceded by the time the entry was made. Unlike capture and its agenda support, journal ensures entries are maintained in chronological order with calendar support. Since org files are plain text files, an entry can be written anywhere and later ported to the journal. Or, written directly in the journal file if we add the necessary Org-header: Asterisks and time. The separation of concerns is to emphasise the capture stage as being quick and relatively mindless, whereas the journaling stage as being mindful. Even though we may utilise capture to provide quick support for including journal entries, I have set my journal to be on a yearly basis ---one file per year--- since I want to be able to look at previous entries when making the current entry; after all, it's hard to compare and contrast easily unless there's multiple entries opened already. As such, ideally at the end of the day, I can review what has happened, and what has not, and why this is the case, and what I intend to do about it, and what problems were encountered and how they were solved ---in case the problem is encountered again in the future. *Consequently, if I encounter previously confronted situations, problems,* *all I have to do is reread my journal to get an idea of how to progress.* Read more about [[https://www.google.com/search?q=on+the+importance+of+reviwing+your+day+daily&oq=on+the+importance+of+reviwing+your+day+daily&aqs=chrome..69i57.367j0j7&sourceid=chrome&ie=UTF-8][the importance of reviewing your day on a daily basis]]. Moreover, by journaling with Org on a daily basis, it can be relatively easy to produce a report on what has been happening recently, at work for example. I'd like to have multiple journals, for work and for personal life, as such I will utilise a prefix argument to obtain my work specific entries. *** The Setup :PROPERTIES: :CUSTOM_ID: The-Setup :END: # As of Oct2022, Using org-journal breaks my permissions. E.g., org-time-stamp / C-c . results in " # directory-files-recursively: Opening directory: Operation not permitted, /Users/musa/Desktop" Anyhow, the setup: #+begin_src emacs-lisp (use-package org-journal ;; C-u C-c j ⇒ Work journal ;; C-c j ⇒ Personal journal :bind (("C-c j" . my/org-journal-new-entry)) :config (setq org-journal-dir "~/Desktop/" ;; "~/Dropbox/journal/" org-journal-file-type 'yearly org-journal-file-format "Personal-%Y-%m-%d.org") (defun my/org-journal-new-entry (prefix) "Open today’s journal file and start a new entry. With a prefix, we use the work journal; otherwise the personal journal." (interactive "P") (let ((org-journal-dir (if prefix "~/Desktop/" org-journal-dir)) (org-journal-file-format (if prefix "Work-%Y-%m-%d.org" org-journal-file-format))) (org-journal-new-entry nil) (org-mode) (org-show-all)))) #+end_src # Convenient org-journal Snippet Extensions # Kill journal buffer after saving buffer (By @dhruvparamhans) # (defun org-journal-save-entry-and-exit() # "Simple convenience function. # Saves the buffer of the current day's entry and kills the window # Similar to org-capture like behavior" # (interactive) # (save-buffer) # (kill-buffer-and-window)) # (define-key org-journal-mode-map (kbd "C-x C-s") # 'org-journal-save-entry-and-exit) # *** Super Terse Tutorial :PROPERTIES: :CUSTOM_ID: Super-Terse-Tutorial :END: Bindings available in ~org-journal-mode~, when journaling: + ~C-c C-j~: Insert a new entry into the current journal file. - Note that keys for ~org-journal-new-entry~ shadow those for ~org-goto~. + ~C-c C-s~: Search the journal for a string. - Note that keys for ~org-journal-search~ shadow those for ~org-schedule~. # C-c C-f - go to the next journal file. # C-c C-b - go to the previous journal file. # Some key-bindings in org-journal overwrite org-mode key bindings # Major modes are supposed to only use key bindings of the form C-c C-?, where ? # can be any letter. With org-mode already using most interesting keys, # collisions are inevitable. Currently, org-journal overwrites # # C-c C-f (org-forward-heading-same-level) with org-journal-open-next-entry # C-c C-b (org-backward-heading-same-level) with org-journal-open-previous-entry # All journal entries are registered in the Emacs Calendar. To see available journal entries do ~M-x calendar~. Bindings available in the calendar-mode: + ~j~: View an entry in a new buffer. # C-j - view an entry but do not switch to it. + ~i j~: ‘I’nsert a new ‘j’ournal entry into the day’s file. + ~f w/m/y/f/F~: ‘F’ind, search, in all entries of the current week, month, year, all of time, of in all entries in the future. # # [ - go to previous day with journal entries. # ] - go to next day with journal entries. All journal entries are registered in the Emacs Calendar. To see available journal entries do M-x calendar. Bindings available in the calendar-mode: - [[kbd: j d]] display an entry; use [[kbd: j r]] to jump to the new reading buffer; reading is in doc:view-mode: [[kbd:q]] to quit reading and [[kbd:SPC]] to scroll. + [[kbd: j s w/m/y/f]] search the journal entries of the current week/month/year or for all time + ~[/]~ go the previous/next day with journal entries -------------------------------------------------------------------------------- *** Guided Journaling :PROPERTIES: :CUSTOM_ID: Guided-Journaling :END: Sometimes it can be tough to journal, but filling in a template can be a way to get started. Later on, we will setup [[Snippets ---Template Expansion]] which will allow us to write =journal_guided= then ~TAB~ to obtain the template below. Each =$𝓃= indicates a position that we may input text, after which we ~TAB~ to move to next location. Just like the ~undo-tree~ setup at the start of this read, we use a =noweb-ref= to present this template in a natural position; then later when template expansion it setup, we request it to be tangled. #+begin_src org :noweb-ref templates-from-other-places-in-my-init :tangle no ,** journal_guided: Introspection & Growth I'm writing from ${1:location}. Gut answer, today I feel ${2:scale}/10. ⇒ ${3:Few words or paragraphs to explain what's on your mind.} ${4: All things which cause us to groan or recoil are part of the tax of life. These things you should never hope or seek to escape. Life is a battle, and to live is to fight. ⟨ Press TAB once you've read this mantra. ⟩ $(when yas-moving-away-p "") } `(progn (eww "https://www.dailyinspirationalquotes.in/") (sit-for 2) (when nil let eww load) (read-only-mode -1) (goto-line 52) (kill-line) (kill-buffer) (yank))` ${7: Self Beliefs: + I am working on a healthier lifestyle, including a low-carb diet. - I’m also investing in a healthy, long-lasting relationship. ➩ These are what I want and are important to me. ⇦ + I will not use any substances to avoid real issues in my life. I must own them. + Everything I’m searching for is already inside of me. + Progress is more important than perfection. ⟨ Press TAB once you've read these beliefs. ⟩ $(when yas-moving-away-p "") } *Three things I'm grateful for:* 1. ${8:??? … e.g., old relationship, something great yesterday, an opportunity I have today, something simple near me within sight} 2. ${9:??? … e.g., old relationship, something great yesterday, an opportunity I have today, something simple near me within sight} 3. ${10:??? … e.g., old relationship, something great yesterday, an opportunity I have today, something simple near me within sight} *Three things that would make today great:* 1. ${11:???} 2. ${12:???} 3. ${13:???} *What one thing is top of mind today?* ${14:???} *What’s one opportunity I want to go after?* ${15:???} *What’s one thing I’m really proud of OR I’m amazed and in awe of?* ${16:???} $0 #+end_src Besides a bit of webscraping to obtain a daily inspirational quote image, and the necessary yasnippet code, this template was taken from a discussion on [[https://news.ycombinator.com/item?id=20849148][Hacker news: “I find journaling indispensable”]]. In time, I will likely alter it to meet my needs, but I like it as it is right now (•̀ᴗ•́)و ** Workflow States :PROPERTIES: :CUSTOM_ID: Workflow-States :END: Here are some of my common workflow states, ---the ‘X/Y’ indicates to do action ‘X’ when entering a state and ‘Y’ when leaving it, with ‘!’ denoting a timestamp should be generated and ‘@’ denoting a user note should be made. #+begin_src emacs-lisp ;; TODO: Add the line “(declare (indent defun))” right after the docstring of “lf-define”, ;; so that Emacs indents it like a “defun”. ;; See https://www.gnu.org/software/emacs/manual/html_node/elisp/Indenting-Macros.html ;; ;; Until then, use the following incantation: (lf-define (get 'lf-define 'lisp-indent-function) 'defun) (lf-define my/declare-workflow-states (states) [:requires (listp states) :ensures (stringp result)] "Declare STATES as todo-states. STATES is a list of (name on-entry on-exit color) lists." (setq org-todo-keywords (list (cons 'sequence (cl-loop for (state . props) in states for first-letter = (downcase (substring state 0 1)) for on-entry = (plist-get props :on-entry) for on-exit = (plist-get props :on-exit) for entry = (if (not on-entry) "" (if (equal on-entry 'note) "@" "!")) for exit = (if (not on-exit) "" (if (equal on-exit 'note) "/@" "/!")) collect (if (equal state "|") state (format "%s(%s%s%s)" state first-letter entry exit)))))) (setq org-todo-keyword-faces (cl-loop for (state . props) in states collect (list state :foreground (plist-get props :foreground) :weight 'bold))) "✔ Invoke “org-mode-restart” in existing Org buffers for this to take effect.") ;; These denote ‘progress’ on a task. ;; For contextual information, one uses “tags”. ;; For example, a delegated task could be in state “STARTED” and tagged “:delegate:James:”. (my/declare-workflow-states ;; Transitions: TODO → INVESTIGATED → STARTED ⟷ {AWAITING_REVIEW | PAUSED} → {DONE | CANCELLED} '( ;; Tasks that are not started and not planned. They could be the ;; backlogs or the GTD’s someday/maybe. These tasks could be converted ;; to NEXT during a weekly review. ("TODO" :foreground "red") ;; A task moves from TODO to NEXT only when I've actually split the task ;; into small achievable chunks; ie i've done some investigation into ;; the task and thought about what steps I need to do to actually get ;; the task done. With this planning in place, I can then ensure I ;; allocate sufficient timeblocks to work on the subtasks of this ;; task. (Aside, a “project” is a task with multiple subtasks.) ;; ;; ;; Use my 1/1 time with my manager/peers to review my INVESTIGATED/NEXT ;; findings for the tickets of the current sprint. I just want to make ;; sure I'm on the right track, before starting to work on them. Or, if ;; a ticket is not investigated, do that with my manager. I find that while it ;; might take me half an hour or more, it'll take like 5 minutes with ;; him since he's familiar with the code-base. ;; ;; ;; ≈ NEXT. Tasks that are not started but planned to do as soon as I ;; can. When there is no actionable STARTED (e.g., blocked), I start one ;; of those and convert it to STARTED. ("INVESTIGATED" :on-entry timestamp :foreground "dark orange") ;; Tasks that are working in progress (“open loops”). I work on these ;; tasks before starting another NEXT task to avoid too many open loops ;; at any moment. ("STARTED" :on-entry timestamp :foreground "blue") ;; When did I start? ("PAUSED" :on-entry note :on-exit timestamp :foreground "magenta") ;; When & why is the task paused? ;; When a task goes into WAITING, I've finished the task as much as possible ;; and now need to rely on someone else; e.g., for feedback. ;; ;; I don't want to see these scheduled tasks in my Agenda. ;; ;; In the Weekly Review, I will take time to look at my WAITING tasks ;; and if they've been waiting ~2 weeks, then I should message the relevant people to unblock it. ;; ;; “In progress, but blocked by others” ("WAITING" :on-entry timestamp :foreground "red2") ;; When did this task enter in-review? ("|") ;; All states after this special marker are “terminal” and so not shown in the org-agenda: (setq org-agenda-skip-scheduled-if-done t) ("DONE" :on-entry timestamp :foreground "forest green") ;; When did this task finish? ;; This is useful at work to keep track of *why* we have decided against doing a task; in my Org file there may be more, private, reasons not mentioned in the company's Jira file. (For example). ;; ;; This is useful at home since I might have had the same task/idea again and it's useful to know *why* I didn't do it last time; also useful for when doing Annual Reviews. ("CANCELLED" :on-entry note :foreground "saddle brown"))) ;; Why was this task cancelled? (Notes come with a timestamp!) ;; Since DONE is a terminal state, it has no exit-action. ;; Let's explicitly indicate time should be noted. (setq org-log-done 'time) ;; I prefer to log TODO creation also (setq org-treat-insert-todo-heading-as-state-change t) ;; When a task goes into the WAITING state, please remove its schedule ;; so that it does not appear in my agenda. (However, it's still not “done” and so appears when I do “C-c a t” for example.) ;; Source: https://emacs.stackexchange.com/a/2760 (add-hook 'org-after-todo-state-change-hook (defun rasmus/remove-schedule () "Remove SCHEDULED-cookie is switching state to WAITING." (save-excursion (and (equal (org-get-todo-state) "WAITING") (org-get-scheduled-time (point)) (when (search-forward-regexp org-scheduled-time-regexp nil t) (or (delete-region (match-beginning 0) (match-end 0)) t)) (get-buffer "*Org Agenda*") (with-current-buffer "*Org Agenda*" (org-agenda-redo)))))) #+end_src In particular, we transition from ~TODO~ to ~STARTED~ once 15 minutes, or a reasonable amount, of work has transpired. Since all but one state are marked for logging, we could use the ~lognotestate~ logging facility of org-mode, which prompts for a note every time a task’s state is changed. Entering a comment about what I've done, even if it's very little, feels like I'm getting something done. It's an explicit marker of progress and motivates me to want to change my task's states more often until I see it marked ~DONE~. Now we press ~C-c C-t~ then the letter shortcut to actually make the state of an org heading. #+begin_src emacs-lisp (setq org-use-fast-todo-selection t) #+end_src We can also change through states using Shift- left, or right. # (setq org-treat-S-cursor-todo-selection-as-state-change nil) Let's draw a state diagram to show what such a workflow looks like. [[http://plantuml.com/index][PlantUML]] supports drawing diagrams in a tremendously simple format ---it even supports Graphviz/DOT directly and many other formats. Super simple setup instructions can be found [[http://eschulte.github.io/babel-dev/DONE-integrate-plantuml-support.html][here]]; below are a bit more involved instructions. Read the manual [[http://plantuml.com/guide][here]]. #+begin_src emacs-lisp ;; Install the tool ; (async-shell-command "brew tap adoptopenjdk/openjdk; brew cask install adoptopenjdk13") ;; Dependency ; (async-shell-command "brew install plantuml") ;; Tell emacs where it is. ;; E.g., (async-shell-command "find / -name plantuml.jar") (setq org-plantuml-jar-path "/usr/local/Cellar/plantuml/1.2022.14/libexec/plantuml.jar") ;; Enable C-c C-c to generate diagrams from plantuml src blocks. (add-to-list 'org-babel-load-languages '(plantuml . t) ) (require 'ob-plantuml) ; Use fundamental mode when editing plantuml blocks with C-c ' (add-to-list 'org-src-lang-modes '("plantuml" . fundamental)) #+end_src # # (async-shell-command "cp workflow.png ~/alhassy.github.io/assets/img/") Let's use this! # The source block is replaced with the generated image in-place, by default. # #+begin_src plantuml :file workflow.png :exports code :cache (progn (async-shell-command "cp workflow.png ~/alhassy.github.io/assets/img/") "yes") #+begin_src plantuml :file images/workflow.png :tangle no :exports both :eval never-export :results replace skinparam defaultTextAlignment center /' Text alignment '/ skinparam titleBorderRoundCorner 15 skinparam titleBorderThickness 2 skinparam titleBorderColor red skinparam titleBackgroundColor Aqua-CadetBlue title My Personal Task States [*] -> Todo /' This is my starting state '/ Done -right-> [*] /' This is an end state '/ Cancelled -up-> [*] /' This is an end state '/ /'A task is “Todo”, then it's “started”, then finally it's “done”. '/ Todo -right-> Started Started -down-> Waiting Waiting -up-> Started Started -right-> Done /'Along the way, I may pause the task for some reason then return to it. This may be since I'm “Blocked” since I need something, or the task has been put on “hold” since it may not be important right now, and it may be “cancelled” eventually. '/ Todo -down-> Waiting Waiting -up-> Todo Waiting -up-> Done Todo -down-> On_Hold On_Hold -> Todo On_Hold -down-> Cancelled Waiting -down-> Cancelled Todo -down-> Cancelled /' The Org-mode shortcuts for these states are as follows. '/ Todo : t On_Hold : h Started : s Waiting : w Cancelled : c Done : d /' If a task is paused, we should document why this is the case. '/ note right of Waiting: Note what is\nblocking us. note right of Cancelled: Note reason\nfor cancellation. note bottom of On_Hold: Note reason\nfor reduced priority. center footer ♥‿♥ Org-mode is so cool (•̀ᴗ•́)و /' Note that we could omit the “center, left, right” if we wished, or used a “header” instead.'/ #+end_src #+RESULTS: [[file:images/workflow.png]] # (org-display-inline-images t t) # (shell-command "rm workflow.png") # +HTML: My Personal Task States Of note: + Multiline comments are with ~/' comment here '/~, single quote starts a one-line comment. + Nodes don't need to be declared, and their names may contain spaces if they are enclosed in double-quotes. + One forms an arrow between two nodes by writing a line with ~x ->[label here] y~ or ~y <- x~; or using ~-->~ and ~<--~ for dashed lines. The label is optional. To enforce a particular layout, use ~-X->~ where ~X ∈ {up, down, right, left}~. + To declare that a node ~x~ has fields ~d, f~ we make two new lines having ~x : f~ and ~x : d~. + One adds a note near a node ~x~ as follows: ~note right of x: words then newline\nthen more words~. Likewise for notes on the ~left, top, bottom~. - A note can be on several lines. It's terminated by ~end note~. + Interesting sprites and many other things can be done with PlantUML. Read the docs. This particular workflow is inspired by [[http://doc.norang.ca/org-mode.html][Bernt Hansen]] ---while quickly searching through the PlantUML [[http://plantuml.com/guide][manual]]: The above is known as an “activity diagram” and it's covered in §4. Org-mode may be used with PlantUML: + See §11,12 for using Org-mode notation to form ‘mindmaps’ and ‘work breakdown structures’. + Org-mode text formatters are also acknowledged but the delimiters must be doubled; see §16.1. You can quickly write and see the resulting UMLs using https://liveuml.com/, for the most part. ** Clocking Work Time :PROPERTIES: :CUSTOM_ID: Clocking-Work-Time :END: Let's keep track of the time we spend working on tasks that we may have captured for ourselves the previous day. Such statistics provides a good idea of how long it actually takes me to accomplish a certain task in the future and it lets me know where my time has gone. + Clock in :: on a heading with ~I~, or in the subtree with ~C-c C-x C-i~. + Clock out :: of a heading with ~O~, or in the subtree with ~C-c C-x C-o~. + Clock report :: See clocked times with ~C-c C-x C-r~. After clocking out, the start and end times, as well as the elapsed time, are added to a drawer to the heading. We can punch in and out of tasks as many times as desired, say we took a break or switched to another task, and they will all be recorded into the drawer. #+begin_src emacs-lisp ;; Record a note on what was accomplished when clocking out of an item. (setq org-log-note-clock-out t) #+end_src To get started, we could estimate how long a task will take and clock-in; then clock-out and see how long it actually took. # To review the day’s accomplishments, type ‘l’ # (org-agenda-log-mode) from the agenda view. Sometimes, at the beginning at least, I would accidentally invoke the transposed command ~C-x C-c~, which saves all buffers and quits Emacs. So here's a helpful way to ensure I don't quit Emacs accidentally. #+begin_src emacs-lisp (setq confirm-kill-emacs 'yes-or-no-p) #+end_src A few more settings: #+begin_src emacs-lisp ;; Resume clocking task when emacs is restarted (org-clock-persistence-insinuate) ;; Show lot of clocking history (setq org-clock-history-length 23) ;; Resume clocking task on clock-in if the clock is open (setq org-clock-in-resume t) ;; Sometimes I change tasks I'm clocking quickly ---this removes clocked tasks with 0:00 duration (setq org-clock-out-remove-zero-time-clocks t) ;; Clock out when moving task to a done state (setq org-clock-out-when-done t) ;; Save the running clock and all clock history when exiting Emacs, load it on startup (setq org-clock-persist t) ;; Do not prompt to resume an active clock (setq org-clock-persist-query-resume nil) ;; Include current clocking task in clock reports (setq org-clock-report-include-clocking-task t) #+end_src *** Finding tasks to clock in :PROPERTIES: :CUSTOM_ID: Finding-tasks-to-clock-in :END: Use one of the following options, with the top-most being the first to be tried. + From anywhere, ~C-u C-c C-x C-i~ yields a pop-up for recently clocked in tasks. + Pick something off today's agenda scheduled items. + Pick a ~Started~ task from the agenda view, work on this unfinished task. + Pick something from the ~TODO~ tasks list in the agenda view. # Reporting activities # C-c C-x i ~C-c C-x C-d~ also provides a quick summary of clocked time for the current org file. *** Estimates versus actual time :PROPERTIES: :CUSTOM_ID: Estimates-versus-actual-time :END: Before clocking into a task, add to the properties drawer ~:Effort: 1:25~ or ~C-c C-x C-e~, for a task that you estimate will take an hour and twenty-five minutes, for example. Now the modeline will mention the time elapsed alongside the task name. *Woah!* #+begin_src emacs-lisp (push '("Effort_ALL" . "0:15 0:30 0:45 1:00 2:00 3:00 4:00 5:00 6:00 0:00") org-global-properties) #+end_src #+begin_quote org Use speed keys ~e/E~ to insert an effort estimate, with the above being provided options, or to increment the current effort to the next one in the above list. #+end_quote This is also useful when you simply want to put a time limit on a task that wont be completed anytime soon, say writing a thesis or a long article, but you still want to work on it for an hour a day and be warned when you exceed such a time constraint. :Not_working_for_me: Even if you switch tasks then clock into this task again, the alarm will ring again, nagging you to actual listen to yourself and work on other matters. #+begin_src emacs-lisp (setq org-clock-sound "~/.emacs.d/school-bell.wav") #+end_src :end: When you've gone above your estimate time, the modeline colours it red. ** Habit Formation :PROPERTIES: :CUSTOM_ID: Habit-Formation :END: /The/ reason to use habits is that they come with a graph indicating consistency by colour, and the goal of the game is to have [[https://lifehacker.com/jerry-seinfelds-productivity-secret-281626][the longest possible chain]] ---no red days! A ‘habit’ is a usual (recurring) task marked as a habit: Use =C-c C-x p= to set the =STYLE= property to =habit= on a task to set it as a habit. #+BEGIN_SRC emacs-lisp (require 'org-habit) ;; To actually see the consistency graph in org-agenda ;; Show habits for every day in the agenda. (setq org-habit-show-habits t) (setq org-habit-show-habits-only-for-today nil) ;; This shows the ‘Seinfeld consistency’ graph closer to the habit heading. (setq org-habit-graph-column 90) ;; In order to see the habit graphs, which I've placed rightwards, let's ;; always open org-agenda in ‘full screen’. ;; (setq org-agenda-window-setup 'only-window) (setq org-habit-completed-glyph ?😊) (setq org-habit-today-glyph 33) ;; When showing the agenda, do not collect habits together at the bottom, as is the default. (add-to-list 'org-agenda-sorting-strategy '(agenda time-up priority-down category-keep)) #+END_SRC | /inch by inch anything's a cinch!/ | ~!~ means today and ~⋆~ means a task has been done on that day; intuitively green means you're on track, yellow is warning sign of overdue, red is overdue, and blue is an acceptable break day. # ! means today and * means a task has been done on that day. The color interpretation is intuitive: Green means on track, yellow warning sign of overdue, red overdue, and blue “still early, don’t feel bad taking a break”. [[https://cpbotha.net/2019/11/02/forming-and-maintaining-habits-using-orgmode/org-habits-screenshot.png]] Here's an example habit from the [[https://orgmode.org/manual/Tracking-your-habits.html][Org-mode manual]], where ~.+𝒳d/𝒴d~ reads /perform the habit once every 𝒳 days, but never let me go 𝒴 days without doing it./ - (If you are procrastinating often, you can add a period in front of the repeater, such as .+2d which will reschedule the completed item two days /after it is completed/.) #+begin_example org ,** TODO Shave SCHEDULED: <2020-01-08 Wed .+2d/4d> :PROPERTIES: :STYLE: habit :END: #+end_example /Shave every 2 days, but we can take a 3-day break; however, on the 4th day, gotta shave!/ (To “ignore” a habit, just reschedule it for another day.) Remember that in the agenda view if you alter a task, say with ~t~ to mark it done, then you need to use ~s~ to save the underlying todo/notes files; otherwise, any ~g~ will revert the change in the agenda buffer. Habits are about “getting a little bit better at being alive, every day”. - I don't mark habits with =TODO= so that they don't clutter up by Org-Agenda Todos List, =C-c a t=. However, I still press ~t~ and set them to ~DONE~ and see a message indicating that they're now rescheduled for a future time. # (If you use ~t~ on a habit headline and mark it as ~Done~, you'll see that it # briefly toggles to ~DONE~ and then automatically switches back to ~TODO~.) - When this happens, the :LAST_REPEAT: timestamp will be automatically updated to the current time, and, more importantly, the SCHEDULED date will be automatically updated to the next earliest date when this habit should be exercised. - Habits appear grey in the agenda view, so that the non-habits stick out, and hopefully in-time the habits become second nature and no longer need to be in my agenda ---or at least, require little thought (which is happening with the aid of checklists for my habits). /People underestimate what can be done if you have a spare hour./ ** Actually Doing Things ---or /Sending notifications from Emacs/ :PROPERTIES: :CUSTOM_ID: Actually-Doing-Things :END: Let's setup a little audio-visual reminder to regularly check my agenda and ensure I'm not narrowing on a single task and ignoring others. + More generally, we can use this snippet of code to let Emacs notify us of other things. + For instance, the doc:message function shows text in the minibuffer, which might be missed when there are multiple incoming messages or focus is on a non-Emacs application (gasp!). Then, doc:my/notify could be used to produce MacOS system-wide notifications. The [[https://askubuntu.com/a/501917][text-to-speech tool]] we'll use is ~say~; which [[https://www.lifewire.com/mac-say-command-with-talking-terminal-2260772#:~:text=In%20the%20left%20pane%2C%20select,voices%20your%20Mac%20can%20use.][on a Mac can be activated]] in a browser: Select some text, right-click, select ~Speech~, then ~Start/Stop Speaking~. - TODO: Make the command below randomly use an English speaking voice; “tldr say” to learn more. - TODO: Also finish reading the above mentioned links, with nice examples. #+begin_src emacs-lisp ;; Obtain a notifications and text-to-speech utilities (system-packages-ensure "say") ;; Built-into MacOS, but can be downloaded in Ubuntu (system-packages-ensure "terminal-notifier") ;; MacOS specific ;; System Preferences → Notifications → Terminal Notifier → Allow “alerts”. ;; E.g.,: (shell-command "terminal-notifier -title \"Hiya\" -message \"hello\"") #+end_src By default, notifications are in banner style ---they go away automatically--- we can use alert style ---in which they stay until dismissed--- in MacOS as follows: =System Preferences → Notifications → terminal-notifier → Alerts=. #+begin_src emacs-lisp (cl-defun my/notify (message &key (titled "") at repeat-every-hour open) "Notify user with both an visual banner, with a beep sound, and a text-to-speech recitation. When the user clicks on the resulting notification, unless a given OPEN url is provided, the Emacs application is brough into focus. MESSAGE and TITLE are strings; AT is a time string as expected of `run-at-time' such as \"11.23pm\" or \"5 sec\"; REPEAT-EVERY-HOUR is a floating-point number of hours to continuously repeat the alert. OPEN is a URL that is opened when the user clicks the notification. This can be a web or file URL, or any custom URL scheme. I initially used optional arguments, but realised that in due time it would be more informative to use named arguments instead. Example uses: ;; In 5 minutes from now, remind me to watch this neato video! (my/notify \"🔔 Get things done! 📎 💻 \" :open \"https://www.youtube.com/watch?v=23tusPiiNZk&ab_channel=Motiversity\" :at \"5 minutes\") ;; :at \"5 sec\" ;; Remind me to exercise every 1.5hours; starting at 8:00am. (my/notify \"Take a 5min break and get your blood flowing!\" :titled \"Exercise\" :at \"8:00am\" :repeat-every-hour 1.5) ;; Actually getting things done! (my/notify \"Is what you're doing actually in alignment with your goals? Maybe it's time to do another task?\" :titled \"Check your agenda!\" :at \"10:00am\" :repeat-every-hour 2) " (run-at-time at ;; the time to make the alert (when repeat-every-hour (* 60 60 repeat-every-hour)) #'async-shell-command (format "%s" (s-replace "\n" "" (s-join " " (--map (format "%s" it) `(terminal-notifier -title ,(pp-to-string titled) -message ,(s-replace "\\n" "\n" (pp-to-string message)) ;; Play a random sound when the notification appears. See sound names with: ls /System/Library/Sounds ;; Use the special NAME “default” for the default notification sound. -sound ,(progn (require 'seq) (seq-random-elt (s-split "\n" (shell-command-to-string "ls /System/Library/Sounds")))) ;; Don't create duplicates of the notification, just one instance; ;; i.e., each notification belongs to a group and only one alert of the group may be present at any one time. -group ,(pp-to-string titled) ;; Activate the application specified by ID when the user clicks the notification. -activate org.gnu.Emacs ,@(when open `(-open ,(pp-to-string open))) ;; Run the shell command COMMAND when the user clicks the notification. ;; -execute COMMAND ;; & ;; … and then speak! … ;; NOTE:This was getting annyoning in the middle of work meetings. ;; say ,(s-replace "\\n" " " (pp-to-string message)) ))))))) #+end_src #+RESULTS: : my/notify The following two actual uses cases are also mentioned in doc:my/notify docstring, since I want the documentation to be self-contained. #+begin_src emacs-lisp ;; (Emojis look terrible in Lisp; but much better when the alert is actually made!) ;; Remind me to exercise every 1.5hours; starting at 8:00am. (my/notify "Take a 5min break and get your blood flowing!\n\t\t🚣 🏃‍♂️ 🧗‍♂️ 🧘‍♂️ 🏊 🏋 🚴‍♂️" :titled "🤾‍♀️ Exercise 🚵‍♂️" :at "8:00am" :repeat-every-hour 1.5 :open "https://www.youtube.com/watch?v=23tusPiiNZk&ab_channel=Motiversity") ;; Actually getting things done! (my/notify "Is what you're doing actually in alignment with your goals? ✔️📉 Maybe it's time to do another task? 📋" :titled "📆 Check your agenda! 🔔" :at "10:00am" :repeat-every-hour 2) #+end_src + [[https://emacs.stackexchange.com/questions/3844/good-methods-for-setting-up-alarms-audio-visual-triggered-by-org-mode-events][Here]] is an approach to triggering audio-visual alarms from Org-mode events ---using ~org-agenda-to-appt~. + Emacs's built in [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Appointments.html][appointment notification facility]] can also be used as a alarm clock via ~M-x appt-add~. The [[https://github.com/jcs-elpa/marquee-header][marquee-header]] package let's show messages as “horizontal moving text along the top of the Emacs frame”, which is neat. - Slightly related is [[https://github.com/jcs-elpa/logms][logms]], which let's us make ~message~ calls but we can also /see/ the context/source where those calls were made; as well as a clickable link back to the source. ** Using Gnus for Gmail :Disabled: :PROPERTIES: :CUSTOM_ID: Using-Gnus-for-Gmail :header-args: :tangle no :END: :Alread_done_elsewhere: 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 "alhassy@gmail.com") #+end_src For some fun, run this cute method. #+BEGIN_SRC emacs-lisp :tangle no (animate-birthday-present user-full-name) #+END_SRC :End: By default, in Emacs, we may send mail: Write it in Emacs with ~C-x m~ ---or doc:compose-mail---, 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 earlier (•̀ᴗ•́)و Folowing [[https://eschulte.github.io/emacs24-starter-kit/starter-kit-gnus.html][this tutorial]], 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~. # (gnutls-available-p) 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 ;; Get mail using port 993/IMAP/“Internet Message Access Protocol” (setq gnus-select-method '(nnimap "gmail" (nnimap-address "imap.gmail.com") (nnimap-server-port 993) (nnimap-stream ssl))) ;; Send mail using port 587/SMTP/“Simple Mail Transfer Protocol” (setq message-send-mail-function 'smtpmail-send-it smtpmail-starttls-credentials '(("smtp.gmail.com" 587 nil nil)) smtpmail-auth-credentials '(("smtp.gmail.com" 587 "alhassy@gmail.com" nil)) smtpmail-default-smtp-server "smtp.gmail.com" smtpmail-smtp-server "smtp.gmail.com" smtpmail-smtp-service 587) #+end_src # (system-packages-install "starttls") # (setq gnus-ignored-newsgroups "^to\.\|^[0-9. ]+\( \|$\)\|^["]"[#'()]") 3. Get an email password for GNUS: 1. Go to https://myaccount.google.com/security. 2. Enable ~2-Step Verification~ 3. Click on ~App passwords~, login, then generate a new password with, say, name ~Emacs Gnus~. 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 smtp.gmail.com login alhassy@gmail.com password xxxxxxxxxxxxxxxx port 587 ​machine imap.gmail.com login alhassy@gmail.com password xxxxxxxxxxxxxxxx port 993 default login alhassy@gmail.com password xxxxxxxxxxxxxxxx #+end_src 4. 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]]. Only news groups with /unread mail/ are shown; to see all your groups (Gmail ‘tags’), press ~A A~ (doc:gnus-group-list-active), then press ~u~ to toggle (un)subscription to such groups and they will show up in the main group buffer ---if they have /unread mail/. See [[https://sachachua.com/blog/2008/05/emacs-gnus-organize-your-mail/][here]] for a tutorial on splitting mail groups, automatically or fancily filing them away. -------------------------------------------------------------------------------- #+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. (when my/personal-machine? (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. - Other possibly useful links: + [[http://www.cataclysmicmutation.com/2010/11/multiple-gmail-accounts-in-gnus/][Multiple Gmail accounts in Gnus]] -------------------------------------------------------------------------------- #+begin_details Super Terse Tutorial link-here: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. #+end_details #+begin_details GNUS Prettifications link-here:gnus-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 :config (all-the-icons-install-fonts 'install-without-asking)) ;; Make mail look pretty (use-package all-the-icons-gnus :disabled 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 :disabled t :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 #+end_details #+begin_details "Sending Mail with Lisp ---e.g., as a Bulk Mailer" link-here:bulk-mailer #+begin_src emacs-lisp :results replace :wrap template (defun my/email (to subject body) (compose-mail to subject) (insert body) (message-send-mail) ;; Appends info to the message buffer ; (let ((kill-buffer-query-functions nil)) (kill-this-buffer)) (ignore-errors (undo)) ;; Undo that addition (message-kill-buffer) (message "Send email to %s" to)) ;; Close that message buffer #+end_src #+begin_src emacs-lisp :results replace :wrap template :tangle no ;; Example (my/email (format "%s <%s>" user-full-name user-mail-address) ;; To "Test" ;; Subject "Why hello there!") ;; Email body #+end_src #+end_details #+begin_details [Disabled] Auto-completing mail addresses # :CUSTOM_ID: 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 :tangle no (use-package gmail2bbdb :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. #+end_details #+begin_details [Disabled] Feeds to Blogs link-here:gnus-feeds-to-blogs 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. #+end_details *** Capturing Mail as Todo/Notes :PROPERTIES: :CUSTOM_ID: Capturing-Mail-as-Todo-Notes :END: 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]], above, is a way to, well, capture ideas and notes /without/ interrupting the current workflow. Above, in the section on capturing, we define doc: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 [[kbd:c]] captures mail content with the option to provide additional remarks, and [[kbd:C]] to silently do so without additional remarks. #+BEGIN_SRC emacs-lisp (with-eval-after-load 'gnus ;; Orginally: c ⇒ gnus-summary-catchup-and-exit (bind-key "c" #'my/org-capture-buffer gnus-article-mode-map) ;; Orginally: C ⇒ gnus-summary-cancel-article (bind-key "C" (lambda (&optional keys) (interactive "P") (my/org-capture-buffer keys 'no-additional-remarks)) gnus-article-mode-map)) #+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./ *** Email contacts :PROPERTIES: :CUSTOM_ID: Email-contacts :END: I have a personal file, ~contacts.org~, with Emacs Lisp src blocks contributing to a list variable ~my/contacts~. This list consists of entries of the shape: #+begin_src emacs-lisp :tangle no (:name "Jasim Jasonsama" :phone 123-455-4321 :email bobert_billiam@emacsmail.com) #+end_src With the following snippet, I can write ~contacts~ then kbd:TAB to select a personal contact. #+begin_src org :noweb-ref templates-from-other-places-in-my-init :tangle no :comments none ,** contacts: Get the email of one of my personal contacts ${1:`(and (or (featurep 'my/contacts) (org-babel-load-file "~/Dropbox/contacts.org")) (yas-choose-value (--map (format "%s <%s>" (getf it :name) (getf it :email)) my/contacts)))`} $0 #+end_src ** [[https://github.com/sabof/stripe-buffer][Add stripes to "list" buffers]] :PROPERTIES: :CUSTOM_ID: https-github-com-sabof-stripe-buffer-Add-stripes-to-list-buffers :END: #+begin_src emacs-lisp ;; Make every other line of a buffer grey (or whatever you like). ;; Useful for buffers that list things. ;; I want it to make my Org tables look nice. Even better when org-modern is activated. (use-package stripe-buffer :defer 100 :config (add-hook 'org-mode-hook 'turn-on-stripe-table-mode)) #+end_src ** Basic Agenda Config #+begin_src emacs-lisp (setq org-agenda-files (list "~/Documents/notes.org")) (setq org-agenda-span 'week) ;; (setq org-agenda-custom-commands '(("o" "Open Loops" tags-tree "TODO=\"STARTED\"" ))) (setq org-log-into-drawer t) ;; hide the log state change history a bit better (setq org-fold-catch-invisible-edits 'show-and-error ;; Avoid accidental edits to folded sections org-special-ctrl-a/e t ;; C-a/C-e know about leading “*” and ending :tags: ;; Agenda styling org-agenda-tags-column -80 org-agenda-time-grid '((daily today require-timed) (800 1000 1200 1400 1600 1800 2000) " ───── " "───────────────") org-agenda-current-time-string "◀── now ─────────────────────────────────────────────────") ;; p to “p”ush back a task to the next day. (add-hook 'org-agenda-mode-hook ;; Note: There's also org-agenda-date-earlier to change the date by -1 day. (lambda () (define-key org-agenda-mode-map "p" 'org-agenda-date-later) ;; Reschedule agenda items to today, “right 𝓃ow”, with a single command (define-key org-agenda-mode-map "n" (defun org-agenda-reschedule-to-today () (interactive) (flet ((org-read-date (&rest rest) (current-time))) (call-interactively 'org-agenda-schedule)))))) #+end_src ** Informative [[https://arc.net/l/quote/jhczngfy][log notes]] Let's change only two pieces, to make them more informative. #+begin_src emacs-lisp (setq org-log-note-headings '((done . "CLOSING NOTE %t") (state . "State %-12s from %-12S %t") (note . "Note taken on %t") ;; (reschedule . "Rescheduled from %S on %t") ;; Default (reschedule . "Schedule changed on %t: %S -> %s") (delschedule . "Not scheduled, was %S on %t") (redeadline . "Deadline changed on %t: %S -> %s") ;; (redeadline . "New deadline from %S on %t") ;; Default (deldeadline . "Removed deadline, was %S on %t") (refile . "Refiled on %t") (clock-out . ""))) #+end_src ** COMMENT See all motivational images I have, and indent things so it's clearer when something is a child of something else TODO: Causes weird max-image-size issues? #+begin_src emacs-lisp (defun my/org-settings () (org-display-inline-images) (org-indent-mode) nil) (add-hook 'org-mode-hook #'my/org-settings) #+end_src ** COMMENT org-num-mode :SeemsTooBrokenForMe: #+begin_src emacs-lisp ;; Please dynamically number all headlines (setq org-startup-numerated t) ;; More options @ https://orgmode.org/manual/Dynamic-Headline-Numbering.html ;; Prefer “𝓏” instead of “𝓍.𝓎.𝓏” ---indentation ‘org-indent-mode’ resolves ambiguity (setq org-num-format-function (defun my/org-num-format (number-path) (format "%s " (car (last number-path))))) #+end_src ** How tasks look in org agenda #+begin_src emacs-lisp ;; Start each agenda item with ‘○’, then show me it's %timestamp and how many ;; times it's been re-%scheduled. (setq org-agenda-prefix-format " ○ %?-12t%-6e%s ") ;; (setq org-agenda-deadline-leaders '("DUE: " "In %3d d.: " "%2d d. ago: ")) ;; Don't say “Scheduled ⟨Task⟩”, just show “⟨Task⟩”. ;; If something's overdue, say “Overdue 𝓃× ⟨Task⟩”. (setq org-agenda-scheduled-leaders '("" "Overdue%2dx ")) #+end_src ** I use lots of “checkbox lists” in my routines: I want to think about things once, then follow the routine on auto-pilot #+begin_src emacs-lisp ;; list items will be treated like low-level headlines; i.e., folded by default. (setq org-cycle-include-plain-lists 'integrate) ;; clear checkbox when repeating a todo task (add-hook 'org-todo-repeat-hook #'org-reset-checkbox-state-subtree) ;; clear sub-sub-tasks when repeating a todo task. ;; ⇒ If I have “** A [%] \n #+STYLE: habit \n SCHEDULED: <2024-04-19 Fri .+1d> \n *** DONE B” ;; then on “** A” I press ‘t’ then mark it as ‘DONE’, then the “*** B” task resets to ‘TODO’. (add-hook 'org-todo-repeat-hook (lambda () (org-map-entries (lambda () (when (> (length (org-get-outline-path)) 1) (org-todo "TODO"))) t 'tree))) ;; “C-c a t” ⇒ List all (non-recurring non-someday) todos sorted by state, priority, effort (setq org-agenda-custom-commands '(("t" "My list of all TODO entries" tags-todo "-recurring-someday" ((org-agenda-overriding-header "\nTODOs sorted by state, priority, effort") (org-agenda-sorting-strategy '(todo-state-down priority-down effort-up)))))) #+end_src ** Work links :PROPERTIES: :CUSTOM_ID: my-work-links :END: In my private ~work.el~ file, I have declarations of the form ~(my/work-links "REPO" "https://⟨COMPANY⟩.atlassian.net/browse/REPO-%s")~ so that I can write things like ~REPO:1234~ to get a nice green bold link in Org-mode that will take me to that Jira link. I also have similar links to take me to the Github repositories, backlogs, and Kanban boards. #+begin_src emacs-lisp (cl-defmacro my/work-links (type url &optional (export-display '(format "%s-%s" type label))) "Given a link of TYPE with a URL, produce the correct org-link. EXPORT-DISPLAY is string-valued term that may mention the symbolic names ‘type’ and ‘label’. This is how the link looks upon export." `(org-link-set-parameters ,type :follow (lambda (label) (browse-url (format ,url label))) :export (lambda (label description backend) (-let [full-url (format ,url label)] (pcase backend ('html (format "%s" full-url (-let [type ,type] ,export-display))) ('latex (format "\\href{%s}{%s}" full-url label)) (_ full-url)))) :face '(:foreground "green" :weight bold :underline "blue" :overline "blue"))) #+end_src #+begin_src emacs-lisp (defvar COMPANY (getenv "COMPANY") "Name of place I work at.") (my/work-links "FWD" (lf-string "https://bugs.local.${(downcase COMPANY)}.com/browse/FWD-%s")) (my/work-links "OUT" (lf-string "https://bugs.local.${(downcase COMPANY)}.com/browse/OUT-%s")) (my/work-links "gerrit" (lf-string "https://gerrit.local.${(downcase COMPANY)}.com/c/fwd/+/%s")) ;; Open all such links in Chrome (setq browse-url-browser-function 'browse-url-default-macosx-browser) #+end_src ** Quick Capture #+begin_src elisp ;; Location of my todos / captured notes file (setq org-default-notes-file "~/Documents/notes.org") #+end_src #+begin_src elisp (defmacro def-capture (name location template) "Creates a method “my/capture-NAME”, which opens a capture buffer named NAME showing TEMPLATE. When you press `C-c C-c`, the note is saved as an entry (ie TEMPLATE should start with “* ”.) in `org-default-notes-file' section named LOCATION. + NAME, LOCATION, TEMPLATE are all strings that may contain spaces. ⇒ If you want to evaluate a function in TEMPLATE, use the syntax “%(f args)”. See https://stackoverflow.com/a/69331239 for an example. + Example: (def-capture \"Friends Info\" \"Journal\" \"* %t\") This can be used as “M-x my/capture-friends-info” or via an Org link: “[[elisp:( my/capture-friends-info)]]”. Note: My “Journal” is nested in a section called “Workflow”, and capture finds it anyways (。◕‿◕。) (More precisely, Org-Capture looks for the first (sub)headline called “Journal” /anywhere/ and uses that as the target location.) Usage: 1. M-x my/capture-NAME ⇒ Capture something to my LOCATION; no menu used. 2. C-u M-x my/capture-NAME ⇒ Jump to my LOCATION. 3. C-u C-u M-x my/capture-NAME ⇒ Goto last note stored (by any my/capture-* method). " `(defun ,(intern (concat "my/capture-" (s-replace-regexp " " "-" (downcase name)))) (&optional prefix) (interactive "p") (-let [org-capture-templates ;; I'm using omega 𝓌 as a placeholder; i.e., a gensym-like key. `(("𝓌" ,,name entry (file+headline ,,org-default-notes-file ,,location) ,,template))] (org-capture (list prefix) "𝓌") (unless (> prefix 1) (rename-buffer ,name))))) #+end_src *** C-c c ⇒ Capture inbox entry #+begin_src elisp ;; I mark inbox captures as “TODO” so that they appear in my task list (C-c a t) in the event ;; I've forgotten to process my inbox. ;; ;; When I process my “* Inbox” section, I am left with an empty Org section and so delete it. ;; ⇒ “C-c c” will create the section if it does not exist. ;; ⇒ Deleting it is a nice ‘cheery on top’ when processing my inbox (✿◠‿◠) (bind-key* "C-c c" (def-capture "Inbox Entry" "Inbox" "* TODO %?\n Captured: %U \n")) #+end_src *** Capture morning journal entry #+begin_src elisp (defalias #'my/new-journal-entry (def-capture "Journal Entry" "Journal" (s-join "\n\n" (list (format-time-string "* %Y-%m-%d %a %R :mood_𝒏%?:") "My most important goal for the day is: " (let (todays-agenda) (org-agenda-list 1) (setq todays-agenda (buffer-string)) (org-agenda-quit) (concat "Here's what my day looks like so far:\n" todays-agenda)))))) #+end_src *** Capture evening journal entry #+begin_src elisp (def-capture "Daily Retrospective" "Journal" (concat ;; The “:mood_𝓃:” tag is so that I can easily see my moods across my entries. ;; Keeping track of mood is a simple way to monitor success; e.g., whether GTD is working for me or not. (format-time-string "* %Y-%m-%d %a %R :mood_𝒏%?:retro:daily:\n") " Instructions: Read section contents and replace them with your own words. ,** Monitoring Mood For Success - Why do I feel the way I do? - 1 ≈ 😢 extremely negative; 5 ≈ :neutral_face: neutral; 10 ≈ 😁 extremely positive. ,** Celebrating Successes - Acknowledge and celebrate your achievements, no matter how small. - Recognising your progress boosts morale and motivation, inspiring you to continue striving for success. - /Give yourself credit for your efforts!/ ,** Goal Alignment - Were today's actions directed at achieving the sprint's goals? Stay focused! - Review your day: use my/what-did-i-do-today to review the tasks you clocked into. [[elisp:(let ((org-clock-clocktable-default-properties (-snoc org-clock-clocktable-default-properties :block 'today :timestamp 'nil))) (beginning-of-line) (kill-line) (org-clock-report))][What Did I Work on Today?]] - Set SMART goals for tomorrow. ,** Stress Reduction - Any challenges or frustrations encountered today? ,** Continuous Improvement - What went well, what didn't go as planned, and were there any unexpected challenges today? - Learn from your experiences & brainstorm strategies better outcomes in the future. - Consider what factors contributed to these challenges and how you responded to them. - What thought patterns were influencing my outcomes? ,** Express Gratitude - Conclude your retrospective by expressing gratitude for the positive aspects of your day. - Reflect on the people, opportunities, and experiences that brought you joy or fulfilment. ")) #+end_src ** my/see-sorted-todos #+begin_src elisp (defun my/see-sorted-todos () "See TODOs sorted by state, priority, then effort. This view shows me all tasks, sorted, and from there I can pick & schedule & edit as desired. Much nicer than me physically sorting tasks! The main benefit here is I can keep /active/ tasks under their own disjoint ‘projects’, yet still have a nice unified view of all of my /active/ *and* non-active tasks. ⇒ You can mark DONE and archive from the sorted view. ⇒ But leave that to the Weekly Review, so that you can extract any reference information or Lessons Learned out of the tasks to be archived. See: https://emacs.stackexchange.com/questions/9585/org-how-to-sort-headings-by-todo-and-then-by-priority " (interactive) (let ((org-agenda-custom-commands '(("𝓌" nil todo "*" ((org-agenda-overriding-header "\nTODOs sorted by state, priority, effort") (org-agenda-sorting-strategy '(todo-state-down priority-down effort-up))))))) (org-agenda nil "𝓌" t) (beginning-of-buffer))) #+end_src ** Keep agenda in view, so I don't get distracted from the day's goals [[https://orgmode.org/worg/org-hacks.html#orgdabf067:~:text=This%20keeps%20my%20tasks%20%22always%20in%20mind%22%20whenever%20I%20come%20back%20to%20Emacs%20after%20doing%20something%20else][Source]] #+begin_src emacs-lisp (defun jump-to-org-agenda () (interactive) (let ((buf (get-buffer "*Org Agenda*")) wind) (if buf (if (setq wind (get-buffer-window buf)) (select-window wind) (if (called-interactively-p) (progn (select-window (display-buffer buf t t)) (org-fit-window-to-buffer) ;; (org-agenda-redo) ) (with-selected-window (display-buffer buf) (org-fit-window-to-buffer) ;; (org-agenda-redo) ))) (call-interactively 'org-agenda-list))) ;;(let ((buf (get-buffer "*Calendar*"))) ;; (unless (get-buffer-window buf) ;; (org-agenda-goto-calendar))) ) (run-with-idle-timer 300 t 'jump-to-org-agenda) #+end_src * Lisp Programming :PROPERTIES: :CUSTOM_ID: Lisp-Programming :END: ** highlight quoted symbols :PROPERTIES: :CUSTOM_ID: highlight-quoted-symbols :END: #+begin_src emacs-lisp (use-package highlight-quoted :defer nil :config (add-hook 'emacs-lisp-mode-hook 'highlight-quoted-mode)) ;; If everything worked fine, then “ 'b ” below should be coloured nicely in Emacs Lisp mode. (when nil (-let [x 'somevar] (list x 'b "c" :e))) #+end_src ** Get CheatSheets and view them easily :Disabled: :PROPERTIES: :CUSTOM_ID: Get-CheatSheets-and-view-them-easily :header-args: :tangle no :END: #+begin_src emacs-lisp (defvar my/cheatsheet/cached-topics nil) (cl-defun my/cheatsheet (&optional topic) "Clone Al-hassy's ⟨TOPIC⟩CheatSheet repository when called from Lisp; visit the pretty HTML page when called interactively. - Example usage: (my/cheatsheet \"Vue\") - Example usage: M-x my/cheatsheet RET Vue RET." (interactive) (if (not topic) (browse-url (format "https://alhassy.github.io/%sCheatSheet" (completing-read "Topic: " my/cheatsheet/cached-topics))) (push topic my/cheatsheet/cached-topics) (maybe-clone (format "https://github.com/alhassy/%sCheatSheet" topic)))) #+end_src Let's actually get some repos locally, and use: ~M-x my/cheatsheet~ to view the pretty HTML (or PDF) sheets. #+begin_src emacs-lisp (mapcar #'my/cheatsheet '("ELisp" "GojuRyu" "Rust")) ; Python Prolog Vue Agda JavaScript ; Clojure Ruby Oz Coq Cats Haskell FSharp OCaml #+end_src * Prose :PROPERTIES: :CUSTOM_ID: Prose :END: Emacs can be setup with a spellchecker and other expected features of a word processing tool ---however these features apply Emacs-wide since nearly everything is essentially text (•̀ᴗ•́)و - Org-mode is a writer's best friend; it's large enough to deserve its own sections. - See [[https://lucidmanager.org/tags/emacs/][Emacs Writing Studio]] for a series of articles on making/using Emacs for writing. ** Bidirectional Text #+begin_src emacs-lisp ;; Sometimes I have Arabic in my buffers, however I am an English speaker ;; and so Left-to-Right is most natural to me. As such, even when Arabic ;; is present, or any bidirectional text, just use Left-to-Right. (setq-default bidi-paragraph-direction 'left-to-right) #+end_src ** Whitespace :PROPERTIES: :CUSTOM_ID: Whitespace :END: Let's start off by cleaning-up any accidental trailing whitespace and in other places upon save. #+begin_src emacs-lisp (add-hook 'before-save-hook 'whitespace-cleanup) #+end_src See [[http://ergoemacs.org/emacs/whitespace-mode.html][here]] for making whitespace visible; including spaces, tabs, and newlines ** Formatting Text :PROPERTIES: :CUSTOM_ID: Formatting-Text :END: The following incantation, doc:my/org-mode-format, makes it so that we can select some text then press kbd:C-c_f (to get a list of possible character completions) then press the symbol we want our text to be surrounded with. #+begin_details #+begin_src emacs-lisp (local-set-key (kbd "C-c f") #'my/org-mode-format) (defun my/org-mode-format (&optional text) "Surround selected region with the given Org emphasises marker. E.g., if this command is bound to “C-c f” then the sequence “C-c f b” would make the currenly selected text be bold. Likewise, “C-c f *” would achieve the same goal. When you press “C-c f”, a message is shown with a list of useful single-character completions. Note: “C-c f 𝓍”, for an unrecognised marker 𝓍, just inserts the character 𝓍 before and after the selected text." (interactive "P") ;; Works on a region ; (message "b,* ⟨Bold⟩; i,/ ⟨Italics⟩; u,_ ⟨Underline⟩; c,~ ⟨Monotype⟩") (message "⟨Bold b,*⟩ ⟨Italics i,/⟩ ⟨Underline u,_⟩ ⟨Monotype c,~⟩") (let ((kind (read-char))) ;; Map letters to Org formatting symbols (setq kind (or (plist-get '(b ?\* i ?\/ u ?\_ c ?\~) (intern (string kind))) kind)) (insert-pair text kind kind))) #+end_src #+end_details ** Fill-mode ---Word Wrapping :PROPERTIES: :CUSTOM_ID: Fill-mode-Word-Wrapping :END: In fill mode, when you type past the end of a line, Emacs automatically starts a new line, cleverly formatting paragraphs. This is a powerful form of “word wrap”. #+BEGIN_SRC emacs-lisp (setq-default fill-column 80 ;; Let's avoid going over 80 columns truncate-lines nil ;; I never want to scroll horizontally indent-tabs-mode nil) ;; Use spaces instead of tabs #+END_SRC Certain variables are sensibly local to a buffer, and so ~setq~ only alters their value for one buffer. Using ~setq-default~ we change a variable's default value, in every buffer. #+BEGIN_SRC emacs-lisp ;; Wrap long lines when editing text (add-hook 'text-mode-hook 'turn-on-auto-fill) (add-hook 'org-mode-hook 'turn-on-auto-fill) #+END_SRC We may press ~M-q~ to cleverly redistribute the line breaks within any paragraph, thereby making it look better. With a prefix argument, it justifies it as well ---i.e., pads extra white space to make the paragraph appear rectangular. Note that ~M-o M-s~ centres a line of text ;-) Fun stuff! Fill-mode is also known as “hard word wrapping”, which has the counterpart “soft word wrapping” … Visual line mode is built-in and provides support for editing by visual lines: Lines off the screen are visually word wrapped, but logically remain one line. Moreover =C-a,e,k= operate on visual lines rather than logical lines. #+begin_src emacs-lisp ;; Bent arrows at the end and start of long lines. (setq visual-line-fringe-indicators '(left-curly-arrow right-curly-arrow)) (global-visual-line-mode 1) #+end_src Visual line mode is useful when I have way too many windows open or when using smaller frames. ** Pretty Lists Markers :PROPERTIES: :CUSTOM_ID: Pretty-Lists-Markers :END: When writing, it's common to use ~+,-,*~ to enumerate unordered lists ---especially so in Org-mode wherein they denote structured text. Let's render them visually as Unicode bullets. #+begin_src emacs-lisp ;; (x y z) ≈ (existing-item replacement-item positivity-of-preceding-spaces) (cl-loop for (x y z) in '(("+" "◦" *) ("-" "•" *) ("*" "⋆" +)) do (font-lock-add-keywords 'org-mode `((,(format "^ %s\\([%s]\\) " z x) (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) ,y))))))) #+end_src ** Fix spelling as you type ---thesaurus & dictionary too! :PROPERTIES: :CUSTOM_ID: Fix-spelling-as-you-type-thesaurus-dictionary-too :END: I would like to check spelling on the fly. + ~C-;~ :: Cycle through corrections for word at point. + ~M-$~ :: Check and correct spelling of the word at point + ~M-x ispell-change-dictionary RET TAB~ :: To see what dictionaries are available. Install spell-checking application as well as a reliable English dictionary, [[https://wordnet.princeton.edu/][WordNet]]. #+begin_src emacs-lisp (system-packages-ensure "aspell") (system-packages-ensure "wordnet") #+end_src ~flyspell-prog-mode~ enables spell checking for programming by only considering comments and strings. #+BEGIN_SRC emacs-lisp (use-package flyspell :hook ((prog-mode . flyspell-prog-mode) ((org-mode text-mode) . flyspell-mode))) #+END_SRC Enabling fly-spell for text-mode enables it for org and latex modes since they derive from text-mode. Flyspell needs a spell checking tool, which is not included in Emacs. We install ~aspell~ spell checker using, say, homebrew via ~brew install aspell~. Note that Emacs' ~ispell~ is the interface to such a command line spelling utility. # See available dictionary via ~aspell dicts~. #+BEGIN_SRC emacs-lisp (setq ispell-program-name (s-trim (shell-command-to-string "which aspell"))) (setq ispell-dictionary "en_GB") ;; set the default dictionary #+END_SRC [Disabled] Allow spelling support for CamlCase words like “EmacsIsCool”. #+BEGIN_SRC emacs-lisp :tangle no (setq ispell-extra-args '("--sug-mode=ultra" "--run-together" "--run-together-limit=5" "--run-together-min=2")) #+END_SRC Let us select a correct spelling merely by clicking on a word ---for the rare days I have a mouse. #+begin_src emacs-lisp (eval-after-load "flyspell" ' (progn (define-key flyspell-mouse-map [down-mouse-3] #'flyspell-correct-word) (define-key flyspell-mouse-map [mouse-3] #'undefined))) #+end_src Colour incorrect works; default is an underline. #+BEGIN_SRC emacs-lisp (global-font-lock-mode t) (custom-set-faces '(flyspell-incorrect ((t (:inverse-video t))))) #+END_SRC Finally, save to user dictionary without asking: #+BEGIN_SRC emacs-lisp (setq ispell-silently-savep t) #+END_SRC Let's keep track of my personal word set by having it be in my version controlled .emacs directory. [[http://aspell.net/man-html/Format-of-the-Personal-and-Replacement-Dictionaries.html][Note]] that the default location is ~~/.[i|a]spell.DICT~ for a specified dictionary ~DICT~. #+BEGIN_SRC emacs-lisp (setq ispell-personal-dictionary "~/.emacs.d/.aspell.en.pws") #+END_SRC Nowadays, I very rarely write non-literate programs, but if I do I'd like to check spelling only in comments/strings. E.g., #+BEGIN_SRC emacs-lisp (add-hook 'c-mode-hook 'flyspell-prog-mode) (add-hook 'emacs-lisp-mode-hook 'flyspell-prog-mode) #+END_SRC Use the thesaurus Emacs frontend [[https://github.com/hpdeifel/synosaurus][Synosaurus]] to avoid unwarranted repetition. #+begin_src emacs-lisp (use-package synosaurus :defer 100 :init (synosaurus-mode) :config (setq synosaurus-choose-method 'popup) ;; 'ido is default. (global-set-key (kbd "M-#") 'synosaurus-choose-and-replace)) #+end_src The thesaurus is powered by the Wordnet ~wn~ tool, which can be invoked without an internet connection! #+begin_src emacs-lisp ;; (shell-command "brew cask install xquartz &") ;; Dependency ;; (shell-command "brew install wordnet &") #+end_src Let's use Wordnet as a dictionary via the [[https://github.com/gromnitsky/wordnut][wordnut]] package. #+BEGIN_SRC emacs-lisp (use-package wordnut :defer 100 :bind ("M-!" . wordnut-lookup-current-word)) ;; Use M-& for async shell commands. #+END_SRC Use ~M-↑,↓~ to navigate dictionary results, and ~wordnut-search~ for a new search. An alternative to =wordnut= is to use the lightweight ~define-word~ package; which I think is not ideal since it provides way less information. :PowerthesaurusCurrentlyNotWorking: #+BEGIN_SRC emacs-lisp :tangle no (load "~/dotfiles/.emacs.d/powerthesaurus.el") (global-set-key (kbd "M-#") 'powerthesaurus-lookup-word-at-point) ;; Website currently down ... https://github.com/SavchenkoValeriy/emacs-powerthesaurus/issues/6 #+END_SRC :End: ** Touch Typing :Disabled: :PROPERTIES: :CUSTOM_ID: Touch-Typing :header-args: :tangle no :END: Use this game to help you learn to spell words that you're having trouble with; e.g., I have a file ~~/Dropbox/spelling.txt~ with words I have trouble spelling, which I open then run ~M-x typing-of-emacs~ in order to improve spelling said words. #+BEGIN_SRC emacs-lisp :tangle no ;; The Typing Of Emacs, a game. (use-package typing-of-emacs :quelpa (typing :fetcher wiki :url "https://www.emacswiki.org/emacs/typing.el")) #+END_SRC Practice touch typing using [[https://github.com/hagleitn/speed-type][speed-type]]. #+begin_src emacs-lisp (use-package speed-type ) #+end_src Running ~M-x speed-type-region~ on a region of text, or ~M-x speed-type-buffer~ on a whole buffer, or just ~M-x speed-type-text~ will produce the selected region, buffer, or random text for practice. The timer begins when the first key is pressed and stats are shown when the last letter is entered. Other typing resources include: + [[https://www.emacswiki.org/emacs/TypingOfEmacs][Typing of Emacs]] ---an Emacs alternative to speed type, possibly more engaging. + [[https://alternativeto.net/software/klavaro/][Klavaro]] ---a GUI based yet language-independent typing tutor. - I'm enjoying this tool in getting started with Arabic typing. + [[https://typing.io/][Typing.io]] is a tutor for coders: Lessons are based on open source code, such some XMonad written in Haskell or Linux written in C. + [[https://www.gnu.org/software/gtypist/index.html#downloading][GNU Typist]] ---which is interactive in the terminal, so not ideal in Emacs--, To assist in language learning, it may be nice to have an Emacs [[https://github.com/atykhonov/google-translate][interface]] to Google translate ---e.g., invoke ~google-translate-at-point~. #+BEGIN_SRC emacs-lisp (use-package google-translate :config (global-set-key "\C-ct" 'google-translate-at-point)) #+END_SRC Select the following then ~C-c t~, #+begin_quote Hey buddy, what're you up to? #+end_quote Then /detect language/ then /Arabic/ to obtain: #+begin_quote مرحباً يا صديقي ، ماذا تفعل؟ #+end_quote Neato 😲 ** Using a Grammar & Style Checker :Disabled: :PROPERTIES: :CUSTOM_ID: Using-a-Grammar-Style-Checker :header-args: :tangle no :END: [ A possibly better alternative is [[https://emacstil.com/til/2022/03/05/setting-up-vale-prose-linter-on-emacs/][Vale]]. ] Let's install [[https://github.com/mhayashi1120/Emacs-langtool][a grammar and style checker]]. We get the offline tool from the bottom of the [[https://languagetool.org/][LanguageTool]] website, then relocate it as follows. #+BEGIN_SRC emacs-lisp (use-package langtool :custom (langtool-language-tool-jar "~/Applications/LanguageTool-4.5/languagetool-commandline.jar")) #+END_SRC Now we can run ~langtool-check~ on the subsequent grammatically incorrect text ---which is from the LanguageTool website--- which colours errors in red, when we click on them we get the reason why; then we may invoke ~langtool-correct-buffer~ to quickly use the suggestions to fix each correction, and finally invoke ~langtool-check-done~ to stop any remaining red colouring. #+begin_example org LanguageTool offers spell and grammar checking. Just paste your text here and click the 'Check Text' button. Click the colored phrases for details on potential errors. or use this text too see an few of of the problems that LanguageTool can detecd. What do you thinks of grammar checkers? Please not that they are not perfect. Style issues get a blue marker: It's 5 P.M. in the afternoon. The weather was nice on Thursday, 27 June 2017 --uh oh, that's the wrong date ;-) #+end_example By looking around the source code, I can do all three stages smoothly (•̀ᴗ•́)و #+BEGIN_SRC emacs-lisp ;; Quickly check, correct, then clean up /region/ with M-^ (eval-after-load 'langtool (progn (add-hook 'langtool-error-exists-hook (lambda () (langtool-correct-buffer) (langtool-check-done))) (global-set-key "\M-^" (lambda () (interactive) (message "Grammar checking begun ...") (langtool-check))))) #+END_SRC The checking command is silent, we added a bit of comforting acknowledgement to the user. ** Lightweight Prose Proofchecking :PROPERTIES: :CUSTOM_ID: Lightweight-Prose-Proofchecking :END: Let's [[https://github.com/bnbeckwith/writegood-mode][write good]]! #+BEGIN_SRC emacs-lisp (use-package writegood-mode ;; Load this whenver I'm composing prose. :hook (text-mode org-mode) ;; Don't show me the “Wg” marker in the mode line :defer 100 ;; Some additional weasel words. :config (--map (push it writegood-weasel-words) '("some" "simple" "simply" "easy" "often" "easily" "probably" "clearly" ;; Is the premise undeniably true? "experience shows" ;; Whose? What kind? How does it do so? "may have" ;; It may also have not! "it turns out that"))) ;; How does it turn out so? ;; ↯ What is the evidence of highighted phrase? ↯ #+END_SRC Inspired by Matt Might's [[http://matt.might.net/articles/shell-scripts-for-passive-voice-weasel-words-duplicates/][3 shell scripts to improve your writing, or "My Ph.D. advisor rewrote himself in bash"]], this Emacs interface emphasises, via underline, the following weaknesses in writing ---so that I can fix them or decide that they are appropriate for the scenario. Sentences that cut out the following problems may become stronger ---by being more terse or precise. + Weasel Words :: Phrases that sound good without conveying information; such as vague precision or subjective phrases. E.g., /a number of, surprisingly, very close/. It's okay not to have exact details, but rather than “I don't know” explain why not and what the next steps will be. + Passive Voice :: Phrases wherein interest is in the object experiencing an action, rather than the subject that performs the action. - Bad: The house /was built by/ my father. - Good: My father /built/ this house. Likewise, including relevant or explanatory information as in “X guarantees Y” is an improvement over “Y is guaranteed”. Sometimes the subject really is irrelevant, such as “We did X” whereas “X happened” suffices. 👍 If the relevant subject is unclear and, also, the text reads better in the active, then change a phrase. + Duplicated Words :: Occurrences of, say, “the the”. Harder to catch manually, but easier mechanically ;-) ** Placeholder Text ---For Learning & Experimenting :PROPERTIES: :CUSTOM_ID: Placeholder-Text-For-Learning-Experimenting :END: When learning about Emacs formatting commands, such as zap-to-char ~M-z~ or transpose ~M-t~, it's best to have filler text ---even better when it's automatically generated instead of typing it out ourselves. The following will give us a series of commands ~lorem-ipsum-insert-⋯~ for inserting lists, sentences, paragraphs and using a prefix argument, with ~C-u~, we can request to generate any number of them. #+BEGIN_SRC emacs-lisp (use-package lorem-ipsum ) #+END_SRC ‘Lorem’ is not a word itself, but it comes from the Latin ‘Dolorem Ipsum’ which means “pain in and of itself”. See this [[https://github.com/alhassy/emacs.d/blob/master/CheatSheet.pdf][Emacs Cheat Sheet]] to try out the textual navigation and formatting bindings on lorem ipsum, gibberish text. ** Some text to make us smile :PROPERTIES: :CUSTOM_ID: Some-text-to-make-us-smile :END: The [[https://github.com/davep/dad-joke.el][dad-joke]] queries [[https://icanhazdadjoke.com][https://icanhazdadjoke.com]] to bring us some funny. #+begin_src emacs-lisp (use-package dad-joke :config (defun dad-joke () (interactive) (insert (dad-joke-get)))) #+end_src For example, ~M-x dad-joke~ now inserts: #+begin_quote org What are the strongest days of the week? Saturday and Sunday...the rest are weekdays. #+end_quote ** Unicode Input via Agda Input :PROPERTIES: :CUSTOM_ID: Unicode-Input-via-Agda-Input :END: :agda2-include-dirs-Issue: #+BEGIN_SRC emacs-lisp ;; (load (shell-command-to-string "agda-mode locate")) ;; ;; Seeing: One way to avoid seeing this warning is to make sure that agda2-include-dirs is not bound. ; (makunbound 'agda2-include-dirs) #+END_SRC :End: [[https://mazzo.li/posts/AgdaSort.html][Agda]] is one of my favourite languages, it's like Haskell on steroids. Let's set it up for the main sake of its Unicode input ---you may do likewise using TeX input. ( [[https://www.joelonsoftware.com/2003/10/08/the-absolute-minimum-every-software-developer-absolutely-positively-must-know-about-unicode-and-character-sets-no-excuses/][The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)]] ) /Agda input mode makes it extremely easy to use unicode in documents, something I/ /strongly prefer to do. When I can use symbols directly, instead of (for/ /instance) LaTeX commands, it makes my plaintext far more readable./ --- [[https://github.com/armkeh/dotfiles/tree/master/emacs][Armkeh .emacs config]] #+begin_src emacs-lisp (system-packages-ensure "agda") #+end_src #+begin_details To use the Agda standard library by default #+BEGIN_SRC shell :tangle no mkdir -p ~/.agda echo /usr/local/lib/agda/standard-library.agda-lib >>~/.agda/libraries echo standard-library >>~/.agda/defaults #+END_SRC Invoke ~brew info agda~ to get these instructions and the version of Agda just installed. #+end_details #+begin_details Get font support for subscripts, if need be - Download and unzip the [[https://fontlibrary.org/en/font/symbola][symbola]] font - kbd:CMD_+_SPC ⇒ =font book= ⇒ CMD+O ⇒ select the symbola directory you just unzipped (Note: In the before time, you could brew install this font.) #+end_details Executing ~agda-mode setup~ appends the following text to the ~.emacs~ file. Let's put it here ourselves. #+begin_src emacs-lisp (unless noninteractive (load-file (let ((coding-system-for-read 'utf-8)) (shell-command-to-string "agda-mode locate")))) #+end_src I almost always want the ~agda-mode~ input method ---it's like the TeX method, but better. #+BEGIN_SRC emacs-lisp ;; TODO: Maybe don't bother installing Agda, and just get agda-input.el ;; from: https://github.com/agda/agda/blob/master/src/data/emacs-mode/agda-input.el ;; then loading that! (url-copy-file "https://raw.githubusercontent.com/agda/agda/master/src/data/emacs-mode/agda-input.el" "~/.emacs.d/elpa/agda-input.el" :ok-if-already-exists) (load-file "~/.emacs.d/elpa/agda-input.el") ;; MA: This results in "Package cl is deprecated" !? (unless noninteractive (use-package agda-input :ensure nil ;; I have it locally. :demand t :hook ((text-mode prog-mode) . (lambda () (set-input-method "Agda"))) :custom (default-input-method "Agda"))) ;; Now C-\ or M-x toggle-input-method turn it on and offers ;; TODO add a hook that when the input method becomes Agda, just don't bother showing me in the modeline. ;; E.g., "Π" when using unicode input with Agda ;; Useful to have in the modeline, say when typing in Arabic. ;; (add-variable-watcher ;; 'current-input-method ;; (lambda (_ newvalue 'set _) ;; (setq current-input-method-title ;; (if (equal newvalue "Agda") nil newvalue)))) #+END_SRC :agda_Command_line_arguments: "+RTS -H4.5G -M4.5G -K256M -S/tmp/AgdaRTS.log -A1G -RTS -i ." Wolfram Kahl has recommended the following settings. #+begin_src emacs-lisp ;;(setq agda2-program-args (quote ("RTS" "-M4G" "-H4G" "-A128M" "-RTS"))) #+end_src These arguments specify | ~+RTS~, ~-RTS~ | Flags between these are arguments to the ~ghc~ runtime | | ~-M[size]~ | Maximum heap size | | ~-H[size]~ | Suggested heap size | | ~-A[size]~ | Allocation area size used by the garbage collector | Full documentation for the ~ghc~ runtime argumentscan be found [[https://downloads.haskell.org/~ghc/7.8.4/docs/html/users_guide/runtime-control.html][here]]. Additional arguments that may be useful include | ~-S[file]~ | Produces information about “each and every garbage collection” | | | - Outputs to ~stderr~ by default | :end: #+begin_quote Unicode doesn't intend to cover things that are achievable with markup, so only a limited subset of the alphabet is available as subscript; but all is available as superscript, except ‘q’. ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓ ⁰ ¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ⁺ ⁻ ⁼ ⁽ ⁾ ₀ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ₊ ₋ ₌ ₍ ₎ ᵃ ᵇ ᶜ ᵈ ᵉ ᶠ ᵍ ʰ ⁱ ʲ ᵏ ˡ ᵐ ⁿ ᵒ ᵖ ʳ ˢ ᵗ ᵘ ᵛ ʷ ˣ ʸ ᶻ ᴬ ᴮ ᴰ ᴱ ᴳ ᴴ ᴵ ᴶ ᴷ ᴸ ᴹ ᴺ ᴼ ᴾ ᴿ ᵀ ᵁ ⱽ ᵂ ᵅ ᵝ ᵞ ᵟ ᵋ ᶿ ᶥ ᶲ ᵠ ᵡ ᵦ ᵧ ᵨ ᵩ ᵪ ~brew cask install font-symbola~ ⇒ Includes fonts for subscripts; e.g., ₐₙₑₕᵢⱼₖₗₘₙₒₚₜₛ #+end_quote Below are my personal Agda input symbol translations; e.g., ~\set → 𝒮ℯ𝓉~. Note that we could give a symbol new Agda TeX binding interactively: ~M-x customize-variable agda-input-user-translations~ then ~INS~ then for key sequence type ~set~ then ~INS~ and for string paste ~𝒮ℯ𝓉~. #+BEGIN_SRC emacs-lisp (unless noninteractive (add-to-list 'agda-input-user-translations '("set" "𝒮ℯ𝓉"))) #+END_SRC Better yet, as a loop: #+BEGIN_SRC emacs-lisp (unless noninteractive (cl-loop for item in '(;; Arabic ornate parenthesis U+FD3E / U+FD3F ("(" "﴾") (")" "﴿") ("cmd" "⌘") ;; categorial ;; ("alg" "𝒜𝓁ℊ") ("split" "▵") ("join" "▿") ("adj" "⊣") (";;" "﹔") (";;" "⨾") (";;" "∘") ;; logic ("if" "⇐") ("onlyif" "⇒") ;; lattices ;; ("meet" "⊓") ("join" "⊔") ;; tortoise brackets, infix relations ("((" "〔") ("))" "〕") ;; residuals ("syq" "╳") ("over" "╱") ("under" "╲") ;; Z-quantification range notation ;; ;; e.g., “∀ x ❙ R • P” ;; ("|" "❙") ("with" "❙") ;; Z relational operators ("domainrestriction" "◁") ("domr" "◁") ("domainantirestriction" "⩤") ("doma" "⩤") ("rangerestriction" "▷") ("ranr" "▷") ("rangeantirestriction" "⩥") ("rana" "⩥") ;; adjunction isomorphism pair ;; ("floor" "⌊⌋") ("lower" "⌊⌋") ("lad" "⌊⌋") ("ceil" "⌈⌉") ("raise" "⌈⌉") ("rad" "⌈⌉") ;; Replies ("yes" "✔") ("no" "❌") ;; Arrows ("<=" "⇐") ;; more (key value) pairs here ) do (add-to-list 'agda-input-user-translations item))) #+END_SRC Also some silly stuff: #+begin_src emacs-lisp (unless noninteractive ;; Add to the list of translations using “emot” and the given, more specfic, name. ;; Whence, \emot shows all possible emotions. (cl-loop for emot in `(;; angry, cry, why-you-no ("whyme" "ლ(ಠ益ಠ)ლ" "ヽ༼ಢ_ಢ༽ノ☂" "щ(゜ロ゜щ)" "‿︵(ಥ﹏ಥ)‿︵" "ಠ_ಠ" "(╬ ಠ益ಠ)" "・゚(*❦ω❦)*・゚" "(╯°□°)╯︵ ┻━┻") ;; flip the table ;; confused, disapprove, dead, shrug, awkward ("what" "「(°ヘ°)" "(ಠ_ಠ)" "(✖╭╮✖)" "¯\\_(ツ)_/¯" "(´°ω°`)" "・✧_✧・") ;; dance, csi ("cool" "┏(-_-)┓┏(-_-)┛┗(-_- )┓" ,(s-collapse-whitespace "•_•) ( •_•)>⌐■-■ (⌐■_■)")) ;; love, pleased, success, yesss, smile, excited, yay ("smile" "♥‿♥" "(─‿‿─)" "(•̀ᴗ•́)و" "ᕦ( ᴼ ڡ ᴼ )ᕤ" "(งಠ_ಠ)ง" "(。◕‿◕。)" "(◕‿◕)" "( ˃ ヮ˂)" "[ ⇀ ‿ ↼ ]" "٩(⁎❛ᴗ❛⁎)۶" "ᴵ’ᵐ ᵇᵉᵃᵘᵗⁱᶠᵘˡ" "(✿◠‿◠)") ;; flower high-5 ("hug" "♡(✿ˇ◡ˇ)人(ˇ◡ˇ✿)♡" "(づ。◕‿◕。)づ" "(づ。◕‿‿‿‿◕。)づ")) do (add-to-list 'agda-input-user-translations emot) (add-to-list 'agda-input-user-translations (cons "emot" (cdr emot))))) #+end_src # If you change this setting manually, without using the # customization buffer, you need to call (agda-input-setup) in # order for the change to take effect. Finally let's effect such translations. #+begin_src emacs-lisp ;; activate translations (unless noninteractive (agda-input-setup)) #+end_src Note that the effect of [[http://ergoemacs.org/emacs/emacs_n_unicode.html][Emacs unicode input]] could be approximated using ~abbrev-mode~. :May_need_to_install_stix_font: ;; install STIX font from Ubuntu store!! ;; (set-fontset-font t 'unicode (font-spec :name "STIX") nil 'append) :End: ** Increase/decrease text size :PROPERTIES: :CUSTOM_ID: Increase-decrease-text-size :END: The ‘usual’ text zoom keys ~C-±~ … #+BEGIN_SRC emacs-lisp (global-set-key (kbd "C-+") 'text-scale-increase) (global-set-key (kbd "C--") 'text-scale-decrease) ;; C-x C-0 restores the default font size #+END_SRC If thou knowst the ELisp, forgive this shadowing of the ~negative-argument~ … we've still got ~M--~ though. Curious, this is one of the very first things I did when began using Emacs; yet, perhaps I would not have done it if I was simply told the defaults: + ~C-x C-=,+~ increases text size + ~C-x C--~ decreases test size + ~C-x C-0~ restores it to the default size So, the above snippet seems to save us of the prefix ~C-x~ and we lose on using ‘=’ for text increase and worse we need the shift-key to get access to the ‘+’. I suppose this is just a habit inherited from using other tools. Fortunately, I did not inherit the need for the /common user access/ bindings ~C-x~ kill, ~C-c~ copy, ~C-v~ paste, nor ~C-z~ undo of other applications. If you're interested, ~M-x cua-mode~ to enable [[https://www.gnu.org/software/emacs/manual/html_node/emacs/CUA-Bindings.html][CUA Bindings]]. ** Moving Text Around :PROPERTIES: :CUSTOM_ID: Moving-Text-Around :END: This extends Org-mode's ~M-↑,↓~ to other modes, such as when coding. #+BEGIN_SRC emacs-lisp ;; M-↑,↓ moves line, or marked region; prefix is how many lines. (use-package move-text :config (move-text-default-bindings)) #+END_SRC ** Enabling CamelCase Aware Editing Operations :PROPERTIES: :CUSTOM_ID: Enabling-CamelCase-Aware-Editing-Operations :END: [[https://www.gnu.org/software/emacs/manual/html_node/ccmode/Subword-Movement.html][Subword]] movement lets us treat “EmacsIsAwesome” as three words ─“Emacs”, “Is”, and “Awesome”─ which is desirable since such naming is common among coders. Now, for example, ~M-f~ moves along each subword. #+begin_src emacs-lisp (global-subword-mode 1) #+end_src ** Mouse Editing Support :Disabled: :PROPERTIES: :CUSTOM_ID: Mouse-Editing-Support :header-args: :tangle no :END: # :Strange:This_Seems_To_Require_Deprecated_CL:Message_show_at_startup: Text selected with the mouse is automatically copied to clipboard. #+begin_src emacs-lisp (setq mouse-drag-copy-region t) #+end_src ** Delete Selection Mode :PROPERTIES: :CUSTOM_ID: Delete-Selection-Mode :END: Delete Selection mode lets you treat an Emacs region much like a typical text selection outside of Emacs: You can replace the active region. We can delete selected text just by hitting the backspace key. #+BEGIN_SRC emacs-lisp (delete-selection-mode 1) #+END_SRC **   ~M-n,p~: Word-at-Point Navigation ╱╲ Automatic highlighting current symbol/word :Disabled: :PROPERTIES: :CUSTOM_ID: M-n-p-Word-at-Point-Navigation :header-args: :tangle no :END: Let's mimic the ~C-n,p~ constructs from line to word, so that unoccupied ~M-n,p~ now serve to take us to the next or previous instance of the word under the cursor. This is less intrusive than searching ~C-s~ or listing all occurrences ~M-s o~. Moreover, let's automaticly highlight current symbols, words under the cursour, and cycle quickly between highlkighted locations with doc:ahs-forward / [[kbd:M-→]] and doc:ahs-backward / [[kbd:M-←]]. #+BEGIN_SRC emacs-lisp ;; Default: M-→/← moves to the next/previous instance of the currently highlighted word ;; These are already meaningful commands in Org-mode, so we avoid these key re-bindings in Org-mode; TODO. (use-package auto-highlight-symbol) ;; :hook ((text-mode . auto-highlight-symbol-mode) ;; (prog-mode . auto-highlight-symbol-mode))) ;; ;; This breaks Org Exports; e.g., ;; C-c C-e h o ⇒ Match data clobbered by buffer modification hooks #+END_SRC Let's also quickly replace the word at point: #+BEGIN_SRC emacs-lisp (defun my/symbol-replace (replacement) "Replace all standalone symbols in the buffer matching the one at point." (interactive (list (read-from-minibuffer "Replacement for thing at point: " nil))) (save-excursion (let ((symbol (or (thing-at-point 'symbol) (error "No symbol at point!")))) (beginning-of-buffer) ;; (query-replace-regexp symbol replacement) (replace-regexp (format "\\b%s\\b" (regexp-quote symbol)) replacement)))) #+END_SRC (Unfortunately, as it currently is, there is no universal argument support: ~C-u 2 M-p~ does /not/ take you to the second previous instance of a word ---the prefix is instead ignored. We can quickly form a work-around with doc:defhydra ---which gives us temoporary modal modes.) Finally, let's use [[kbd:M-n ╱ M-p ╱ M-']] to move to the next, previous, and replace the word at point /but also/ to give us a meanu to repeat any of these actions. #+BEGIN_SRC emacs-lisp (defmacro my/make-navigation-hydra (initial-action) `(defhydra word-navigation (:body-pre (,initial-action)) "Word-at-point Navigation" ("n" ahs-forward "Next instance") ("p" smartscan-symbol-go-backward "Previous instance") ("r" my/symbol-replace "Replace all occurances") ("s" ahs-display-stat "Stats"))) ;; (bind-key* str func) ≈ (global-set-key (kbd str) func) (bind-key* "M-n" (my/make-navigation-hydra ahs-forward)) (bind-key* "M-p" (my/make-navigation-hydra ahs-backward)) (bind-key* "M-'" (my/make-navigation-hydra my/symbol-replace)) #+END_SRC An alternative is the [[https://github.com/mickeynp/smart-scan][smart-scan]] library ---which does the same thing, but does not provide the highlighting occurances at point. # The default symbol replacement is [[https://github.com/mickeynp/smart-scan/issues/23][over-zealous]] in that it replaces sub-terms # occurring as parts of larger words. Let's do something about that. ** Letter-based Navigation :Disabled: :PROPERTIES: :CUSTOM_ID: Letter-based-Navigation :header-args: :tangle no :END: At a glance of possible positions, across windows, and a key to jump there is a feature provided to us by [[https://github.com/winterTTr/ace-jump-mode/wiki/AceJump-FAQ][ace-jump]] ---here is an [[https://www.youtube.com/watch?feature=player_embedded&v=UZkpmegySnc#!][emacs-rocks 2-minute video]]. For example, =C-c SPC m= greys our all windows and places a red letter at the start of any word that begins with /m/, then I may press a letter to jump to the associated position in the associated window. Using ~C-u C-c SPC~ and ~C-u C-u C-c SPC~ let me jump to any character or to any visible line. ➩ Super simple use case: Fix your eyes on an occurence of a word, then ~C-c SPC~ to quickly jump to it so as to edit the sentence in which it occurs. - It's like ~C-s~ but more lightweight. #+begin_src emacs-lisp (use-package ace-jump-mode :config (bind-key* "C-c SPC" 'ace-jump-mode)) ;; See ace-jump issues to configure for use of home row keys. #+end_src There is a newer and somewhat more powerful package, [[https://github.com/abo-abo/avy][avy]], which accompishes the same goal. It uses a tree style to jumipng: Locations are given two letter combinations, one presses one letter to jump to a group of text, then another letter to jump somewhere in that grouping. I prefer ace-jump since it greys everthing out, whereas avy surrounds jump locations with a box. Here is an [[https://www.youtube.com/watch?v=zar4GsOBU0g][emacs-doom 6-minute video]] for avy. There is also [[https://github.com/tam17aki/ace-isearch][ace-isearch]] for bridinging different navgiational methods ---one begins incremental search, ~s-f~, then according to a pause and length of input, one of the navgiational methods, such as isearch or avy or helm-swoop, will be begun. I'm okay with using ~C-s~ for helm-swoop and ~C-c SPC~ for ace-jump, and still have ~s-f~ for incremental search, which I hardly use. *What is bind-keys**? Major modes provide specfic use and so their bindings always take precedence over global bindings ---e.g., the major mode binding may do what the global does but with extra mode-specfic behaviour, such as indentation. Other times, a major mode's binding simply uses the same key presses with completely unrelated behaviour. If we want to avoid having our global keybindings shadowed by a major mode, we may use the ~bind-key*~ /macro/ of ~use-package~, or the ~bind-keys*~ /macro/ when there are multiple keys; these are macros, not clauses. ---These essentially creates a dedicated minor mode behind the scenes, which saves us the work of [[https://emacs.stackexchange.com/a/358/10352][doing it ourselves]]. | | ~(bind-keys* (k₁ . f₁) … (kₙ . fₙ))~ | | ≈ | These keybindings override all minor modes that use keys =kᵢ=. | Of course we can also use it without the asterisk; e.g.: #+begin_src emacs-lisp ;; C-x o ⇒ Switch to the other window ;; C-x O ⇒ Switch back to the previous window (bind-key "C-x O" (lambda () (interactive) (other-window -1))) #+end_src **   =C-c e n,p=: Taking a tour of one's edits :Disabled: :PROPERTIES: :CUSTOM_ID: C-c-e-n-p-Taking-a-tour-of-one's-edits :header-args: :tangle no :END: This package allows us to move around the edit points of a buffer /without/ actually undoing anything. We even obtain a brief description of what happend at each edit point. This seems useful for when I get interrupted or lose my train of thought: Just press [[kbd:C-c e p]] to see what I did recently and where ---the “e” is for “e”dit. #+BEGIN_SRC emacs-lisp ;; Give me a description of the change made at a particular stop. (use-package goto-chg :custom (glc-default-span 0)) (my/defhydra "C-c e" "Look at them edits!" bus :\ ("p" goto-last-change "Goto nᵗʰ last change") ("n" goto-last-change-reverse "Goto more recent change")) #+END_SRC Compare this with ~C-x u~, or ~undo-tree-visualise~, wherein undos are actually performed. Notice, as a hydra, I can use ~C-c e~ followed by any combination of ~p~ and ~n~ to navigate my recent edits /without/ having to supply the prefix each time. ** visual-regexp :PROPERTIES: :CUSTOM_ID: visual-regexp :END: #+begin_src emacs-lisp ;; While constructing the regexp in the minibuffer, get live visual feedback for the (group) matches. ;; E.g., try: M-% use-\(.+?\) \(.+\)\b ENTER woah \1 and \2 ;; ;; C-u M-% do to regexp replace, without querying. (use-package visual-regexp :config (define-key global-map (kbd "M-%") (lambda (&optional prefix) (interactive "P") (call-interactively (if prefix #'vr/replace #'vr/query-replace))))) #+end_src ** LaTeX ⇐ Org-Mode :Disabled: :PROPERTIES: :CUSTOM_ID: Org-Mode-PDF-HTML :header-args: :tangle no :END: In this section we consider the Org-mode export for PDFs (LaTeX). For example, we account for LaTeX citations. # Installing LaTex # $ brew install --cask mactex # $ eval "$(/usr/libexec/path_helper)" # $ type pdflatex # => pdflatex is /Library/TeX/texbin/pdflatex # # The ~minted~ environment can't appear as an argument to another command; a # [[https://tex.stackexchange.com/questions/102416/error-when-compiling-a-minted-listings-inside-a-memoir-subfloat][work-around]] is to use a ‘box’. Learn more about LaTeX boxes [[https://latexref.xyz/Boxes.html][here]]. *** Get LaTeX: :PROPERTIES: :CUSTOM_ID: Get-LaTeX :END: # +begin_src shell # time brew cask install mactex-no-gui # +end_src #+begin_src emacs-lisp (system-packages-ensure "mactex") #+end_src - This is a redistribution of TeX Live specifically for macOS. - We get the 4GB version since it has [[https://tex.stackexchange.com/a/1041/69371][everything]] and so do not need to worry about missing style files. - This took about 12 minutes on my machine. Restart Emacs, enter =$e^{i \cdot \pi} + 1 = 0$= then press kbd:C-c_C-x_C-l to have it rendered inline. + *Minted:* Get tool for colourful code snippets for LaTeX ---see “minted” in the main article. #+begin_src emacs-lisp (system-packages-ensure "pygments") #+end_src + *Not anymore:* Get a neato PDF presentation console: =brew install pdfpc= - With =pdfpc myfile.pdf= you get a nice timer and multiple views of the current slide and upcoming slides ---with support for multiple monitors. - Install ScreenBrush, from the Apple Store, for easily drawing/annotating my screen ---e.g., when I'm giving a virtual lecture to my students. - An alternative is =brew install mupdf= then =mupdf-gl myfile.pdf= and press =f= for fullscreen then =a= for adding/adorning drawings ---it was too rough to use live. Finally, within Emacs: =M-x pdf-tools-install= :Possibly_breaking_toc-org: #+BEGIN_SRC emacs-lisp :tangle no ;; Use 3 headlines of export, which is the default ;; (setq org-export-headline-levels 4) ;; no numbers by default at export ;; (setq org-export-with-section-numbers nil) #+END_SRC :End: *** Working with Citations :Disabled:Not_Used: :PROPERTIES: :CUSTOM_ID: Working-with-Citations :END: # TODO Fix me, breaking Github Actions test setup [[https://github.com/jkitchin/org-ref][An exquisite system]] for handling references. The following entity will display useful data when the mouse hovers over it (•̀ᴗ•́)و If you click on it, then you're in for a lot of super neat stuff, such as searching for the pdf online! cite:agda_overview ( In HTML export, the citation doesn't link anywhere. ) #+BEGIN_SRC emacs-lisp :tangle no (unless noninteractive (use-package org-ref :custom ;; Files to look at when no “╲bibliography{⋯}” is not present in a file. ;; Most useful for non-LaTeX files. (reftex-default-bibliography '("~/thesis-proposal/papers/References.bib")) (bibtex-completion-bibliography (car reftex-default-bibliography)) (org-ref-default-bibliography reftex-default-bibliography)) ;; Quick BibTeX references, sometimes. (use-package helm-bibtex) (use-package biblio) ) #+END_SRC Execute ~M-x helm-bibtex~ or =C-c ]= and, say, enter =emacs= and you will be presented with all the entries in the bib database that mention ‘emacs’. Super cool stuff. Moreover, if no such entries exist, then we can look some up using the interface! Read the manual [[https://github.com/jkitchin/org-ref/blob/master/org-ref.org][online]] or better yet as an org-file with ~M-x org-ref-help~. This is an Org-mode application since the citations have tooltips and export nicely to LaTeX & HTML via the Org-mode exporter. *** Bibliography & Coloured LaTeX using Minted :PROPERTIES: :CUSTOM_ID: Bibliography-Coloured-LaTeX-using-Minted :END: Execute the following for bibliography references as well as minted Org-mode uses the Minted package for source code highlighting in PDF/LaTeX ---which in turn requires the pygmentize system tool. #+BEGIN_SRC emacs-lisp (setq org-latex-listings 'minted org-latex-packages-alist '(("" "minted")) org-latex-pdf-process '("pdflatex -shell-escape -output-directory %o %f" "biber %b" "pdflatex -shell-escape -output-directory %o %f" "pdflatex -shell-escape -output-directory %o %f")) #+END_SRC For faster pdf generation, possibly with errors, consider invoking: #+begin_example emacs-lisp (setq org-latex-pdf-process '("pdflatex -interaction nonstopmode -output-directory %o %f")) #+end_example By default, Org exports LaTeX using the ~nonstopmode~ option, which tries its best to produce a PDF ---which ignores typesetting errors altogether, which is not necessary ideal when using LaTeX. *** COMMENT Excellent PDF Viewer :PROPERTIES: :CUSTOM_ID: Excellent-PDF-Viewer :END: Need to (re)build the epdfinfo program, do it now ? (y or n) n -------------------------------------------------------------------------------- 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 ; :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. ** HTML ⇐ Org-mode :PROPERTIES: :CUSTOM_ID: HTML-Org-mode :END: In this section we consider the Org-mode exporters for PDFs and HTMLs. For example, we account for LaTeX citations and reliable HTML anchors. #+BEGIN_SRC emacs-lisp (use-package htmlize ) ;; Main use: Org produced htmls are coloured. ;; Can be used to export a file into a coloured html. #+END_SRC *** HTML “Folded Drawers” :Move_to_org_special_block_extras: :PROPERTIES: :CUSTOM_ID: HTML-Folded-Drawers :END: #+BEGIN_SRC emacs-lisp (defun my/org-drawer-format (name contents) "Export to HTML the drawers named with prefix ‘fold_’, ignoring case. The resulting drawer is a ‘code-details’ and so appears folded; the user clicks it to see the information therein. Henceforth, these are called ‘fold drawers’. Drawers without such a prefix may be nonetheless exported if their body contains ‘:export: t’ ---this switch does not appear in the output. Thus, we are biased to generally not exporting non-fold drawers. One may suspend export of fold drawers by having ‘:export: nil’ in their body definition. Fold drawers naturally come with a title. Either it is specfied in the drawer body by ‘:title: ⋯’, or otherwise the drawer's name is used with all underscores replaced by spaces. " (let* ((contents′ (replace-regexp-in-string ":export:.*\n?" "" contents)) (fold? (s-prefix? "fold_" name 'ignore-case)) (export? (string-match ":export:\s+t" contents)) (not-export? (string-match ":export:\s+nil" contents)) (title′ (and (string-match ":title:\\(.*\\)\n" contents) (match-string 1 contents)))) ;; Ensure we have a title. (unless title′ (setq title′ (s-join " " (cdr (s-split "_" name))))) ;; Output (cond ((and export? (not fold?)) contents′) (not-export? nil) (fold? (thread-last contents′ (replace-regexp-in-string ":title:.*\n" "") (format "
%s %s
" title′)))))) (setq org-html-format-drawer-function 'my/org-drawer-format) #+END_SRC With the following invocations we only see the odd indexed ‘hello’s, where the latter two are folded up. #+BEGIN_SRC org :tangle no :this-drawer-is-exported: :export: t hello 1 :End: :this-drawer-is-NOT-exported: hello 2 :End: :fold_This_drawer_has_a_title_in_the_body: :title: I am the drawer title 0 hello 3 :End: :fold_This_drawer_is_NOT_exported: :title: Why are we here? :export: nil hello 4 :End: :fold_I_am_the_drawer_title_1: hello 5 :End: #+END_SRC I doubt I could show an example in the Github README, since no HTML export is happening using my setup. In case you're reading this on my blog, which has exported HTML. Here's the example: :fold_hello_world: :title: ¡Hola! Buenas tardes, Amigo Hey bud, hope you're enjoying this read ^_^ :End: Now that I've written this, I'm thinking it may have been preferably to use an org-block…? *** Diagrams with Mermaid ---Not Reccommended :Disabled: :PROPERTIES: :CUSTOM_ID: Diagrams-with-Mermaid-Not-Reccommended :header-args: :tangle no :END: Let's try out an alternative to PlantUML ---covered below in §[[Workflow States]]. First, let's get the tool. #+BEGIN_SRC shell :tangle no npm install mermaid.cli sudo git clone git@github.com:arnm/mermaid-layer.git ~/.emacs.d/private/mermaid #+END_SRC Then, let's get the associated ~mermaid~ package. #+BEGIN_SRC emacs-lisp (use-package ob-mermaid :custom ob-mermaid-cli-path "~/node_modules/.bin/mmdc") #+END_SRC Then, =C-c C-c= on the following: #+BEGIN_SRC mermaid :file test.png :theme neutral :background-color green :tangle no :results replace :eval never sequenceDiagram A-->B: Works! #+END_SRC #+RESULTS: [[file:test.png]] + =C-c C-x C-v= ⇒ Show images inline + Mermaid supported headers: - ~file~ to name the svg/png/pdf output - ~width~ or ~height~ or the resulting image - ~theme~ used, such as ~default, forest, dark, neutral~, for foreground entities - ~background-color~ such as ~transparent, red, #F0F0F0~ * The transparent option is nice ^_^ + You can insert new lines using ~
~ and horizontal rules via ~
~. Similarly you can use other HTML tags such as ~
~; if you have too many you can make CSS file then use the header argument ~:css-file~. + Add “non-breaking space” with ~ ~. This is a forced extra space and it prevents a line break at its location. You can insert it repeatedly, but for two spaces use ~ ~ and for four spaces use ~ ~. If link text cuts off prematurely, use extra space /with/ a newline: ~A-- text  
-->B~. *Warning*: JavaScript has some issues when working with Unicode and so, being a JavaScript utility, ~mermaid~ hangs when Unicode is used. On the upside, being a JavaScript utility, ~mermaid~ entities can have [[https://mermaid-js.github.io/mermaid/#/flowchart?id=interaction][arbitrary code attached]] to them to be executed upon clicks ---for use in browsers. - However, the Greek letters are supported; e.g., γ and Σ. See [[https://mermaid-js.github.io/mermaid/#/flowchart?id=nodes-amp-shapes][here]] for possible node shapes. #+begin_quote After forming an intricate diagram of related design patterns, I had to use a number of HTML notions, such as =, , ,

,  ,
,
,
= and it was a bit more than I would have liked. In particular, the only way to change font size was to use the deprecated HTML tag == or heading tags like =

=; even worse, the resulting PDF image did not look nice ---I had to stretch it out. The command line tool is *lacking functionality* and so the docs are not helpful. E.g., I cannot produce pie charts using the command line tool. #+end_quote ***   [[https://revealjs.com/?transition=zoom#/][Reveal.JS]] -- The HTML Presentation Framework :PROPERTIES: :CUSTOM_ID: https-revealjs-com-transition-zoom-Reveal-JS-The-HTML-Presentation-Framework :END: Org-mode documents can be transformed into beautiful slide decks with [[https://github.com/yjwen/org-reveal/blob/master/Readme.org][org-reveal]] with the following two simple lines. #+BEGIN_SRC emacs-lisp (use-package ox-reveal :custom (org-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js")) #+END_SRC # MA: ??? It looks like ox-reveal is being abandoned in favor of org-re-reveal, # a fork compatible with org-mode 9.2? For example, execute, ~C-x C-e~ after the closing parenthesis of, the following block to see an example slide-deck (─‿‿─) #+BEGIN_SRC emacs-lisp :tangle no (progn (shell-command "curl https://raw.githubusercontent.com/yjwen/org-reveal/696613edef0fe17a9c53146f79933fe7c4101100/Readme.org >> Trying_out_reveal.org") (switch-to-buffer (find-file "Trying_out_reveal.org")) (org-reveal-export-to-html-and-browse)) #+END_SRC Org-mode exporting, ~C-c C-e~, now includes an option ~R~ for such reveal slide decks. :Hide: [[https://alhassy.github.io/next-700-module-systems/proposal/defence-slides.html#/sec-title-slide][Here]] ([[https://raw.githubusercontent.com/alhassy/next-700-module-systems/master/proposal/defence-slides.org][source]]) is an example of org-reveal slides where I add a number to each page, use multiple columns, and extend the margins perhaps a bit too much. :End: Two dimensional slides may be a bit new to some people, so I like to give viewers an option, in tiny font, to view the slide-deck continuously and remind them that ~?~ provides useful shortcuts. #+BEGIN_SRC emacs-lisp (setq org-reveal-title-slide "

%t

%a

⟪ Flattened View ; Press ? for Help ⟫ ") #+END_SRC One should remove the ~&showNotes=true~ if they do not want to include speaker notes in the flattened view. Within the flatenned view, one may wish to ~CTRL/CMD+P~ then save the resulting PDF locally. ** C-c C-l Org-mode ⇐ HTML :PROPERTIES: :CUSTOM_ID: Org-mode-HTML :END: The following let's us copy htlm into org format using eww, Emacs' built-in web browser. #+BEGIN_SRC emacs-lisp :tangle no ;; See: https://emacs.stackexchange.com/questions/7171/paste-html-into-org-mode (use-package org-eww :quelpa (org-eww :fetcher git :url "https://github.com/Fuco1/org-mode.git")) #+END_SRC It does not work as I'd like, but may prove useful to have around. + Possibly useful: Open a webpage with ~M-x eww~ then toggle ~M-x read-only-mode~ to edit the text, say for notes or deletions, as you read! No need to copy-paste. -------------------------------------------------------------------------------- [[https://github.com/alphapapa/org-web-tools][org-web-tools]] claims to /view, capture, and archive Web pages in Org-mode/; this may be a very useful tool. #+begin_src emacs-lisp (use-package org-web-tools :config ;; Insert an Org-mode link to the URL in the clipboard or kill-ring. Downloads ;; the page to get the HTML title. ;; (bind-key* "C-c C-l" #'org-web-tools-insert-link-for-url) ;; Instead, see my/org-insert-link-dwim below. ) #+end_src Other useful functions, needing pandoc: doc:org-web-tools-insert-web-page-as-entry and doc:org-web-tools-convert-links-to-page-entries. #+begin_src emacs-lisp ;; C-u C-c C-l ⇒ Paste URL with title, WITHOUT prompting me for anything. ;; C-c C-l ⇒ Prompt me for title. (bind-key* "C-c C-l" (lambda () (interactive) (call-interactively (if current-prefix-arg #'org-web-tools-insert-link-for-url #'my/org-insert-link-dwim)))) ;; From: (defun my/org-insert-link-dwim () "Like `org-insert-link' but with personal dwim preferences. - When text is selected, use that as the link description --and prompt for link type - When a URL is in the clipboard, use that as the link type - On an existing Org link, prompt to alter the link then to alter the description - With a ‘C-u’ prefix, prompts for a file to link to. - It is relative to the current directory; use ‘C-u C-u’ to get an absolute path. It fallsback to `org-insert-link' when possible. Functin Source: https://xenodium.com/emacs-dwim-do-what-i-mean/" (interactive) (let* ((point-in-link (org-in-regexp org-link-any-re 1)) (clipboard-url (when (string-match-p "^http" (current-kill 0)) (current-kill 0))) (region-content (when (region-active-p) (buffer-substring-no-properties (region-beginning) (region-end))))) (cond ((and region-content clipboard-url (not point-in-link)) (delete-region (region-beginning) (region-end)) (insert (org-make-link-string clipboard-url region-content))) ((and clipboard-url (not point-in-link)) (insert (org-make-link-string clipboard-url (read-string "title: " (with-current-buffer (url-retrieve-synchronously clipboard-url) (dom-text (car (dom-by-tag (libxml-parse-html-region (point-min) (point-max)) 'title)))))))) (t (call-interactively 'org-insert-link))))) #+end_src * TODO RDD ** get the pkg #+begin_src emacs-lisp :tangle init.el (use-package repl-driven-development) (setq repl-driven-development-echo-duration 10) #+end_src ** terminal #+begin_src emacs-lisp :tangle init.el ;; Sometimes I see a bunch of shell incantations in a README or something and I'd like to execute them right there and then, ;; and not have to bother with copying them over to a terminal and execute there. As such, here's a quick key binding to execute ;; shell commands from anywhere. ;; (repl-driven-development [C-x C-t] "bash" :prompt "bash-3.2\\$") (repl-driven-development [C-x C-t] terminal) #+end_src ** jshell #+begin_src emacs-lisp :tangle init.el ;; Set “C­x C­j” to evaluate Java code in a background REPL. (repl-driven-development [C-x C-j] ;; enable assertions, and add everything installed, via `mvn', in scope. (format "jshell --class-path %s --enable-preview -R -ea --feedback silent" (concat ".:" (shell-command-to-string "find ~/.m2/repository -name \"*.jar\" -type f 2>/dev/null | tr '\n' ':'"))) :prompt "jshell>" :init "\n /set mode EmacsJavaMode normal -command \n /set format EmacsJavaMode display \"{pre}added import {name}{post}\" import-added \n /set format EmacsJavaMode display \"{pre}re-added import {name}{post}\" import-modified,replaced \n /set format EmacsJavaMode result \"{type} {name} = {value}{post}\" added,modified,replaced-primary-ok \n /set truncation EmacsJavaMode 40000 \n /set feedback EmacsJavaMode \n System.out.println(\"Enjoy Java with Emacs (。◕‿◕。))\")") ;; TODO [Truncation; Low] https://github.com/xiongtx/eros/blob/master/eros.el#L202 #+end_src ** mvn #+begin_src emacs-lisp :tangle init.el (defun mvn (groupId artifactId) "Quickly install a library from Maven Central." (async-shell-command (format "mvn org.apache.maven.plugins:maven-dependency-plugin:2.8:get -Dartifact=%s:%s:LATEST:jar:sources" groupId artifactId))) (when nil ⨾⨾ Example use of `mvn` ;; ⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾ ;; First confirm C-x C-j works as intended IntStream.range(0, 15).mapToObj(i -> i % 15 == 0 ? "FizzBuzz" : i % 3 == 0 ? "Fizz" : i % 5 == 0 ? "Buzz" : String.valueOf(i)).toList() ;; ⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾ (mvn "org.antlr" "antlr4") ;; C-x C-e ;; Now re-start the Java C-x C-j repl via a C-x C-e (lame! not ergonomic!) ;; Now check that you have access to antrl in your repl by importing it and looking at one of its classes: ;; ⦃ jshell --class-path /Users/musa/.m2/repository/org/antlr/antlr4-runtime/4.13.0/antlr4-runtime-4.13.0.jar ⦄ import org.antlr.v4.runtime.*; CommonTokenStream.class ;; NOTE: This is the runtime, to use the actual tool: java -jar /Users/musa/.m2/repository/org/antlr/antlr4/4.13.0/antlr4-4.13.0-complete.jar ;; Alternatively, ;; $ jshell ;; > var x = 5 ;; > import org.antlr.v4.runtime.*; // CRASHES since antlr is not in scope ;; > /reset --class-path /Users/musa/.m2/repository/org/antlr/antlr4-runtime/4.13.0/antlr4-runtime-4.13.0.jar ;; > import org.antlr.v4.runtime.*; // WORKS, yay ;; > x // CRASHES, not in scope ;; ⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾ import org.apache.commons.lang3.StringUtils; StringUtils.class; /imports // JShell command to list all imports, it now contains apache! // Guava is the Google core libraries for Java import com.google.common.collect.ImmutableMap; ImmutableMap.of(1, "A", 2, "B") // ⇒ {1=A, 2=B} ;; (mvn "com.google.code.gson" "gson") // Then C-x C-e to update the repl definition of C-x C-j to include the updated gson library. import com.google.gson.*; String json = new Gson().toJson(Map.of("me", List.of(1, 2,3), "you", Map.of("Love", "Lisp", "Hate", "Verbosity"))) ;; ⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾ ;; TestNG is a testing framework; supporting tests configured by annotations, data-driven testing, parametric tests, etc. (mvn "org.testng" "testng") import org.testng.*; /imports // Now can see org.testng at the end of the list ;; ⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾⨾ ;; TODO[Low]: For working with Lombok annotations, use the jshell `/reset --class-path` command to include the lombok compiled file into ;; the current Jshell session. ;; ;; See: https://stackoverflow.com/questions/74084364/how-to-use-lombok-in-jshell ;; ;; (mvn "org.projectlombok" "lombok") ;; import lombok.*; ;; @lombok.Data class Test { private String name; } ;; new Test().equals(new Test()) ) #+end_src ** Adding support for “//!use” & “//!omit” top level repl commands #+begin_src emacs-lisp ;; Adding support for “//!use” & “//!omit” top level repl commands (setq repl/jshell/classpath (shell-command-to-string "find ~/.m2/repository -name \"*.jar\" -type f | tr '\n' ':'")) (advice-add 'repl/jshell :around (lambda (repl &rest args) (if (equal nil current-prefix-arg) ;; No prefix supplied (progn (setq rdd---current-input (s-replace-regexp "\n" "" (s-trim-left (if (region-active-p) (buffer-substring-no-properties (region-beginning) (region-end)) (substring-no-properties (thing-at-point 'line)))))) (if (s-starts-with? "//!use" rdd---current-input) (-let [jar (s-trim (s-chop-prefix "//!use" rdd---current-input))] (repl/top-level//!use jar)) (if (s-starts-with? "//!omit" rdd---current-input) (-let [jar (s-trim (s-chop-prefix "//!omit" rdd---current-input))] (repl/top-level//!omit jar)) ;; otherwise business as usual (apply repl args)))) (pcase current-prefix-arg (-1 ;; reset classpath to default, then business as usual. (setq repl/jshell/classpath (shell-command-to-string "find ~/.m2/repository -name \"*.jar\" -type f | tr '\n' ':'")) (apply repl args)) (999 (message-box "It worked")) ;; otherwise business as usual (t (apply repl args)))))) ;; remove all advice ;; (-let [sym 'repl/jshell] (advice-mapc (lambda (advice _props) (advice-remove sym advice)) sym)) (cl-defun repl/top-level//!omit (str) (with-temp-buffer (setq repl/jshell/classpath (s-replace-regexp (format ":[^:]*%s[^:]*:" str) ":" repl/jshell/classpath)) (insert "/env --class-path ") (insert repl/jshell/classpath) (repl/jshell (point-min) (point-max)))) (cl-defun repl/top-level//!use (str) "If the given jar cannot be added successful, the existing classpath remains untouched. Return to your default classpath by invoking the repl with the -1 prefix. Example usage: //!use ~/path/to/compiled/java/classes import com.x.y.z; Where directory hierarchy com/x/y/z denotes a Java package under the above //!use path. " (with-temp-buffer (setq repl/jshell/classpath (concat (s-trim str) ":" repl/jshell/classpath)) (insert "/env --class-path ") (insert repl/jshell/classpath) (repl/jshell (point-min) (point-max)))) #+end_src ** Misc top level cmds TODO: Make repl load all company class files, that way I have them upon start up. OR: Provide a command `//!load ~/dir` which recursively loads/imports all java files? <---Probably this! ** COMMENT C-x C-e ∷ REPL-driven development for ELisp / NodeJS / Python / Etc ! :PROPERTIES: :CUSTOM_ID: C-x-C-e-REPL-driven-development-for-NodeJS :END: | /Evaluate code and see the results inline ---A feedback loop that is faster than ever!/ | Within Emacs, kbd:C-x_C-e evaluates a Lisp expression /anywhere/; e.g., at the end of ~(message-box "hello world")~ press ~C-x C-e~ to see a greeting. # Likewise, evaluating a variable shows you its value: user-full-name. - We ran some code /without/ explicitly running an interpreter/repl/compiler! - This is known as “REPL driven development” (RDD): There is a running REPL server for your language, implicitly in the background, and your editor (say with ~C-x C-e~) will send it a line (or a selected region) of code for evaluation; we then see the result as an overlay in our current buffer. + You /choose/ which code gets (re)evaluated. - A quick introduction to RDD can be viewed at [[https://purelyfunctional.tv/lesson/what-is-repl-driven-development/][PurelyFunctional.tv]]. *** ELisp :PROPERTIES: :CUSTOM_ID: ELisp :END: By default, Emacs Lisp's kbd:C-x_C-e shows results only in the minibuffer; near the bottom of the screen. Let's also have evaluation results displayed as inline overlays ---at the location that the user, us, is actually looking/working; rather than forcing their eyes to shift up&down when writing&evaluating. + kbd:C-u_C-x_C-e inserts the evaluation result at point; kbd:C-u_0_C-x_C-e does so /without truncating/ lengthy output. + Read this [[https://karthinks.com/software/an-elisp-editing-tip/][Sweet & short blog/GIFs]] on practical uses of kbd:C-x_C-e when working with Lisp. #+begin_src emacs-lisp ;; Evaluation Result OverlayS for Emacs Lisp (use-package eros :defer nil :init (eros-mode t)) #+end_src # In particular, this gives us the Lisp function # `eros--eval-overlay', which evaluates its argument X # and produces an overlay of the result at position P. # E.g., (eros--eval-overlay "hola" (point-min)) # Shows a momentary overlay at the first line. *** JavaScript :PROPERTIES: :CUSTOM_ID: JavaScript :END: Let's setup RDD for JavaScript ---by having a NodeJS repl server running in the background. - See [[https://github.com/anonimitoraf/skerrick][skerrick: REPL-driven development for Javascript]] for animated GIFs; it works with modules as well. #+begin_src emacs-lisp (use-package skerrick :defer nil :init ;; Needs to be run on the very first install of skerrick. Or when you want to upgrade. (unless (equal (shell-command-to-string "type skerrick") "skerrick not found\n") (skerrick-install-or-upgrade-server-binary))) ;; Should be run in a JS buffer; it is buffer specific. ;; (skerrick-start-server) ;; Now main function, entry point is: ;; M-x skerrick-eval-region #+end_src Let's provide a quick keyboard shortcut. E.g., kbd:C-x_C-e evaluates ELisp, so let's mimic that for JS buffers: #+begin_src emacs-lisp (require 'js) ;; Defines js-mode-map ;; Evaluate a region, if any is selected; otherwise evaluate the current line. (bind-key "C-x C-e" (lambda () (interactive) (if (use-region-p) (skerrick-eval-region) (beginning-of-line) (set-mark-command nil) (end-of-line) (skerrick-eval-region) (pop-mark))) 'js-mode-map) #+end_src Trying something else instead of skerrick, until https://github.com/anonimitoraf/skerrick/issues/7 is resolved. In any JS file, I just press ~C-x C-e~ and the JS interpreter is brought up with the evaluation results shown: #+begin_src emacs-lisp (use-package js-comint :defer nil) (define-key js-mode-map (kbd "C-x C-e") (lambda () (interactive) (call-interactively #'js-comint-repl) (other-window -1) (js-comint-send-last-sexp))) #+end_src For instance, #+begin_src js :tangle no // Start the server... then // On each line and press C-x C-e let a = "hello" let b = "world" (a + ' ' + b).toUpperCase() // The final line should show: HELLO WORLD #+end_src **** Preserving the context :PROPERTIES: :CUSTOM_ID: Preserving-the-context :END: When testing an application, you might notice a bug in a particular context ---i.e., a particular configuration in the app. 1. The classic approach is to kill the app; i.e., stop the server that is, well, serving the app. 2. Solve the problem. 3. Try to get back to the configuration, context, you were in beforehand and check that the problem has been resolved. A better approach is to ignore the bookkeeping steps, 1&3, and just do step 2. For that, there are numerous packages: - [[https://github.com/mishoo/livenode/][livenode: Live-code your NodeJS applications]] ⨾⨾ [[https://vimeo.com/60636079][Video demo]] ~11min ⨾⨾ Last updated 2013 - [[https://github.com/skeeto/skewer-mode][skewer-mode: Live web development in Emacs]] ⨾⨾ [[https://www.youtube.com/watch?v=4tyTgyzUJqM&ab_channel=Skeeto][Silent video demo]] ~5min ⨾⨾ Last updated 2020 - [[https://github.com/swank-js/swank-js][swank-js: Swank backend for Node.JS and in-browser JavaScript]] ⨾⨾ Last updated 2015 For more RDD alternatives in Emacs, see https://github.com/anonimitoraf/skerrick/issues/8. *** Python, etc :PROPERTIES: :CUSTOM_ID: Python-etc :END: It /seems/ to be /super easy/ to add such a support for other languages, such as Python. See the final comment [[https://karthinks.com/software/an-elisp-editing-tip/][here]] for the tiny change required. ** COMMENT Quickly Run Code Snippets :PROPERTIES: :CUSTOM_ID: Quickly-Run-Code-Snippets :END: Sometimes we want to quickly run some code /without making a dedicated file/ or with a file but /without remembering the terminal incantation to do so/, enter ~quickrun~. Anywhere, we can select a snippet of code and run TODO. doc.quickrun-region to execute that snippet after selecting the associated programming language, or TODO. doc.quickrun-replace-region if we want the results in-line. If our language of choice does not exist, we can [[https://github.com/emacsorphanage/quickrun#user-defined-command][easily add support for it]]. #+begin_src emacs-lisp ;; In any programming buffer, “M-x quickrun” to execute that program. ;; Super useful when wanting to quickly test things out, in a playground. ;; ;; E.g., Make a new file named “hello.py” containing “print "hi"”, then “M-x quickrun”. ;; ;; Enable “quickrun-autorun-mode” to run code after every save. (use-package quickrun ;; ⇒ “C-c C-r” to see output, “q” to close output ;; ⇒ “C-u C-c C-r” prompts for a language (Useful when testing snippets different from current programming mode) ;; ⇒ In a non-programming buffer, “C-c C-r” runs selected region. :defer nil :config (bind-key* "C-c C-r" (lambda (&optional start end) (interactive "r") (if (use-region-p) (quickrun-region start end) (quickrun current-prefix-arg))))) #+end_src Example... #+begin_src emacs-lisp (system-packages-ensure "rust") ;; Rust Compiler ;; Select the following then press C-c C-r: fn main() { println!("Hello, World!"); } #+end_src Actually, let's get a [[https://github.com/brotzeit/rustic#intro][full Rust development environment for Emacs]] (which also has [[https://github.com/brotzeit/rustic#org-babel][great support for Org-babel]].) #+begin_src emacs-lisp (use-package rustic :defer nil) ;; Open any Rust file, and run “M-x lsp” which will then prompt you to install ;; rust-analyzer, the rust LSP. ;; ;; LSP for Rust ⇒ Goto definition (M-. / ⌘-l), code completion with types and ;; docstrings, colourful documentation on hover, “Run [Test] | Debug” overlays, ;; super nice stuff! Run “M-!”/[M-x company-show-doc-buffer] if you want the doc in a colourful buffer. ;; ;; Below, hover over “Vec” and see nice, scrollable, colourful docs on vectors. ;; let v:Vec<_> = vec![1, 2, 3]; ;; The offical Rust toolchain installer (system-packages-ensure "rustup") (shell-command "rustup update") #+end_src :More_on_rust: Now, C-c C-c C-d on something to get help/description of it. (setq rustic-racer-rust-src-path "/Users/musa/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/") # Get Rustup, load it into current terminal session curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh source ~/.bash_profile source ~/.bashrc type rustup # Get stuff rustup update rustup component add rust-src cargo +nightly install racer rustup toolchain install nightly rustup component add rustc-dev --toolchain=nightly cargo +nightly install racer :End: * Don't show updating/installation shell buffers #+begin_src emacs-lisp ;; By default, say, (async-shell-command "date") produces a buffer ;; with the result. In general, such commands in my init.el are for ;; updating/installing things to make sure I have the same up-to-date ;; setup where-ever I use my Emacs. As such, I don't need to see such buffers. (add-to-list 'display-buffer-alist '("\\*Async Shell Command\\*.*" display-buffer-no-window)) ;; For an approach that does not inhibit async-shell-command this way, ;; see https://emacs.stackexchange.com/questions/299/how-can-i-run-an-async-process-in-the-background-without-popping-up-a-buffer #+end_src * TODO Programming :PROPERTIES: :CUSTOM_ID: Programming :END: Herein we configure utilites for version control, function and variable lookup, and template expansion for inescapably repetitive scenarios. TODO: Fix these docs # TODO: + Checkout branches/PRs with doc : my/gh-checkout # + See all company related PRs with doc : w-PRs # + Quickly look up language/library docs /within/ Emacs with kbd:C-c_d. #+begin_src emacs-lisp (when my/work-machine? (setq doom-modeline-buffer-file-name-style 'truncate-except-project)) #+end_src ** COMMENT devdocs :PROPERTIES: :CUSTOM_ID: devdocs :END: #+begin_src emacs-lisp ;; 1. Get docs of a languages: M-x devdocs-install ;; 2. Lookup docs: [C-u] M-x devdocs-lookup ;; 𝟚. Lookup docs: [C-u] C-c d (use-package devdocs :defer nil :bind ("C-c d" . #'devdocs-lookup) :config (when nil ;; “C-x C-e” the following once. (cl-loop for lang in '(javascript ramda typescript html css sass vue~3 vuex~4 vue_router~4 "angularjs~1.6" nginx webpack~5 web_extensions ;; eslint jest jq jsdoc prettier mocha chai jasmine ;; bash docker~19 git homebrew elisp ;; postgresql~14 redis sqlite ;; rust ruby~3 minitest "rails~7.0") do (devdocs-install (list (cons 'slug (format "%s" lang))))))) #+end_src ** COMMENT How do I do something? :PROPERTIES: :CUSTOM_ID: How-do-I-do-something :END: When programming, sometimes you just gotta Google “how do I do ⋯”. - The usual process is (1) open a browser, (2) make a Google query, (3) look at StackOverflow's most upvoted answer for your query, (4) copy-paste the code solution/example to your editor; [(5) get distracted by interesting things you'd like to read]. - Better would be to use the [[https://github.com/gleitz/howdoi][howdoi]] tool, which gives instant coding answers for common questions via the command line. - Below, my /Emacs Lisp/ function doc : howdoi let's me reduce the 4-step process to just 2 steps: Write your query /anywhere/ then call ~M-x howdoi~ on it to replace the query with the answer. (Or ~C-u M-x howdoi~ to see the full answer and a link to it on StackOverflow.) | /⚡ Never open your browser to look for help again ⚡/ | #+begin_src emacs-lisp (system-packages-ensure "howdoi") (cl-defun howdoi (&optional show-full-answer) "Instantly insert coding answers. Replace a query with a code solution; replace it with an entire answer if a prefix is provided. Example usage: On a new line, write a question such as: search and replace buffer Emacs Lisp Then invoke ‘M-x howdoi’ anywhere on the line to get a code snippet; or ‘C-u M-x howdoi’ to get a full answer to your query. " (interactive "P") (let ((query (s-collapse-whitespace (substring-no-properties (thing-at-point 'line)))) (flag (if show-full-answer "-a" ""))) (beginning-of-line) (kill-line) (insert (shell-command-to-string (format "howdoi %s %s" query flag))))) #+end_src ** Sleek Semantic Selection :PROPERTIES: :CUSTOM_ID: Sleek-Semantic-Selection :END: Super sleek way to select regions: Anywhere press kbd:⌘-r to select the current word, press it again to select sentence, then again for the current paragraph, then more to get the current section. #+begin_src emacs-lisp (use-package expand-region :bind (("s-r" . #'er/expand-region))) #+end_src You can watch an introductory ~3 minute video to expand-region at [[http://emacsrocks.com/e09.html][Emacs Rocks!]]. That is, /repeated ⌘+r expands the selection to the next logical segment of text:/ In writing this means “Word, sentence, paragraph”, and in programming this means “identifier, then incrementally larger scopes”. ** COMMENT Managing Processes/Servers from within Emacs ---Work-specific functions :PROPERTIES: :CUSTOM_ID: Managing-Processes-Servers-from-within-Emacs-Work-specific-functions :END: Let's make a few interactive Emacs Lisp functions to reduce the amount of time I need to be in a terminal. I'll use the /prefix ~“w-”~ for work stuff/. Example tasks: + Start/stop my servers + Interactively select an app to be opened in the browser + Do database migrations/rollbacks ⟨ Obfuscated with lorem ipsum text. ⟩ [[file:images/services-dashboard.png]] -------------------------------------------------------------------------------- Not using this, a bit too verbose to setup for each service but, more accurately, does not Just Workᵀᴹ for my needs. #+begin_src emacs-lisp ;; “M-x prodigy”, then press “s” to start a service; “S” to stop it; “$” to see it; “r”estart (use-package prodigy :disabled t) ;; C-h v prodigy-services ⇒ See possible properties. #+end_src *** my/defaliases :PROPERTIES: :CUSTOM_ID: my-defaliases :END: #+begin_src emacs-lisp (defalias 'defaliases 'my/defaliases) (defmacro my/defaliases (src &rest tgts) "Provide names TGTS as synonymous aliases for SRC, for discovarability. Often a function SRC can be construed from different perspectives, names, purposes TGTS. Another example is when I define things with the ‘my/’ prefix, but also want to use them without. Example use: (my/defaliases view-hello-file greet-others learn-about-the-world) In particular: (my/defaliases OLD NEW) ≈ (defalias 'NEW 'OLD)." `(--map (eval (quote (defalias `,it (quote ,src)))) (quote ,tgts))) #+end_src *** Making unkillable buffers & shells :PROPERTIES: :CUSTOM_ID: Making-unkillable-buffers-shells :END: #+begin_details "Making unkillable buffers & shells" #+begin_src emacs-lisp (defun my/declare-unkillable-buffer (name) (add-hook 'kill-buffer-query-functions `(lambda () (or (not (equal (buffer-name) ,name)) (progn (message "Not allowed to kill %s, burying instead; otherwise use “M-x force-kill”" (buffer-name)) (bury-buffer)))))) (my/defaliases my/force-kill force-kill w-force-kill) (cl-defun my/force-kill (&optional buffer-name) (interactive) (-let [kill-buffer-query-functions nil] (if buffer-name (kill-buffer buffer-name) (kill-current-buffer)) (ignore-errors (delete-window)))) (cl-defun my/run-unkillable-shell (command &optional (buffer-name command)) "Example use: (my/run-unkillable-shell \"cd ~/my-noejds-project; npm run dev\" \"my-nodejs-project\")" (-let [it (get-buffer buffer-name)] (if it (switch-to-buffer-other-window it) (async-shell-command command buffer-name) (my/declare-unkillable-buffer buffer-name)))) #+end_src #+end_details *** w-start/stop-services :PROPERTIES: :CUSTOM_ID: w-start-stop-services :END: #+begin_src emacs-lisp (defvar my/services nil "List of all services defined; used with `w-start-services' and `w-stop-services'.") (defun w-start-services () (interactive) (cl-loop for 𝑺 in my/services do (funcall (intern (format "w-start-%s" 𝑺))))) (defun w-stop-services () (interactive) (cl-loop for 𝑺 in my/services do (funcall (intern (format "w-stop-%s" 𝑺))))) #+end_src *** w-status-of-services :PROPERTIES: :CUSTOM_ID: w-status-of-services :END: #+begin_src emacs-lisp ;; It takes about ~3 seconds to build the Status of Services page, so let's jump to it if it's already built, and the user/me can request a refresh, if need be. (global-set-key (kbd "M-S-SPC") (lambda () (interactive) (-let [buf (get-buffer "Status of Services")] (if buf (switch-to-buffer buf) (w-status-of-services))))) ;; ;; Since M-S-SPC brings up the transient menu, and most commands close the status buffer or are transient, we get the perception that the transient menu is "sticky"; i.e., stuck to the buffer, even though this is not true. I do not yet know how to make a transient menu stuck to a buffer. ;; #+end_src #+begin_src emacs-lisp (defun w-status-of-services () "Show me status of all servers, including their current git branch, and most recent emitted output." (interactive) (defvar w-status-of-services/branch-name-width 12 "What is the length of the longest branch name? Let's use that to ensure there's enough whitespace.") (--> (-let [ shells (--filter (s-starts-with? "Shell" (process-name it)) (process-list)) ] (cl-loop for 𝑺 in (mapcar #'pp-to-string my/services) for associated-shell = (--find (s-contains? (format "%s" 𝑺) (cl-third (process-command it))) shells) for status = (or (ignore-errors (process-status associated-shell)) '💥) for branch = (-let [default-directory (format "~/%s" 𝑺)] (magit-get-current-branch)) for _ = (setq w-status-of-services/branch-name-width (max (length branch) w-status-of-services/branch-name-width)) for 𝑺-buffer = (--find (s-starts-with? (format "*Server:%s" 𝑺) it) (mapcar 'buffer-name (buffer-list))) for saying = (let (most-recent-shell-output (here (current-buffer))) (if (not 𝑺-buffer) " ─Server not started─ " (switch-to-buffer 𝑺-buffer) (end-of-buffer) (beginning-of-line) (setq most-recent-shell-output (or (thing-at-point 'line t) "")) (switch-to-buffer here) ;; FIXME:here (--> (s-truncate 135 (s-trim most-recent-shell-output)) (if (s-contains? "|" it) (cl-second (s-split "|" it)) it) (s-trim it) (if (<= (length it) 3) (s-repeat 70 " ") it)))) for _ = (if (or (s-contains? "Error" saying) (not 𝑺-buffer)) (setq status '💥)) for keymap = (copy-keymap org-mouse-map) do (cl-loop for (key action) on `( ;; Checkout branch/PR c (w-pr-checkout (format "~/%s" ,𝑺)) ;; Restart service, remaining on current branch [not switching to “main”!] r (-let [current-prefix-arg t] (funcall (intern (format "w-stop-%s" ,𝑺))) (funcall (intern (format "w-start-%s" ,𝑺)))) f (-let [default-directory (format "~/%s" ,𝑺)] (call-interactively #' projectile-find-file)) t (vterm-shell-command (format "clear; cd ~/%s; git status" ,𝑺) (format "vterm/%s" ,𝑺)) ;; See the repo in the web w (--> (format "%s" ,𝑺) (if (s-contains? "/" it) (f-parent it) it) (format "https://github.com/%s/%s" work/gh-user it) (browse-url it)) ;; Visit service shell (when ,𝑺-buffer (delete-other-windows) (split-window-below) (switch-to-buffer ,𝑺-buffer) (end-of-buffer) (other-window 1)) ;; See service magit buffer (progn (magit-status (format "~/%s" ,𝑺)) (delete-other-windows))) by #'cddr do (define-key keymap (kbd (format "%s" key)) `(lambda () (interactive) ,action))) collect ;; “%𝑾s” ⇒ Print a string with at least width 𝑾: If length(str) ≤ 𝑾, then pad with spaces on the left side. ;; Use “%-𝑾s” to instead pad with spaces to the right. (list keymap (format (format "%%s %%-20s %%-%ss %%s" (+ 5 w-status-of-services/branch-name-width)) status 𝑺 branch saying)))) ;; Setup buffer (-let [buf "Status of Services"] (ignore-errors (kill-buffer buf)) (switch-to-buffer buf) (delete-other-windows) it) ;; Insert out buttons (--each it ;; https://www.gnu.org/software/emacs/manual/html_node/elisp/Overlay-Properties.html (-let [help (s-join "\n" '("Keybindings:" "[C-u] c ∷ Checkout PR [or branch] \t\t\t b ∷ Browse an app" "tab ∷ See service magit buffer \t\t\t i ∷ Inject users" "return ∷ Visit service shell \t\t\t s ∷ SQL buffer" "r ∷ Restart service \t\t\t w ∷ See the repo in the web" "f ∷ Find file in project \t\t\t t ∷ Terminal" "g ∷ Refresh this view \t\t\t q ∷ Quit, and kill, this buffer"))] (insert-text-button (s-replace "\"" "″" (s-replace "run" "✅" (nth 1 it))) 'face nil ;; 'mouse-face '(:box t) ;; I use the cursor more than the mouse, so don't want two distinct views. 'keymap (nth 0 it) ;; NOTE: The functions are called only when the minor mode cursor-sensor-mode is turned on. ;; When cursor enters the button, we temporarily make it a box and show shortcuts in message area. 'cursor-sensor-functions `((lambda (_ old-pos entered?) (message ,help) (setq entered? (equal entered? 'entered)) (-let [self (button-at (if entered? (point) old-pos))] (read-only-mode 0) ;; Temporarily disable help-mode's read-only-mode setup. (if entered? (button-put self 'face '(:box "yellow" :weight bold)) (button-put self 'face nil))))) 'help-echo help) (insert "\n"))) ;; Do some highlighting, as a cautionary measure. (highlight-regexp ".*crashed.*" 'hi-red-b) ;; Forbid editing (help-mode) ;; This wont do the button face changes I like when cursor moves; so I disable read-only-mode temporarily when making the changes. (cursor-sensor-mode) (stripe-buffer-mode) (visual-line-mode -1) (toggle-truncate-lines) ;; Add some specific work related bindings (local-set-key "b" #'w-browse-app) (local-set-key "i" #'w-inject-users) (local-set-key "s" (lambda () (interactive) (w-sql) (delete-other-windows))) ;; Add general view keys (local-set-key "g" (lambda () "Refresh this view" (interactive) (ignore-errors (kill-buffer-and-window)) (w-status-of-services))) (local-set-key "q" (lambda () "Quit buffer" (interactive) (ignore-errors (kill-buffer-and-window)))) ;; Go to the first entry, so my “homemade echo menu” appears. (beginning-of-buffer))) #+end_src **** COMMENT Old definition with colourful menu :ignore: :PROPERTIES: :CUSTOM_ID: COMMENT-Old-definition-with-colourful-menu :END: #+begin_src emacs-lisp (defun w-status-of-services () "Show me status of all servers, including their current git branch, and most recent emitted output." (interactive) (thread-last (-let [ shells (--filter (s-starts-with? "Shell" (process-name it)) (process-list)) ] (cl-loop for 𝑺 in (mapcar #'pp-to-string my/services) for associated-shell = (--find (s-contains? (format "%s" 𝑺) (cl-third (process-command it))) shells) for status = (or (ignore-errors (process-status associated-shell)) '💥) for branch = (-let [default-directory (format "~/%s" 𝑺)] (magit-get-current-branch)) for saying = (let (most-recent-shell-output (here (current-buffer))) (switch-to-buffer (--find (s-starts-with-p 𝑺 (buffer-name it)) (buffer-list))) (end-of-buffer) (beginning-of-line) (setq most-recent-shell-output (or (thing-at-point 'line t) "")) (switch-to-buffer here) (s-truncate 135 (s-trim most-recent-shell-output))) collect ;; “%𝑾s” ⇒ Print a string with at least width 𝑾: If length(str) ≤ 𝑾, then pad with spaces on the left side. ;; Use “%-𝑾s” to instead pad with spaces to the right. (format "%s %-20s %-12s %s" status 𝑺 branch (-let [it (s-trim (if (s-contains? "|" saying) (cl-second (s-split "|" saying)) saying))] (if (<= (length it) 3) "-" it))))) (--map (format "%s" it)) (s-join "\n") (s-replace "run" "✅") (funcall (lambda (it) (-let [max-mini-window-height 0] (ignore-errors (kill-buffer "Status of Services")) (display-message-or-buffer it "Status of Services") (delete-other-windows) (switch-to-buffer "Status of Services") (highlight-regexp ".*crashed.*" 'hi-red-b) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Start describing the keys (end-of-buffer) (insert "\n \n \n \n \n") (let ((register-action (lambda (key-desc-actions) (if (stringp key-desc-actions) (propertize key-desc-actions 'font-lock-face '(face success)) (-let [ [key desc &rest actions] key-desc-actions ] ;; For buffer-local keys, you cannot use local-set-key, unless you want to modify the keymap of the entire major-mode in question: local-set-key is local to a major-mode, not to a buffer. ;; (use-local-map (copy-keymap text-mode-map)) (local-set-key key `(lambda () (interactive) ,@(seq-into actions 'list))) (format "%s %s" (propertize (format "%s" key) 'font-lock-face '(face error)) (propertize desc 'font-lock-face '(:foreground "grey"))))))) (repo-at-point (lambda () (-as-> (beginning-of-line) repo (thing-at-point 'line t) (s-split " " repo) cl-second)))) (insert (s-join "\n" (seq-mapn (lambda (&rest cols) (apply #'format (s-repeat (length cols) "%-40s ") (--map (funcall register-action it) cols))) ["This view" ["g" "Refresh this view" (ignore-errors (kill-buffer-and-window)) (w-status-of-services)] ["q" "Quit buffer" (ignore-errors (kill-buffer-and-window))] ["" "" ""]] `["Current service" ["c" "Checkout PR" (w-pr-checkout (format "~/%s" (funcall ,repo-at-point)))] [[return] "Visit service shell" (delete-other-windows) (split-window-below) (switch-to-buffer (--find (s-starts-with? (funcall ,repo-at-point) (buffer-name it)) (buffer-list))) (end-of-buffer) (other-window 1)] [[tab] "See service magit buffer" (magit-status (format "~/%s" (funcall ,repo-at-point))) (delete-other-windows)]] ["Misc" ["b" "Browse an app" (w-browse-app)] ["i" "Inject users" (w-inject-users)] ["s" "SQL buffer" (w-sql) (delete-other-windows)]])))) (beginning-of-buffer) ;; (help-mode) ;; For some reason, default fundamental-mode does not regoznise proprtised strings. ;; Also, this is read-only be default and emits a nice message for undefiend single key bindings. ;; (help-mode) ))))) #+end_src *** my/defservice :PROPERTIES: :CUSTOM_ID: my-defservice :END: #+begin_src emacs-lisp ;; Even though I'm doing frequent prunes, it helps to give docker some leeway. ;; NOTE: Docker Icon → Preferences → Resources → 4 CPUs; 8gb Memory; 2gb Swap; 120 DiskImageSize. (cl-defmacro my/defservice (repo &key (main-setup "git checkout main; git pull; git status; hr; npm ci; hr; time docker system prune -af") (cmd "npm run docker:dev") (example "")) "Example use: (my/defservice 𝒟 :cmd 𝒞 :example ℰ) ⇒ (w-start-𝒟) ≈ Unkillable shell: cd 𝒟; 𝒞 (w-is-up-𝒟?) ≈ Open browser at ℰ (w-stop-𝒟) ≈ Kill all emacs-buffers & docker-images containing 𝒟 in their name (w-[start|stop]-services) ⇒ Starts/stops all defined services." (add-to-list 'my/services repo) `(list (cl-defun ,(intern (format "w-start-%s" repo)) () "Start server off of ‘main’, with prefix just start server off of current branch." (interactive) (let ((command (format "cd ~/%s; pwd; hr; %s; hr; %s" (quote ,repo) (if current-prefix-arg "" ,main-setup) ,cmd)) (buf-name (format "*Server:%s/%s*" (quote ,repo) (if current-prefix-arg "main" (-let [default-directory ,(format "~/%s" repo)] (magit-get-current-branch)))))) (my/run-unkillable-shell (format "echo %s; hr; %s" (pp-to-string command) command) ;; Show command being run in output buffer, then run that command buf-name) (with-current-buffer buf-name (read-only-mode)))) (cl-defun ,(intern (format "w-stop-%s" repo)) () "Force-kill all unkillable buffers that mention REPO in their name. Also stop any docker services mentioning REPO in their name." (interactive) (my/docker-stop ,(pp-to-string repo)) (thread-last (buffer-list) (mapcar 'buffer-name) (--filter (s-contains-p ,(pp-to-string repo) it)) (mapcar #'my/force-kill))) (if ,example (cl-defun ,(intern (format "w-is-up-%s?" repo)) () (interactive) (browse-url ,example) (message "If the URL is busted, then the repo is not up correctly or the server has an error!"))))) #+end_src In my private ~work.el~ I have declarations of the form ~(my/defservice ⟨directory⟩ :cmd "npm run dev" :example "Example URL to try out for this server")~. #+begin_src emacs-lisp (when my/work-machine? (load-file "~/Desktop/work.el")) #+end_src ** Project management & navigation :PROPERTIES: :CUSTOM_ID: Project-management-navigation :END: Version controlled repositories are considered “projects” ---no setup needed---, but you can declare your own too. Videos: + ~5mins: https://youtu.be/bFS0V_4YfhY + ~1hr: https://www.youtube.com/watch?v=INTu30BHZGk This is so sweet at work (and possibly at home!): From anywhere, + [[kbd:C-x p p]] ⟨select your project⟩ RET ⟨start typing to see any file anywhere in the project⟩ + [[kbd:C-x p b ]] ⇒ Switch to buffers only in the current “stream of thought” (project). + [[kbd:C-x p f ]] ⇒ Find files only in the current “stream of thought” (project). + [[kbd:C-x p s g ]] ⇒ Search the project using grep; TAB in the resulting buffer to open files. + [[kbd:C-x p S ]] ⇒ Save all project buffers + [[kbd:C-x p k ]] ⇒ Kill all buffers relating to the parent project + [[kbd:C-x p & ]] ⇒ Runs an async-shell-command in the project's root directory + [[kbd:C-x p x s ]] ⇒ Start or visit a shell for the project + [[kbd:C-x p r ]] ⇒ Runs interactive query-replace on all files in the projects + [[kbd:C-x p e ]] ⇒ Show a list of recently visited files, in the current project + [[kbd:C-x p V ]] ⇒ Open a project that has been modified, but not pushed with version control. #+begin_src emacs-lisp ;; More info & key bindings: https://docs.projectile.mx/projectile/usage.html (use-package projectile :config (projectile-mode +1) (define-key projectile-mode-map (kbd "C-x p") 'projectile-command-map) ;; Replace usual find-file with a project-wide version :smile: (global-set-key (kbd "C-x f") #'projectile-find-file) ;; Makes indexing large projects much faster, after first time. ;; Since its caching, some files may be out of sync; you can delete the cache ;; with: C-u C-x f (setq projectile-enable-caching t)) (use-package projectile :config (define-key projectile-mode-map (kbd "C-x p s") ;; I prefer helm-do-grep-ag since it shows me a live search (lambda () (interactive) (let ((default-directory (car (projectile-get-project-directories (projectile-acquire-root))))) ;; (shell-command-to-string "echo $PWD") (helm-do-grep-ag nil))))) ;; “p”roject “s”earch #+end_src Let's get the file path of the current file. We bind it and make it a top-level invokable function. #+begin_src emacs-lisp (use-package projectile :defer nil :config (define-key projectile-mode-map (kbd "C-x p c") (defun my/copy-current-file-path () "Add current file path to kill ring." (interactive) (message (kill-new buffer-file-name))))) #+end_src ** TODO Projectile :PROPERTIES: :CUSTOM_ID: Projectile :END: #+begin_src emacs-lisp ;; https://cestlaz.github.io/posts/using-emacs-33-projectile-jump/ ;; https://github.com/bbatsov/projectile (use-package projectile :config (projectile-global-mode) (define-key projectile-mode-map (kbd "s-p") 'projectile-command-map)) #+end_src ** TODO COMMENT Are there any errors in my code? :PROPERTIES: :CUSTOM_ID: Are-there-any-errors-in-my-code :END: [[https://www.flycheck.org/en/latest/][Flycheck]] gives us syntax checking and linting tools to automatically check the contents of buffers while you type, and reports warnings and errors directly in the buffer, or in an optional error list. Sadly, the default reporting of errors looks like ~FlyC 3~ in the modeline, but with [[https://github.com/liblit/flycheck-status-emoji][flycheck-status-emoji]] we can see the status using cute & compact emoji such as ~😱3~. #+begin_src emacs-lisp (use-package flycheck-status-emoji :defer nil :config (load-library "flycheck-status-emoji") ;; (diminish-undo 'flycheck-mode) (flycheck-status-emoji-mode)) #+end_src Let's also have a nifty modal menu to quickly navigate between errors. #+begin_src emacs-lisp (use-package helm-flycheck :defer nil) (bind-key* "C-c !" (defhydra my/flycheck-hydra (:color blue :hint nil) "Move around flycheck errors and get info about them" ("n" flycheck-next-error "next" :column "Navigation") ("p" flycheck-previous-error "previous") ("f" flycheck-first-error "first") ("l" flycheck-list-errors "list") ("h" helm-flycheck "helm") ;; Jump to an error / see-errors from a nice interactive menu ("e" flycheck-explain-error-at-point "explain" :column "Current errror") ("c" flycheck-copy-errors-as-kill "copy") ("d" flycheck-describe-checker "Describe checker" :column "More") ("s" flycheck-select-checker "Select checker") ("S" flycheck-verify-setup "Suggest setup") ("m" flycheck-manual "manual"))) #+end_src *** On the fly syntax checking :PROPERTIES: :CUSTOM_ID: On-the-fly-syntax-checking :END: [[https://www.flycheck.org/en/latest/][Flycheck]] is a on-the-fly syntax checker that relies on external programs to check buffers; which must be installed separately. + E.g., ghc is required for Haskell; whereas Emacs Lisp is checked by Emacs' own byte compiler, ~emacs-lisp~. + Sometimes more than one checking tool applies, use ~C-c ! s~ to select a different checker. + =C-c ! n,p,l= takes you to the ‘n’ext or ‘p’revious error, or ‘l’ist all errors in another buffer. ~C-c ! c~ to explicitly recheck the buffer. #+begin_src emacs-lisp (use-package flycheck :defer nil :init (global-flycheck-mode) :config ;; There may be multiple tools; I have GHC not Stack, so let's avoid that. (setq-default flycheck-disabled-checkers '(haskell-stack-ghc emacs-lisp-checkdoc)) :custom (flycheck-display-errors-delay .3)) #+end_src In an org-src block, we press ~C-c '~ to get into the language's mode where flycheck will provide warnings. #+begin_src haskell :tangle no module Main where main :: IO () main = putStrLn $ "nice" ++ f 0 f :: Int -> String f x = x -- show x -- type error #+end_src In-general, flycheck is intended for self-contained raw code ---not for source blocks in Org-mode. Whence, the above example is a complete Haskell program, with a named module and ~main~ method. I think the built-in [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Flymake.html][flymake]] syntax checker is better for Emacs Lisp, so let's use that for ELisp. #+BEGIN_SRC emacs-lisp (use-package flymake :defer nil :hook ((emacs-lisp-mode . (lambda () (flycheck-mode -1))) (emacs-lisp-mode . flymake-mode)) :bind (:map flymake-mode-map ("C-c ! n" . flymake-goto-next-error) ("C-c ! p" . flymake-goto-prev-error))) #+END_SRC Try it out: #+begin_src emacs-lisp :tangle no (setq 1 2) ;; Error: ‘1’ is not a variable. #+end_src ** COMMENT Jumping to definitions & references :PROPERTIES: :CUSTOM_ID: Jumping-to-definitions-references :END: Out-of-the-box Emacs has ‘xref’ utilities ~M-.~ and ~C-u M-.~ to [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Xref.html#Xref][Find Identifier References]]; however, tags to source definitions need to be generated using the =etags= program. Nonetheless, the xref utilites are impressive and some just work: For example, ~M-?~ cleverly finds /all/ references for an identifier in ‘near by’ files; whereas ~C-u M-. RET my/.*~, for example, uses the given regular expression to list all identifiers with prefix ~my/~, thereby listing my personally defined names ^_^ | =C-M-. 𝓇𝓮ℊ𝓮𝓍= | Find all identifiers whose name matches the given pattern | Let's get [[https://github.com/jacktasia/dumb-jump][dumb-jump]], where the ‘dumb’ is possibly due to the fact that it works by brute-force regular-expression lookup of pre-defined ‘definitional template’ rules. It “just works” ^_^ #+BEGIN_SRC emacs-lisp (use-package dumb-jump :defer nil :bind (("M-g q" . dumb-jump-quick-look) ;; Show me in a tooltip. ("M-g ." . dumb-jump-go-other-window) ("M-g b" . dumb-jump-back) ("M-g p" . dumb-jump-go-prompt) ("M-g a" . xref-find-apropos)) ;; aka C-M-. :config ;; If source file is visible, just shift focus to it. (setq dumb-jump-use-visible-window t)) #+END_SRC In Lisp, for binding macros, it lists all possible mentions of the bound variable ---the first is likely what is desired. Alternatively, one could just add the necessary rule to the variable =dumb-jump-find-rules=. Otherwise, it works fine even for locally bound definitions. It works depending on the extension of a file. ** COMMENT Which function are we writing? :PROPERTIES: :CUSTOM_ID: Which-function-are-we-writing :END: In the modeline, show the name of the function we're currently writing. #+BEGIN_SRC emacs-lisp (add-hook 'prog-mode-hook #'which-function-mode) (add-hook 'org-mode-hook #'which-function-mode) #+END_SRC In Org-mode, this places the current heading in the modeline. In Lisp mode, ensure we always have matching parens. #+BEGIN_SRC emacs-lisp (add-hook 'emacs-lisp-mode-hook #'check-parens) #+END_SRC ** COMMENT Highlight defined Lisp symbols :PROPERTIES: :CUSTOM_ID: Highlight-defined-Lisp-symbols :END: Usually Emacs only highlights macro names, the [[https://github.com/Fanael/highlight-defined][following]] incantation makes it highlight all defined names ---as long as we're in Lisp mode, whence in org-src blocks we use ~C-c '~. #+BEGIN_SRC emacs-lisp ;; Emacs Lisp specific (use-package highlight-defined :defer nil :hook (emacs-lisp-mode . highlight-defined-mode)) #+END_SRC Super helpful in making my Emacs configuration: If a name is not highlighted, then I've misspelled it or it doesn't exist! :smile: ** TODO Aggressive Indentation :Broken_Subsection: :PROPERTIES: :CUSTOM_ID: Aggressive-Indentation :END: With a single space or tab, my code should always remain indented. [[https://github.com/Malabarba/aggressive-indent-mode][aggressive-indent-mode]] is a minor mode that keeps your code always indented. It reindents after every change, making it more reliable than electric-indent-mode. #+BEGIN_SRC emacs-lisp ;; Always stay indented: Automatically have blocks reindented after every change. (use-package aggressive-indent :defer nil :config (global-aggressive-indent-mode t)) ;; Use 4 spaces in places of tabs when indenting. (setq-default indent-tabs-mode nil) (setq-default tab-width 4) #+END_SRC This mode requires my Lisp forms to be well-balanced; e.g., having a missing parenthesis does not indent. For instance, I have a form ~F~ and I write ~(when F~ but have not yet enclosed the final parens, I still want nice indentation. So... *** TODO  [[https://github.com/jiahaowork/el-fly-indent-mode.el][Indent Emacs Lisp on the fly]] :Disabled:Breaks_C_c_C_v_C_t: :PROPERTIES: :CUSTOM_ID: https-github-com-jiahaowork-el-fly-indent-mode-el-Indent-Emacs-Lisp-on-the-fly :END: #+begin_src emacs-lisp :tangle no ;; This minor mode toggles on along with elisp-mode and indents on the fly when ;; you edit the code. No special key strokes needed. ;; Demo: https://www.youtube.com/watch?v=zrFmfFZfj-A&ab_channel=JiahaoLi (use-package el-fly-indent-mode :defer nil :config (add-hook 'emacs-lisp-mode-hook #'el-fly-indent-mode)) #+end_src ** COMMENT Being Generous with Whitespace :PROPERTIES: :CUSTOM_ID: Being-Generous-with-Whitespace :END: The following minor mode automatically adds spacing around operators. #+begin_src emacs-lisp (use-package electric-operator :defer nil :hook (c-mode . electric-operator-mode)) #+end_src I dislike it when users write ~x=y+1~ ---whitespace is free and helpful. ⟨ Also, languages with arbitrary identifiers, like Lisp and Agda, would accept ~x=y+1~ as an identifier, not an expression! ⟩ ** Coding with a Fruit Salad: Semantic Highlighting :PROPERTIES: :CUSTOM_ID: Coding-with-a-Fruit-Salad-Semantic-Highlighting :END: What should be highlighted when we write code? Static keywords with fixed uses, or dynamic user-defined names? + /Syntax/ highlighting ⇨ Specific words are highlighted in strong colours so that the /structure/ can be easily gleaned. - Generally this only includes a language's keywords, such as ~if, loop, begin, end, cond~. - User defined names generally share one colour; usually black. - Hence, an ~if~ block may be seen as one coloured keyword followed by a blob of black text. /Obvious keywords are highlighted while the rest remains in black!/ + /Semantic/ highlighting ⇨ Identifiers obtain unique colouring. - This makes it much easier to visually spot dependencies with a quick glance. + One can *see* how data flows through a function. - In dynamic languages, this is a visual form of typing: Different colours are for different names. + Especially helpful for (library) names that are almost the same. + This can be accomplished anywhere in Emacs by pressing ~M-s h .~ on a selected phrase. For Emacs, [[https://github.com/ankurdave/color-identifiers-mode][Color Identifiers Mode]] gives unique highlighting to identifiers. - It comes with support for a bunch of languages, and one can add support for others. - It picks colours adaptively to fit the theme; one uses ~M-x color-identifiers:regenerate-colors~ after a theme change. #+begin_src emacs-lisp (use-package color-identifiers-mode :config (global-color-identifiers-mode)) ;; Sometimes just invoke: M-x color-identifiers:refresh #+END_SRC When writing a new name, after about ~5 seconds it obtains a colour which is then propagated immediately to any new occurrences. This timeout before recolouring is to avoid any lag from multithreading and can be changed by altering the following line (#64) in the source file, changing the ~5~ to a smaller number. #+BEGIN_SRC emacs-lisp :tangle no (run-with-idle-timer 5 t 'color-identifiers:refresh) #+END_SRC Here are further reads: - [[https://medium.com/@evnbr/coding-in-color-3a6db2743a1e][Coding in color: How to make syntax highlighting more useful]] ---an excellent, terse, read - [[https://zwabel.wordpress.com/2009/01/08/c-ide-evolution-from-syntax-highlighting-to-semantic-highlighting/][C++ IDE Evolution: From Syntax Highlighting to Semantic Highlighting]] + Names with a similar prefix share a colour, and class-local items share a colour. - [[https://wordsandbuttons.online/lexical_differential_highlighting_instead_of_syntax_highlighting.html][Lexical differential highlighting instead of syntax highlighting]] + /Ideally, the smaller the lexical difference, the greater the color difference should be./ - [[https://github.com/jacksonrayhamilton/context-coloring][Colouring by Context]] ---an Emacs package - [[http://www.linusakesson.net/programming/syntaxhighlighting/][A case against syntax highlighting]] ** TODO Text Folding ---Selectively displaying portions of a program :causes_HIDING_ALL_BLOCKS_issue: :PROPERTIES: :CUSTOM_ID: Text-Folding :END: ⟦‼ TODO: hideshowvis shows ="*hideshowvis*"= literally in src code blocks upon export; as such we need to hook to exports so as to temporarily disable it. ⟧ Literate programming within Org-mode is not always ideal or possible, so we use a programming mode directly and then may want to have arbitrary ‘sections’ of text folded up. Let's describe how to accomplish this goal. I've tried the feature-full folding modes [[https://github.com/gregsexton/origami.el][Origami-mode]] and ~yafolding-mode~, but it seems the /built-in/ TODO. doc.hs-minor-mode is the best: (0) It folds comments, (1) it's the fastest, and (2) it Just Worksᵀᴹ out-of-the-box. Coupled with Vimish folding, for arbitrary region folding; and a helpful hydra, I'm rather happy with my setup. On two JavaScript files, I found the following timings (using TODO. doc.elp-instrument-function and TODO. doc.elp-results): | Folding function | ~500 lines | ~2300 lines | |-------------------------+------------+-------------| | hs-hide-all | 0.00277 | 0.004358 | | origami-close-all-nodes | 0.00304 | 0.025569 | | yafolding-hide-all | 7.051795 | 74.252598 | (TODO: Maybe these timings are due to my config, I should try to reproduce these with ~emacs -Q~.) #+begin_box "There's a built-in, less featureful, alternative based on indentation!" Quickly fold-away code with TODO. doc.set-selective-display; e.g., folds away all code indented 4 spaces, whereas is for 8 spaces, and disables the folding. This is built into Emacs. I've incorporated this into my folding hydra, below. #+end_box #+begin_box "Vimish fold - Fold regions based on selection, not syntax" Sometimes we want to fold some random piece of text ---rather than fold accoding to a programming language's syntactic constructions. [[https://github.com/mrkkrp/vimish-fold][Vimish-fold]] gives us just that and with many fancy features, from the README: + folding of active regions; + good visual feedback: it's obvious which part of text is folded with a ‘⋮’ in the fringe. + create folds from regions between ~{{{~ and ~}}}~ automatically (marks are customizable; TODO. doc.vimish-fold-from-marks); + persistence by default: when you kill a buffer your folds don't disappear; + persistence scales well, you can work on hundreds of files with lots of folds without adverse effects; + it does not break indentation; + folds can be toggled from folded state to unfolded and back very easily; + quick navigation between existing folds; + you can use mouse to unfold folds (good for beginners and not only for them). #+begin_src emacs-lisp (use-package vimish-fold :config (vimish-fold-global-mode 1)) #+end_src #+end_box *** TODO Actual Setup :ignore: :PROPERTIES: :CUSTOM_ID: Actual-Setup :END: -------------------------------------------------------------------------------- TODO: Below I setup hideshow along with [doc : hideshowvis-symbols] [hideshowvis] which gives us a nice clickable “+/-” marker for foldable blocks as well as an overlay that indicates how many lines are folded away. For more on hideshow-mode, see the [[https://writequit.org/denver-emacs/presentations/2017-11-02-hidden-gems.html][Hidden Gem Packages]] presentation which contains a small self-contained introduction, or [[https://github.com/emacs-mirror/emacs/blob/master/lisp/progmodes/hideshow.el#L127][these super useful source comments]] which discuss hooks and support for special modes ([[https://superuser.com/questions/576447/enable-hideshow-for-more-modes-e-g-ruby][like this one]]). #+BEGIN_SRC emacs-lisp (use-package hideshow :defer nil :init ;; https://github.com/emacsmirror/emacswiki.org/blob/master/hideshowvis.el (quelpa '(hideshowvis :fetcher wiki)) ;; Press “C-c TAB” to toggle a block's visibility or “C-c f” for my folding hydra. :bind (("C-c TAB" . hs-toggle-hiding)) ;; https://github.com/shanecelis/hideshow-org/tree/master ;; This extension bring Org-mode tab behaviour to folding, at the block level ;; and buffer level ---but not cycling visibility. ;; (use-package hideshow-org) ;; Disabled as commented below. :hook ((prog-mode . (lambda () (hs-minor-mode +1) (hideshowvis-minor-mode t) (hideshowvis-symbols) ;; This hook along with hs-org mode breaks editing of src blocks in Org files. ;; That's OK, since my folding hydra does a better job for my needs. ;; (hs-org/minor-mode t) (hs-hide-all))))) #+END_SRC Along with a hydra for super quick navigation and easily folding, unfolding blocks! Love this one (•̀ᴗ•́)و #+BEGIN_SRC emacs-lisp (my/defhydra "C-c f" "Folding text" archive :Current ("h" hs-hide-block "Hide") ("s" hs-show-block "Show") ("t" hs-toggle-hiding "Toggle") ;; "l" hs-hide-level "Hide blocks n levels below this block"; TODO: Enable folding feature :Buffer ("H" hs-hide-all "Hide") ("S" hs-show-all "Show") ("T" my/hs-toggle-buffer "Toggle") :Style ("i" my/clever-selective-display "Fold along current indentation" :toggle selective-display) ("e" auto-set-selective-display-mode "Explore; walk and see" :toggle t) :Region ("f" (lambda () (interactive) (vimish-fold-toggle) (vimish-fold (region-beginning) (region-end))) "Fold/Toggle") ("d" vimish-fold-delete "Delete fold") ("U" vimish-fold-unfold-all "Unfold all") ("D" vimish-fold-delete-all "Delete all") ("n" vimish-fold-next-fold "Next fold") ("p" vimish-fold-previous-fold "Previous fold") :... ("w" hl-todo-occur "Show WIPs/TODOs" :exit t) ("m" lsp-ui-imenu "Menu of TLIs" :exit t) ;; TLI ≈ Top Level Items ;; ("i" imenu-list "iMenu (General)") ;; It seems the above is enough for both prog and otherwise. ("r" (progn (hs-minor-mode -1) (hs-minor-mode +1)) "Reset Hideshow") ;; Remove all folds from the buffer and reset all hideshow-mode. Useful if it messes up! ("q" nil "Quit" :color blue)) ;; Features from origami/yafolding that maybe I'd like to implement include: ;; narrowing to block or folding everything except block, navigating back and forth between folded blocks. ;; Finally, if we want to cycle the visibility of a block (as in Org-mode), we can use a combination of hs-show-block and hs-hide-level. #+END_SRC #+begin_details "Folding Hydra Helpers" #+BEGIN_SRC emacs-lisp (defvar my/hs-hide nil "Current state of hideshow for toggling all.") (defun my/hs-toggle-buffer () "Toggle hideshow all." (interactive) (setq my/hs-hide (not my/hs-hide)) (if my/hs-hide (hs-hide-all) (hs-show-all))) #+END_SRC #+BEGIN_SRC emacs-lisp (defun my/clever-selective-display (&optional level) "Fold text indented same of more than the cursor. This function toggles folding according to the level of indentation at point. It's convenient not having to specify a number nor move point to the desired column. " (interactive "P") (if (eq selective-display (1+ (current-column))) (set-selective-display 0) (set-selective-display (or level (1+ (current-column)))))) #+END_SRC #+BEGIN_SRC emacs-lisp ;; Src: https://emacs.stackexchange.com/questions/52588/dynamically-hide-lines-indented-more-than-current-line (define-minor-mode auto-set-selective-display-mode "Automatically apply `set-selective-display' at all times based on current indentation." nil "$" nil (if auto-set-selective-display-mode (add-hook 'post-command-hook #'auto-set-selective-display nil t) (remove-hook 'post-command-hook #'auto-set-selective-display t) (with-temp-message "" (set-selective-display nil)))) ;; (defun auto-set-selective-display () "Apply `set-selective-display' such that current and next line are visible. Scroll events are excluded in order to prevent wild flickering while navigating." (unless (eq last-command #'mwheel-scroll) (let*((this-line-indent (current-indentation)) (next-line-indent (save-excursion (forward-line) (current-indentation)))) (with-temp-message "" ; Suppress messages. (set-selective-display (1+ (max this-line-indent next-line-indent))))))) #+END_SRC #+end_details With expected support for searching. #+BEGIN_SRC emacs-lisp ;; Open folded nodes if a search stops there. (add-hook 'helm-swoop-after-goto-line-action-hook #'my/search-hook-function) (defun my/search-hook-function () (when hs-minor-mode (set-mark-command nil) (hs-show-block) (pop-to-mark-command))) #+END_SRC #+begin_details "What if I want my own hideshow overlays, instead of hideshowvis?" #+BEGIN_SRC emacs-lisp :tangle no ;; Add an overlay to display the number of hidden lines 😁;; https://github.com/shanecelis/hideshow-org/tree/master (setq hs-set-up-overlay (defun my-display-code-line-counts (ov) (when (eq 'code (overlay-get ov 'hs)) (overlay-put ov 'display (propertize (format " ... ﴾%d﴿" (count-lines (overlay-start ov) (overlay-end ov))) 'face 'font-lock-type-face))))) #+END_SRC #+end_details #+begin_details "[Disabled / Alternative] origami" #+BEGIN_SRC emacs-lisp :tangle no (use-package origami ;; In Lisp languages, by default only function definitions are folded. ;; :hook ((agda2-mode lisp-mode c-mode) . origami-mode) ;; Please open any code with top level items folded away. :hook (prog-mode . (lambda () (interactive) (origami-close-all-nodes (current-buffer)))) ;; MA: It seems that this is not ideal; it takes a bit longer than I'd like to fold the whole file. :config ;; For any major-mode that doesn't have explicit support, origami will use the ;; indentation of the buffer to determine folds. (global-origami-mode) ;; With basic support for one of my languages. (push '(agda2-mode . (origami-markers-parser "{-" "-}")) origami-parser-alist)) #+END_SRC With expected support for searching. #+BEGIN_SRC emacs-lisp :tangle no (defun my/search-hook-function () (when origami-mode (origami-open-node-recursively (current-buffer) (point)))) ;; Open folded nodes if a search stops there. (add-hook 'helm-swoop-after-goto-line-action-hook #'my/search-hook-function) ;; ;; Likewise for incremental search, isearch, users. ;; (add-hook 'isearch-mode-end-hook #'my/search-hook-function) #+END_SRC And hydra... #+BEGIN_SRC emacs-lisp :tangle no (my/defhydra "C-c f" "Folding text" archive :Current ("h" origami-close-node-recursively "Hide") ("s" origami-open-node-recursively "Show") ("c" origami-recursively-toggle-node "Cycle") ;; Acts like org-mode header collapsing. Cycle a fold between open, recursively open, closed. ("f" origami-show-only-node "Focus") ;; Close everything but the folds necessary to see the point. Very useful for concentrating on an area of code. ;; ("H" origami-close-all-nodes "Hide All") ;; ("S" origami-open-all-nodes "Show All") :Navigate ("n" origami-next-fold "Next") ("p" origami-previous-fold "Previous") :... ("t" origami-toggle-all-nodes "Toggle buffer") ("m" lsp-ui-imenu "Menu of TLIs" :exit t) ;; TLI ≈ Top Level Items ;; ("i" imenu-list "iMenu (General)") ;; It seems the above is enough for both prog and otherwise. ("r" origami-reset)) ;; Remove all folds from the buffer and reset all origami state. Useful if origami messes up! #+END_SRC # *Disabled:* I've looked at a few folding modes, and I like this one. # However, I seldom need it. #+end_details #+begin_details "[Disabled / Alternative] yafolding" I found using yafolding-mode to be way too slow. #+begin_src emacs-lisp :tangle no ;; “Yet Another Folding” just works: Indented elements are folded away; no setup required. ;; (use-package discover) (use-package yafolding ;; Please open any code with top level items folded away: ;; Open the file super quick, but when I'm inactive for 5 seconds, ;; then I'm probably doing other stuff so do the folding then. ;; :hook prog-mode . (lambda () (interactive) ;; (run-with-idle-timer 5 nil #'yafolding-hide-all)) ;; MA: It seems that this is not ideal; it takes a bit longer than I'd like to fold the whole file. ) (defhydra yafolding-hydra (:color pink :columns 3) "Fold code based on indentation levels" ;; First row ("s" yafolding-show-element "show element") ("S" yafolding-show-all "show all") ("" (lambda () (interactive) (let ((next (car (yafolding-get-overlays (point) (point-max)))) pos) (if (not next) (message "No more folded regions") (setq pos (overlay-start next)) (yafolding-hide-element) (goto-char pos) (yafolding-show-element)))) "forward element") ;; Second row ("h" yafolding-hide-element "hide element") ("H" yafolding-hide-all "hide all") ("" (lambda () (interactive) (let ((previous (car (yafolding-get-overlays (point-min) (point)))) pos) (if (not previous) (message "No more folded regions") (setq pos (overlay-start previous)) (yafolding-hide-element) (goto-char pos) (yafolding-show-element)))) "backward element") ;; Third row ("SPC" yafolding-toggle-element "toggle element") ("T" yafolding-toggle-all "toggle all") ("q" nil "quit" :color red) ("p" yafolding-hide-parent-element "hide parent") ("" (lambda () (interactive) (or (ignore-errors (yafolding-go-parent-element)) (message "Already at the top level."))) "go to parent") ("r" (lambda () (interactive) (yafolding-hide-region (region-beginning) (region-end))) "hide region" )) (global-set-key (kbd "s-f") 'yafolding-hydra/body) #+end_src #+end_details *** E2E Test :ignore: :PROPERTIES: :CUSTOM_ID: E2E-Test :END: #+begin_details Test #+begin_src emacs-lisp :tangle init-test.el (ert-deftest hideshow-is-enabled-and-folds-by-default () :tags '(hideshow) ;; Make a temporary scratch.js file with the given contents. (let* ((contents "function fierce(name) { \n return `${name}: ROAR` \n }") (scratch.js (make-temp-file "scratch" nil ".js" contents))) ;; Hideshow is enabled whenever we open a code file (find-file scratch.js) (should hs-minor-mode) ;; Function definition is a hidden block (end-of-line) (backward-char 2) (should (hs-already-hidden-p)) ;; Indeed, the hidden block starts at the first line break and ends just after the second. (-let [ov (hs-already-hidden-p)] (-let [(first\n second\n) (-elem-indices "\n" (s-split "" contents))] (should (equal (overlay-start ov) first\n)) ;; ≈ 25 (should (equal (overlay-end ov) (+ second\n 2))))) ;; ≈ 52 (kill-buffer))) #+end_src #+end_details ** COMMENT Jump between windows using Cmd+Arrow & between recent buffers with Meta-Tab :relocate: :PROPERTIES: :CUSTOM_ID: Jump-between-windows-using-Cmd-Arrow-between-recent-buffers-with-Meta-Tab :END: We can use ~C-x o~ to switch to the ‘o’ther window, and ~C-u 𝓃 C-x o~ to switch to the 𝓃-th next clockwise window, but using ~s-↑,↓,←,→~ may be faster. #+BEGIN_SRC emacs-lisp (use-package windmove :defer nil :config ;; use command key on Mac (windmove-default-keybindings 'super) ;; wrap around at edges (setq windmove-wrap-around t)) #+END_SRC The [[https://github.com/killdash9/buffer-flip.el][docs]], for the following, have usage examples. #+BEGIN_SRC emacs-lisp (use-package buffer-flip :defer nil :bind (:map buffer-flip-map ("M-" . buffer-flip-forward) ("M-S-" . buffer-flip-backward) ("C-g" . buffer-flip-abort)) :config (setq buffer-flip-skip-patterns '("^\\*helm\\b"))) ;; key to begin cycling buffers. (global-set-key (kbd "M-") 'buffer-flip) #+END_SRC See [[https://www.emacswiki.org/emacs/buffer-move.el][buffer-move]] if you're interested in moving the buffers, and their windows, into new configurations. ** hr: [[https://github.com/LuRsT/hr][A horizontal for your terminal]] :relocate: :PROPERTIES: :CUSTOM_ID: hr-https-github-com-LuRsT-hr-A-horizontal-for-your-terminal :END: When working in the terminal, at least in my day job, it can be helpful to visually segregate large chunks of output. With the ~hr~ command, I can run ~hr 'output5`~, for example, to have the same the string /output5/ repeated horizontal across one line of my terminal. We can also just run ~hr~ which is the same as ~hr '#'~. Finally, you can also run =hr '-#-' '-' '-#-'= to have 3 horizontal lines and more generally =hr p₁ p₂ … pₙ= will produce /n/-many horizontal lines with the $i^{th}$-line having pattern =pᵢ=. #+begin_src emacs-lisp (system-packages-ensure "hr") ;; ≈ brew install hr #+end_src ** COMMENT Browse remote files :PROPERTIES: :CUSTOM_ID: Browse-remote-files :END: Sometimes you want to see the current file in Github; e.g., you select a region and press ~M-x browse-at-remote-kill~ to get the URL for that region in Github then you can send that URL to your peers when referencing something. - Or just ~M-x browse-at-remote~ to see it in Github. - [[https://github.com/rmuslimov/browse-at-remote][browse-at-remote: Browse target page on github/bitbucket from emacs buffers]]. #+begin_src emacs-lisp ;; Usage: [Optionally select a region then] M-x browse-at-remote[-kill] (use-package browse-at-remote :defer nil) #+end_src ** COMMENT [[https://github.com/sshaw/copy-as-format][copy-as-format:]] Emacs function to copy buffer locations as GitHub/Slack/JIRA etc... formatted code. :PROPERTIES: :CUSTOM_ID: https-github-com-sshaw-copy-as-format-copy-as-format-Emacs-function-to-copy-buffer-locations-as-GitHub-Slack-JIRA-etc-formatted-code :END: #+begin_src emacs-lisp ;; Usage: [C-u] M-x copy-as-format ⇒ Copies selected region, or current line. ;; Also use: copy-as-format-𝒮, to format to a particular 𝒮tyle. ;; Without suffix 𝒮, format defaults to `copy-as-format-default`. ;; With a prefix argument prompt for the format style 𝒮. ;; Easy to add more formats. (use-package copy-as-format :defer nil) #+end_src ** w-screencapture :PROPERTIES: :CUSTOM_ID: w-screencapture :END: #+begin_src emacs-lisp (bind-key "C-c s" (cl-defun w-screencapture () "Interactively capture screen and save to clipboard; then paste in Slack, etc, with ⌘-c. After we run this command, we can swipe up on mousepad to select different desktops, then click & drag to select portition of screen to capture. Captured screen is NOT saved to disk, only copied to clipboard. In MacOs, + Command + Shift + 5 ⇒ Select screen record + Command + Shift + 4 ⇒ Selection Screenshot + Command + Shift + 3 ⇒ Screenshot See: https://osxdaily.com/2011/08/11/take-screen-shots-terminal-mac-os-x" (interactive) (async-shell-command "screencapture -i -c"))) (cl-defun w-delete-all-screenshots () "Delete all “Screen Shot ⋯” files in ~/Desktop." (interactive) (thread-last (shell-command-to-string "cd ~/Desktop; ls") (s-split "\n") (--filter (s-starts-with-p "Screen Shot" it)) (--map (f-delete (format "~/Desktop/%s" it))))) #+end_src ** COMMENT Screencapturing the Current Emacs Frame :PROPERTIES: :CUSTOM_ID: Screencapturing-the-Current-Emacs-Frame :END: 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! ** Comment-boxes up to the fill-column ---or banner instead? :PROPERTIES: :CUSTOM_ID: Comment-boxes-up-to-the-fill-column :END: GIF: https://endlessparentheses.com/images/comment-box.gif #+begin_src emacs-lisp (defun my/comment-box (b e) "Draw a box comment around the region but arrange for the region to extend to at least the fill column. Place the point after the comment box. Source: http://irreal.org/blog/?p=374 To do fancy stuff like removing boxes, centering them, etc see https://github.com/lewang/rebox2/blob/master/rebox2.el" (interactive "r") (let ((e (copy-marker e t))) (goto-char b) (end-of-line) (insert-char ? (- fill-column (current-column))) (comment-box b e 1) (goto-char e) (set-marker e nil))) #+end_src A comment box sometimes increases the size of a file more than I'd like, or more than others on my dev team would like. As such, let's try [[https://github.com/WJCFerguson/banner-comment][banner comments]]: - GIF: https://github.com/WJCFerguson/banner-comment/blob/35d3315683d3f97605207691b77e9f447af18fe2/demo.png #+begin_src emacs-lisp (use-package banner-comment :defer nil) #+end_src Pretty slick! ** TODO COMMENT Auto-format on Save :Reenabled:Should_be_Disabled:Breaks_HTML_export_of_org_files_with_unicode_or_emojis: :PROPERTIES: :CUSTOM_ID: Auto-format-on-Save :END: [[https://github.com/lassik/emacs-format-all-the-code/tree/eb2a7fa6da15d23b57921218a36ac67d523e81f1][Auto-format source code in many languages with one command]] Let's auto-format source code in many languages using the same command for all languages, instead of learning a different Emacs package and formatting command for each language. Just do ~M-x format-all-buffer~ and it will try its best to do the right thing. To auto-format code on save, use the minor mode format-all-mode. Better yet, we ask for it to do so “on save”. - You will need to install external programs to do the formatting. If ~format-all-buffer~ can't find the right program, it will try to tell you how to install it. - An alternative Emacs tool is [[https://github.com/radian-software/apheleia][apheleia: 🌷 Run code formatter on buffer contents without moving point, using RCS patches and dynamic programming.]] #+begin_src emacs-lisp (use-package format-all :defer nil ;; To enable format on save for most programming language buffers: :hook (prog-mode . format-all-mode) :config ;; Please use the default formatters; I don't care too much. (add-hook 'format-all-mode-hook 'format-all-ensure-formatter)) #+end_src [[https://prettier.io/docs/en/index.html][Prettier]] is an opinionated code formatter for numerous web related languages, including JS, TS, HTML, CSS, JSON, Vue, Markdown, and YAML. #+begin_src emacs-lisp ;; For JavaScript prettification: It automatically inserts semicolons, forces newlines, inserts parens, etc. ;; Lots of redundant stuff, but stuff to make it easy to work with others. (shell-command "npm install --global prettier") ;; Specific package to do only JS prettification: https://github.com/prettier/prettier-emacs #+end_src For example, in a NodeJS app make a file ~.prettierrc.json~ whose contents are #+begin_src json :tangle no { "singleQuote": true, "arrowParens": "avoid", "printWidth": 120, "semi": false, "trailingComma": "none" } #+end_src Unfortunately, my current team prefers ~"semi": true~ ---which is understandable for people not familar with how JavaScript does [[https://www.freecodecamp.org/news/lets-talk-about-semicolons-in-javascript-f1fe08ab4e53/][semicolon insertion]]. ( If you're having trouble getting prettier to work on save, consider using this [[https://github.com/codesuki/add-node-modules-path][package]] which detects your node modules' path. ) Anyhow, with these rules: - I write =const f = (x) => "x" == x= then on save it becomes =const f = x => 'x' == x;= - I write =let xs = [1, 2, 3,]= then on save it becomes =let xs = [1, 2, 3];= - I write =foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis(), isThereSeriouslyAnotherOne());= then nothing happens on save; but if you add one more argument, =, 12=, then it gets reformatted with each arg on one line. Notice that in the first example above, only minor syntactic changes were made; nothing altering semantics or quality of code ---for that, we use a /linter/. In particular, [[https://emacs-lsp.github.io/lsp-mode/page/lsp-eslint/][LSP ESLint]] when activated will show us two errors: It's almost always better to use ~===~ instead of ~==~ and literals should be on the right side of the equality. We can use ~M-x lsp-execute-code-action~ to get a list of actions that can be performed to fix this /quality/ problem. ** Searching Hydra :PROPERTIES: :CUSTOM_ID: Searching-Hydra :END: #+begin_src emacs-lisp (my/defhydra "s-f" "\t\tLocate Everything" search :Buffer ;; find all the occurrences of a string, pull out the lines containing the string to another buffer where [F2] I can edit and save, ("e" helm-swoop "Editable") ;; Implicit Regex, colourful ("c" swiper "Classic") :Project ;; “:toggle ℰ”: ℰ is a Boolean expression that is evaluated to tell us whether the state is on-or-off ("t" (lambda () (interactive)) "Ignore specs/jsons" :toggle (let* ((with-hole "ag %s --line-numbers -S --color --nogroup %%s %%s %%s") ;; ≈ original value of ‘helm-grep-ag-command’ (ignores "--ignore=\"*spec.js\" --ignore=\"*.json\" --ignore=\"*.json5\"") (on (equal helm-grep-ag-command (format with-hole ignores)))) (if on (progn (setq helm-grep-ag-command (format with-hole "")) nil) ;; ≈ turn off the toggle (setq helm-grep-ag-command (format with-hole ignores))))) ("f" (lambda () (interactive) (helm-do-grep-ag t)) "File type") ("d" (lambda () (interactive) (-let [default-directory (read-directory-name "Where do you want to search? ")] (helm-do-grep-ag nil))) "Directory") ("D" (lambda () (interactive) (-let [default-directory (read-directory-name "Where do you want to search? ")] (helm-do-grep-ag t))) "Directory & type")) #+end_src ** TODO ⌘-e: Edit Everything in a separate buffer :PROPERTIES: :CUSTOM_ID: e-Edit-Everything-in-a-separate-buffer :END: Edit comment/string/docstring/code block in separate buffer with your favourite mode. + ⌘-e to toggle “e”diting of thing at point. + ⌘-e will try to edit thing at point, or selection, if possible; otherwise it will exit edit session. - 🔥 Avoid escape nightmares by editing strings in a separate buffer. - On a string, press ⌘-e and a new buffer pops up with unescaped content, letting you edit raw strings directly. It then takes care of automatically escaping strings for you when you press ⌘-e, or ~C-c C-c~ ---or discard your changes with ~C-c C-k~. + ⌘-e can be used within edit session to create new edit sessions, if possible; then ⌘-e will “pop-off-the-stack” as expected. - (I already use ⌘-e to toggle editing Org src blocks, and when editing a block ⌘-e will try to do a recursive edit if possible, or exit the edit session.) - (The separedit package also let's me press ⌘-e to edit variable values when describing them, and to edit text when in the minibuffer.) From [[https://github.com/twlz0ne/separedit.el][source]]: #+begin_example org +----------+ Edit +-----------+ Edit +-----------+ | | ---------------------> | edit | ---------------------> | edit | ... | | point-at-comment? | buffer | point-at-comment? | buffer | | source | point-at-string? | | point-at-string? | | ... | buffer | point-at-codeblock? | (markdown | point-at-codeblock? | (markdown | ... | | point-at-...? | orgmode | point-at-...? | orgmode | | | <--------------------- | ...) | <--------------------- | ...) | ... +----------+ Commit changes +-----------+ Commit changes +-----------+ #+end_example [In-general, we can invoke [[https://github.com/Fanael/edit-indirect/blob/e3d86416bcf8ddca951d7d112e57ad30c5f9a081/edit-indirect.el#L124][M-x edit-indirect-region]] to edit any selected piece of text in its own buffer, then ~C-c C-c~ (or ~⌘-e~ with my setup) to commit the edit. Actually, I've altered by ~⌘-e~ setup to also account for a selected region 😉] #+begin_src emacs-lisp (use-package separedit :defer nil) ;; ;; # Example Usage ;; ;; 1. Press ⌘-e on this line, to edit this entire comment. ;; 2. Press ⌘-e to exit the edit session. ;; ;; Since my ⌘-e is context sensitive, to determine whether to continue editing or ;; exit; you can explicitly request an edit with C-c ' and an exit with C-c C-c. ;; ;; ``` ;; ;; 3. Press ⌘-e on this line, to edit this source block! ;; ;; 4. Press ⌘-e on this line, to edit this inner-most comment! ;; ;; 5. At start of next line, press “⌘-r ⌘-e” to edit just the source block ;; ;; ;; (cl-defun index (&rest args) ;; "6. Press ⌘-e to edit this string, \"7. and again in these quotes\"" ;; "

8. Press ⌘-e to edit this HTML block, in Web-mode

") ;; ;; ;; 9. Press C-u ⌘-e to guess the language of the next string (Rust); then ⌘-r C-c C-r to quickly run the code. ;; "fn main() { println!(\"{}\", \"hello!\"); }" ;; ;; ;; 10. Select & press “C-u ⌘-e” on the following, to edit it in whatever mode you want. ;; ;; select * from table -- Or just press ⌘-e and have the mode detected. ;; ;; ``` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Setup to make the above ⌘-e behaviour happen. ;; Make “⌘-e” toggle editing string literals / select region / [Org/markdown] code block / comment block when programming. (--map (bind-key "s-e" #'separedit it) '(prog-mode-map minibuffer-local-map help-mode-map)) ;; TODO: helpful-mode-map #+end_src #+begin_src emacs-lisp ;; TODO:Merge these changes upstream ;; I'm focusing on a specific region to edit, so let's not be distracted by anything else. ;; This makes the “editing stack” feel like a stack, with ⌘-e pushing new editing session buffers, ;; and C-c C-c, or ⌘-e on non-editable lines, to pop-off the stack. ;; (advice-add #'separedit :after (lambda (&rest _) (delete-other-windows))) ;; ;; NOTE: This actually breaks the stack nature of popping with ⌘-e; we need to actually save the stack via some list of buffers than push/pop buffers on that variable. ;; I don't want to be bothered for what mode I'm in, when a region is selected using current major mode. ;; I'll use a prefix, “C-u ⌘-e”, if I want to select a mode for my current selected text. (advice-add #'separedit--select-mode :before-until (lambda (&rest _) (when (and (not current-prefix-arg) (region-active-p)) (pp-to-string major-mode)))) ;; Also: When on a string ∷ (advice-add #'separedit--select-mode :before-until (lambda (&rest _) "When on a string ∷ + ⌘-e ⇒ Edit string at point + C-u ⌘-e ⇒ Auto-detect my string's major mode + C-u C-u ⌘-e ⇒ Let me select a major mode" (-let [str? (ignore-errors (thing-at-point 'string))] (case (car current-prefix-arg) (4 (when str? (pp-to-string (my/detect-prog-mode str?)))) (_ nil))))) ;; NOTE: By default, separedit provides colouring for 'strings', "strings", and `strings' ;; This doesn't look very good when I have a single quote within double quotes: ;; In an Emacs Lisp buffer, editing the string "Bob's Work" gives unexpected highlighting. ;; ``` ;; (advice-add #'separedit :after ;; (lambda (&rest _) ;; (when (s-ends-with? "string-mode" (pp-to-string major-mode)) ;; (text-mode)))) ;; ``` #+end_src #+begin_src emacs-lisp ;; In the indirect buffer, make ⌘-e finish editing. (use-package edit-indirect :config (bind-key "s-e" (lambda () (interactive) (or (ignore-errors (call-interactively #'separedit)) (call-interactively #'edit-indirect-commit))) #'edit-indirect-mode-map)) ;; I also have “s-e” bound to `org-edit-src-exit'. (advice-add 'org-edit-src-exit :before-until (lambda (&rest r) (when (ignore-errors (separedit)) t))) #+end_src #+begin_src emacs-lisp ;; → ⌘-e on an Org paragraph pops-up an edit session in Org mode. ;; → ⌘-e on a selection in Org mode pops-up an edit session in Org mode. ;; TODO: Consider forming an alist for special blocks to refer to their preferred ;; edit mode, defaulting to Org-mode? Perhaps something to consider /after/ ;; addressing the bug below. ;; (advice-unadvice 'org-edit-special) MA: TODO: FIXME: Delete this? (advice-add 'org-edit-special :around (lambda (orginal-function &rest r) (cond ((region-active-p) (call-interactively #'edit-indirect-region) (org-mode)) ((equal 'paragraph (car (org-element-at-point))) (mark-paragraph) (call-interactively #'edit-indirect-region) (org-mode)) (t (or (ignore-errors (apply orginal-function r)) ;; We try to edit a special block when orginal-function fails. ;; This way src blocks are not confused with the more generic idea of special blocks. (when (my/org-in-any-block-p) ;; Note using org-element-at-point doesn't work well with special blocks when you're somewhere within the block. ;; It only works correctly when you're on the boundary of the special block; which is not ideal. ;; This is why I'm not using: (org-element-property :begin elem). (-let [(start . end) (my/org-in-any-block-p)] (set-mark-command start) (goto-char end) (previous-line 2) (end-of-line) ;; FIXME: Still shows #+end_XXX for some reason. (call-interactively #'edit-indirect-region) (org-mode)))))))) #+end_src #+begin_src emacs-lisp ;; where... (defun my/org-in-any-block-p () "Return non-nil if the point is in any Org block. The Org block can be *any*: src, example, verse, etc., even any Org Special block. This function is heavily adapted from `org-between-regexps-p'. Src: https://scripter.co/splitting-an-org-block-into-two/" (save-match-data (let ((pos (point)) (case-fold-search t) (block-begin-re "^[[:blank:]]*#\\+begin_\\(?1:.+?\\)\\(?: .*\\)*$") (limit-up (save-excursion (outline-previous-heading))) (limit-down (save-excursion (outline-next-heading))) beg end) (save-excursion ;; Point is on a block when on BLOCK-BEGIN-RE or if ;; BLOCK-BEGIN-RE can be found before it... (and (or (org-in-regexp block-begin-re) (re-search-backward block-begin-re limit-up :noerror)) (setq beg (match-beginning 0)) ;; ... and BLOCK-END-RE after it... (let ((block-end-re (concat "^[[:blank:]]*#\\+end_" (match-string-no-properties 1) "\\( .*\\)*$"))) (goto-char (match-end 0)) (re-search-forward block-end-re limit-down :noerror)) (> (setq end (match-end 0)) pos) ;; ... without another BLOCK-BEGIN-RE in-between. (goto-char (match-beginning 0)) (not (re-search-backward block-begin-re (1+ beg) :noerror)) ;; Return value. (cons beg end)))))) #+end_src I'd like to [[https://github.com/andreasjansson/language-detection.el][guess the language]] I'm in, when working with strings. #+begin_src emacs-lisp (use-package language-detection :defer nil) ;; Usage: M-x language-detection-buffer ⇒ Get programming language of current buffer ;; Also, (language-detection-string "select * from t") ;; ⇒ sql ;; TODO: Push this upstream; https://github.com/andreasjansson/language-detection.el/issues/1 (cl-defun my/detect-prog-mode (&optional string) "Guess programming mode of the current buffer, or STRING if it is provided. When called interactively, it enables the mode; from Lisp it just returns the name of the associated mode. ;; Example Lisp usage (call-interactively #'my/detect-prog-mode) `language-detection-buffer' returns a string which is not always the name of the associated major mode; that's what we aim to do here." (interactive) (defvar my/detect-prog-mode/special-names '((c . c-mode) (cpp . c++-mode) (emacslisp . emacs-lisp-mode) (html . web-mode) ;; I intentionally want to use this alternative. (matlab . octave-mode) (shell . shell-script-mode) (visualbasic . visual-basic-mode) (xml . sgml-mode)) "Names in this alist map a language to its mode; all other languages 𝒳 have mode ‘𝒳-mode’ afaik.") (let* ((lang (if string (language-detection-string string) (language-detection-buffer))) (mode (or (cdr (assoc lang my/detect-prog-mode/special-names)) (intern (format "%s-mode" lang))))) (if (called-interactively-p 'any) (progn (call-interactively mode) (message "%s enabled!" mode)) mode))) #+end_src + ⌘-e to edit an Org table cell; see [[https://orgmode.org/manual/Built_002din-Table-Editor.html][here]] for built-in table editing commands. #+begin_src emacs-lisp (advice-add #'org-edit-special :before-until (lambda (&rest r) (when (equal 'table-row (car (org-element-at-point))) (call-interactively #'org-table-edit-field)))) #+end_src ** COMMENT Pair Programming :PROPERTIES: :CUSTOM_ID: Pair-Programming :END: # Add: Quickly toggle my Emacs for when doing pair programming I try to make my Emacs look more predictable for my colleagues, by introducing a bunch of UI elements that I normally have off by default but would otherwise serve as useful aids when working together or provide a sense of familiarity for my VSCoder counterparts. #+begin_src emacs-lisp :tangle ~/Desktop/work.el (defun my/toggle-pair-programming () "Toggle enabling features that might aid communication when sharing screen, or pair programming - Full screen mode - Line numbers, and column numbers, and cat progress bar - Blamer overlays - Dark theme - Tabs - Folder navigator " (interactive) (defvar my/sharing-screen? +1) (setq my/sharing-screen? (- my/sharing-screen?)) ;; Toggle between +1 and -1. ;; When pair programming, exit full screen mode so others can “draw” on my screen, eg using Slack (toggle-frame-fullscreen) ;; Line numbers in the left margin (setq display-line-numbers-width-start t) (global-display-line-numbers-mode my/sharing-screen?) ;; Column & line numbers in the modeline; always want these on? (column-number-mode +1) ;; Enabled in doom-modeline by default? (line-number-mode +1) ;; Enabled in doom-modeline by default? ;; Progress bar, graphic and percentage (use-package nyan-mode ) (nyan-mode my/sharing-screen?) ;; Who did what and when? (global-blamer-mode my/sharing-screen?) ;; Make things look like VSCode; reduce friction with others ;; → Dark theme, tabs, and clickable folder navigation (if (= +1 my/sharing-screen?) (my/toggle-theme 'doom-dracula) (my/toggle-theme 'doom-solarized-light)) (use-package centaur-tabs) (setq centaur-tabs-set-icons t) (setq centaur-tabs-gray-out-icons 'buffer) (setq centaur-tabs-set-bar 'over) (centaur-tabs-mode my/sharing-screen?) ;; Treemacs is feature-right; e.g. highlights file associated to current buffer automatically and fully mouse-complaint. ;; press ? to summon a helpful hydra (use-package treemacs) (if (= +1 my/sharing-screen?) ;; Toggle showing treemacs window (treemacs) (ignore-errors (delete-window (treemacs-get-local-window)))) ;; Press “C-e” to see the end of length file names, or use the mouse to widen treemacs (treemacs-toggle-fixed-width) (ignore-errors (treemacs-indent-guide-mode +1)) (treemacs-resize-icons 18) (other-window -1) ;; Kickoff message (if (= +1 my/sharing-screen?) (message "Pair programming provides an excellent test of technical and social skills"))) (my/toggle-pair-programming) #+end_src ** Eldoc for Lisp and Haskell ---documentation in the mini-buffer :PROPERTIES: :CUSTOM_ID: Eldoc-for-Lisp-and-Haskell :END: In =emacs-lisp-mode= we can enable =eldoc-mode= ---“Elisp Live Documentation”--- to display information about a function or a variable in the echo area. Likewise for Haskell. #+BEGIN_SRC emacs-lisp (use-package eldoc :hook (emacs-lisp-mode . turn-on-eldoc-mode) (lisp-interaction-mode . turn-on-eldoc-mode) (haskell-mode . turn-on-haskell-doc-mode) (haskell-mode . turn-on-haskell-indent)) ;; Slightly shorten eldoc display delay. (setq eldoc-idle-delay 0.4) ;; Default 0.5 #+END_SRC The less casual Haskeller would likely want to use [[https://haskell-lang.org/intero][intero]] to obtain more support; e.g., obtain suggestions from GHC about redundant imports or type signatures. ** COMMENT Modern Browsing within Emacs :Disabled: :PROPERTIES: :CUSTOM_ID: Modern-Browsing-within-Emacs :END: #+begin_src shell :tangle no # https://github.com/d12frosted/homebrew-emacs-plus $ brew tap d12frosted/emacs-plus $ brew install emacs-plus@29 --with-xwidgets $ /usr/local/Cellar/emacs-plus@29/29.0.50/bin/emacs-29.0.50 & # In ~/.bashrc, put the following at the end: alias emacs="/usr/local/Cellar/emacs-plus@29/29.0.50/bin/emacs-29.0.50" #+end_src -------------------------------------------------------------------------------- I like using Chrome ---I like the integration of all things Google. #+begin_src emacs-lisp (cl-defun internet (&optional (url (concat "https://www." (read-string "https://www.")))) "Browse to URL using `xwidget-webkit-browse-url'; see also `browse-url'." (interactive) (delete-other-windows) (split-window-right) (xwidget-webkit-browse-url url)) (my/defhydra "C-c p" "Emacs Browser" gamepad :Internet ("m" (internet "https://mail.google.com/mail/u/0/#inbox") "gMail" :exit t) ("c" (internet "https://calendar.google.com/calendar/u/0/r") "gCalendar" :exit t) ("e" (internet "https://www.reddit.com/r/emacs/") "Emacs Forum" :exit t) ("b" (internet) "Browse" :exit t)) #+end_src ** An “auto read only” detection mechanism ---when jumping to definitions :Disabled: :PROPERTIES: :CUSTOM_ID: An-auto-read-only-detection-mechanism-when-jumping-to-definitions :header-args: :tangle no :END: Files in 3ʳᵈ-party directories should be read-only whenever I open them, when I'm jumping to definitions (with ~M-.~). #+begin_src emacs-lisp ;; Usage: Press “M-”. “use-package” below and you can accidentally alter the source code! ;; But in this case you likely just wanted to see the 3ʳᵈ-party definition, not alter it. ;; As such, with this advice, the source will not be alterable (unless you toggle read-only mode). (advice-add #'xref-find-definitions :after (lambda (&rest _) (when (--map (s-ends-with? it (f-parent buffer-file-name)) '("lisp/emacs-lisp" "/lisp" ".emacs.d/elpa/")) (read-only-mode)))) #+end_src * Open PDFs in Emacs #+begin_src emacs-lisp ;; In Org-mode, clicking on PDF should open it in Emacs ;; Example: [[~/Desktop/stuff-I'm-learning.pdf::12]] ;; Opens the pdf to page 12 ;; Another Example: docview:~/Desktop/stuff-I'm-learning.pdf::12 (ignore-errors (add-to-list 'org-file-apps '("\\.pdf\\'" . emacs))) ;; Required code to make the above links work as expected. ;; Source: https://www.reddit.com/r/emacs/comments/re0dx8/how_do_you_link_a_specific_pdf_page_in_org_mode/ (defun my-org-docview-open-hack (orig-func &rest args) (let* ((link (car args)) path page) (string-match "\\(.*?\\)\\(?:::\\([0-9]+\\)\\)?$" link) (setq path (match-string 1 link)) (setq page (and (match-beginning 2) (string-to-number (match-string 2 link)))) (org-open-file path 1) (when page (cond ((eq major-mode 'pdf-view-mode) (pdf-view-goto-page page)) (t (doc-view-goto-page page)))))) (advice-add 'org-docview-open :around #'my-org-docview-open-hack) ;; Alternatively, there's a dedicated package for this ;; https://github.com/fuxialexander/org-pdftools/tree/967f48fb5038bba32915ee9da8dc4e8b10ba3376 #+end_src * empv #+begin_src emacs-lisp (use-package empv ) ;; Then, M-x empv-play https://invidious.fdn.fr/watch?v=hlTqCmpP5eo, to listen to Dua Ifitiah in the background ;; Or: M-x empv-play https://invidious.fdn.fr/watch?v=9m9yE7qtq5w ;; lol maybe make a hydra(ie playlist) of things I commonly listen to in the background while working. ;; Require: brew install mpv ;; See also: ;; Maybe better? https://github.com/spiderbit/ytdious/tree/941460b51e43ef6764e15e2b9c4af54c3e56115f ;; Maybe better? https://melpa.org/#/yeetube ;; https://github.com/maximus12793/helm-youtube/tree/e7272f1648c7fa836ea5ac1a61770b4931ab4709 ;; https://github.com/isamert/empv.el/tree/1721a581d68f211a7f0104554858ea2afb1723ff ;; (setq empv-invidious-instance "https://invidious.fdn.fr.com") #+end_src * TODO COMMENT Eglot ----Fancy IDE Features that Just Workᵀᴹ For JavaScript: #+begin_src emacs-lisp ;; Get the language server for JavaScript ;; $ npm install -g javascript-typescript-langserver ;; Now open an empty buffer, switch to JS mode, M-x eglot, M-x company-mode, ;; then enter “console.” and see the completion of possibilities! <3 #+end_src For Java, install doc:eglot-java-mode and activate that in a Java file ---it automatically installs the required language server. 🚀 * COMMENT Lost Souls :Outdated_Documentation:Not_yet_tangled: :PROPERTIES: :CUSTOM_ID: Lost-Souls :END: ** Note: M-S-SPC is for my personal servers dashboard. :PROPERTIES: :CUSTOM_ID: Note-M-S-SPC-is-for-my-personal-servers-dashboard :END: #+begin_src emacs-lisp :tangle no ;; Note: M-S-SPC is for my personal servers dashboard. (global-set-key (kbd "M-SPC") (lambda () (interactive) (setq org-agenda-files (list org-default-notes-file)) (org-agenda nil "a") (delete-other-windows) (beginning-of-buffer))) (use-package ace-jump-mode ;; Already installed above, somewhere. :config (bind-key* "C-c SPC" 'ace-jump-mode)) #+end_src ** Zoom :PROPERTIES: :CUSTOM_ID: Zoom :END: #+begin_src emacs-lisp :tangle no ;; An automatic window-resizing mechanism. ;; A “calmer” alternative to golden-ratio. ;; https://github.com/cyrus-and/zoom (use-package zoom :config (zoom-mode t)) #+end_src ** Ibuffer :PROPERTIES: :CUSTOM_ID: Ibuffer :END: #+begin_src emacs-lisp :tangle no ;; Let's use an improved buffer list. (use-package ibuffer ;; This is built-into Emacs. :bind ("C-x C-b" . ibuffer)) ;; It uses similar commands as does dired; e.g., ;; / . org ;; This filters (“/”) the list with extensions (“.”) being “org”. (use-package ibuffer-vc :hook (ibuffer . (lambda () (ibuffer-vc-set-filter-groups-by-vc-root) (unless (eq ibuffer-sorting-mode 'alphabetic) (ibuffer-do-sort-by-alphabetic)))) :custom (ibuffer-formats '((mark modified read-only " " (name 18 18 :left :elide) " " (size 9 -1 :right) " " (mode 16 16 :left :elide) " " (vc-status 16 16 :left) " " (vc-relative-file))))) #+end_src + [[http://martinowen.net/blog/2010/02/03/tips-for-emacs-ibuffer.html][Tips for using Emacs Ibuffer]] + (~10 minute video) [[https://cestlaz.github.io/posts/using-emacs-34-ibuffer-emmet/][Using Emacs - 34 - ibuffer and emmet - C'est la Z]] + (~10 minute video) [[https://www.youtube.com/watch?v=6KN_oSLFf-k&ab_channel=ProtesilaosStavrou][Emacs: introduction to IBUFFER - YouTube]] ** find function at point :PROPERTIES: :CUSTOM_ID: find-function-at-point :END: Friendly reminder that ~M-.~ takes you to the definition, and ~M-,~ takes you back to where you originally where. ** COMMENT centered-cursor: Keep focus in the centre of the screen :Eeek: :PROPERTIES: :CUSTOM_ID: COMMENT-centered-cursor-Keep-focus-in-the-centre-of-the-screen :END: #+begin_src emacs-lisp :tangle no ;; When scrolling, move the screen and leave the cursour in the (vertical) center of the screen (use-package centered-cursor-mode :config (global-centered-cursor-mode)) #+end_src ** “C-x 2” and “C-x 3” now create a new window horizontally/vertically and send cursor there :PROPERTIES: :CUSTOM_ID: C-x-2-and-C-x-3-now-create-a-new-window-horizontally-vertically-and-send-cursor-there :END: #+begin_src emacs-lisp :tangle no ;; When we split open a new window, we usually want to jump to the new window. (advice-add #'split-window-below :after (lambda (&rest _) (other-window 1))) (advice-add #'split-window-right :after (lambda (&rest _) (other-window 1))) #+end_src ** Semantic Change :PROPERTIES: :CUSTOM_ID: Semantic-Change :END: Using kbd:⌘-i and kbd:⌘-o we can quickly, for example, delete a string or its contents; or delete a {}-block or just its contents; or delete a ()-argument list or just its contents, etc. ~change-inner~ gives you vim's ~ci~ command: - ~change-inner {~ ⇒ Delete all text starting from the first ‘{’ delimiter to the next one; but /keep the delimiters/. - ~change-outer {~ ⇒ As above, but also delete the delimiters. #+begin_src emacs-lisp :tangle no (use-package change-inner :bind (("s-i" . #'change-inner) ("s-o" . #'change-outer))) #+end_src ** Drag Stuff :Disabled: :PROPERTIES: :CUSTOM_ID: Drag-Stuff :END: #+begin_src emacs-lisp :tangle no :tangle no ;; Move current word ←/→, or current line ↑/↓. ;; Todo: Compare with org-metaup and org-metadown... (use-package drag-stuff :config (cl-loop for (key . action) in '(("" . drag-stuff-down) ("" . drag-stuff-up) ("" . drag-stuff-right) ("" . drag-stuff-left)) do (bind-key key action org-mode-map)) (drag-stuff-global-mode 1)) #+end_src Ruins Org-mode's M-↑/↓ for moving entire sections around. ** Indentation Guide :PROPERTIES: :CUSTOM_ID: Indentation-Guide :END: The following is also “OK” in Org-mode ;-) #+begin_src emacs-lisp :tangle no ;; Add a visual indent guide (use-package highlight-indent-guides :hook (prog-mode . highlight-indent-guides-mode) :custom (highlight-indent-guides-method 'character) (highlight-indent-guides-character ?|) (highlight-indent-guides-responsive 'stack)) #+end_src ** TODO COMMENT [Nope?] JavaScript :PROPERTIES: :CUSTOM_ID: COMMENT-Nope-JavaScript :END: + ;; FIXME Error (use-package): Failed to install ob-js: Package ‘ob-js-’ is unavailable #+begin_src emacs-lisp :tangle no (use-package ob-js :config (add-to-list 'org-babel-load-languages '(js . t)) (org-babel-do-load-languages 'org-babel-load-languages org-babel-load-languages) (add-to-list 'org-babel-tangle-lang-exts '("js" . "js")) (system-packages-ensure "node")) ;; use “:results output” for js blocks! #+end_src ** Commenting :PROPERTIES: :CUSTOM_ID: Commenting :END: Let's get some nifty [[https://github.com/remyferre/comment-dwim-2][commenting]] features ---the link has nice usage gifs. + [[kbd:][M-;]] repeatedly does (1) comments current line, (2) inserts a comment at the end of the current line, and (3) deletes an existing end-of-line comment. [[kbd:][C-u_M-;]] indents the current enf-of-line comment with any above it. For use with Org-mode, it's best to use doc:org-edit-src-code ---which I've bound to [[kbd:][⌘ e]]. #+begin_src emacs-lisp :tangle no (use-package comment-dwim-2 :bind ("M-;" . comment-dwim-2)) ;; Not ideal: M-; comments a parent Org heading and not the current line. ;; (define-key org-mode-map (kbd "M-;") 'org-comment-dwim-2) #+end_src ** Having a workspace manager in Emacs :PROPERTIES: :CUSTOM_ID: Having-a-workspace-manager-in-Emacs :END: 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 :tangle no (use-package perspective :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 😃 ** Editor Documentation with Contextual Information :PROPERTIES: :CUSTOM_ID: Editor-Documentation-with-Contextual-Information :END: /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 :tangle no (use-package helpful ) (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. **   =README= ---From =init.org= to =init.el= :PROPERTIES: :CUSTOM_ID: README-From-init-org-to-init-el :END: 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 :PROPERTIES: :CUSTOM_ID: The-my-make-init-el-and-README-function :END: We ‘hook on’ the following function to the usual save method that is associated with this file only. # +name: enable making init and readme #+name: startup-code #+begin_src emacs-lisp :tangle no :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= :PROPERTIES: :CUSTOM_ID: The-Org-block-named-make-readme :END: 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 :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 "

")) ;; 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 :smile: ,#+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/