;;; init.el --- my emacs configuration -*- lexical-binding: t; -*- ;;; Commentary: ;; Welcome to my emacs! It's not much, but it's mine :) ;; (profiler-start 'cpu+mem) ;;; Getting started (add-to-list 'load-path (expand-file-name (concat user-emacs-directory "site-lisp"))) (add-to-list 'exec-path "/home/noa/.config/Scripts") (use-package use-package :custom (use-package-compute-statistics t) (use-package-enable-imenu-support t)) ;; Give me stats on garbage collection and startup time (add-hook 'emacs-startup-hook #'(lambda () (message (format "Initialised in %s seconds with %s garbage collections." (emacs-init-time) gcs-done)))) ;; keep the emacs directory clean (use-package no-littering :ensure t) ;;; Make a nice interface (use-package modus-themes :ensure t :custom (modus-themes-bold-constructs t) (modus-themes-italic-constructs t) (modus-themes-variable-pitch-ui nil) (modus-themes-mixed-fonts t) (modus-themes-common-palette-overrides '((builtin unspecified) (comment unspecified) (constant unspecified) (fnname unspecified) (keyword unspecified) (preprocessor unspecified) (docstring unspecified) (string unspecified) (type unspecified) (variable unspecified))) :config (load-theme 'modus-operandi t)) (setopt tab-bar-format '(tab-bar-format-history tab-bar-format-tabs tab-bar-separator tab-bar-format-add-tab tab-bar-format-align-right tab-bar-format-global) tab-bar-mode t) (setopt repeat-mode t) (setopt imenu-flatten 'prefix) ;; Using a proportional font is the right way to do things, but emacs is very old and comes from a time before the innovation of legibility. As a result, there are some things that require a monospaced font, so i set one here. (custom-set-faces '(default ((t (:family "Noto Sans Mono" :height 120)))) '(variable-pitch ((t (:family "Noto Sans" :height 120)))) '(fixed-pitch ((t (:family "Noto Sans Mono" :height 120)))) ) ;; Previously, i used a heavily modified version of [[https://github.com/cstby/fixed-pitch-mode][fixed pitch mode]] to use a variable pitch font by default, and switch to monospace when necessary. However, i have revised that decision, as my list of exceptions kept getting longer, and emacs has a variable pitch mode built in, which does the opposite. (use-package face-remap :hook ((text-mode . variable-pitch-mode) (prog-mode . variable-pitch-mode))) ;; I also noticed problems with hanzi being displayed in emacs as their Korean variants. I read simplified chinese, so i want to ensure that han characters are displayed using a font designed for simplified chinese. (when (display-graphic-p) (dolist (charset '(han cjk-misc)) (set-fontset-font t charset (font-spec :family "Noto Serif CJK SC")))) ;; In a similar vein, i want to set my preferred emoji fonts. The default noto fonts have coloured emojis, but there is a separate font which has monochrome emojis, and i also set the [[https://fsd.it/shop/fonts/fsd-emoji/][FSD emoji]] font to be used whenever it has a supported emoji. (when (and (display-graphic-p) (member "FSD Emoji" (font-family-list))) (set-fontset-font t 'emoji (font-spec :family "FSD Emoji") nil 'prepend)) (when (and (display-graphic-p) (member "Noto Emoji" (font-family-list))) (set-fontset-font t 'emoji (font-spec :family "Noto Emoji") nil 'append)) ;; Don't advertise gnu on startup (setq inhibit-startup-echo-area-message "noa") ;; #userfreedom ;; startup.el does not provide itself, cannot be used with use-package (setopt inhibit-startup-screen t) ;; Help with keybindings (use-package which-key :hook (after-init . which-key-mode)) (use-package mouse :custom (mouse-yank-at-point t) (mouse-drag-and-drop-region nil) ;; Allow for slightly sloppier clicking. (mouse-prefer-closest-glyph t)) ;; Shift click to select region with the mouse (https://christiantietze.de/posts/2022/07/shift-click-in-emacs-to-select/) (global-set-key (kbd "S-") #'mouse-set-mark) ;; Doesn't work great with a proportional font but could still be useful. (global-set-key (kbd "C-S-") #'mouse-drag-region-rectangle) ;;; Minibuffer ;; It is often useful to be able to run a command while i am already in the process of running a command in the minibuffer. This is rarely two extended commands; usually it is completion. (use-package savehist :hook (after-init . savehist-mode)) (use-package mb-depth :hook (after-init . minibuffer-depth-indicate-mode)) (use-package vertico :ensure t :hook (after-init . vertico-mode) :custom (vertico-resize nil) ;; don't shrink when there are less candidates ) (use-package vertico-directory :after vertico ;; If i type ~/ etc in a find-file prompt, get rid of the preceding directory names for a cleaner look. :hook (rfn-eshadow-update-overlay . vertico-directory-tidy)) ;; Taken from configuration for the vertico stack: ;; Add prompt indicator to `completing-read-multiple'. ;; We display [CRM], 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) ;; Annotations for completing-read (use-package marginalia :defer t :ensure t :hook (after-init . marginalia-mode) ;; :custom ;; (marginalia-mode t) ) ;; Basic matches candidates with the same text before the point, and the text after the point as a substring. ;; Orderless takes any number of space separated components and displays candidates that much every component in any order. (use-package orderless :after minibuffer :ensure t :custom (completion-styles '(orderless basic)) (completion-category-overrides '((file (styles basic partial-completion))))) (use-package consult :defer t ;; :commands (consult-complete-in-region) :bind ( ([remap switch-to-buffer] . consult-buffer) ([remap yank-pop] . consult-yank-pop) ([remap goto-line] . consult-goto-line) ;; ([remap imenu] . consult-imenu) ;; ([remap info] . consult-info) ([remap repeat-complex-command] . consult-complex-command) ([remap bookmark-jump] . consult-bookmark) :map comint-mode-map ([remap comint-history-isearch-backward-regexp] . consult-history)) ;; :custom ;; Use consult for completion at point ;; (completion-in-region-function 'consult-completion-in-region) ) ;;; Dictionary ;; Emacs has built in support for interfacing with dictd. With dictd and some dictionaries installed on debian, this works fine out of the box. ;; Unfortunately, dictionaries in this format tend to be hard wrapped and there isn't a lot of coverage outside of English. (use-package dictionary :custom (dictionary-search-interface 'help dictionary-default-dictionary "*")) ;;; Offline web browsing ;; Despite the name, eww is a delight to use for text-heavy websites. (use-package browse-url :disabled t :defer t :custom (browse-url-browser-function 'browse-url-default-browser) (browse-url-secondary-browser-function 'browse-url-default-browser)) (use-package shr :defer t :custom (shr-use-colors nil) ;; I like the colours i already have set (shr-max-width nil) ;; Don't insert hard linebreaks to wrap paragraphs (shr-bullet " • ") (shr-discard-aria-hidden t)) (use-package eww :defer t :custom ;; Better buffer naming for eww buffers. See the help for a function which gives the page title and the url. (eww-auto-rename-buffer 'title)) ;; Goto address mode makes urls and email address in a buffer clickable. I want these clickable links to look like links, because that's what they are. The two mouse face variables are what face is used on hover, which at the moment i ignore. It might also be worth setting them to 'highlight. (use-package goto-addr :hook (after-init . global-goto-address-mode)) ;;; Programming (use-package flymake :hook (prog-mode . flymake-mode)) ;; If i write a script, i will always run chmod +x after saving it. This command means i don't have to do that. (add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p) (use-package sly :disabled t :if (executable-find "sbcl") :ensure t :commands 'sly :custom ((inferior-lisp-program (executable-find "sbcl")) (common-lisp-hyperspec-root "file:///usr/share/doc/hyperspec/")) :mode ((rx (or ".lisp" ".cl") eos) . common-lisp-mode)) (use-package ediff :defer t :custom (ediff-window-setup-function 'ediff-setup-windows-plain) (ediff-split-window-function 'split-window-horizontally)) (use-package elastic-indent :disabled t :hook (prog-mode . elastic-indent-mode)) ;; Change the width of tab characters within lines to make subsequent lines form a table-like layout. (use-package elastic-table :disabled t :hook (prog-mode-hook . elastic-table-mode)) ;; Indenting lisp with tabs is horrible. H/t acdw (defvar space-indent-modes '(emacs-lisp-mode lisp-interaction-mode lisp-mode scheme-mode python-mode) "Modes to indent with spaces, not tabs.") (add-hook 'prog-mode-hook (defun indent-tabs-mode-maybe () (setq indent-tabs-mode (if (apply #'derived-mode-p space-indent-modes) nil t)))) ;; Scroll along with text in compilation mode, and stop scrolling at the first error. (use-package compile :defer t :custom (compilation-scroll-output 'first-error)) ;;; Writing prose ;; Use abbrev as a simple autocorrect mode. M-x list-abbrevs for stats. ;; Check ~/.config/emacs/abbrev_defs to update any mistakes (use-package abbrev :hook (text-mode . abbrev-mode)) (use-package flyspell :hook (text-mode . flyspell-mode) :hook (prog-mode . flyspell-prog-mode) :custom (flyspell-abbrev-p t)) ;; Unfill commands (defun unfill-paragraph () "Takes a multi-line paragraph and makes it into a single line of text." (interactive) (let ((fill-column (point-max))) (fill-paragraph nil))) (global-set-key (kbd "M-Q") #'unfill-paragraph) (use-package simple :bind (([remap capitalize-word] . capitalize-dwim) ([remap downcase-word] . downcase-dwim) ([remap upcase-word] . upcase-dwim))) (use-package wc-goal-mode) ;;; Reading mail ;; I keep coming back to rmail, despite its many, many warts. ;; Currently i fetch mail into Spool.mbox with fdm, then rmail moves it into Inbox.mbox. (use-package rmail :commands (rmail) :custom (rmail-primary-inbox-list '("~/Documents/Library/Mail.new/Spool.mbox")) (rmail-file-name "~/Documents/Library/Mail.new/Inbox.mbox") (rmail-user-mail-address-regexp (rx "noa@noa.pub")) (rmail-mime-prefer-html nil) (rmail-mime-attachment-dirs-alist '(("" "~/media"))) (rmail-displayed-headers (rx bol (or "To" "Cc" "From" "Date" "Subject") ":")) (rmail-delete-after-output t) (rmail-default-file "~/Documents/Library/Mail/Archive.mbox") (mail-dont-reply-to-names rmail-user-mail-address-regexp) (rmail-display-summary nil) (rmail-redisplay-summary t) (rmail-summary-line-count-flag nil) ;; :hook ;; ((rmail-show-message . visual-line-fill-column-mode)) ) (use-package mu4e :commands (mu4e) :custom (mu4e-maildir "~/Library/Fastmail") (mu4e-get-mail-command "mbsync -a -c ~/.config/mbsyncrc") (mu4e-change-filenames-when-moving t) (mu4e-search-skip-duplicates t) (mu4e-view-show-images t) (mu4e-view-show-addresses t) (mu4e-compose-format-flowed nil) (mu4e-refile-folder "/Archive") (mu4e-sent-folder "/Sent") (mu4e-drafts-folder "/Drafts") (mu4e-trash-folder "/Trash") (mu4e-use-fancy-chars nil) (mu4e-bookmarks '((:name "Unread messages" :query "flag:unread AND NOT flag:trashed AND NOT maildir:/Trash AND NOT maildir:/Spam" :key ?u) (:name "Today's messages" :query "date:today..now" :hide-unread t :key ?t) (:name "Last 7 days" :query "date:7d..now" :hide-unread t :key ?w))) (mu4e-maildir-shortcuts '((:name "Inbox" :maildir "/INBOX" :key ?i) (:name "Sent" :maildir "/Sent" :key ?s) (:name "Trash" :maildir "/Trash" :key ?d) (:name "Spam" :maildir "/Spam" :key ?S))) (mu4e-confirm-quit nil) (read-mail-command 'mu4e) :config (fset 'noa/mu4e-trash-message "md") (define-key mu4e-headers-mode-map (kbd "d") 'noa/mu4e-trash-message) (define-key mu4e-view-mode-map (kbd "d") 'noa/mu4e-trash-message) ) ;;; Sending mail (use-package message :defer t :init (defun noa/message-default-headers () (format "Fcc: ~/Documents/Library/Mail/outbox/%s.mbox" (format-time-string "%Y-%m"))) :custom (message-default-headers #'noa/message-default-headers) (message-fill-column nil) ;; Don't enable auto-fill (user-full-name "noa") (user-mail-address "noa@noa.pub") (message-signature t) (message-signature-file "~/.config/signature") (message-send-mail-function 'message-send-mail-with-sendmail) (sendmail-program (executable-find "msmtpq")) (message-sendmail-extra-arguments '("--read-envelope-from")) (message-sendmail-envelope-from 'header) (message-kill-buffer-on-exit t) (message-sendmail-f-is-evil t) (message-forward-as-mime t) (message-interactive t) (message-auto-save-directory "~/Documents/Library/Mail/drafts")) ;;; Bibliography management (use-package citar :ensure t :commands (citar-insert-citation citar-open-files citar-open-notes) :custom (citar-bibliography '("~/Documents/References.bib")) (citar-library-paths '("~/Library/Papers")) (citar-notes-paths '("~/Documents/Notes"))) (use-package citar-org :after oc citar :custom (org-cite-insert-processor 'citar) (org-cite-follow-processor 'citar) (org-cite-activate-processor 'citar)) (use-package citar-embark :after citar embark :ensure t :custom (citar-at-point-function 'embark-act) :hook ((org-mode . citar-embark-mode))) (use-package bibtex :defer t :custom (bibtex-dialect 'biblatex) (bibtex-entry-format '(opts-or-alts page-dashes required-fields numerical-fields whitespace last-comma delimiters unify-case sort-fields)) (bibtex-field-delimiters 'braces) (bibtex-entry-delimiters 'braces) ;; Use one name, appending ETAL if there is more than one author ;; The name should be all caps (bibtex-autokey-names 1) (bibtex-autokey-name-case-convert-function 'upcase) (bibtex-autokey-additional-names "ETAL") ;; Follow this with the full year ;; Probably don't need more than two digits actually (bibtex-autokey-name-year-separator "-") (bibtex-autokey-year-length 4) ;; Follow this with initials of the title (bibtex-autokey-year-title-separator "-") (bibtex-autokey-titlewords t) (bibtex-autokey-titleword-length -1) ;; One character (bibtex-autokey-titleword-separator "") (bibtex-autokey-titleword-case-convert-function 'upcase)) (use-package biblio :disabled t :ensure t :defer t :custom (biblio-bibtex-use-autokey t) (biblio-download-directory "~/Documents/Library/Papers")) ;;; Taking notes (use-package org :defer t :init :custom (org-modules '(ol-doi ol-w3m ol-bbdb ol-bibtex ol-docview ol-gnus ol-info ol-irc ol-mhe ol-rmail ol-eww org-habit)) (org-clock-sound "~/Library/Ringtones/Loud ding.wav") (org-imenu-depth 3) (org-tags-column 0) (org-agenda-files '("~/Documents/Agenda.org")) (org-refile-targets '((org-agenda-files :maxlevel . 3))) (org-refile-use-outline-path 'file) (org-outline-path-complete-in-steps nil) (org-refile-allow-creating-parent-nodes 'confirm) (org-replace-disputed-keys t) (org-support-shift-select 'always) ;; Still refuses to fix on timestamps :/ (org-log-done 'time) (org-pretty-entities t) (org-startup-with-inline-images t) (org-image-actual-width '(300)) (org-ellipsis " [+]") (org-insert-heading-respect-content t) (org-fontify-done-headline nil) (org-fontify-todo-headline nil) (org-fontify-whole-heading-line t) (org-src-fontify-natively t) (org-src-tab-acts-natively t) (org-edit-src-content-indentation 0) (org-use-sub-superscripts '{}) (org-export-preserve-breaks t) (org-agenda-remove-tags t) (org-agenda-prefix-format '((agenda . " %?-14t% s") (todo . "") (tags . " %i %-12:c") (search . " %i %-12:c"))) (org-agenda-scheduled-leaders '("Scheduled: " "Sched.%2dx: ")) (org-agenda-deadline-leaders '("Deadline: " "In %3d d.: " "%2d d. ago: ")) (org-agenda-time-grid '((daily today require-timed remove-match) nil "" "")) (org-agenda-show-current-time-in-grid nil) (org-agenda-format-date "%n%Y-%m-%d %A") (org-agenda-span 'day) (org-agenda-custom-commands '(("n" "Agenda and unassigned to-dos" ((agenda "") (alltodo "" ((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done 'scheduled 'deadline)) (org-agenda-overriding-header "\nDrifting tasks"))))))) ;; ((agenda "") ;; (tags-todo "-SCHEDULED={.+}"))))) (org-agenda-skip-scheduled-if-deadline-is-shown t) (org-treat-insert-todo-heading-as-state-change t) (org-log-into-drawer t) :custom-face (org-table ((t (:inherit fixed-pitch)))) (org-date ((t (:inherit fixed-pitch :underline t)))) ;; :bind (:map org-mode-map ;; ([remap imenu] . consult-org-heading)) ) (defun log-todo-next-creation-date (&rest ignore) "Log NEXT creation time in the property drawer under the key 'ACTIVATED'" (when (and (string= (org-get-todo-state) "NEXT") (not (org-entry-get nil "ACTIVATED"))) (org-entry-put nil "ACTIVATED" (format-time-string "[%Y-%m-%d]")))) (add-hook 'org-after-todo-state-change-hook #'log-todo-next-creation-date) (use-package oc :defer t :after org :custom (org-cite-global-bibliography '("~/Documents/References.bib")) (org-cite-export-processors '((beamer csl) (latex csl) (t csl)))) (use-package oc-csl :defer t :after oc :custom (org-cite-csl-styles-dir "~/Library/Citation styles")) ;; Support chinese in latex pdf export (require 'ox-latex) (add-to-list 'org-latex-classes '("ctexart" "\\documentclass{ctexart}" ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}") ("\\paragraph{%s}" . "\\paragraph*{%s}") ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))) (add-to-list 'org-latex-classes '("ctexbook" "\\documentclass[11pt]{ctexbook}" ("\\part{%s}" . "\\part*{%s}") ("\\chapter{%s}" . "\\chapter*{%s}") ("\\section{%s}" . "\\section*{%s}") ("\\subsection{%s}" . "\\subsection*{%s}") ("\\subsubsection{%s}" . "\\subsubsection*{%s}"))) ;; Not working how i want (use-package org-capture :bind ("C-c c" . org-capture) :custom (org-capture-templates '(("d" "Today's diary" plain (file+olp+datetree "~/Documents/Diary.org") "%?") ("t" "Todo" entry (file "~/Documents/Agenda.org") "* TODO %?\n%U\n%a\n%i") ("s" "Source" entry (file "~/Documents/Sources.org") "* TODO %?\n%U\n#+begin_src bibtex :tangle References.bib\n#+end_src\n") ("a" "Appointment" entry (file"~/Documents/Agenda.org") "* %?\n%T\n" :time-prompt t) ))) ;; See https://llazarek.github.io/blog/2018/07/organization-with-org-mode.html ;; for configuring the org agenda (use-package org-agenda :bind ("C-c a" . org-agenda)) ;; Experimental website generator (use-package ox-hugo :disabled t :ensure t :after ox) ;; Markdown (use-package markdown-mode :ensure t :mode ("\\.\\(?:md\\|markdown\\|mkd\\|mdown\\|mkdn\\|mdwn\\)\\'" . markdown-mode) :custom (markdown-disable-tooltip-prompt t) ; When inserting a link, only prompt for url and link text (markdown-enable-html nil) ; I don't believe markdown should have html (markdown-hide-urls t) ; Make inline urls look a bit neater (markdown-url-compose-char ?~) (markdown-max-image-size '(640 . 480)) ;; :hook ((markdown-mode . noa/set-buffer-name-to-markdown-title)) ) ;; Org node (use-package org-node :after org :ensure t :bind (("M-s M-f" . org-node-find) ("M-s M-i" . org-node-insert-link) ("M-s M-s" . org-node-seq-dispatch)) :config (org-node-cache-mode) :custom (org-node-ask-directory "~/Documents/howm/") (org-node-datestamp-format "%Y-%m-%d-%H%M%S") (org-node-slug-fn (lambda (title) "")) (org-node-seq-defs (list (org-node-seq-def-on-filepath-sort-by-basename "d" "Dailies" "~/Documents/howm/Diary/" nil t)))) ;;; System administration (add-hook 'eshell-mode-hook #'eat-eshell-mode) (use-package proced :commands (proced) :custom (proced-auto-update-flag t) (proced-show-remote-processes t)) (use-package apt-utils :disabled t :commands (apt-utils-search)) ;;; File management (use-package dired :commands (dired dired-jump) :custom (dired-recursive-deletes 'always) ;; Don't ask me things (dired-recursive-copies 'always) ;; Don't ask me things (dired-dwim-target t) ;; Synergy between two dired windows ;; I have quite a bit of storage and also make bad decisions regularly ;; People who have used computers in the last forty years or so will likely be familiar with this innovation. (delete-by-moving-to-trash t) ;; -l is required by dired ;; -a to also show dotfiles ;; -v to sort numbers properly ;; -F to visually distinguish different types of file ;; -h for human readable file sizes ;; I won't explain the meaning of the long flag. (dired-listing-switches "-alvFh --group-directories-first") ;; By default, don't show dired details :hook ((dired-mode . dired-hide-details-mode) (dired-mode . hl-line-mode)) ) (use-package image-dired :defer t :custom (image-dired-thumbnail-storage 'standard)) (use-package recentf :hook (after-init . recentf-mode) :bind (("C-x C-r" . recentf))) (use-package bookmark :custom (bookmark-save-flag 1)) ;; Remember my position in files (use-package saveplace :hook (after-init . save-place-mode)) ;;; Window and buffer navigation (use-package isearch :bind (:map isearch-mode-map ("" . noa/isearch-done-goto-start) ; always put point at the beginning of the match ("C-g" . isearch-cancel) ; always quit on C-g ("C-o" . isearch-occur)) :init (defun noa/isearch-done-goto-start (&optional nopush edit) "End isearch at the start of the match." (interactive) (funcall #'isearch-done nopush edit) (when isearch-forward (goto-char isearch-other-end))) :custom (isearch-lax-whitespace t) (isearch-lazy-count t) (isearch-allow-motion t) (isearch-repeat-on-direction-change t) (isearch-wrap-pause 'no)) (use-package avy :ensure t :bind (("M-j" . avy-goto-char-timer) :map isearch-mode-map ("M-j" . avy-isearch)) :custom (avy-keys (number-sequence ?a ?z)) (avy-timeout-seconds 0.2)) ;; Make window management commands easier to press (use-package ace-window :ensure t :bind ([remap other-window] . ace-window)) ;; Better buffer naming (use-package uniquify :custom (uniquify-after-kill-buffer-p t) (uniquify-buffer-name-style 'forward) (uniquify-ignore-buffers-re "^\\*") (uniquify-separator "/")) ;;; Embark (use-package embark :ensure t :bind (("C-." . embark-act) ([remap describe-bindings] . embark-bindings)) :init (setq prefix-help-command #'embark-prefix-help-command)) (use-package embark-consult :ensure t :after (embark consult) :hook (embark-collect-mode . consult-preview-at-point-mode)) ;;; Reading pdfs and epub files (use-package pdf-tools :ensure t :when (display-graphic-p) :magic ("%PDF" . pdf-view-mode) :config (pdf-tools-install :no-query)) ;;; Music (use-package emms :disabled t :config (require 'emms-setup) (require 'emms-info-metaflac) (require 'emms-info-mp3info) (require 'emms-info-ogginfo) (emms-all) (emms-default-players) (setq emms-source-file-default-directory "~/Music/") (setq emms-browser-covers 'emms-browser-cache-thumbnail-async) (setq emms-playlist-default-major-mode 'emms-playlist-mode) (setq emms-track-description-function 'emacs-init/emms-track-description) (add-to-list 'emms-info-functions 'emms-info-libtag) (add-to-list 'emms-info-functions 'emms-info-mp3info) (add-to-list 'emms-info-functions 'emms-info-ogginfo) (global-set-key (kbd "C-c e p") 'emms-playlist-mode-go) (global-set-key (kbd "C-c e r") 'emms-toggle-repeat-playlist) (global-set-key (kbd "C-c e s") 'emms-toggle-random-playlist)) ;;; Useful functions (defun noa/yank-buffer-file-name () (interactive) (when (buffer-file-name) (kill-new (file-name-nondirectory (buffer-file-name))))) (defun noa/twenty-five-minutes () (interactive) (org-timer-set-timer 25)) ;; Insert date and time at point ;; Sometimes it's useful to be able to add a timestamp. I use this for notes. Use the same binding as notepad.exe. (defun noa/insert-date (&optional time) "Insert the current date into the buffer. With prefix argument TIME, also add the current time." (interactive "P") (if time (insert (format-time-string "[%Y-%m-%d %H:%M]")) (insert (format-time-string "[%Y-%m-%d]")))) (defun noa/insert-date-time () (interactive) (noa/insert-date t)) (global-set-key (kbd "") #'noa/insert-date-time) ;; Insert a star rating into the buffer. Currently doesn't check bounds. (defun noa/star-rating (rating) "Insert a star rating (★★★☆☆) into the buffer at point as RATING out of five stars." (interactive "nRating: ") (insert-char ?★ rating) (insert-char ?☆ (- 5 rating))) (defun snarf-song (url) (interactive "sYoutube url:") (async-shell-command (concat "yt-dlp -x --audio-format=mp3 -o " (shell-quote-argument "~/Downloads/%(title)s [%(id)s].%(ext)s") " " (shell-quote-argument url)))) ;;; Tramp (use-package tramp :defer t :custom (tramp-inline-compress-start-size 1000) (tramp-verbose 3)) ;; (add-to-list 'tramp-remote-path 'tramp-own-remote-path) ;;; Window management ;; Don't override display actions when i manually initiate a buffer switch. (use-package window :custom ;; My computer has a small screen, so i find that it's more beneficial for me to split the frame into columns, so i get more context. However, splitting in this way only gives me a (window-width) of 61, so emacs will always split into vertically stacked windows. By setting this to 80, the first split should always be vertical. (split-width-threshold 80) (switch-to-buffer-obey-display-actions t) ;; C-l from top to bottom (recenter-positions '(top middle bottom))) ;; Easily grabbable bars for resizing windows (use-package frame :hook (after-init . window-divider-mode) :custom (window-divider-default-places t) (window-divider-default-right-width 1) (window-divider-default-bottom-width 1) :custom-face (window-divider ((t (:inherit vertical-border))))) ;; Update the calendar. We want weeks to start on a monday, the first day of the week. Holidays should be highlighted, and the date format should put the year first. (use-package calendar :defer t :custom (calendar-week-start-day 1) (calendar-date-style 'iso) :config ;; Ensure the calendar always displays at the bottom of the screen, rather than wrapping weirdly and looking bad when it shows up in a side window. (add-to-list 'display-buffer-alist '("\\*Calendar\\*" (display-buffer-in-side-window) (side . bottom)))) ;; Squeeze text into a more legible sliver. (use-package visual-fill-column :ensure t :custom (visual-fill-column-enable-sensible-window-split t) (visual-fill-column-fringes-outside-margins nil) ; Keep fringes on the inside so relevant icons are in the right place (visual-fill-column-center-text t) :hook ( ;; (text-mode . visual-fill-column-mode) (eww-after-render . visual-line-fill-column-mode)) ) ;; (advice-add 'text-scale-adjust :after #'visual-fill-column-adjust) ;; Doesn't indent nicely with proportional fonts ;; (use-package adaptive-wrap :ensure t) ;; (add-hook 'visual-fill-column-mode-hook #'visual-wrap-prefix-mode) (use-package simple :hook ((text-mode . visual-line-mode) (after-init . auto-save-mode)) :bind ([remap kill-buffer] . kill-current-buffer) :custom (save-interprogram-paste-before-kill t) ;; Hide commands in M-x which do not work in the current mode. (read-extended-command-predicate 'command-completion-default-include-p) ;; My brain is not ready for advanced emacs undo (undo-no-redo t)) (use-package help :defer t :custom ;; When opening source files from a help window, use the same window (help-window-keep-selected t)) (use-package emacs :custom (window-combination-resize t) (window-resize-pixelwise t) (frame-resize-pixelwise t) (shell-file-name "/bin/sh") ;; yes-or-no-p uses y-or-no-p, and read-answer accepts single key press answers (use-short-answers t) ;; Use a bar cursor and blink it and don't stop blinking it. (cursor-type 'bar) ;; Support opening new minibuffers from inside existing minibuffers. (enable-recursive-minibuffers t) ;; Whether to drop into the debugger on any error. This seems cool, but in practice is a bit annoying. (debug-on-error nil) (create-lockfiles nil) (auto-save-interval 6) ;; every six keystrokes (auto-save-timeout 5) ;; every 5 seconds (auto-save-no-message t) ;; Always give me a line of text when i'm near the edges of the screen (scroll-margin 2)) (use-package files :custom (view-read-only t) (confirm-kill-emacs 'y-or-n-p) ;; We are on a unix system, so it makes sense to end files in the unix system way. I'm surprised this isn't the default. (require-final-newline t) (remote-file-name-inhibit-locks t) (remote-file-name-inhibit-auto-save t) (remote-file-name-inhibit-auto-save-visited t) (backup-directory-alist '(("." . "~/.config/emacs/backups/"))) (make-backup-files nil) (backup-by-copying t) (version-control t) ;; this will auto save to the current file (auto-save-visited-mode t) (auto-save-visited-interval 0.1) (save-silently t) ;; don't message when saving ) ;; Disable disabled commands (setq disabled-command-function nil) ;; Don't save changes in the customize interface (use-package cus-edit :custom (custom-unlispify-tag-names nil) (custom-file (make-temp-file "custom"))) ;; Emacs server ;; (unless (server-running-p) (server-start))) ;; Better support for long lines. (use-package so-long :hook (after-init . global-so-long-mode)) ;; Automatically revert buffers when they change on disk. This doesn't apply to tramp. (use-package autorevert :hook (after-init . global-auto-revert-mode) :custom (global-auto-revert-non-file-buffers t)) ;; Zap up to char (global-set-key (kbd "M-z") 'zap-up-to-char) (setenv "PAGER" "cat") (setenv "TERM" "dumb") (setenv "NO_COLOR") (setenv "GPG_AGENT_INFO" nil) ;;; Cleanup ;; (profiler-stop)