From 99ac82bf4ca1af4d451a41718d83335d92503a69 Mon Sep 17 00:00:00 2001 From: Peter Tillemans Date: Tue, 30 Jul 2024 14:40:34 +0200 Subject: [PATCH] initial commit after split from dotfiles --- .gitignore | 8 + early-init.el | 74 +++ init.org | 1680 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1762 insertions(+) create mode 100644 .gitignore create mode 100644 early-init.el create mode 100644 init.org diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e495906 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +# ignore everything +* + +# except +!early-init.el +!init.org +!README.org +!.gitignore \ No newline at end of file diff --git a/early-init.el b/early-init.el new file mode 100644 index 0000000..d6ea60f --- /dev/null +++ b/early-init.el @@ -0,0 +1,74 @@ +;;; Bootstrap elpaca +(setq package-enable-at-startup nil) + +(defvar elpaca-installer-version 0.7) +(defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory)) +(defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory)) +(defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory)) +(defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git" + :ref nil :depth 1 + :files (:defaults "elpaca-test.el" (:exclude "extensions")) + :build (:not elpaca--activate-package))) +(let* ((repo (expand-file-name "elpaca/" elpaca-repos-directory)) + (build (expand-file-name "elpaca/" elpaca-builds-directory)) + (order (cdr elpaca-order)) + (default-directory repo)) + (add-to-list 'load-path (if (file-exists-p build) build repo)) + (unless (file-exists-p repo) + (make-directory repo t) + (when (< emacs-major-version 28) (require 'subr-x)) + (condition-case-unless-debug err + (if-let ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*")) + ((zerop (apply #'call-process `("git" nil ,buffer t "clone" + ,@(when-let ((depth (plist-get order :depth))) + (list (format "--depth=%d" depth) "--no-single-branch")) + ,(plist-get order :repo) ,repo)))) + ((zerop (call-process "git" nil buffer t "checkout" + (or (plist-get order :ref) "--")))) + (emacs (concat invocation-directory invocation-name)) + ((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch" + "--eval" "(byte-recompile-directory \".\" 0 'force)"))) + ((require 'elpaca)) + ((elpaca-generate-autoloads "elpaca" repo))) + (progn (message "%s" (buffer-string)) (kill-buffer buffer)) + (error "%s" (with-current-buffer buffer (buffer-string)))) + ((error) (warn "%s" err) (delete-directory repo 'recursive)))) + (unless (require 'elpaca-autoloads nil t) + (require 'elpaca) + (elpaca-generate-autoloads "elpaca" repo) + (load "./elpaca-autoloads"))) +(add-hook 'after-init-hook #'elpaca-process-queues) +(elpaca `(,@elpaca-order)) + +;; Enable :elpaca use-package keyword. +(elpaca elpaca-use-package + (elpaca-use-package-mode)) + +;; Wait for elpaca +(elpaca-wait) + +;; tangling to generate scripts for the local bin directory. This +;; causes the name of the scripts to be returned in the car of the +;; tangle command which is used to load the file. The net result is +;; that not the initialization is loaded, but the first exported +;; script. +(use-package ob-tangle) +(let ((src (concat user-emacs-directory "init.org")) + (tgt (concat user-emacs-directory "init.el"))) + (if (file-newer-than-file-p src tgt) + (progn + (message "snamellit.org has been changed, tangling...") + (org-babel-tangle-file src tgt "emacs-lisp")))) + + +;; Configure Crafted Emacs +(setq crafted-package-system 'elpaca) +(setq crafted-package-installer #'elpaca-try) +(setq crafted-package-installed-predicate #'elpaca-installed-p) + + +;; Startup Crafted Emacs +(setq crafted-emacs-home "~/.local/share/crafted-emacs") +(load (expand-file-name "modules/crafted-package-config" crafted-emacs-home)) +(load (expand-file-name "modules/crafted-early-init-config" crafted-emacs-home)) + diff --git a/init.org b/init.org new file mode 100644 index 0000000..b1fc772 --- /dev/null +++ b/init.org @@ -0,0 +1,1680 @@ +#+TITLE: My Emacs Configuration +#+PROPERTY: header-args :tangle yes + +* Literate Configuration for Emacs +** Goals +- Manage *init.el* as an org-file +- Use a simple robust tangling solution which works together with + Crafted Emacs +- Leverage inclusion of ~use-package~ in Emacs29 for configuration and + loading +- Reduce dependencies : read evaluate the value a package brings + before including it +- Refactor existing configuration + +* First Things First + +*** Make tangled file read-only + +Add a preamble to the file to ensure some global settings, most +importantly make the file read-only to avoid editing the tangled file by +accident instead of the source in the org-mode file. + +#+BEGIN_SRC emacs-lisp + ;;; -*- lexical-binding: t; read-only-mode: t; -*- + ;; + ;; DO NOT EDIT!!! + ;; + ;; This file is automatically generated from the source in *init.org*. + ;; +#+END_SRC + +Also immediately set lexical binding mode. +** Set the garbage collector threshold, to avoid collections + +#+begin_src emacs-lisp +(setq gc-cons-percentage 0.5 + gc-cons-threshold (* 128 1024 1024)) +#+end_src + +** Report time spent loading the configuration + +#+begin_src emacs-lisp +(defconst emacs-start-time (current-time)) + +(defun report-time-since-load (&optional suffix) + (message "Loading init... (%.3fs) %s" + (float-time (time-subtract (current-time) emacs-start-time)) + suffix)) + +(add-hook 'after-init-hook + #'(lambda () (report-time-since-load " [after-init]")) + t) +#+end_src + +When looking for where the time goes, the `report-time-since-load` +with a string indicating the location in the init process can report +on the time since start. + +*** Save customizations in a separate file + +By default customization settings are saved at the end of the *init.el* +file. This wreaks havoc with managing the files in git and will not +work with the tangled version anyway as it will be removed/overwritten +each time the file is regenerated. +Here we set the location of the file to save the customizations and +then load it. + +#+BEGIN_SRC emacs-lisp + ;;; Code: + (setq custom-file (concat user-emacs-directory "custom.el")) + (when (and custom-file + (file-exists-p custom-file)) + (load custom-file nil :nomessage)) +#+END_SRC + +** Crafted Emacs + +When I could not solve the persistent slowness I experienced on +Windows using Doom Emacs, I wanted to switch to a more hands-on +minimal Emacs configuration. I just watched the 'Rational Emacs' +episode and that seemed as good a starting point as any. Then in +August '22 it was renamed to Crafted Emacs which prompted a +(minor) refactoring. +I used for a quite a long term Doom on Linux and Rational/Crafted on +Windows till I was comfortable enough and not started Doom anymore. + +*** Initialize Crafted Emacs + +Loads the Crafted Emacs initialization configuration: + +#+BEGIN_SRC emacs-lisp + ;; initialize crafted-emacs + (setq crafted-startup-inhibit-splash t) + (load "~/.local/share/crafted-emacs/modules/crafted-init-config") +#+END_SRC + + +*** Add and configure packages and load crafted and custom modules + +Lists and installs a variety of packages and includes custom configurations for them: +#+BEGIN_SRC emacs-lisp + (require 'crafted-completion-packages) + (require 'crafted-evil-packages) + (require 'crafted-lisp-packages) + (require 'crafted-writing-packages) + (require 'crafted-ui-packages) + + (message "loading packages") + (crafted-package-install-selected-packages) + (elpaca-wait) + + (require 'crafted-defaults-config) + (require 'crafted-evil-config) + (require 'crafted-completion-config) + (require 'crafted-org-config) + (require 'crafted-lisp-config) + (require 'crafted-writing-config) + (require 'crafted-ui-config): +#+END_SRC + +#+RESULTS: +: pti-org-config + +** Integration with Environment +*** Set default Coding System to Use UTF-8 Everywhere + +Ensures UTF-8 is the default coding system everywhere. + +#+BEGIN_SRC emacs-lisp + (set-default-coding-systems 'utf-8) + (set-language-environment 'utf-8) + (setq locale-coding-system 'utf-8) + (prefer-coding-system 'utf-8) + + + ;; Treat clipboard input as UTF-8 string first; compound text next, etc. + (setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)) +#+END_SRC + +Here’s a breakdown of what each line does: + +1. *(set-default-coding-systems 'utf-8)* + + This line sets the default coding system for new buffers. When you + create a new buffer or open a file, Emacs will use UTF-8 encoding + by default. It will also set the default terminal and keyboard + coding systems. This applies to all internal operations where a + specific coding system has not been specified. + +2. *(set-language-environment 'utf-8)* + + This sets the language environment to UTF-8. Emacs uses the + language environment to guess the preferred coding systems for + reading and writing files and for other operations. Setting this to + UTF-8 ensures that UTF-8 is preferred in all language-related + contexts. + +3. *(setq locale-coding-system 'utf-8)* + + This sets the coding system for locale data, such as environment + variables and system messages. It ensures that Emacs correctly + interprets UTF-8 encoded data coming from the operating system. + +4. *(prefer-coding-system 'utf-8)* + + This makes UTF-8 the preferred coding system for any situation + where Emacs needs to choose an encoding. It ensures that Emacs + prefers UTF-8 over other encodings. + +5. *(setq x-select-request-type ...)* + Treat clipboard input as UTF8_STRING first, compound text next, + etc... . + + +*** Set Path from shell configuration +In order to get the paths in Emacs to be consistent with the ones in +the terminals we get them from a started shell instead of the current +environment which can be considerably different in X, Wayland or on +Mac because the shell initialization scripts have not run yet. + +#+BEGIN_SRC emacs-lisp + ;; set path from shell when started in UI-RELATED + (use-package exec-path-from-shell + :ensure t + :defer 1 + :if (or (daemonp) (memq window-system '(mac ns x))) + :config (exec-path-from-shell-initialize)) +#+END_SRC +*** Setup backup directories +Configures where backup files are stored: +#+BEGIN_SRC emacs-lisp + ;; setup backup directories + ;; see https://www.emacswiki.org/emacs/BackupDirectory + (setq backup-directory-alist + `(("." . ,(file-name-concat user-emacs-directory "backups")))) +#+END_SRC + +*** Enable integration with the Unix Password Store aka *pass* +The *pass* command gives a super practical way to store secrets +encrypted using *gpg* and use them in *.envrc* files, batch scripts on the +command line and, of course, in *Emacs*. + +#+BEGIN_SRC emacs-lisp + ;; enable unix password-store + ;;(use-package epg) + ;;(setq epg-pinentry-mode 'loopback) + (auth-source-pass-enable) +#+END_SRC + +This enables *pass* secrets to be used for all subsystems supporting +*auth-source* (which are probably all of them nowadays). It does require +some finagling to map the parts on the name in the pass system. + +- [[https://www.passwordstore.org/][Pass Website]] +- [[info:auth#The Unix password store][auth#The Unix password store in the info pages]] + +**** Use of Pass Secrets in ELisp + +It is very convenient to get secrets from this store (once gpg is set +up, which a totally different can of worms). A function +`auth-source-pass-get` is provided : + +#+BEGIN_SRC emacs-lisp :tangle no +(auth-source-pass-get 'secret "dummy/password") +#+END_SRC + +#+RESULTS: +: shht!secret + +*** GUIX support + +[[https://gitlab.com/emacs-guix/emacs-guix][Emacs-guix]] is a module to interact with the guix system and help +manage packages and profiles. It also offers support for creating +additional profiles and packages for Guix. + +#+BEGIN_SRC emacs-lisp + (use-package guix + :ensure t + :defer 5) +#+END_SRC + +I find it a bit a confusing module. + +It provides a minor-mode + +** Editor Features +*** Evil Vim Keybindings + +I configure all my apps to use vim keybindings if possible to maximise +muscle memory. + +#+BEGIN_SRC emacs-lisp + ;; some provided automations + (crafted-evil-discourage-arrow-keys) + (crafted-evil-vim-muscle-memory) + +#+END_SRC + +#+BEGIN_SRC emacs-lisp + + ;; set leader keys + (evil-set-leader nil (kbd "C-SPC")) + (evil-set-leader 'normal (kbd "SPC")) + (evil-set-leader 'normal "," t) ;; set localleader +#+END_SRC + +#+BEGIN_SRC emacs-lisp + ;; make vc commands available via leader key + (evil-define-key 'normal 'global + (kbd "v") 'vc-prefix-map) +#+END_SRC + +#+BEGIN_SRC emacs-lisp + ;; make project commands available via leader key + (evil-define-key 'normal 'global + (kbd "p") 'project-prefix-map) +#+END_SRC + +#+BEGIN_SRC emacs-lisp + (evil-define-key 'normal 'global + (kbd "ff") #'find-file + (kbd "fo") #'recentf + (kbd "fr") #'revert-buffer + (kbd "fd") #'diff-buffer-with-file) +#+END_SRC + +#+BEGIN_SRC emacs-lisp + ;; dired integration + (evil-collection-dired-setup) +#+END_SRC + +#+BEGIN_SRC emacs-lisp + (use-package harpoon + :ensure t + :commands (harpoon-add-file harpoon-go-to-1 harpoon-go-to-2 harpoon-toggle-quick-menu) + :config + (let ((pti-harpoon-map (make-sparse-keymap))) + (keymap-set pti-harpoon-map (kbd "h") 'harpoon-toggle-quick-menu) + (keymap-set pti-harpoon-map (kbd "a") 'harpoon-add-file) + (keymap-set pti-harpoon-map (kbd "f") 'harpoon-toggle-file) + (keymap-set pti-harpoon-map (kbd "j") 'harpoon-go-to-1) + (keymap-set pti-harpoon-map (kbd "k") 'harpoon-go-to-2) + (keymap-set pti-harpoon-map (kbd "l") 'harpoon-go-to-3) + (keymap-set pti-harpoon-map (kbd ";") 'harpoon-go-to-4) + (keymap-set pti-harpoon-map (kbd "h") 'harpoon-toggle-quick-menu) + (evil-define-key 'normal 'global + (kbd "h") (pti-harpoon-map)))) +#+END_SRC +*** User Interface +**** Do not show default splash screen +#+BEGIN_SRC emacs-lisp +;; Show splash screen +(unless crafted-startup-inhibit-splash + (setq initial-buffer-choice #'crafted-startup-screen)) +#+END_SRC + +**** + +#+BEGIN_SRC emacs-lisp +;; Profile emacs startup +(add-hook 'emacs-startup-hook + (lambda () + (message "Emacs started in %s." + (emacs-init-time)))) +#+END_SRC +**** Configure Fonts with Fontaine + +#+BEGIN_SRC emacs-lisp + ;; + ;; Font configuration + ;; + + (defun pti-find-installed-font (fonts) + "Find the first font in FONTS which is installed on this system." + (seq-find + (lambda (f) (find-font (font-spec :family f))) + fonts)) + + (defun pti-configure-fonts () + "Set configuration of fonts based on display size." + (defvar pti-font-size + (if (> (display-pixel-height) 1600) 22 14)) + + ;; set startup and default fontsets to make org-bullet work + + (set-fontset-font "fontset-startup" nil "DejaVu Sans Mono" nil) + (set-fontset-font "fontset-default" nil "DejaVu Sans Mono" nil) + + + (setq fontaine-presets + `((t + :default-family ,(pti-find-installed-font + '("FiraCode NF" "FiraCode Nerd Font")) + :default-weight regular + :default-height ,pti-font-size + :variable-pitch-family ,(pti-find-installed-font + '("Lato" "Literation Sans" "FiraCode NF Propo" "FiraCode Nerd Font Propo" "DejaVu Sans")) + :variable-pitch-height 1.33 + :bold-weight heavy + :line-spacing 1 + ) + (regular + :default-height 100) + (small + :default-height 75) + (medium + :default-height 125) + (large + :default-height 150))) + + (fontaine-set-preset 'regular)) + + (use-package fontaine + :ensure t + :if (display-graphic-p) + :demand t + :config (progn + (pti-configure-fonts) + (add-hook 'after-make-frame-functions #'pti-configure-fonts))) +#+END_SRC + +**** Update configuration of each created frame + +When running as daemon there is no graphical context. This means that +all graphical related settings cannot be set properly at initial +startup if we need to interrogate the capabilities of the current +screen. + +#+BEGIN_SRC emacs-lisp + ;; + ;; remove chrome from the frames + ;; also support client frames + ;; + (defun pti-display-tweaks (&optional frame) + "Configure a newly created frame." + + (unless frame + (setq frame (selected-frame))) + (when (display-graphic-p) + (with-selected-frame frame + ;; remove chrome from UI + (menu-bar-mode -1) + (tool-bar-mode -1) + (scroll-bar-mode -1) + ))) + + (add-hook 'after-make-frame-functions #'pti-display-tweaks) + ;; run it in the current frame, because the hooks have already fired + (pti-display-tweaks) + +#+END_SRC +**** Set Theme +#+BEGIN_SRC emacs-lisp + ;; Set theme + (use-package modus-themes + :ensure t + :demand t + :custom + (modus-themes-italic-constructs t) + (modus-themes-bold-constructs t) + (modus-themes-mixed-fonts t "enable mixed fonts in org and markdown et al.") + + (modus-themes-to-toggle '(modus-operandi-tinted modus-vivendi-tinted)) + + (modus-themes-completions '((matches . (extrabold background intense)) + (selection . (semibold accented intense)))) + (modus-themes-org-blocks 'tinted-background) + (modus-themes-mixed-fonts t) + + (modus-themes-headings '((1 . (monochrome extrabold background overline variable-pitch 1.6)) + (2 . (monochrome bold overline 1.4)) + (3 . (monochrome semibold overline 1.3)) + (4 . (monochrome 1.2)) + (5 . (monochrome 1.1)) + (agenda-date . (semilight 1.5)) + (agenda-structure . (variable-pitch light 1.9)) + (t . (monochrome light)))) + :config + (load-theme 'modus-operandi-tinted :no-confirm) + :bind + (("" . #'modus-themes-toggle))) +#+END_SRC + +There is a keybinding on ** to toggle between light and dark mode. + +#+RESULTS: +: modus-themes-toggle + +**** Limit Height of Selected Popup Windows +#+BEGIN_SRC emacs-lisp + + (push '("\\*Occur\\*" + ;; display-buffer functions, first one that succeeds is used + (display-buffer-reuse-mode-window + display-buffer-below-selected) + ;; Parameters + (window-height . (lambda (window) (fit-window-to-buffer window 12)))) ;; fit window, max 12 lines + display-buffer-alist) + (push '("\\*Warnings\\*" + ;; display-buffer functions, first one that succeeds is used + (display-buffer-reuse-mode-window + display-buffer-below-selected) + ;; Parameters + (window-height . (lambda (window) (fit-window-to-buffer window 12)))) ;; fit window, max 12 lines + display-buffer-alist) + (push '("\\*Geiser Debug\\*" + ;; display-buffer functions, first one that succeeds is used + (display-buffer-reuse-mode-window + display-buffer-below-selected) + ;; Parameters + (window-height . (lambda (window) (fit-window-to-buffer window 12)))) ;; fit window, max 12 lines + display-buffer-alist) + + ;; set custom info folder + (add-to-list 'Info-default-directory-list "~/.local/share/info/") +#+END_SRC + +*** Yasnippet configuration +Enables and configures Yasnippet, a template system for Emacs: +#+BEGIN_SRC emacs-lisp + ;; configure yasnippet + (use-package yasnippet + :ensure nil + :defer 5 + :config + (yas-global-mode 1)) +#+END_SRC + +#+RESULTS: +: [nil 26279 35446 711851 nil elpaca-process-queues nil nil 81000 nil] + +*** Enable LLM access with Ellama +Configures access to language models using Ellama. I don't know +whether to put it under writing, comms or programming as it is equally +/useful(?)/ for either activity. So I promoted it to an Editor feature. + +#+BEGIN_SRC emacs-lisp + (use-package llm + :ensure t + :defer 10) + (use-package llm-openai + :ensure nil + :after llm) + ;; enable LLM access with ellama + (use-package ellama + :ensure t + :after llm-openai + :commands (ellama-chat ellama-ask-about ellama-ask-line ellama-ask-selection) + :config + (setopt ellama-language "English") + (setopt ellama-keymap-prefix "a") + (setopt ellama-provider + (make-llm-openai + :key (auth-source-pass-get 'secret "snamellit/openai-api-key") + :chat-model "gpt-4o" + )) + (message "Ellama is available")) +#+END_SRC + +#+RESULTS: +: [nil 26280 51581 64425 nil elpaca-process-queues nil nil 916000 nil] + +It seems the *gpt-4o* model provides better responses. I should +investigate local models more. + +*** Dired Configuration +Enables an alternative file navigation behavior in Dired, Emacs' directory editor: +#+BEGIN_SRC emacs-lisp + (put 'dired-find-alternate-file 'disabled nil) +#+END_SRC +*** Use Magit for version control +#+BEGIN_SRC emacs-lisp + ;; default to magit for version control + (use-package magit + :ensure t + :commands (magit-status) + :bind + (("C-x p v" . magit-status) + (" p v" . magit-status))) + +#+END_SRC +*** Better EDiff support +#+BEGIN_SRC emacs-lisp + ;; better ediff setting suggested by cwebber + (use-package ediff + :commands (ediff ediff-files ediff3 ediff-files3) + :config + (setq + ediff-window-setup-function 'ediff-setup-windows-plain + ediff-split-window-function 'split-window-horizontally)) +#+END_SRC + +#+RESULTS: +: t + +*** Replace normal Buffer Support with IBuffer +#+BEGIN_SRC emacs-lisp + ;;; enhance ibuffer with ibuffer-project if it is available. + (defun crafted-ide-enhance-ibuffer-with-ibuffer-project () + "Set up integration for `ibuffer' with `ibuffer-project'." + (setq ibuffer-filter-groups (ibuffer-project-generate-filter-groups)) + (unless (eq ibuffer-sorting-mode 'project-file-relative) + (ibuffer-do-sort-by-project-file-relative))) + + (use-package ibuffer-project + :ensure t + :hook + (ibuffer-mode . crafted-ide-enhance-ibuffer-with-ibuffer-project) + :bind (("C-x C-b" . #'ibuffer))) +#+END_SRC +*** Enable EditorConfig Standard Support +#+BEGIN_SRC emacs-lisp + ;;; load editorconfig support + (use-package editorconfig + :hook + (prog-mode . (lambda() (editorconfig-mode 1)))) +#+END_SRC + +#+RESULTS: +| editorconfig-mode | + +*** Enable Direnv Integration + +Direnv is the key to have isolated project folders which maintain their own bubble to support whatever is done in that folder. +- set environment variables +- run prep scripts or start services +- load nix or guix profiles to provide support applications + +Emacs' direnv module gives first class support to Emacs projects so they use the right tools for the project. + +#+BEGIN_SRC emacs-lisp + ;; enable direnv mode + (use-package direnv + :ensure t + :config + (direnv-mode)) +#+END_SRC + +This enables direnv globally. + +** Writing +*** Org Mode + +**** Mixed Pitch Support by Default in Org +#+BEGIN_SRC emacs-lisp + (defun pti-org-mode-config () + "Set options for better writing in org buffers." + (mixed-pitch-mode) + (visual-line-mode) + (turn-on-auto-fill) + ) + + (use-package org-bullets + :ensure t + :if (display-graphic-p) + :hook (org-mode . org-bullets-mode)) + + (use-package mixed-pitch + :ensure t + :if (display-graphic-p) + :hook + (org-mode . mixed-pitch-mode)) +#+END_SRC + +#+RESULTS: +: [nil 26279 36119 854408 nil elpaca-process-queues nil nil 376000 nil] +**** Org Configuration +#+BEGIN_SRC emacs-lisp + (use-package org + :ensure nil + :custom + (org-agenda-skip-scheduled-if-done t) + (org-agenda-skip-deadline-if-done t) + (org-todo-keywords '((sequence "TODO(t)" "NEXT(n)" "WAITING(w)" "SOMEDAY(s)" "PROJ(p)" + "|" "DONE(d)" "CANCELED(c)") + (sequence "[ ](T)" "[-](S)" "[?](W)" + "|" "[X](D)") + (sequence "|" "OKAY(o)" "YES(y)" "NO(n)"))) + (org-todo-keywords-for-agenda '((sequence "NEXT(n)" "TODO(t)" "WAITING(w)" "SOMEDAY(s)" "PROJ(p)" "|" "DONE(d)" "CANCELED(c)"))) + (line-spacing 0.1) + (left-margin-width 2) + (right-margin-width 2) + (org-hide-emphasis-markers t) + (org-agenda-files (list "~/Dropbox/org/" "~/org/snamellit/" "~/org/customer/" "~/org/personal/")) + (org-refile-targets '( + (org-agenda-files . (:level . 1)) + ("~/org/personal/bijen.org" . (:level . 1)) + ("~/org/personal/fitness.org" . (:level . 1)) + )) + :config + ;; set files for agenda views + (setq +org-capture-todo-file "~/Dropbox/org/inbox.org" + +org-capture-notes-file "~/Dropbox/org/inbox.org" + +org-capture-journal-file "~/Dropbox/org/journal.org" + +org-capture-projects-file "~/Dropbox/org/projects.org") + :hook + (org-mode . pti-org-mode-config) + ) +#+END_SRC + +#+RESULTS: +: ((\.org\' . org) (\.rs\' . rust-mode) (\.ya?ml\' . yaml-ts-mode) (\.ts\' . typescript-ts-mode) (\.tsx\' . tsx-ts-mode) (\.toml\' . toml-ts-mode) (\.rs\' . rust-ts-mode) (\(?:\.\(?:rbw?\|ru\|rake\|thor\|jbuilder\|rabl\|gemspec\|podspec\)\|/\(?:Gem\|Rake\|Cap\|Thor\|Puppet\|Berks\|Brew\|Vagrant\|Guard\|Pod\)file\)\' . ruby-ts-mode) (\.py[iw]?\' . python-ts-mode) (\.lua\' . lua-ts-mode) (\.json\' . json-ts-mode) (\.js\' . js-ts-mode) (\.java\' . java-ts-mode) (\.html\' . html-ts-mode) (\.heex\' . heex-ts-mode) (go\.mod\' . go-mod-ts-mode) (\.go\' . go-ts-mode) (\.ex\' . elixir-ts-mode) ([/\]\(?:Containerfile\|Dockerfile\)\(?:\.[^/\]*\)?\' . dockerfile-ts-mode) (\.css\' . css-ts-mode) (\.cpp\' . c++-ts-mode) (\.cmake\' . cmake-ts-mode) (\.cs\' . csharp-ts-mode) (\.c\' . c-ts-mode) (\.sh\' . bash-ts-mode) (\.tf\(vars\)?\' . terraform-mode) (\.nomad\' . hcl-mode) (\.hcl\' . hcl-mode) ([./]opam_?\' . tuareg-opam-mode) (\.mly\' . tuareg-menhir-mode) (\.eliomi?\' . tuareg-mode) (\.ml[ip]?\' . tuareg-mode) (\.rs\' . rustic-mode) (\.envrc\' . direnv-envrc-mode) (/git-rebase-todo\' . git-rebase-mode) (/guix/drvs/[[:alnum:]]\{2\}/[[:alnum:]]\{30\}-\(?:[+._[:alnum:]-]+\)\.drv\' . guix-build-log-mode) (/gnu/store/\(?:[+._[:alnum:]-]+\)\.drv\' . guix-derivation-mode) (/etc/profile\' . guix-env-var-mode) (/tmp/guix-build-\(?:[+._[:alnum:]-]+\)\.drv-[[:digit:]]+/environment-variables\' . guix-env-var-mode) (/guix/profiles/system\(?:[+._[:alnum:]-]+\)*/\(?:boot\|parameters\)\' . guix-scheme-mode) (/gnu/store/\(?:[0-9a-df-np-sv-z]\{32\}\)-\(?:activate\|activate-service\|boot\|parameters\|shepherd\.conf\|shepherd\(?:[+._[:alnum:]-]+\)\.scm\|\(?:[+._[:alnum:]-]+\)-builder\)\' . guix-scheme-mode) (\(?:build\|profile\)\.boot\' . clojure-mode) (\.cljs\' . clojurescript-mode) (\.cljc\' . clojurec-mode) (\.\(clj\|cljd\|dtm\|edn\|lpy\)\' . clojure-mode) (\.\(?:md\|markdown\|mkd\|mdown\|mkdn\|mdwn\)\' . markdown-mode) (\.hva\' . LaTeX-mode) (\.gpg\(~\|\.~[0-9]+~\)?\' nil epa-file) (\.elc\' . elisp-byte-code-mode) (\.zst\' nil jka-compr) (\.dz\' nil jka-compr) (\.xz\' nil jka-compr) (\.lzma\' nil jka-compr) (\.lz\' nil jka-compr) (\.g?z\' nil jka-compr) (\.bz2\' nil jka-compr) (\.Z\' nil jka-compr) (\.editorconfig\' . editorconfig-conf-mode) (\.vr[hi]?\' . vera-mode) (\(?:\.\(?:rbw?\|ru\|rake\|thor\|axlsx\|jbuilder\|rabl\|gemspec\|podspec\)\|/\(?:Gem\|Rake\|Cap\|Thor\|Puppet\|Berks\|Brew\|Fast\|Vagrant\|Guard\|Pod\)file\)\' . ruby-mode) (\.re?st\' . rst-mode) (/\(?:Pipfile\|\.?flake8\)\' . conf-mode) (\.py[iw]?\' . python-mode) (\.m\' . octave-maybe-mode) (\.less\' . less-css-mode) (\.scss\' . scss-mode) (\.cs\' . csharp-mode) (\.awk\' . awk-mode) (\.\(u?lpc\|pike\|pmod\(\.in\)?\)\' . pike-mode) (\.idl\' . idl-mode) (\.java\' . java-mode) (\.m\' . objc-mode) (\.ii\' . c++-mode) (\.i\' . c-mode) (\.lex\' . c-mode) (\.y\(acc\)?\' . c-mode) (\.h\' . c-or-c++-mode) (\.c\' . c-mode) (\.\(CC?\|HH?\)\' . c++-mode) (\.[ch]\(pp\|xx\|\+\+\)\' . c++-mode) (\.\(cc\|hh\)\' . c++-mode) (\.\(bat\|cmd\)\' . bat-mode) (\.[sx]?html?\(\.[a-zA-Z_]+\)?\' . mhtml-mode) (\.svgz?\' . image-mode) (\.svgz?\' . xml-mode) (\.x[bp]m\' . image-mode) (\.x[bp]m\' . c-mode) (\.p[bpgn]m\' . image-mode) (\.tiff?\' . image-mode) (\.gif\' . image-mode) (\.png\' . image-mode) (\.jpe?g\' . image-mode) (\.webp\' . image-mode) (\.te?xt\' . text-mode) (\.[tT]e[xX]\' . tex-mode) (\.ins\' . tex-mode) (\.ltx\' . latex-mode) (\.dtx\' . doctex-mode) (\.org\' . org-mode) (\.dir-locals\(?:-2\)?\.el\' . lisp-data-mode) (\.eld\' . lisp-data-mode) (eww-bookmarks\' . lisp-data-mode) (tramp\' . lisp-data-mode) (/archive-contents\' . lisp-data-mode) (places\' . lisp-data-mode) (\.emacs-places\' . lisp-data-mode) (\.el\' . emacs-lisp-mode) (Project\.ede\' . emacs-lisp-mode) (\(?:\.\(?:scm\|sls\|sld\|stk\|ss\|sch\)\|/\.guile\)\' . scheme-mode) (\.l\' . lisp-mode) (\.li?sp\' . lisp-mode) (\.[fF]\' . fortran-mode) (\.for\' . fortran-mode) (\.p\' . pascal-mode) (\.pas\' . pascal-mode) (\.\(dpr\|DPR\)\' . delphi-mode) (\.\([pP]\([Llm]\|erl\|od\)\|al\)\' . perl-mode) (Imakefile\' . makefile-imake-mode) (Makeppfile\(?:\.mk\)?\' . makefile-makepp-mode) (\.makepp\' . makefile-makepp-mode) (\.mk\' . makefile-gmake-mode) (\.make\' . makefile-gmake-mode) ([Mm]akefile\' . makefile-gmake-mode) (\.am\' . makefile-automake-mode) (\.texinfo\' . texinfo-mode) (\.te?xi\' . texinfo-mode) (\.[sS]\' . asm-mode) (\.asm\' . asm-mode) (\.css\' . css-mode) (\.mixal\' . mixal-mode) (\.gcov\' . compilation-mode) (/\.[a-z0-9-]*gdbinit . gdb-script-mode) (-gdb\.gdb . gdb-script-mode) ([cC]hange\.?[lL]og?\' . change-log-mode) ([cC]hange[lL]og[-.][0-9]+\' . change-log-mode) (\$CHANGE_LOG\$\.TXT . change-log-mode) (\.scm\.[0-9]*\' . scheme-mode) (\.[ckz]?sh\'\|\.shar\'\|/\.z?profile\' . sh-mode) (\.bash\' . sh-mode) (/PKGBUILD\' . sh-mode) (\(/\|\`\)\.\(bash_\(profile\|history\|log\(in\|out\)\)\|z?log\(in\|out\)\)\' . sh-mode) (\(/\|\`\)\.\(shrc\|zshrc\|m?kshrc\|bashrc\|t?cshrc\|esrc\)\' . sh-mode) (\(/\|\`\)\.\([kz]shenv\|xinitrc\|startxrc\|xsession\)\' . sh-mode) (\.m?spec\' . sh-mode) (\.m[mes]\' . nroff-mode) (\.man\' . nroff-mode) (\.sty\' . latex-mode) (\.cl[so]\' . latex-mode) (\.bbl\' . latex-mode) (\.bib\' . bibtex-mode) (\.bst\' . bibtex-style-mode) (\.sql\' . sql-mode) (\(acinclude\|aclocal\|acsite\)\.m4\' . autoconf-mode) (\.m[4c]\' . m4-mode) (\.mf\' . metafont-mode) (\.mp\' . metapost-mode) (\.vhdl?\' . vhdl-mode) (\.article\' . text-mode) (\.letter\' . text-mode) (\.i?tcl\' . tcl-mode) (\.exp\' . tcl-mode) (\.itk\' . tcl-mode) (\.icn\' . icon-mode) (\.sim\' . simula-mode) (\.mss\' . scribe-mode) (\.f9[05]\' . f90-mode) (\.f0[38]\' . f90-mode) (\.indent\.pro\' . fundamental-mode) (\.\(pro\|PRO\)\' . idlwave-mode) (\.srt\' . srecode-template-mode) (\.prolog\' . prolog-mode) (\.tar\' . tar-mode) (\.\(arc\|zip\|lzh\|lha\|zoo\|[jew]ar\|xpi\|rar\|cbr\|7z\|squashfs\|ARC\|ZIP\|LZH\|LHA\|ZOO\|[JEW]AR\|XPI\|RAR\|CBR\|7Z\|SQUASHFS\)\' . archive-mode) (\.oxt\' . archive-mode) (\.\(deb\|[oi]pk\)\' . archive-mode) (\`/tmp/Re . text-mode) (/Message[0-9]*\' . text-mode) (\`/tmp/fol/ . text-mode) (\.oak\' . scheme-mode) (\.sgml?\' . sgml-mode) (\.x[ms]l\' . xml-mode) (\.dbk\' . xml-mode) (\.dtd\' . sgml-mode) (\.ds\(ss\)?l\' . dsssl-mode) (\.js[mx]?\' . javascript-mode) (\.har\' . javascript-mode) (\.json\' . js-json-mode) (\.[ds]?va?h?\' . verilog-mode) (\.by\' . bovine-grammar-mode) (\.wy\' . wisent-grammar-mode) (\.erts\' . erts-mode) ([:/\]\..*\(emacs\|gnus\|viper\)\' . emacs-lisp-mode) (\`\..*emacs\' . emacs-lisp-mode) ([:/]_emacs\' . emacs-lisp-mode) (/crontab\.X*[0-9]+\' . shell-script-mode) (\.ml\' . lisp-mode) (\.ld[si]?\' . ld-script-mode) (ld\.?script\' . ld-script-mode) (\.xs\' . c-mode) (\.x[abdsru]?[cnw]?\' . ld-script-mode) (\.zone\' . dns-mode) (\.soa\' . dns-mode) (\.asd\' . lisp-mode) (\.\(asn\|mib\|smi\)\' . snmp-mode) (\.\(as\|mi\|sm\)2\' . snmpv2-mode) (\.\(diffs?\|patch\|rej\)\' . diff-mode) (\.\(dif\|pat\)\' . diff-mode) (\.[eE]?[pP][sS]\' . ps-mode) (\.\(?:PDF\|EPUB\|CBZ\|FB2\|O?XPS\|DVI\|OD[FGPST]\|DOCX\|XLSX?\|PPTX?\|pdf\|epub\|cbz\|fb2\|o?xps\|djvu\|dvi\|od[fgpst]\|docx\|xlsx?\|pptx?\)\' . doc-view-mode-maybe) (configure\.\(ac\|in\)\' . autoconf-mode) (\.s\(v\|iv\|ieve\)\' . sieve-mode) (BROWSE\' . ebrowse-tree-mode) (\.ebrowse\' . ebrowse-tree-mode) (#\*mail\* . mail-mode) (\.g\' . antlr-mode) (\.mod\' . m2-mode) (\.ses\' . ses-mode) (\.docbook\' . sgml-mode) (\.com\' . dcl-mode) (/config\.\(?:bat\|log\)\' . fundamental-mode) (/\.?\(authinfo\|netrc\)\' . authinfo-mode) (\.\(?:[iI][nN][iI]\|[lL][sS][tT]\|[rR][eE][gG]\|[sS][yY][sS]\)\' . conf-mode) (\.la\' . conf-unix-mode) (\.ppd\' . conf-ppd-mode) (java.+\.conf\' . conf-javaprop-mode) (\.properties\(?:\.[a-zA-Z0-9._-]+\)?\' . conf-javaprop-mode) (\.toml\' . conf-toml-mode) (\.desktop\' . conf-desktop-mode) (/\.redshift\.conf\' . conf-windows-mode) (\`/etc/\(?:DIR_COLORS\|ethers\|.?fstab\|.*hosts\|lesskey\|login\.?de\(?:fs\|vperm\)\|magic\|mtab\|pam\.d/.*\|permissions\(?:\.d/.+\)?\|protocols\|rpc\|services\)\' . conf-space-mode) (\`/etc/\(?:acpid?/.+\|aliases\(?:\.d/.+\)?\|default/.+\|group-?\|hosts\..+\|inittab\|ksysguarddrc\|opera6rc\|passwd-?\|shadow-?\|sysconfig/.+\)\' . conf-mode) ([cC]hange[lL]og[-.][-0-9a-z]+\' . change-log-mode) (/\.?\(?:gitconfig\|gnokiirc\|hgrc\|kde.*rc\|mime\.types\|wgetrc\)\' . conf-mode) (/\.mailmap\' . conf-unix-mode) (/\.\(?:asound\|enigma\|fetchmail\|gltron\|gtk\|hxplayer\|mairix\|mbsync\|msmtp\|net\|neverball\|nvidia-settings-\|offlineimap\|qt/.+\|realplayer\|reportbug\|rtorrent\.\|screen\|scummvm\|sversion\|sylpheed/.+\|xmp\)rc\' . conf-mode) (/\.\(?:gdbtkinit\|grip\|mpdconf\|notmuch-config\|orbital/.+txt\|rhosts\|tuxracer/options\)\' . conf-mode) (/\.?X\(?:default\|resource\|re\)s\> . conf-xdefaults-mode) (/X11.+app-defaults/\|\.ad\' . conf-xdefaults-mode) (/X11.+locale/.+/Compose\' . conf-colon-mode) (/X11.+locale/compose\.dir\' . conf-javaprop-mode) (\.~?[0-9]+\.[0-9][-.0-9]*~?\' nil t) (\.\(?:orig\|in\|[bB][aA][kK]\)\' nil t) ([/.]c\(?:on\)?f\(?:i?g\)?\(?:\.[a-zA-Z0-9._-]+\)?\' . conf-mode-maybe) (\.[1-9]\' . nroff-mode) (\.art\' . image-mode) (\.avs\' . image-mode) (\.bmp\' . image-mode) (\.cmyk\' . image-mode) (\.cmyka\' . image-mode) (\.crw\' . image-mode) (\.dcr\' . image-mode) (\.dcx\' . image-mode) (\.dng\' . image-mode) (\.dpx\' . image-mode) (\.fax\' . image-mode) (\.heic\' . image-mode) (\.hrz\' . image-mode) (\.icb\' . image-mode) (\.icc\' . image-mode) (\.icm\' . image-mode) (\.ico\' . image-mode) (\.icon\' . image-mode) (\.jbg\' . image-mode) (\.jbig\' . image-mode) (\.jng\' . image-mode) (\.jnx\' . image-mode) (\.miff\' . image-mode) (\.mng\' . image-mode) (\.mvg\' . image-mode) (\.otb\' . image-mode) (\.p7\' . image-mode) (\.pcx\' . image-mode) (\.pdb\' . image-mode) (\.pfa\' . image-mode) (\.pfb\' . image-mode) (\.picon\' . image-mode) (\.pict\' . image-mode) (\.rgb\' . image-mode) (\.rgba\' . image-mode) (\.tga\' . image-mode) (\.wbmp\' . image-mode) (\.webp\' . image-mode) (\.wmf\' . image-mode) (\.wpg\' . image-mode) (\.xcf\' . image-mode) (\.xmp\' . image-mode) (\.xwd\' . image-mode) (\.yuv\' . image-mode) (\.tgz\' . tar-mode) (\.tbz2?\' . tar-mode) (\.txz\' . tar-mode) (\.tzst\' . tar-mode) (\.drv\' . LaTeX-mode)) + +**** Org Babel Support +#+BEGIN_SRC emacs-lisp + ;; enable verb package to do REST calls + (use-package verb + :ensure t + :defer 3 + :bind + (:map org-mode-map + ("C-c C-r" . verb-command-map))) + + (use-package ob-verb + :after verb + :defer 3) + ;; configure babel languages + + (use-package org-babel + :no-require + :after '(ob-verb) + :config + (org-babel-do-load-languages + 'org-babel-load-languages + '((emacs-lisp . t) + (shell . t) + (python . t) + (latex . t) + (verb . t) + (scheme . t) + (plantuml . t)))) + + ;; fix a bug in ob-scheme which causes an error to be thrown when evaluating + ;; a cons cell or improper list + ;; see https://list.orgmode.org/87bkk3x1bu.fsf@gajsin.name/T/ + (defun org-babel-scheme--table-or-string (results) + "Convert RESULTS into an appropriate elisp value. + If the results look like a list or tuple, then convert them into an + Emacs-lisp table, otherwise return the results as a string." + (let ((res (org-babel-script-escape results))) + (cond ((proper-list-p res) + (mapcar (lambda (el) + (if (or (null el) (eq el 'null)) + org-babel-scheme-null-to + el)) + res)) + (t res)))) +#+END_SRC + +#+RESULTS: +: org-babel-scheme--table-or-string + +**** Org Export +#+BEGIN_SRC emacs-lisp + (use-package ox + :ensure nil) + +#+END_SRC + +***** Org Export to Markdown +#+BEGIN_SRC emacs-lisp + (use-package ox-md + :ensure nil + :after ox + ) +#+END_SRC +***** Org Latex Export +****** Syntax Highlighting requires Multiple Passes +#+BEGIN_SRC emacs-lisp + ;; support for minted in LaTeX for code highlighting + (use-package ox-latex + :ensure nil + :after ox + :config + (setq org-latex-compiler "lualatex") + (setq org-latex-pdf-process + '("%latex -shell-escape -interaction nonstopmode -output-directory %o %f" + "%latex -interaction nonstopmode -output-directory %o %f" + "%latex -interaction nonstopmode -output-directory %o %f")) + ) +#+END_SRC +****** Load Customer Latex Classes +#+BEGIN_SRC emacs-lisp + ;; Customer LaTeX classes + (setq mlx-latex-classes + '( + ("mlx-beamer" "\\documentclass{mlx-beamer} + [NO-DEFAULT-PACKAGES] + [NO-PACKAGES]" + ("\\section{%s}" . "\\section*{%s}") + ("\\subsection{%s}" . "\\subsection*{%s}") + ("\\subsubsection{%s}" . "\\subsubsection*{%s}")) + ("mlx-book" "\\documentclass{mlx-book} + [NO-DEFAULT-PACKAGES] + [NO-PACKAGES]" + ("\\part{%s}" . "\\part*{%s}") + ("\\chapter{%s}" . "\\chapter*{%s}") + ("\\section{%s}" . "\\section*{%s}") + ("\\subsection{%s}" . "\\subsection*{%s}") + ("\\subsubsection{%s}" . "\\subsubsection*{%s}")) + ("mlx-report" "\\documentclass{mlx-report} + [NO-DEFAULT-PACKAGES] + [NO-PACKAGES]" + ("\\part{%s}" . "\\part*{%s}") + ("\\chapter{%s}" . "\\chapter*{%s}") + ("\\section{%s}" . "\\section*{%s}") + ("\\subsection{%s}" . "\\subsection*{%s}") + ("\\subsubsection{%s}" . "\\subsubsection*{%s}")) + ("mlx-article" "\\documentclass{mlx-article} + [NO-DEFAULT-PACKAGES] + [NO-PACKAGES]" + ("\\section{%s}" . "\\section*{%s}") + ("\\subsection{%s}" . "\\subsection*{%s}") + ("\\subsubsection{%s}" . "\\subsubsection*{%s}") + ("\\paragraph{%s}" . "\\paragraph*{%s}") + ("\\subparagraph{%s}" . "\\subparagraph*{%s}")) + ("mlx-project-charter" "\\documentclass{mlx-project-charter} + [NO-DEFAULT-PACKAGES] + [NO-PACKAGES]" + ("\\section{%s} + \\begin{mdframed}" "\\end{mdframed}") + ("\\subsection{%s}" . "\\subsection*{%s}") + ("\\subsubsection{%s}" . "\\subsubsection*{%s}") + ("\\paragraph{%s}" . "\\paragraph*{%s}") + ("\\subparagraph{%s}" . "\\subparagraph*{%s}")))) +#+END_SRC +****** Load My Latex Classes +#+BEGIN_SRC emacs-lisp + (setq snm-latex-classes + '( + ("snm-beamer" "\\documentclass{snm-beamer} + [NO-DEFAULT-PACKAGES] + [NO-PACKAGES]" + ("\\section{%s}" . "\\section*{%s}") + ("\\subsection{%s}" . "\\subsection*{%s}") + ("\\subsubsection{%s}" . "\\subsubsection*{%s}")) + ("snm-book" "\\documentclass{snm-book} + [NO-DEFAULT-PACKAGES] + [NO-PACKAGES]" + ("\\part{%s}" . "\\part*{%s}") + ("\\chapter{%s}" . "\\chapter*{%s}") + ("\\section{%s}" . "\\section*{%s}") + ("\\subsection{%s}" . "\\subsection*{%s}") + ("\\subsubsection{%s}" . "\\subsubsection*{%s}")) + ("snm-report" "\\documentclass{snm-report} + [NO-DEFAULT-PACKAGES] + [NO-PACKAGES]" + ("\\part{%s}" . "\\part*{%s}") + ("\\chapter{%s}" . "\\chapter*{%s}") + ("\\section{%s}" . "\\section*{%s}") + ("\\subsection{%s}" . "\\subsection*{%s}") + ("\\subsubsection{%s}" . "\\subsubsection*{%s}")) + ("snm-invoice" "\\documentclass{snm-invoice} + [NO-DEFAULT-PACKAGES] + [NO-PACKAGES]" + ("\\section{%s}" . "\\section*{%s}") + ("\\subsection{%s}" . "\\subsection*{%s}") + ("\\subsubsection{%s}" . "\\subsubsection*{%s}") + ("\\paragraph{%s}" . "\\paragraph*{%s}") + ("\\subparagraph{%s}" . "\\subparagraph*{%s}")) + ("snm-article" "\\documentclass{snm-article} + [NO-DEFAULT-PACKAGES] + [NO-PACKAGES]" + ("\\section{%s}" . "\\section*{%s}") + ("\\subsection{%s}" . "\\subsection*{%s}") + ("\\subsubsection{%s}" . "\\subsubsection*{%s}") + ("\\paragraph{%s}" . "\\paragraph*{%s}") + ("\\subparagraph{%s}" . "\\subparagraph*{%s}")))) + + (with-eval-after-load 'ox-latex + (dolist + (class (append mlx-latex-classes snm-latex-classes)) + (add-to-list 'org-latex-classes class))) +#+END_SRC +**** Org Blogging +#+BEGIN_SRC emacs-lisp + + + ;; I started blogging again. To stimulate this let's add a template to get started + ;; on a new post. + (defvar snamellit/blog-title "test-title") + (defun snamellit/capture-blog-post-file () + "Create a new blog post file with a title and date." + (let* ((title (read-from-minibuffer "Post Title: ")) + (slug (replace-regexp-in-string "[^a-z0-9]+" "-" (downcase title)))) + (setq snamellit/blog-title title) + (format "~/src/website/content/blog/%s-%s.md" + (format-time-string "%Y-%m-%d" (current-time)) + slug))) +#+END_SRC +**** Org Capture Customization +#+BEGIN_SRC emacs-lisp + (use-package org-capture + :ensure nil + :config (progn + (setq org-capture-templates '()) + (add-to-list 'org-capture-templates + '("t" "Todo" entry (file+headline +org-capture-todo-file "Tasks") + "* TODO %?\n %i\n %a")) + (add-to-list 'org-capture-templates + '("n" "Note" entry (file+headline +org-capture-todo-file "Notes") + "* %?\n %i\n %a")) + (add-to-list 'org-capture-templates + '("j" "Journal" entry (file+datetree +org-capture-journal-file) + "* %?\nEntered on %U\n %i\n %a")) + (add-to-list 'org-capture-templates + '("b" "Blog Post" plain + (file snamellit/capture-blog-post-file) + "+++\ntitle = %(progn snamellit/blog-title)\ndate = %t\nauthor = Peter Tillemans\nemail = pti@snamellit.com\n[taxonomies]\ntags = []\ncategories = []\n+++\n\n%?")) + + ;; configure support for recipes + (add-to-list 'org-capture-templates + '("c" "Cookbook" entry (file "~/org/cookbook.org") + "%(org-chef-get-recipe-from-url)" + :empty-lines 1)) + (add-to-list 'org-capture-templates + '("m" "Manual Cookbook" entry (file "~/org/cookbook.org") + "* %^{Recipe title: }\n :PROPERTIES:\n :source-url:\n :servings:\n :prep-time:\n :cook-time:\n :ready-in:\n :END:\n** Ingredients\n %?\n** Directions\n\n"))) + :bind (("X" . org-capture))) + +#+END_SRC + +#+RESULTS: +: t +**** Evil Support for Org + +Better keybinding for evil mode from [[https://github.com/Somelauw/evil-org-mode][the evil-org github repo]]. +#+BEGIN_SRC emacs-lisp + (use-package evil-org + :ensure t + :after org + :hook + (org-mode . evil-org-mode) + :config + (require 'evil-org-agenda) + (evil-org-agenda-set-keys)) +#+END_SRC + +Here is a snapshot of the keybindings dd <2024-07-30 Tue>. + +***** Quick overview + + |----------------+---------------------------| + | key | explanation | + |----------------+---------------------------| + | gh, gj, gk, gl | navigate between elements | + | vae | select an element | + |----------------+---------------------------| + +***** Headings and items + + |--------------+------------------------| + | key | explanation | + |--------------+------------------------| + | M-ret | insert heading | + | , g TAB | fold / unfold headings | + | M-h or << | promote a heading | + | M-l or >> | demote a heading | + | M-k | move subtree up | + | M-j | move subtree down | + | M-S-h or aR | demote a subtree | + | vaR | select a subtree | + |--------------+------------------------| + +***** Tables + + |-----------+--------------------------------| + | key | explanation | + |-----------+--------------------------------| + | ( | previous table cell | + | ) | next table cell | + | { | beginning of table | + | } | end of table | + | M-h / M-l | move table column left / right | + | M-k / M-j | move table column up / down | + | vae | select table cell | + | vaE | select table row | + | var | select whole table | + |-----------+--------------------------------| + +***** Agenda + + |-------------------------+-------------------------+-----------------------------------------------------------------------------------| + | Evil key | Emacs key | explanation | + |-------------------------+-------------------------+-----------------------------------------------------------------------------------| + | gH | | Move cursor to the top of window | + | gM | | Move cursor to the middle of window | + | gL | | Move cursor to the bottom of window | + | , S- | | go to the corresponding entry at point | + | g TAB | | go to the corresponding entry at point | + | | | go to the Org mode file which contains the item at point | + | M- | L | Display Org file and center around the item | + | | | scroll up | + | or | or | scroll down | + | j, k | n, p | next, previous line | + | gj, gk, C-j, C-k | N, P | next, previous item | + | [, ] | b, f | previous, next week | + | J, K | -, +, S-down, S-up | down, up priority | + | H, L | S-left, S-right | modify date to earlier, later | + | t | t | cycle TODO keywords | + | M-j, M-k | M-down, M-up | drag line forward, backward | + | C-S-h, C-S-l | C-S-left, C-S-right | previous, next keyword | + | u | C-_, C-/ | undo | + | dd | C-k | delete item | + | da | a | ask and archive item | + | dA | $ | archive item | + | ct | : | set tags | + | ce | e | set effort | + | cT | ; | set timer | + | i | i | insert entry in diary | + | a | z | add note | + | A | A | append to agenda | + | C | k | capture | + | m | m | mark | + | * | * | toggle all marks | + | % | % | mark regexp | + | M | U | remove all marks | + | x | B | execute action on marks | + | gr | r | refresh agenda | + | gR | g | refresh all agendas | + | ZQ | x | exit agenda | + | ZZ | Q | quit agenda | + | gD | v | tweak display (deadlines, diary, follow/log-mode, entry text, grid, day/week/year | + | ZD | # | dim blocked tasks | + | sc, sr, se, st, s^ | <, =, _, /, ^ | filter by category, regexp, effort, tag, top headline | + | S | \vert | remove all filters | + | ss | ~ | filter/limit interactively | + | I | I | clock in | + | O | O | clock out | + | cg | J | jump to the currently clocked in task within the agenda | + | cc | X | cancel the current running clock | + | cr | R | toggle clocktable mode in an agenda buffer | + | . | . | go to today's date | + | gc | c | pop up calendar | + | gC | C | pop up date converter | + | p | > | pop up date selector | + | gh | H | pop up holiday calendar | + | gm | M | pop up phases of the moon | + | gs | S | pop up sunrise/sunset times | + | gt | T | pop up tag list | + | +, - | [, ] | manipulate the query by adding a search term with positive or negative selection | + |-------------------------+-------------------------+-----------------------------------------------------------------------------------| + +**** Org GCal Support + +#+BEGIN_SRC emacs-lisp + ;; configure support for google calendar + (use-package org-gcal + :ensure t + :defer 10 + :init + (setq + org-gcal-client-id (auth-source-pass-get 'secret "snamellit/org-gcal-client") + org-gcal-client-secret (auth-source-pass-get "id" "snamellit/org-gcal-client") + org-gcal-fetch-file-alist '(("pti@snamellit.com" . "~/org/schedule.org")))) +#+END_SRC + +**** Org Jira Integration + +#+BEGIN_SRC emacs-lisp + ;; configure org-jira + + + (use-package org-jira + :ensure t + :defer 10 + :config + (make-directory "~/.org-jira" 'parents) + (setq jiralib-url (auth-source-pass-get "url" "customer/jira")) + (jiralib-login + (auth-source-pass-get "user" "customer/jira") + (auth-source-pass-get 'secret "customer/jira"))) +#+END_SRC + +*** Denote + +#+BEGIN_SRC emacs-lisp + ;; configure denote + (use-package denote + :ensure t + :config (progn + (setq denote-directory (file-name-concat (expand-file-name "~") "Dropbox/denote")) + (setq denote-dired-directories + (list denote-directory + (expand-file-name "~/Documents/denote")))) + :hook (dired-mode . denote-dired-mode-in-directories) + :bind ( + (" n d" . (lambda () (interactive) (dired denote-directory))) + (" n n" . #'denote) + (" n N" . #'denote-type) + (" n c" . #'denote-link-or-create) + (" n t" . #'denote-template) + (" n z" . #'denote-signature) + (" n l" . #'denote-link) + (" n L" . #'denote-find-link) + (" n k" . #'denote-keywords-add) + (" n b" . #'denote-link-backlink) + (" n B" . #'denote-find-backlink) + (" n r" . #'denote-rename-file) + (" n R" . #'denote-rename-file-using-front-matter) + (" n f" . (lambda () (interactive) (consult-find denote-directory))))) +#+END_SRC + +***** TODO explain what denote-dired-mode-in-directories does. + +** Programming +*** Programming Support Infrastructure +**** Integration with LSP Servers for language support +#+BEGIN_SRC emacs-lisp + ;; configure eglot-mode + (use-package eglot + :config + (evil-define-key 'normal eglot-mode-map + (kbd "c a") 'eglot-code-actions + (kbd "c d") 'eglot-find-declaration + (kbd "c i") 'eglot-find-implementation + (kbd "c k") 'eglot-find-typeDefinition + (kbd "c f") 'eglot-format + (kbd "c F") 'eglot-format-buffer + (kbd "c r") 'eglot-rename + (kbd "c Q") 'eglot-shutdown + (kbd "c q") 'eglot-reconnect + (kbd "c n") 'flymake-goto-next-error + (kbd "c p") 'flymake-goto-prev-error) + + ;; Shutdown server when last managed buffer is killed + (setq eglot-autoshutdown t) + + ;; from https://www.reddit.com/r/emacs/comments/ye18nd/setting_up_eglot_for_python/ + (add-to-list 'eglot-server-programs '(python-mode . ("pylsp"))) + + (setq-default eglot-workspace-configuration + '((:pylsp . (:configurationSources ["flake8"] + :plugins (:pycodestyle (:enabled nil) + :mccabe (:enabled nil) + :flake8 (:enabled t)))))) + + :hook + (go-ts-mode . eglot-ensure) + (rust-ts-mode . eglot-ensure) + (java-ts-mode . eglot-ensure) + (python-ts-mode . eglot-ensure) + ) + +#+END_SRC + +**** Use Treesitter parser support +#+BEGIN_SRC emacs-lisp + + ;; set locations for treesitter grammars + (use-package treesit + :config + (setq treesit-language-source-alist + '((bash "https://github.com/tree-sitter/tree-sitter-bash") + (cmake "https://github.com/uyha/tree-sitter-cmake") + (css "https://github.com/tree-sitter/tree-sitter-css") + (elisp "https://github.com/Wilfred/tree-sitter-elisp") + (go "https://github.com/tree-sitter/tree-sitter-go") + (gomod "https://github.com/camdencheek/tree-sitter-go-mod.git") + (haskell "https://github.com/tree-sitter/tree-sitter-haskell" "master" "src" nil nil) + (html "https://github.com/tree-sitter/tree-sitter-html") + (java "https://github.com/tree-sitter/tree-sitter-java.git") + (javascript "https://github.com/tree-sitter/tree-sitter-javascript" "master" "src") + (json "https://github.com/tree-sitter/tree-sitter-json") + (lua "https://github.com/tjdevries/tree-sitter-lua") + (make "https://github.com/alemuller/tree-sitter-make") + (markdown "https://github.com/ikatyang/tree-sitter-markdown") + (ocaml "https://github.com/tree-sitter/tree-sitter-ocaml" nil "ocaml/src") + (python "https://github.com/tree-sitter/tree-sitter-python") + (toml "https://github.com/tree-sitter/tree-sitter-toml") + (tsx "https://github.com/tree-sitter/tree-sitter-typescript" "master" "tsx/src") + (typescript "https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src") + (yaml "https://github.com/ikatyang/tree-sitter-yaml"))) + :hook + ((prog . treesit-inspect-mode))) + + (use-package treesit-auto + :ensure t + :defer 5 + :custom + (treesit-auto-install 'prompt) + :config + (treesit-auto-add-to-auto-mode-alist 'all) + (global-treesit-auto-mode)) +#+END_SRC + +***** Recompiling all Treesitter Grammars +To recompile all treesitter grammars, execute following block with +*C-c C-c*. +#+BEGIN_SRC emacs-lisp :tangle no + (mapc #'treesit-install-language-grammar (mapcar #'car treesit-language-source-alist)) +#+END_SRC + +#+RESULTS: +| bash | cmake | css | elisp | go | gomod | haskell | html | java | javascript | json | lua | make | markdown | ocaml | python | toml | tsx | typescript | yaml | + +***** Treesitter Grammars for Windows +Windows does not come with a handy-dandy C-compiler available as *cc* or *gcc* or even *c99* and treesitter does not have a handy dandy feature to remap that to *zig cc* and similar. + +However we can download it from the release page of the tree-sitter-langs repo or even better install the *tree-sitter-langs* package. + +This will download the dynamic library to _user-emacs-directory_/elpa/tree-sitter-langs/bin + +However they'll have the wrong filename and are not in the path where treesitter looks in. + +- Open the folder with *dired* +- switch to writable with `C-xC-q` to enable *wdired* +- :%s/[a-z-]\*.dll/libtree-sitter-\\0/ +- `C-cC-c` to confirm the changes if it looks ok +- `%m` to mark all files starting with libtree +- `R` them to the *~/.config/emacs/tree-sitter* folder + +and they will now be installed. + +Normally that should work for other OSes too, but there is no real need for it. + +see also [[https://www.masteringemacs.org/article/how-to-get-started-tree-sitter][How to get started with tree sitter]] . + +**** Create a folder to store downloaded LSP servers +#+BEGIN_SRC emacs-lisp + ;; LSP support + (let ((lsp_dir (file-name-concat user-emacs-directory "lsp"))) + (if (not (file-exists-p lsp_dir)) + (mkdir lsp_dir t))) +#+END_SRC +*** Configure Selected Languages +**** Rust Support +#+BEGIN_SRC emacs-lisp + + ;; configure rust support + (let* ((release-date "2023-10-30") + (os (pcase system-type + ('darwin "x86_64-apple-darwin") + ('gnu/linux "x86_64-unknown-linux-gnu") + ('windows-nt "x86_64-pc-windows-msvc") + (_ "unknown"))) + (releases-url "https://github.com/rust-lang/rust-analyzer/releases/download/") + (download-url (concat releases-url release-date "/rust-analyzer-" os ".gz")) + (rust-analyzer + (file-name-concat + user-emacs-directory + (concat "lsp/rust-analyzer" (if (eq system-type 'windows-nt) ".exe" ""))))) + (if (not (file-exists-p rust-analyzer)) + (let ((rust-analyzer-archive (concat rust-analyzer ".gz" ))) + (message "install rust-analyzer from %s at %s" download-url rust-analyzer) + (url-copy-file download-url rust-analyzer-archive t) + (call-process "gzip" nil "*snam-install*" t "-d" (concat rust-analyzer ".gz")) + (call-process "chmod" nil "*snam-install*" t "+x" rust-analyzer) + (message "rust-analyzer installed at %s" rust-analyzer))) + (with-eval-after-load 'eglot + (add-to-list 'eglot-server-programs `((rust-mode) rust-analyzer)))) + + (use-package rustic + :ensure t + :init + (setq rustic-lsp-client 'eglot)) +#+END_SRC +**** OCaml Support +#+BEGIN_SRC emacs-lisp + ;; configure Ocaml support + (setq opam-emacs-dir (file-name-concat (expand-file-name "~") "/.opam/default/share/emacs/site-lisp")) + (use-package ocp-indent + :ensure t + :load-path opam-emacs-dir + :if (file-exists-p opam-emacs-dir)) + (use-package tuareg + :ensure t + :hook + (tuareg-mode . eglot-ensure)) +#+END_SRC +**** Go Support +#+BEGIN_SRC emacs-lisp :tangle no + ;; configure go support + (use-package go-ts-mode + :init + (add-to-list 'auto-mode-alist '("\\.go\\'" . go-ts-mode)) + :hook + (go-ts-mode . eglot-ensure)) +#+END_SRC +**** Javascript, Typescript, TSC, etc... +#+BEGIN_SRC emacs-lisp :tangle no + ;; configure typescript support + (use-package typescript-ts-mode + :init + (add-to-list 'auto-mode-alist '("\\.ts\\'" . typescript-ts-mode)) + :hook + (typescript-ts-mode . eglot-ensure)) + + ;; configure javascript support + (use-package javascript-ts-mode + :init + (add-to-list 'auto-mode-alist '("\\.js\\'" . js-ts-mode)) + :hook + (javascript-ts-mode . eglot-ensure)) + + ;; configure react support + (use-package tsx-ts-mode + :init + (add-to-list 'auto-mode-alist '("\\.[jt]sx\\'" . tsx-ts-mode)) + :hook + (tsx-ts-mode . eglot-ensure)) +#+END_SRC +**** Java and other JVM languages +#+BEGIN_SRC emacs-lisp + ;; configure java support + (use-package java-ts-mode + :hook + (javascript-ts-mode . eglot-ensure) + :config + (let* ((download-url"https://www.eclipse.org/downloads/download.php?file=/jdtls/snapshots/jdt-language-server-latest.tar.gz") + (jdtls-dir (file-name-concat user-emacs-directory "lsp/jdtls")) + (jdtls-prog (concat jdtls-dir "/bin/jdtls" (if (eq system-type 'windows-nt) ".bat" ""))) + (archive (file-name-concat jdtls-dir "jdtls.tgz"))) + (if (not (file-exists-p jdtls-dir)) + (mkdir jdtls-dir t)) + (if (not (file-exists-p jdtls-prog)) + (progn + (message "install jdtls at %s" jdtls-dir) + (if (not (file-exists-p archive)) + (url-copy-file download-url archive)) + (call-process "tar" nil "*snam-install*" t "-C" jdtls-dir "-xzvf" archive ) + (message "jdtls installed at %s" jdtls-prog))) + (with-eval-after-load 'eglot + (progn + (setenv "JAVA_HOME" (getenv "GUIX_PROFILE")) + (add-to-list 'eglot-server-programs `((java-mode java-ts-mode) ,jdtls-prog)))))) + +#+END_SRC +**** Lisp and Scheme Support +***** Enable ParEdit in lispy modes + +#+BEGIN_SRC emacs-lisp + ;; Lisp support + (use-package paredit + :ensure nil + :commands (enable-paredit-mode evil-paredit-mode) + :init + (dolist (mode '(emacs-lisp-mode-hook + lisp-interaction-mode-hook + lisp-mode-hook + scheme-mode-hook)) + (add-hook mode #'enable-paredit-mode))) + (use-package evil-paredit + :ensure t + :commands (evil-paredit-mode) + :init + (dolist (mode '(emacs-lisp-mode-hook + lisp-interaction-mode-hook + lisp-mode-hook + scheme-mode-hook)) + (add-hook mode #'evil-paredit-mode))) + +#+END_SRC + +#+RESULTS: + +***** Enable Geiser Mode in Scheme Mode + +Configure Geiser and Scheme + - map .scm file by default to Guile + - configure chicken scheme interpreter and compiler + +#+BEGIN_SRC emacs-lisp + (use-package geiser + :ensure nil + :defer t + :commands (geiser-mode) + :config + ;; chicken-install -s srfi-18 apropos chicken-doc + (setq-default geiser-chicken-binary "csi") + (setq-default flycheck-scheme-chicken-executable "csc") + ;; configure geiser to assume guile for .scm files + (setq geiser-implementation-alist + ,(add-to-list 'geiser-implementation-alist '((regexp "\\.scm\\'") guile)))) + (use-package scheme-mode + :ensure nil + :defer t + :commands (scheme-mode) + :hook (scheme-mode . geiser-mode)) +#+END_SRC + +#+RESULTS: + +**** Terraform Support +#+BEGIN_SRC emacs-lisp + ;; configure terraform support + (use-package terraform-mode + :ensure t + :config + (setq + terraform-indent-level 2 + terraform-format-on-save t) + (evil-define-key 'normal terraform-mode-map + (kbd "c k") #'terraform-open-doc + (kbd "c f") #'terraform-format + (kbd "c F") #'terraform-format-buffer + (kbd "c n") 'flymake-goto-next-error + (kbd "c p") 'flymake-goto-prev-error) + :hook + (terraform-mode . (lambda () (outline-minor-mode 1))) + :bind) +#+END_SRC + +#+RESULTS: +: pti-ide-config + +*** Debugger Support +#+BEGIN_SRC emacs-lisp + ;; install DAP servers + (setq snam-vscode-js-debug-dir (file-name-concat user-emacs-directory "dape/vscode-js-debug")) + (defun snam-install-vscode-js-debug () + "Run installation procedure to install JS debugging support." + (interactive) + (mkdir snam-vscode-js-debug-dir t) + (let ((default-directory (expand-file-name snam-vscode-js-debug-dir))) + + (vc-git-clone "https://github.com/microsoft/vscode-js-debug.git" "." nil) + (message "git repository created") + (call-process "npm" nil "*snam-install*" t "install") + (message "npm dependencies installed") + (call-process "npx" nil "*snam-install*" t "gulp" "dapDebugServer") + (message "vscode-js-debug installed"))) + + (setq snam-codelldb-dir (file-name-concat user-emacs-directory "dape/codelldb")) + (defun snam-install-codelldb () + "Install Vadimcn.Vscode-Lldb DAP server for C/C++/RUST." + (interactive) + (let* ((default-directory snam-codelldb-dir) + (arch (car (split-string system-configuration "-" nil nil))) + (os (pcase system-type + ('windows-nt "windows") + ('gnu/linux "linux") + ('darwin "darwin") + (_ "unknown"))) + (version "1.10.0") + (release-url (concat "https://github.com/vadimcn/codelldb/releases/download/v" version "/codelldb-" arch "-" os ".vsix"))) + (mkdir default-directory t) + (url-copy-file release-url "codelldb.zip" t) + (message "codelldb archive downloaded") + (call-process "unzip" nil "*snam-install*" t "codelldb.zip") + (message "codelldb installed") + )) + + ;; configure dape (dap-mode) + (use-package dape + :ensure (dape :host github :repo "svaante/dape" :wait t) + :config (progn + ;; Use n for next etc. in REPL + ;; (setq dape-repl-use-shorthand t) + + ;; By default dape uses gdb keybinding prefix + ;; (setq dape-key-prefix "d") + (evil-define-key 'normal 'global (kbd "d") dape-global-map) + + ;; Kill compile buffer on build success + ;; (add-hook 'dape-compile-compile-hooks 'kill-buffer) + + ;; Projectile users + ;; (setq dape-cwd-fn 'projectile-project-root)) + (add-to-list 'dape-configs + `(vscode-js-node + modes (js-mode js-ts-mode typescript-mode typescript-ts-mode) + host "localhost" + port 8123 + command "node" + command-cwd ,(file-name-concat snam-vscode-js-debug-dir "dist") + command-args ("src/dapDebugServer.js" "8123") + :type "pwa-node" + :request "launch" + :cwd dape-cwd-fn + :program dape-find-file-buffer-default + :outputCapture "console" + :sourceMapRenames t + :pauseForSourceMap nil + :enableContentValidation t + :autoAttachChildProcesses t + :console "internalConsole" + :killBehavior "forceful")) + (add-to-list 'dape-configs + `(delve + modes (go-mode go-ts-mode) + command "dlv" + command-args ("dap" "--listen" "127.0.0.1:55878") + command-cwd dape-cwd-fn + host "127.0.0.1" + port 55878 + :type "debug" ;; needed to set the adapterID correctly as a string type + :request "launch" + :cwd dape-cwd-fn + :program dape-cwd-fn)) + (add-to-list 'dape-configs + `(codelldb + modes (c-mode c-ts-mode + c++-mode c++-ts-mode + rust-ts-mode rust-mode) + ;; Replace vadimcn.vscode-lldb with the vsix directory you just extracted + command ,(expand-file-name + (file-name-concat + snam-codelldb-dir + (concat "extension/adapter/codelldb" + (if (eq system-type 'windows-nt) + ".exe" + "")))) + host "localhost" + port 5818 + command-args ("--port" "5818") + :type "lldb" + :request "launch" + :cwd dape-cwd-fn + :program dape-find-file)) + (add-to-list 'dape-configs + `(debugpy + modes (python-ts-mode python-mode) + command "python" + command-args ("-m" "debugpy.adapter") + :type "executable" + :request "launch" + :cwd dape-cwd-fn + :program dape-find-file-buffer-default)) + + )) + + +#+END_SRC + +#+RESULTS: + +*** Copilot Support +#+BEGIN_SRC emacs-lisp + (use-package copilot + :ensure (:host github :repo "zerolfx/copilot.el" + :branch "main" + :files ("dist" "*.el")) + :hook + (prog-mode . copilot-mode)) +#+END_SRC + +#+RESULTS: +: [nil 26279 35677 893347 nil elpaca-process-queues nil nil 951000 nil] + +**** TODO move scheme configuration to the scheme section +or leave it here but move it to config, whatever makes most sense at +the moment. +*** Flymake Error Diagnostics +#+BEGIN_SRC emacs-lisp + (evil-define-key 'normal #'eglot-mode-map + (kbd "]d") #'flymake-goto-next-error + (kbd "[d") #'flymake-goto-prev-error + ) +#+END_SRC + +*** Electric Return +#+BEGIN_SRC emacs-lisp + ;; enable electric return to automatically indent code + (defvar electrify-return-match + "[\]}\)\"]" + "The text after the cursor to do an \"electric\" return.") + + (defun electrify-return-if-match (arg) + "Insert a newline, and indent it when needed. + + If the text after the cursor matches `electrify-return-match' then + open and indent an empty line between the cursor and the text. Move the + cursor to the new line. + + With a prefix argument ARG, insert that many newlines" + (interactive "P") + (let ((case-fold-search nil)) + (if (looking-at electrify-return-match) + (save-excursion (newline-and-indent))) + (newline arg) + (indent-according-to-mode))) + + ;; Using local-set-key in a mode-hook is a better idea. + ;(global-set-key (kbd "RET") 'electrify-return-if-match) +#+END_SRC + + +**** TODO Evaluate if Electric Return is still useful + +** Communication and Interwebs +*** ERC configuration +Set up ERC, an IRC client for Emacs, to automatically join specific channels and log in: +#+BEGIN_SRC emacs-lisp + (use-package erc + :ensure t + :defer 5 + :config + (erc-services-mode 1) + (erc-autojoin-mode 1) + (erc-update-modules) + (setq erc-autojoin-channels-alist + '(('znc + "#emacs" "#erc" "#spritely" "#guix" + "#systemcrafters" "#systemcrafters-guix" "#systemcrafters-emacs")))) +#+END_SRC + +#+RESULTS: +: t + +Setup all channels which are joined by default. + + +**** ERC connect function + +Define a function to login to ERC when needed. In principle ERC +reconnects after suspend, and sometimes it even works, but mostly I +run this function again to reconnect, which will update the buffers +with the rooms I joined. + +#+BEGIN_SRC emacs-lisp + (defun snam-erc () + "Login to znc bouncer and join rooms." + (interactive) + (erc-tls :id 'znc + :server (auth-source-pass-get "server" "snamellit/znc") + :port (auth-source-pass-get "port" "snamellit/znc") + :user (auth-source-pass-get "user" "snamellit/znc") + :nick (auth-source-pass-get "nick" "snamellit/znc") + :password (auth-source-pass-get 'secret "snamellit/znc"))) +#+END_SRC + +#+RESULTS: +: snam-erc + +In practice this has proven to be a very useful function to reconnect +in all kind of weird situation. + +***** TODO Figure out how to make ERC reconnect more reliably +Although running `snam-erc` is not a big deal, it should not be needed +so much. I should figure out in what cases reconnects fail or dropping +connections fail to be recognized. +I often see that ERC is _reconnecting_ however this seems to silently +fail. I dunno, there is something fishy here... + +**** Integrate ERC with i3 Desktops + +I like to have my IRC channels on workspace 5 in *i3*. + +#+BEGIN_SRC emacs-lisp + (defun my-erc () + "Create new frame and move to ws5 and launch erc." + (interactive) + (let ((frame (make-frame)) + (channel "#systemcrafters")) + (call-process "i3" nil nil nil "move window to workspace 5") + (snam-erc) + (call-process "i3" nil nil nil "workspace 5") + (message "Waiting to connect to IRC...") + (dotimes (i 12) + (unless (member channel (mapcar 'buffer-name (erc-channel-list nil))) + (sleep-for 0.25))) + (set-window-buffer + (frame-selected-window frame) channel))) +#+END_SRC + +#+RESULTS: +: my-erc + + +*** Elfeed configuration +Configures Elfeed, an RSS feed reader for Emacs: +#+BEGIN_SRC emacs-lisp + ;; configure elfeed + (use-package elfeed + :ensure t + :defer 5 + :config + (setq elfeed-feeds + '("https://www.spritelyproject.org/feed.xml" + "https://www.emacswiki.org/emacs?action=rss" + ;; "https://www.reddit.com/r/emacs.rss" + ;; "https://www.reddit.com/r/guix.rss" + ;; "https://www.reddit.com/r/linux.rss" + "https://sachachua.com/blog/feed/" + "https://blog.benoitj.ca/posts/index.xml" + "https://tylerdback.com/index.xml" + "https://www.snamellit.com/rss.xml" + "https://richarddavis.xyz/en/blog/rss.xml" + "https://protesilaos.com/master.xml" + ))) +#+END_SRC + +#+RESULTS: +: [nil 26279 35504 577569 nil elpaca-process-queues nil nil 161000 nil] + + +*** Enable 0x0 to share snippets and files easily +Configures the ~0x0~ package for easily sharing code snippets and files: +#+BEGIN_SRC emacs-lisp + ;; configure 0x0 + (use-package 0x0 + :ensure t + :config (setq 0x0-use-curl t)) +#+END_SRC + +#+RESULTS: +: [nil 26279 35497 183387 nil elpaca-process-queues nil nil 963000 nil] + +The commands this package offers: + +- ~0x0-dwim~ chooses the right one of the following, based on location or previous commands. +- ~0x0-upload-text~ will upload the active region or if not existent the entire buffer +- ~0x0-upload-file~ will upload a selected file +- ~0x0-upload-kill-ring~ uploads the content of the kill ring +- ~0x0-popup~ creates and displays a buffer, lets you upload its contents later +- ~0x0-shorten-uri~ prompts for a uri, shortens it + + IMSMR the shorten-uri functionality is disabled in the 0x0 service + as apparently is was abused too much. + +* Finishing Touches +** Tell the module system that *init.el* is available +Indicates the ~init.el~ file is provided by and ends here: +#+BEGIN_SRC emacs-lisp + (provide 'init) + ;;; init.el ends here +#+END_SRC +