Emacs Configuration

Table of Contents

System Dependent Variables

Hopefully only need to change these to get setup on a new machine.

(defconst jmn-config-location "~/dotfiles/emacs.org"
  "Location of literate org file used to automaitically tangle to init.el")

(let ((gtd-path '((gnu/linux . "~/Documents/gtd/")
                 (windows-nt . "c:/Users/nehlsj/OneDrive/Documents/gtd/"))))
  (defconst jmn-gtd-directory (alist-get system-type gtd-path)
  "Location of gtd org files: projects, inbox, next,  whip, journal, and habits"))

(defconst jmn-connected-systems '("lat" "dsk" "xps")
  "Systems which should download packages. Others get the 'pure' configuration.")

(defconst jmn-connected-extras t
  "Flag of weather to use the purely astetic packages or not on connected system")

(defconst jmn-pureplus-systems '("lat" "testbed")
  "Systems which use the pure setup with the plus packages")

(defconst jmn-dark-mode t
  "Do we want Emacs in a dark mode? Note: no dark-mode for windows as of now")

(defconst jmn-font-height-alist '(("xps" . 110)
                                  ("dsk" . 110)
                                  ("lat" . 115))
  "Set text-hight for each machine-- default will be 100")

Packages and Management

Auto-tangle to init file

;; Automatically tangle our Emacs.org config file when we save it
(defun efs/org-babel-tangle-config ()
  (when (string-equal (buffer-file-name)
                      (expand-file-name jmn-config-location))
    ;; Dynamic scoping to the rescue
    (let ((org-confirm-babel-evaluate nil))
      (org-babel-tangle))))

(add-hook 'org-mode-hook
          (lambda () (add-hook 'after-save-hook #'efs/org-babel-tangle-config)))

Package management

Archives: elpa is default, melpa is community.

  • Use-package is macro that is used like a package manager. Use-Package Documentation
  • Update all packages with M-x package-upgrade-all
(if (member system-name jmn-connected-systems)
    (defconst jmn-pure nil "Indicating if we are pure or using packages")
  (if (member system-name jmn-pureplus-systems)
      (defconst jmn-pure "plus" "Indicating we are using pure config with plus packages")
    (defconst jmn-pure t "Indicating if we are pure, not using any packages")))

;; flag
(defconst jmn-term (not (display-graphic-p (selected-frame)))
  "Indicating if emacs is being run within a terminal or not")

(defun jmn-connect-to-repositories ()
  (require 'package)
  (setq package-archives '(("melpa" . "https://melpa.org/packages/")
                           ("elpa" . "https://elpa.gnu.org/packages/")))

  (if (version< emacs-version "29")
      (progn
        (setq package-check-signature nil)  ;; bugfix: keys are not installed
        (setq gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3")))  ;; to load melpa

  (package-initialize)

  (unless package-archive-contents
    (package-refresh-contents))

  (unless (package-installed-p 'use-package)
    (package-install 'use-package));; Initialize use-package on non-Linux platforms

  (require 'use-package)
  (setq use-package-always-ensure t) ; no need for :ensure t for each package.
  (setq use-package-verbose t)) ; log configure/loading messages in *Messages*

(if jmn-pure
      (defmacro use-package (&rest _))  ;; define use-package macro to do nothing
  (jmn-connect-to-repositories)) ;; connect to repos  and use use-package macros to config below

TODO Pure-plus

Code above is for pure (offline) and non-pure (online). The below is for a pure = 'plus', when we include a few packages.

(defun jmn-install-plus-packages()
  (interactive)
  (jmn-connect-to-repositories)
  ;;install for all versions
  (dolist (pluslist '(which-key undo-tree ws-butler prescient ivy ivy-rich ivy-prescient counsel htmlize evil-nerd-commenter))
    (package-install pluslist 1))
  ;; install if version 27 and above
  (if (version<= "27" emacs-version)
      (progn
        ;; For emacs 28
        ;; magit requires ver >= 2.24 which is greater than built in version
        ;; Warnings buffer gives the following advice
        ;; TODO -- need it install seq-2.24 by hand in package-list-packages
        (setq package-install-upgrade-built-in t)
        (dolist (pluslist '(seq magit markdown-mode))
          (package-install pluslist 1))
        (unload-feature 'seq t)
        (require 'seq))))

If it fails here, we need to add the packages to ~/.emacs.d/elpa

(if (string= jmn-pure "plus") ;; turn on all of the plus modes
    (progn
      (package-initialize)
      (require 'prescient)
      (dolist (pluslist '(which-key-mode undo-tree-mode ws-butler-mode prescient-persist-mode ivy-mode ivy-rich-mode ivy-prescient-mode counsel-mode))
        (funcall pluslist 1))
      ;; evil nerd commenter
      (global-set-key (kbd "C-;") 'evilnc-comment-or-uncomment-lines)))

TODO can the above do list be replaced with (package-initialize)?

General Emacs

UI Configurations

Basic

;;;; Basic ;;;;
(setq inhibit-startup-message t)                ; inhibit startup message
(tool-bar-mode -1)                            ; remove toolbar
(menu-bar-mode -1)                              ; Disable the menu bar
(scroll-bar-mode -1)                          ; remove side scrollbar
(tooltip-mode -1)                               ; Disable tooltips
(set-fringe-mode 6)                             ; Give some breathing room
(setq visible-bell t)                         ; Set up the visible bell
(save-place-mode 1)                             ; Open file where last visited
(setq Buffer-menu-name-width 35)                ; give name more room
(setq-default indicate-empty-lines t)           ; indicate long lines
(defalias 'yes-or-no-p 'y-or-n-p)               ; Make  =yes or no= prompts shorter
(column-number-mode 1)                          ; show column number in modeline
(winner-mode 1)                                 ; redo and undo window changes
(if (version<= "28" emacs-version)
    (repeat-mode 1))                 ; multi-key sequences can be repeated

;; hooks
;; This modifies too much and makes git/diffs difficult-- need to find better solution
;; (if jmn-pure
;;     (add-hook 'before-save-hook 'whitespace-cleanup)); non-pure uses ws-butler

(unless (eq system-type 'windows-nt)
  (add-hook 'text-mode-hook 'flyspell-mode))    ; enable spellcheck on text mode

;; The following help syncing
(global-auto-revert-mode 1)                     ; refresh buffer if changed on disk
(setq auto-revert-use-notify nil)               ; don't notify?
(setq auto-revert-verbose nil)                  ;

;; Mouse
(setq mouse-yank-at-point t) ;; yank at cursor not click location

;; By default Emacs prefers `.elc' to `.el' in all cases, `load-prefer-newer's
;; alway prefers the last-edited file, preventing this problem.
(setq load-prefer-newer t)

;; Buffer Menu
(add-hook 'Buffer-menu-mode-hook 'hl-line-mode)
(add-hook 'bookmark-bmenu-mode-hook 'hl-line-mode)

Backup files

Save backups in .emacs.d.

(setq backup-directory-alist
      '( ("." . "~/.emacs.d/filebackups")))

Transparency

Set transparency

(defun transparency (value)
  "Sets the transparency of the frame window. 0=transparent/100=opaque"
  (interactive "nTransparency Value 0 - 100 opaque:")
  (if (version<= "29" emacs-version)
      (set-frame-parameter nil 'alpha-background value)
    (set-frame-parameter (selected-frame) 'alpha value)))

Scrolling

;;;; Scrolling ;;;;
;; Fully redraw the display before it processes queued input events.
(setq redisplay-dont-pause            t)

;; Number of lines of continuity to retain when scrolling by full screens
(setq next-screen-context-lines       2)  ;; golden ration pkg will replaced this if loaded

;; only 'jump' when moving this far off the screen
(setq scroll-conservatively         100)
(setq scroll-step                     1) ;; Keyboard scroll one line at a time
(setq mouse-wheel-progressive-speed nil) ;; Don't accelerate scrolling
(setq mouse-wheel-follow-mouse        t) ;; Scroll window under mouse
(setq fast-but-imprecise-scrolling    t) ;; No (setq less) lag while scrolling lots.
(setq auto-window-vscroll           nil) ;; Cursor move faster
(setq pixel-scroll-precision-mode     1) ;; pixel based scrolling
  • Fast Scroll

    To ensure scrolling is fast in Emacs, disable non-essential things while the window is being scrolled:

    (use-package fast-scroll
      :defer 2
      :config
      (add-hook 'fast-scroll-start-hook (lambda () (flycheck-mode -1)))
      (add-hook 'fast-scroll-end-hook (lambda () (flycheck-mode 1)))
      (fast-scroll-config)
      (fast-scroll-mode 1))
    

Undo-tree

  • C-x u visualizes undo history as a tree for easy navigation
  • C-_ undo
  • M-_ redo Archive: elpa
(use-package undo-tree
  :defer 2
  :config
  (global-undo-tree-mode 1)
  (setq undo-tree-auto-save-history nil)) ;; don't save ~undo-tree~ file

Modeline

NOTE: The first time you load your configuration on a new machine, you’ll need to run M-x all-the-icons-install-fonts so that mode line icons display correctly. (Fixed?)

  ;;;; Modeline ;;;;
(if jmn-connected-extras
    (use-package all-the-icons
      :defer 1
      :init
      (when (and (not (member "all-the-icons" (font-family-list))) ;; autoinstall fonts
                 (window-system))
        (all-the-icons-install-fonts t))))

(use-package doom-modeline
  :init (doom-modeline-mode 1)
  :custom ((doom-modeline-height 10)
           (doom-modeline-vcs-max-length 20))) ;; default is 12

Auto-clean white space

Archive: melpa

;;;; Cleanup whitespace only on lines I touched ;;;;
(use-package ws-butler
    :defer 4
    :hook ((text-mode . ws-butler-mode)
           (prog-mode . ws-butler-mode)))

Keybindings

  • Eventually I will assume repeat-mode is builtin and remove the C-o bindings to 'other-window
    • repeat mode is built in starting in Emacs 28
;; general improvements
(global-set-key (kbd "<escape>") 'keyboard-escape-quit)
(global-set-key (kbd "C-x C-b") 'buffer-menu)   ;; open buffer menue in current buffer
(global-set-key (kbd "C-x C-k") 'kill-current-buffer)   ;; "C-x k" asks which buffer
(global-set-key (kbd "C-o") 'other-window)  ;; default is "C-x o"
(global-set-key (kbd "M-o") 'previous-multiframe-window)
(global-set-key (kbd "C-c C-c") 'eval-buffer)
(global-set-key (kbd "C-c C-r") 'eval-region)

;; make font bigger/smaller.
(global-set-key (kbd "C-=") 'text-scale-increase)
(global-set-key (kbd "C--") 'text-scale-decrease)
(global-set-key (kbd "C-0") 'text-scale-adjust)

;; f1 is a leader key for help
;; f2 is a leader key for 2 columns
;; f3 is kmacro-start-macro-or-insert-counter -- start recording a macro
;; f4 ends the macro recording
;; f5-f10 are clear by default
;; f11 is toggle full screen
;; f12 is clear by default

;; See recentfiles
(global-set-key (kbd "<f5>") 'compile)
;; See recentfiles
(global-set-key (kbd "<f6>") 'recentf-open-files)

;; writing/editing
(global-set-key (kbd "<f9>") 'ispell-word)
(global-set-key (kbd "<f10>") 'dictionary-lookup-definition)

;; Buffer-menu-mode
(define-key Buffer-menu-mode-map (kbd "C-o") 'other-window)
(define-key Buffer-menu-mode-map (kbd "M-o") 'previous-multiframe-window)
;; "o" opens in another buffer and moves focus
;; "C-M-o" opens in another buffer and keeps focus in the Buffer-menu
(define-key Buffer-menu-mode-map (kbd "C-M-o") 'Buffer-menu-switch-other-window)

;; xref
(with-eval-after-load 'xref
  (define-key xref--xref-buffer-mode-map (kbd "C-o") 'other-window)
  (define-key xref--xref-buffer-mode-map (kbd "o") 'xref-show-location-at-point))

;; bookmark-bmenue
(with-eval-after-load 'bookmark
  (define-key bookmark-bmenu-mode-map (kbd "C-o") 'other-window)
  (define-key bookmark-bmenu-mode-map (kbd "M-o") 'previous-multiframe-window)
  (define-key bookmark-bmenu-mode-map (kbd "C-M-o") 'bookmark-bmenu-switch-other-window))

;; compilation-modes
(defun jmn-compilation-keybindings()
  (define-key compilation-mode-map (kbd "C-o") 'other-window)
  (define-key compilation-mode-map (kbd "C-M-o") 'compilation-display-error))

(add-hook 'compilation-mode-hook 'jmn-compilation-keybindings)

;; grep-mode
(defun jmn-grep-keybindings()
  (define-key grep-mode-map (kbd "o") 'compilation-display-error)
  (define-key grep-mode-map (kbd "C-o") 'other-window))

(add-hook 'grep-mode-hook #'jmn-grep-keybindings)

;; adjust windows with keybindings
(global-set-key (kbd "C-<up>") 'enlarge-window)
(global-set-key (kbd "C-<down>") 'shrink-window)
(global-set-key (kbd "C-<right>") 'enlarge-window-horizontally)
(global-set-key (kbd "C-<left>") 'shrink-window-horizontally)

;; non-org C-c <blank> bindings
(global-set-key (kbd "C-c r") 'revert-buffer)
(global-set-key (kbd "C-c =") 'vc-diff) ;; also bound to "C-x v ="

Dired

More to do at here: dired-open

  • "W" will open file in native environment (including another Emacs)
  • "(" toggle file info
  • M-x du shows the size of the files in the buffer (toggle for human readable)
;;;; Dired ;;;;
(add-hook 'dired-mode-hook 'dired-hide-details-mode) ;; hide default -- '(' to toggle
(add-hook 'dired-mode-hook 'hl-line-mode)

(with-eval-after-load 'dired
  (require 'dired-x) ;; may need to be (load "dired-x" for old versions
  (setq dired-auto-revert-buffer t)  ;; auto-revert dired when revisiting
  (setq dired-vc-rename-file t)      ;; renamed files are under version control
  ;; good for my persional machine
  ;; (setq dired-listing-switches "-agho --group-directories-first" )
  ;; good as a user of
  (setq dired-listing-switches "-alh --group-directories-first" )
  (setq find-ls-option '("-print0 | xargs -0 ls -agho" . ""))
  (setq dired-dwim-target t) ;; guess other dired directory for copy and rename
  (setq wdired-allow-to-change-permissions t)
  (define-key dired-mode-map (kbd "C-o") 'other-window)
  (setq dired-guess-shell-alist-user '(
                                       ("\\.png\\'" "shotwell")
                                       ("\\.jpg\\'" "shotwell")
                                       ("\\.jpeg\\'" "shotwell")
                                       ("\\.mp4\\'" "vlc")
                                       ("\\.avi\\'" "vlc")
                                       ("\\.iso\\'" "vlc")
                                       ("\\.webm\\'" "vlc")
                                       ("\\.mkv\\'" "vlc")
                                       ("\\.odt\\'" "libreoffice")
                                       ("\\.docx\\'" "libreoffice")
                                       ("\\.mp3\\'" "rhythmbox")
                                       ("\\.html\\'" "firefox")
                                       ("\\.epub\\'" "ebook-viewer")
                                       ("\\.pdf\\'" "evince")
                                       ("\\.ipynb\\'" "code")
                                       ("\\.py\\'" "python")
                                       ("\\.sh\\'" "bash")))
  ;;;; maybe use again in the future -- does not align with pure emacs
  ;; (if (version<= "28" emacs-version)
      ;; (setf dired-kill-when-opening-new-dired-buffer t))
  )

;; use a single dired session ;; needed for emacs version < 28
;; (use-package dired-single
;;   :after dired
;;   :defer t
;;   :hook (dired-mode .
;;                     (lambda () (define-key dired-mode-map [remap dired-find-file]
;;                                            'dired-single-buffer)
;;                       (define-key dired-mode-map
;;                                   [remap dired-mouse-find-file-other-window]
;;                                   'dired-single-buffer-mouse)
;;                       (define-key dired-mode-map [remap dired-up-directory]
;;                                   'dired-single-up-directory))))
(if jmn-connected-extras
    (use-package all-the-icons-dired
      :after dired
      :defer t
      :hook (dired-mode . all-the-icons-dired-mode)))

Proced

;;;; Proced ;;;;
(defun proced-settings ()
    (proced-toggle-auto-update 5)) ;; auto update every 4 seconds

(add-hook 'proced-mode-hook 'proced-settings)

Native Compilation

Suppress compilation warnings

;;;; Native comp ;;;;
(setq native-comp-async-report-warnings-errors nil)

TODO Goto last change

Check with other environments to settle on "C-;" or "M-;" – the other us used for evil-nerd comment

  • we cleared "C-;" from Flyspell above
(use-package goto-last-change
  :ensure t
  :bind ("C-c g" . goto-last-change))

Input Buffer, Directory Search

Ivy, Ivy-Rich, and Counsel

Ivy displays vertical completions of input buffer.

  • When we want the text accepted over the suggest completion use "C-M-j" which executes (ivy-immediate-done) source
(use-package ivy
  :after counsel
  :defer t
  :config
  (ivy-mode 1)
  ;; remove ^ on the inputbuffer
  (setq ivy-initial-inputs-alist nil))

Ivy-Rich

Ivy-rich provides information to display in input buffer to counsel.

(use-package ivy-rich
  :after counsel
  :init
  (ivy-rich-mode 1))

Ivy-prescient

prescient.el provides some helpful behavior for sorting Ivy completion candidates based on how recently or frequently you select them. This can be especially helpful when using M-x to run commands that you don’t have bound to a key but still need to access occasionally.

(use-package ivy-prescient
  :after counsel
  :custom
  (ivy-prescient-enable-filtering nil)
  :config
  ;; Uncomment the following line to have sorting remembered across sessions!
  (prescient-persist-mode 1)
  (ivy-prescient-mode 1))

Counsel

Counsel displays ivy-rich info along with suggestions in input buffer.

  • M-o allows access to help in input buffer. Archive: elpa, melpa
(use-package counsel
  :defer t
  :bind (("M-x" . counsel-M-x)      ; displays ivy-rich info in minibuffer
         ("C-x C-f" . counsel-find-file)
         :map minibuffer-local-map
         ("C-r" . 'counsel-minibuffer-history)
         ))

Helpful and Which-key

Helpful

Better version of help. We remap normal help keys to Helpful's versions. Archive: melpa

(use-package helpful
  :defer 3
  :commands (helpful-callable helpful-variavle helpful-command helpful-key)
  :custom
  (counsel-describe-function-function #'helpful-callable)
  (counsel-describe-variable-function #'helpful-variable)
  :bind
  ([remap describe-function] . counsel-describe-function)
  ([remap describe-command] . helpful-command)
  ([remap describe-variable] . counsel-describe-variable)
  ([remap describe-key] . helpful-key))

Which-key

Archive: elpa, melpa

(use-package which-key
  :defer 1
  :config(which-key-mode)
  (setq which-key-idle-delay 0.8))

Font size

(defun jmn-set-font-height(value)
  (interactive "nFont height (default is 100): ")
  (set-face-attribute  'default nil :height value))

(jmn-set-font-height (alist-get (system-name) jmn-font-height-alist
                                100 nil 'string=)) ;; default is 100

(setq text-scale-mode-step 1.05)

General Helper Function

Quickly load init

Defined earlier in the init so it will load even if there is an error later in the config

(defun jmn-load-init ()
  (interactive)
  (if (version<= "27" emacs-version)
      (load "~/.emacs.d/init.el")
    (load "~/.emacs")))

jmn-vscode-current-buffer-file-at-point

(defun jmn-vscode-current-buffer-file-at-point ()
  (interactive)
  (start-process-shell-command "code"
                               nil
                               (concat "code --goto "
                                       (buffer-file-name)
                                       ":"
                                       (number-to-string (line-number-at-pos))
                                       ":"
                                       ;; +1 who knows why
                                       (number-to-string (+ 1 (current-column))))))

  (defun jmn-vscode-current-project-root ()
    (interactive)
    (start-process-shell-command "code" nil (concat "code -r " (project-root (project-current t)))))

  (defun jmn-vscode-current-buffer-in-project-root ()
    (interactive)
    (start-process-shell-command "code" nil (concat "code -r " (project-root (project-current t))
                                                    " --goto "
                                                    (buffer-file-name)
                                                    ":"
                                                    (number-to-string (line-number-at-pos))
                                                    ":"
                                                    ;; +1 who knows why
                                                    (number-to-string (+ 1 (current-column))))))

(define-key global-map (kbd "<f12>")
            'jmn-vscode-current-buffer-in-project-root)

jmn-remove-M-1-9-from-mode-map

Clear up to allow the tab-bar keybindings.

(defun jmn-remove-M-1-9-from-mode-map (modemap)
  "remove 'M-[d]' keybinding from a mode-map"
  (dolist (num '("1" "2" "3" "4" "5" "6" "7" "8" "9"))
    (define-key modemap (kbd (concat "M-" num)) nil)))

Remove use-package s-expressions

 (defun remove-function-sexps (functionName)
   "Remove all sexps with the input car in the current buffer."
     (save-excursion
       (goto-char (point-min))
       ;; break up the string so it is not replaced here
       (while (re-search-forward (concat "(" functionName) nil t)
         (backward-sexp)
         (backward-char)
         (kill-sexp))))

(defun remove-if-var-sexps (varName)
  "Remove all sexps which start with 'if input' in the current buffer."
     (save-excursion
       (goto-char (point-min))
       ;; break up the string so it is not replaced here
       (while (re-search-forward (concat "(if " varName) nil t)
         (backward-sexp)
         (backward-sexp)
         (backward-char)
         (kill-sexp))))

 (defun jmn-purify-my-config-and-save ()
   "Create a 'pure' configuration file '~/.emacs.d/pure_init.el' by removing impure configuration sexps."
   (interactive)
   (find-file "~/.emacs.d/init.el")
   (remove-function-sexps "use-package")
   (remove-if-var-sexps "jmn-connected-extras")
   (set-visited-file-name "~/.emacs.d/pure_init.el")
   (save-buffer)
   (kill-current-buffer))

Emacs in the terminal

(if (and jmn-term (version<= "29" emacs-version))
    (progn
      (xterm-mouse-mode 1)
      (setopt mode-line-end-spaces nil)  ;; Only matters for jmn-pure, doom-modeline is uneffected
      (set-display-table-slot standard-display-table 'vertical-border (make-glyph-code ?│))))

MISC

Async-shell-command

 ;; Suppress Async Shell Command  buffers
(add-to-list 'display-buffer-alist '("*Async Shell Command*" display-buffer-no-window (nil)))
;; Could use this instead
;; (setq async-shell-command-display-buffer nil)

;; Only have one Async Shell Command buffer and it holds most recent output
(setq async-shell-command-buffer 'rename-buffer)

General Development

Indentation

Guess the indentation offset and 'indent-tabs-mode' from the file using internal heuristics.

(use-package dtrt-indent
  :hook (prog-mode . (lambda () (dtrt-indent-mode 1))))

Prog-mode

;;Prog-mode
(add-hook 'prog-mode-hook 'display-line-numbers-mode)
(add-hook 'prog-mode-hook 'visual-line-mode)
(add-hook 'prog-mode-hook 'hs-minor-mode) ;; hide show, use C-c @ prefix
(unless (eq system-type 'windows-nt)
    (add-hook 'prog-mode-hook 'flyspell-prog-mode)) ;; flyspell-comments

Parens/delimiters

(show-paren-mode    1) ; Highlight parentheses pairs.

TODO Rainbow Delimiters

Need to test the org-mode hook Archive: melpa

(use-package rainbow-delimiters
  :defer t
  :hook ((prog-mode . rainbow-delimiters-mode)
    (org-mode . (lambda () (rainbow-delimiters-mode 0)))))

Smartparens

Auto-creates closing parenthesis and bar and, smartly, writes it over if it is typed. Archive: melpa

(use-package smartparens
  :hook (prog-mode . smartparens-mode))

Magit

Magit Documentation Archive: melpa

(use-package magit
  :commands (magit-status)
  :custom
  (magit-display-buffer-function
   #'magit-display-buffer-same-window-except-diff-v1))


;; moved here incase magit installed on a pure machine ;; replace original keybinding?
(with-eval-after-load 'magit
  (add-hook 'magit-status-mode-hook
            (lambda () (define-key magit-mode-map (kbd "C-<tab>") nil)))
  (jmn-remove-M-1-9-from-mode-map magit-mode-map))

TODO Git-Gutter

  • [ ] replace with diff-hl mode?

Git-gutter-fringe displays git-gutter in the fringe

(use-package git-gutter
  :hook ((prog-mode . git-gutter-mode)
         ;; (text-mode . git-gutter-mode) ;; effects org-ellipsis
         )
  :config (setq git-gutter:update-interval 0.02))

(use-package git-gutter-fringe
  :after git-gutter
  :config
  (set-face-background 'git-gutter-fr:deleted  (face-background 'default))
  (set-face-foreground 'git-gutter-fr:deleted  "red"))

Highlight-indent-guide

(if jmn-connected-extras
    (use-package highlight-indent-guides
      :defer 2
      :hook prog-mode
      :config
      (setq highlight-indent-guides-method 'fill)
      (setq highlight-indent-guides-auto-odd-face-perc -7)
      (setq highlight-indent-guides-auto-even-face-perc 7)))

Company-Mode

Currently company-mode gets called by lsp-mode by default, providing the auto-complete box that lsp presents it's suggestions in.

(use-package company
  :ensure t
  :hook (eglot-managed-mode . company-mode)
  :custom
  (company-minimum-prefix-length 1)
  (company-idle-delay 0.5))

company-box-mode

Brings up a another box with information about the highlighted recommended item in the company/lsp box.

(use-package company-box
  :after company
  :hook (company-mode . company-box-mode))

company-prescient

Help in sorting the completion results.

(use-package company-prescient
  :after company
  :config
  (company-prescient-mode +1))

Treemacs

(use-package treemacs
  :ensure t
  :defer t
  :bind (("C-c t" . treemacs ))
  :config
  (treemacs-project-follow-mode t) ;; open root of selected file in treemacs
  (setq treemacs-width 30))

Eglot

Combines with flymake, eldoc, company-mode, and xref to make an IDE experience.

  • can search in a company completion box!
  • Flymake shows underlines: errors in red, warnings in blue
  • "C-h ." provides eldoc info at the point

Function/keybinding learning area:

  • eglot
    • eglot find declaration
    • eglot find definition
    • eglot format buffer or region
    • eglot rename
  • xref (uses eglot for the backend)
    • xrf-find-defintions "M-." –return back with "M-,"
      • [ ] uses tags so replace?
    • xrf-find-references "M-?"
(if jmn-connected-extras
    (use-package eglot
      :defer 1
      :ensure-system-package
      ((bash-language-server . "sudo dnf install -y nodejs-bash-language-server")
       (pyright . "pip install pyright")
       (clangd . "sudo dnf install -y clang-tools-extra")
       (texlab . "cargo install --git https://github.com/latex-lsp/texlab --locked --tag v5.22.0") ;; ensure texlab is added to bash PATH
       )
      :hook ((sh-mode . eglot-ensure)
             (python-mode . eglot-ensure)
             (c-mode . eglot-ensure)
             (c++-mode . eglot-ensure)
             (haskell-mode . eglot-ensure)
             (LaTeX-mode . eglot-ensure))
      :config
      ;; make shorter ones later?
      ;; use "M-." for xref-find-definitions
      ;; use "M-?" for xref-find-references
      ;; use "M-g n" and "M-g p" to go to next and previous location
      ;; use ... for xref-pop-marker-stack
      (define-prefix-command 'eglot-prefix)
      (global-set-key (kbd "C-x e") 'eglot-prefix)
      (define-key eglot-prefix (kbd "r") 'eglot-rename)
      (define-key eglot-prefix (kbd "o") 'eglot-code-action-organize-imports)
      (define-key eglot-prefix (kbd "h") 'eldoc)
      (define-key eglot-prefix (kbd "d") 'eglot-find-declaration)
      (define-key eglot-prefix (kbd "a") 'eglot-format-buffer)
      (define-key eglot-prefix (kbd "n") 'flymake-goto-next-error)
      (define-key eglot-prefix (kbd "p") 'flymake-goto-prev-error)
      (define-key eglot-prefix (kbd "b") 'flymake-show-buffer-diagnostics)))

Breadcrumb

Add breadcrumbs to the top of buffers. Works great with Eglot.

(use-package breadcrumb
  :ensure t
  :config
  (breadcrumb-mode))

CMake

Lsp-mode requires the language server on the system: pip install cmake-language-server.

CMake-mode

Archive: melpa

(use-package cmake-mode
  :defer t
  :ensure-system-package ((cmake . "sudo dnf install -y cmake"))
  :mode ("CMakeLists\\.txt\\'" "\\.cmake\\'"))

(use-package cmake-font-lock
  :ensure t
  :after cmake-mode
  :config (cmake-font-lock-activate))

TODO CMake project

In the source directory containing CMakeLists.txt run M-x cmake-project-configure-project. As a preference, use the /bin/ option to keep the cmake files out of the source directory. After this, the compile automatically holds the correct command. Archive: melpa

(use-package cmake-project
  :defer 2
  ;; :hook ((c++-mode . cmake-project-mode )
  ;;        (c-mode . cmake-project-mode))
  )

Yasnippet

Archive: elpa, melpa

(if jmn-connected-extras
    (use-package yasnippet
      :defer 1
      :config
      (yas-global-mode 1))

  (use-package yasnippet-snippets
    :after yas-minor-mode))  ;; load basic snippets from melpa

TODO Flyspell

C-, flyspell-goto-next-error C-. flyspell-auto-correct-word

;; free up C-; for evil-nerd-commenter
(with-eval-after-load 'flyspell
  (define-key flyspell-mode-map (kbd "C-;") nil))
   ;; (progn
   ;;   (define-key flyspell-mode-map (kbd "C-;") nil)
   ;;   (define-key flyspell-mode-map (kbd "M-;") flyspell-auto-correct-previous-word)))

Evil nerd commenter

Archive: melpa

(use-package evil-nerd-commenter
:bind ("C-;". evilnc-comment-or-uncomment-lines))

Tree Sitter Highlighting

Archive: melpa

(use-package tree-sitter-langs
  :hook ((sh-mode . tree-sitter-hl-mode)
         (python-mode . tree-sitter-hl-mode)
         (c-mode . tree-sitter-hl-mode)
         (c++-mode . tree-sitter-hl-mode)))

Ripgrep

Archive: melpa

(use-package ripgrep
  :defer 2
  :ensure-system-package
  ((rg . "sudo dnf install -y ripgrep")))

YAML

Archive: melpa

(use-package yaml-mode
  :defer t)

TOML

(use-package toml-mode
  :defer t)

Docker

Archive: melpa

(use-package dockerfile-mode
  :defer t)

TODO Compilation mode in async output

Note: not used or tangled

(defadvice compile (before ad-compile-smart activate)
  "Advises `compile' so it sets the argument COMINT to t."
  (ad-set-arg 1 t))

(add-hook 'shell-mode-hook 'compilation-shell-minor-mode)

Devdocs

To install documentation, use M-x evdocs-install

(use-package devdocs
 :defer 2
 :bind  ("C-h D" . 'devdocs-lookup))

Pure Dev Tools

smerge

(defun jmn-smerge-accept-all-lower()
  "Accept all lower changes below the cursor point"
  (interactive)
  (while (not (smerge-next))
    (smerge-keep-lower)))

(defun jmn-smerge-accept-all-upper()
  "Accept all upper changes below the cursor point"
  (interactive)
  (while (not (smerge-next))
    (smerge-keep-upper)))

(with-eval-after-load 'smerge
  (define-key smerge-mode-map (kbd "C-c ^ L") 'jmn-smerge-accept-all-lower)
  (define-key smerge-mode-map (kbd "C-c ^ U") 'jmn-smerge-accept-all-upper))

Tab completion

Gives of tab completion in:

  • [X] python-mode
  • [X] emacs-lisp-mode
  • [X] shell-script mode (bash)

With this setup, TAB - which is usually bound to indent-for-tab-command - first tries to adjust the indentation according to the mode's settings, but if the indentation is already correct, completion is triggered. This is usually the desired behavior, and IMHO works better than third-party plugins like smart-tab. reference

I believe this is replacing what company-mode does for us.

(defun jmn-tab-complete-mode ()
  "My tab complete config"
  (setq tab-always-indent 'complete) ;;complete if indented
  (add-to-list 'completion-styles 'initials t))

;; turning it on everywhere and only turning company mode on for prog-mode
;;;; if this stayes it will just be moved up to under the 'General Emacs'
(jmn-tab-complete-mode)

As of 28.1 there is tab-first-completion. Could add a setup for it above which depends on Emacs version.

vc-mode

(if jmn-pure
    (global-set-key (kbd "C-c g") (lambda () (interactive)
                                    (vc-dir (file-name-directory (buffer-file-name))))))

(defun jmn-vc-commit ()
    "My command to show the vc-diff along with the commit input"
    (interactive)
    (vc-next-action (buffer-file-name))
    (previous-multiframe-window)
    (vc-diff)
    (previous-multiframe-window)
    (switch-to-buffer"*vc-log*")
    ;; (set-window-text-height (selected-window) 4)
    ;; (kill-buffer "*log-edit-files*")
    )

(with-eval-after-load 'vc-dir
  (define-key vc-dir-mode-map (kbd "C-o") 'other-window)
  (define-key vc-dir-mode-map (kbd "C-M-o") 'vc-dir-display-file)
  (define-key vc-dir-mode-map (kbd "c") 'jmn-vc-commit))

Languages

Emacs-lisp

Use C-c ' to view elisp babel blocks in their own file to get completion in company-mode.

  • added flycheck-mode-hook in it's use-package config.
(add-hook 'emacs-lisp-mode-hook 'display-line-numbers-mode)
(add-hook 'emacs-lisp-mode-hook 'visual-line-mode)

Bash

  • debuggers are available
(defun my-sh-mode-hook-fn()
  (setq sh-basic-offset 2
        sh-indentation 2)) ;; defaults is 4
(add-hook 'sh-mode-hook #'my-sh-mode-hook-fn)

(use-package sh-script
  :defer t
  :config
  (setq sh-basic-offset 2
        sh-indentation 2)) ;; defaults are 4

Python

Pyvenv

Archive: melpa

(use-package pyvenv
:ensure t
:defer t
:diminish
:config

(setenv "WORKON_HOME" "/home/ape/.conda/envs")
        ; Show python venv name in modeline
        (setq pyvenv-mode-line-indicator
              '(pyvenv-virtual-env-name ("[venv:" pyvenv-virtual-env-name "] ")))
        (pyvenv-mode t))

After package installation, you should have M-x pyvenv-workon command with a list of your virtual environments.

The only lack of this is that you need to restart LSP workspace at least once when you change venv by pyvenv-workon command.

So the flow should be like this:

M-x pyvenv-workon <your-venv> # or conda environment? M-x lsp-restart-workspace # eglot's corresponding command?

After changing venv all installed packages from venv should be visible for LSP server.

Python-mode

For lsp, have pyright installed pip install pyright

  • python-mode
    (with-eval-after-load 'python
      ;; if emacs is not run from a terminal, we need to add the PYTHONPATH set in .bashrc
      (unless (getenv "PYTHONPATH")
        (setenv "PYTHONPATH" (shell-command-to-string "$SHELL --login -c 'echo -n $PYTHONPATH'")))
      ;; allow completion
      (setq python-shell-completion-native-enable 1)
      ;; keybindings
      (define-key python-mode-map (kbd "C-RET") 'python-shell-send-statement)
      ;; use ipython for interpreter if it exists
      (if (executable-find "ipython")
          (progn (setq python-shell-interpreter "ipython")
                 (setq python-shell-interpreter-args "-i --simple-prompt"))))
    

Hook

(defun my-python-mode-hook-fn ()
  (with-eval-after-load 'devdocs
    (setq-local devdocs-current-docs '("python~3.12"))))

(add-hook 'python-mode-hook #'my-python-mode-hook-fn)

C/C++

Compilation Buffer

(setq compilation-scroll-output t) ;;*Compilation* buffer scroll with the output.

The following keeps the compilation buffer if there are warnings or errors, and buries it otherwise (after 1 second). source

(defun bury-compile-buffer-if-successful (buffer string)
 "Bury a compilation buffer if succeeded without warnings "
 (when (and
         (buffer-live-p buffer)
         (string-match "compilation" (buffer-name buffer))
         (string-match "finished" string)
         (not
          (with-current-buffer buffer
            (goto-char (point-min))
            (search-forward "warning" nil t))))
    (run-with-timer 1 nil
                    (lambda (buf)
                      (bury-buffer buf)
                      (switch-to-prev-buffer (get-buffer-window buf) 'kill))
                    buffer)))
(add-hook 'compilation-finish-functions 'bury-compile-buffer-if-successful)

Hook

Currently lsp-mode works with clangd backend without any initial setup. company-clang needs clang installed on the system.

;;settings
(setq-default c-basic-offset 2)

;; gdb
(with-eval-after-load 'gdb-mi
  (setq gdb-many-windows t
        gdb-show-main t))

(advice-add 'gdb :after (lambda (&rest r) (tool-bar-mode 1)))

;; mode
(defun my-c++-mode-hook-fn ()
  (with-eval-after-load 'devdocs
    (setq-local devdocs-current-docs '("cpp"))))

(defun my-c-mode-hook-fn ()
  (with-eval-after-load 'devdocs
    (setq-local devdocs-current-docs '("c"))))

(add-hook #'c-mode-hook #'my-c-mode-hook-fn)
(add-hook #'c++-mode-hook #'my-c++-mode-hook-fn)

Octave

(add-to-list 'auto-mode-alist '("\\.m$" . octave-mode))

Markdown

Use markdown-preview-mode to view in browser.

(use-package markdown-mode
  :defer t
  :hook ((markdown-mode-hook .  flyspell-mode)
         (markdown-mode-hook .  visual-line-mode)))

(use-package  markdown-preview-mode
  :defer t)

TODO Haskell

1) Install GHCup

Installs the following:

  • ghcup - The Haskell toolchain installer
  • ghc - The Glasgow Haskell Compiler
  • cabal - The Cabal build tool for managing Haskell software
  • stack - A cross-platform program for developing Haskell projects (similar to cabal)
  • hls - (optional) A language server for developers to integrate with their editor/IDE
(use-package haskell-mode
  :hook (haskell-mode . interactive-haskell-mode)  ;; For GHCi integration
  )
  ;; (use-package lsp-haskell)

;; Enable haskell-mode and lsp-mode automatically for Haskell files
(add-hook 'haskell-mode-hook #'eglot-ensure)
(add-hook 'haskell-mode-hook #'interactive-haskell-mode) ; For GHCi integration

(use-package flymake-hlint)

Org-Mode

Notes on completion: use C-c C-l to insert a path link and get completion.

  • alternatively company-mode will complete in the buffer, but will get in the way in the org-blocks.

Mode setup

(defun jmn/org-mode-setup ()
  (unless (or jmn-term (version<=  emacs-version "26.1"))
    (org-indent-mode))
  (variable-pitch-mode 1)
  (visual-line-mode 1)
  ;; fix issue where it matches > with partentheses -- may break blocks with <>
  (modify-syntax-entry ?< ".")
  (modify-syntax-entry ?> ".")
  )

Fonts

(defun jmn/org-font-setup ()
  (unless jmn-term ;; Replace list hyphen with dot
    (font-lock-add-keywords
     'org-mode '(("^ *\\([-]\\) "
                  (0 (prog1 () (compose-region (match-beginning 1)
                                               (match-end 1) "•")))))))

  ;; Set faces for heading levels
  (let ((jmn-org-hfont-alist '((gnu/linux . "Cantarell")
                              (windows-nt . unspecified))))
    (dolist (face '((org-level-1 . 1.2)
                    (org-level-2 . 1.1)
                    (org-level-3 . 1.1)
                    (org-level-4 . 1.1)
                    (org-level-5 . 1.1)
                    (org-level-6 . 1.1)
                    (org-level-7 . 1.1)
                    (org-level-8 . 1.1)
                    ))
      (set-face-attribute (car face) nil
                          :font (alist-get system-type jmn-org-hfont-alist)
                          :weight 'regular :height (cdr face))))

  ;; Ensure that anything that should be fixed-pitch in Org files appears that way
  ;; (set-face-attribute 'org-block nil :foreground nil :inherit 'fixed-pitch) ;;warning
  (set-face-attribute 'org-block nil :foreground 'unspecified' :inherit 'fixed-pitch)
  (set-face-attribute 'org-code nil   :inherit '(shadow fixed-pitch))
  (set-face-attribute 'org-table nil   :inherit '(shadow fixed-pitch))
  (set-face-attribute 'org-verbatim nil :inherit '(shadow fixed-pitch))
  (set-face-attribute 'org-special-keyword nil
                      :inherit '(font-lock-comment-face fixed-pitch))
  (set-face-attribute 'org-meta-line nil
                      :inherit '(font-lock-comment-face fixed-pitch))
  (set-face-attribute 'org-checkbox nil :inherit 'fixed-pitch))

Start

(add-hook 'org-mode-hook  'jmn/org-mode-setup)

(with-eval-after-load 'org
  (jmn/org-font-setup)
  (unless (or jmn-term jmn-pure)
    (progn (setq org-ellipsis " ▾")
           (set-face-underline 'org-ellipsis nil)))
  (setq org-hide-emphasis-markers t
        org-src-fontify-natively t
        org-fontify-quote-and-verse-blocks t
        org-src-tab-acts-natively t
        org-edit-src-content-indentation 2 ;; I undo this somewhere 4tangling
        org-hide-block-startup nil
        org-src-preserve-indentation nil
        org-startup-folded 'content
        org-cycle-separator-lines 2
        org-capture-bookmark nil
        org-list-indent-offset 1
        org-image-actual-width nil    ;; fix to allow picture resizing
        org-return-follows-link t
        org-use-speed-commands t
        org-use-property-inheritance t  ;; preperties to affect nested sections
        org-export-babel-evaluate nil ;; don't run src blocks on export
        org-agenda-tags-column (alist-get
                                (system-name) '(("xps" . -85)
                                                ("dsk" . -90))
                                'auto nil 'string=)))

Bullets

Archive: melpa

(unless (or jmn-term (not jmn-connected-extras))
  (use-package org-bullets
    :hook (org-mode . org-bullets-mode)
    :custom
    (org-bullets-bullet-list '("◉" "○" "●" "○" "●" "○" "●"))))

Center column and line wrapping

Archive: melpa

(defun jmn/org-mode-visual-fill ()
  (setq visual-fill-column-width 120
        visual-fill-column-center-text t)
  (visual-fill-column-mode 1))

(use-package visual-fill-column
  :hook (org-mode . jmn/org-mode-visual-fill))

Org-babel

Either start a source block with <[template] or C-c C-, to select from org-select.

Notes: Use # -- org-src-preserve-indentation: t; -- when doing tangle/detangle with python

(with-eval-after-load 'org
  (org-babel-do-load-languages 'org-babel-load-languages
                               (append org-babel-load-languages
                                       '((shell  . t)
                                         (python . t)
                                         (makefile . t)
                                         (latex  . t)
                                         (C      . t)))))

(setq org-confirm-babel-evaluate nil)

(with-eval-after-load 'org

  (if (version<= "27" emacs-version)
      (progn
        (require 'org-tempo) ;; needed for <sh to complete on new systems -- works without on v 26.1
        (add-to-list 'org-structure-template-alist '("la" . "src latex"))
        (add-to-list 'org-structure-template-alist '("m" . "src makefile"))
        (add-to-list 'org-structure-template-alist '("js" . "src js"))
        (add-to-list 'org-structure-template-alist '("sh" . "src shell"))
        (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
        (add-to-list 'org-structure-template-alist '("py" . "src python  :results output"))
        (add-to-list 'org-structure-template-alist '("pyim" . "src python :results file :var f=strNameWithDoubleQuotes
import matplotlib.pyplot as plt
plt.savefig(f)
f"))
        (add-to-list 'org-structure-template-alist '("cpp" . "src C++  :includes <iostream>"))
        (add-to-list 'org-structure-template-alist '("cppnm" . "src C++  :main no")))
    (progn
      (add-to-list 'org-structure-template-alist '("m" "#+BEGIN_SRC makefile
?
#+END_SRC"))
      (add-to-list 'org-structure-template-alist '("js" "#+BEGIN_SRC js
?
#+END_SRC"))
      (add-to-list 'org-structure-template-alist '("sh" "#+BEGIN_SRC shell
?
#+END_SRC"))
      (add-to-list 'org-structure-template-alist '("el" "#+BEGIN_SRC emacs-lisp
?
#+END_SRC"))
      (add-to-list 'org-structure-template-alist '("py" "#+BEGIN_SRC python
?
#+END_SRC"))
      (add-to-list 'org-structure-template-alist '("cpp" "#+BEGIN_SRC C++ :includes <iostream>
?
#+END_SRC"))
      (add-to-list 'org-structure-template-alist '("cppnm" "#+BEGIN_SRC C++ :main no
?
#+END_SRC")))))

Inline latex

Note: I had to install texlive dependencies for latex framents to work. I found what needed to be installed by running pdflatex on the generated tex file in /tmp/ created by org.

  • sudo dnf install texlive-scheme-full
  • texlive-ulem

Set in-line LaTeX's relative font size

(with-eval-after-load 'org
  (defconst jmn-latex-scale 1.1 "scaling factor for latex fragments")
  (setq org-format-latex-options (plist-put org-format-latex-options :scale jmn-latex-scale)))

In-line LaTeX size when buffer scale is changed

Create a function to align the size of displayed latex framents with overall org-mode font size.

(defun update-org-latex-fragments ()
  (org-latex-preview '(64))
  (plist-put org-format-latex-options :scale
             (+ jmn-latex-scale  (* 0.3 text-scale-mode-amount)))
  (org-latex-preview '(16)))
(add-hook 'text-scale-mode-hook 'update-org-latex-fragments)

org-fragtog

Automatically toggle between displaying and editing LaTeX depending on the cursor's position.

(unless jmn-pure
  (use-package org-fragtog
    :hook
    (org-mode . org-fragtog-mode)))

Keybindings

(global-set-key (kbd "C-c x") #'org-html-export-to-html)
(global-set-key (kbd "C-c s") #'org-store-link)
(global-set-key (kbd "C-c i") #'org-insert-link)
(global-set-key (kbd "C-c c") #'org-capture)

HTML

htmlize

For syntax highlighting in export html, we want htmlize version > 1.34

(use-package htmlize
  :ensure t
  :defer t)

Save to HTML on save

This org file converts to html on save, via the file's local variables. See the end of the file.

  • Here we designate the auto convert to html on save as safe so we are not prompted to designate it safe on startup the first time.

    ;; initialize if not already
    (if (not (bound-and-true-p jmn-org-files-to-html-on-save))
        (setq  jmn-org-files-to-html-on-save nil))
    
    (defun jmn-org-export-html-on-save-list()
      (when (member (buffer-file-name)  jmn-org-files-to-html-on-save)
        (org-html-export-to-html)))
    
    (add-hook 'after-save-hook #'jmn-org-export-html-on-save-list)
    
    (defun jmn-export-to-html-on-save()
      (interactive)
      (add-to-list 'jmn-org-files-to-html-on-save (buffer-file-name)))
    (add-to-list 'safe-local-variable-values '(eval jmn-export-to-html-on-save))
    
(add-to-list  'safe-local-variable-values `(eval add-hook 'after-save-hook #'org-html-export-to-html))

Make Read The Org setup file safe

Make the setup file safe for non-pure machines.

(if (and (version<= "29" emacs-version) (not jmn-pure))
    (with-eval-after-load 'org
      (add-to-list 'org-safe-remote-resources "\\`https://fniessen\\.github\\.io/org-html-themes/org/html-theme-readtheorg\\.setup\\'")))

Agenda and GTD

General

Following the tutorial here.

;; Org Agenda
(setq org-agenda-window-setup 'other-window) ;; other good option: reorganize-frame
;; Exited with ‘q’ or ‘x’ and the old state is restored.
(setq org-agenda-restore-windows-after-quit 1)
(setq org-agenda-span 'day)

;; SOMEDAY itmes are ommitted from GTD interface on purpose
(setq org-todo-keywords '((sequence "TODO(t)" "NEXT(n)" "HOLD(h)" "|"
                                    "DONE(d)" "IGNORE(i)")))

(require 'find-lisp) ;; may not be needed"\\.\\(org\\|org_archived\\)$"

; use "\.org$" or "\\.\\(org\\|org_archived\\)$" exclude or include archives
(setq org-agenda-files (find-lisp-find-files jmn-gtd-directory
                                              ".*\\.\\(org\\|org_archive\\)$"))               ;; "\\.\\(org\\|org_archive\\)$"))

;; level/maxlevel = order in hierarchy
(setq org-refile-targets
      '(("projects.org" :maxlevel . 2)
        ("someday.org" :maxlevel . 1)
        ("whip.org" :level . 1)
        ("next.org" :level . 0)))

;; https://github.com/syl20bnr/spacemacs/issues/3094
(setq org-refile-use-outline-path 'file org-outline-path-complete-in-steps nil)
(setq org-refile-allow-creating-parent-nodes 'confirm)

(setq org-agenda-prefix-format '((agenda . " %i %-10:c%t [%e]% s ")
                                 (todo . " %i %-10:c [%-4e] ")
                                 (tags . " %i %-12:c")))

(setq org-deadline-warning-days 30)

(setq org-agenda-start-with-log-mode t)  ;; allows us to see closed in calendar
(setq org-log-done 'time)  ;; creates CLOSED time tag
(setq org-log-into-drawer t)  ;; creates a LOGBOOK drawer for notes
(setq org-agenda-use-time-grid nil)  ;; no grid at top of agenda

(setq org-agenda-custom-commands
      '(("d" "Dashboard"
         ((agenda ""
                  ((org-deadline-warning-days 30)))
          (todo "NEXT"
                ((org-agenda-overriding-header "Next Un-Scheduled Tasks")))
          (todo "TODO"
                ((org-agenda-overriding-header "Active Un-Scheduled Tasks")))))

        (" " "Agenda"
         ((agenda ""
                  ((org-agenda-span 'day)
                   (org-deadline-warning-days 7)))

          (todo "TODO"
                ((org-agenda-overriding-header "To Refile")
                 (org-agenda-files (list (concat jmn-gtd-directory "inbox.org")))))

          (todo "NEXT"
                ((org-agenda-overriding-header "In Progress")
                 (org-agenda-files (list (concat jmn-gtd-directory "projects.org")
                                         (concat jmn-gtd-directory "next.org")
                                         (concat jmn-gtd-directory "inbox.org")))))

          (todo "TODO"
                ((org-agenda-overriding-header "Projects")
                 (org-agenda-files (list (concat jmn-gtd-directory  "projects.org")))))

          (todo "TODO"
                ((org-agenda-overriding-header "One-off Tasks")
                 (org-agenda-files (list (concat jmn-gtd-directory  "next.org"))))
                (org-agenda-skip-function '(org-agenda-skip-entry-if
                                            'deadline 'scheduled)))

          (todo "HOLD"
                    ((org-agenda-overriding-header "HOLD")
                     (org-agenda-files (list (concat jmn-gtd-directory "projects.org")
                                             (concat jmn-gtd-directory "next.org")
                                             (concat jmn-gtd-directory "inbox.org")))))
          ))))

(defun jmn-someday() "Quick access to someday.org (no links in agenda)"
       (interactive)
       (find-file (concat jmn-gtd-directory "someday.org")))

(setq org-agenda-todo-ignore-scheduled 'all) ;; cant get it to work for deadlines

hook

(add-hook 'org-agenda-mode-hook 'hl-line-mode)

Habits

File holding habits must be added to org-agenda-files above

  • habits must have the :STYLE: habit property
;; org habit;;
(with-eval-after-load 'org
  (add-to-list 'org-modules 'org-habit))
;;(require 'org-habit)
(with-eval-after-load 'org-habit
  (setq org-habit-graph-column
        (alist-get (system-name) '(("xps" . 56)
                                   ("dsk" . 52))
                   50 nil 'string=))) ;; default is 40

Capture Templates

Capture templates (The Org Manual)

  • C-c c i : add an entry to the inbox
  • C-c c w : add an entry to the whip
    • Creates a time stamp for now, need to add time for when I'd like to be reminded, C-c .
    • C-c C-c : adds tags to a heading
(setq org-capture-templates
      `(("t" "Todo [inbox]" entry
         (file ,(concat jmn-gtd-directory "inbox.org"))
         "* TODO %i%?" :empty-lines 1)

        ("T" "Todo Today [inbox]" entry
           (file ,(concat jmn-gtd-directory "inbox.org"))
           "* TODO %?\nDEADLINE: %t" :empty-lines 1)

        ("l" "Linked Todo [inbox]" entry
         (file ,(concat jmn-gtd-directory "inbox.org"))
         "* TODO %i%? \n %a" :empty-lines 1)

        ("s" "Schedule" entry
         (file+headline ,(concat jmn-gtd-directory "whip.org")  "Whip")
         "* %i%? \n %U %^t" :empty-lines 1)

        ("j" "Journal" entry
         (file+datetree ,(concat jmn-gtd-directory "journal.org"))
         "* %?\nEntered on %U\n  %i\n  %a"  :empty-lines 1)))

Archive

(defun org-archive-done-tasks-tree ()
  "Archive all DONE tasks in the current org tree"
  (interactive)
  (org-map-entries
   (lambda ()
     (org-archive-subtree)
     (setq org-map-continue-from (org-element-property :begin (org-element-at-point))))
   "/DONE" 'tree))

(defun org-archive-done-tasks-file ()
  "Archive all DONE tasks in the current org file"
  (interactive)
  (org-map-entries
   (lambda ()
     (org-archive-subtree)
     (setq org-map-continue-from (org-element-property :begin (org-element-at-point))))
   "/DONE" 'file))

Process Inbox Item

https://github.com/jethrokuan/.emacs.d/blob/master/init.el

;; could set in the inbox header instead (where tags are set)
(customize-set-variable 'org-global-properties
                        '(("Effort_ALL" . "0:05 0:15 0:30 1:00 2:00 4:00")))

(defun jmn/org-agenda-process-inbox-item ()
  "Process a single item in the org-agenda."
  (interactive)
  (org-with-wide-buffer  ; what does this do?
   ;; (org-agenda-set-tags) ; may want in the future
   (org-agenda-priority)
   (org-agenda-set-effort)
   (org-agenda-refile nil nil t)))

(global-set-key (kbd "C-c p") 'jmn/org-agenda-process-inbox-item)

Advice

(defmacro func-ignore (fnc)
  "Return function that ignores its arguments and invokes FNC."
  `(lambda (&rest _rest)
     (funcall ,fnc)))

(advice-add 'org-archive-done-tasks-tree
            :after (func-ignore #'org-save-all-org-buffers))
(advice-add 'org-archive-done-tasks-file
            :after (func-ignore #'org-save-all-org-buffers))
(advice-add 'org-refile
            :after (func-ignore #'org-save-all-org-buffers))
(advice-add 'org-deadline
            :after (func-ignore #'org-save-all-org-buffers))
(advice-add 'org-schedule
            :after (func-ignore #'org-save-all-org-buffers))
(advice-add 'org-store-log-note
            :after (func-ignore #'org-save-all-org-buffers))
(advice-add 'org-todo
            :after (func-ignore #'org-save-all-org-buffers))

;; if agenda is already open, update it with new capture;; work?
(advice-add 'org-capture-finalize
             :after (func-ignore #'org-agenda-redo-all))
;; ;; (advice-add 'org-capture-finalize
;; ;;             :after (func-ignore #'org-agenda-redo-all))

Keybindings

  • C-c C-s : scheduled timestamp
  • C-c C-d : deadline timestamp
  • C-c C-w : refile actionable/project items from inbox to appropriate area.
  • C-x C-s : org-save-all-org-buffers
  • C-c C-x e : org-set-effort
  • o : open agenda item in other window –after my change
  • +, - , , : increase, decrease, and set priority
  • Shift-right : advance the todo state
  • S-<down> : org-agenda-priority-down
  • S-<left> : org-agenda-do-date-earlier
  • S-<right> : org-agenda-do-date-later
  • S-<up> : org-agenda-priority-up
(defun jmn-agenda (&optional arg) (interactive "P") (org-agenda arg " "))

(global-set-key (kbd "C-c a") 'jmn-agenda)

(with-eval-after-load 'org-agenda
  (define-key org-agenda-keymap (kbd "o") 'org-agenda-goto)) ;; more like dired

Terminals

term-mode

  • Slower than vterm at printing large amounts of information.
  • For more than one terminal, you must M-x rename-uniquely the terminal.
  • C-c prefix for term commands

Line-mode vs char-mode selection shows on the modeline: C-c C-k -> char-mode C-c C-j -> line-mode

Keybindings

Just some changes to make it feel more like the other modes.

  • I don't really use the M-p nor the M-n, but they could be nice.
(with-eval-after-load 'term
  (define-key term-raw-map (kbd "C-o") 'other-window)
  (define-key term-raw-map (kbd "M-o") 'previous-multiframe-window)

  ;; access prev commandsin line mode, though line and char-mode rings are separate
  (define-key term-raw-map (kbd "M-p") 'term-send-up)
  (define-key term-raw-map (kbd "M-n") 'term-send-down)

  ;; add "C-x" as escape character and use it for keybindings
  (let (term-escape-char)
    (term-set-escape-char ?\C-x)) ; set "C-x" as an escape character
  (define-key term-raw-map (kbd "C-x C-k") 'kill-current-buffer)
  (define-key term-raw-map (kbd "C-c C-k") 'nil)) ; no only used to change from char to line mode

Better term-mode colors

Archive: melpa

(use-package eterm-256color
  :hook (term-mode . eterm-256color-mode))

Version 26.1

(if (version<= emacs-version "29" )
  (setq explicit-shell-file-name "/bin/bash"))

vterm

Faster terminal due to being compiled. Default is a better mode than term-mode; it's like a Char-mode but with ability to access function list with M-x. vterm Documentation

  • For more than one terminal, you must M-x rename-uniquely the terminal.
  • C-c prefix for term commands
  • C-c C-c = send C-c to the terminal (kill running command)

Extra installs needed: For some reason :ensure-system-package did not work here so it is done explicitly in the :init Need to install: cmake, libtool

(use-package vterm
  :ensure t
  :ensure-system-package ((cmake . "sudo dnf install -y cmake")
                          (libtool . "sudo dnf install -y libtool" )) ;; compilation
  :defer t
  :bind (("C-`". vterm)  ;; gets overwriteent by vterm-toggle
         (:map vterm-mode-map ("C-o" . other-window)))
  :config
  (jmn-remove-M-1-9-from-mode-map vterm-mode-map)
  (defun vterm-send-escape ()
    "Sends `ESC` to the libvterm."
    (interactive)
    (vterm-send-key "<escape>")
    )
  (setq vterm-max-scrollback 10000)
  :init (let ((package "cmake"))
          (unless (executable-find package)
            (async-shell-command (concat "sudo dnf install -y " package))))
  (let ((package "libtool"))
    (unless (executable-find package)
      (async-shell-command (concat "sudo dnf install -y " package)))))


(use-package vterm-toggle
  :after vterm
  :config
  (setq vterm-toggle-fullscreen-p nil)
  (global-set-key (kbd "C-`") 'vterm-toggle)
  (add-to-list 'display-buffer-alist
               '((lambda(bufname _) (with-current-buffer
                                        bufname (equal major-mode 'vterm-mode)))
                 (display-buffer-reuse-window display-buffer-at-bottom)
                 ;;(display-buffer-reuse-window display-buffer-in-direction)
                 ;;display-buffer-in-direction/direction/dedicated is added in emacs27
                 ;;(direction . bottom)
                 ;;(dedicated . t) ;dedicated is supported in emacs27
                 (reusable-frames . visible)
                 (window-height . 0.3))))

Theme and Frame

To modify a theme it is helpful to use:

  • list-faces-display to show the fonts used showing an example of how they look
  • describe-face to show the font under the cursor

Setup and helpers

(setq custom-safe-themes t)                                                              ;; don't ask if theme is safe
(add-to-list 'custom-theme-load-path "~/.emacs.d/themes/")

(defun jmn-disable-all-themes()
  (interactive)
  (while custom-enabled-themes
    (disable-theme (car custom-enabled-themes))))

Gruvbox and Modifications

(use-package gruvbox-theme)

Modify Themes

  • General Org
    (deftheme jmn-gruvbox-general-org
      "Altering all the gruvbox themes-- load after loading the gruvbox theme")
    
    ;; changes which will be permanent
    (with-eval-after-load 'org
      (setq org-todo-keyword-faces
            `(("NEXT" . ,(face-foreground font-lock-function-name-face))
              ("HOLD" . ,(face-foreground font-lock-builtin-face)))))
    
      ;; changes which will be undone when disabling the theme
      (custom-theme-set-faces
       'jmn-gruvbox-general-org
    
       `(org-priority ((t (:foreground ,(face-foreground 'font-lock-constant-face)))))
       `(org-block-begin-line ((t (:inherit font-lock-comment-face
                                   :background ,(face-background 'default)))))
       `(org-block-end-line ((t (:inherit font-lock-comment-face
                                 :background ,(face-background 'default))))))
    
    (provide-theme 'jmn-gruvbox-general-org)
    
  • Dark
    • Hard
      (deftheme jmn-gruvbox-dark-hard
        "Altering the gruvbox-dark-hard theme-- load after loading the gruvbox theme")
      
      (let ((done-color "gray35"))
      
        ;; changes which will be permanent
        (with-eval-after-load 'org
          (setq org-todo-keyword-faces
                `(("NEXT" . ,(face-foreground font-lock-function-name-face))
                  ("HOLD" . ,(face-foreground font-lock-builtin-face))
                  ("DONE" . done-color)
                  ("IGNORE" . done-color))))
      
        ;; changes which will be undone when disabling the theme
         (custom-theme-set-faces
          'jmn-gruvbox-dark-hard
          ;; Basics
          `(mode-line-inactive ((t (:background "gray22" :extend t))))
          `(mode-line-active ((t (:background "gray35" :extend t))))
          `(line-number ((t :background ,(face-attribute 'default :background))))
          `(fringe ((t :background ,(face-attribute 'default :background))))
      
          ;; org
          `(org-agenda-done ((t (:foreground ,done-color))))
          `(org-headline-done ((t (:foreground ,done-color))))
          `(org-done ((t (:foreground ,done-color))))
          `(org-block ((t (:background "gray8" :extend t))))))
      
      (provide-theme 'jmn-gruvbox-dark-hard)
      
    • Hardest
      (deftheme jmn-gruvbox-dark-hardest
        "Altering the gruvbox-dark-hard theme-- load after loading the gruvbox theme")
      
      (let ((done-color "gray35"))
      
        ;; changes which will be permanent
        (with-eval-after-load 'org
          (setq org-todo-keyword-faces
                `(("NEXT" . ,(face-foreground font-lock-function-name-face))
                  ("HOLD" . ,(face-foreground font-lock-builtin-face))
                  ("DONE" . done-color)
                  ("IGNORE" . done-color))))
      
        ;; changes which will be undone when disabling the theme
         (custom-theme-set-faces
          'jmn-gruvbox-dark-hardest
          ;; Basics
          `(default ((t (:background "gray11" :extend t))))
          `(link ((t (:foreground "#83a598" :extend t))))  ;;'tree-sitter-hl-face inherits it
      
          `(line-number ((t :background "gray11" :extend t )))
          `(fringe ((t :background "gray11" :extend t )))
      
          ;; treesitter
          `(tree-sitter-hl-face:variable ((t :foreground "#FFA500" :extend t)))
          `(tree-sitter-hl-face:variable.parameter ((t :foreground ,(face-attribute 'default :foreground))))
          `(tree-sitter-hl-face:label ((t :foreground "#d65d0e" :extend t)))
      
          ;; org
          `(org-agenda-done ((t (:foreground ,done-color))))
          `(org-headline-done ((t (:foreground ,done-color))))
          `(org-done ((t (:foreground ,done-color))))
          `(org-block ((t (:background "gray7" :extend t))))))
      
      (provide-theme 'jmn-gruvbox-dark-hardest)
      
    • Medium
      (deftheme jmn-gruvbox-dark-medium
        "Altering the gruvbox-dark-medium theme-- load after loading the gruvbox theme")
      
      (let ((done-color "gray35"))
      
        ;; changes which will be permanent
        (with-eval-after-load 'org
          (setq org-todo-keyword-faces
                `(("NEXT" . ,(face-foreground font-lock-function-name-face))
                  ("HOLD" . ,(face-foreground font-lock-builtin-face))
                  ("DONE" . done-color)
                  ("IGNORE" . done-color))))
      
        ;; changes which will be undone when disabling the theme
         (custom-theme-set-faces
          'jmn-gruvbox-dark-medium
          ;; Basics
          ;; `(default ((t (:foreground "moccasin" :extend t))))
          `(line-number ((t :background ,(face-attribute 'default :background))))
          `(font-lock-comment-face ((t (:foreground "#98be65" :extend t))))
      
          ;; org
          `(org-agenda-done ((t (:foreground ,done-color))))
          `(org-headline-done ((t (:foreground ,done-color))))
          `(org-done ((t (:foreground ,done-color))))))
      
      (provide-theme 'jmn-gruvbox-dark-medium)
      
  • Light
    • Hard
      (deftheme jmn-gruvbox-light-hard
        "Altering the gruvbox-light-hard theme-- load after loading the gruvbox theme")
      
      (let ((done-color "Navajowhite3"))
      
        ;; changes which will be permanent
        (with-eval-after-load 'org
          (setq org-todo-keyword-faces
                `(("NEXT" . ,(face-foreground font-lock-function-name-face))
                  ("HOLD" . ,(face-foreground font-lock-builtin-face))
                  ("DONE" . done-color)
                  ("IGNORE" . done-color))))
      
        ;; changes which will be undone when disabling the theme
         (custom-theme-set-faces
          'jmn-gruvbox-light-hard
          ;; org
          `(org-agenda-done ((t (:foreground ,done-color))))
          `(org-headline-done ((t (:foreground ,done-color))))
          `(org-done ((t (:foreground ,done-color))))
          `(org-block ((t (:background "#fbf1c7" :extend t))))))
      
      (provide-theme 'jmn-gruvbox-light-hard)
      
    • Medium
      (deftheme jmn-gruvbox-light-medium
        "Altering the gruvbox-light-medium theme-- load after loading the gruvbox theme")
      
      (let ((done-color "Navajowhite3"))
      
        ;; changes which will be permanent
        (with-eval-after-load 'org
          (setq org-todo-keyword-faces
                `(("NEXT" . ,(face-foreground font-lock-function-name-face))
                  ("HOLD" . ,(face-foreground font-lock-builtin-face))
                  ("DONE" . done-color)
                  ("IGNORE" . done-color))))
      
        ;; changes which will be undone when disabling the theme
         (custom-theme-set-faces
          'jmn-gruvbox-light-medium
          ;; org
          `(org-agenda-done ((t (:foreground ,done-color))))
          `(org-headline-done ((t (:foreground ,done-color))))
          `(org-done ((t (:foreground ,done-color))))))
      
      (provide-theme 'jmn-gruvbox-light-medium)
      
    • Soft
      (deftheme jmn-gruvbox-light-soft
        "Altering the gruvbox-light-soft theme-- load after loading the gruvbox theme")
      
      (let ((done-color "Navajowhite3"))
      
        ;; changes which will be permanent
        (with-eval-after-load 'org
          (setq org-todo-keyword-faces
                `(("NEXT" . ,(face-foreground font-lock-function-name-face))
                  ("HOLD" . ,(face-foreground font-lock-builtin-face))
                  ("DONE" . done-color)
                  ("IGNORE" . done-color))))
      
        ;; changes which will be undone when disabling the theme
         (custom-theme-set-faces
          'jmn-gruvbox-light-soft
      
          ;; org
          `(org-agenda-done ((t (:foreground ,done-color))))
          `(org-headline-done ((t (:foreground ,done-color))))
          `(org-done ((t (:foreground ,done-color))))
          `(org-block ((t (:background "#ebdbb2" :extend t))))))
      
      (provide-theme 'jmn-gruvbox-light-soft)
      
      

Loading functions

(defun jmn-load-gruvbox-dark-medium ()
  "Theme for a medium dark time"
  (interactive)
    (jmn-disable-all-themes)
    (load-theme 'gruvbox-dark-medium )
    (load-theme 'jmn-gruvbox-general-org)
    (load-theme 'jmn-gruvbox-dark-medium))

(defun jmn-load-gruvbox-dark-hard ()
  "Theme for dark time"
  (interactive)
    (jmn-disable-all-themes)
    (load-theme 'gruvbox-dark-hard )
    (load-theme 'jmn-gruvbox-general-org)
    (load-theme 'jmn-gruvbox-dark-hard))

(defun jmn-load-gruvbox-dark-hardest()
  "Theme for very darkest of times"
  (interactive)
   (jmn-disable-all-themes)
    (load-theme 'gruvbox-dark-hard )
    (load-theme 'jmn-gruvbox-general-org)
    (load-theme 'jmn-gruvbox-dark-hardest))

(defun jmn-load-gruvbox-light-medium()
  "Theme for light time"
  (interactive)
    (jmn-disable-all-themes)
    (load-theme 'gruvbox-light-medium )
    (load-theme 'jmn-gruvbox-general-org)
    (load-theme 'jmn-gruvbox-light-medium))

(defun jmn-load-gruvbox-light-hard()
  "Theme for very light time"
  (interactive)
    (jmn-disable-all-themes)
    (load-theme 'gruvbox-light-hard )
    (load-theme 'jmn-gruvbox-general-org)
    (load-theme 'jmn-gruvbox-light-hard))

 (defun jmn-load-gruvbox-light-soft()
  "Theme for very light time"
  (interactive)
    (jmn-disable-all-themes)
    (load-theme 'gruvbox-light-soft )
    (load-theme 'jmn-gruvbox-general-org)
    (load-theme 'jmn-gruvbox-light-soft))

Pure Themes

Modifying Themes

  • Light
    (deftheme jmn-light
      "Altering the default theme-- load after loading the default theme")
    
    ;; variables which will be permanent
    (with-eval-after-load 'org
      (setq org-todo-keyword-faces
                `(("TODO" . "Red1")
                  ("NEXT" . "orange")
                  ("HOLD" . "Black")
                  ("IGNORE" . "gray60"))))
    
      (custom-theme-set-faces
       'jmn-light
    
       ;; Org-mode
       `(org-block ((t (:background "gray93" :extend t))))
    
       ;; Tab bar
       '(tab-bar ((t (:inherit org-default :extend t))))
       `(tab-bar-tab ((t (:background "darkseagreen2"))))
       '(tab-bar-tab-inactive ((t (:inherit org-default :extend t)))))
    
    (provide-theme 'jmn-light)
    
  • Wombat
    (deftheme jmn-wombat
      "Altering the wombat theme-- load after loading the wombat theme")
    
    ;; variables which will be permanent
    (with-eval-after-load 'org
      (setq org-todo-keyword-faces
                `(("TODO" . "Pink")
                  ("NEXT" . "gold2")
                  ("HOLD" . "orange3")
                  ("IGNORE" . "#99968b"))))
    
      (custom-theme-set-faces
       'jmn-wombat
    
       ;; Basics
       `(mode-line ((t (:background "gray44" :extend t))))
    
       ;; Org-mode
       `(org-block ((t (:background "gray10" :extend t))))
       `(org-level-7 ((t (:background "MediumPurple1" :extend t))))
    
       ;; Tab bar
       '(tab-bar ((t (:inherit org-default :extend t))))
       '(tab-bar-tab ((t (:inherit mode-line :weight bold :box nil :extend t))))
       '(tab-bar-tab-inactive ((t (:inherit org-default :extend t)))))
    
    (provide-theme 'jmn-wombat)
    

Loading functions

(defun jmn-load-pure-light-theme()
  (interactive)
  (jmn-disable-all-themes)
 (load-theme 'jmn-light))

(defun jmn-load-pure-dark-theme()
  (interactive)
  (jmn-disable-all-themes)
  (load-theme 'wombat)
 (load-theme 'jmn-wombat))

Startup Theme switches

(if jmn-dark-mode
    (if (eq system-type 'gnu/linux) ;; dark theme looks bad on windows
        (if jmn-pure
             (jmn-load-pure-dark-theme)  ;; dark, linux, pure
           (jmn-load-gruvbox-dark-hardest))
      (load-theme 'jmn-light))  ;; dark on windows (show light)
  (load-theme 'jmn-light))  ;; non-dark

Frame Height

(if (window-system)
    (set-frame-height (selected-frame) 53))

Shell Backend and Windows Core Setup

TODO Linux

This allows aliases set in .bashrc can then be used when running shells It also does not causes a print of errors which stops the use of getenv

(if (eq system-type 'gnu/linux)
    (setq shell-command-switch "-ic"))  ;; add -i so it loads .bashrc (aliases!)

Windows

Major changes for windows. Small changes also exist in the rest of the config.

(cond
 ((eq system-type 'windows-nt)
  ;;set font
  (when (member "Consolas" (font-family-list))
    (set-frame-font "Consolas" t t))

  (add-hook 'shell-mode-hook 'ansi-color-for-comint-mode-on) ;; needed?
  (add-hook 'term-mode-hook 'ansi-color-for-comint-mode-on)  ;; needed?
  ;; (setq explicit-shell-file-name "c:/Program Files/Git/bin/bash.exe") ;; gitbash

  (setq explicit-shell-file-name "c:/Program Files/Emacs/libexec/emacs/27.2/x86_64-w64-mingw32/cmdproxy.exe") ;; Emacs recommended
  (setq shell-file-name explicit-shell-file-name)

  ;; give emacs access to gnu coreutils, though should already be in =path=
  (add-to-list 'exec-path "c:/Program Files/Git/") ;; git-bash, git-cmd
  (add-to-list 'exec-path "c:/Program Files/Git/bin") ;; bash
  (add-to-list 'exec-path "c:/Program Files/Git/usr/bin") ;; coreutils (diff, ls, etc.)
  (add-to-list 'exec-path "c:/Program Files/Python312") ;; python-- not working

  ;; likey no octave, so read only matlab files
  (add-hook 'octave-mode-hook 'read-only-mode)

  ;; external ls for directories first support (dired) -- may not be needed with above
  (setq ls-lisp-use-insert-directory-program t)  ;; use external ls
  (setq inserft-directory-program "c:/Program Files/Git/user/bin/ls.exe") ;; ls loc
  ))

Hacky Fixes

Corrects error: File mode specification error: (error Invalid image type ‘svg’) (not used)

(setq image-types '(svg png gif tiff jpeg xpm xbm pbm))

Keep transparency when toggling to full-screen in Emacs 29+

Was needed at one point– keeping just in case it is need in the future.

(if (version<= "29" emacs-version)
    (progn
      (defun toggle-full-screen-with-transparency ()
        "Toggle full screen and adjust frame transparency."
        (interactive)
        (let ((current-alpha (frame-parameter nil 'alpha-background)))
          (transparency 100)
          (toggle-frame-fullscreen)
          (sit-for 0.7)
          (transparency current-alpha)
          ))
      ;; (global-unset-key (kbd "<f11>"))
      ;; (global-set-key (kbd "<f11>") 'toggle-full-screen-with-transparency)
      ))

Transparency in term (emacs -nw)

source: https://github.com/syl20bnr/spacemacs/issues/7262

(defun jmn-set-background-unspecified ()
  "Set background of buffer and line numbers to unspecified"
  (interactive)
  (set-face-background 'default "unspecified-bg" (selected-frame))
  (set-face-background 'line-number "unspecified-bg"))

(if jmn-term
    (add-hook 'window-setup-hook 'jmn-set-background-unspecified))

Org LaTeX Preview

For unknown reasons I must change ("dvipng -D %D -T tight -o %O %f") to ("dvipng -D %D -T tight -o %O %F") to preview LaTeX in org-mode. All of the code below is used only to make this one small change.

(setq org-preview-latex-process-alist
      '((dvipng :programs
                ("latex" "dvipng")
                :description "dvi > png" :message "you need to install the programs: latex and dvipng." :image-input-type "dvi" :image-output-type "png" :image-size-adjust
                (1.0 . 1.0)
                :latex-compiler
                ("latex -interaction nonstopmode -output-directory %o %f")
                :image-converter
                ("dvipng -D %D -T tight -o %O %F")
                :transparent-image-converter
                ("dvipng -D %D -T tight -bg Transparent -o %O %f"))
        (dvisvgm :programs
                 ("latex" "dvisvgm")
                 :description "dvi > svg" :message "you need to install the programs: latex and dvisvgm." :image-input-type "dvi" :image-output-type "svg" :image-size-adjust
                 (1.7 . 1.5)
                 :latex-compiler
                 ("latex -interaction nonstopmode -output-directory %o %f")
                 :image-converter
                 ("dvisvgm %f --no-fonts --exact-bbox --scale=%S --output=%O"))
        (imagemagick :programs
                     ("latex" "convert")
                     :description "pdf > png" :message "you need to install the programs: latex and imagemagick." :image-input-type "pdf" :image-output-type "png" :image-size-adjust
                     (1.0 . 1.0)
                     :latex-compiler
                     ("pdflatex -interaction nonstopmode -output-directory %o %f")
                     :image-converter
                     ("convert -density %D -trim -antialias %f -quality 100 %O"))))

Setting Org-figures width

  • Won't work with #+ATTR_ORG :width 600
  • This will set it globally for the time being
(setq org-image-actual-width 600)

Set certain file extensions to read only buffer

(defun jmn-set-files-read-only ()
     "Set buffers to read only if have one of the extensions hard coded in the function"
     (dolist (extension'("dat" "out"))
       (if (string-match extension (car (last (split-string (buffer-name) "\\."))))
           (read-only-mode 1))))

(add-hook 'find-file-hook 'jmn-set-files-read-only)

Set certain file extensions to display line numbers

(defun jmn-set-files-display-line-numbers ()
     "Set buffers to read only if have one of the extensions hard coded in the function"
     (dolist (extension'("txt"))
       (if (string-match extension (car (last (split-string (buffer-name) "\\."))))
           (display-line-numbers-mode 1))))

(add-hook 'find-file-hook 'jmn-set-files-display-line-numbers)

Startup and Tabs

Recentf

Recentf is needed for recent files on the dashboard.

(recentf-mode 1) ;; needed for recent files in dashboard
(setq recentf-max-menu-items 25) ;; max number of entries
;; (run-at-time nil (* 5 60) 'recentf-save-list) ;; save recent files periodically

;; Exclude the following files from the recents list
(add-to-list 'recentf-exclude "~/.emacs.d/recentf")
(add-to-list 'recentf-exclude (concat jmn-gtd-directory "inbox.org"))
(add-to-list 'recentf-exclude (concat jmn-gtd-directory "next.org"))
(add-to-list 'recentf-exclude (concat jmn-gtd-directory "whip.org"))
(add-to-list 'recentf-exclude (concat jmn-gtd-directory "someday.org"))
(add-to-list 'recentf-exclude (concat jmn-gtd-directory "journal.org"))
(add-to-list 'recentf-exclude (concat jmn-gtd-directory "habits.org"))
(add-to-list 'recentf-exclude
             (concat (file-name-directory jmn-config-location) "emacs.html"))

(if (or jmn-pure (not jmn-connected-extras))
    (progn
      ;; display recent files on startup
      ;; (add-hook 'after-init-hook (lambda () (recentf-open-files)))
      ;; display projects.org on startup
      (add-hook 'after-init-hook
                (lambda () (find-file
                            (concat jmn-gtd-directory "projects.org"))))
      ;; make display recent files the dashboard command
      (global-set-key (kbd "C-c d") 'recentf-open-files)))

Tabs

(if (version<= "27" emacs-version)
    (progn
      (setq tab-bar-close-button-show nil        ; remove close button
            tab-bar-show 1                       ; only show tab bar if #tabs>1
            tab-bar-select-tab-modifiers '(meta) ; Alt-i switch to the tab numbered i
            tab-bar-tab-hints t)                 ; show a number on each tabs

      (tab-bar-mode 1)
      (tab-bar-close-other-tabs) ; ensures (tab-bar-mode 1) works on older systems

      (defun jmn-create-my-tabs()
        "Create my default tabs"
        (interactive)
        (tab-close-other)
        (delete-other-windows)
        ;; TAB 1
        (tab-bar-rename-tab "gtd")
        (find-file (concat jmn-gtd-directory "projects.org"))
        (jmn-agenda)

        ;; TAB 2
        (tab-bar-new-tab)
        (tab-bar-rename-tab "workspace")
        (dired "/home/ape/Programming/projects/radar/radar-signal-processing")
        (unless jmn-pure
          (magit-status))

        ;; ;; TAB
        ;; (tab-bar-new-tab)
        ;; (if jmn-pure
        ;;     (progn
        ;;       (term "/bin/bash")
        ;;       (tab-bar-rename-tab "term"))
        ;;   (progn
        ;;     (vterm)
        ;;     (tab-bar-rename-tab "vterm")))
        ;; (delete-other-windows)

        ;; TAB N
        (tab-bar-new-tab)
        (tab-bar-rename-tab "config")
        (find-file jmn-config-location)
        (unless jmn-pure
          (magit-status))

        (tab-bar-select-tab 2))))

Dashboard

Dash board for initial startup of emacs. github link

  • For the icons to display correctly, I needed to execute all-of-the-icons-install-fonts.
(if jmn-connected-extras
    (progn
      (defun my-dashboard-hook()
        "Needed to define these after hook for some reason"
        (define-key dashboard-mode-map (kbd "n")  'dashboard-next-line)
        (define-key dashboard-mode-map (kbd "p")  'dashboard-previous-line))

      (use-package dashboard
        :ensure t
        :init (dashboard-setup-startup-hook)
        :bind ("C-c d" . dashboard-open)
        :hook ((dashboard-mode . hl-line-mode)
               (dashboard-mode . my-dashboard-hook))
        :config
        (setq dashboard-banner-logo-title "Habits, not goals.")

        (setq dashboard-startup-banner 2)  ;; (nil . no-banner)  ([1-5] . plain-text banners)
        (setq dashboard-center-content 1)
        (setq dashboard-show-shortcuts 1)  ;; show the single-character shortcuts
        (setq dashboard-items '((recents  . 5)
                                (bookmarks . 5)
                                (projects . 5)
                                (agenda . 5)
                                (registers . 5)))

        (setcdr (assoc 'projects dashboard-item-shortcuts) "j")
        (setq dashboard-set-heading-icons t)
        (setq dashboard-set-file-icons t)
        (setq dashboard-projects-backend 'project-el)
        (dashboard-modify-heading-icons '((recents . "file-text")))
        (setq dashboard-set-footer nil)
        (setq dashboard-startupify-list
              '(dashboard-insert-banner
                dashboard-insert-newline
                dashboard-insert-banner-title
                dashboard-insert-newline
                dashboard-insert-init-info
                dashboard-insert-items
                dashboard-insert-newline
                ;;dashboard-insert-footer
                )))))

Recreation

Hacker News Reader

(unless jmn-pure
  (use-package hnreader))

gptel

To highlight the LLM response, use gptel-highlight-mode

  • requires transient – shipped in emacs in 29.1
;; Helper function to read from authinfo.gpg
(defun my-get-api-key (host)
  (let ((entry (car (auth-source-search :host host))))
    (when entry
      (funcall (plist-get entry :secret)))))

;; Configure gptel to use the keys
(use-package gptel
  :defer t
  :ensure t
  :config
  (global-set-key(kbd "C-c RET") 'gptel-send)
  (setq gptel-default-mode 'org-mode)
  (gptel-make-gemini "Gemini" :key (my-get-api-key "generativelanguage.googleapis.com") :stream t)
  (setq
   gptel-model 'gemini-2.5-pro
   gptel-backend (gptel-make-gemini "Gemini"
                   :key (my-get-api-key "generativelanguage.googleapis.com")
                   :stream t))
  )

(use-package gptel-agent)

Author: ape

Created: 2026-04-04 Sat 06:09

Validate