emacs-config/init.org

306 KiB
Raw Blame History

My Emacs Configuration

Literate Configuration for Emacs

Goals

  • Manage init.el as an org-file
  • 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

Notes on Elpaca and dev versions of emacs.

Elpaca needs the build date of emacs to compare to package versions or something. However it does not support all dev versions.

For guix emacs-next packages you can find the date with: ( <C-c C-c> in the source block below:

stat /gnu/store/*emacs-next-[23]*.drv | rg Birth | cut -d' ' -f3 | tr -d '-'
20240727
20240727

It is possible there are more so probably the most recent one is the one to use.

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.

  ;; init.el --- Literate configuration for Emacs -*- lexical-binding: t; read-only-mode: t; -*-
  ;;
  ;;; Commentary:
  ;;
  ;; DO NOT EDIT!!!
  ;;
  ;; This file is automatically generated from the source in *init.org*.
  ;;

Also immediately set lexical binding mode.

Global Configuration

  (setq snm-docker-executable 'podman) ;; use docker or podman

  (use-package org
    :ensure t)

First Things First

Bootstrapping Emacs Configuration

  ;; early-init.el --- Bootstrap Emacs -*- lexical-binding: t; read-only-mode: t; -*-

  ;;; Bootstrap elpaca
  (setq package-enable-at-startup nil)

  ;; for guix emacs-next packages you can find the date with
  ;; ➜ stat /gnu/store/*emacs-next-[23]*.drv | rg Birth | cut -d' ' -f3 | tr -d '-'
  ;; 20240727
  ;;
  ;; it is possible there are more so probably the most recent one is the one to use.

  (when emacs-build-time
  (setq elpaca-core-date (format-time-string "%Y%m%d" emacs-build-time)))

    
  (defvar elpaca-installer-version 0.11)
  (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 :inherit ignore
                                :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)
      (let ((load-source-file-function nil)) (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))

Tangle the init file if it has been updated. I maintain the same configuration on multiple machines and fetch changes with a git pull outside emacs so there is on opportunity to tangle the new file. If I see it is outdated I tangle it.

;; 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"))
      (enable-local-variables nil))   ;; disable local variables to prevent asking confirmation before frame is available
  (when (file-newer-than-file-p src tgt)
    (message "tangling init.org")
    (delete-file tgt)
    (org-babel-tangle-file src tgt "emacs-lisp")))

Set the garbage collector threshold, to avoid collections

The emacs history goes back to times where memory was counted in bytes, not gigabytes. We can afford to be a bit more generous with the garbage collector settings.

(setq gc-cons-percentage 0.5
      gc-cons-threshold (* 128 1024 1024))

Report time spent loading the configuration

  (defconst emacs-start-time (current-time))

  (defun report-time-since-load (&optional suffix)
    "Report the time since the file was init script was started.

  If SUFFIX is provided, it is appended to the message."
    (message "%.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)
  (report-time-since-load "start init file")

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.

  ;;; Code:
  (setq custom-file (concat user-emacs-directory "custom.el"))
  (when (and custom-file
             (file-exists-p custom-file))
    (load custom-file nil :nomessage))

Load transient as used by a lot of packages

  (use-package transient
    :ensure t)
[nil 26760 24522 367252 nil elpaca-process-queues nil nil 826000 nil]

Wait for initial installations

  (elpaca-wait)

Start Emacs Servers

To get a fast snappy editing experience in terminals emacs allows running in client-server where the actual editing runs in an already running process. This enables interaction with the other buffers in the context/project and more importantly the client does not need to load all the customizations in the init file. However the server must be started for this to work.

If a server is already running it will be restarted. If clients are connected to that server, confirmation will be asked. see server-start documentation .

  (server-start)

Utility Functions

Reload dir local variables

(defun snm-reload-dir-locals-for-current-buffer ()
  "Reload dir locals for the current buffer."
  (interactive)
  (let ((enable-local-variables :all))
    (hack-dir-local-variables-non-file-buffer)))
snm-reload-dir-locals-for-current-buffer

Get latest version of a github released project

Many projects nowadays use github to release their software. However there is no easy way to get the latest version of a project provided. This functions uses the releases API to get the latest metadata and get the version number from the JSON.

  (require 'url)
  (defun snm-latest-github-release (repo)
    "Return the latest version of the releases for REPO.

              The repo should be in the form of `owner/repo'."
    (with-temp-buffer
        (url-insert-file-contents
         (format "https://api.github.com/repos/%s/releases/latest" repo))
      (let ((result (json-read)))
        (cdr (assoc 'name result)))))
snm-latest-github-release
  (snm-latest-github-release "plantuml/plantuml")
v1.2025.1

Integration with Environment

(report-time-since-load "Integration with Environment")

Set default Coding System to Use UTF-8 Everywhere

Ensures UTF-8 is the default coding system everywhere.

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

Heres 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.

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

Setup backup directories and other file saving settings

Configures where backup files are stored:

  ;; setup backup directories
  ;; see https://www.emacswiki.org/emacs/BackupDirectory
  (setq backup-directory-alist
   `(("." . ,(file-name-concat user-emacs-directory "backups"))))
((. . /home/pti/.config/emacs/backups))

To avoid losing files when deleting in dired, move them to the trash:

  (setq delete-by-moving-to-trash t)

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.

For setups with GnuPG >= 2.1, pinentry package is not needed anymore.

Quote from the Emacs News.26 file:

The pinentry.el library has been removed.

That package (and the corresponding change in GnuPG and pinentry) was intended to provide a way to input passphrase through Emacs with GnuPG 2.0. However, the change to support that was only implemented in GnuPG >= 2.1 and didn't get backported to GnuPG 2.0. And with GnuPG 2.1 and later, pinentry.el is not needed at all. So the library was useless, and we removed it. GnuPG 2.0 is no longer supported by the upstream project.

To adapt to the change, you may need to set 'epa-pinentry-mode' to the symbol 'loopback'. Alternatively, leave 'epa-pinentry-mode' at its default value of nil, and remove the 'allow-emacs-pinentry' setting from your 'gpg-agent.conf' configuration file, usually found in the '~/.gnupg' directory.

Note that previously, it was said that passphrase input through minibuffer would be much less secure than other graphical pinentry programs. However, these days the difference is insignificant: the 'read-password' function sufficiently protects input from leakage to message logs. Emacs still doesn't use secure memory to protect passphrases, but it was also removed from other pinentry programs as the attack is unrealistic on modern computer systems which don't utilize swap memory usually.

See also a discussion on why pinentry was removed from Emacs core.

So a setup may now consist of:

In Emacs' user-init-file:

(require 'epg)
(setq epg-pinentry-mode 'loopback)

In ~/.gnupg/gpg-agent.conf:

allow-emacs-pinentry
# on Mac OS
pinentry-program /usr/local/bin/pinentry-mac

Enable pass secrets

  (auth-source-pass-enable)

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.

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 :

(auth-source-pass-get 'secret "dummy/password")
shht!secret

Major mode to edit pass keychain

  (use-package pass
    :ensure t)
[nil 26721 10916 917127 nil elpaca-process-queues nil nil 78000 nil]

Add a fully featured terminal emulator

  (use-package eat
    :commands eat
    :ensure (eat
  	   :host codeberg
  	   :repo "akib/emacs-eat"
  	   :files ("*.el" ("term" "term/*.el") "*.texi"
  		   "*.ti" ("terminfo/e" "terminfo/e/*")
  		   ("terminfo/65" "terminfo/65/*")
  		   ("integration" "integration/*")
  		   (:exclude ".dir-locals.el" "*-tests.el"))))
[nil 26538 15682 653403 nil elpaca-process-queues nil nil 807000 nil]

Enable vterm

  (use-package vterm
    :ensure t
    :commands vterm)
[nil 26557 35507 493630 nil elpaca-process-queues nil nil 948000 nil]

Docker Integration

(use-package docker
	:defer t
	:ensure t
	:bind ("C-c d" . docker)
	:config
	(pcase snm-docker-executable
	  ('docker
	   (setf docker-command "docker"
			 docker-compose-command "docker-compose"
			 docker-container-tramp-method "docker"))
	  ('podman
	   (setf docker-command "podman"
			 docker-compose-command "podman-compose"
			 docker-container-tramp-method "podman"))))
[nil 26557 44955 396008 nil elpaca-process-queues nil nil 436000 nil]

Editor Features

(report-time-since-load "Editor Features")

Emacs configuration

;; A few more useful configurations...
(use-package emacs
  :custom
  ;; Support opening new minibuffers from inside existing minibuffers.
  (enable-recursive-minibuffers t)
  ;; Hide commands in M-x which do not work in the current mode.  Vertico
  ;; commands are hidden in normal buffers. This setting is useful beyond
  ;; Vertico.
  (read-extended-command-predicate #'command-completion-default-include-p)
  :init
  ;; Add prompt indicator to `completing-read-multiple'.
  ;; We display [CRM<separator>], e.g., [CRM,] if the separator is a comma.
  (defun crm-indicator (args)
    (cons (format "[CRM%s] %s"
                  (replace-regexp-in-string
                   "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
                   crm-separator)
                  (car args))
          (cdr args)))
  (advice-add #'completing-read-multiple :filter-args #'crm-indicator)

  ;; Do not allow the cursor in the minibuffer prompt
  (setq minibuffer-prompt-properties
        '(read-only t cursor-intangible t face minibuffer-prompt))
  (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode))

Save history over sessions

Persist history over Emacs restarts. Vertico sorts by history position.

  (use-package savehist
    :init
    (savehist-mode))

Completion Configuration

Use Vertico for better selection lists

Vertico is a big package by minad. Well documented in the vertico github repo.

  ;; Enable vertico
  (use-package vertico
    :ensure t
    :custom
    (vertico-scroll-margin 0) ;; Different scroll margin
    (vertico-count 12) ;; Show more candidates
    (vertico-resize t) ;; Grow and shrink the Vertico minibuffer
    (vertico-cycle t) ;; Enable cycling for `vertico-next/previous'
    :init
    (vertico-mode))

Orderless for better narrowing

  (use-package orderless
    :ensure t
    :custom
    ;; Configure a custom style dispatcher (see the Consult wiki)
    ;; (orderless-style-dispatchers '(+orderless-consult-dispatch orderless-affix-dispatch))
    ;; (orderless-component-separator #'orderless-escapable-split-on-space)
    (completion-styles '(orderless basic))
    ;;(completion-category-defaults nil)
    (completion-category-overrides '((file (styles partial-completion)))))

Marginalia for better context awareness

  ;; Enable rich annotations using the Marginalia package
  (use-package marginalia
    :ensure t
    
    ;; Bind `marginalia-cycle' locally in the minibuffer.  To make the binding
    ;; available in the *Completions* buffer, add it to the
    ;; `completion-list-mode-map'.
    :bind (:map minibuffer-local-map ("M-A" . marginalia-cycle))

    :init
    (marginalia-mode))
marginalia-cycle

Add consult

See the excellent documentation on Minad's consult repo.

    ;; Example configuration for Consult
  (use-package consult
    :ensure t
    ;; Replace bindings. Lazily loaded by `use-package'.
    :bind (;; C-c bindings in `mode-specific-map'
           ("C-c M-x" . consult-mode-command)
           ("C-c h" . consult-history)
           ("C-c k" . consult-kmacro)
           ("C-c m" . consult-man)
           ("C-c i" . consult-info)
           ([remap Info-search] . consult-info)
           ;; C-x bindings in `ctl-x-map'
           ("C-x M-:" . consult-complex-command)     ;; orig. repeat-complex-command
           ("C-x b" . consult-buffer)                ;; orig. switch-to-buffer
           ("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window
           ("C-x 5 b" . consult-buffer-other-frame)  ;; orig. switch-to-buffer-other-frame
           ("C-x t b" . consult-buffer-other-tab)    ;; orig. switch-to-buffer-other-tab
           ("C-x r b" . consult-bookmark)            ;; orig. bookmark-jump
           ("C-x p b" . consult-project-buffer)      ;; orig. project-switch-to-buffer
           ;; Custom M-# bindings for fast register access
           ("M-#" . consult-register-load)
           ("M-'" . consult-register-store)          ;; orig. abbrev-prefix-mark (unrelated)
           ("C-M-#" . consult-register)
           ;; Other custom bindings
           ("M-y" . consult-yank-pop)                ;; orig. yank-pop
           ;; M-g bindings in `goto-map'
           ("M-g e" . consult-compile-error)
           ("M-g f" . consult-flymake)               ;; Alternative: consult-flycheck
           ("M-g g" . consult-goto-line)             ;; orig. goto-line
           ("M-g M-g" . consult-goto-line)           ;; orig. goto-line
           ("M-g o" . consult-outline)               ;; Alternative: consult-org-heading
           ("M-g m" . consult-mark)
           ("M-g k" . consult-global-mark)
           ("M-g i" . consult-imenu)
           ("M-g I" . consult-imenu-multi)
           ;; M-s bindings in `search-map'
           ("M-s d" . consult-find)                  ;; Alternative: consult-fd
           ("M-s c" . consult-locate)
           ("M-s g" . consult-grep)
           ("M-s G" . consult-git-grep)
           ("M-s r" . consult-ripgrep)
           ("M-s l" . consult-line)
           ("M-s L" . consult-line-multi)
           ("M-s k" . consult-keep-lines)
           ("M-s u" . consult-focus-lines)
           ;; Isearch integration
           ("M-s e" . consult-isearch-history)
           :map isearch-mode-map
           ("M-e" . consult-isearch-history)         ;; orig. isearch-edit-string
           ("M-s e" . consult-isearch-history)       ;; orig. isearch-edit-string
           ("M-s l" . consult-line)                  ;; needed by consult-line to detect isearch
           ("M-s L" . consult-line-multi)            ;; needed by consult-line to detect isearch
           ;; Minibuffer history
           :map minibuffer-local-map
           ("M-s" . consult-history)                 ;; orig. next-matching-history-element
           ("M-r" . consult-history))                ;; orig. previous-matching-history-element

    ;; Enable automatic preview at point in the *Completions* buffer. This is
    ;; relevant when you use the default completion UI.
    :hook (completion-list-mode . consult-preview-at-point-mode)

    ;; The :init configuration is always executed (Not lazy)
    :init

    ;; Optionally configure the register formatting. This improves the register
    ;; preview for `consult-register', `consult-register-load',
    ;; `consult-register-store' and the Emacs built-ins.
    (setq register-preview-delay 0.5
          register-preview-function #'consult-register-format)

    ;; Optionally tweak the register preview window.
    ;; This adds thin lines, sorting and hides the mode line of the window.
    (advice-add #'register-preview :override #'consult-register-window)

    ;; Use Consult to select xref locations with preview
    (setq xref-show-xrefs-function #'consult-xref
          xref-show-definitions-function #'consult-xref)

    ;; Configure other variables and modes in the :config section,
    ;; after lazily loading the package.
    :config

    ;; Optionally configure preview. The default value
    ;; is 'any, such that any key triggers the preview.
    ;; (setq consult-preview-key 'any)
    ;; (setq consult-preview-key "M-.")
    ;; (setq consult-preview-key '("S-<down>" "S-<up>"))
    ;; For some commands and buffer sources it is useful to configure the
    ;; :preview-key on a per-command basis using the `consult-customize' macro.
    (consult-customize
     consult-theme :preview-key '(:debounce 0.2 any)
     consult-ripgrep consult-git-grep consult-grep
     consult-bookmark consult-recent-file consult-xref
     consult--source-bookmark consult--source-file-register
     consult--source-recent-file consult--source-project-recent-file
     ;; :preview-key "M-."
     :preview-key '(:debounce 0.4 any))

    ;; Optionally configure the narrowing key.
    ;; Both < and C-+ work reasonably well.
    (setq consult-narrow-key "<") ;; "C-+"

    ;; Optionally make narrowing help available in the minibuffer.
    ;; You may want to use `embark-prefix-help-command' or which-key instead.
    ;; (keymap-set consult-narrow-map (concat consult-narrow-key " ?") #'consult-narrow-help)
  )
[nil 26434 3705 536018 nil elpaca-process-queues nil nil 266000 nil]

TODO Add consult-omni

Consult omni allows Alfred/Spotlight like universal searching among many sources : filesystem, web, wikipedia, LLMs, …

This requires quite a bit of setup and experimentation

for more details see the consult-omni github . There is a long in depth youtube video demoing and going deep in the setup.

  (use-package consult-omni
  :ensure (consult-omni :type git :host github :repo "armindarvish/consult-omni" :branch "main" :files (:defaults "sources/*.el"))
  :after consult
  :custom
   ;; General settings that apply to all sources
  (consult-omni-show-preview t) ;;; show previews
  (consult-omni-preview-key "C-o") ;;; set the preview key to C-o
  :config
  ;; Load Sources Core code
  (require 'consult-omni-sources)
  ;; Load Embark Actions
  (require 'consult-omni-embark)

  ;; Either load all source modules or a selected list

  ;;; Select a list of modules you want to aload, otherwise all sources all laoded
  ; (setq consult-omni-sources-modules-to-load (list 'consult-omni-wkipedia 'consult-omni-notes))
  (consult-omni-sources-load-modules)
  ;;; set multiple sources for consult-omni-multi command. Change these lists as needed for different interactive commands. Keep in mind that each source has to be a key in `consult-omni-sources-alist'.
  (setq consult-omni-multi-sources '("calc"
                                     ;; "File"
                                     ;; "Buffer"
                                     ;; "Bookmark"
                                     "Apps"
                                     ;; "gptel"
                                     "Brave"
                                     "Dictionary"
                                     ;; "Google"
                                     "Wikipedia"
                                     "elfeed"
                                     ;; "mu4e"
                                     ;; "buffers text search"
                                     "Notes Search"
                                     "Org Agenda"
                                     "GitHub"
                                     ;; "YouTube"
                                     "Invidious"))

;; Per source customization

  ;;; Set API KEYs. It is recommended to use a function that returns the string for better security.
  (setq consult-omni-google-customsearch-key "YOUR-GOOGLE-API-KEY-OR-FUNCTION")
  (setq consult-omni-google-customsearch-cx "YOUR-GOOGLE-CX-NUMBER-OR-FUNCTION")
  (setq consult-omni-brave-api-key "YOUR-BRAVE-API-KEY-OR-FUNCTION")
  (setq consult-omni-stackexchange-api-key "YOUR-STACKEXCHANGE-API-KEY-OR-FUNCTION")
  (setq consult-omni-pubmed-api-key "YOUR-PUBMED-API-KEY-OR-FUNCTION")
  (setq consult-omni-openai-api-key "YOUR-OPENAI-API-KEY-OR-FUNCTION")

;;; Pick you favorite autosuggest command.
  (setq consult-omni-default-autosuggest-command #'consult-omni-dynamic-brave-autosuggest) ;;or any other autosuggest source you define

 ;;; Set your shorthand favorite interactive command
  (setq consult-omni-default-interactive-command #'consult-omni-multi))
[nil 26745 28425 788184 nil elpaca-process-queues nil nil 528000 nil]
TODO Create Brave Search API key

to enable consult omni brave searches I need a brave API key. Here is the account registration page.yuy i u uuu u u iu u u

Frequently used File/Project Operations

  (keymap-global-set "C-c v" vc-prefix-map)
  (keymap-global-set  "C-c p" project-prefix-map)

  (recentf-mode 1) ;; enable recent file tracking
  (keymap-global-set  "C-c f f" #'find-file)
  (keymap-global-set  "C-c f o" #'recentf)
  (keymap-global-set  "C-c f r" #'revert-buffer)
  (keymap-global-set  "C-c f d" #'diff-buffer-with-file)

User Interface

Display startup time

;; Profile emacs startup
(add-hook 'emacs-startup-hook
          (lambda ()
            (message "Emacs started in %s."
                     (emacs-init-time))))

Configure Fonts with Fontaine

  ;;
  ;; Font configuration
  ;;

  (defun snm-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 snm-configure-fonts (&optional frame)
    "Set configuration of fonts based on display size.

  The FRAME argument makes it possible to set the fonts for new frames by
  adding this function to `after-make-frame-functions' which must have
  this argument."
    (defvar snm-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 ,(snm-find-installed-font
                               '("FiraCode NF" "FiraCode Nerd Font"))
             :default-weight regular
             :default-height ,snm-font-size
             :variable-pitch-family ,(snm-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
              (snm-configure-fonts)
              (add-hook 'after-make-frame-functions #'snm-configure-fonts)))

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.

  ;;
  ;; remove chrome from the frames
  ;; also support client frames
  ;;
  (defun snm-display-tweaks (&optional frame)
    "Configure a newly created FRAME."
    (interactive)
    (menu-bar-mode 1)
    (tool-bar-mode -1)
    (if (display-graphic-p)
      (scroll-bar-mode -1)))

  (add-hook 'after-make-frame-functions #'snm-display-tweaks)
  ;; run it in the current frame, because the hooks have already fired
  (snm-display-tweaks)

Set Theme

  ;; Set theme
  (use-package catppuccin-theme
    :ensure t
    :demand t
    :config
    (setq catppuccin-flavor 'mocha)
    (load-theme 'catppuccin t)
    (defun catppuccin-toggle ()
      (interactive)
      (setq catppuccin-flavor (if (eq catppuccin-flavor 'mocha) 'latte 'mocha))
      (catppuccin-reload)
      (message (format "Cattpuccin Flavor set to %s" catppuccin-flavor)))
    :bind
    (("<f5>" . #'catppuccin-toggle)))
  (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
    (("<f5>" . #'modus-themes-toggle)))

There is a keybinding on <F5> to toggle between light and dark mode.

modus-themes-toggle

EF Themes

  (use-package ef-themes
    :ensure t
    :init
    ;; If you like two specific themes and want to switch between them, you
  ;; can specify them in `ef-themes-to-toggle' and then invoke the command
  ;; `ef-themes-toggle'.  All the themes are included in the variable
  ;; `ef-themes-collection'.
  (setq ef-themes-to-toggle '(ef-day ef-night))

  (setq ef-themes-headings ; read the manual's entry or the doc string
        '((0 variable-pitch light 1.9)
          (1 variable-pitch light 1.8)
          (2 variable-pitch regular 1.7)
          (3 variable-pitch regular 1.6)
          (4 variable-pitch regular 1.5)
          (5 variable-pitch 1.4) ; absence of weight means `bold'
          (6 variable-pitch 1.3)
          (7 variable-pitch 1.2)
          (t variable-pitch 1.1)))

  ;; They are nil by default...
  (setq ef-themes-mixed-fonts t
        ef-themes-variable-pitch-ui t)
  :config
  ;; Disable all other themes to avoid awkward blending:
  (mapc #'disable-theme custom-enabled-themes)

  ;; load the theme which also calls `ef-themes-post-load-hook':
  (ef-themes-select 'ef-night)

  :bind
  ("<f5>" . #'ef-themes-toggle)
  
  )
[nil 26538 31881 746655 nil elpaca-process-queues nil nil 312000 nil]

Spacious Padding

  (use-package spacious-padding
    :ensure t
    :bind (("<f8>" . #'spacious-padding-mode)))
[nil 26538 30896 455747 nil elpaca-process-queues nil nil 302000 nil]

Limit Height of Selected Popup Windows

  (push '("\\*Occur\\*"
          ;; display-buffer functions, first one that succeeds is used
          (display-buffer-reuse-mode-window
           display-buffer-below-selected)
          ;; Parameters
          (window-height . 10))
        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 . 10))
        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 . 10))
        display-buffer-alist)
  (push '("magit:.*"
            ;; display-buffer functions, first one that succeeds is used
            (display-buffer-reuse-mode-window
             display-buffer-below-selected)
            ;; Parameters
            (window-height . 10))
          display-buffer-alist)
  (push '("\\*sly-mrepl for .*\\*"
            ;; display-buffer functions, first one that succeeds is used
            (display-buffer-reuse-mode-window
             display-buffer-below-selected)
            ;; Parameters
            (window-height . 10))
          display-buffer-alist)
  (add-to-list 'Info-default-directory-list "~/.local/share/info/")
~/.local/share/info/

a quick way to test this it to generate a warning with

  (warn "This is a warning")
t

Yasnippet configuration

Enable Yasnippet

Enables and configures Yasnippet, a template system for Emacs:

  ;; configure yasnippet
  (use-package yasnippet
    :ensure nil
    :defer 5
    :config
    (yas-global-mode 1)
    (add-to-list 'yas-snippet-dirs "~/src/guix/etc/snippets/yas"))
t

Add Snippet Collection

  ;; add yasnippet collection
  (use-package yasnippet-snippets
    :ensure t)
[nil 26481 25510 926111 nil elpaca-process-queues nil nil 690000 nil]

Programming

(report-time-since-load "Programming")

Programming Support Infrastructure

(report-time-since-load "Programming - Infrastructure")

Integration with LSP Servers for language support

  ;; configure eglot-mode
  (use-package eglot
    :config
    (keymap-set eglot-mode-map "C-c c a" #'eglot-code-actions)
    (keymap-set eglot-mode-map "C-c c d" #'eglot-find-declaration)
    (keymap-set eglot-mode-map "C-c c i" #'eglot-find-implementation)
    (keymap-set eglot-mode-map "C-c c k" #'eglot-find-typeDefinition)
    (keymap-set eglot-mode-map "C-c c f" #'eglot-format)
    (keymap-set eglot-mode-map "C-c c F" #'eglot-format-buffer)
    (keymap-set eglot-mode-map "C-c c r" #'eglot-rename)
    (keymap-set eglot-mode-map "C-c c Q" #'eglot-shutdown)
    (keymap-set eglot-mode-map "C-c c q" #'eglot-reconnect)
    (keymap-set eglot-mode-map "C-c c n" #'flymake-goto-next-error)
    (keymap-set eglot-mode-map "C-c 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)
    (zig-mode . eglot-ensure))

Use Treesitter parser support

Recently with verion 0.25 the treesitter library changed ABI version to 15. Newer parsers will complain about a version mismatch if the installed library used by emacs is lower than this version. This ABI version was introduced in the 0.25 branch of treesitter.

The best course of action till lib treesitter is updated is to pin the version of the parser to the last version supporting ABI 14.

With the new ABI 15 version, parsers are required to provide a treesitter.json file with additional metadata which can be used as proxy to find a version which still supports ABI-14, i.e. the commit before that.

A lot of the parsers are provided by the treesitter project as sub repos, and they follow the same version convention as the library, selecting the last tag before the 0.25 tag is a good way to find a compatible version.

This branch can be added after the repo url in the treesit-language-source-alist variable. Note that if you use treesit-auto-install-all to get it over with, you have to probably restart your emacs as treesit-auto apparently caches the value during iniitialisation and changes are not picked up.

  ;; set locations for treesitter grammars
  (use-package treesit
    :config
    (setq treesit-language-source-alist
          '((bash "https://github.com/tree-sitter/tree-sitter-bash" "v0.23.3")
  	  (c "https://github.com/tree-sitter/tree-sitter-c" "v0.23.6")
            (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")
  	  (rust "https://github.com/tree-sitter/tree-sitter-rust" "v0.23.3")
            (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)))

Treesit-auto automatically configures things behind the scenes to use the treesitter modes and download them if needed. \

  (use-package treesit-auto
    :ensure t
    :defer 5
    :custom
    (treesit-auto-install 'prompt)
    (treesit-auto-langs '(awk bash c c-sharp clojure cmake commonlisp cpp css
  			    dart dockerfile elixir go gomod html java
  			    javascript json julia kotlin lua make markdown nix
  			    org perl proto python r ruby rust scala sql toml tsx
  			    typescript vue yaml)) ; reduced langs list somewhat
    :config
    (treesit-auto-add-to-auto-mode-alist 'all)
    (global-treesit-auto-mode))
[nil 26284 45426 709595 nil elpaca-process-queues nil nil 734000 nil]

I always get errors compiling support for janet so I pruned the `treesit-auto-langs` to exclude it and other things I don't use.

TODO figure out why Latex does not want to install
TODO decide whether to keep using treesitter-auto at all or just plain treesit with some support functions.
Recompiling all Treesitter Grammars

To recompile all treesitter grammars, execute following block with C-c C-c.

  (mapc #'treesit-install-language-grammar (mapcar #'car treesit-language-source-alist))
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 How to get started with tree sitter .

Create a folder to store downloaded LSP servers

  ;; LSP support
  (let ((lsp_dir (file-name-concat user-emacs-directory "lsp")))
    (if (not (file-exists-p lsp_dir))
        (mkdir lsp_dir t)))

Configure Selected Languages

(report-time-since-load "Programming - Selected Languages")

Rust Support

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

  (use-package rustic
    :ensure t
    :after (flymake flycheck eglot)
    :init (setq rustic-lsp-client 'eglot))

OCaml Support

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

Go Support

        ;; configure go support
        (use-package go-ts-mode
          :init
          (add-to-list 'auto-mode-alist '("\\.go\\'" . go-ts-mode))
          :hook
          (go-ts . eglot-ensure))

Javascript, Typescript, TSC, etc…

        ;; 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))
eglot-ensure

Java and other JVM languages

        ;; 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 )
                  (report-time-since-load "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))))))

Lisp and Scheme Support

Aggressive Indent for lisp modes
  (use-package aggressive-indent
   :ensure t
   :hook ((lisp-mode-hook scheme-mode-hook clojure-mode-hook)))
Enable ParEdit in lispy modes
  ;; Lisp support
  (use-package package-lint-flymake
    :ensure t) ;; needed before activating lisp-interaction-mode-hook
  (use-package paredit
    :ensure nil
    :after  package-lint-flymake
    :commands (enable-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)))
Rainbow Parentheses
  (use-package rainbow-delimiters
    :ensure t
    :commands (rainbow-delimiters-mode)
    :hook
    (prog-mode . rainbow-delimiters-mode))
[nil 26418 38138 672360 nil elpaca-process-queues nil nil 82000 nil]
Configure Sly for Common Lisp
  (use-package sly
    :ensure t
    :config
    (require 'sly-autoloads)
    :hook (lisp-mode-hook . #'sly-editing-mode))

  (use-package sly-quicklisp
    :ensure t
    :after sly)

  (use-package sly-repl-ansi-color
    :ensure t
    :after sly)

  (use-package sly-asdf
    :ensure t
    :after sly)
[nil 26432 28005 611924 nil elpaca-process-queues nil nil 196000 nil]
Configure CLHS documentation

Common Lisp Hyperspec is distributed as a package for

AOC/2024/02> (ql:quickload "clhs")
To load "clhs":
  Install 1 Quicklisp release:
    clhs
; Fetching #<URL "http://beta.quicklisp.org/archive/clhs/2015-04-07/clhs-0.6.3.tgz">
; 2186.27KB
==================================================
2,238,743 bytes in 0.04 seconds (50845.91KB/sec)
; Loading "clhs"
[package clhs].

("clhs")

There is a wizard to help installing it in Emacs:

AOC/2024/02> (clhs:print-emacs-setup-form)

[ Quicklisp directory: "/home/pti/quicklisp/" (exists)
  If the above location is not correct, do:
  (setf clhs:*quicklisp-directory* "/path/to/quicklisp/") ]

clhs-use-local.el was not found in your quicklisp directory.
This means you're at step 1 of 2 for configuring Emacs/Slime
to perform lookups/browsing with your local copy of the CLHS.

Please run (clhs:install-clhs-use-local) in the (Common Lisp) REPL.
This will install clhs-use-local.el in your quicklisp directory.

Then, run (clhs:print-emacs-setup-form) again for instructions for step 2.

; No values
AOC/2024/02> (clhs:install-clhs-use-local)
T
AOC/2024/02> (clhs:print-emacs-setup-form)

[ Quicklisp directory: "/home/pti/quicklisp/" (exists)
  If the above location is not correct, do:
  (setf clhs:*quicklisp-directory* "/path/to/quicklisp/") ]

(clhs-use-local.el was found in your quicklisp directory.
Moreover, its version matches the one bundled with this CLHS ASDF wrapper.
You may proceed with step 2 of 2 below.)


Make Emacs evaluate this form to browse the CLHS locally:

(load "/home/pti/quicklisp/clhs-use-local.el" t)


Use C-c C-d h make-instance RET to test if the change was successful.
If it was, then this will open your browser and the URL will begin with "file:///".

Put the form in your ~/.emacs to persist the change for future sessions.


The README file has some further information,
including a list of 3 useful Slime CLHS lookup commands
and how to get Emacs to open CLHS pages in a different browser.
(Location: /home/pti/quicklisp/dists/quicklisp/software/clhs-0.6.3/README)

; No values
AOC/2024/02>
(load (expand-file-name "~/quicklisp/clhs-use-local.el") t)
t
Enable Geiser Mode in Scheme Mode

Configure Geiser and Scheme

  • map .scm file by default to Guile
  (use-package geiser-guile
    :ensure t
  :commands (geiser-guile))
  (use-package geiser-chicken
    :ensure t
    :commands (geiser-chicken))
  (use-package geiser-racket
    :ensure t
    :commands (geiser-racket))

  (use-package geiser
    :ensure t 
    :commands (geiser geiser-mode)
    :config
    (add-to-list 'geiser-implementations-alist `((dir ,(expand-file-name "~/src/guile")) guile))
    (add-to-list 'geiser-implementations-alist `((dir ,(expand-file-name "~/src/chicken")) chicken))
    (add-to-list 'geiser-implementations-alist `((dir ,(expand-file-name "~/src/racket")) racket))
    (setq geiser-default-implementation 'guile))

  (use-package scheme-mode
    :ensure nil 
    :commands (scheme-mode)
    :hook (scheme-mode . geiser-mode))
[nil 26508 11537 881112 nil elpaca-process-queues nil nil 113000 nil]
GUIX support

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.

    (use-package guix
      :ensure t
      :after geiser)

I find it a bit a confusing module.

It provides a minor-mode

Enable Cider for Clojure mode
  (use-package clojure-mode
    :ensure t
    :mode (("\\.clj\\'" . clojure-mode)
           ("\\.edn\\'" . clojure-mode))
    :init
    (add-hook 'clojure-mode-hook #'yas-minor-mode)         
    (add-hook 'clojure-mode-hook #'linum-mode)             
    (add-hook 'clojure-mode-hook #'subword-mode)           
    (add-hook 'clojure-mode-hook #'smartparens-mode)       
    (add-hook 'clojure-mode-hook #'rainbow-delimiters-mode)
    (add-hook 'clojure-mode-hook #'eldoc-mode)             
    (add-hook 'clojure-mode-hook #'idle-highlight-mode))
  (use-package cider
    :ensure t
    :defer t
    :init (add-hook 'cider-mode-hook #'clj-refactor-mode)
    :diminish subword-mode
    :config
    (setq nrepl-log-messages t                  
          cider-repl-display-in-current-window t
          cider-repl-use-clojure-font-lock t    
          cider-prompt-save-file-on-load 'always-save
          cider-font-lock-dynamically '(macro core function var)
          nrepl-hide-special-buffers t            
          cider-overlays-use-font-lock t)         
    (cider-repl-toggle-pretty-printing))
  (use-package clj-refactor
    :defer t
    :ensure t
    :diminish clj-refactor-mode
    :config (cljr-add-keybindings-with-prefix "C-c C-m"))
Allow saving of an SBCL images

When trying to call `(save-lisp-and-die #p"somefile")` , sbcl will throw an error that it cannot comply when multiple threads are active.

For each project using this you need to define some helper function to stop the repl threads before saving the image like the following:

(defun sbcl-save-sly-and-die ()
  "Save a sbcl image, even when running from inside Sly.
This function should only be used in the *inferior-buffer* buffer,
inside emacs."
  (mapcar #'(lambda (x)
              (slynk::close-connection 
               x nil nil))
          slynk::*connections*)
  (dolist (thread (remove
                   (slynk-backend::current-thread)
                   (slynk-backend::all-threads)))
    (slynk-backend::kill-thread thread))
  (sleep 1)
  (sb-ext:save-lisp-and-die #P"~/your-main-program.exe"
                            :toplevel #'your-main-function-here
                            :executable t
                            :compression t))

This function has to be called in the sly-inferior-lisp

;; in *sly-inferior-lisp* buffer
(sbcl-save-sly-and-die)

Terraform Support

  ;; configure terraform support
  (use-package terraform-mode
    :ensure t
    :config
    (setq
     terraform-indent-level 2
     terraform-format-on-save t)

    (keymap-set terraform-mode-map "C-c c k" #'terraform-open-doc)
    (keymap-set terraform-mode-map "C-c c f" #'terraform-format)
    (keymap-set terraform-mode-map "C-c c F" #'terraform-format-buffer))

Map the keymap consistently to the eglot mappings.

Zig Support

  ;; configure zig support
  (use-package zig-mode
    :ensure t
    :hook
    (zig-mode . eglot-ensure))

Python Support

Enable Pyvenv

(pyvenv-mode 1))

  (use-package pyvenv
    :ensure t
    :hook
        (python-mode . pyvenv-mode)
        (python-ts-mode . pyvenv-mode))
Enable UV
(require 'treesit)

(use-package uv
  :ensure (uv :type git :host github :repo "johannes-mueller/uv.el" :wait t)
  :init
  (add-to-list 'treesit-language-source-alist '(toml "https://github.com/tree-sitter-grammars/tree-sitter-toml"))
  (unless (treesit-language-available-p 'toml)
    (treesit-install-language-grammar 'toml)))
[nil 26751 43082 733388 nil elpaca-process-queues nil nil 945000 nil]

Docker Support

(use-package dockerfile-mode
  :defer t
  :ensure t
  :config
  (pcase snm-docker-executable
	('docker
	 (setq dockerfile-mode-command "docker"))
	('podman
	 (setq dockerfile-docker-command "podman"))))
[nil 26557 44943 649 nil elpaca-process-queues nil nil 729000 nil]

Gitlab CI Yaml Support

  (use-package gitlab-ci-mode
    :ensure (:host gitlab :repo "ptillemans/gitlab-ci-mode" :branch "fixes_2024")
    :mode "\\.gitlab-ci\\.yml\\'"
    :custom
    (gitlab-ci-url (auth-source-pass-get "url" "customer/gitlab/token"))
    (gitlab-ci-api-token (auth-source-pass-get 'secret "customer/gitlab/token")))
[nil 26760 26150 568817 nil elpaca-process-queues nil nil 219000 nil]

Debugger Support

(report-time-since-load "Programming - Debugger Support")
  ;; install DAP servers
  (setq snm-vscode-js-debug-dir (file-name-concat user-emacs-directory "dape/vscode-js-debug"))
  (defun snm-install-vscode-js-debug ()
    "Run installation procedure to install JS debugging support."
    (interactive)
    (mkdir snm-vscode-js-debug-dir t)
    (let ((default-directory (expand-file-name snm-vscode-js-debug-dir)))

      (vc-git-clone "https://github.com/microsoft/vscode-js-debug.git" "." nil)
      (report-time-since-load "git repository created")
      (call-process "npm" nil "*snm-install*" t "install")
      (report-time-since-load "npm dependencies installed")
      (call-process "npx" nil "*snm-install*" t "gulp" "dapDebugServer")
      (report-time-since-load "vscode-js-debug installed")))

  (setq snm-codelldb-dir (file-name-concat user-emacs-directory "dape/codelldb"))
  (defun snm-install-codelldb ()
    "Install Vadimcn.Vscode-Lldb DAP server for C/C++/RUST."
    (interactive)
    (let* ((default-directory snm-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)
      (report-time-since-load "codelldb archive downloaded")
      (call-process "unzip" nil "*snm-install*" t "codelldb.zip")
      (report-time-since-load "codelldb installed")
      ))

  ;; configure dape (dap-mode)
  (use-package dape
    :ensure (dape :host github :repo "svaante/dape" :wait t)
    :defer 5
    :config (progn
              ;; Use n for next etc. in REPL
              ;; (setq dape-repl-use-shorthand t)

              ;; Projectile users
              (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 snm-vscode-js-debug-dir "dist")
                             command-args ("src/dapDebugServer.js" "8123")
                             :type "pwa-node"
                             :request "launch"
                             :cwd dape-cwd
                             :program dape-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-command-cwd
                             host "127.0.0.1"
                             port 55878
                             :type "debug"       ;; needed to set the adapterID correctly as a string type
                             :request "launch"
                             :cwd dape-cwd
                             :program dape-buffer-default))
              (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
                                        snm-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
                             :program dape-buffer-default))
              (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
                             :program dape-buffer-default))

              ))

TODO move scheme configuration to the scheme section

or leave it here but move it to config, whatever makes most sense at the moment.

BoilerPlate Generator

The way I do advent of code requires me to create a main file and a test file every day with some boilerplate content.

The org-generate package uses an org file to structure boilerplate templates to be generated. By default it uses

  (use-package org-generate
    :ensure t
    )
[nil 26455 5250 886750 nil elpaca-process-queues nil nil 641000 nil]

Writing and Planning

(report-time-since-load "Writing and Planning")

Org Mode

Configure Org Mode

  (use-package org-mode
    :mode "\\.org$"
    :ensure nil
    :after org
    :config
    (progn
      (setq org-log-done 'time)
      (setq org-confirm-babel-evaluate nil)
      (setq org-export-babel-evaluate nil)
      (setq org-html-validation-link nil)
      ;; ... more stuff
    )
  )

Mixed Pitch Support by Default in Org

    (defun snm-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))
[nil 26279 36119 854408 nil elpaca-process-queues nil nil 376000 nil]

Org Appear

Hide the org markers when point is not on the element being decorated.

    (use-package org-appear
      :ensure t
      :hook (org-mode . org-appear-mode))
[nil 26432 31427 677147 nil elpaca-process-queues nil nil 738000 nil]

Org Babel Support

I have a test file which has samples of babel features for easy testing in my org babel test file.

Support Mermaid Diagrams in Babel Blocks
    ;; enable mermaid for org-babel
    (use-package mermaid-mode
      :ensure t
      :demand t
      :mode "\\.mmd\\'")
[nil 26499 60213 709442 nil elpaca-process-queues nil nil 672000 nil]

Mermaid needs support of the mermaid-cli which is a node package. It can be installed with

npm install -g @mermaid-js/mermaid-cli
added 281 packages, removed 60 packages, and changed 134 packages in 18s
52 packages are looking for funding
run `npm fund` for details
Support PlantUML Diagrams in Babel Blocks

Requires nothing special, other than plantuml.jar archive installed.

The following code block depends on Get latest version of a github released project utility function. This block will check if there is a plantuml.jar in the emacs config directory and if not download the latest version from github. The `snm-download-latest-plantuml` is made interactive to manually install the latest version if needed.

      (defun snm-download-latest-plantuml ()
        "Download the latest version of PlantUML.

        This function is interactive to make it easy to upgrade to
      the latest, current version with `M-x snm-download-latest-plantuml'."
        (interactive)
        (let* ((version (snm-latest-github-release "plantuml/plantuml"))
               (url (format
                     "https://github.com/plantuml/plantuml/releases/download/%s/plantuml-%s.jar"
                     version (substring version 1))))
          (message "Downloading PlantUML version %s from %s" version url)
          (url-copy-file
           url
           (concat  user-emacs-directory "plantuml.jar")
           t)))

      (let ((plantuml-jar (concat user-emacs-directory "plantuml.jar")))
        (if (not (file-exists-p plantuml-jar))
            (snm-download-latest-plantuml))

        (setq org-plantuml-jar-path plantuml-jar))
~/.config/emacs/plantuml.jar
Configure Babel Languages
  ;; configure babel languages
  (use-package ob
    :custom
    (org-babel-load-languages '((emacs-lisp . t)
  			      (shell . t)
  			      (python . t)
  			      (latex . t)
  			      (scheme . t)
  			      (plantuml . t)
  			      (mermaid . t)
  			      (sql . t)
  			      (dot . t)))
    )
Temporary Patches for Org Babel
Fix Scheme Babel Bug
  ;; 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))))
org-babel-scheme--table-or-string
TODO Move babel test file to emacs config folder

Alternatively I might add a sample after each configured block to keep it in the same context. Hmmm…. sounds even better.

Org Export

Org Export to Markdown
  (use-package ox-md
    :ensure nil
    :after ox
    )
Org Latex Export
Syntax Highlighting requires Multiple Passes
  ;; 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"))
    )
Load Customer Latex Classes
    ;; 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}"))))
Load My Latex Classes
    (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)))
Export to Confluence
  (use-package org-contrib
    :ensure t)
[nil 26576 2769 688869 nil elpaca-process-queues nil nil 120000 nil]
  (use-package ox-confluence
    :after org-contrib 
    :ensure nil)
  (use-package ox-confluence-en
    :after ox-confluence
    :ensure (ox-confluence-en :host github :repo "correl/ox-confluence-en"))
[nil 26576 2809 520906 nil elpaca-process-queues nil nil 290000 nil]
Blogging with Zola

gicrisf has created a package to export org files to Zola.

          (use-package ox-hugo
            :ensure t
            :after ox)
          (use-package ox-zola
              :ensure (ox-zola :host github :repo "gicrisf/ox-zola" :files (:defaults "*.el" "backend" "stylesheets"))
              :after (ox-hugo)
              :config
              (require 'ox-hugo)
  	    )

It is a wrapper around ox-hugo to export org files to Zola. It supports most features of it and directs the user to the the hugo exporter manual.

Org Capture Customization

  (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" entry
                             (file+headline "~/org/snamellit/blog.org" "Blog Posts")
                             "** %^{Blog Title:}\n:PROPERTIES:\n:EXPORT_FILE_NAME %^{Export Filename:}\n:EXPORT_DATE: %t\n:END:\n%i%?"))

              ;; 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 (("C-c C" . org-capture)))
t

Org GCal Support

  ;; configure support for google calendar
  (use-package org-gcal
    ;; :ensure t
    :custom
    (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")))
    :commands (org-gcal-sync org-gcal-fetch) )

Org Jira Integration

  ;; configure org-jira
  (use-package org-jira
    :ensure  t  ; (:host github :repo "ptillemans/org-jira" :branch "pti_fix_getUsers")
    :commands (org-jira-get-issues org-jira-get-projects)
    :config
    (setq org-jira-users '(("Peter Tillemans". "557058:bdf83521-663b-4ae6-9b71-487bb98e2add")))
    (setq jiralib-agile-page-size 1000)
    (setq org-jira-custom-jqls
          '(
            
            
            ))
    (make-directory "~/.org-jira" 'parents)
    (setq jiralib-url (auth-source-pass-get "url" "customer/jira"))
    (setq org-jira-custom-jqls
          '((:jql " assignee = currentUser() and (created > '2024-01-01' or updated > '2024-01-01') order by created DESC"
                  :limit 100 :filename "this-year")
  	  (:jql " assignee = currentUser() and project = UNILRN order by priority DESC "
  		:limit 100 :filename "~/Projects/unifylearn/jira")
  	  (:jql " assignee = currentUser() and project = TTRK order by priority DESC "
  		:limit 100 :filename "~/Projects/timetrak/jira")
            )
  	)
    (jiralib-login
     (auth-source-pass-get  "user" "customer/jira")
     (auth-source-pass-get 'secret "customer/jira"))
    :bind (("C-c j i g" . 'org-jira-get-issues)
           ("C-c j p g" . 'org-jira-get-projects)
           ("C-c j i j" . 'org-jira-get-issues-from-custom-jql)))
[nil 26760 26288 212481 nil elpaca-process-queues nil nil 913000 nil]

It is very useful to create for each active project a custom JQL to make a snapshot of issues just for that project in the folder of the project to keep everything in context.

Keybindings
Key Command
C-c pg org-jira-get-projects
C-c bg org-jira-get-boards
C-c iv org-jira-get-issues-by-board
C-c ib org-jira-browse-issue
C-c ig org-jira-get-issues
C-c ij org-jira-get-issues-from-custom-jql
C-c ih org-jira-get-issues-headonly
C-c if org-jira-get-issues-from-filter-headonly
C-c iF org-jira-get-issues-from-filter
C-c iu org-jira-update-issue
C-c iw org-jira-progress-issue
C-c in org-jira-progress-issue-next
C-c ia org-jira-assign-issue
C-c isr org-jira-set-issue-reporter
C-c ir org-jira-refresh-issue
C-c iR org-jira-refresh-issues-in-buffer
C-c ic org-jira-create-issue
C-c ik org-jira-copy-current-issue-key
C-c sc org-jira-create-subtask
C-c sg org-jira-get-subtasks
C-c cc org-jira-add-comment
C-c cu org-jira-update-comment
C-c wu org-jira-update-worklogs-from-org-clocks
C-c tj org-jira-todo-to-jira
C-c if org-jira-get-issues-by-fixversion
TODO add focused project support

There should be some things we can do to better integrate this with projects:

  • update issues in background when opening the project.
  • run custom JQL defined in the project iso globally.

Daviwil's Productivity Tools

@daviwil has a set of productivity tools which are very useful. I have

Find all tasks with a specific tag (or without)
  (setq org-agenda-custom-commands
        '(("p" "Planning" tags-todo "+@planning")))
p Planning tags-todo +@planning

We can remove tasks with a certain tag by adding `-@work` to the pattern.

Find all tasks with a specific tag (or without)
  (setq org-agenda-custom-commands
        '(("u" "Untagged Tasks" tags-todo "-{.*}")))
u Untagged Tasks tags-todo -{.*}
Combine multiple filters

We can add a list of queries

  (setq org-agenda-custom-commands
        '(("p" "Planning" ((tags-todo "+@planning")
                           (tags-todo "-{.*}")))))
p Planning ((tags-todo +@planning) (tags-todo -{.*}))
We can add settings to each filter
      (setq org-agenda-custom-commands
            '(("p" "Planning" ((tags-todo "+@planning"
                                          ((org-agenda-overriding-header "Planning Tasks")))
                               (tags-todo "-{.*}"
                                          ((org-agenda-overriding-header "Untagged Tasks")))))))
p Planning ((tags-todo +@planning ((org-agenda-overriding-header Planning Tasks))) (tags-todo -{.*} ((org-agenda-overriding-header Untagged Tasks))))
Add support for an input file
  (setq org-agenda-custom-commands
        '(("i" "Inbox" ((todo ".*"
                             ((org-agenda-files '("~/Nextcloud/org/"))
                              (org-agenda-overriding-header "Unprocessed Inbox Items")))))))
i Inbox ((todo .* ((org-agenda-files '(~/Nextcloud/org/)) (org-agenda-overriding-header Unprocessed Inbox Items))))
Daily Agenda
    (setq org-agenda-custom-commands
          '(("d" "Daily Agenda"
             ((agenda "" ((org-agenda-span 'day)
                          (org-deadline-warning-days 1)
                          (org-agenda-overriding-header "Today's Agenda")))
               (tags-todo "+PRIORITY=\"A\""
                          ((org-agenda-overriding-header "High-Priority Unfinished Tasks")
                           ))))))
d Daily Agenda ((agenda ((org-agenda-span 'day) (org-deadline-warning-days 1) (org-agenda-overriding-header Today's Agenda))) (tags-todo +PRIORITY="A" ((org-agenda-overriding-header High-priority unfinished tasks))))
Weekly Review
  (setq org-log-done 'time)               ; log the time when a task is completed
  (setq org-agenda-start-with-log-mode t) ; show the log mode when starting the agenda

  (setq org-agenda-custom-commands
        '(("w" "Weekly Review"
           ((agenda "" ((org-agenda-overriding-header "Completed Tasks")
                        (org-agenda-skip-function (org-agenda-skip-entry-if 'nottodo 'done))
                        (org-agenda-span 'week)))
            (agenda "" ((org-agenda-overriding-header "Unfinished Scheduled Tasks")
                        (org-agenda-skip-function (org-agenda-skip-entry-if 'todo 'done))
                        (org-agenda-span 'week)))
            ))))
w Weekly Review ((agenda ((org-agenda-overriding-header Completed Tasks) (org-agenda-skip-function (org-agenda-skip-entry-if 'nottodo 'done)) (org-agenda-span 'week))) (agenda ((org-agenda-overriding-header Unfinished Scheduled Tasks) (org-agenda-skip-function (org-agenda-skip-entry-if 'todo 'done)) (org-agenda-span 'week))))
Customer GTD

Create an agenda view specifically focused on customer tasks both in the org tree and in the customer JIRA.

  (add-to-list 'org-agenda-custom-commands
  	     '("G" "Customer GTD"
  	       ((alltodo ".*"
  			 ((org-agenda-files '("~/org/customer/" "~/.org-jira/"))
  			  (org-agenda-overriding-header "Customer GTD"))))))
G Customer GTD ((alltodo .* ((org-agenda-files '(~/org/customer/ ~/.org-jira/)) (org-agenda-overriding-header Customer GTD))))
G Customer GTD ((alltodo .* ((org-agenda-files '(~/org/customer/)) (org-agenda-overriding-header Customer GTD))))
w Weekly Review ((agenda ((org-agenda-overriding-header Completed Tasks) (org-agenda-skip-function (org-agenda-skip-entry-if 'nottodo 'done)) (org-agenda-span 'week))) (agenda ((org-agenda-overriding-header Unfinished Scheduled Tasks) (org-agenda-skip-function (org-agenda-skip-entry-if 'todo 'done)) (org-agenda-span 'week))))

Org Configuration

  (use-package org
    :ensure nil
    :demand t
    :custom
    (org-return-follows-link t)
    (org-mouse-1-follows-link t)
    (org-link-descriptive t)
    (org-agenda-skip-scheduled-if-done t)
    (org-agenda-skip-deadline-if-done t)
    (org-hide-emphasis-markers t)
    
    (line-spacing 0.1)
    (left-margin-width 2)
    (right-margin-width 2)

    (org-todo-keywords '((sequence "TODO(t)" "NEXT(n)" "WAITING(w)" "SOMEDAY(s)" "PROJ(p)"
                                   "|" "DONE(d)" "CANCELED(c)")))
    (org-todo-keywords-for-agenda '((sequence "NEXT(n)" "TODO(t)" "WAITING(w)" "SOMEDAY(s)" "PROJ(p)" "|" "DONE(d)" "CANCELED(c)")))
    (org-agenda-files (list
  		     "~/Nextcloud/org/"
  		     "~/org/snamellit/"
  		     "~/org/customer/"
  		     "~/org/personal/"
  		     "~/.org-jira/"
  		     ))
    (org-refile-targets '(
                          (org-agenda-files . (:level . 1))
  			("~/org/customer/gtd.org" . (:level . 1))
                          ("~/org/personal/bijen.org" . (:level . 1))
                          ("~/org/personal/fitness.org" . (:level . 1))
                          ))
    (org-babel-load-languages '((emacs-lisp . t)
  			      (shell . t)
  			      (python . t)
  			      (latex . t)
  			      (scheme . t)
  			      (plantuml . t)
  			      (dot . t)))
    :config
    (message "Configuring org mode")
    ;; set files for agenda views
    (setq +org-capture-todo-file "~/Nextcloud/org/inbox.org"
          +org-capture-notes-file "~/Nextcloud/org/inbox.org"
          +org-capture-journal-file "~/Nextcloud/org/journal.org"
          +org-capture-projects-file "~/Nextcloud/org/projects.org")
    (org-babel-do-load-languages 'org-babel-load-languages org-babel-load-languages)
    :hook (
  	 (org-mode . snm-org-mode-config)
  	 (org-mode . org-indent-mode)
           )
    :bind (
           ("C-c a" . org-agenda)
           ("C-c l" . org-store-link)
           ))
org-store-link

Denote

  ;; configure denote
  (use-package denote
    :ensure t
    :init
    (setq denote-directory (file-name-concat (expand-file-name "~") "Nextcloud/denote"))
    (setq denote-dired-directories
          (list denote-directory
                (expand-file-name "~/Documents/denote")))
    :hook (dired-mode . denote-dired-mode-in-directories)
    :bind (
           ("<leader> n d" . (lambda () (interactive) (dired denote-directory)))
           ("<leader> n n" . #'denote)
           ("<leader> n N" . #'denote-type)
           ("<leader> n c" . #'denote-link-or-create)
           ("<leader> n t" . #'denote-template)
           ("<leader> n z" . #'denote-signature)
           ("<leader> n l" . #'denote-link)
           ("<leader> n L" . #'denote-find-link)
           ("<leader> n k" . #'denote-keywords-add)
           ("<leader> n b" . #'denote-link-backlink)
           ("<leader> n B" . #'denote-find-backlink)
           ("<leader> n r" . #'denote-rename-file)
           ("<leader> n R" . #'denote-rename-file-using-front-matter)
           ("<leader> n f" . (lambda () (interactive) (consult-find denote-directory)))))
[nil 26544 34903 498589 nil elpaca-process-queues nil nil 963000 nil]
TODO explain what denote-dired-mode-in-directories does.

AI Support

AI Provider API keys

As many tools require API keys, I better define them once and reuse them in the configuration of the individual tools.

  (setq openai-api-key (auth-source-pass-get 'secret "snamellit/openai-api-key"))
  (setq anthropic-api-key (auth-source-pass-get 'secret "snamellit/anthropic-api-key"))
  (setq gemini-api-key (auth-source-pass-get 'secret "snamellit/gemini-api-key"))
AIzaSyBAkk0xHNkIBxCzkfvbOgYVb-B6BguWVUI

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.

    (use-package llm :ensure t)
[nil 26497 15516 337719 nil elpaca-process-queues nil nil 495000 nil]
  (use-package ellama 
    :ensure t
    :defer t
    :requires (llm)
    :config 
    (message "Ellama loaded")
    (keymap-global-set "C-c e" 'ellama-transient-main-menu)
    :custom
    (ellama-language "English")
    (ellama-sessions-directory (expand-file-name "~/Nextcloud/ellama-sessions"))
    (ellama-providers
     '(("openai" . (progn
                     (require 'llm-openai)
  		   (make-llm-openai
  		    :key openai-api-key
  		    :chat-model "gpt-4o"))))
     ))
[nil 26497 19957 511863 nil elpaca-process-queues nil nil 742000 nil]

It seems the gpt-4o model provides better responses.

I should investigate local models more.

Use Gemini with ellama

This is mostly for those who want to use Google Cloud specifically, most users should use Gemini instead, which is easier to set up.

You can set up with make-llm-vertex, with the following parameters:

paramter description
:project Your project number from Google Cloud that has Vertex API enabled.
:chat-model A model name from the list of Vertex's model names. This is optional, and will default to a reasonable model.
:embedding-model A model name from the list of Vertex's embedding model names. This is optional, and will default to a reasonable model.

In addition to the provider, which you may want multiple of (for example, to charge against different projects), there are customizable variables:

  • llm-vertex-gcloud-binary: The binary to use for generating the API key.
  • llm-vertex-gcloud-region: The gcloud region to use. It's good to set this to a region near where you are for best latency. Defaults to "us-central1". If you haven't already, you must run the following command before using this:
gcloud beta services identity create --service=aiplatform.googleapis.com --project=PROJECT_ID

However this is not for the Gemini Code Assistant. It is unclear how to use that backend. Gemini Code Assistant is part of the Google Cloud Tools.

GPTel LLM support

Most of the mind share seems to be around gptel as basis for LLM integration in Emacs.

  (use-package gptel
    :ensure t
    :commands (gptel)
    :config
    ;;(setq gptel-default-mode 'org-mode)
    (setq gptel-api-key openai-api-key)
    (gptel-make-gemini "Gemini" :key gemini-api-key :stream t)
    (gptel-make-anthropic "Claude" :stream t :key anthropic-api-key)
    (gptel-make-ollama "Qwen Coder"
      :models '(qwen2.5-coder:14b
  	      :description "QWen 2.5 Coder 14b"))
    ;; define some tools for gptel  
    (gptel-make-tool
     :function (lambda (url)
                 (with-current-buffer (url-retrieve-synchronously url)
  		 (goto-char (point-min)) (forward-paragraph)
  		 (let ((dom (libxml-parse-html-region (point) (point-max))))
                     (run-at-time 0 nil #'kill-buffer (current-buffer))
                     (with-temp-buffer
                       (shr-insert-document dom)
                       (buffer-substring-no-properties (point-min) (point-max))))))
     :name "read_url"
     :description "Fetch and read the contents of a URL"
     :args (list '(:name "url"
  		       :type "string"
  		       :description "The URL to read"))
     :category "web")

    (gptel-make-tool
     :function (lambda (buffer text)
                 (with-current-buffer (get-buffer-create buffer)
  		 (save-excursion
                     (goto-char (point-max))
                     (insert text)))
                 (format "Appended text to buffer %s" buffer))
     :name "append_to_buffer"
     :description "Append text to the an Emacs buffer.  If the buffer does not exist, it will be created."
     :args (list '(:name "buffer"
  		       :type "string"
  		       :description "The name of the buffer to append text to.")
                 '(:name "text"
  		       :type "string"
  		       :description "The text to append to the buffer."))
     :category "emacs")

    ;; Message buffer logging tool
    (gptel-make-tool
     :function (lambda (text)
                 (message "%s" text)
                 (format "Message sent: %s" text))
     :name "echo_message"
     :description "Send a message to the *Messages* buffer"
     :args (list '(:name "text"
  		       :type "string"
  		       :description "The text to send to the messages buffer"))
     :category "emacs")

    ;; buffer retrieval tool
    (gptel-make-tool
     :function (lambda (buffer)
                 (unless (buffer-live-p (get-buffer buffer))
  		 (error "Error: buffer %s is not live." buffer))
                 (with-current-buffer  buffer
  		 (buffer-substring-no-properties (point-min) (point-max))))
     :name "read_buffer"
     :description "Return the contents of an Emacs buffer"
     :args (list '(:name "buffer"
  		       :type "string"
  		       :description "The name of the buffer whose contents are to be retrieved"))
     :category "emacs")


    (gptel-make-tool
     :function (lambda (directory)
  	       (mapconcat #'identity
                            (directory-files directory)
                            "\n"))
     :name "list_directory"
     :description "List the contents of a given directory"
     :args (list '(:name "directory"
  		       :type "string"
  		       :description "The path to the directory to list"))
     :category "filesystem")

    (gptel-make-tool
     :function (lambda (parent name)
                 (condition-case nil
                     (progn
                       (make-directory (expand-file-name name parent) t)
                       (format "Directory %s created/verified in %s" name parent))
  		 (error (format "Error creating directory %s in %s" name parent))))
     :name "make_directory"
     :description "Create a new directory with the given name in the specified parent directory"
     :args (list '(:name "parent"
  		       :type "string"
  		       :description "The parent directory where the new directory should be created, e.g. /tmp")
                 '(:name "name"
  		       :type "string"
  		       :description "The name of the new directory to create, e.g. testdir"))
     :category "filesystem")

    (gptel-make-tool
     :function (lambda (path filename content)
                 (let ((full-path (expand-file-name filename path)))
  		 (with-temp-buffer
                     (insert content)
                     (write-file full-path))
  		 (format "Created file %s in %s" filename path)))
     :name "create_file"
     :description "Create a new file with the specified content"
     :args (list '(:name "path"
  		       :type "string"
  		       :description "The directory where to create the file")
                 '(:name "filename"
  		       :type "string"
  		       :description "The name of the file to create")
                 '(:name "content"
  		       :type "string"
  		       :description "The content to write to the file"))
     :category "filesystem")

    (gptel-make-tool
     :function (lambda (filepath)
  	       (with-temp-buffer
  		 (insert-file-contents (expand-file-name filepath))
  		 (buffer-string)))
     :name "read_file"
     :description "Read and display the contents of a file"
     :args (list '(:name "filepath"
  		       :type "string"
  		       :description "Path to the file to read.  Supports relative paths and ~."))
     :category "filesystem")

    ;; follow output
    (add-hook 'gptel-post-stream-hook 'gptel-auto-scroll)
    ;; move to next prompt after response
    (add-hook 'gptel-post-response-functions 'gptel-end-of-response)
    )
[nil 26626 15252 706789 nil elpaca-process-queues nil nil 969000 nil]

Copilot Support

(report-time-since-load "Programming - Copilot Support")
  (use-package copilot
    :ensure (copilot :host github :repo "zerolfx/copilot.el"
                   :branch "main"
                   :files ("dist" "*.el"))
    :bind ("C-S-y" . copilot-accept-completion)
    :config
    (add-to-list 'copilot-indentation-alist '(scheme-mode . 2))
    (add-to-list 'copilot-indentation-alist '(emacs-lisp-mode . 2))
    (add-to-list 'copilot-indentation-alist '(lisp-mode . 2))
    :custom
    (copilot-indent-offset-warning-disabled t "Disable indent offset warning" )
  	      
    :hook
    (prog-mode . copilot-mode)
    (org-mode . copilot-mode))
[nil 26506 39443 528692 nil elpaca-process-queues nil nil 667000 nil]

SuperMaven Support

There is a lot of positive hubbub around SuperMaven.

  (use-package supermaven
    :ensure (supermaven :host github :repo "crazywolf132/supermaven.el"
                        :branch "main")
    :init
    (setq supermaven-ignore-filetypes '("org" "txt"))
    (setq supermaven-disable-inline-completion nil)
    (setq supermaven-keymaps
  	'((accept-suggestion . "TAB")
            (clear-suggestion . "C-]")
            (accept-word . "C-j")))
    (setq supermaven-log-level 'debug)
    :hook (prog-mode . supermaven-mode))
[nil 26528 36711 407251 nil elpaca-process-queues nil nil 656000 nil]

<2025-01-28 Tue> Tried it out but did not work. I got following errors :

[2025-01-28 14:47:15] [INFO] Supermaven process started successfully
[2025-01-28 14:47:17] [INFO] Stopping Supermaven process...
[2025-01-28 14:47:17] [INFO] Supermaven process killed

[2025-01-28 14:47:17] [INFO] Supermaven process started successfully
[2025-01-28 14:47:19] [INFO] Stopping Supermaven process...
[2025-01-28 14:47:19] [INFO] Supermaven process killed

[2025-01-28 14:47:19] [INFO] Supermaven process started successfully
[2025-01-28 14:47:21] [INFO] Stopping Supermaven process...
[2025-01-28 14:47:21] [INFO] Supermaven process killed

[2025-01-28 14:47:21] [INFO] Supermaven process started successfully
[2025-01-28 14:47:21] [INFO] Stopping Supermaven process...
[2025-01-28 14:47:21] [INFO] Supermaven process killed

[2025-01-28 14:47:21] [INFO] Supermaven process started successfully
[2025-01-28 14:47:21] [INFO] Supermaven process started successfully
[2025-01-28 14:47:22] [INFO] Supermaven process hangup

In a separate terminal I see the pid change every 2 seconds. It seems to work fine in vscode. Not tested in neovim yet. It works just fine in Neovim.

Supermaven requires company support at the moment. There is a Feature Request open to add corfu support.

Gemini Code Completion

  (use-package google-gemini
    :ensure (google-gemini
  	   :host github
  	   :repo "emacs-openai/google-gemini")
    :config
    (setq google-gemini-key gemini-api-key)
    )
[nil 26546 33151 856622 nil elpaca-process-queues nil nil 426000 nil]
  (use-package gemini-code-completion
    :ensure (gemini-code-completion
  	    :host github
  	    :repo "shishimaru/gemini-code-completion.el"
  	    :files ("*.el"))
    :bind
    ("C-c g" . gemini-code-completion)
    :config
    ;;https://github.com/shishimaru/gemini-code-completion.el/issues/13
    (setq gemini-code-completion-min-number-of-candidates 1)
    (setq gemini-code-completion-disable-completion-keybinding t)
    )
[nil 26525 26586 616591 nil elpaca-process-queues nil nil 615000 nil]

Aider Support

Aider is a package which works as a pair programming partner to generate code.

  (use-package aidermacs
    :ensure (:host github :repo "MatthewZMD/aidermacs" :files ("*.el"))
    :config
    ;; Use claude-3-5-sonnet cause it is best in aider benchmark
    ;;(setq aider-args '("--model" "anthropic/claude-3-5-sonnet-20241022"))
    ;;(setenv "ANTHROPIC_API_KEY" anthropic-api-key)
    ;; Or use chatgpt model since it is most well known
    ;; (setq aider-args '("--model" "gpt-4o-mini"))
    ;; (setenv "OPENAI_API_KEY" <your-openai-api-key>)
    ;; Or use gemini v2 model since it is very good and free
    (setq aidermacs-default-model "gemini-2.0-flash-thinking-exp-01-21")
    (setenv "GEMINI_API_KEY" google-gemini-key)
    (setq aidermacs-auto-commits t)
    (setq aidermacs-use-architect-mode t)
    (setq aidermacs-backend 'vterm)
    ;;
    ;; Optional: Set a key binding for the transient menu
    )
[nil 26557 35519 420170 nil elpaca-process-queues nil nil 238000 nil]

Dired Configuration

Enables an alternative file navigation behavior in Dired, Emacs' directory editor:

  (put 'dired-find-alternate-file 'disabled nil)

Use Magit for version control

  ;; default to magit for version control
  (use-package magit
    :ensure (:wait t)
    :commands (magit-status)
    :bind
    (("C-x p v" . magit-status)
     ("<leader> p v" . magit-status)))
[nil 26709 8564 25469 nil elpaca-process-queues nil nil 123000 nil]

Better EDiff support

  ;; 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))
t

Replace normal Buffer Support with IBuffer

Configure ibuffer support with project contexts, courtesy of crafted emacs

  ;;; enhance ibuffer with ibuffer-project if it is available.
  (defun snm-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 . snm-ide-enhance-ibuffer-with-ibuffer-project)
    :bind (("C-x C-b" . #'ibuffer-other-window)))

Enable EditorConfig Standard Support

  ;;; load editorconfig support
  (use-package editorconfig
    :hook
    (prog-mode . (lambda() (editorconfig-mode 1))))
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.

  ;; enable direnv mode
  (use-package direnv
    :ensure t
    :config
    (direnv-mode))

This enables direnv globally.

Configure Embark

  (use-package embark
    :ensure t
    :bind
    (("C-." . embark-act)
     ("C-;" . embark-dwim)
     ("C-h B" . embark-bindings))
    :config
    (setq prefix-help-command #'embark-prefix-help-command))

  ;; Consult users will also want the embark-consult package.
  (use-package embark-consult
    :ensure t ; only need to install it, embark loads it after consult if found
    :hook
    (embark-collect-mode . consult-preview-at-point-mode))
[nil 26502 58499 164859 nil elpaca-process-queues nil nil 179000 nil]

Configure Helpful

  (use-package helpful
    :ensure t
    :bind (
           ( "<remap> <describe-command>" . #'helpful-command)
           ( "<remap> <describe-function>" . #'helpful-callable)
           ( "<remap> <describe-key>" .      #'helpful-key)
           ( "<remap> <describe-symbol>" .   #'helpful-symbol)
           ( "<remap> <describe-variable>" . #'helpful-variable)
           ( "C-h F" .                       #'helpful-function)))
[nil 26432 30485 62583 nil elpaca-process-queues nil nil 114000 nil]

Add emacslisp-demos

A package which enhances helpful by adding API demos to the the help

  (use-package elisp-demos
    :ensure t
    :init
    (advice-add 'helpful-update :after #'elisp-demos-advice-helpful-update))
[nil 26745 27316 824254 nil elpaca-process-queues nil nil 497000 nil]

Enable breadcrumbs

Show breadcrumbs in the header line to keep context of the file being worked on. See the breadcrumb repo for more details.

  (use-package breadcrumb
    :ensure t
    :init
    (breadcrumb-mode))
[nil 26432 31533 350342 nil elpaca-process-queues nil nil 899000 nil]

Enable Avy jumping

Avy mode replace ace-jump mode with a lot of functionality hidden

see Avy can Do Anything article by the author on the design philosophy behind it.

  (use-package avy
    :ensure t
    :commands (avy-goto-char
  	     avy-goto-char-2
  	     avy-goto-char-2-above
  	     avy-goto-char-2-below
  	     avy-goto-char-in-line
  	     avy-goto-char-timer
  	     avy-goto-line
  	     avy-goto-line-above
  	     avy-goto-line-below
  	     avy-goto-subword-0
  	     avy-goto-subword-1
  	     avy-goto-symbol-1
  	     avy-goto-symbol-1-above
  	     avy-goto-symbol-1-below
  	     avy-goto-word-0
  	     avy-goto-word-1
  	     avy-goto-word-1-above
  	     avy-goto-word-1-below
  	     avy-goto-word-or-subword-1)
    :bind
    (("C-:" . avy-goto-char-timer)
     ("M-g w" . avy-goto-word-0)
     ("M-g f" . avy-goto-line))
    :config
    (avy-setup-default) ;; setup C-' to jump with isearch
    )
[nil 26507 37720 141708 nil elpaca-process-queues nil nil 278000 nil]

Ace Window Configuration

        (use-package ace-window
        :ensure t
        :bind
        ("C-x o" . ace-window)
        :config
        (setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l))
        (setq aw-dispatch-always t))
[nil 26523 35809 462186 nil elpaca-process-queues nil nil 958000 nil]

Command Log Mode

surprising sequence of events happened. Command Log Mode is a package that records all the commands that are executed in the buffer and provides a way to replay them.

  (use-package command-log-mode
    :ensure t
    :commands (command-log-mode))
[nil 26503 37477 396927 nil elpaca-process-queues nil nil 128000 nil]

Abbrev Files

  (use-package abbrev
        :ensure nil
        :config
        (setq abbrev-file-name (expand-file-name "abbrev_defs" user-emacs-directory))
        (setq save-abbrevs 'silently)
        (if (file-exists-p abbrev-file-name)
                (quietly-read-abbrev-file)))

Communication and Interwebs

(report-time-since-load "Communication and Interwebs")

ERC configuration   disabled

Set up ERC, an IRC client for Emacs, to automatically join specific channels and log in:

  (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")))
    (custom-set-variables
     '(erc-fool-highlight-type 'all)
     '(erc-fools '("Marvin2"))
     '(erc-keyword-highlight-type 'all)
     '(erc-pal-highlight-type 'all)
     '(erc-pals '("Fade" "daviwil" "alternateved" "trev" "talos" "dthompson"))
     '(erc-prompt-for-password nil)))
[nil 26384 63712 203392 nil elpaca-process-queues nil nil 785000 nil]

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.

  (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")))
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. o longer useful : I seldom use i3, but it is useful for inspiration

  (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")
      (report-time-since-load "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)))
my-erc

rcirc configuration

  (use-package rcirc

    :config
    (setopt
     rcirc-server-alist 
            (let ((data (auth-source-pass-parse-entry "snamellit/znc")))
              `((,(auth-source-pass--get-attr "server" data)
                 :port ,(auth-source-pass--get-attr "port" data)
                 :encryption tls
                 :server-alias "znc"
                 :nick ,(auth-source-pass--get-attr "nick" data)
                 :user-name ,(concat
                              (auth-source-pass--get-attr "user" data)
                              "@"
                              (car (string-split (system-name) "\\.")))
                 :password ,(auth-source-pass--get-attr 'secret data)
                 :channels '("#emacs"
                             "#erc"
                             "#spritely"
                             "#guix"
                             "#systemcrafters"
                             "#systemcrafters-guix"
                             "#systemcrafters-emacs"
                             )))))
    (setopt rcirc-reconnect-delay 15)
    (setopt rcirc-dim-nicks '("Marvin2"))
    (setopt rcirc-bright-nicks '("daviwil"
                                 "benoitj"
                                 "Fade"
                                 "trev"
                                 "shom"
                                 "alternateved"
                                 "dthompson"))
    (rcirc-track-minor-mode))
t

Elfeed configuration

Configures Elfeed, an RSS feed reader for Emacs:

  ;; 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"
            )))
[nil 26279 35504 577569 nil elpaca-process-queues nil nil 161000 nil]

TODO OPML To Elfeed

  (use-package opml-to-elfeed-feeds
    :ensure (:host "codeberg.org" :repo "kakafarm/emacs-opml-to-elfeed-feeds"
                   :main "opml-to-elfeed-feeds.el")
    :custom
    (o2e-opml-list . (("https://craftering.systemcrafters.net/Craftering.opml" blog craftering opml-to-elfeed-feeds)))
    :commands
    (opml-to-elfeed-feeds-update-o2e-elfeed-feeds)
    )
[nil 26750 42629 851189 nil elpaca-process-queues nil nil 224000 nil]

This does all kind of weird things. Apparently elpaca or use-package magically namespaces all the functions and for some reason it fails to require 'elfeed'. … The weird thing is that the emacs lips feature is used to expand o2e to opml-to-elfeed-feeds, by setting the `read-symbol-shorthands` variable to `(("o2e" . "opml-to-elfeed-feeds"))`.

Agreed with cow_2000 to create an issue on the repo with my recommendation for public API.

Enable 0x0 to share snippets and files easily

Configures the 0x0 package for easily sharing code snippets and files:

  ;; configure 0x0
  (use-package 0x0
    :disabled
    :ensure t
    :config (setq 0x0-use-curl t))

  (defun 0x0-upload-text ()
      (interactive)
      (let* ((contents
              (if (use-region-p)
                  (buffer-substring-no-properties (region-beginning) (region-end))
                (buffer-string)))
             (temp-file (make-temp-file "0x0" nil ".txt" contents)))
        (message "Sending %s to 0x0.st..." temp-file)
        (let ((url (string-trim-right
                    (shell-command-to-string
                     (format "curl -s -F'file=@%s' -Fsecret= -Fexpires=24 https://0x0.st"
                             temp-file)))))
          (message "Yanked ‘%s’ into kill ring." url)
          (kill-new url)
          (delete-file temp-file))))

    (defun 0x0-upload-file (file-path)
      (interactive "fSelect a file to upload: ")
      (message "Sending %s to 0x0.st..." file-path)
      (let ((url (string-trim-right
                  (shell-command-to-string
                   (format "curl -s -F'file=@%s' -Fsecret= -Fexpires=24 https://0x0.st"
                           (expand-file-name file-path))))))
        (message "Yanked ‘%s’ into kill ring." url)
        (kill-new url)))
+0x0-upload-file

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.

Systemcrafters live stream

  (defun systemcrafters ()
    "Watch SystemCrafters on Twitch"
    (interactive)
    (message "Watching SystemCrafters on Twitch...")
    (start-process "livestream" "*livestream*"
  		 "mpv"
  		 "--quiet"
  		 "https://twitch.tv/SystemCrafters"))
systemcrafters

Finishing Touches

(report-time-since-load "Finishing Touches")

Tell the module system that init.el is available

Indicates the init.el file is provided by and ends here:

  (provide 'init)
  ;;; init.el ends here

Future

(report-time-since-load "Future")

DONE Add support for zola blogposts writing in org-mode

CLOSED: [2025-01-20 Mon 10:28]

CLOCK: [2024-08-15 Thu 13:47][2024-08-16 Fri 00:57] => 11:10

see ox-zola for exporting org-mode to zola markdown.

Final Words

(report-time-since-load "Final Words")

This is the end of the configuration file.

Add a variable so the file is tangling itself each time it is saved.

Project: ASG

Project: D291358

Project: D292630

Project: D293275

Project: CPMO245424

Project: DAPS

Project: DFTR

Project: FTP

Project: GASIA

Project: MLX92253

Project: RD0000099

Project: CALL

Project: P3D

Project: IEP3D

Project: IT5S

Project: AI90360

Project: VL90407

Project: ABS2GCAL

Project: AC002

Project: ACCM

Project: ADCXH018

Project: ADXP018

Project: ADXT018

Project: ADEHL

Project: ASR

Project: ATSD

Project: APM

Project: AAB

Project: ACO

Project: AIG

Project: AIANOMIN

Project: AIOCR

Project: AIOEE

Project: AISR

Project: SMRTRTST

Project: AIS

Project: AIVI

Project: AIWATSON

Project: AK

Project: AMALTHEA

Project: AWS

Project: FAMS

Project: ANTS

Project: APE

Project: APESC

Project: API

Project: AD

Project: D294440

Project: TP

Project: AR

Project: ATHENA

Project: JIRA

Project: AUD

Project: AFC

Project: ACE

Project: MPTB

Project: BHTI

Project: BHIB

Project: BHIG

Project: BCK

Project: D285444

Project: BDAI

Project: BMTD

Project: BT

Project: BCP

Project: EXN

Project: CDNCR

Project: CDDM

Project: CDNS

Project: GRID

Project: CADT

Project: CALLISTO

Project: CTF

Project: CMGT

Project: CASSA

Project: CBSCR

Project: CBSRMA

Project: CEPXI

Project: CPF

Project: UNILRN

Project: CEP

Project: CA

Project: CERTAUD

Project: CP

Project: CHIEF

Project: CB

Project: CICD

Project: CSP

Project: CRC

Project: CONF

Project: CDOC

Project: D260375

Project: CPMO

Project: CPMOAFI

Project: CPMO148792

Project: CPMO148793

Project: CPMO148794

Project: CPMO148795

Project: CPMO148796

Project: CPMO148797

Project: CPMO211966

Project: CPMO211967

Project: CVHX

Project: CPMO69973

Project: CPTIL

Project: CQBL

Project: CRCIM

Project: CNR

Project: CRS

Project: CUM

Project: CUSTAUD

Project: CMG

Project: CSR

Project: CVS

Project: HRHD

Project: D2024GTO

Project: DASK

Project: DAF

Project: D2024DG

Project: DATALAKE

Project: DOPS

Project: D260374

Project: DATSUPP

Project: D260380

Project: DAT

Project: D291359

Project: DCCT

Project: TSTPC

Project: DSL

Project: D285446

Project: DEMOPS

Project: RD0000113

Project: DENV

Project: DINF

Project: DRD

Project: DEVENG

Project: DQA

Project: DTS

Project: DIP3DOE2

Project: DIP9DOE1

Project: DROP

Project: MLXEMUL

Project: MLXPROG

Project: ERT

Project: EATESS

Project: EKST

Project: EDIMLX

Project: ES

Project: INKLESS

Project: EMC

Project: EMCCC

Project: EUC

Project: ESE

Project: EST

Project: EAT

Project: EA

Project: JBOSS

Project: ER

Project: EQMS

Project: EQUIP

Project: EQMOV

Project: EQREL

Project: ERPPS

Project: ERPPSV1

Project: ESS

Project: EI

Project: ECSA

Project: EVK75301CS

Project: ETCRC

Project: EYI

Project: EDA

Project: FACCORBEIL

Project: FACILDRE

Project: FACILDUE

Project: FACILEF

Project: FACILIEP

Project: FACILKUC

Project: FACILKIEV

Project: FACILSOF

Project: FACILSOFXP

Project: FACTESS

Project: FTTI

Project: FTSK

Project: FA

Project: FAQ

Project: D285445

Project: MPTF

Project: FL

Project: FIR

Project: FIRMX

Project: FAT

Project: FTIM

Project: FUFI

Project: FDKS

Project: GGL4MLX

Project: GAIA

Project: GANYMEDE

Project: GEM

Project: GEMBAAUDIT

Project: GEMINI

Project: RD0000094

Project: GTF

Project: GIT

Project: GTRF

Project: GTRF2XF

Project: RD0000089

Project: GMWTOOL

Project: GCP

Project: GGLDAP

Project: HD

Project: D260372

Project: RD0000026

Project: HOLD

Project: HSM

Project: HRIF

Project: HRTMIS

Project: HRIS

Project: IBIMP

Project: IPIR

Project: IMPALA

Project: INC

Project: IPSAT

Project: RD0000072

Project: INTAUD

Project: INVAPP

Project: INVAPP2009

Project: IAS

Project: INVFRE

Project: IOCAPE

Project: IPBM

Project: IBUY

Project: ICM

Project: IRTC

Project: ISMS

Project: ISPL

Project: IT

Project: IAM

Project: ITBI

Project: ITBCP

Project: RFC

Project: ITINFRA

Project: ITMGT

Project: NANO

Project: IPOT

Project: ITPM

Project: PI

Project: ITSR

Project: SD

Project: ITTLS

Project: IEMSF

Project: IEMST

Project: WSAA

Project: IMFS

Project: ANOMIN

Project: IIJEP

Project: IDA

Project: IBKGC

Project: IFRQ

Project: IRQ

Project: ITSM

Project: ITTRAIN

Project: JAR

Project: JIPO

Project: JNX

Project: KU

Project: KGCC

Project: KGCF

Project: KE

Project: K8S

Project: LAS

Project: LSG

Project: PPM

Project: LABTESS

Project: TSDERF

Project: LABIEP

Project: TSD

Project: LSB

Project: LEHL

Project: LAMP

Project: LEANUPLD

Project: LEC

Project: LGL

Project: LEO

Project: LLA

Project: LLD

Project: LICMGT

Project: MLXLIMEX

Project: LCT

Project: LED

Project: LOHLTR

Project: LOTRI

Project: RD0000057

Project: MGNPROC

Project: MERF

Project: MFGDM

Project: MFG

Project: MPR

Project: MSE

Project: MCAF

Project: MCU32SW

Project: MLXCM

Project: MCM

Project: MCMV2

Project: WEB

Project: MLXSIM

Project: MLXHWIP

Project: MIPS

Project: ITSD

Project: MLP

Project: MPT

Project: MUM

Project: MEMSDEV

Project: MSDT

Project: MTOOLS

Project: MIPSKII

Project: MIS

Project: ADLIB

Project: MAS

Project: MLXDBG

Project: MLXCA

Project: MLXCOMP

Project: MLXDEVTRA

Project: MLXIDDQ

Project: MIFP

Project: MPDSBOX

Project: MTL

Project: PLTF

Project: MLXENV

Project: MLX00009

Project: MLX00071

Project: MLX00078

Project: MLX00079

Project: MLX02402

Project: MLX12106

Project: MLX12109

Project: MLX12116

Project: MLX12117

Project: MLX12123

Project: MLX12126

Project: MLX12127

Project: MLX12127SW

Project: MLX12129

Project: MLX12129SW

Project: MLX12130

Project: MLX12130SW

Project: MLX12132

Project: MLX12133

Project: MLX12134

Project: MLX12135

Project: MLX14016

Project: MLX14101

Project: MLX14603

Project: MLX14605

Project: MLX14606

Project: MLX14607

Project: MLX14608

Project: MLX14609

Project: MLX14610

Project: MLX14611

Project: MLX14612

Project: MLX14612SW

Project: MLX14614

Project: MLX14614SW

Project: MLX14615

Project: MLX16107

Project: TPMS

Project: MLX16603

Project: MLX16801

Project: MLX16904

Project: MLX16906

Project: MLX16908

Project: MLX18001

Project: MLX33300

Project: MLX33301

Project: MLX34102

Project: MLX34103

Project: MLX34104

Project: MLX34105

Project: MLX36101

Project: MLX36102

Project: MLX36103

Project: MLX36104

Project: MLX36105

Project: MLX49102

Project: MLX73290

Project: MLX74190

Project: MLX74191

Project: MLX75023

Project: MLX75024

Project: MLX75025

Project: MLX75026

Project: MLX75027

Project: MLX75030

Project: MLX75301

Project: MLX75305

Project: MLX75307

Project: MLX75308

Project: MLX75310

Project: MLX75312

Project: MLX75320

Project: MLX75322

Project: MLX75403

Project: MLX75411

Project: MLX75412

Project: MLX75413

Project: MLX75421

Project: MLX75423

Project: MLX75431

Project: MOSTFOT

Project: MLX80004

Project: MLX80050

Project: MLX80070

Project: LINSWITCH

Project: MLX80131

Project: MLX80132

Project: MLX80134

Project: MLX80142

Project: MLX80151

Project: MLX80153

Project: MLX80154

Project: MLX80240

Project: MLX80240SW

Project: MLX80250

Project: MLX80251

Project: MLX80252

Project: MLX80300

Project: MLX80302

Project: MLX80339

Project: MLX80X5X

Project: MLX81107

Project: MLX81112

Project: MLX81113

Project: MLX81114

Project: MLX81115

Project: MLX81116

Project: MLX81117

Project: MLX81118

Project: MLX81119

Project: MLX81123

Project: MLX81124

Project: MLX81125

Project: MLX81130

Project: MLX81134

Project: MLX81141

Project: MLX81142

Project: MLX81143

Project: MLX81144

Project: MLX81150

Project: MLX81160

Project: MLX81200

Project: MLX81205

Project: MLX81206

Project: MLX81207

Project: MLX81215

Project: MLX81300

Project: MLX81300SW

Project: MLX81310

Project: MLX81315

Project: MLX81316

Project: MLX81325

Project: MLX81330

Project: MLX81332

Project: MLX81334

Project: MLX81339

Project: MLX81340

Project: MLX81344

Project: MLX81346

Project: MLX81349

Project: MLX81350

Project: MLX81351

Project: MLX81354

Project: MLX81355

Project: MLX81366

Project: MLX82001

Project: MLX82050

Project: MLX82100

Project: MLX82200

Project: MLX82300

Project: MLX82400

Project: MLX83102

Project: MLX83204

Project: MLX83213

Project: MLX83243

Project: MLX83266

Project: MLX90216

Project: MLX90248

Project: M90287

Project: MLX90288

Project: MLX90290

Project: MLX90291

Project: MLX90294

Project: MLX90295

Project: MLX90296

Project: MLX90297

Project: MLX90298

Project: MLX90317

Project: MLX90324

Project: MLX90327

Project: MLX90330

Project: MLX90331

Project: MLX90334

Project: MLX90337

Project: MLX90338

Project: MLX90339

Project: MLX90342

Project: MLX90343

Project: MLX90360

Project: MLX90363

Project: MLX90364

Project: MLX90365

Project: MLX90367

Project: MLX90367SW

Project: MLX90368

Project: MLX90370

Project: MLX90370SW

Project: MLX90371

Project: MLX90371SWIBM

Project: MLX90372

Project: MLX90373

Project: MLX90374

Project: MLX90375

Project: MLX90376

Project: MLX90377

Project: MLX90380

Project: MLX90381

Project: MLX90382

Project: MLX90384

Project: MLX90390

Project: MLX90391

Project: MLX90392

Project: MLX90393

Project: MLX90394

Project: MLX90396

Project: MLX90397

Project: MLX90398

Project: MLX90399

Project: MLX90405

Project: MLX90405SDI

Project: MLX90411

Project: MLX90412

Project: MLX90415

Project: MLX90416

Project: WAMD

Project: MLX90420

Project: MLX90423

Project: MLX90424

Project: MLX90425

Project: MLX90427

Project: MLX90429

Project: MLX90430

Project: MLX90431

Project: MLX90435

Project: MLX90451

Project: MLX90500

Project: MLX90510

Project: AE90510

Project: MLX90512

Project: MLX90513

Project: AE90513

Project: MLX90514

Project: MLX90518

Project: MLX90614

Project: MLX90615

Project: MLX90616

Project: MLX90617

Project: MLX90620

Project: MLX90630

Project: MLX90631

Project: MLX90632

Project: MLX90635

Project: MLX90638

Project: MLX90640

Project: MLX90641

Project: MLX90642

Project: MLX90650

Project: MLX90670

Project: MLX90807

Project: MLX90809

Project: MLX90812DB

Project: MLX90817

Project: MLX90818

Project: MLX90819

Project: MLX90820

Project: MLX90821

Project: MLX9082X

Project: MLX90829

Project: MLX90830

Project: MLX90332

Project: MLX90834

Project: MLX90835

Project: MLX90900

Project: MLX90901

Project: MLX91206

Project: MLX91207

Project: MLX91208

Project: MLX91209

Project: MLX91209VA

Project: MLX91210

Project: MLX91211

Project: MLX91214

Project: MLX91216

Project: MLX91218

Project: MLX91220

Project: MLX91222

Project: MLX91224

Project: MLX91230

Project: MLX91235

Project: MLX91236

Project: MLX91237

Project: MLX91241

Project: MLX91299

Project: MLX91803

Project: MLX91804

Project: MLX91805

Project: MLX91807

Project: MLX91810

Project: MLX92211

Project: MLX92212

Project: MLX92216

Project: MLX92221

Project: MLX92223

Project: MLX92231

Project: MLX92242

Project: MLX92255

Project: MLX92292

Project: MLX92300

Project: MLX92342

Project: MLX92352

Project: MLX92362

Project: MLX92442

Project: MLX99528

Project: MLX99999

Project: MIR

Project: MLXLEANTRA

Project: MLXAGILE

Project: MLXCCT

Project: MLXIDE

Project: MLXLAN18

Project: MMC16

Project: MMFCVT

Project: MMOS

Project: MSDEBVX

Project: MSDEHL

Project: MULAN

Project: PNC

Project: MYMLX

Project: NGS

Project: BZLAB

Project: BZTOOLS

Project: EGN

Project: MAILGEN2

Project: NOS

Project: NSYNC

Project: NM

Project: OBSG

Project: OCAP

Project: TDONB

Project: OCC

Project: ODA

Project: OP

Project: ORACPQ

Project: OR12

Project: R1228

Project: OU

Project: D260373

Project: OSP

Project: OV2

Project: OSPIPR

Project: OCS2

Project: PD

Project: PCDP

Project: PTMGT

Project: PNT

Project: P360

Project: PRTNR

Project: PAT

Project: RD0000007

Project: PO

Project: PCN

Project: PCNLEAN

Project: PSI

Project: PDP2

Project: PESUPIEP

Project: PET

Project: PPRO

Project: PFASFREE

Project: PFUSV2

Project: MLX75321

Project: PL007

Project: PL

Project: PL033

Project: PLPSP

Project: PLCM

Project: PLON

Project: PMWS23

Project: PMO0000435

Project: PDCH

Project: PMO0000619

Project: PMO0000620

Project: PMO0000621

Project: PMO0000622

Project: PMO0000623

Project: PMO0000624

Project: PMO0000625

Project: PMO0000626

Project: PMO0000627

Project: PMO0000628

Project: PMO0000629

Project: PMO0000633

Project: PMO0000725

Project: PMO0000745

Project: PTRMV

Project: PMO0000812

Project: PMO0000834

Project: PMO0000835

Project: PMO0000836

Project: OSPC

Project: CORDATMIG

Project: PMO453

Project: PCPC

Project: POS

Project: POL

Project: D257644

Project: PSCHINAFAE

Project: PIR

Project: ADT

Project: D287146

Project: PPPURE

Project: PSW

Project: PPP2SPC

Project: PRMF

Project: PRM

Project: PRBPROC

Project: PRB

Project: PROC

Project: PIQCM

Project: PWOW

Project: PTP

Project: PDP

Project: PMMGT

Project: POP

Project: PRDCTRL

Project: PCS2

Project: D293273

Project: PIRE

Project: PRGS

Project: PFUS

Project: PTD

Project: PTC05

Project: PTC05MGR

Project: POR

Project: PVEHL

Project: PLPF

Project: PLSC

Project: PLS

Project: PPH

Project: PVN

Project: QCM

Project: QIPO

Project: EQDS

Project: QRQC

Project: BZEVB3

Project: RPB

Project: RD0000008

Project: RD0000019

Project: RD0000034

Project: RD0000055

Project: RD0000061

Project: RD0000062

Project: RD0000069

Project: RD0000070

Project: RD0000074

Project: RD0000075

Project: RD0000081

Project: RD0000082

Project: RD0000083

Project: RD0000084

Project: RD0000085

Project: RD0000087

Project: RD0000090

Project: RD0000091

Project: RD0000092

Project: RD0000093

Project: RD0000098

Project: RD0000103

Project: RD0000123

Project: RD0000125

Project: RD0000130

Project: RD000127

Project: RD0000132

Project: CSA

Project: RUC

Project: RTWM

Project: REL

Project: REMT

Project: RRR

Project: D290190

Project: RPR

Project: RR

Project: RMA

Project: RL

Project: RMUC

Project: RUN

Project: RDECK

Project: RVM

Project: SAHL

Project: S4S

Project: SPDC

Project: SAXT011

Project: SCPM

Project: SCGAPP

Project: SCREENING

Project: SDM

Project: SE008TC001

Project: SEC

Project: SO

Project: SP

Project: SNISRFC

Project: SENTIP

Project: SLM

Project: SIT

Project: SFA

Project: SFP

Project: SIA

Project: LEANSIT

Project: SJL

Project: SFTD

Project: SDEV

Project: SSC

Project: SPS

Project: SPC4MLX

Project: SPC4MLX2

Project: SPECREQ

Project: SPL

Project: QUA

Project: SQAS

Project: SCN

Project: RD0000116

Project: SM

Project: STO

Project: SCM

Project: SCAR

Project: SMR

Project: SAI

Project: SWCHMISC

Project: SWCC

Project: SWDT

Project: SYMEX

Project: SAEOM

Project: SAEDRV

Project: TES

Project: BHTD

Project: TPDEMO

Project: TBS

Project: TCOR

Project: TDA

Project: TTSM

Project: TTSC

Project: TSFP

Project: TS

Project: TEST

Project: TCTO

Project: TDCE

Project: TDW

Project: TESTHW

Project: TI

Project: TSR

Project: TSTSOL

Project: TSDEV

Project: TPT

Project: THIL

Project: TTRK

Project: TMIS

Project: TB

Project: OPTO

Project: RD0000017

Project: TAP

Project: TRIP

Project: STS107

Project: STS334

Project: TSC

Project: UBC

Project: BZULT

Project: D260379

Project: VCSA

Project: VIPR

Project: VUPLD

Project: VISOPS

Project: VISUAL

Project: VMW

Project: VSA

Project: WIKIOSK

Project: WIN10

Project: XFBI

Project: MLXHWX

Project: XT011

Project: XTA

Project: XTAOLD

Project: XTATES

Project: XTC

Project: XTDHELP

Project: XRTP

Project: XTD

Project: CCB4XTD

Project: XTS

Project: XTT

Project: YUB