About This web page is a tangled version of my literate elisp config . Throughout this document, you may find helpful notes, tricks, and comments. I have been tinkering with emacs in some way since 2013. I made this repeatable and git-controlled in 2019, and in 2023 I began to publish it on my web site.
Notes on code blocks If a code block is active, then it will appear with emacs-lisp
syntax highlighting. If it’s not being used, then it will show up with plaintext
highlighting. For example:
(print "This code will not be run!")
( print "This code is run in my config!" )
Whether a code block is run at load time is controlled by the :load yes
block argument of literate-elisp
.
init.el
This is the main init file that wraps all the other code I love and rely heavily on straight.el
.
( setq comp-speed 2 )
;; Packages setup to prepare literate-elisp
( defvar bootstrap-version )
( let (( bootstrap-file
( expand-file-name "straight/repos/straight.el/bootstrap.el"
( or ( bound-and-true-p straight-base-dir )
user-emacs-directory )))
( bootstrap-version 7 ))
( unless ( file-exists-p bootstrap-file )
( with-current-buffer
( url-retrieve-synchronously
"<https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el>"
'silent
'inhibit-cookies )
( goto-char ( point-max ))
( eval-print-last-sexp )))
( load bootstrap-file nil 'nomessage ))
( setq straight-use-package-by-default t )
( straight-use-package 'use-package )
( require 'use-package )
( setq use-package-compute-statistics t )
( use-package general )
( use-package org )
( use-package
literate-elisp
:demand t
:config
;; ;; To make \`elisp-refs' work with \`literate-elisp', we need to add an advice to \`elisp-refs–read-all-buffer-forms'.
( eval-after-load "elisp-refs"
' ( advice-add
'elisp-refs– ;read-all-buffer-forms
:around #' literate-elisp-refs– ;read-all-buffer-forms))
;; To make \`elisp-refs' work with \`literate-elisp', we need to add an advice to \`elisp-refs–loaded-paths'.
( eval-after-load "elisp-refs"
' ( advice-add
'elisp-refs– ;loaded-paths
:filter-return #' literate-elisp-refs– ;loaded-paths))
;; To make \`helpful' work with \`literate-elisp', we need to add an advice to \`helpful–find-by-macroexpanding'.
( eval-after-load 'helpful
' ( advice-add
'helpful– ;find-by-macroexpanding
:around #' literate-elisp-helpful– ;find-by-macroexpanding)))
; Start literate load
( literate-elisp-load
( expand-file-name "init.org" user-emacs-directory ))
( setq custom-file ( concat user-emacs-directory "custom.el" ))
( if ( not ( file-exists-p custom-file ))
( f-touch custom-file ))
( load custom-file )
MacOS specific settings Command key as control ( when ( eq system-type 'darwin )
( setq mac-command-modifier 'control )
( setq mac-right-command-modifier 'meta )
( add-to-list 'default-frame-alist ' ( ns-transparent-titlebar . t ))
( add-to-list
'default-frame-alist
' ( ns-appearance . light )) ;; or dark - depending on your theme
( use-package
osx-plist
:straight
' ( osx-plist :type git :host github :repo "gonewest818/osx-plist" )))
Window size Big and Centered I’ll do this first, so that it goes back to this when I exit full screen mode (if enabled).
( add-to-list 'default-frame-alist ' ( width . 130 )) ; Set width to 80 columns
( add-to-list 'default-frame-alist ' ( height . 40 )) ; Set height to 24 lines
( defun center-frame ()
"Center the frame on the screen, respecting the size set in default-frame-alist."
( interactive )
( let* (( desired-width
( or ( cdr ( assq 'width default-frame-alist )) 80 ))
( desired-height
( or ( cdr ( assq 'height default-frame-alist )) 24 ))
( screen-width ( x-display-pixel-width ))
( screen-height ( x-display-pixel-height ))
( char-width ( frame-char-width ))
( char-height ( frame-char-height ))
( frame-pixel-width ( * desired-width char-width ))
( frame-pixel-height ( * desired-height char-height ))
( left ( max 0 ( / ( - screen-width frame-pixel-width ) 2 )))
( top ( max 0 ( / ( - screen-height frame-pixel-height ) 2 ))))
( message
"Screen size: %dx%d, Desired frame size: %dx%d, Position: (%d, %d)"
screen-width screen-height desired-width desired-height left top )
( set-frame-size ( selected-frame ) desired-width desired-height )
( set-frame-position ( selected-frame ) left top )
( message "Frame set to %dx%d at (%d, %d)"
( frame-width )
( frame-height )
( frame-parameter nil 'left )
( frame-parameter nil 'top ))))
;; Call this function when Emacs starts
( add-hook 'window-setup-hook #' center-frame )
Full Screen ( defun toggle-fullscreen-or-maximized ()
"Toggle between fullscreen on macOS and maximized on Linux."
( interactive )
( if ( eq system-type 'darwin ) ; macOS
( set-frame-parameter
nil 'fullscreen
( if ( frame-parameter nil 'fullscreen )
nil
'fullboth ))
( set-frame-parameter
nil 'fullscreen
( if ( eq ( frame-parameter nil 'fullscreen ) 'maximized )
nil
'maximized )))) ; Linux
( add-hook 'window-setup-hook #' toggle-fullscreen-or-maximized )
Copying syntax highlighting to the clipboard ( when ( eq system-type 'darwin )
( use-package
highlight2clipboard
:straight
' ( highlight2clipboard
:type git
:host github
:repo "Lindydancer/highlight2clipboard" )))
Path ( setq default-directory "~/" )
( use-package package )
( use-package
exec-path-from-shell
:demand t
:init
( setq exec-path-from-shell-variables
' ( "PATH" "MANPATH" "OPENAI_API_KEY" ))
( setq exec-path-from-shell-arguments ' ( "-l" "-i" ))
:config
( when ( memq window-system ' ( mac ns x ))
( exec-path-from-shell-initialize )))
Terminal Vterm ( use-package
vterm
:init
( setq vterm-copy-exclude-prompt nil )
( setq vterm-always-compile-module t )
( setq vterm-timer-delay 0.01 )
( setq vterm-tramp-shells
' ( "/usr/bin/bash" "/bin/bash" "/bin/zsh" "docker" "/bin/sh" ))
:config
( define-key
vterm-mode-map ( kbd "M-k" )
( lambda ()
( interactive )
( kill-this-buffer nil )))
( define-key vterm-mode-map ( kbd "M-0" ) nil )
( define-key vterm-mode-map ( kbd "M-1" ) nil )
( define-key vterm-mode-map ( kbd "M-2" ) nil )
( define-key vterm-mode-map ( kbd "M-3" ) nil )
( define-key vterm-mode-map ( kbd "M-T" ) nil )
( define-key vterm-mode-map ( kbd "M-R" ) nil )
( define-key vterm-mode-map ( kbd "M-G" ) nil )
( define-key vterm-mode-map ( kbd "M-:" ) nil )
( define-key vterm-mode-map ( kbd "M-s" ) 'nil )
( defun kill-buffer-and-its-windows ( buffer )
"Kill BUFFER and delete its windows. Default is `current-buffer'.
BUFFER may be either a buffer or its name (a string)."
( interactive ( list
( read-buffer "Kill buffer: "
( current-buffer )
'existing )))
( setq buffer ( get-buffer buffer ))
( if ( buffer-live-p buffer ) ; Kill live buffer only.
( let
(( wins ( get-buffer-window-list buffer nil t ))) ; On all frames.
( when ( and ( buffer-modified-p buffer )
( fboundp '1on1-flash-ding-minibuffer-frame ))
( 1on1-flash-ding-minibuffer-frame t )) ; Defined in `oneonone.el'.
( when
( kill-buffer buffer ) ; Only delete windows if buffer killed.
( dolist ( win wins ) ; (User might keep buffer if modified.)
( when ( window-live-p win )
;; Ignore error, in particular,
;; "Attempt to delete the sole visible or iconified frame".
( condition-case nil
( delete-window win )
( error nil ))))))
( when ( interactive-p )
( error
"Cannot kill buffer. Not a live buffer: `%s'" buffer ))))
( setq vterm-kill-buffer-on-exit t )
( define-key
vterm-mode-map ( kbd "M-k" )
( lambda ()
( interactive )
( kill-buffer-and-its-windows ( current-buffer ))))
;; Add goto-address-mode to vterm-mode-hook for clickable links
( add-hook 'vterm-mode-hook 'goto-address-mode ))
Vterm Toggle ( use-package
vterm-toggle
:after vterm
:demand t
:bind
( :map
vterm-mode-map
( "<escape>" . vterm-send-C-c )
( "M-T" . vterm-toggle )
( "M-R" . vterm-toggle-cd )
( "M-n" . vterm-toggle-forward )
( "M-p" . vterm-toggle-backward ))
( :map global-map ( "M-T" . vterm-toggle ) ( "M-R" . vterm-toggle-cd ))
:config
( define-key vterm-mode-map ( kbd "M-T" ) 'vterm-toggle )
( define-key vterm-mode-map ( kbd "M-R" ) 'vterm-toggle-cd )
( setq vterm-toggle-scope 'dedicated )
( setq vterm-toggle-project-root t )
( setq vterm-toggle-cd-auto-create-buffer nil )
( setq vterm-toggle-reset-window-configration-after-exit t )
( setq vterm-toggle-fullscreen-p nil )
( setq vterm-toggle-hide-method 'bury-all-vterm-buffer )
( add-to-list
'display-buffer-alist
' (( lambda ( buffer-or-name _ )
( let (( buffer ( get-buffer buffer-or-name )))
( with-current-buffer buffer
( or ( equal major-mode 'vterm-mode )
( string-prefix-p
vterm-buffer-name ( buffer-name buffer ))))))
( display-buffer-reuse-window display-buffer-in-direction )
( direction . bottom )
( dedicated . t )
( reusable-frames . visible )
( window-height . 0.3 )
( window-width . 0.3 )))
;; Function to get project root without prompting
( defun my/get-project-root ()
( or ( when ( fboundp 'project-root )
( when-let (( project ( project-current nil )))
( project-root project )))
( when ( fboundp 'projectile-project-root )
( projectile-project-root ))
default-directory ))
;; Override vterm-toggle--new
( defun vterm-toggle--new ( &optional buffer-name )
"New vterm buffer."
( let* (( buffer-name ( or buffer-name vterm-buffer-name ))
( default-directory
( if vterm-toggle-project-root
( my/get-project-root )
default-directory )))
( if vterm-toggle-fullscreen-p
( vterm buffer-name )
( if ( eq major-mode 'vterm-mode )
( let (( display-buffer-alist nil ))
( vterm buffer-name ))
( vterm-other-window buffer-name )))))
;; Override vterm-toggle--project-root
( defun vterm-toggle--project-root ()
( my/get-project-root )))
Window management Zoom I think this is a little too aggressive right now, but it’s a cool idea.
(use-package
zoom
:init (setq zoom-size '(0.618 . 0.618))
:config (zoom-mode))
Visual Fill Column (use-package
visual-fill-column
:init (setq visual-fill-column-center-text t)
:config (visual-fill-column-mode 1))
Centered window ( use-package
centered-window
:init ( setq cwm-centered-window-width 180 )
:ensure t
:config ( centered-window-mode t )
; reload the fringe color after loading the theme
( cwm-update-fringe-background ))
Editing Packages Global Keybindings ( global-set-key ( kbd "M-k" ) ( lambda () ( interactive ) ( kill-this-buffer nil )))
( global-set-key ( kbd "C-c C-b" ) 'compile )
( global-set-key ( kbd "M-0" ) 'delete-window )
( global-set-key ( kbd "M-1" ) 'delete-other-windows )
( global-set-key ( kbd "M-2" ) 'split-window-below )
( global-set-key ( kbd "M-3" ) 'split-window-right )
; Unbind reverse search because we'll use swiper
( global-unset-key ( kbd "C-r" ))
( global-set-key ( kbd "M-u" ) 'upcase-dwim )
( global-set-key ( kbd "M-l" ) 'downcase-dwim )
( global-set-key ( kbd "C-." ) 'xref-find-definitions-other-window )
( define-key global-map ( kbd "RET" ) 'newline-and-indent )
Font Lock (setq font-lock-support-mode 'jit-lock-mode)
(setq jit-lock-defer-time nil)
(setq jit-lock-stealth-time 0)
Ctrl-F ( use-package
ctrlf
:config
( define-key
ctrlf-minibuffer-mode-map ( kbd "C-r" ) 'ctrlf-backward-default )
( setq ctrlf-default-search-style 'fuzzy-regexp )
( setq ctrlf-default-search-style 'literal )
( ctrlf-mode t ))
Whole line or region ( use-package
whole-line-or-region
:config ( whole-line-or-region-global-mode t ))
Popper (use-package
popper
:bind
(("C-`" . popper-toggle-latest)
("M-`" . popper-cycle)
("C-M-`" . popper-toggle-type))
:init
(setq popper-reference-buffers
'("\\*Messages\\*"
"Output\\*$"
"\\*Async Shell Command\\*"
help-mode
compilation-mode))
(popper-mode +1) (popper-echo-mode +1))
Page break lines ( use-package page-break-lines :config ( global-page-break-lines-mode ))
Eldoc ( use-package eldoc :hook ( prog-mode . eldoc-mode ))
Which Key ( use-package which-key :config ( which-key-mode 1 ))
Ibuffer ( use-package
ibuffer
:config
( global-set-key ( kbd "C-x C-b" ) 'ibuffer )
( define-key ibuffer-mode-map ( kbd "M-o" ) nil ))
;; Use ace-popup-menu for completions
(use-package
ace-popup-menu
:config
(ace-popup-menu-mode 1)
(setq ace-popup-menu-show-pane-header t))
Line Numbers in Code ( setq require-final-newline t )
( setq show-trailing-whitespace t )
( setq native-comp-async-report-warnings-errors nil )
;; Show the line number of the cursor in the mode bar at the bottom of each buffer
( setq line-number-mode t )
Bells ;; Disable the loud bell
( setq ring-bell-function
( lambda ()
( let (( orig-fg ( face-foreground 'mode-line )))
( set-face-foreground 'mode-line "#F2804F" )
( run-with-idle-timer 0.1 nil
( lambda ( fg )
( set-face-foreground 'mode-line fg ))
orig-fg ))))
Backups ;; Make sure all backup files only live in one place
( setq backup-directory-alist ` (( ".*" . , temporary-file-directory )))
( setq auto-save-file-name-transforms
` (( ".*" , temporary-file-directory t )))
;; Don't truncate lines
( setq truncate-lines t )
( setq-default indent-tabs-mode nil )
;; Don't show the scroll bar on the side of buffers
( scroll-bar-mode -1 )
;; Don't show the toolbar, it just takes up space
( tool-bar-mode -1 )
;; Show column number in the modeline
( setq column-number-mode t )
( setq blink-paren-function nil )
( setq inhibit-startup-screen t )
Expand Region Vanilla (use-package
expand-region
:config (global-set-key (kbd "M-J") 'er/expand-region))
With Tree Sitter Support ( use-package
expreg
:config ( global-set-key ( kbd "M-J" ) 'expreg-expand ))
Direnv ( use-package
direnv
:init
; An attempt to run direnv earlier in the startup process
( setq direnv--hooks
' ( find-file-hook
post-command-hook before-hack-local-variables-hook ))
:config ( direnv-mode 't ))
Fish (use-package fish-mode)
Window movement keybindings ( define-key term-raw-map ( kbd "M-o" ) 'next-multiframe-window )
( define-key term-raw-map ( kbd "M-i" ) 'previous-multiframe-window )
( define-key global-map ( kbd "M-o" ) 'next-multiframe-window )
( define-key global-map ( kbd "M-i" ) 'previous-multiframe-window )
( setq aw-keys ' ( ?a ?s ?d ?f ?g ?h ?j ?k ?l ))
Anzu ( use-package
anzu
:bind
(( [remap query-replace] . #' anzu-query-replace )
( [remap query-replace-regexp] . #' anzu-query-replace-regexp ))
:config ( global-anzu-mode +1 ))
Line Highlighting ( use-package lin :init ( lin-global-mode t ))
( use-package
hl-line
:config
( add-hook
'eshell-mode-hook ( lambda () ( setq-local global-hl-line-mode nil )))
( add-hook
'term-mode-hook ( lambda () ( setq-local global-hl-line-mode nil )))
( add-hook
'vterm-mode-hook ( lambda () ( setq-local global-hl-line-mode nil )))
( global-hl-line-mode t ))
;; Replace the text of selections
( pending-delete-mode t )
Undo Undo-Fu ( use-package
undo-fu
:bind ( "C-/" . undo-fu-only-undo ) ( "C-?" . undo-fu-only-redo ))
( use-package undo-fu-session :config ( global-undo-fu-session-mode ))
Vundo ( use-package
vundo
:init ( setq vundo-glyph-alist vundo-unicode-symbols ))
So long mode I find this breaks browsing of long json files, more than it helps me out.
(global-so-long-mode t)
Emacs startup profiler (esup) ( use-package esup )
Scratch ( use-package
scratch
:defer t
:straight
' ( scratch
:host nil
:type git
:repo "https://codeberg.org/emacs-weirdware/scratch.git" )
:config ( scratch--create 'emacs-lisp-mode "*scratch*" ))
Multiple Cursors (MC) ( use-package
multiple-cursors
:config
( global-set-key ( kbd "C-M-j" ) 'mc/edit-lines )
( global-set-key ( kbd "C->" ) 'mc/mark-next-like-this )
( global-set-key ( kbd "C-<" ) 'mc/mark-previous-like-this )
( global-set-key ( kbd "C-c C-<" ) 'mc/mark-all-like-this )
( global-set-key ( kbd "C-M-=" ) 'mc/mark-all-symbols-like-this ))
Dashboard ( use-package
dashboard
:straight
' ( emacs-dashboard
:type git
:host github
:repo "emacs-dashboard/emacs-dashboard"
:files ( "banners" :defaults ))
:config ( setq dashboard-projects-backend 'project-el )
;; Set the title
( setq dashboard-banner-logo-title "Welcome to Emacs!" )
;; Set the banner
( setq dashboard-startup-banner 'official )
( setq dashboard-items
' (( projects . 5 )
( recents . 5 ) ( bookmarks . 5 )
;; (agenda . 5)
( registers . 5 )))
;; ;; Value can be
;; ;; 'official which displays the official emacs logo
;; ;; 'logo which displays an alternative emacs logo
;; ;; 1, 2 or 3 which displays one of the text banners
;; ;; "path/to/your/image.png" which displays whatever image you would prefer
;; ;; Content is not centered by default. To center, set
( setq dashboard-center-content t )
( setq initial-buffer-choice ( lambda () ( get-buffer "*dashboard*" )))
;; ;; To disable shortcut "jump" indicators for each section, set
;; (setq dashboard-show-shortcuts nil)
;; Override this function so that we can filter remote projects
( defun dashboard-projects-backend-load-projects ()
"Depending on `dashboard-projects-backend' load corresponding backend.
Return function that returns a list of projects."
( cl-remove-if
( lambda ( x ) ( string-search "/ssh" x ))
( cl-case
dashboard-projects-backend
( ` projectile
( require 'projectile )
( dashboard-mute-apply ( projectile-cleanup-known-projects ))
( projectile-load-known-projects ))
( ` project-el
( require 'project )
( dashboard-mute-apply
( dashboard-funcall-fboundp #' project-forget-zombie-projects ))
( project-known-project-roots ))
( t
( display-warning
' ( dashboard ) "Invalid value for `dashboard-projects-backend'"
:error )))))
( dashboard-setup-startup-hook ))
Breadcrumb mode Because I’m trying this out, I’m going to disable LSP’s breadcrumb mode, which I’ve been disappointed with.
(use-package
breadcrumb
:straight '(breadcrumb :type git :host github :repo "joaotavora/breadcrumb")
:config (breadcrumb-mode t))
Parens ( setq show-paren-when-point-inside-paren 't )
( setq show-paren-style 'mixed )
( setq show-paren-context-when-offscreen 't )
( setq show-paren-context-when-offscreen t )
( setq show-paren-style 'mixed )
;; Treat ‘<’ and ‘>’ as if they were words, instead of ‘parenthesis’.
( modify-syntax-entry ?< "w<" )
( modify-syntax-entry ?> "w>" )
;; Show matching parens
( setq show-paren-delay 0 )
( show-paren-mode t )
Electric Pair (use-package
elec-pair
:config ;; Disable electric pair in minibuffer
(defun my/inhibit-electric-pair-mode (char)
(or (minibufferp) (electric-pair-conservative-inhibit char)))
(setq electric-pair-inhibit-predicate
#'my/inhibit-electric-pair-mode)
(electric-pair-mode t)
;; The ‘<’ and ‘>’ are not ‘parenthesis’, so give them no compleition.
(setq electric-pair-inhibit-predicate
(lambda (c)
(or (member c '(?< ?> ?~))
(electric-pair-default-inhibit c)))))
Smartparens ( use-package
smartparens
:hook ( prog-mode . smartparens-mode )
:hook ( text-mode . smartparens-mode )
:hook ( org-mode . smartparens-mode )
:hook ( markdown-mode . smartparens-mode )
:config
;; load default config
( require 'smartparens-config ))
Rsync-mode Loving use of my old colleague Ryan Pilgrim ’s package to sync accross our secure environments. Edit: Now trying out handcrafted Unison mode.
(use-package
rsync-mode
:straight '(rsync-mode :type git :host github :repo "jsigman/rsync-mode"))
Unison sync mode This is my own little package for syncing with Unison.
( use-package
unison-sync-mode
:straight
( :host github :repo "jsigman/unison-sync-mode" ))
Ripgrep ( use-package wgrep :config ( setq wgrep-auto-save-buffer t ))
( use-package rg :config ( rg-enable-menu ) ( setq rg-executable "rg" ))
Avy Main Package ( use-package
avy
:config
;; (global-set-key
;; (kbd "C-;")
;; 'avy-goto-char-timer) ;; I use this most frequently
( global-set-key
( kbd "C-'" )
'avy-goto-line ) ;; Consistent with ivy-avy
( global-set-key
( kbd "C-M-'" )
'avy-goto-end-of-line ) ;; Consistent with ivy-avy
( setq avy-case-fold-search nil ) ;; case sensitive makes selection easier
( setq avy-indent-line-overlay t ))
Casual Avy ( use-package casual-avy
:ensure t
:bind ( "M-'" . casual-avy-tmenu ))
Apheleia ( use-package
apheleia
:config
( setf ( alist-get 'isort apheleia-formatters )
' ( "isort" "--stdout" "-" ))
( setf ( alist-get 'python-ts-mode apheleia-mode-alist ) ' ( isort black ))
( add-to-list
'apheleia-formatters
' ( prettier-toml
npx "prettier" "--stdin-filepath" filepath "--parser=toml" ))
( add-to-list 'apheleia-mode-alist ' ( conf-toml-mode . prettier-toml ))
( defun apheleia-indent-region+ ( orig scratch callback )
( with-current-buffer scratch
( setq-local indent-line-function
( buffer-local-value 'indent-line-function orig ))
( indent-region ( point-min ) ( point-max ))
( funcall callback scratch )))
( push ' ( jsonian-mode . prettier-json ) apheleia-mode-alist )
( setq apheleia-mode-alist
( assq-delete-all 'emacs-lisp-mode apheleia-mode-alist ))
;; Add shfmt for direnv-envrc-mode
( add-to-list 'apheleia-mode-alist ' ( direnv-envrc-mode . shfmt ))
( apheleia-global-mode t ))
Elisp Autofmt ( use-package
elisp-autofmt
:commands ( elisp-autofmt-mode elisp-autofmt-buffer )
:hook ( emacs-lisp-mode . elisp-autofmt-mode )
:init ( setq elisp-autofmt-check-elisp-autofmt-exists 'always )
:straight
' ( elisp-autofmt
;; :files (:defaults "elisp-autofmt")
:host nil
:type git
:repo "https://codeberg.org/ideasman42/emacs-elisp-autofmt.git" )
:config
( setq elisp-autofmt-on-save-p
; return t unless in "~/.emacs.d/straight"
( lambda ()
( not
( string-match-p
( concat
"^"
( regexp-quote
( expand-file-name "straight" user-emacs-directory )))
( buffer-file-name ))))))
Whitespace butler ( use-package ws-butler
:hook ( prog-mode . ws-butler-mode )
:hook ( org-mode . ws-butler-mode ))
Indentation Electric Indent ( electric-indent-mode 0 )
Highlight Indent Guides ( use-package
highlight-indent-guides
:hook (( python-mode ) . highlight-indent-guides-mode ))
Indent bars This is not working now due to stipple support, I believe.
(use-package
indent-bars
:straight (indent-bars :type git :host github :repo "jdtsmith/indent-bars")
:config
(require 'indent-bars-ts) ; not needed with straight
:custom
(indent-bars-treesit-support t)
(indent-bars-treesit-ignore-blank-lines-types '("module"))
;; Add other languages as needed
(indent-bars-treesit-scope
'((python
function_definition
class_definition
for_statement
if_statement
with_statement
while_statement)))
;; Note: wrap may not be needed if no-descend-list is enough
;;(indent-bars-treesit-wrap '((python argument_list parameters ; for python, as an example
;; list list_comprehension
;; dictionary dictionary_comprehension
;; parenthesized_expression subscript)))
:hook ((python-mode) . indent-bars-mode))
Dired ( setq
dired-omit-files
"^\\.?#\\|^\\.\\(DS_Store\\|localized\\|AppleDouble\\)$\\|^\\.\\.$" )
( setq dired-kill-when-opening-new-dired-buffer t )
( when ( eq system-type 'darwin ) ( setq
insert-directory-program "gls"
dired-use-ls-dired t ))
( setq dired-listing-switches "-al --group-directories-first" )
;; wdired settings
( use-package
wdired
:config
( setq wdired-allow-to-change-permissions t )
( define-key dired-mode-map ( kbd "e" ) 'wdired-change-to-wdired-mode )
( define-key dired-mode-map ( kbd "M-G" ) nil ))
Project Packages Project.el Migrating from projectile
to project.el
for better integration with eglot
and Flymake
. This setup extends the default project detection to handle Git submodules correctly while maintaining existing functionality.
( use-package
project
:demand t
:straight ( :type built-in )
:bind-keymap ( "C-c p" . project-prefix-map )
:config ( setq project-vc-include-untracked t )
;; Custom project detection function
( defun my/project-try-local ( dir )
"Determine if DIR is a project by finding the nearest .git directory.
This helps with correctly identifying Git submodules as separate projects."
( let (( root ( locate-dominating-file dir ".git" )))
( when root
( cons 'transient ( expand-file-name root )))))
;; TODO - This function not working yet for monorepo folders
;; Add our custom function to the beginning of project-find-functions
;; (add-hook 'project-find-functions #'my/project-try-local)
;; Set up project-vc-ignores
( setq project-vc-ignores
' ( "venv/"
"typings/"
"node_modules/"
".mypy_cache/"
".pytest_cache/"
".cache/"
".dvc/cache/"
".dvc/tmp/"
".jekyll-cache/"
"!*.org"
"!/notes/" ))
;; Function to add ignored directories to project-vc-ignores
( defun my/add-project-ignore ( dir )
"Add DIR to the list of ignored directories in project-vc-ignores."
( add-to-list 'project-vc-ignores dir ))
;; Add additional directories to project-vc-ignores
( dolist ( dir
' ( "venv"
"data"
"typings"
"node_modules"
".mypy_cache"
".pytest_cache"
".cache"
".dvc/cache"
".dvc/tmp"
".jekyll-cache" ))
( my/add-project-ignore dir ))
;; Disable automatic project remembering
( advice-add 'project-remember-project :override #' ignore )
;; Prevent automatic removal of projects not found
( advice-add 'project--remove-from-project-list :override #' ignore ))
Notes on changes and functionality: Custom project detection: We’ve added a new function `my/project-try-local` that finds the nearest .git directory. This function is added to the beginning of `project-find-functions`, allowing it to handle Git submodules correctly. If `my/project-try-local` doesn’t find a project, the default `project-try-vc` will still run. Existing functionality preserved: The `project-vc-ignores` setup remains unchanged. The `my/add-project-ignore` function and the `dolist` that adds additional ignores are kept as is. Advice to disable automatic project remembering and removal is maintained. Usage of built-in project.el: The `:straight (:type built-in)` ensures we’re using the built-in version of project.el, which is important for compatibility with eglot and Flymake. Keybinding: The `C-c p` keybinding for the project prefix map is preserved. This setup should now correctly handle Git submodules as separate projects while maintaining all the customizations and ignores you had previously set up. The custom project detection function will be tried first, falling back to the default behavior if it doesn’t find a project.
Projectile (use-package
projectile
:init (setq projectile-git-submodule-command nil)
;; always ignore the home directory and root
(setq projectile-ignored-projects
`("/" "~/" ,(expand-file-name "~/")))
(setq projectile-track-known-projects-automatically nil)
;; Use alien as the default, and project-wise add other files
(setq projectile-indexing-method 'native)
(setq projectile-enable-caching t)
(setq projectile-files-cache-expire 300)
(setq projectile-file-exists-remote-cache-expire nil)
:config
(define-key projectile-mode-map (kbd "s-p") 'projectile-command-map)
(define-key
projectile-mode-map (kbd "C-c p") 'projectile-command-map)
(define-key projectile-mode-map (kbd "M-K") 'projectile-kill-buffers)
(add-to-list 'projectile-globally-ignored-directories "/venv")
(add-to-list 'projectile-globally-ignored-directories "/data")
(add-to-list 'projectile-globally-ignored-directories "/typings")
(add-to-list
'projectile-globally-ignored-directories "/node_modules")
(add-to-list 'projectile-globally-ignored-directories "/.mypy_cache")
(add-to-list
'projectile-globally-ignored-directories "/.pytest_cache")
(add-to-list 'projectile-globally-ignored-directories "/.cache")
(add-to-list 'projectile-globally-ignored-directories "/.dvc/cache")
(add-to-list 'projectile-globally-ignored-directories "/.dvc/tmp")
(add-to-list
'projectile-globally-ignored-directories "/.jekyll-cache")
(projectile-mode +1)
;; (add-hook 'magit-run-section-hook 'projectile-invalidate-cache)
(add-hook
'magit-section-post-command-hook 'projectile-invalidate-cache)
(setq projectile-ignored-project-function
(lambda (project-root)
(string-match-p tramp-file-name-regexp project-root))))
Completions Corfu ( use-package
corfu
;; Optional customizations
:custom
( corfu-cycle t ) ;; Enable cycling for `corfu-next/previous'
( corfu-auto t ) ;; Enable auto completion
;; (corfu-commit-predicate nil) ;; Do not commit selected candidates on next input
( corfu-quit-at-boundary 'separator ) ;; Automatically quit at word boundary
( corfu-quit-no-match 'separator ) ;; Automatically quit if there is no match
( corfu-scroll-margin 5 ) ;; Use scroll margin
;; (corfu-preview-current nil) ;; Do not preview current candidate
( corfu-auto-delay 0.0 )
( corfu-auto-prefix 1 )
( corfu-on-exact-match 'quit )
;; (corfu-separator ?\s) ;; Orderless field separator
;; (corfu-preview-current nil) ;; Disable current candidate preview
;; (corfu-preselect-first nil) ;; Disable candidate preselection
;; (corfu-on-exact-match nil) ;; Configure handling of exact matches
;; (corfu-echo-documentation nil) ;; Disable documentation in the echo area
;; (corfu-scroll-margin 5) ;; Use scroll margin
;; You may want to enable Corfu only for certain modes.
;; :hook ((prog-mode . corfu-mode)
;; (shell-mode . corfu-mode)
;; (eshell-mode . corfu-mode))
;; Recommended: Enable Corfu globally.
;; This is recommended since dabbrev can be used globally (M-/).
:init ( global-corfu-mode )
;; :config
;; (define-key corfu-map (kbd "M-p") #'corfu-doc-scroll-down) ;; corfu-next
;; (define-key corfu-map (kbd "M-n") #'corfu-doc-scroll-up) ;; corfu-previous
;; Quit on save
:hook ( before-save-hook . corfu-quit )
:load-path "straight/build/corfu/extensions"
:config
( require 'corfu-history )
( corfu-history-mode 1 )
( savehist-mode 1 )
( add-to-list 'savehist-additional-variables 'corfu-history )
;; (corfu-mode-hook . corfu-doc-mode)
)
( advice-add 'corfu--candidates :around
( lambda ( orig-fun &rest args )
( message "corfu--candidates called with args: %S" args )
( let (( result ( apply orig-fun args )))
( message "corfu--candidates returned: %S" result )
result )))
Corfu/Eglot integration From this source .
( advice-add 'eglot-completion-at-point :around #' cape-wrap-buster )
;; Option 1: Specify explicitly to use Orderless for Eglot
( setq completion-category-overrides ' (( eglot ( styles orderless ))
( eglot-capf ( styles orderless ))))
;; Option 2: Undo the Eglot modification of completion-category-defaults
( with-eval-after-load 'eglot
( setq completion-category-defaults nil ))
;; Enable cache busting, depending on if your server returns
;; sufficiently many candidates in the first place.
( advice-add 'eglot-completion-at-point :around #' cape-wrap-buster )
( defun my/eglot-capf ()
( setq-local completion-at-point-functions
( list ( cape-capf-super
#' eglot-completion-at-point
;; #'yas-expand
#' cape-file ))))
( add-hook 'eglot-managed-mode-hook #' my/eglot-capf )
Cape ( defun add-cape-completions ()
( add-to-list 'completion-at-point-functions #' cape-file )
;; (add-to-list 'completion-at-point-functions
;; #'cape-keyword)
;; (add-to-list 'completion-at-point-functions
;; #'cape-symbol)
)
;; Add extensions
( use-package
cape
;; Bind dedicated completion commands
;; :bind (("C-c p p" . completion-at-point) ;; capf
;; ("C-c p t" . complete-tag) ;; etags
;; ("C-c p d" . cape-dabbrev) ;; or dabbrev-completion
;; ("C-c p f" . cape-file)
;; ("C-c p k" . cape-keyword)
;; ("C-c p s" . cape-symbol)
;; ("C-c p a" . cape-abbrev)
;; ("C-c p i" . cape-ispell)
;; ("C-c p l" . cape-line)
;; ("C-c p w" . cape-dict)
;; ("C-c p \\" . cape-tex)
;; ("C-c p _" . cape-tex)
;; ("C-c p ^" . cape-tex)
;; ("C-c p &" . cape-sgml)
;; ("C-c p r" . cape-rfc1345))
:hook ( corfu-mode . add-cape-completions ))
;; A few more useful configurations...
( setq completion-cycle-threshold 3 )
Orderless ;; Optionally use the `orderless' completion style.
( use-package
orderless
:after vertico
:init
;; Tune the global completion style settings to your liking!
;; This affects the minibuffer and non-lsp completion at point.
( setq
completion-styles ' ( orderless partial-completion basic )
completion-category-defaults nil
completion-category-overrides nil ))
;; ;; Use dabbrev with Corfu!
;; (use-package dabbrev
;; ;; Swap M-/ and C-M-/
;; :bind (("M-/" . dabbrev-completion)
;; ("C-M-/" . dabbrev-expand)))
;; A few more useful configurations...
( use-package
emacs
:init
;; TAB cycle if there are only few candidates
( setq completion-cycle-threshold 3 )
;; Emacs 28: Hide commands in M-x which do not apply to the current mode.
;; Corfu commands are hidden, since they are not supposed to be used via M-x.
;; (setq read-extended-command-predicate
;; #'command-completion-default-include-p)
)
Vertico ;; Enable vertico
( use-package
vertico
:init ( vertico-mode )
:bind ( :map vertico-map ( "C-j" . vertico-exit-input ))
;; Different scroll margin
;; (setq vertico-scroll-margin 0)
;; Show more candidates
;; (setq vertico-count 20)
;; Grow and shrink the Vertico minibuffer
;; (setq vertico-resize t)
;; Optionally enable cycling for `vertico-next' and `vertico-previous'.
;; (setq vertico-cycle t)
)
;; Persist history over Emacs restarts. Vertico sorts by history position.
( use-package savehist :init ( savehist-mode ))
;; A few more useful configurations...
( use-package
emacs
: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 )
;; Emacs 28: Hide commands in M-x which do not work in the current mode.
;; Vertico commands are hidden in normal buffers.
;; (setq read-extended-command-predicate
;; #'command-completion-default-include-p)
;; Enable recursive minibuffers
( setq enable-recursive-minibuffers t ))
Marginalia ( use-package marginalia
;; 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 ))
;; The :init section is always executed.
:init
;; Marginalia must be activated in the :init section of use-package such that
;; the mode gets enabled right away. Note that this forces loading the
;; package.
( marginalia-mode ))
Embark ( use-package embark
:ensure t
:bind
(( "C-." . embark-act ) ;; pick some comfortable binding
;; ("C-;" . embark-dwim) ;; good alternative: M-.
( "C-h B" . embark-bindings )) ;; alternative for `describe-bindings'
:init
;; Optionally replace the key help with a completing-read interface
( setq prefix-help-command #' embark-prefix-help-command )
;; Show the Embark target at point via Eldoc. You may adjust the
;; Eldoc strategy, if you want to see the documentation from
;; multiple providers. Beware that using this can be a little
;; jarring since the message shown in the minibuffer can be more
;; than one line, causing the modeline to move up and down:
;; (add-hook 'eldoc-documentation-functions #'embark-eldoc-first-target)
;; (setq eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly)
:config
;; Hide the mode line of the Embark live/completions buffers
( add-to-list 'display-buffer-alist
' ( "\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
nil
( window-parameters ( mode-line-format . none )))))
;; 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 ))
Consult ;; Example configuration for Consult
( use-package
consult
;; Replace bindings. Lazily loaded due by `use-package'.
:demand t
: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)
( "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
( defun consult-ripgrep-project-root ( &optional initial )
( interactive "P" )
( let (( dir ( funcall consult-project-function )))
( consult--grep
"Ripgrep" #' consult--ripgrep-make-builder dir initial )))
;; 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.
;; (define-key consult-narrow-map (vconcat consult-narrow-key "?") #'consult-narrow-help)
;; By default `consult-project-function' uses `project-root' from project.el.
;; Optionally configure a different project root function.
;;;; 1. project.el (the default)
;; (setq consult-project-function #'project-root)
;;;; 2. vc.el (vc-root-dir)
;; (setq consult-project-function (lambda (_) (vc-root-dir)))
;;;; 3. locate-dominating-file
;; (setq consult-project-function (lambda (_) (locate-dominating-file "." ".git")))
;;;; 4. projectile.el (projectile-project-root)
;; (autoload 'projectile-project-root "projectile")
;; (setq consult-project-function
;; (lambda (_) (projectile-project-root)))
;;;; 5. No project support
;; (setq consult-project-function nil)
)
Snippets Yasnippet ( use-package
yasnippet
:demand t
:init
( load "yasnippet.el" ) ; get rid of weird invalid function issue
)
( use-package
yasnippet-snippets
:demand t
:straight
' ( yasnippet-snippets
:type git
:host github
:repo "jsigman/yasnippet-snippets" ))
( yas-global-mode 1 )
Consult Yasnippet ( use-package
consult-yasnippet
:after consult
:config ( global-set-key ( kbd "M-Y" ) 'consult-yasnippet ))
Yasnippet-Capf ( use-package
yasnippet-capf
:after cape
:init
( setq yasnippet-capf-lookup-by 'key ) ;; key or name
:config ( add-to-list 'completion-at-point-functions #' yasnippet-capf ))
LSP Server Support Eglot ( use-package
eglot
:ensure t
:demand t
:straight nil
:init
( defun file-is-remote-p ()
"Return non-nil if the current file is remote."
( and ( buffer-file-name ) ( file-remote-p ( buffer-file-name ))))
( defun maybe-start-eglot ()
"Start Eglot if the current file is not remote."
( unless ( file-is-remote-p )
( when ( fboundp 'eglot-ensure )
( eglot-ensure ))))
( defun disable-eglot-if-remote ()
"Disable Eglot if the current file is remote."
( when ( and ( file-is-remote-p )
( fboundp 'eglot-managed-p )
( fboundp 'eglot-shutdown )
( eglot-managed-p ))
( eglot-shutdown )))
:hook
(( python-mode
markdown-mode
sh-mode
yaml-mode
json-mode
dockerfile-mode
LaTeX-mode )
. maybe-start-eglot )
:hook ( find-file . disable-eglot-if-remote )
:config
( add-to-list
'eglot-server-programs
' ( python-mode . ( "pyright-langserver" "--stdio" )))
( add-to-list
'eglot-server-programs
' ( markdown-mode . ( "vscode-markdown-language-server" "--stdio" )))
( add-to-list
'eglot-server-programs
' ( sh-mode . ( "bash-language-server" "start" )))
( add-to-list
'eglot-server-programs
' ( yaml-mode . ( "yaml-language-server" "--stdio" )))
( add-to-list
'eglot-server-programs
' ( json-mode . ( "vscode-json-languageserver" "--stdio" )))
( add-to-list
'eglot-server-programs
' ( dockerfile-mode . ( "docker-langserver" "--stdio" )))
( add-to-list 'eglot-server-programs ' ( LaTeX-mode . ( "texlab" )))
( custom-set-faces
' ( eglot-highlight-symbol-face
(( t ( :inherit highlight :underline t )))))
;; Basic settings
( setq eglot-autoshutdown t )
( setq eglot-extend-to-xref t )
;; Configure completion
( setq completion-category-defaults nil )
( setq completion-cycle-threshold 3 )
( setq tab-always-indent 'complete )
;; Increase read-process-output-max
( setq read-process-output-max ( * 1024 1024 ))
;; Ignore certain directories for file watching
( setq eglot-ignored-server-capabilities
' ( :documentOnTypeFormattingProvider ))
;; Key bindings (optional)
:bind
( :map
eglot-mode-map
( "C-c l a" . eglot-code-actions )
( "C-c l r" . eglot-rename )
( "C-c l f" . eglot-format )
( "C-c l d" . eglot-find-declaration )))
Eglot-iedit ( use-package
iedit
:custom-face ( iedit-occurrence (( t ( :background "Red" ))))
:bind ( :map eglot-mode-map ( "M-S" . eglot-iedit-highlights ))
:init
( defun eglot-iedit-highlights ()
"Start an `iedit' operation on the documentHighlights at point.
This can be used as a primitive `eglot-rename' replacement if the
language server doesn't support renaming.
See also `eglot-server-capable' for :documentHighlightProvider."
( interactive )
( unless ( eglot-server-capable :documentHighlightProvider )
( error "Server does not support documentHighlights" ))
( let
(( highlights
( eglot--request
( eglot--current-server-or-lose )
:textDocument/documentHighlight ( eglot--TextDocumentPositionParams )))
( -compare-fn
( lambda ( hl1 hl2 )
( and ( equal
( plist-get ( plist-get hl1 :range ) :start )
( plist-get ( plist-get hl2 :range ) :start ))
( equal
( plist-get ( plist-get hl1 :range ) :end )
( plist-get ( plist-get hl2 :range ) :end ))))))
( iedit-mode )
( dolist ( highlight ( -distinct highlights ))
( let* (( range ( plist-get highlight :range ))
( start
( eglot--lsp-position-to-point
( plist-get range :start )))
( end
( eglot--lsp-position-to-point ( plist-get range :end ))))
( iedit-add-occurrence-overlay start end ))))))
Programming Modes Elisp Elisp code libraries ( use-package dash )
( use-package ht )
( use-package pcre2el )
( use-package async )
Elisp UI ( use-package eros :config ( eros-mode t ))
( use-package
lisp-extra-font-lock
:config ( lisp-extra-font-lock-global-mode 1 ))
( use-package elisp-docstring-mode )
( use-package
highlight-function-calls
:hook ( emacs-lisp-mode . highlight-function-calls-mode ))
( use-package
inspector
:straight
' ( inspector :type git :host github :repo "mmontone/emacs-inspector" ))
( setq eval-expression-print-length nil )
( setq eval-expression-print-level nil )
Python ( use-package
python
:init
( setq python-shell-interpreter "python3" )
( setq python-shell-interpreter-args "-i" )
:config
( define-key python-mode-map ( kbd "C-c C-c" ) nil )
( define-key python-mode-map ( kbd "C-c C-p" ) nil ))
( add-hook
'python-mode-hook
( lambda ()
( mapc
( lambda ( pair ) ( push pair prettify-symbols-alist ))
' ( ;; Syntax
;; ("def" . #x2131)
;; ("not" . #x2757)
( "in" . #x2208 )
;; ("not in" . #x2209)
( "return" . #x27fc ) ( "yield" . #x27fb )
;; ("for" . #x2200)
;; Base Types
;; ("int" . #x2124)
;; ("float" . #x211d)
;; ("str" . #x1d54a)
;; ("True" . #x1d54b)
;; ("False" . #x1d53d)
;; Mypy
;; ("Dict" . #x1d507)
;; ("List" . #x2112)
;; ("Tuple" . #x2a02)
;; ("Set" . #x2126)
;; ("Iterable" . #x1d50a)
;; ("Any" . #x2754)
;; ("Union" . #x22c3)
))))
( use-package
pip-requirements
:init
( add-to-list
'auto-mode-alist
` ( , ( rx "requirements" ( zero-or-more anything ) ".in" string-end )
. pip-requirements-mode )))
Docstrings (use-package
buftra
:straight
'(buftra.el :type git :host github :repo "humitos/buftra.el"))
(use-package
py-pyment
:straight
'(py-cmd-buffer.el
:type git
:host github
:repo "humitos/py-cmd-buffer.el")
:after python
:config (setq py-pyment-options '("--output=google")))
Copying lines as a single line for pasting into the pdbpp debugger ( defun python-multiline-to-singleline ()
"Convert multi-line Python code in the current region to a single line with single spaces."
( interactive )
( when ( use-region-p )
( let* (( start ( region-beginning ))
( end ( region-end ))
( multi-line-code ( buffer-substring start end ))
( single-line-code ( replace-regexp-in-string "[ \t\n]+" " " multi-line-code )))
( kill-new single-line-code )
( message "Single-line code copied to kill ring." ))))
( with-eval-after-load 'python
( define-key python-mode-map ( kbd "C-c C-l" ) 'python-multiline-to-singleline ))
Markdown ( use-package
markdown-mode
:mode ( "README\\.md\\'" . gfm-mode )
:init ( setq markdown-command "multimarkdown" )
:config )
HTML ( use-package
sgml-mode
:mode ( "\\.html\\'" . html-mode )
:bind
( :map
sgml-mode-map
( "M-o b" . nil )
( "M-o d" . nil )
( "M-o i" . nil )
( "M-o l" . nil )
( "M-o o" . nil )
( "M-o u" . nil )
( "M-o M-o" . nil )
( "M-o" . nil ))
:bind
( :map
html-mode-map
( "M-o b" . nil )
( "M-o d" . nil )
( "M-o i" . nil )
( "M-o l" . nil )
( "M-o o" . nil )
( "M-o u" . nil )
( "M-o M-o" . nil )
( "M-o" . nil )))
( use-package html-ts-mode )
Web mode ( use-package
web-mode
:config
( add-to-list 'auto-mode-alist ' ( "\\.liquid\\'" . web-mode )))
Dotenv Mode ( use-package dotenv-mode :defer t )
Latex ( use-package
tex
:straight auctex
:defer t
:hook ( LaTeX-mode . visual-line-mode )
:hook ( LaTeX-mode . flyspell-mode )
:hook ( LaTeX-mode . LaTeX-math-mode )
:hook ( LaTeX-mode . TeX-source-correlate-mode )
:config
( setq TeX-auto-save t )
( setq TeX-parse-self t )
( setq-default TeX-master nil )
;; (add-hook 'LaTeX-mode-hook 'company-auctex-init)
;; (add-hook 'LaTeX-mode-hook 'company-mode)
( add-hook 'LaTeX-mode-hook 'turn-on-reftex )
( setq reftex-plug-into-AUCTeX t )
( setq TeX-PDF-mode t ))
( use-package cdlatex )
;; -------------------------/AucTex-------------------------------;;
MATLAB (use-package
matlab-mode
'(matlab-mode
:type git
:repo "https://git.code.sf.net/p/matlab-emacs/src")
:defer t)
(require 'matlab)
(setq matlab-shell-command-switches '("-nodesktop" "-nosplash"))
SQL So far, I am unable to find a way to compile sqlite3 on my own, but I would love to be able to do this with straight package management.
;; Override the 'yes-or-no-p' temporarily
( let (( original-yes-or-no-p ( symbol-function 'yes-or-no-p )))
( fset 'yes-or-no-p ( lambda ( &rest args ) t ))
;; Load the sqlite3 package
( use-package
sqlite3
:straight
( sqlite3
:type git
:host github
:repo "pekingduck/emacs-sqlite3-api"
:files ( "*.c" "*.h" "*.el" "Makefile" )))
;; Restore the original function
( fset 'yes-or-no-p original-yes-or-no-p ))
Yaml ( use-package yaml-mode )
( use-package yaml-ts-mode )
DAP Mode I’m currently not using DAP mode, and prefer to use python from the command line (vterm) with pdb
. Some day I’d like to learn this.
(use-package
dap-mode
:init
(if (eq system-type 'gnu/linux)
(add-to-list 'image-types 'svg))
:config
(add-hook
'dap-stopped-hook (lambda (arg) (call-interactively #'dap-hydra)))
;; Enabling only some features
(setq dap-auto-configure-features
'(sessions locals controls tooltip))
(setq dap-python-debugger 'debugpy)
(require 'dap-mode)
(require 'dap-python)
(require 'dap-ui)
(add-hook 'python-mode-hook 'dap-mode)
(add-hook 'python-mode-hook 'dap-ui-mode)
(add-hook 'python-mode-hook 'dap-tooltip-mode)
(define-key python-mode-map (kbd "M-D") #'dap-hydra))
JQ ( use-package jq-mode )
Json-ts-mode ( use-package json-mode )
( use-package json-ts-mode )
Jsonian (use-package
jsonian
:straight
'(jsonian
:type git
:host github
:repo "iwahbe/jsonian"
:build (:not autoloads)))
Large Language Models in Emacs Github Copilot ( use-package
copilot
:straight ( :host github :repo "zerolfx/copilot.el" :files ( "dist" "*.el" ))
:bind
( :map
copilot-completion-map ( "M-<return>" . copilot-accept-completion ))
:hook
(( prog-mode yaml-mode org-mode direnv-envrc-mode conf-mode )
.
my/copilot-mode-setup )
; setting tab-width to 4 for python-mode currently fixes issues with copilot
:hook ( python-mode . ( lambda () ( setq tab-width 4 )))
:config
( setq copilot-max-char -1 )
( setq copilot-indent-offset-warning-disable t )
;; Custom function to disable copilot on "coder.*" SSH hosts
( defun my/copilot-mode-setup ()
"Disable copilot-mode if connected to a 'coder.*' host."
( let (( remote-host ( file-remote-p default-directory 'host )))
( unless ( and remote-host
( string-match-p "^coder\\." remote-host ))
( copilot-mode 1 )))))
C3PO This one didn’t work very well
(use-package
c3po
:straight (:host github :repo "d1egoaz/c3po.el")
:config
(setq chat-api-key
(f-read-text (expand-file-name "~/.openai/emacs-key.txt"))))
OpenAI (use-package
openai
:straight
(openai :type git :host github :repo "emacs-openai/openai")
:init (setq openai-key (getenv "OPENAI_API_KEY")))
(use-package
chatgpt
:straight (chatgpt :type git :host github :repo "emacs-openai/chatgpt")
;; :config (setq chatgpt-model "gpt-4-0613")
)
(use-package
codegpt
:straight
(codegpt :type git :host github :repo "emacs-openai/codegpt"))
(use-package
dall-e
:straight
(dall-e :type git :host github :repo "emacs-openai/dall-e"))
Ellama (use-package llm)
(use-package
ellama
:init
(setopt
ellama-provider (make-llm-ollama :chat-model "codellama:34b")))
Linting Flycheck (use-package
flycheck
:init
(define-fringe-bitmap 'my-flycheck-fringe-indicator
(vector
#b00000000
#b00000000
#b00000000
#b00000000
#b00000000
#b00000000
#b00000000
#b00011100
#b00111110
#b00111110
#b00111110
#b00011100
#b00000000
#b00000000
#b00000000
#b00000000
#b00000000))
(flycheck-define-error-level
'error
:severity 2
:overlay-category 'flycheck-error-overlay
:fringe-bitmap 'my-flycheck-fringe-indicator
:fringe-face 'flycheck-fringe-error)
(flycheck-define-error-level
'warning
:severity 1
:overlay-category 'flycheck-warning-overlay
:fringe-bitmap 'my-flycheck-fringe-indicator
:fringe-face 'flycheck-fringe-warning)
(flycheck-define-error-level
'info
:severity 0
:overlay-category 'flycheck-info-overlay
:fringe-bitmap 'my-flycheck-fringe-indicator
:fringe-face 'flycheck-fringe-info)
;; :config
;; (use-package flycheck-pos-tip )
;; (flycheck-pos-tip-mode)
; Flycheck
(setq flycheck-idle-change-delay 0.1)
(setq flycheck-display-errors-delay 0.1)
(setq flycheck-idle-buffer-switch-delay 0.1)
(setq flycheck-checkers (remove 'python-pylint flycheck-checkers))
(setq flycheck-checkers (remove 'python-pycompile flycheck-checkers))
(setq flycheck-checkers (remove 'python-pyright flycheck-checkers))
(add-hook 'after-init-hook #'global-flycheck-mode)
(setq flycheck-global-modes
'(python-base-mode js-mode python-mode python-ts-mode))
(defun debug-flycheck-ruff-disabled (symbol newval operation where)
"Log debug information when python-ruff is added to flycheck--automatically-disabled-checkers."
(when (and (eq operation 'set) (member 'python-ruff newval))
(let ((debug-on-error t)
(debug-buffer (get-buffer-create "*flycheck-ruff-debug*")))
(with-current-buffer debug-buffer
(goto-char (point-max))
(insert "\n\n")
(insert
(format-time-string
"Debug info captured at %Y-%m-%d %H:%M:%S\n\n"))
(insert
"python-ruff has been added to automatically disabled checkers.\n\n")
(insert (format "All disabled checkers: %s\n\n" newval))
(insert
(format ":enabled predicate returned: %s\n\n"
(flycheck-checker-get 'python-ruff :enabled)))
(insert
(format "Error threshold: %s\n\n"
flycheck-checker-error-threshold))
(insert "Stack trace:\n")
(insert
(with-output-to-string
(backtrace)))
(insert "\n\nEnd of debug info.\n"))
(display-buffer debug-buffer))))
(add-variable-watcher
'flycheck--automatically-disabled-checkers
#'debug-flycheck-ruff-disabled)
(setq flycheck-checker-error-threshold nil)
(setq flycheck-debug t))
Toggling flycheck buffer with “M-C” I have my own little hook to open the flycheck buffer with M-C
, and close it again with another M-C
keystroke.
(defvar should-delete-flycheck-list-buffer nil)
(defun my/flycheck-list-errors ()
"Open flycheck list if it doesn't exist. If it does, close it."
(interactive)
(let* ((target-buffer-name "*Flycheck errors*")
(target-buffer (get-buffer target-buffer-name))
(target-window (get-buffer-window target-buffer)))
(if (and target-buffer target-window)
;; the target buffer exists and window is visible
(progn
(when should-delete-flycheck-list-buffer
(delete-window target-window))
(kill-buffer target-buffer))
;; the target buffer doesn't exist or the window isn't visible
(let* ((starting-window-count (count-windows)))
(flycheck-list-errors)
(setq should-delete-flycheck-list-buffer
(> (count-windows) starting-window-count))))))
(define-key flycheck-mode-map (kbd "M-C") 'my/flycheck-list-errors)
Flymake ( use-package flymake :ensure t )
( add-hook 'python-ts-mode-hook 'flymake-mode )
Ruff ( use-package flymake-ruff :ensure t :config ( flymake-ruff-load ))
JSON (use-package flymake-json :ensure t :config (flymake-json-load))
Bugfix: Ruff ( defun flymake-ruff--check-buffer ()
"Generate a list of diagnostics for the current buffer."
( let (( code-buffer ( current-buffer ))
( start-line ( line-number-at-pos ( point-min ) t ))
( code-content
( without-restriction
( buffer-substring-no-properties ( point-min ) ( point-max ))))
( dxs ' ()))
( with-temp-buffer
( insert code-content )
( let* (( config
( and ( project-current )
( seq-find
#' file-readable-p
( mapcar
( lambda ( f )
( expand-file-name
f
( project-root ( project-current ))))
flymake-ruff--default-configs ))))
( args
( if config
( append
( list "check" "--config" config )
flymake-ruff-program-args )
( cons "check" flymake-ruff-program-args ))))
( apply #' call-process-region
( point-min )
( point-max )
flymake-ruff-program
t
t
nil
args ))
( goto-char ( point-min ))
( while ( search-forward-regexp flymake-ruff--output-regex
( point-max )
t )
( when ( match-string 2 )
( let* (( line ( string-to-number ( match-string 2 )))
( col ( string-to-number ( match-string 3 )))
( code ( match-string 4 ))
( msg ( match-string 5 ))
( description ( format "Ruff: %s %s" code msg ))
( region
( flymake-diag-region
code-buffer ( 1+ ( - line start-line )) col ))
( dx
( flymake-make-diagnostic
code-buffer
( car region )
( cdr region )
:error description )))
( add-to-list 'dxs dx )))))
dxs ))
( setq flymake-ruff-program-args
' ( "--output-format" "concise" "--exit-zero" "--quiet" "-" ))
( defun my-python-ruff-setup ()
( add-hook 'flymake-diagnostic-functions #' flymake-ruff--run-checker nil t ))
( add-hook 'python-base-mode-hook #' my-python-ruff-setup )
TRAMP General Settings ( setq enable-remote-dir-locals 't )
( setq tramp-chunksize 4050 )
( setq tramp-verbose 10 )
( setq vc-ignore-dir-regexp
( format "\\(%s\\)\\|\\(%s\\)"
vc-ignore-dir-regexp
tramp-file-name-regexp ))
( defun open-remote-dired ()
"Opens a Dired buffer at the path specified by REMOTE_HOST and REMOTE_PATH environment variables."
( interactive )
( condition-case nil
( let (( remote-host ( getenv "REMOTE_HOST" ))
( remote-path ( getenv "REMOTE_PATH" )))
( if ( and remote-host remote-path )
( progn
( message "Attempting to open remote directory..." )
( dired ( concat "/ssh:" remote-host ":" remote-path ))
( message "Remote directory opened." ))
( message
"Error: REMOTE_HOST or REMOTE_PATH environment variables not set." )))
( error
( message
"Error: Unable to open remote directory. Check your connection and environment variables." ))))
;; Bind the function to M-R globally
( global-set-key ( kbd "M-R" ) 'open-remote-dired )
Local PATH ( defun my-setup-tramp-path ()
( let (( local-path ( cdr ( assoc 'my-project-specific-path dir-local-variables-alist ))))
( when local-path
( add-to-list 'tramp-remote-path
( concat "/sshx:your_username@remote_host:" local-path )))))
( add-hook 'hack-dir-local-variables-hook #' my-setup-tramp-path )
Eglot in Tramp ( defun my/eglot-project-function ( dir )
"Custom project function for Eglot that avoids using Projectile for remote directories."
( if ( file-remote-p dir )
( cons 'transient dir ) ; Treat remote dirs as transient projects
( project-try-vc dir ))) ; Use VC-based detection for local dirs
( setq eglot-project-function #' my/eglot-project-function )
Lock files Disable lock files in TRAMP
( defun my-tramp-file-name-handler ( operation &rest args )
"Disable file locks for TRAMP files."
( if ( eq operation 'vc-registered )
nil
( let (( file-name-handler-alist
( remove ( cons "\\`/\\(ssh\\|scp\\|ftp\\):" 'my-tramp-file-name-handler )
file-name-handler-alist )))
( apply operation args ))))
( add-to-list 'file-name-handler-alist
' ( "\\`/\\(ssh\\|scp\\|ftp\\):" . my-tramp-file-name-handler ))
Direnv in Tramp Code exists in an unmerged branch .
( defcustom my-direnv-enabled-hosts nil
"List of remote hosts to use Direnv on.
Each host must have `direnv' executable accessible in the default
environment."
:type ' ( repeat string )
:group 'my )
( defun tramp-sh-handle-start-file-process@my-direnv ( args )
"Enable Direnv for hosts in `my-direnv-enabled-hosts'."
( with-parsed-tramp-file-name
( expand-file-name default-directory ) nil
( if ( member host my-direnv-enabled-hosts )
( progn ( pcase-let (( ` ( , name , buffer , program . , args ) args ))
` ( , name , buffer "direnv" "exec" , localname , program ,@ args )) ( debug ))
args )))
( with-eval-after-load "tramp-sh"
( advice-add
'tramp-sh-handle-start-file-process
:filter-args #' tramp-sh-handle-start-file-process@my-direnv ))
Sudo editing shortcut ( defun edit-current-file-as-root ()
"Reopen the current file as root."
( interactive )
( let (( file ( buffer-file-name )))
( if ( not ( file-writable-p file ))
( find-file ( concat "/sudo::" file ))
( message "File is already writable" ))))
Vertico improvements I get False Positive completions using coder
with TRAMP ssh completions. This filter removes them.
( defun my/tramp-ssh-completion-filter ( completions )
( cl-remove-if-not
( lambda ( completion )
( not ( or ( string-match-p "^coder-vscode--:$" completion ) ; Exclude "coder-vscode--:"
( string-match-p "^coder-vscode\\.coder\\.infiniaml\\.net--:$" completion ) ; Exclude "coder-vscode.coder.infiniaml.net--:"
( string-match-p "^coder\\.:$" completion )))) ; Exclude "coder.:"
completions ))
( advice-add 'tramp-completion-handle-file-name-all-completions
:filter-return #' my/tramp-ssh-completion-filter )
Dir-Locals ( add-to-list
'auto-mode-alist
' ( "\\.dir-locals\\(?:-2\\)?\\.el\\'" . emacs-lisp-mode ))
This really improves use with local variables. You get a highly visible warning when a dir-local file is unreadable or misconfigured.
( defun my/dir-local-error-warning ( orig-fun &rest args )
"Advice to display a warning on directory local variable read errors."
( condition-case err
( apply orig-fun args )
( error
( display-warning
'dir-locals
( format "Error reading .dir-locals.el: %s"
( error-message-string err ))
:error ))))
( advice-add
'hack-dir-local-variables
:around #' my/dir-local-error-warning )
Application Development Docker ( use-package
dockerfile-mode
:config ( setq dockerfile-mode-command "docker" ))
( use-package docker
:ensure t
:bind ( "C-c d" . docker ))
Heroku ( use-package
heroku
:ensure t
:defer t
:bind ( :map global-map ( "C-c h" . heroku-command-map ))
:commands
( heroku-list
heroku-logs
heroku-restart
heroku-dynos-kill
heroku-run-detached
heroku-run-python
heroku-run-bash )
:init
( define-prefix-command 'heroku-command-map )
( define-key heroku-command-map ( kbd "l" ) 'heroku-list )
( define-key heroku-command-map ( kbd "s" ) 'heroku-sql )
:config
;; Function to get app name, falling back to the default
( defun my/heroku-get-app-name ()
( or ( and ( boundp 'heroku-app-name ) heroku-app-name )
( getenv "HEROKU_APP_NAME" )
heroku-default-app ))
;; Advice to use our custom function
( advice-add 'heroku-get-app-name :override #' my/heroku-get-app-name )
:custom
( heroku-timestamp-regex
"^[[:digit:]]\\{4\\}-[[:digit:]]\\{2\\}-[[:digit:]]\\{2\\}T[[:digit:]:\+\.]*" )
( heroku-app-name-re "^[[:alnum:]-]*" )
( heroku-region-re "\(\\([[:alnum:]]*\\)\)" )
( heroku-collab-re "[[:alnum:]]*@[[:alnum:]\.-_]*" ))
Org Mode ( defun org-src-format-and-save ()
( interactive )
( when ( provided-mode-derived-p major-mode 'python-base-mode )
( if ( and ( boundp 'apheleia-formatter )
( seq-contains-p apheleia-formatter 'ruff ))
( apheleia-format-buffer 'ruff )
( apheleia-format-buffer 'black )))
( when ( eq major-mode 'emacs-lisp-mode )
( elisp-autofmt-buffer ))
( sit-for 0.100 )
( org-edit-src-save ))
Org Mode Settings ( use-package
org
:defer t
:hook ( org-mode-hook . visual-line-mode )
:hook ( org-babel-after-execute-hook . org-display-inline-images )
:hook ( org-babel-after-execute-hook . append )
:hook
( org-mode
.
( lambda ()
( remove-hook
'completion-at-point-functions #' pcomplete-completions-at-point
t )
( remove-hook
'completion-at-point-functions #' ispell-completion-at-point
t )))
:bind
( :map
org-mode-map
( "C-'" . nil )
( "C-c C-c" . org-ctrl-c-ctrl-c )
( "C-M-<return>" . my/org-babel-execute-and-next )
( "C-c <" . nil )
( "M-g o" . consult-org-heading )
:map
org-src-mode-map
( "C-x C-s" . org-src-format-and-save ))
:config
( setq org-format-latex-options
( plist-put org-format-latex-options :scale 2.0 ))
( setq org-latex-create-formula-image-program 'dvisvgm )
( defun my/org-babel-execute-and-next ()
( interactive )
( org-babel-execute-src-block )
( org-babel-next-src-block ))
( org-babel-do-load-languages
'org-babel-load-languages
' (( emacs-lisp . t )
( sqlite . t )
( shell . t )
( jq . t )
( jupyter . nil )
( python . t )))
; Needed to make org-babel-jupyter work, want to refresh the kernels to see what is visible in venv
( defun my/load-org-jupyter ()
( org-babel-do-load-languages
'org-babel-load-languages ' (( jupyter . t )))
( org-babel-jupyter-aliases-from-kernelspecs 'refresh ))
( visual-line-mode t )
; Org-babel stuff
( setf ( alist-get "bash" org-src-lang-modes nil nil #' equal )
'bash-ts )
( setq org-log-done t )
;; adding this does some weird stuff in colors
( setq org-startup-indented nil )
;; fontify code in code blocks
( setq org-src-fontify-natively t )
( setq org-confirm-babel-evaluate nil ) ;don't prompt me to confirm everytime I want to evaluate a block
( setq org-src-preserve-indentation t )
( defun my-update-direnv-in-org-src ()
"Update direnv environment variables for org-src buffers."
( when ( and ( bound-and-true-p direnv-mode )
( eq major-mode 'org-mode ))
( let (( org-file-path ( buffer-file-name ( buffer-base-buffer ))))
( when org-file-path
( direnv-update-environment org-file-path )))))
( add-hook 'org-src-mode-hook 'my-update-direnv-in-org-src ))
Org Babel ( setq ob-ipython-command "jupyter" )
( use-package
plantuml-mode
:after org
:config
( setq plantuml-default-exec-mode 'executable )
( setq org-plantuml-exec-mode 'plantuml )
( add-to-list 'auto-mode-alist ' ( "\\.plantuml\\'" . plantuml-mode )))
( use-package ob-napkin )
( use-package mermaid-mode )
( use-package ob-mermaid )
( use-package htmlize :after org )
Colors in Org ( use-package
org-bullets
:after org
:hook ( org-mode-hook . org-bullets-mode ))
;;nil means to wrap lines in org mode
( setq org-startup-truncated nil )
Org Modern I thought I liked this at first, but now I’m going to disable it because of some annoying text interactions.
(use-package org-modern :after org :config (global-org-modern-mode))
(use-package
org-modern-indent
:after org
:straight
'(org-modern-indent
:type git
:host github
:repo "jdtsmith/org-modern-indent")
:config
(setq org-startup-indented t)
(add-hook 'org-mode-hook #'org-modern-indent-mode 90))
Org Roam I’m not currently using Org Roam, but maybe some day I’d like to.
( use-package org-roam
:ensure t
:custom
( org-roam-directory ( file-truename "~/Dropbox/org-roam/" )) ;; Define org-roam-directory first
:bind
(( "C-c n l" . org-roam-buffer-toggle )
( "C-c n f" . org-roam-node-find )
( "C-c n g" . org-roam-graph )
( "C-c n i" . org-roam-node-insert )
( "C-c n c" . org-roam-capture )
;; Dailies
( "C-c n j" . org-roam-dailies-capture-today ))
:init
;; Delay setting org-id-locations-file until after org-roam is loaded
( with-eval-after-load 'org-roam
( setq org-id-locations-file ( expand-file-name "org-id-locations" org-roam-directory )))
:config
;; If using a vertical completion framework, customize completion interface
( setq org-roam-node-display-template
( concat
"${title:*} " ( propertize "${tags:10}" 'face 'org-tag )))
;; Automatically sync the database
( org-roam-db-autosync-mode )
;; Check and update org-id-locations file if it doesn't exist
( unless ( file-exists-p org-id-locations-file )
( message "Org-id-locations file not found. Generating a new one..." )
( org-roam-update-org-id-locations ))
;; If using org-roam-protocol
( require 'org-roam-protocol ))
Jupyter ZMQ ( use-package
zmq
:straight ' ( zmq :host github :repo "nnicandro/emacs-zmq" )
:init
; macro to wrap loading
( defmacro safe-wrap ( fn &rest clean-up )
` ( unwind-protect
( let ( retval )
( condition-case ex
( setq retval
( progn
, fn ))
( 'error
( message ( format "Caught exception: [%s]" ex ))
( setq retval ( cons 'exception ( list ex )))))
retval )
,@ clean-up ))
( defun fix-zmq-file-naming ()
"copy .so to .dylib so that we can proceed with installing zmq"
( let* (( tag ( concat "tags/" zmq-emacs-version ))
( api-url
"https://api.github.com/repos/nnicandro/emacs-zmq/" )
( repo-url "https://github.com/nnicandro/emacs-zmq/" )
( release-url ( concat api-url "releases/" ))
( info
( zmq--download-url
( concat release-url tag ) ( require 'json )
( let (( json-object-type 'plist ))
( ignore-errors
( json-read )))))
( tag-name
( or ( plist-get info :tag_name ) ( throw 'failure nil )))
( ezmq-sys ( concat "emacs-zmq-" ( zmq--system-configuration )))
( assets
( cl-remove-if-not
( lambda ( x ) ( string-prefix-p ezmq-sys x ))
( mapcar
( lambda ( x ) ( plist-get x :name ))
( append ( plist-get info :assets ) nil )))))
( when assets
( let (( default-directory
( file-name-directory ( locate-library "zmq" ))))
;; We have a signature file and a tar.gz file for each binary so the
;; minimum number of files is two.
( if ( > ( length assets ) 2 )
( error "TODO More than one file found" )
( let* (( tgz-file
( cl-find-if
( lambda ( x ) ( string-suffix-p "tar.gz" x )) assets ))
( lib
( expand-file-name ( concat
"emacs-zmq" module-file-suffix )
( expand-file-name
( file-name-sans-extension
( file-name-sans-extension
tgz-file ))))))
( let* (( source-file
( concat ( file-name-sans-extension lib ) ".so" )))
( when ( not ( f-exists? lib ))
( print ( format "Copy from %s to %s" source-file lib ))
( copy-file source-file lib )))
t ))))))
( let ( original-noninteractive-value
noninteractive )
;; this is a hack so i don't have to ask about downloading the compatible binary
( setq noninteractive t )
( safe-wrap
( condition-case nil
( require 'zmq )
( error
( fix-zmq-file-naming )
( require 'zmq )))) ;; set the variable back to its original value
( setq noninteractive original-noninteractive-value )))
ob-jupyter ( use-package
jupyter
:demand t
:custom ( jupyter-repl-echo-eval-p t )
:config
( defun my-org-babel-jupyter-use-python-ts-mode ( &rest _ )
"Set python-ts-mode for jupyter-python in org-src-lang-modes."
( setf ( alist-get "jupyter-python" org-src-lang-modes
nil
nil
#' equal )
"python-ts" ))
( advice-add
'org-babel-jupyter-make-local-aliases
:after #' my-org-babel-jupyter-use-python-ts-mode ))
Org Markdown This is really only used in the publishing script of my emacs config, I don’t need to load this on every startup.
ox-md (use-package ox-jekyll-md :init (setq org-jekyll-md-include-yaml-front-matter nil))
org-jekyll-lite This is really only used in the publishing script of my emacs config, I don’t need to load this on every startup.
(use-package
ox-jekyll-lite
:after org
:straight
'(ox-jekyll-lite
:type git
:host github
:repo "jsigman/ox-jekyll-lite"))
Org Appear ( use-package
org-appear
:straight
' ( org-appear :type git :host github :repo "awth13/org-appear" )
:after org
:hook ( org-mode-hook . org-appear-mode ))
Org Ref (use-package request)
(use-package
org-ref
:after org
:config
(setq org-latex-pdf-process
(list "latexmk -shell-escape -bibtex -f -pdf %f")))
Magit and Version Control Magit ( use-package
magit
:demand t
:init
( setq magit-delete-by-moving-to-trash nil )
:bind
( :map
magit-mode-map
( "C-x g" . nil )
( "M-G" . nil )
( "M-0" . nil )
( "M-1" . nil )
( "M-2" . nil )
( "M-3" . nil ))
:bind ( :map global-map ( "M-G" . magit-status ))
:config ( use-package transient ))
Forge Haven’t yet found a great use for this.
( use-package
forge
:after magit
:config
; This is so we can access forge information
( setq auth-sources ' ( "~/.authinfo.gpg" ))
( setq epa-pinentry-mode 'loopback )
( setq forge-status-buffer-default-topic-filters
( forge--topics-spec
:type 'pullreq
:active t
:state 'open
:order 'newest )))
( use-package ghub :defer t )
Magit Todos mode This is currently broken for me.
(use-package
magit-todos
:straight t
:after magit
:hook (magit-mode . magit-todos-mode)
:config
(setq magit-todos-scanner 'magit-todos--scan-with-rg)
(setq magit-todos-keywords '("TODO" "FIXME" "BUG" "HACK"))
(setq magit-todos-ignore-case t)
(setq magit-todos-search-regexp "\\b(TODO|FIXME|BUG|HACK)\\b")
(setq magit-todos-keyword-suffix "")
(magit-todos-mode 1))
Homegrown Magit TODOs ( require 'magit )
( defface magit-todos-line-number
' (( t :inherit magit-filename :weight bold ))
"Face for line numbers in Magit-Todos."
:group 'magit-faces )
( defvar magit-todos-debug nil
"Enable debug messages for magit-todos." )
( defun magit-todos-debug-message ( format-string &rest args )
"Print a debug message if `magit-todos-debug' is non-nil."
( when magit-todos-debug
( apply #' message
( concat "magit-todos debug: " format-string )
args )))
( defun magit-todos-jump-to-file ( file line-number )
"Jump to FILE at LINE-NUMBER."
( magit-todos-debug-message "Jumping to %s:%s" file line-number )
( let (( full-path ( expand-file-name file ( magit-toplevel ))))
( if ( file-exists-p full-path )
( progn
( find-file full-path )
( goto-char ( point-min ))
( forward-line ( 1- line-number )))
( message "File not found: %s" full-path ))))
( defun magit-todos-jump-to-todo ()
"Jump to the TODO at point."
( interactive )
( let (( file ( get-text-property ( point ) 'magit-todos-file ))
( line-number ( get-text-property ( point ) 'magit-todos-line )))
( magit-todos-debug-message
"magit-todos-jump-to-todo called with %s:%s"
file line-number )
( if ( and file line-number )
( magit-todos-jump-to-file file line-number )
( magit-todos-debug-message
"No file or line number found at point" ))))
( defun magit-insert-todos ()
"Insert a section for TODOs found in comments."
( magit-todos-debug-message "Inserting TODOs section" )
( magit-insert-section
( todos ) ( magit-insert-heading "TODOs" )
( let* (( root ( magit-toplevel ))
( todos-output
( shell-command-to-string
( concat "rg --vimgrep '(#|//|;|<!--)\\s*TODO' " root )))
( todos-list ' ()))
( magit-todos-debug-message "Found %d lines of TODOs"
( length
( split-string todos-output "\n" t )))
;; Parse and collect todos
( dolist ( line ( split-string todos-output "\n" t ))
( when ( string-match
"\\([^:]+\\):\\([0-9]+\\):\\([0-9]+\\):\\(.*\\)" line )
( let (( file ( file-relative-name ( match-string 1 line ) root ))
( line-number ( string-to-number ( match-string 2 line )))
( content ( match-string 4 line )))
( push ( list file line-number content ) todos-list ))))
;; Sort todos by filename and line number
( setq todos-list
( sort todos-list
( lambda ( a b )
( or ( string< ( car a ) ( car b ))
( and ( string= ( car a ) ( car b ))
( < ( cadr a ) ( cadr b )))))))
( magit-todos-debug-message "Sorted %d TODOs" ( length todos-list ))
;; Insert sorted todos
( dolist ( todo todos-list )
( let* (( file ( nth 0 todo ))
( line-number ( nth 1 todo ))
( content ( nth 2 todo ))
( todo-string
( concat
( propertize file 'face 'magit-filename ) ":"
( propertize ( number-to-string line-number )
'face 'magit-todos-line-number )
": " content )))
( magit-insert-section
( todo )
( insert
( propertize todo-string
'magit-todos-file
file
'magit-todos-line
line-number
'keymap
( let (( map ( make-sparse-keymap )))
( define-key
map ( kbd "RET" ) 'magit-todos-jump-to-todo )
( define-key
map [mouse-1] 'magit-todos-jump-to-todo )
map )
'mouse-face
'highlight
'help-echo
"mouse-1 or RET: visit this TODO" ))
( insert "\n" ) ( magit-insert-heading )))))))
( define-minor-mode magit-todos-mode
"Display TODOs in Magit status using rg."
:lighter " Magit-Todos"
:global
t
( if magit-todos-mode
( magit-todos-mode-enable )
( magit-todos-mode-disable )))
( defun magit-todos-mode-enable ()
"Enable Magit-Todos integration."
( magit-todos-debug-message "Enabling magit-todos-mode" )
( add-hook 'magit-status-sections-hook 'magit-insert-todos t ))
( defun magit-todos-mode-disable ()
"Disable Magit-Todos integration."
( magit-todos-debug-message "Disabling magit-todos-mode" )
( remove-hook 'magit-status-sections-hook 'magit-insert-todos ))
( provide 'magit-todos )
;;; magit-todos.el ends here
;; Automatically enable the mode
( magit-todos-mode 1 )
Magit coloring git-gutter
( use-package
git-gutter+
:config ( global-git-gutter+-mode t )
( setq git-gutter+-modified-sign " " ) ;; two space
( setq git-gutter+-added-sign "+" ) ;; multiple character is OK
( setq git-gutter+-deleted-sign "-" )
( set-face-foreground 'git-gutter+-modified "magenta" )
( set-face-foreground 'git-gutter+-added "dark green" )
( set-face-foreground 'git-gutter+-deleted "red" ))
; add this so that git gutter plus does not screw up tramp sessions
; it disables GGP during tramp
( defun git-gutter+-refresh ()
( git-gutter+-clear )
( when ( not ( file-remote-p ( buffer-file-name )))
( let (( file ( buffer-file-name )))
( when ( and file ( file-exists-p file ))
( if ( file-remote-p file )
( let* (( repo-root ( git-gutter+-root-directory file ))
( default-directory
( git-gutter+-remote-default-directory
repo-root file )))
( git-gutter+-process-diff
( git-gutter+-remote-file-path repo-root file )))
( git-gutter+-process-diff
( git-gutter+-local-file-path file )))))))
diff-hl
(use-package
diff-hl
:config (global-diff-hl-mode)
(add-hook 'magit-pre-refresh-hook 'diff-hl-magit-pre-refresh)
(add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh))
Ediff ( use-package
ediff
:defer t
:init ( setq ediff-split-window-function 'split-window-horizontally )
( setq ediff-window-setup-function
'ediff-setup-windows-plain )
( setq ediff-diff-options "-w" ))
Treemacs Treemacs base packages ( use-package
treemacs
:bind (( "M-P" . treemacs ))
:config ( setq treemacs-persist-file "treemacs-persist" ))
Addons (use-package treemacs-projectile)
( use-package treemacs-icons-dired :config ( treemacs-icons-dired-mode ))
Appearance ;; Treat all themes as safe; no query before use.
( setf custom-safe-themes 't )
( setq frame-title-format nil )
Emacs Theme Leuven Leuven is always a nice option, beautiful with Org Mode.
(use-package leuven-theme
:config
(load-theme 'leuven-dark t))
Trying this theme out for a little bit.
(use-package
catppuccin-theme
:config
(load-theme 'catppuccin :no-confirm)
(setq catppuccin-flavor 'frappe)
(catppuccin-reload))
ef-day I’m always changing my theme, but I tend to like light themes with yellow-ish backgrounds. Almost like handwriting with a yellow memo pad.
( use-package ef-themes :config ( load-theme 'ef-day t ))
Gruvbox-dark (use-package
gruvbox-theme
:ensure t
:config (load-theme 'gruvbox-dark-hard :no-confirm)
;; :after (centered-window-mode) :hook (centered-window-mode . cwm-update-fringe-background)
)
Nano (use-package
nano
:straight
(nano-emacs :type git :host github :repo "rougier/nano-emacs")
:config (require 'nano))
Colors in Dired (use-package rainbow-mode :hook (LaTeX-mode . rainbow-mode))
(use-package dired-hacks :hook (emacs-lisp-mode . rainbow-mode))
Catch if running centered-window-mode This may be needed to set the fringe to the new background color. Run it when startup is finished.
( add-hook 'emacs-startup-hook
( lambda ()
( when ( fboundp 'cwm-update-fringe-background )
( cwm-update-fringe-background ))))
Fonts ;; Font settings
( if ( eq system-type 'darwin )
( if ( and ( display-graphic-p ) ( > ( x-display-pixel-width ) 1440 ))
;; Set default font larger if on a big screen
( set-face-font 'default "roboto mono-15" )
;; (set-face-font 'default "arial-15")
;; else
( set-face-font 'default "roboto mono-14" )
;; (set-fontset-font "fontset-default" "Menlo 12")
)
;; else
( if ( not ( eq window-system nil ))
( if ( and ( display-graphic-p ) ( > ( x-display-pixel-width ) 1440 ))
;; Set default font larger if on a big screen
( set-face-font 'default "roboto mono-12" )
;; else
( set-face-font 'default "roboto mono-12" )
;; (set-fontset-font "fontset-default" "Menlo 12")
)
;; else
))
Modeline Simple Modeline ( use-package
simple-modeline
:init
( setq simple-modeline-segments
' (( simple-modeline-segment-modified
simple-modeline-segment-buffer-name
simple-modeline-segment-position )
(
;; simple-modeline-segment-minor-modes
simple-modeline-segment-input-method
simple-modeline-segment-eol
simple-modeline-segment-encoding
simple-modeline-segment-vc
simple-modeline-segment-misc-info
simple-modeline-segment-process
simple-modeline-segment-major-mode )))
:hook ( after-init . simple-modeline-mode ))
Telephone Line (use-package
telephone-line
:config
(setq telephone-line-lhs
'((evil . (telephone-line-evil-tag-segment))
(accent
.
(telephone-line-vc-segment
telephone-line-erc-modified-channels-segment
telephone-line-process-segment))
(nil
.
(telephone-line-minor-mode-segment
telephone-line-buffer-segment))))
(setq telephone-line-rhs
'((nil . (telephone-line-misc-info-segment))
(accent . (telephone-line-major-mode-segment))
(evil . (telephone-line-airline-position-segment))))
(telephone-line-mode t))
Doom Modeline Bugfix: Requires installing shrink path manually on linux. Not sure why.
(use-package
shrink-path
:straight
'(shrink-path :type git :host github :repo "zbelial/shrink-path.el"))
(use-package
doom-modeline
:config
(doom-modeline-mode 1)
(setq doom-modeline-unicode-fallback t)
(defun doom-modeline--project-root ()
"Get the path to the project root.
Return nil if no project was found."
(or doom-modeline--project-root
(setq doom-modeline--project-root
(cond
((and (memq
doom-modeline-project-detection '(auto ffip))
(fboundp 'ffip-project-root))
(let ((inhibit-message t))
(ffip-project-root)))
((and (memq
doom-modeline-project-detection
'(auto projectile))
(bound-and-true-p projectile-mode))
(projectile-project-root))
((and (memq
doom-modeline-project-detection '(auto project))
(fboundp 'project-current))
(when-let ((project (project-current)))
(expand-file-name
(if (fboundp 'project-root)
(project-root project)
(car
(with-no-warnings
(project-roots project))))))))))))
Awesome Tray (use-package
awesome-tray
:straight
'(awesome-tray
:type git
:host github
:repo "manateelazycat/awesome-tray")
:init
(setq awesome-tray-active-modules
'("buffer-name" "mode-name" "belong"))
:config (awesome-tray-mode 1) awesome-tray-active-modules)
Icons All the icons ( use-package all-the-icons )
( use-package
all-the-icons-ibuffer
:hook ( ibuffer-mode . all-the-icons-ibuffer-mode ))
;; I don't think I like buffer expose after all
( use-package
all-the-icons-completion
:config ( all-the-icons-completion-mode )
:hook ( marginalia-mode . all-the-icons-completion-marginalia-setup ))
SVG Support ; Re-enable with SVG support
( use-package
svg-lib
:straight ' ( svg-lib :host github :repo "emacs-straight/svg-lib" ))
( use-package
kind-icon
:straight ' ( kind-icon :host github :repo "jdtsmith/kind-icon" )
:ensure t
:after corfu
:config
; On my linux machine, I need to use smaller icons like this due to the 4k display
( when ( eq system-type 'gnu/linux )
( setq kind-icon-default-style
' ( :padding
-1
:stroke 0
:margin 0
:radius 0
:height 0.5
:scale 1.0 )))
:custom
( kind-icon-default-face 'corfu-default ) ; to compute blended backgrounds correctly
:config ( add-to-list 'corfu-margin-formatters #' kind-icon-margin-formatter ))
Fuzzy ( use-package fuzzy )
( use-package fuzzy-match )
Free keys ( use-package free-keys )
( use-package restart-emacs )
; ---- Auto Revert Modes ----- ;
Auto Revert on Images ( autoload 'eimp-mode "eimp" "Emacs Image Manipulation Package." t )
( add-hook 'image-mode-hook 'auto-revert-mode )
; --- CSV --- ;
CSV Mode ( use-package
csv-mode
:straight
' ( csv-mode :type git :host github :repo "emacsmirror/csv-mode" ))
Explain Pause Mode ( use-package
explain-pause-mode
:straight
' ( explain-pause-mode
:type git
:host github
:repo "lastquestion/explain-pause-mode" ))
Helpful ;; use helpful instead of the normal help buffers
;; Note that the built-in `describe-function' includes both functions
;; and macros. `helpful-function' is functions only, so we provide
;; `helpful-callable' as a drop-in replacement.
(use-package
helpful
:defer t
:bind
("C-h f" . helpful-callable)
("C-h v" . helpful-variable)
("C-h k" . helpful-key))
Dimmer (use-package
dimmer
:config
(dimmer-configure-which-key)
(dimmer-configure-org)
(dimmer-configure-posframe)
(dimmer-configure-magit)
(dimmer-configure-hydra)
(setq dimmer-fraction 0.15)
(dimmer-mode t))
Volatile Highlights ( use-package
volatile-highlights
:config ( volatile-highlights-mode t ))
Highlight todos ( use-package hl-todo :init ( global-hl-todo-mode ))
Tree-Sitter Tree-Sitter mode This is the old way of using tree-sitter
in emacs. In emacs 29, this moved to treesit.
(use-package
tree-sitter
:config (global-tree-sitter-mode t)
:hook ((tree-sitter-after-on-hook . tree-sitter-hl-mode) (python-mode . tree-sitter-hl-mode)))
(use-package tree-sitter-langs)
Combobulate Combobulate seems like a cool idea, but I’ve found it to be more frustrating than helpful in python.
( use-package
combobulate
:straight ' ( combobulate :type git :host github :repo "mickeynp/combobulate" )
;; You can manually enable Combobulate with `M-x
;; combobulate-mode'.
:hook
(( python-ts-mode . combobulate-mode )
( js-ts-mode . combobulate-mode )
( css-ts-mode . combobulate-mode )
( yaml-ts-mode . combobulate-mode )
( typescript-ts-mode . combobulate-mode )
( tsx-ts-mode . combobulate-mode ))
;; Amend this to the directory where you keep Combobulate's source
;; code.
:bind
( :map
combobulate-key-map (( "C-;" . combobulate-avy-jump ) ( "M-k" . nil ))))
Treesit ( defun copy-different-key-bindings ( source-map dest-map )
"Copy key bindings from SOURCE-MAP to DEST-MAP, but only where they differ."
( map-keymap
( lambda ( binding value )
( unless ( eq value ( lookup-key dest-map ( vector binding )))
( define-key dest-map ( vector binding ) value )))
source-map ))
( defun merge-mode-hooks ( source-hook dest-hook )
"Merge hooks from SOURCE-HOOK to DEST-HOOK without duplicates."
( dolist ( hook ( symbol-value source-hook ))
( unless ( memq hook ( symbol-value dest-hook ))
( add-hook dest-hook hook ))))
Treesit Auto ( use-package
treesit-auto
:straight
' ( treesit-auto :type git :host github :repo "renzmann/treesit-auto" )
:demand t
:init
( setq treesit-auto-install t )
( setq treesit-auto-langs
' ( awk
bash
bibtex
c
cmake
commonlisp
cpp
css
dockerfile
html
java
javascript
json
make
markdown
proto
python
ruby
toml
typescript
yaml ))
:config ( global-treesit-auto-mode ))
Merge mode hooks in treesit I want the same hooks to be used in treesit as in the original mode.
;; Merge hooks for relevant modes
( merge-mode-hooks 'python-mode-hook 'python-ts-mode-hook )
( merge-mode-hooks 'yaml-mode-hook 'yaml-ts-mode-hook )
( merge-mode-hooks 'html-mode-hook 'html-ts-mode-hook )
( merge-mode-hooks 'json-mode-hook 'json-ts-mode-hook )
;; (merge-mode-hooks 'js-mode-hook 'js-ts-mode-hook)
;; (merge-mode-hooks 'markdown-mode-hook 'markdown-ts-mode-hook)
( merge-mode-hooks 'sh-mode-hook 'bash-ts-mode-hook )
;; Copy different key bindings
( copy-different-key-bindings python-mode-map python-ts-mode-map )
( copy-different-key-bindings yaml-mode-map yaml-ts-mode-map )
( copy-different-key-bindings html-mode-map html-ts-mode-map )
( copy-different-key-bindings json-mode-map json-ts-mode-map )
;; (copy-different-key-bindings markdown-mode-map markdown-ts-mode-map)
( copy-different-key-bindings sh-mode-map bash-ts-mode-map )