summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--emacs/init.el686
1 files changed, 686 insertions, 0 deletions
diff --git a/emacs/init.el b/emacs/init.el
new file mode 100644
index 0000000..fa52557
--- /dev/null
+++ b/emacs/init.el
@@ -0,0 +1,686 @@
+(add-to-list 'load-path (expand-file-name (concat user-emacs-directory "site-lisp")))
+
+(setq garbage-collection-messages t)
+
+(setopt user-mail-address "noa@gaiwan.org")
+
+;; properly distinguish these chords from their ascii legacy
+(define-key input-decode-map [?\C-i] [C-i])
+(define-key input-decode-map [?\C-m] [C-m])
+
+(use-package tubthumping-theme
+ :config
+ (load-theme 'tubthumping t))
+
+
+(global-set-key (kbd "<backspace>") 'backward-delete-char-untabify)
+
+;; I make my caps lock a menu key, so i can open the command palette with it
+;; https://alexschroeder.ch/wiki/2020-07-16_Emacs_everything
+
+(define-key context-menu-mode-map (kbd "<menu>") nil)
+(global-unset-key (kbd "M-x"))
+(global-set-key (kbd "<menu>") 'execute-extended-command)
+
+(setopt
+ cursor-type 'bar
+ minibuffer-depth-indicate-mode t)
+
+(setopt
+ browse-url-browser-function 'eww-browse-url
+ url-cookie-trusted-urls '()
+ url-cookie-untrusted-urls '(".*"))
+
+
+;;; candidate completion
+(use-package vertico
+ :ensure t
+ :custom
+ (vertico-mode t)
+ (vertico-count 12)
+ (vertico-cycle t)
+ (read-buffer-completion-ignore-case t)
+ (read-file-name-completion-ignore-case t)
+ (completion-ignore-case t)
+)
+
+(use-package orderless
+ :ensure t
+ :custom
+ (completion-styles '(orderless basic))
+ (completion-category-defaults nil)
+ (completion-category-overrides '((file (styles partial-completion)))))
+
+(setopt confirm-nonexistent-file-or-buffer 'after-completion)
+
+(use-package nano-modeline
+ :ensure t
+ :hook (
+ (prog-mode . nano-modeline-prog-mode)
+ (text-mode . nano-modeline-text-mode)
+ (org-mode . nano-modeline-org-mode)
+ (pdf-view-mode . nano-modeline-pdf-mode)
+ (mu4e-headers-mode . nano-modeline-mu4e-headers-mode)
+ (mu4e-view-mode . nano-modeline-mu4e-message-mode)
+ (term-mode . nano-modeline-term-mode)
+ (xwidget-webkit-mode . nano-modeline-xwidget-mode)
+ (messages-buffer-mode . nano-modeline-message-mode)
+ (org-capture-mode . nano-modeline-org-capture-mode)
+ (org-agenda-mode . nano-modeline-org-agenda-mode)
+ )
+ :config
+ (setq-default mode-line-format nil)
+ )
+
+(use-package beacon
+ :ensure t
+ :delight
+ :custom (beacon-mode t))
+
+(use-package nov
+ :ensure t
+ :mode ("\\.epub\\'" . nov-mode))
+
+(use-package shr
+ :custom
+ (shr-max-width nil))
+
+;; also check out jinx https://github.com/minad/jinx
+(use-package spell-fu
+ :ensure t
+ :hook (text-mode . spell-fu-mode))
+
+(use-package minimap
+ :ensure t
+ :custom
+ (minimap-width-fraction 0.1)
+ (minimap-minimum-width 10)
+ (minimap-window-location 'right)
+ (minimap-update-delay 0.15))
+
+;; consult-buffer replaces the buffer menu. as well as listing buffers, it lists bookmarks and recent files.
+(use-package consult
+ :ensure t
+ :bind (
+ ([remap switch-to-buffer] . consult-buffer)
+ ([remap yank-pop] . consult-yank-pop)
+ ([remap goto-line] . consult-goto-line))
+ :custom
+ (completion-in-region-function 'consult-completion-in-region)
+ )
+
+(use-package marginalia
+ :ensure t
+ :custom
+ (marginalia-mode t)
+ (marginalia-max-relative-age most-positive-fixnum)
+ (marginalia-align 'right))
+
+
+(global-set-key (kbd "<C-i>") 'completion-at-point)
+
+;; Add extensions
+(use-package cape
+ :ensure t
+ :bind
+ ("C-c p f" . cape-file)
+ ("C-c p :" . cape-emoji)
+ :init
+ (add-hook 'completion-at-point-functions #'cape-file)
+ (add-hook 'completion-at-point-functions #'cape-dabbrev)
+ (add-hook 'completion-at-point-functions #'cape-elisp-block)
+ ;;(add-hook 'completion-at-point-functions #'cape-history)
+ ;;(add-hook 'completion-at-point-functions #'cape-keyword)
+ ;;(add-hook 'completion-at-point-functions #'cape-tex)
+ ;;(add-hook 'completion-at-point-functions #'cape-sgml)
+ ;;(add-hook 'completion-at-point-functions #'cape-rfc1345)
+ ;;(add-hook 'completion-at-point-functions #'cape-abbrev)
+ ;;(add-hook 'completion-at-point-functions #'cape-dict)
+ ;;(add-hook 'completion-at-point-functions #'cape-elisp-symbol)
+ ;;(add-hook 'completion-at-point-functions #'cape-line)
+)
+
+(use-package org-roam
+ :ensure t
+ :custom
+ (org-roam-directory (expand-file-name "~/data/notes/"))
+ (org-roam-completion-everywhere t)
+ :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)
+ )
+ :custom
+ ;; If you're using a vertical completion framework, you might want a more informative completion interface
+ (org-roam-node-display-template (concat "${title:*} " (propertize "${tags:10}" 'face 'org-tag)))
+ (org-roam-db-autosync-mode t)
+ :config
+ (require 'org-roam-protocol)
+)
+
+(use-package org
+ :custom
+ (org-startup-indented t)
+ (org-pretty-entities t)
+ (org-use-sub-superscripts "{}")
+ (org-hide-emphasis-markers t)
+ (org-startup-with-inline-images t)
+ (org-image-actual-width '(300))
+ (org-auto-align-tags nil)
+ (org-tags-column 0)
+ (org-catch-invisible-edits 'show-and-error)
+ (org-special-ctrl-a/e t)
+ (org-insert-heading-respect-content t)
+ (org-ellipsis "…")
+
+ (org-agenda-tags-column t)
+ (org-agenda-block-separator ?─)
+ (org-agenda-time-grid
+ '((daily today require-timed)
+ (800 1000 1200 1400 1600 1800 2000)
+ " ┄┄┄┄┄ " "┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄"))
+ (org-agenda-current-time-string
+ "◀── now ─────────────────────────────────────────────────")
+
+ :custom-face
+ ;;(org-ellipsis ((t (:inherit default :box nil))))
+)
+
+(use-package org-appear
+ :ensure t
+ :after org
+ :hook
+ (org-mode . org-appear-mode))
+
+(use-package org-modern
+ :ensure t
+ :after org
+ :hook
+ (org-mode . org-modern-mode)
+ (org-agenda-finalize . org-modern-agenda)
+ :custom
+ (org-modern-keyword nil)
+ (org-modern-checkbox nil)
+ (org-modern-table nil))
+
+(use-package olivetti
+ :ensure t
+ :hook (
+ (text-mode . olivetti-mode)
+ (eww-after-render . olivetti-mode)
+ (nov-post-html-render . olivetti-mode)
+ (mu4e-view-mode . olivetti-mode)
+ )
+)
+
+(use-package delight
+ :ensure t)
+
+(use-package eldoc
+ :delight
+ :custom
+ (global-eldoc-mode t))
+
+(use-package rainbow-mode
+ :ensure t
+ :delight
+ :hook (prog-mode . rainbow-mode))
+
+(use-package hyperspace
+ :disabled
+ :ensure t
+ :bind ("<C-m>" . hyperspace))
+
+(use-package hyperbole
+ :disabled
+ :ensure t)
+
+;; use a bar cursor and blink it and don't stop blinking it. i don't know how i feel about this yet to be honest, but it helps me know which window is active so for now i'm keeping it
+(setopt
+ cursor-type 'bar
+ blink-cursor-mode -1
+ blink-cursor-interval 0.7)
+
+(setopt
+ dired-recursive-deletes 'always
+ dired-recursive-copies 'always
+ dired-clean-up-buffers-too nil
+ delete-by-moving-to-trash t
+ dired-dwim-target t
+ dired-listing-switches "-alv")
+
+;;; indentation: tabs and whitespace settings
+
+;; my rules for inserting tabs are that the tab key should insert tabs. i personally prefer tabs. spaces don't work for me because then people go trigger happy with alignment, which doesn't really work with proportional fonts.
+
+;; todo: elastic tabstops
+;; https://nickgravgaard.com/elastic-tabstops/
+;; https://github.com/tenbillionwords/spaceship-mode
+
+;; always insert \t for tab.
+(setopt
+ electric-indent-mode nil
+ backward-delete-char-untabify-method nil
+ tab-width 8)
+
+;; do a naive duplicate whitespace on return, vi/nano style.
+(define-key global-map (kbd "TAB") 'self-insert-command)
+(defun naive-return-and-indent ()
+ "insert a newline and copy the indentation of the previous line"
+ (interactive)
+ (open-line 1)
+ (let* ((start (progn (beginning-of-line) (point)))
+ (indent (progn (back-to-indentation) (point)))
+ (end (progn (end-of-line) (point)))
+ (whitespace (buffer-substring start indent)))
+ (delete-trailing-whitespace start end)
+ (beginning-of-line 2)
+ (insert whitespace)))
+(define-key global-map (kbd "RET") 'naive-return-and-indent)
+
+;;; interface
+
+;; we want to make sure that various bits of the interface are hidden. but this isn't an "all gui chrome is useless" rampage. i personally think the scrollbar is useful, i like the visual indication it gives of how far i am through a file.
+(setopt
+ menu-bar-mode nil
+ tool-bar-mode nil
+ tooltip-mode nil ;; show tooltips in the echo area
+ echo-keystrokes 0.1) ;; show chord progress (almost) immediately
+
+(use-package paren
+ :custom
+ (show-paren-mode t)
+
+ )
+
+;; populate and enable the context menu
+;; (setopt context-menu-functions '(
+;; context-menu-ffap
+;; occur-context-menu
+;; context-menu-region
+;; context-menu-undo
+;; goto-address-context-menu)
+;; context-menu-mode t)
+(use-package mouse
+ :custom
+ (context-menu-mode t))
+
+
+
+(use-package tab-bar
+ :disabled
+ :custom
+ (tab-bar-mode t)
+ (tab-bar-format '(tab-bar-format-align-right tab-bar-format-global tab-bar-format-menu-bar))
+
+ )
+
+
+(use-package font-lock
+ :custom
+ (global-font-lock-mode t)
+ (font-lock-maximum-decoration nil))
+
+(setopt
+ display-time-mode t
+ display-battery-mode t
+ inhibit-startup-screen t
+ mouse-drag-and-drop-region nil
+ mouse-yank-at-point t
+ delete-selection-mode nil ;; deleting should be an explicit action
+ )
+
+(global-set-key (kbd "C-t") 'tab-new)
+
+;; shift click to select region with the mouse. This annoyingly rings the bell for an error
+(global-unset-key (kbd "S-<down-mouse-1>"))
+(global-set-key (kbd "S-<down-mouse-1>") 'mouse-save-then-kill)
+
+;;; packages
+(setopt package-archives '(
+ ("gnu" . "https://elpa.gnu.org/packages/")
+ ("nongnu" . "https://elpa.nongnu.org/nongnu/")
+ ("melpa-stable" . "https://stable.melpa.org/packages/")
+ ("melpa" . "https://melpa.org/packages/")))
+
+;;; saving
+
+;; backups are pointless in long emacs sessions imo, but autosaves are useful
+(setopt make-backup-files nil
+ backup-by-copying t
+ create-lockfiles nil
+ auto-save-mode 1
+ auto-save-interval 20 ;; every twenty keystrokes
+ auto-save-timeout 5 ;; every 5 seconds
+ auto-save-default t
+ auto-save-no-message t
+ version-control t
+ auto-save-visited-mode t)
+(add-hook 'focus-out-hook (lambda () (interactive) (save-some-buffers t)))
+(add-hook 'mouse-leave-buffer-hook (lambda () (interactive) (save-some-buffers t)))
+
+;;; scrolling
+(setopt
+ mouse-wheel-scroll-amount '(1 ((shift) . 1))
+ mouse-wheel-progressive-speed nil
+ mouse-wheel-follow-mouse 't
+ scroll-step 1
+ pixel-scroll-precision-mode t
+ pixel-scroll-precision-use-momentum t)
+
+
+;;; sentences
+(setopt sentence-end-double-space nil)
+
+;;; spellcheck
+(setopt
+ flyspell-mode t
+ ispell-program-name "aspell"
+ ispell-dictionary "en_GB"
+ ispell-extra-args '("--sug-mode=ultra")
+ require-final-newline t
+ window-min-height 1
+ window-combination-resize t
+ window-resize-pixelwise t
+ frame-resize-pixelwise t)
+
+;;; history
+(setopt
+ history-length 250
+ kill-ring-max 25)
+
+(use-package savehist
+ :custom
+ (savehist-file "~/.config/emacs/savehist")
+ (savehist-additional-variables '(
+ kill-ring
+ command-history
+ set-variable-value-history
+ custom-variable-history
+ query-replace-history
+ read-expression-history
+ minibuffer-history
+ read-char-history
+ face-name-history
+ bookmark-history
+ file-name-history))
+ (savehist-mode t))
+
+(use-package frame
+ :custom
+ (window-divider-mode t)
+ (window-divider-default-right-width 1)
+ (window-divider-default-bottom-width 1)
+ (window-divider-default-places t)
+ )
+
+(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)
+
+ :custom
+ ;; Support opening new minibuffers from inside existing minibuffers.
+ (enable-recursive-minibuffers t)
+ (debug-on-error t)
+
+ ;; Hide commands in M-x which do not work in the current mode.
+ (read-extended-command-predicate 'command-completion-default-include-p))
+
+(setopt
+ recentf-max-menu-items 25
+ recentf-save-file "~/.config/emacs/recentf"
+ recentf-mode 1
+ bookmark-default-file "~/.config/emacs/bookmarks")
+
+
+
+;;; miscellaneous
+(setopt
+ save-place-mode 1)
+
+(use-package goto-addr
+ :custom
+ (global-goto-address-mode t)
+ (goto-address-mail-face 'link)
+ (goto-address-mail-mouse-face 'link)
+ (goto-address-url-face 'link)
+ (goto-address-url-mouse-face 'link)
+)
+
+(setenv "PAGER" "cat")
+(setenv "TERM" "dumb")
+(setenv "GPG_AGENT_INFO" nil)
+(defalias 'yes-or-no-p 'y-or-n-p)
+(setq disabled-command-function nil)
+(setopt custom-file (make-temp-file "custom"))
+(setq inhibit-startup-echo-area-message "noa") ;; #userfreedom
+
+(use-package simple
+ :delight visual-line-mode
+ :custom
+ (global-visual-line-mode t))
+
+(setopt
+ kill-whole-line t
+ uniquify-after-kill-buffer-p t
+ uniquify-buffer-name-style 'forward
+ uniquify-ignore-buffers-re "^\\*"
+ uniquify-separator "/")
+
+(setopt
+ save-interprogram-paste-before-kill t
+ apropos-do-all t
+ mouse-yank-at-point t
+ require-final-newline t
+ visible-bell t
+ load-prefer-newer t
+ frame-inhibit-implied-resize t
+ ediff-window-setup-function 'ediff-setup-windows-plain)
+
+(load "server")
+(unless (server-running-p) (server-start))
+
+(setopt help-at-pt-display-when-idle t)
+
+;;; search
+;; search without regard for unicode characters
+(setopt search-default-mode 'char-fold-to-regexp
+ replace-char-fold t
+ search-highlight t
+ isearch-lazy-highlight t)
+;; when exiting isearch after a match, put the cursor at the beginning of the search rather than the end
+(setq isearch-mode-end-hook '(goto-beginning-of-search))
+(defun goto-beginning-of-search ()
+ "positions the point at the beginning of the isearch match"
+ (when (and (not isearch-mode-end-hook-quit)
+ isearch-forward)
+ (goto-char isearch-other-end)))
+;; wrap searches
+(setopt isearch-wrap-pause 'no-ding)
+;; jump straight to previous match when change search direction
+(setopt isearch-repeat-on-direction-change t)
+
+(use-package ctrlf
+ :ensure t
+ :bind (
+ ([remap isearch-forward] . ctrlf-forward-default)
+ ([remap isearch-backward] . ctrlf-backward-default)
+ ([remap isearch-forward-regexp] . ctrlf-forward-alternate)
+ ([remap isearch-backward-regexp] . ctrlf-backward-alternate)
+ ([remap isearch-forward-symbol] . ctrlf-forward-symbol)
+ ([remap isearch-forward-symbol-at-point] . ctrlf-forward-symbol-at-point)
+ )
+ :custom
+ (ctrlf-go-to-end-of-match nil "It makes more sense to go to the start of the match, because i start searching where i want to be.")
+)
+
+(global-set-key (kbd "M-o") 'other-window)
+
+(global-set-key (kbd "C-x k") 'kill-this-buffer)
+
+;; undo C-/
+;; redo C-S-/
+(setopt undo-no-redo t)
+
+;; mu4e
+(use-package mu4e
+ :custom
+ (mu4e-headers-skip-duplicates t)
+ (mu4e-view-show-images t)
+ (mu4e-view-show-addresses t)
+ (mu4e-compose-format-flowed nil)
+ (mu4e-change-filenames-when-moving t)
+ (mu4e-use-fancy-chars nil)
+ (mu4e-confirm-quit nil)
+ (mu4e-headers-leave-behavior 'apply)
+ (mu4e-headers-precise-alignment t)
+ (mu4e-search-threads nil)
+ (mu4e-hide-index-messages t)
+ (mu4e-get-mail-command "mbsync -c ~/.config/mbsyncrc fastmail")
+ (mu4e-maildir "~/data/mail")
+ (mu4e-drafts-folder "/Drafts")
+ (mu4e-sent-folder "/Sent")
+ (mu4e-refile-folder "/Archive")
+ (mu4e-trash-folder "/Trash")
+ (mu4e-bookmarks '(
+ (:name "Waiting room" :query "\"maildir:/Waiting room\"" :key ?w)
+ (:name "Inbox" :query "maildir:/Inbox" :key ?i)
+ (:name "Feeds" :query "maildir:/Feeds" :key ?f)
+ (:name "Paper trail" :query "\"maildir:/Paper trail\"" :key ?p)))
+
+ :bind (
+ :map mu4e-headers-mode-map
+ ("d" . my-move-to-trash)
+ :map mu4e-view-mode-map
+ ("d" . my-move-to-trash)
+ )
+ :init
+ (fset 'my-move-to-trash "mTrash") ;; function to move mails to trash
+ :custom-face
+ )
+
+(use-package button
+ :custom-face
+ ;;(button ((t (:underline t :foreground unspecified))))
+ )
+
+(use-package faces
+ :custom-face
+ ;; (link ((t (:underline t :foreground unspecified))))
+ ;; (fringe ((t (:background unspecified))))
+ ;; (mode-line ((t (:background unspecified :box 2))))
+ ;; (mode-line-active ((t (:inverse-video t))))
+ ;; (mode-line-inactive ((t (:background unspecified :box 2 :weight unspecified))))
+ ;; (help-key-binding ((t (:inherit default :background unspecified :foreground unspecified :box 1))))
+ )
+
+(global-set-key (kbd "M-z") 'zap-up-to-char)
+
+(use-package markdown-mode
+ :ensure t
+ :mode ("\\.md\\'" . markdown-mode)
+ )
+(use-package valign
+ :ensure t
+ :after markdown-mode
+ :hook (markdown-mode . valign-mode))
+
+
+;;; TOUCHSCREEN
+;; this should be obsolete in emacs 30
+;; Copyright 2024-present Naheel Azawy. All rights reserved.
+(defvar touchscreen-last-time)
+(defvar touchscreen-last-pos-pixel)
+(defvar touchscreen-last-dist 0)
+(defvar touchscreen-begin-char)
+
+(defun touchscreen-time ()
+ "Time in seconds."
+ (time-convert (current-time) 'integer))
+
+(defun touchscreen-handle-touch-begin (input)
+ "Handle touch begining at input INPUT."
+ (interactive "e")
+ (let* ((event (nth 1 input))
+ (pos-pixel (nth 3 event))
+ (pos-char (nth 6 event))
+ (win (nth 1 event)))
+ ;; (message (format "%s" input))
+ (if (not (equal (selected-window) win))
+ ;; switch window
+ (select-window win))
+ ;; set globals
+ (setq touchscreen-last-time (touchscreen-time))
+ (setq touchscreen-last-pos-pixel pos-pixel)
+ (setq touchscreen-begin-char pos-char)
+ ))
+
+(defun touchscreen-handle-touch-update (input)
+ "Handle touch update at input INPUT."
+ (interactive "e")
+ (let* ((event (nth 0 (nth 1 input)))
+ (pos-pixel (nth 3 event))
+ (pos-char (nth 6 event))
+ (diff-time (- (touchscreen-time) touchscreen-last-time))
+ (diff-pixel (- (cdr touchscreen-last-pos-pixel) (cdr pos-pixel)))
+ (diff-char (abs (- touchscreen-begin-char pos-char))))
+
+ (if (= (length (nth 1 input)) 2)
+ ;; pinch zoom
+ (let* ((event2 (nth 1 (nth 1 input)))
+ (pos-pixel2 (nth 3 event2))
+ (dist (sqrt (+ (expt (- (car pos-pixel2) (car pos-pixel)) 2)
+ (expt (- (cdr pos-pixel2) (cdr pos-pixel)) 2))))
+ (dist-diff (- dist touchscreen-last-dist)))
+ (setq touchscreen-last-dist dist)
+ (if (> dist-diff 0)
+ (text-scale-increase 0.1)
+ (if (< dist-diff 0)
+ (text-scale-decrease 0.1)))
+ )
+
+ (if (> diff-time 1)
+ ;; TODO: set marker on long press
+ (goto-char pos-char))
+ (if (> diff-char 1)
+ ;; scroll
+ (progn
+ (move-to-window-line nil)
+ (if (> diff-pixel 0)
+ (pixel-scroll-pixel-up diff-pixel)
+ (if (< diff-pixel 0)
+ (pixel-scroll-pixel-down (* -1 diff-pixel))))
+ (setq touchscreen-last-time (touchscreen-time))
+ (setq touchscreen-last-pos-pixel pos-pixel))
+ ))))
+
+(defun touchscreen-handle-touch-end (input)
+ "Handle touch end at input INPUT."
+ (interactive "e")
+ (let* ((event (nth 1 input))
+ (pos-char (nth 6 event)))
+ (if (= touchscreen-begin-char pos-char)
+ ;; move cursor
+ (goto-char pos-char))))
+
+(global-set-key [touchscreen-begin] #'touchscreen-handle-touch-begin)
+(global-set-key [touchscreen-update] #'touchscreen-handle-touch-update)
+(global-set-key [touchscreen-end] #'touchscreen-handle-touch-end)