iwahbe / emacs-config

This is my Emacs initialization setup

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Overview

This is my emacs configuration file. It should work for all modern versions, and is tested on version 26, 27 and 28. I make heavy use of use-package and straight.el.

Getting Started

This file is expected to reside in ~/.emacs.d so start with cp emacs-config ~/.emacs.d. The cp into the file and type make tangle; make hooks. Then emacs will work as expected.

Dependencies

  • For the basic install
    • git
    • make
    • For vterm, libtool must be installed.
      • On MacOS, glibtool is needed.
    • vterm might hang emacs on a bad install
  • For other packages
    • some packages will be disabled until their relevant components are installed.
    • These packages are discoverable by searching for :if (executable-find "dependencie")
  • spell checking
    • brew install hunspell on MacOS
    • pacman -S hunspell on Arch
      • it needs dictionaries -> run make dict to install all dictionaries

Preliminary

This loads frameworks necessary for much of the following settings. This section should be used only for packages that must be loaded before everything else.

Path Control

We want emacs to work like the terminal version for maximum consistancy. This is where brew puts its formula, which is my most common command line tool location.

(defun add-to-path (path-string &optional appendp)
  "Takes a path string, and if valid, expands it, adding to `exec-path' and `PATH'"
  (let* ((new-path (expand-file-name path-string))
		 (env-path (getenv "PATH")))
    (if (file-accessible-directory-p path-string)
		(progn
		(add-to-list 'exec-path new-path appendp)
      (if (not (member new-path (split-string env-path ":")))
		  (if appendp
			  (setenv "PATH" (concat env-path ":" new-path))
			(setenv "PATH" (concat new-path ":" env-path))))))))

(add-to-path "~/.cargo/bin") ; rust programs
(add-to-path "/usr/local/bin")
(add-to-path "/usr/local/sbin")
(add-to-path "~/.local/bin")

Custom: Not in init.el

We set the custom package to it’s own file, instead of polluting our init.el file. Also, init.el is reset on tangle away on tangle.

(setq custom-file (expand-file-name (concat user-emacs-directory "custom.el")))
(unless (file-exists-p custom-file)
  (write-region "" "" custom-file) (load custom-file))

Package Manager: Straight

straight.el is a package manager.

  • A benefit is that packages installed are defined entirely by this file.
  • unfortunately, straight.el is normally slower then package.el, because it checks for changes in source material. This can be overcome by setting check-for-modifications to be based on file saves, instead of actual changes.
(defvar bootstrap-version)
(setq straight-check-for-modifications '(check-on-save find-when-checking))
(let ((bootstrap-file
       (expand-file-name
	"straight/repos/straight.el/bootstrap.el" user-emacs-directory))
      (bootstrap-version 5))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
        (url-retrieve-synchronously
	 "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))
;;ensures both that use-package is installed and works with straight.el
(straight-use-package 'use-package)
(defun find-or-warn (exec warn &optional missed)
  "Attempt to find a executable.  If not found then warn the user.
`EXEC' is either the executable to find or a list of executables.
`WARN' is the consequence of not finding the executable.
`MISSED' is a list of already missed options"
  (if (not (listp exec))
	  (find-or-warn (list exec) warn missed)
	(let ((f (executable-find (car exec))))
	  (if f f
		(let ((missed (cons (car exec) missed)))
		  (if (cdr exec)
			  (find-or-warn (cdr exec) warn missed)
			(progn (message "Could not find '%s' => %s" missed warn) ())))))))

Tangle init.org on save

We want init.org to have special behavior, specifically we want init.org to tangle to a .el file then byte compile for loading speed. The part that performed the byte-compile is currently disabled as it caused problems with use-package.

(eval-and-compile
  (defvar do-byte-compile nil "If this file should be byte-compiled after tangled"))

(defun tangle-init-call ()
  "Tangles this and only this file on save into init.el"
  (when (equal (buffer-file-name)
	       (expand-file-name (concat user-emacs-directory "init.org")))
    ;; Avoid running hooks when tangling.
    (let ((prog-mode-hook nil))
      (org-babel-tangle-file buffer-file-name (concat user-emacs-directory "init.el"))
      (when do-byte-compile (byte-compile-file (concat user-emacs-directory "init.el"))))))

(add-hook 'after-save-hook 'tangle-init-call)

Speed

We make it faster.

(setq gc-cons-threshold 100000000)
(setq read-process-output-max (* 1024 1024)) ;; 1mb

Cosmetics

Purely cosmetic changes. Nothing else.

Minimize Graphics

(use-package emacs
  :init
  (if (fboundp 'menu-bar-mode) (menu-bar-mode -1))
  (if (fboundp 'tool-bar-mode) (tool-bar-mode -1))
  (if (fboundp 'scroll-bar-mode) (scroll-bar-mode -1))
  (setq inhibit-startup-message t
		initial-scratch-message "")
  (when (featurep 'mac)
	(setq mac-command-modifier 'super
	  mac-option-modifier 'meta
	  mac-pass-command-to-system t))
  (blink-cursor-mode -1)
  (set-fringe-mode 0) ; sets width
  (setq ring-bell-function 'ignore))

Theme and Windowing

(use-package humanoid-themes
  :straight (humanoid-themes :host github :repo "humanoid-colors/emacs-humanoid-themes")
  :when (or window-system (daemonp))
  :config
  (if (boundp 'ns-system-appearance-change-functions)
	  (add-hook 'ns-system-appearance-change-functions
				#'(lambda (appearance)
					(mapc #'disable-theme custom-enabled-themes)
					(pcase appearance
					  ('light (load-theme 'humanoid-light t))
					  ('dark (load-theme 'humanoid-dark t)))))
	(load-theme 'humanoid-dark t)))

FeebleLine

We setup preferences for the mode-line. I turn on battery and time displays, setup smart-mode-line (ssl) and disable the menu-bar. Feebleline is a package to replace the mode-line with the echo area.

(use-package feebleline
  :straight t
  :config
  ;; Note: any function that returns a string is acceptable to feebleline
  (require 'font-lock)
  (setq feebleline-msg-functions
		'((feebleline-line-number         :post "" :fmt "%5s")
		  (feebleline-column-number       :pre ":" :fmt "%-2s")
		  (feebleline-file-directory      :face feebleline-dir-face :post "")
		  (feebleline-file-or-buffer-name :face font-lock-keyword-face :post "")
		  (feebleline-file-modified-star  :face font-lock-warning-face :post "")
		  (feebleline-git-branch          :face font-lock-keyword-face :pre " : ")
		  (feebleline-project-name        :pre "[" :post "] " :align right)
		  ))
  (feebleline-mode +1))

Frame manipulation bindings

Frame bindings

(when (or (daemonp) (window-system))
  (global-set-key (kbd "s-t") 'make-frame)
  (global-set-key (kbd "s-w") 'delete-frame)
  (global-set-key (kbd "s-<return>") 'toggle-frame-fullscreen))

Fira-Code (graphics only)

I use a font called Fira Code. This enables the font as well as the font’s ligatures. The fira code wiki has instructions has advice for setting up on emacs.

The code below was from here

(when (and window-system (x-list-fonts "Fira Code"))
  (add-to-list 'default-frame-alist
			   '(font . "Fira Code"))
  (let ((alist '((33 . ".\\(?:\\(?:==\\|!!\\)\\|[!=]\\)")
				 (35 . ".\\(?:###\\|##\\|_(\\|[#(?[_{]\\)")
				 (36 . ".\\(?:>\\)")
				 (37 . ".\\(?:\\(?:%%\\)\\|%\\)")
				 (38 . ".\\(?:\\(?:&&\\)\\|&\\)")
				 (42 . ".\\(?:\\(?:\\*\\*/\\)\\|\\(?:\\*[*/]\\)\\|[*/>]\\)")
				 (43 . ".\\(?:\\(?:\\+\\+\\)\\|[+>]\\)")
				 (45 . ".\\(?:\\(?:-[>-]\\|<<\\|>>\\)\\|[<>}~-]\\)")
				 (46 . ".\\(?:\\(?:\\.[.<]\\)\\|[.=-]\\)")
				 (47 . ".\\(?:\\(?:\\*\\*\\|//\\|==\\)\\|[*/=>]\\)")
				 (48 . ".\\(?:x[a-zA-Z]\\)")
				 (58 . ".\\(?:::\\|[:=]\\)")
				 (59 . ".\\(?:;;\\|;\\)")
				 (60 . ".\\(?:\\(?:!--\\)\\|\\(?:~~\\|->\\|\\$>\\|\\*>\\|\\+>\\|--\\|<[<=-]\\|=[<=>]\\||>\\)\\|[*$+~/<=>|-]\\)")
				 (61 . ".\\(?:\\(?:/=\\|:=\\|<<\\|=[=>]\\|>>\\)\\|[<=>~]\\)")
				 (62 . ".\\(?:\\(?:=>\\|>[=>-]\\)\\|[=>-]\\)")
				 (63 . ".\\(?:\\(\\?\\?\\)\\|[:=?]\\)")
				 (91 . ".\\(?:]\\)")
				 (92 . ".\\(?:\\(?:\\\\\\\\\\)\\|\\\\\\)")
				 (94 . ".\\(?:=\\)")
				 (119 . ".\\(?:ww\\)")
				 (123 . ".\\(?:-\\)")
				 (124 . ".\\(?:\\(?:|[=|]\\)\\|[=>|]\\)")
				 (126 . ".\\(?:~>\\|~~\\|[>=@~-]\\)")
				 )
			   ))
	(dolist (char-regexp alist)
	  (set-char-table-range composition-function-table (car char-regexp)
							`([,(cdr char-regexp) 0 font-shape-gstring])))))

Universal Alterations

These packages change the normal function of emacs in all major modes.

Universal Variables

Sets up helpful universal variables.

(setq-default
 fill-column 80
 sentence-end "[\\.\\?\\!] +"
 tab-width 4)
(setq enable-recursive-minibuffers t)

Evil: Extensible VI Layer

I’m trying out EVIL, because reasons.

(use-package evil
  :straight t
  :bind
  (("C-\\" . 'evil-toggle))
  :init
  (setq evil-toggle-key (kbd ""))
  :bind (
	 ("C-\\" . 'evil-toggle)
	 :map evil-insert-state-map ("C-:" . 'evil-ex)
	 )
  :config
  (evil-mode -1)
  (evil-define-key nil evil-insert-state-map (kbd "C-d") 'delete-char)
  (defun evil-toggle (&optional prefix-p)
    (interactive "P")
    (if evil-mode
	(if prefix-p
	    (evil-mode 0)
	    (execute-kbd-macro (kbd "<escape>"))
	    )
      (if prefix-p
	  (evil-mode)
	(evil-ex)))))

;;; Evil expects undo-tree
(use-package undo-tree
  :straight t
  :config
  (global-undo-tree-mode 0))

Yasnippet

yasnippet allows snippet expansion Snippets are kept in the folder described by yas-snippet-dirs

(use-package yasnippet
  :straight t
  :after (company) ;due to company-mode
  :init
  (defun setup-yas-company ()
    (defvar company-mode/enable-yas t
      "Enable yasnippet for all backends.")

    (defun company-mode/backend-with-yas (backend)
      "addes company-yasnippet to \"backend\""
      (if (or (not company-mode/enable-yas) (and (listp backend)
						 (member 'company-yasnippet backend)))
	  backend
	(append (if (consp backend) backend (list backend))
		'(:with company-yasnippet))))

    (setq company-backends (mapcar #'company-mode/backend-with-yas
				   company-backends)))
  :config
  (setup-yas-company)
  (yas-global-mode 1)
  (define-key yas-minor-mode-map [(tab)] nil)
  (define-key yas-minor-mode-map (kbd "TAB") nil))

Rename-Current-Buffer Function

Function to rename the file in the current buffer. I found this here.

(defun rename-current-buffer-file ()
  "Renames current buffer and file it is visiting."
  (interactive)
  (let ((name (buffer-name))
        (filename (buffer-file-name)))
    (if (not (and filename (file-exists-p filename)))
        (error "Buffer '%s' is not visiting a file!" name)
      (let ((new-name (read-file-name "New name: " filename)))
        (if (get-buffer new-name)
            (error "A buffer named '%s' already exists!" new-name)
          (rename-file filename new-name 1)
          (rename-buffer new-name)
          (set-visited-file-name new-name)
          (set-buffer-modified-p nil)
          (message "File '%s' successfully renamed to '%s'"
                   name (file-name-nondirectory new-name)))))))

(global-set-key (kbd "C-x C-r") 'rename-current-buffer-file)

FlyCheck

FlyCheck provides dynamic error highlighting from multiple backends. Notes on use:

  • “C-c ! v” provides a diagnosis for flycheck
  • “C-c ! n” & “C-c ! p” navigate to errors
  • “C-c ! l” provides an error list for the current buffer
  • “C-c ! ?” provides information on any syntax checker
(use-package flycheck
  :straight t
  :config
  (global-flycheck-mode +1))

Selectrum

I use helm for fuzzy searching among known options

(use-package company-prescient :straight t)
(use-package selectrum-prescient :straight t)
(use-package selectrum
  :straight t
  :config
  (selectrum-mode +1)
  (selectrum-prescient-mode +1))

Global Key Bindings

We maintain a list of common key-bindings to activate in all modes

(defun current-line-length ()
  (save-excursion
    (beginning-of-line)
    (let ((first-pos (point)))
	  (end-of-line)
	  (- (point) first-pos))))

(defun safe-kill-region (begin end &optional region)
  (interactive "r") (if mark-active (kill-region begin end)))

(global-set-key (kbd "C-w") 'safe-kill-region)
(global-set-key (kbd "C-r") 'scroll-down)
(global-set-key (kbd "C-M-r") 'scroll-other-window-down)
(global-set-key (kbd "C-v") 'scroll-up)
(global-set-key (kbd "C-M-v") 'scroll-other-window)
(global-set-key (kbd "C-l") 'forward-word)
(global-set-key (kbd "C-j") 'backward-word)
(global-set-key (kbd "M-f") 'forward-sentence)
(global-set-key (kbd "M-b") 'backward-sentence)
(global-set-key (kbd "M--") 'undo)
(global-set-key
 (kbd "C-M-n") (lambda (arg) (interactive "P")
				 (with-no-warnings
				   (next-line
					(* 5 (if (equal arg nil) 1 arg))))))
(global-set-key
 (kbd "C-M-p") (lambda (arg) (interactive "P")
				 (with-no-warnings
				   (next-line
					(* -5 (if (equal arg nil) 1 arg))))))
(global-set-key
 (kbd "C-<backspace>") (lambda (arg) (interactive "P")
						 (forward-word)
						 (backward-kill-word
						  (if (equal arg nil) 1 arg))))
(global-set-key (kbd "C-M-v") 'scroll-other-window)
(global-set-key (kbd "C-M-r") 'scroll-other-window-down)
(global-set-key (kbd "s-p") nil) ;used to be print
(global-set-key (kbd "s-o") nil) ;used to be ns-open-file-using-panel
(eval-after-load 'doc-view
  (lambda () (define-key doc-view-mode-map (kbd "C-r") 'image-scroll-down)))
(global-set-key (kbd "M-x") nil) ; used to be pallet

Very Minor Modes

There is a collection of minor modes that trigger after other major modes load.

  • saveplace has reopened files remember the mark position
  • ace-window allows a multi-window mode (vim style)
  • zoom changes window layout on crowded screens to show more of the selected window
  • pending-delete-mode gives autodeletion on the region
  • company-math gives a LaTeX style backend for LaTeX and markdown
  • wc-mode provides a word count in the mode line
  • electric operators provide spacing for prog modes that lack a util to prettify code
  • define-word shows a word definition at point or on lookup
  • helm and helm-company provide fuzzy completion on system searches
  • smartparens gives (semi) smart paired symbol insertion
  • VLF (Very Large Files)
;; Save point position between sessions
(require 'saveplace)
(save-place-mode 1)
(setq save-place-file (expand-file-name ".places" user-emacs-directory))

(use-package ag
  :if (find-or-warn "ag" "The Silver Searcher disabled")
  :straight t)

(use-package expand-region
  :straight t
  :bind (("C-=" . 'er/expand-region)))

(use-package ace-window
  :straight t
  :config (setq aw-scope 'frame)
  :bind (("M-o" . ace-window)))

(use-package zoom
  :straight t
  :config (zoom-mode 1))

;; typing replaces the active region
(pending-delete-mode +1)

;;Word-count gives a total and diffrenced word count in the mode line
(use-package wc-mode
  :straight t
  :hook ((LaTeX-mode ess-mode markdown-mode) . wc-mode)
  :config
  (wc-mode 1))

;;electric-operator adds spaces before and after opperator symbols
(use-package electric-operator
  :straight t
  :hook ((ess-mode) . electric-operator-mode))

(use-package define-word
  :straight t
  :config
  (global-set-key (kbd "C-c d") 'define-word-at-point)
  (global-set-key (kbd "C-c D") 'define-word))

(use-package smartparens
  :straight t
  :config
  (sp-pair "(" ")" :unless '(sp-point-before-word-p))
  (add-hook 'c-mode-hook (lambda () (sp-pair "'" nil :actions :rem)))
  (add-hook 'emacs-lisp-mode-hook (lambda () (sp-pair "'" nil :actions :rem)))
  (smartparens-global-mode +1))

;;Very Large Files
(use-package vlf
  :straight t
  :init
  (require 'vlf-setup) ;not a seperate package, just pre-loading
  (setq vlf-application 'dont-ask))

;; show-paren mode highlights matching parentheses
(setq show-paren-style 'parenthesis)
(show-paren-mode +1)

Git (Magit)

(use-package magit
  :straight t
  :defer (not (daemonp))
  :bind (("C-x g" . magit-status)))

(use-package magit-todos
  :straight t
  :after magit
  :config (magit-todos-mode +1))

(use-package forge
  :straight t
  :after magit)

Company

Company is used for auto-completions. In the spirit of emacs, it can be customized for almost any language, but those customizations are module specific. Here, we only call the main version.

(use-package company
  :straight t
  :init
  (defun add-company-backend (backend &optional add-to-back)
    "Is used to add company backends and include company-yasnippet with each backend"
    ;; (add-to-list 'company-backends `(,symbol-list . '(:with company-yasnippet)))
	(add-to-list 'company-backends (append (if (consp backend) backend (list backend))
					       '(:with company-yasnippet))
				 add-to-back))  
  :config
  (setq company-minimum-prefix-length 1)
  (setq company-idle-delay 0.1) ; this makes company respond in real time (no delay)
  (setq company-dabbrev-downcase 1)
  (setq company-require-match 'never)
  (global-company-mode t)
  
  :bind (:map company-active-map
  	      ("<return>" . nil)
	      ("RET" . nil)
  	      ("C-@" . #'company-complete-selection) ;also means space
	      ("C-SPC" . #'company-complete-selection)
	      ("C-<space>" . #'company-complete-selection)
	      ("M-p" . #'company-select-previous-or-abort)
	      ("M-n" . #'company-select-next-or-abort)))

(use-package company-flx
  :straight t
  :after (company)
  :config
  (company-flx-mode +1))

;;Company-math provides auto-complete for math symbols
(use-package company-math
  :straight t
  :after (company (:any auctex markdown))
  :config
  (add-company-backend 'company-math))

Multiple Cursors (GUI only)

Multiple cursors should be self-explanatory.

(use-package multiple-cursors
  :straight t
  :if (or window-system (daemonp))
  :bind
  (("C->" . mc/mark-next-like-this)
   ("C-<" . mc/mark-previous-like-this)
   ("C-c ," . mc/mark-all-like-this)
   (:map mc/keymap
	 ("<return>" . nil))))

Backups (TODO: get backups working)

Sets all backups to path to .emacs.d instead of cluttering the folder their in

;; sets autosaves to one folder
(setq auto-save-file-name-transforms
      `((".*" ,temporary-file-directory t)))

;; Write backup files to own directory
(setq backup-directory-alist
      `(("." . ,(expand-file-name
		 (concat user-emacs-directory "backups")))))

;; Make backups of files, even when they're in version control
(setq vc-make-backup-files t)

Auto Insert

(defun auto-insert-yas-expand()
  "Replace text in yasnippet template."
  (yas-expand-snippet (buffer-string) (point-min) (point-max)))

(use-package autoinsert
  :init
  (setq auto-insert-query nil)
  (setq auto-insert-directory (concat user-emacs-directory "auto-insert/"))
  (setq auto-insert-alist nil)
  (auto-insert-mode +1)
  :config  
  (define-auto-insert 'python-mode ["python-header.py" auto-insert-yas-expand])
  (define-auto-insert 'cmake-mode ["cmake-basic.yas" auto-insert-yas-expand]))

Vterm

Vterm is a alternative terminal-emulator, to be used instead of ansii-term. It runs primarily in C instead of elisp, and is such so much faster.

(use-package vterm
  :hook (vterm-mode-hook . (lambda () (local-set-key (kbd ("C-M-\\") #'evil-togle))))
  :straight t)

lsp-mode

This is the main lsp-interface for emacs. It is more complicated then eglot, but has the advantage of working over tramp, as well as a larger ecosystem.

(use-package lsp-mode
  :straight t
  :bind (:map lsp-mode-map
			  ("C-c e" . 'lsp-execute-code-action))
  :init
  (setq lsp-prefer-capf nil
		lsp-server-install-dir (concat user-emacs-directory "lsp")
		lsp-enable-completion-at t
		lsp-auto-guess-root t)
  :hook ((c-mode c++-mode objc-mode bash-mode js-mode ;rustic relies on lsp
				 python-mode typescript-mode)
		 .
		 lsp)
  :commands (lsp))

(use-package company-lsp
  :straight t
  :config
  (setq company-lsp-cache-canidates 'auto
		company-lsp-async t
		company-lsp-enable-snippet t
		company-lsp-enable-recompletion t)
  (push 'company-lsp company-backends))

(use-package lsp-ui
  :straight t
  :bind ("C-c s" . 'lsp-ui-doc-show)
  :config
  (setq
   lsp-ui-doc-enable nil
   lsp-ui-doc-use-childframe nil ;; Requires v>=26 + graphics
   lsp-ui-doc-position 'at-point
   lsp-ui-doc-include-signature t
   lsp-ui-flycheck-enable t
   lsp-ui-flycheck-list-position 'right
   lsp-ui-flycheck-live-reporting t
   lsp-ui-peek-enable t
   lsp-ui-peek-list-width 60
   lsp-ui-peek-peek-height 25
   lsp-ui-sideline-enable t
   lsp-ui-doc-alignment 'window))

Projectile

(use-package projectile
  :straight t
  :bind (:map projectile-mode-map
	      ("C-c p" . 'projectile-command-map))
  :config
  (projectile-mode +1))

Ispell

(let ((exec (find-or-warn '("hunspell" "aspell" "ispell") "Ispell mode disabled")))
  (use-package ispell
	:straight t
	:if exec
	:bind (("C-z" . ispell-word))
	:init
	(setenv "DICPATH" (expand-file-name (concat user-emacs-directory "dictionaries/en")))
	(setq ispell-program-name exec)))

Compilation-Mode

Stolen from Stack Exchange. Originally Stolen from Endless Parentheses.

(require 'ansi-color)
(defun endless/colorize-compilation ()
  "Colorize from `compilation-filter-start' to `point'."
  (let ((inhibit-read-only t))
    (ansi-color-apply-on-region
     compilation-filter-start (point))))

(add-hook 'compilation-filter-hook
          #'endless/colorize-compilation)

Avy

(define-key flyspell-mode-map (kbd "C-;") nil)
(global-set-key (kbd "C-;") 'avy-goto-char-timer)

Major Modes

This contains a set of mutually exclusive Major Modes packages, along with their associated settings.

Lisp-Interaction-Mode

For setting up lisp-interaction-mode: the scratch buffer

(add-hook 'lisp-interaction-mode-hook (lambda ()
					(local-set-key (kbd "C-j") 'backward-word)
					(local-set-key (kbd "C-S-j") 'eval-print-last-sexp)))

Programming-Mode

Used to setup modes derived from prog-mode. We use my-prog-mode-called as a flag to indicate wither we load my-prog-mode again. This is necessary because otherwise it gets called repeatedly.

(use-package highlight-numbers
  :straight t
  :hook ((prog-mode . (lambda () (highlight-numbers-mode 1))))
  :config (set-face-foreground 'highlight-numbers-number "DarkOrchid2"))

(defun my-prog-mode ()
  "Run as part of global prog-mode setup"
  (local-set-key (kbd "C-c q") 'comment-or-uncomment-region)
  (setq display-line-numbers t)
  (line-number-mode 0)
  (column-number-mode 1)
  (if (version<= "26.0.50" emacs-version)
      ;;; display-line-numbers-mode was added in v26, so if earlier, we default to linum-mode
      (display-line-numbers-mode 1) ; displays line numbers on the left
    (linum-mode 1))
  (flyspell-prog-mode) ;this tells flyspell to not complain about variable names

  (eldoc-mode 1)
  (setq company-minimum-prefix-length 1) ;we want to active company for programming
  (setq font-lock-maximum-decoration t)
  (setq my-prog-mode-called t))

(add-hook 'prog-mode-hook 'my-prog-mode)

Text-Mode

(use-package olivetti
  :straight t)

(defun my-text-mode ()
  "A hook to call on text-mode init"
  (flyspell-mode +1)         ; recognizes misspellings
  (visual-line-mode +1)      ; we want the words to wrap
  )
(add-hook 'text-mode-hook 'my-text-mode)

Org-Mode

Org mode provides a function text mode, so we give it many text mode type things. Note: htmlize allows org-mode to publish to html more complex stuff like src blocks.

(use-package htmlize
  :straight t
  :after (org))

(use-package org
  :straight t
  :bind (("C-c a" . org-agenda)
	 ("C-c c" . org-capture)
	 :map org-mode-map ("C-j" . 'backward-word))
  :config
  (set-fill-column 120)

  ;; Babel 
  (setq org-babel-python-command "python3")
  (org-babel-do-load-languages 'org-babel-load-languages
			       '((python . t) (emacs-lisp . t) (C . t)))
  
  ;; SRC
  (setq org-src-window-setup 'current-window)
  (setq org-src-fontify-natively t)
  (setq org-src-tab-acts-natively t)
  (setq org-src-preserve-indentation t)
  
  (yas-activate-extra-mode 'text-mode)
  
  ;; Agenda
  (setq org-agenda-files (list "~/.org/school.org"
			       "~/.org/projects.org"))
  (setq org-capture-templates
	'(("s" "School" entry (file+headline "~/.org/school.org" "Tasks")
	   "* TODO %?\n%(if (not (= (length \"%i\") 0))
                         (concat \"%i\" \"\n  \"))  From: %a\n  SCHEDULED: %T")
	  ("p" "Projects" entry (file+headline "~/.org/projects.org" "Tasks")
	   "* TODO %?\n%(if (not (= (length \"%i\") 0))
                         (concat \"%i\" \"\n  \"))  From: %a\n  SCHEDULED: %T")
	  ))
  (setq org-log-done 'time)
  ;; sets up org-mode to use beamer
  (with-eval-after-load 'ox-latex
	(add-to-list 'org-latex-classes
				 '("beamer"
				   "\\documentclass\[presentation\]\{beamer\}"
				   ("\\section\{%s\}" . "\\section*\{%s\}")
				   ("\\subsection\{%s\}" . "\\subsection*\{%s\}")
				   ("\\subsubsection\{%s\}" . "\\subsubsection*\{%s\}")))))

R

ESS (Emacs Speaks Statistics) is a major mode that facilitates S type statistics languages.

(use-package ess
  :straight t
  :mode (("\\.r\\'" . ess-r-mode)
	 ("\\.Rmd\\'" . ess-r-mode)
	 ("\\.R\\'" . ess-r-mode))
  :if (find-or-warn "R" "ESS mode disabled")
  :config
  (setq inferior-ess-r-program (executable-find "R"))
  ;; We assume the ability to generate graphs using a WindowsX(QuartsX) program.
  (setq ess-dialect "R")
  (setq ess-ask-for-ess-directory nil) ; directory defaults to whatever ess-directory-function returns
  (setq ess-directory-function nil) ; directory defaults to ess-directory
  (setq ess-directory nil) ; directory defaults to the directory of the opened file
  (add-hook 'inferior-ess-mode  'ess-execute-screen-options)
  :init
  (load "ess-autoloads"))

Markdown-Mode

I assign markdown to the appropriate extensions, and enable math-mode and wc-mode. I honestly don’t use this much as org-mode does most of what markdown does.

(use-package markdown-mode
  :straight t
  :defer t
  :commands (markdown-mode gfm-mode)
  :mode (("README\\.md\\'" . gfm-mode)
		 ("\\.md\\'" . markdown-mode)
		 ("\\.markdown\\'" . markdown-mode))
  :hook ((markdown-mode . (lambda ()
							(yas-activate-extra-mode 'text-mode)
							(display-line-numbers-mode -1)
							(visual-line-mode 1))))
  :init
  (setq markdown-command (executable-find "multimarkdown"))
  (let (extension (file-name-extension (buffer-file-name)))
	(if (or (equal "md" extension) (equal "markdown" extension))
		(setq markdown-enable-math t)))
  :config
  (add-to-path "/Library/TeX/texbin/" t))

Lisp

I use sly as my lisp editor

(use-package sly
  :straight t
  :if (find-or-warn "clisp" "Sly mode disabled")
  :hook lisp-mode
  :config
  ;; The check prevents setting a new editor at compile time
  (prettify-symbols-mode +1)
  (if (string-suffix-p ".cl" buffer-file-name)
      (setq inferior-lisp-program (executable-find "clisp")))
  (define-key sly-mode-map (kbd "M-h") 'sly-documentation-lookup)
  (define-key sly-mode-map (kbd "C-x C-M-e") 'sly-eval-buffer)
  (setq sly-lisp-implementations `(
	  (clisp (,(executable-find "clisp")))
	  ))
  (setq sly-default-lisp 'clisp)
  (sly-mode)
  (sly)
  )
(add-to-list 'auto-mode-alist '("\\.cl\\'" . lisp-mode))
(add-to-list 'auto-mode-alist '("\\.lisp\\'" . lisp-mode))

Emacs-Lisp

Simple setup for emacs-lisp mode. Does very little.

(defun my-emacs-lisp-mode ()
  "runs on 'emacs-lisp-mode-hook "
  (prettify-symbols-mode +1)
  (modify-syntax-entry ?- "w" emacs-lisp-mode-syntax-table)
  )
(add-hook 'emacs-lisp-mode-hook #'my-emacs-lisp-mode)

Rust

Configure rust, enabling rustic-mode, cargo-mode, flycheck-rust, and company-racer.

(use-package rustic
  :straight t
  :config
  (setq
   rustic-lsp-server 'rust-analyzer
   rustic-lsp-client 'lsp-mode
   rustic-format-trigger 'on-save
   whitespace-line-column 98))

Python

Main Python

Setup shell, highlights, and python-mode. Most work is handled by a lsp-server

;;; Python Minor Modes

;; Indentation Guide
(use-package highlight-indent-guides
  :straight t
  :mode (("\\.py\\'" . (lambda () (highlight-indent-guides-mode +1))))
  :config
  ;; Options: "character", "fill", "column"
  (setq highlight-indent-guides-method 'column)
  ;;;sets character of the highlight, if in character mode
  (setq highlight-indent-guides-character ?\|)
  ;; Options: 'top, 'stack
  (setq highlight-indent-guides-responsive nil)
  (setq highlight-indent-guides-delay 0); respond immediately to the cursor
  ;; Sets if colors are controlled by theme
  (setq highlight-indent-guides-auto-enabled t)
  (set-face-background 'highlight-indent-guides-odd-face "darkcyan")
  (set-face-background 'highlight-indent-guides-even-face "darkcyan")
  (set-face-foreground 'highlight-indent-guides-character-face "dimgrey"))

;; Setup Python3 shell
(defun set-shell-python3 ()
  "Sets the shell to python3"
  (interactive)
  (setq python-shell-interpreter "python3")
  (setq python-shell-interpreter-args "-i")
  (with-eval-after-load 'python
    ;;This makes readline work in the interpreter
    (defun python-shell-completion-native-try ()
      "Return non-nil if can trigger native completion."
      (let ((python-shell-completion-native-enable t)
	    (python-shell-completion-native-output-timeout
	     python-shell-completion-native-try-output-timeout))
	(python-shell-completion-native-get-completions
	 (get-buffer-process (current-buffer))
	 nil "_")))))

(use-package python
  :mode (("\\.py\\'" . python-mode))
  :if (find-or-warn '("python3" "python") "Python mode disabled")
  :init
  (setq python-indent-guess-indent-offset t)
  (setq python-indent-guess-indent-offset-verbose nil)
  :config
  (when (executable-find "python3")
	(set-shell-python3)))

autopep8 enable on save

My autopep8 version

(define-minor-mode autopep8
  "Toggle autopep8 enable on save"
  :init-value nil
  :lighter ap8
  (defvar autopep8-executable nil "The location of the autopep8 command.")
  (defvar autopep8-enable-on-save t "Enable autopep8 on save")
  (defvar autopep8-macro-var nil "A dummy variable for mutable state in macros")
  (defvar autopep8-options '("--aggressive" "--aggressive")
    "A list of options given to autopep8. Must not reroute output.")
  (defmacro autopep8-process-region (exec tmp-buf options)
    "applies 'exec' on curreqnt buff, piping to 'tmp-buf' with 'options'"
    `(eval
	     (progn
	     (setq autopep8-macro-var
		   (reverse '(call-process-region 1 (buffer-size) ,exec nil
						  ,tmp-buf nil)))
	     (dolist (var ,options)
	       (push var autopep8-macro-var))
	     (push "-" autopep8-macro-var)
	     (reverse autopep8-macro-var))))
  
  (defun autopep8-buffer ()
    (interactive)
    (if (equal (file-name-extension (buffer-file-name)) "py")
	(let (
	      (file (buffer-file-name))
	      (tmp-buf (generate-new-buffer "autopep8"))
	      (exec (if autopep8-executable
			autopep8-executable
		      (executable-find "autopep8")))
	      )
	  (if (or (not exec) (equal exec ""))
	      (message "Could not find autopep8")
	    (progn
	      (autopep8-process-region exec tmp-buf autopep8-options)
	      (if (with-current-buffer tmp-buf
		    (not (or (< (buffer-size) 8)
			  (equal (buffer-substring-no-properties 1 7) "[Errno")
			  (equal (buffer-substring-no-properties 1 8) "usage: "))
			 ))
		  (progn (replace-buffer-contents tmp-buf)
			 (message "autopep8 format succeeded"))
		(with-current-buffer tmp-buf
		  (message "%s" (buffer-string))))
	      (kill-buffer tmp-buf))))))

  (defun autopep8-on-save ()
    (if (and autopep8 autopep8-enable-on-save)
	(autopep8-buffer)))
  (add-hook 'before-save-hook #'autopep8-on-save nil t)
  ) ;; end of autopep8 mode

(add-hook 'python-mode-hook 'autopep8)

LaTeX

Sets up latex support along with a collection of skeletons for latex. This is also mostly replaced by org-mode

(use-package tex
  :straight auctex
  :defer t
  :mode ("\\.tex\\'" . LaTeX-mode)
  :hook ((LaTeX-mode . (lambda ()  (yas-activate-extra-mode 'text-mode)
			 (define-key LaTeX-mode-map (kbd "C-j")  'backward-word)
			 )))
  :config
  (setq TeX-auto-save t
	TeX-parse-self t
	font-latex-fontify-script nil
	tex--prettify-symbols-alist nil)
  (visual-line-mode +1)
  (load (expand-file-name (concat user-emacs-directory "LaTeX_skeletons.el")))
  (add-to-path "/Library/TeX/texbin/" t)
  (set-fill-column 100))

Javascript

(use-package js2-mode
  :straight t
  ;; uses lsp-mode
  :mode (("\\.js[x]?$\\'" . js2-mode)
		 ("\\.tsx$\\'" . js-mode))
  :bind (:map js-mode-map
			  ("M-." . xref-find-definitions))
  :config
  (setq js-indent-level 2))

Haskell

Instructions on installing the haskell-language server can be found here.

(add-to-path "~/.cabal/bin") ;for the cabal package manager
(add-to-path "~/.stack/bin") ;for the stack package manager
(use-package lsp-haskell
  :straight t
  :if (find-or-warn "hie" "lsp-haskell disabled")
  :config
  (setq lsp-haskell-process-path-hie (executable-find "hie")))

(use-package haskell-mode
  :straight t
  :defer t
  :config
  (setq haskell-stylish-on-save t))

C/C++

(use-package clang-format
  :straight t
  :if (find-or-warn "clang-format" "C/C++ formating disabled")
  :defer t
  :init
  (setq clang-format-style "file")
  :config
  (defun clang-format-safe-buffer ()
	"Prevents clang-format from working outside it's mode"
    (interactive)
    (when (or
		   (and (eql major-mode 'c-mode) (equal c-file-style nil))
		   (eql major-mode 'c++-mode))
      (clang-format-buffer)))
  (add-hook 'before-save-hook 'clang-format-safe-buffer))

C/Cpp custom code

Binds “C-c C-f” to a function that inserts c function description using yasnippet. Binds “M-p” to a function that switches .cc type files with .hh type files.

(defmacro incr (var) `(setq ,var (1+ ,var)))

(defun check-against-list (list-to-check bool-function)
  (if (funcall bool-function (car list-to-check))
      t
    (if (cdr list-to-check)
	(check-against-list (cdr list-to-check) bool-function)
      nil)))

(cl-defun concat-until-n
    (lst &key (n 0) (before "") (after "") 
	 (count-from 0 count-from-p) (after-count "") (add-before-last ""))
  "Returns a concatonated list of list elements ignoring the last n with an optional count and surrounding text"
  (if (<= (length lst) n)
      ""   ; recursion base case
    (concat ;otherwise
     before
     (s-trim (car lst))
     (if (= (1- (length lst)) n) add-before-last nil)
     after
     (if count-from-p (int-to-string count-from) "")
     after-count
     (if count-from-p
	 (concat-until-n (cdr lst)
			 :n n :before before :after after
			 :count-from (1+ count-from)
			 :after-count after-count
			 :add-before-last add-before-last)
       (concat-until-n (cdr lst)
		       :n n :before before
		       :after after :after-count after-count
		       :add-before-last add-before-last)))))

(defun remove-blank-lines (except num-blank &optional reverse)
  "Removes lines that start with \"excpet\" and blank lines until there are only \"num-blank\" blank lines above."
  (let ((blank-found 0) (exit nil) (direction (if reverse 1 -1)))
    (save-excursion
      (while (and (not exit) (< blank-found 1000))
	(if (equal (thing-at-point 'line t) "\n")
	    (progn
	      (incr blank-found)
	      (if (> blank-found num-blank)
		  (delete-blank-lines))
	      (forward-line direction))
	  (if (string-prefix-p except (thing-at-point 'line t))
	      (progn (forward-line direction) (setq blank-found 0))
	    (setq exit t)))))
    blank-found))

(defun scroll-down-blank ()
  "Scrolls the point down until encountering a non-blank line"
  (while (equal (thing-at-point 'line t) "\n")
    (forward-line 1)))

(defun remove-blank-up (prefix)
  "Deletes blank lines until there are 'prefix' left"
  (interactive "P")
  (let ((num-deleted (remove-blank-lines "//" 1  nil)))
    (if prefix
	(dotimes (a (- prefix 1)) (insert "\n")))
    (if (< num-deleted 2) nil (forward-line -1))))

(global-set-key (kbd "C-x M-p") 'remove-blank-up) ; eats whitespace between files

(defun c-func-description ()
  ;;skipping to the next line with content
  (scroll-down-blank)
  (beginning-of-line)
  (let* ((func-body-pair (split-string (thing-at-point 'line t) "{"))
	 (objects (split-string (car func-body-pair) "[\(,\)]")) (type-name
								  (split-string (car objects) "[ ]+"))
	 (field-n 1)
	 (func-decorations (car (split-string (car (last type-name))
					      "[a-zA-Z-_:]" t)))
	 (func-undecorated (car (split-string (car (last type-name))
					      "[^a-zA-Z-_:]" t)))
	 )
    (defun field-n () (int-to-string field-n))
    ;; We assume that the first thing is the function name
    ;; and everything else is an argument
    (yas-expand-snippet
     (concat "// " func-undecorated ": ${" (field-n) ":Describe Function}\n"
	     (if (and
		  (check-against-list
		   (cdr objects) (lambda (x) (string-match-p "[a-zA-Z]" x)))
		  (not (equal (cadr objects) "void")))
		 (concat "//\n"
			 (concat-until-n
			  (cdr objects) :n 1 :before "// " :after ": ${"
			  :after-count ":Describe Argument}\n" :count-from 2)))
	     (if (or func-decorations (not (string-equal "void" (car type-name))))
		 (concat "//\n" "// return"
			 (concat-until-n type-name :n 1 :before " "
					 :add-before-last func-decorations)
			 ": ${" (int-to-string (length objects))
			 ":Describe Return}\n\n")
	       "\n"))))
  (remove-blank-lines "//" 1))

(defun c-insert-func-description ()
  "Inserts a c-function description when called at or above a c function"
  (interactive)
  ;;Note: requires yasnippet to be installed
  (if (not (fboundp 'yas-expand-snippet))
      (message "%s" "c-func-description requires yas-expand-snippet")
    (c-func-description)))

(defun cpp-file-switch (buffer-prefix buffer-postfix from-postfix to-postfix)
  (if (equal buffer-postfix from-postfix)
      (let ((new-buffer (concat buffer-prefix to-postfix)))
	(if (file-exists-p new-buffer)
	    (progn
	      (find-file new-buffer)
	      nil
	      )
	  (progn (message "%s was not found" new-buffer) nil)))
    t))


(defun switch-cpp-file-type ()
  (interactive)
  (let* ((buf_name (buffer-file-name))
	 (prefix (file-name-sans-extension buf_name))
	 (postfix (concat "."(file-name-extension buf_name)))
	 )
    (if (cpp-file-switch prefix postfix ".cc" ".hh")
	(if (cpp-file-switch prefix postfix ".hh" ".cc")
	    (if (cpp-file-switch prefix postfix ".c" ".h")
		(if (cpp-file-switch prefix postfix ".h" ".c")
		    (message "file with extension \"%s\" not recognized" postfix)))))))

(defun add-c-style-functions ()
  (local-set-key (kbd "C-c C-f") 'c-insert-func-description)
  (if (not (equal major-mode "c-mode"))
      (yas-activate-extra-mode 'c-mode))
  (local-set-key (kbd "M-p") 'switch-cpp-file-type))

(add-hook 'c-mode-hook 'add-c-style-functions)
(add-hook 'c++-mode-hook 'add-c-style-functions)
(add-hook 'objc-mode-hook 'add-c-style-functions)

CMake

CMake files kinda suck, there should be a major mode for them:

(use-package cmake-mode
  :defer t
  :straight t
  :mode "CMakeLists.txt")

ASM (assembly)

For writing assembly

(defun my-asm-setup-mode () (define-key asm-mode-map (kbd "C-j") 'backward-word))
(add-hook 'asm-mode-hook 'my-asm-setup-mode)

ansi-term

(use-package term
  ;; ensure: nil
  :bind (:map term-mode-map
	      ("M-p" . term-send-up)
	      ("M-n" . term-send-down)
	      ("C-y" . term-paste)))

ion-shell

(use-package ion-mode
  :straight (ion-mode
	       :host github :repo "iwahbe/ion-mode")
  :mode (("\\.ion\\'" . ion-mode)
		 ("/ion/initrc\\'" . ion-mode)))

Java

(use-package lsp-java
  :straight t
  :disabled
  :after lsp-mode
  :config 
  (add-hook 'java-mode-hook 'lsp)
  (add-hook 'before-save-hook 'lsp-format-buffer 0 t))

TypeScript

(use-package typescript-mode
  :straight t)

Agda

;; Reiles on agda-mode bieng in path.
(let ((agda (find-or-warn "agda-mode" "Agda mode disabled")))
  (when agda
	(load-file (let ((coding-system-for-read 'utf-8))
				 (shell-command-to-string (concat agda " locate"))))))

About

This is my Emacs initialization setup

License:MIT License


Languages

Language:Emacs Lisp 59.4%Language:YASnippet 22.2%Language:Makefile 15.0%Language:Python 1.8%Language:Shell 1.7%