#+TITLE: KyleKrein's GNU Emacs Config #+STARTUP: showeverything #+OPTIONS: toc:2 #+PROPERTY: header-args:emacs-lisp :lexical t * Table of contents :toc: - [[#important-programs-to-load-first][IMPORTANT PROGRAMS TO LOAD FIRST]] - [[#recent-files][Recent Files]] - [[#keybindings][Keybindings]] - [[#focus-new-windows][Focus new windows]] - [[#support-functions][Support functions]] - [[#emacs-function-launcher][Emacs function launcher]] - [[#copy-to-clipboard][Copy to clipboard]] - [[#detect-wsl][Detect WSL]] - [[#notifications][Notifications]] - [[#app-launcher][App Launcher]] - [[#standalone-run][Standalone run]] - [[#backup-files-tilda-files][Backup files (tilda files)]] - [[#dashboard][Dashboard]] - [[#diminish][Diminish]] - [[#dired-file-manager][DIRED (File manager)]] - [[#emoji][Emoji]] - [[#copy-to-clipboard-1][Copy to clipboard]] - [[#standalone-emoji-picker][Standalone emoji picker]] - [[#fonts][Fonts]] - [[#sane-defaults][Sane defaults]] - [[#battery-info][Battery info]] - [[#epub-support-reader-in-emacs][Epub support (Reader in Emacs)]] - [[#magit][MAGIT]] - [[#minibuffer-escape][Minibuffer escape]] - [[#modeline][Modeline]] - [[#mini-echo-modeline][Mini-echo modeline]] - [[#pixel-scrolling][Pixel-scrolling]] - [[#native][Native]] - [[#pdf-tools][PDF Tools]] - [[#rainbow-delimiters][RAINBOW DELIMITERS]] - [[#calendar][Calendar]] - [[#org-mode][ORG MODE]] - [[#enter-folows-link][Enter folows link]] - [[#enabling-table-of-contents][Enabling Table of Contents]] - [[#modern-org-mode][Modern Org Mode]] - [[#org-level-headers][Org Level Headers]] - [[#source-code-block-tag-expansion][Source Code Block Tag Expansion]] - [[#insert-pictures][Insert pictures]] - [[#org-roam][ORG ROAM]] - [[#org-roam-itself][Org Roam itself]] - [[#org-roam-ui][Org Roam UI]] - [[#inbox][Inbox]] - [[#capture-a-task-directly-into-project][Capture a task directly into Project]] - [[#org-agenda][Org Agenda]] - [[#todos-only-from-projects][TODOs only from Projects]] - [[#todos-in-today][Todos in Today]] - [[#auto-refresh-agenda-view][Auto refresh agenda view]] - [[#org-timeblock][org-timeblock]] - [[#notifications-1][Notifications]] - [[#rainbow-mode][RAINBOW MODE]] - [[#shells-and-terminals][SHELLS AND TERMINALS]] - [[#eshell][Eshell]] - [[#vterm][Vterm]] - [[#vterm-toggle][Vterm-Toggle]] - [[#sudo-edit][SUDO EDIT]] - [[#language-support][Language support]] - [[#nix][Nix]] - [[#nerd-icons][Nerd Icons]] - [[#nerd-icons-completion][Nerd Icons Completion]] - [[#buffer-move][Buffer Move]] - [[#completions][Completions]] - [[#vertico][Vertico]] - [[#orderless][Orderless]] - [[#fido-mode][Fido-mode]] - [[#descriptions][Descriptions]] - [[#communication][Communication]] - [[#telegram][Telegram]] - [[#theme][Theme]] - [[#theme-loading][Theme loading]] - [[#transparency][Transparency]] - [[#which-key][WHICH-KEY]] - [[#emacs-on-android][Emacs on Android]] - [[#fonts-1][Fonts]] - [[#settings][Settings]] - [[#emacs-on-wsl][Emacs on WSL]] - [[#clipboard-fix][Clipboard fix]] * IMPORTANT PROGRAMS TO LOAD FIRST ** Recent Files #+begin_src emacs-lisp (recentf-mode t) #+end_src ** Keybindings #+begin_src emacs-lisp (global-set-key [remap list-buffers] 'ibuffer) (global-set-key (kbd "M-o") 'other-window) ;;(windmove-default-keybindings) ;; move between windows with S-, S-, S-, S- #+end_src ** Focus new windows Found this [[https://emacs.stackexchange.com/questions/21770/automatically-switch-focus-to-new-window][here]] and [[https://github.com/snackon/Witchmacs#creating-a-new-window-switches-your-cursor-to-it][here]] #+begin_src emacs-lisp (defun split-and-follow-horizontally () (interactive) (split-window-below) (balance-windows) (other-window 1)) (defun split-and-follow-vertically () (interactive) (split-window-right) (balance-windows) (other-window 1)) (use-package emacs :bind (:map ctl-x-map ("2" . split-and-follow-horizontally) ("3" . split-and-follow-vertically)) :custom (info-lookup-other-window-flag t) (help-window-select t "Switch to help buffers automatically")) ;; Auto-select new Info buffer window when it’s created. (advice-add 'info-lookup :after (lambda (&rest _) (when-let (window (get-buffer-window "*info*")) (select-window window)))) ;; Auto-select new window after splitting. Splitting commands almost ;;,all use `split-window’, so advice the function for auto selection. (advice-add 'split-window :after (lambda (&rest _) (select-window (get-lru-window)))) #+end_src * Support functions ** Emacs function launcher Launches emacs function as a window #+begin_src emacs-lisp (defun create-launcher-frame () (make-frame '((name . "emacs-run-launcher") (minibuffer . only) (fullscreen . 0) (undecorated . t) (internal-border-width . 10) (width . 80) (height . 11)))) (defun emacs-run-launcher (func) "Create and select a frame called emacs-run-launcher which consists only of a minibuffer and has specific dimensions. Runs func on that frame, which is an emacs command that prompts you to select something and open it dmenu like behaviour. Delete the frame after that command has exited" (interactive) (with-selected-frame (make-frame '((name . "emacs-run-launcher") (minibuffer . only) (fullscreen . 0) ; no fullscreen (undecorated . t) ; remove title bar ;;(auto-raise . t) ; focus on this frame ;;(tool-bar-lines . 0) ;;(menu-bar-lines . 0) (internal-border-width . 10) (width . 80) (height . 11))) (unwind-protect (funcall func) (delete-frame)))) #+end_src ** Copy to clipboard Copies to both kill ring and system clipboard #+begin_src emacs-lisp ;;(setq select-enable-primary t) (defun kylekrein/copy-to-clipboard (text) (with-temp-buffer (insert text) (copy-region-as-kill (point-min) (point-max)) (clipboard-kill-region (point-min) (point-max)))) #+end_src ** Detect WSL #+begin_src emacs-lisp (defun kylekrein/detect-wsl () (and (eq system-type 'gnu/linux) (file-exists-p "/proc/sys/fs/binfmt_misc/WSLInterop"))) #+end_src ** Notifications *** Alert package #+begin_src emacs-lisp (use-package alert :ensure t ) #+end_src *** Android notifications Found the code [[https://www.reddit.com/r/emacs/comments/18xvtns/emacs_notifications_on_linux_and_android/][here]] #+begin_src emacs-lisp (require 'alert) (defun alert-android-notifications-notify (info) (unless (eq system-type 'android) (error "Android notifications are only supported on Android systems")) "Send INFO using android-notifications-notify." (let ((title (or (plist-get info :title) "Org Alert Reminder")) (body (or (plist-get info :message) "")) (urgency (let ((severity (plist-get info :severity))) (cond ((eq severity 'urgent) 'critical) ((eq severity 'high) 'critical) ((eq severity 'moderate) 'normal) ((eq severity 'low) 'low) ((eq severity 'trivial) 'low) (t 'normal)))) (icon (or (plist-get info :icon) alert-default-icon))) (android-notifications-notify :title title :body body :urgency urgency :icon icon ))) (alert-define-style 'android-notifications :title "Android Notifications" :notifier #'alert-android-notifications-notify ) #+end_src *** Windows Notifications Using [[https://github.com/gkowzan/alert-toast][Alert toast]] #+begin_src emacs-lisp (use-package alert-toast :ensure t :after alert) #+end_src *** Setting notification backend #+begin_src emacs-lisp (setq alert-default-style (cond ((eq system-type 'android) 'android-notifications) ((kylekrein/detect-wsl) 'toast) (t 'libnotify))) #+end_src * App Launcher This code creates a menu to launch linux apps, that have Desktop entry. Code was taken from [[https://github.com/SebastienWae/app-launcher/blob/main/app-launcher.el][this awesome repo]] #+begin_src emacs-lisp (require 'xdg) (require 'cl-seq) (defcustom app-launcher-apps-directories (mapcar (lambda (dir) (expand-file-name "applications" dir)) (cons (xdg-data-home) (xdg-data-dirs))) "Directories in which to search for applications (.desktop files)." :type '(repeat directory)) (defcustom app-launcher--annotation-function #'app-launcher--annotation-function-default "Define the function that genereate the annotation for each completion choices." :type 'function) (defcustom app-launcher--action-function #'app-launcher--action-function-default "Define the function that is used to run the selected application." :type 'function) (defvar app-launcher--cache nil "Cache of desktop files data.") (defvar app-launcher--cache-timestamp nil "Time when we last updated the cached application list.") (defvar app-launcher--cached-files nil "List of cached desktop files.") (defun app-launcher-list-desktop-files () "Return an alist of all Linux applications. Each list entry is a pair of (desktop-name . desktop-file). This function always returns its elements in a stable order." (let ((hash (make-hash-table :test #'equal)) result) (dolist (dir app-launcher-apps-directories) (when (file-exists-p dir) (let ((dir (file-name-as-directory dir))) (dolist (file (directory-files-recursively dir ".*\\.desktop$")) (let ((id (subst-char-in-string ?/ ?- (file-relative-name file dir)))) (when (and (not (gethash id hash)) (file-readable-p file)) (push (cons id file) result) (puthash id file hash))))))) result)) (defun app-launcher-parse-files (files) "Parse the .desktop files to return usable informations." (let ((hash (make-hash-table :test #'equal))) (dolist (entry files hash) (let ((file (cdr entry))) (with-temp-buffer (insert-file-contents file) (goto-char (point-min)) (let ((start (re-search-forward "^\\[Desktop Entry\\] *$" nil t)) (end (re-search-forward "^\\[" nil t)) (visible t) name comment exec) (catch 'break (unless start (message "Warning: File %s has no [Desktop Entry] group" file) (throw 'break nil)) (goto-char start) (when (re-search-forward "^\\(Hidden\\|NoDisplay\\) *= *\\(1\\|true\\) *$" end t) (setq visible nil)) (setq name (match-string 1)) (goto-char start) (unless (re-search-forward "^Type *= *Application *$" end t) (throw 'break nil)) (setq name (match-string 1)) (goto-char start) (unless (re-search-forward "^Name *= *\\(.+\\)$" end t) (push file counsel-linux-apps-faulty) (message "Warning: File %s has no Name" file) (throw 'break nil)) (setq name (match-string 1)) (goto-char start) (when (re-search-forward "^Comment *= *\\(.+\\)$" end t) (setq comment (match-string 1))) (goto-char start) (unless (re-search-forward "^Exec *= *\\(.+\\)$" end t) ;; Don't warn because this can technically be a valid desktop file. (throw 'break nil)) (setq exec (match-string 1)) (goto-char start) (when (re-search-forward "^TryExec *= *\\(.+\\)$" end t) (let ((try-exec (match-string 1))) (unless (locate-file try-exec exec-path nil #'file-executable-p) (throw 'break nil)))) (puthash name (list (cons 'file file) (cons 'exec exec) (cons 'comment comment) (cons 'visible visible)) hash)))))))) (defun app-launcher-list-apps () "Return list of all Linux .desktop applications." (let* ((new-desktop-alist (app-launcher-list-desktop-files)) (new-files (mapcar 'cdr new-desktop-alist))) (unless (and (equal new-files app-launcher--cached-files) (null (cl-find-if (lambda (file) (time-less-p app-launcher--cache-timestamp (nth 5 (file-attributes file)))) new-files))) (setq app-launcher--cache (app-launcher-parse-files new-desktop-alist)) (setq app-launcher--cache-timestamp (current-time)) (setq app-launcher--cached-files new-files))) app-launcher--cache) (defun app-launcher--annotation-function-default (choice) "Default function to annotate the completion choices." (let ((str (cdr (assq 'comment (gethash choice app-launcher--cache))))) (when str (concat " - " (propertize str 'face 'completions-annotations))))) (defun app-launcher--action-function-default (selected) "Default function used to run the selected application." (let* ((exec (cdr (assq 'exec (gethash selected app-launcher--cache)))) (command (let (result) (dolist (chunk (split-string exec " ") result) (unless (or (equal chunk "%U") (equal chunk "%F") (equal chunk "%u") (equal chunk "%f")) (setq result (concat result chunk " "))))))) (call-process-shell-command command nil 0 nil))) ;;;###autoload (defun app-launcher-run-app (&optional arg) "Launch an application installed on your machine. When ARG is non-nil, ignore NoDisplay property in *.desktop files." (interactive) (let* ((candidates (app-launcher-list-apps)) (result (completing-read "Run app: " (lambda (str pred flag) (if (eq flag 'metadata) '(metadata (annotation-function . (lambda (choice) (funcall app-launcher--annotation-function choice)))) (complete-with-action flag candidates str pred))) (lambda (x y) (if arg t (cdr (assq 'visible y)))) t nil 'app-launcher nil nil))) (funcall app-launcher--action-function result))) #+end_src ** Standalone run This code snippet runs app launcher without emacs frame To use it, create a global keyboard shortcut with the following code ~emacsclient -cF "((visibility . nil))" -e "(emacs-run-app-launcher)~ #+begin_src emacs-lisp (defun emacs-run-app-launcher() (emacs-run-launcher 'app-launcher-run-app)) #+end_src * Backup files (tilda files) By default, Emacs creates automatic backups of files in their original directories, such “file.el” and the backup “file.el~”. This leads to a lot of clutter, so let’s tell Emacs to put all backups that it creates in the ~.emacs.d~ directory. #+begin_src emacs-lisp (setq backup-directory-alist '((".*" . "~/.emacs.d/tildafiles"))) #+end_src * Dashboard Emacs Dashboard is an extensible startup screen showing you recent files, bookmarks, agenda items and an Emacs banner. #+begin_src emacs-lisp (use-package dashboard :ensure t :after (:all nerd-icons org org-agenda org-roam) :init (setq initial-buffer-choice (lambda () (get-buffer-create dashboard-buffer-name))) (setq dashboard-week-agenda t) ;;(setq dashboard-filter-agenda-entry 'dashboard-no-filter-agenda) (setq dashboard-display-icons-p t) ; display icons on both GUI and terminal (setq dashboard-icon-type 'nerd-icons) ; use `nerd-icons' package (setq dashboard-set-heading-icons t) (setq dashboard-projects-backend 'project-el) (setq dashboard-set-file-icons t) (setq dashboard-banner-logo-title "Emacs Is More Than A Text Editor!") (setq dashboard-startup-banner 'logo) ;; use standard emacs logo as banner ;;(setq dashboard-startup-banner "/home/dt/.config/emacs/images/emacs-dash.png") ;; use custom image as banner (setq dashboard-center-content nil) ;; set to 't' for centered content (setq dashboard-items '((recents . 5) (agenda . 5 ) (bookmarks . 3) (projects . 3) (registers . 3))) (setq dashboard-startupify-list '(dashboard-insert-banner dashboard-insert-newline dashboard-insert-banner-title dashboard-insert-newline dashboard-insert-navigator dashboard-insert-newline dashboard-insert-init-info dashboard-insert-items dashboard-insert-newline dashboard-insert-footer)) (setq dashboard-navigator-buttons `(;; Line 1 ((,(nerd-icons-mdicon "nf-md-inbox" :height 1.1 :v-adjust 0.0) "To Inbox" "Capture to inbox" (lambda (&rest _) (kylekrein/org-roam-capture-inbox))) (,(nerd-icons-mdicon "nf-md-calendar" :height 1.1 :v-adjust 0.0) "Agenda" "View agenda" (lambda (&rest _) (org-agenda))) (,(nerd-icons-mdicon "nf-md-note" :height 1.1 :v-adjust 0.0) "Note" "Find a note" (lambda (&rest _) (org-roam-node-find)))) ;; Line 2 ((,(nerd-icons-mdicon "nf-md-sync" :height 1.1 :v-adjust 0.0) "Sync" "Sync org-roam and agenda" (lambda (&rest _) (org-roam-db-sync) (org-agenda-redo) (message "Org-Roam and Agenda synced!"))) (,(nerd-icons-mdicon "nf-md-calendar_today" :height 1.1 :v-adjust 0.0) "Today" "View today's tasks" (lambda (&rest _) (org-agenda nil "a")))))) :custom (dashboard-modify-heading-icons '((recents . "nf-oct-file_text") (bookmarks . "nf-oct-book"))) :config (dashboard-setup-startup-hook)) #+end_src * Diminish This package implements hiding or abbreviation of the modeline displays (lighters) of minor-modes. With this package installed, you can add ‘:diminish’ to any use-package block to hide that particular mode in the modeline. #+begin_src emacs-lisp (use-package diminish :ensure t) #+end_src * DIRED (File manager) #+begin_src emacs-lisp (use-package dired-open :ensure t :config (setq dired-open-extensions '(("gif" . "gwenview") ("jpg" . "gwenview") ("png" . "gwenview") ("mkv" . "vlc") ("mp4" . "vlc")))) (use-package peep-dired :ensure t :after dired ) #+end_src * Emoji ** Copy to clipboard #+begin_src emacs-lisp (use-package emojify :ensure t) (defun kylekrein/copy-emoji-to-clipboard() (interactive) (require 'emojify) ;;(let ((emoji (emoji--read-emoji))) ;;works without external package, but not so pretty (let ((emoji (emojify-completing-read "Copy Emoji: "))) (when emoji (kylekrein/copy-to-clipboard emoji) (message "Copied: %s" (current-kill 0 t))))) #+end_src ** Standalone emoji picker To use it, create a global keyboard shortcut with the following code ~emacsclient -cF "((visibility . nil))" -e "(emacs-run-emoji-picker)~ #+begin_src emacs-lisp (defun emacs-run-emoji-picker () "Create and select a frame called emacs-run-launcher which consists only of a minibuffer and has specific dimensions. Runs func on that frame, which is an emacs command that prompts you to select something and open it dmenu like behaviour. Delete the frame after some time after that command has exited in order to keep copied text in system clipboard" (interactive) (let ((launcher-frame (create-launcher-frame))) (with-selected-frame launcher-frame (kylekrein/copy-emoji-to-clipboard) (make-frame-invisible launcher-frame) (run-at-time "60 sec" nil (lambda (frame) (delete-frame frame)) launcher-frame)))) #+end_src * Fonts Defining the various fonts that Emacs will use. #+begin_src emacs-lisp (set-face-attribute 'default nil :font "JetBrains Mono" :height 110 :weight 'medium) (set-face-attribute 'variable-pitch nil :font "Ubuntu" :height 120 :weight 'medium) (set-face-attribute 'fixed-pitch nil :font "JetBrains Mono" :height 110 :weight 'medium) ;; Makes commented text and keywords italics. ;; This is working in emacsclient but not emacs. ;; Your font must have an italic face available. (set-face-attribute 'font-lock-comment-face nil :slant 'italic) (set-face-attribute 'font-lock-keyword-face nil :slant 'italic) ;; This sets the default font on all graphical frames created after restarting Emacs. ;; Does the same thing as 'set-face-attribute default' above, but emacsclient fonts ;; are not right unless I also add this method of setting the default font. (add-to-list 'default-frame-alist '(font . "JetBrains Mono-11")) ;; Uncomment the following line if line spacing needs adjusting. (setq-default line-spacing 0.12) #+end_src * Sane defaults The following settings are simple modes that are enabled (or disabled) so that Emacs functions more like you would expect a proper editor/IDE to function. #+begin_src emacs-lisp (electric-indent-mode -1) ;; Turn off the weird indenting that Emacs does by default. (electric-pair-mode 1) ;; Turns on automatic parens pairing ;; The following prevents <> from auto-pairing when electric-pair-mode is on. ;; Otherwise, org-tempo is broken when you try to ) and Redo (C-c ) for windows (setq sentence-end-double-space t) ;; Single space doesn't end a sentence #+end_src ** Battery info #+begin_src emacs-lisp (unless (equal "Battery status not available" (battery)) (display-battery-mode 1)) #+end_src * Epub support (Reader in Emacs) [[https://depp.brause.cc/nov.el/][Nov.el]] is recommended by [[https://www.masteringemacs.org/book][Author of Mastering Emacs book]] #+begin_src emacs-lisp (use-package nov :ensure t) (add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode)) #+end_src * MAGIT Magit is a full-featured git client for Emacs. #+begin_src emacs-lisp (use-package magit :ensure t) #+end_src * Minibuffer escape By default, Emacs requires you to hit ESC three times to escape quit the minibuffer. #+begin_src emacs-lisp (global-set-key [escape] 'keyboard-escape-quit) #+end_src * Modeline The modeline is the bottom status bar that appears in Emacs windows. While you can create your own custom modeline, why go to the trouble when Doom Emacs already has a nice modeline package available. For more information on what is available to configure in the Doom modeline, check out: [[https://github.com/seagle0128/doom-modeline][Doom Modeline]] #+begin_src emacs-lisp (use-package doom-modeline :ensure t :init (doom-modeline-mode 1) :config (setq doom-modeline-height 35 ;; sets modeline height doom-modeline-bar-width 5 ;; sets right bar width doom-modeline-persp-name nil ;; adds perspective name to modeline doom-modeline-time t ;; shows time doom-modeline-persp-icon nil)) ;; adds folder icon next to persp name #+end_src ** Mini-echo modeline The "global modeline" [[https://github.com/eki3z/mini-echo.el][package]], that disables pro buffer modeline and shows itself in echo area. #+begin_src emacs-lispp (use-package mini-echo :ensure t :init (mini-echo-mode) :custom ) #+end_src * Pixel-scrolling ** Native This doesn-t work for me for now, very laggy and inconsistent #+begin_src emacs-lispp ;;; Scrolling. ;; Good speed and allow scrolling through large images (pixel-scroll). ;; Note: Scroll lags when point must be moved but increasing the number ;; of lines that point moves in pixel-scroll.el ruins large image ;; scrolling. So unfortunately I think we'll just have to live with ;; this. (setq gc-cons-threshold #x40000000) (setq fast-but-imprecise-scrolling t) ; No (less) lag while scrolling lots. (setq jit-lock-defer-time 0) ; Just don't even fontify if we're still catching up on user input. (pixel-scroll-mode) (setq pixel-dead-time 0) ; Never go back to the old scrolling behaviour. (setq pixel-resolution-fine-flag t) ; Scroll by number of pixels instead of lines (t = frame-char-height pixels). (setq mouse-wheel-scroll-amount '(1)) ; Distance in pixel-resolution to scroll each mouse wheel event. (setq mouse-wheel-progressive-speed t) ; Progressive speed is too fast for me. #+end_src * PDF Tools [[https://github.com/vedang/pdf-tools][pdf-tools]] is a replacement of DocView for viewing PDF files inside Emacs. It uses the poppler library, which also means that ‘pdf-tools’ can by used to modify PDFs. I use to disable ‘display-line-numbers-mode’ in ‘pdf-view-mode’ because line numbers crash it. #+begin_src emacs-lisp (unless (string-equal system-type "android") ;; fails to compile (use-package pdf-tools :ensure t :defer t :commands (pdf-loader-install) :mode "\\.pdf\\'" ;:bind (:map pdf-view-mode-map ; ("j" . pdf-view-next-line-or-next-page) ; ("k" . pdf-view-previous-line-or-previous-page) ; ("C-=" . pdf-view-enlarge) ; ("C--" . pdf-view-shrink)) :init (pdf-loader-install) :config (add-to-list 'revert-without-query ".pdf")) (add-hook 'pdf-view-mode-hook #'(lambda () (interactive) (display-line-numbers-mode -1) (blink-cursor-mode -1) (doom-modeline-mode -1)))) #+end_src * RAINBOW DELIMITERS Adding rainbow coloring to parentheses. #+begin_src emacs-lisp (use-package rainbow-delimiters :ensure t :hook ((emacs-lisp-mode . rainbow-delimiters-mode) (clojure-mode . rainbow-delimiters-mode))) #+end_src * Calendar #+begin_src emacs-lisp (setq calendar-date-style "european") (setq calendar-week-start-day 1) ;;Line truncation (defun kylekrein/truncate-calendar-hook () "Turn line truncation on." (toggle-truncate-lines 1)) (add-hook 'calendar-mode-hook #'kylekrein/truncate-calendar-hook) ;;Current month is the first (add-hook 'calendar-initial-window-hook #'calendar-scroll-left) ;;Calendar in org agenda (setq org-agenda-include-diary t) (defadvice revert-buffer (after refresh-org-agenda-on-revert activate) (if (member (buffer-file-name (current-buffer)) org-agenda-files) (org-agenda-redo-all t))) #+end_src * ORG MODE ** Enter folows link #+begin_src emacs-lisp (setq org-return-follows-link t) #+end_src ** Enabling Table of Contents #+begin_src emacs-lisp (setq org-directory "~/Documents/org") (use-package toc-org :ensure t :commands toc-org-enable :init (add-hook 'org-mode-hook 'toc-org-enable)) #+end_src ** Modern Org Mode #+begin_src emacs-lisp ;;;; Better Looking Bullets (add-hook 'org-mode-hook 'org-indent-mode) (use-package org-bullets :ensure t) (add-hook 'org-mode-hook (lambda () (org-bullets-mode 1))) #+end_src ** Org Level Headers #+begin_src emacs-lisp (custom-set-faces '(org-level-1 ((t (:inherit outline-1 :height 1.45)))) '(org-level-2 ((t (:inherit outline-2 :height 1.35)))) '(org-level-3 ((t (:inherit outline-3 :height 1.30)))) '(org-level-4 ((t (:inherit outline-4 :height 1.25)))) '(org-level-5 ((t (:inherit outline-5 :height 1.20)))) '(org-level-6 ((t (:inherit outline-5 :height 1.15)))) '(org-level-7 ((t (:inherit outline-5 :height 1.10))))) #+end_src ** Source Code Block Tag Expansion Org-tempo is not a separate package but a module within org that can be enabled. Org-tempo allows for '-${slug}.org" "#+title: ${title}\n#+category: ${title}\n") :unnarrowed t) ("p" "project" plain "* Goals\n\n%?\n\n* Tasks\n\n** TODO Add initial tasks\n\n* Dates\n\n" :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n#+category: ${title}\n#+filetags: Project") :unnarrowed t)) ) (org-roam-dailies-capture-templates '(("d" "default" entry "* %<%I:%M %p>: %?" :if-new (file+head "%<%Y-%m-%d>.org" "#+title: %<%Y-%m-%d>\n")))) :bind (("C-c n l" . org-roam-buffer-toggle) ("C-c n f" . org-roam-node-find) ("C-c n i" . org-roam-node-insert) :map org-mode-map ("C-M-i" . completion-at-point)) :bind-keymap ("C-c n d" . org-roam-dailies-map) :config (require 'org-roam-dailies) ;; Ensure the keymap is available (org-roam-db-autosync-mode) (org-roam-setup)) #+end_src ** Org Roam UI #+begin_src emacs-lisp (use-package org-roam-ui :ensure t :after org-roam) #+end_src ** Inbox #+begin_src emacs-lisp (defun kylekrein/org-roam-capture-inbox () (interactive) (org-roam-capture- :node (org-roam-node-create) :templates '(("i" "inbox" plain "* %?" :if-new (file+head "Inbox.org" "#+title: Inbox\n#+category: Inbox\n#+filetags: Project"))))) (global-set-key (kbd "C-c n b") #'kylekrein/org-roam-capture-inbox) #+end_src ** Capture a task directly into Project Doesn't work for now #+begin_src emacs-lisp (defun kylekrein/org-roam-project-finalize-hook () "Adds the captured project file to `org-agenda-files' if the capture was not aborted." ;; Remove the hook since it was added temporarily (remove-hook 'org-capture-after-finalize-hook #'kylekrein/org-roam-project-finalize-hook) ;; Add project file to the agenda list if the capture was confirmed (unless org-note-abort (with-current-buffer (org-capture-get :buffer) (add-to-list 'org-agenda-files (buffer-file-name))))) (defun kylekrein/org-roam-capture-task () (interactive) ;; Add the project file to the agenda after capture is finished (add-hook 'org-capture-after-finalize-hook #'kylekrein/org-roam-project-finalize-hook) ;; Capture the new task, creating the project file if necessary (org-roam-capture- :node (org-roam-node-read nil (kylekrein/org-roam-filter-by-tag "Project")) :templates '(("p" "project" plain "* TODO %?" :if-new (file+head+olp "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n#+category: ${title}\n#+filetags: Project" ("Tasks")))))) (global-set-key (kbd "C-c n t") #'kylekrein/org-roam-capture-task) #+end_src * Org Agenda ** TODOs only from Projects Collect and show todos only defined in files with tag ~Project~ #+begin_src emacs-lisp (require 'org-roam-node) (defun kylekrein/org-roam-filter-by-tag (tag-name) (lambda (node) (member tag-name (org-roam-node-tags node)))) (defun kylekrein/org-roam-list-notes-by-tag (tag-name) (mapcar #'org-roam-node-file (seq-filter (kylekrein/org-roam-filter-by-tag tag-name) (org-roam-node-list)))) (defun kylekrein/org-roam-refresh-agenda-list () (interactive) (setq org-agenda-files (kylekrein/org-roam-list-notes-by-tag "Project"))) (setq org-agenda-files nil org-roam-node-display-template "${title} ${tags}" org-agenda-start-on-weekday 1 ;; Week starts on Monday instead of Sunday ) ;; Build the agenda list the first time for the session (kylekrein/org-roam-refresh-agenda-list) #+end_src ** Todos in Today Automatically copies all *DONE* TODOs to Today's daily #+begin_src emacs-lisp (defun kylekrein/org-roam-copy-todo-to-today () (interactive) (let ((org-refile-keep t) ;; Set this to nil to delete the original! (org-roam-dailies-capture-templates '(("t" "tasks" entry "%?" :if-new (file+head+olp "%<%Y-%m-%d>.org" "#+title: %<%Y-%m-%d>\n#+filetags: Daily\n" ("Completed Tasks:"))))) (org-after-refile-insert-hook #'save-buffer) today-file pos) (save-window-excursion (org-roam-dailies--capture (current-time) t) (setq today-file (buffer-file-name)) (setq pos (point))) ;; Only refile if the target file is different than the current file (unless (equal (file-truename today-file) (file-truename (buffer-file-name))) (org-refile nil nil (list "Tasks" today-file nil pos))))) (add-to-list 'org-after-todo-state-change-hook (lambda () (when (equal org-state "DONE") (kylekrein/org-roam-copy-todo-to-today)))) #+end_src ** Auto refresh agenda view [[https://emacs.stackexchange.com/a/68767][Link]] #+begin_src emacs-lis (defvar refresh-agenda-time-seconds 300) (defvar refresh-agenda-timer nil "Timer for `refresh-agenda-timer-function' to reschedule itself, or nil.") (defun refresh-agenda-timer-function () ;; If the user types a command while refresh-agenda-timer ;; is active, the next time this function is called from ;; its main idle timer, deactivate refresh-agenda-timer. (when refresh-agenda-timer (cancel-timer refresh-agenda-timer)) (org-agenda nil "a") (setq refresh-agenda-timer (run-with-idle-timer ;; Compute an idle time break-length ;; more than the current value. (time-add (current-idle-time) refresh-agenda-time-seconds) nil 'refresh-agenda-timer-function))) (run-with-idle-timer refresh-agenda-time-seconds t 'refresh-agenda-timer-function) #+end_src ** org-timeblock [[https://github.com/ichernyshovvv/org-timeblock][Github]] #+begin_src emacs-lispp (use-package compat :ensure t) ;;needed for org-timeblock (use-package org-timeblock :ensure t :after compat) #+end_src ** Notifications *** Org wild notifier Found the solution [[https://www.reddit.com/r/orgmode/comments/15ayqvv/orgnotifications_for_scheduled_tasks_in_orgagenda/][Here]] But it doesn't work on Android in GUI because of async (interprocess communications) #+begin_src emacs-lispp (use-package org-wild-notifier :ensure t :after 'org ) (org-wild-notifier-mode) (setq alert-default-style 'libnotify org-wild-notifier-alert-time '(0 5 10 60) org-wild-notifier-day-wide-alert-times "7:00" org-wild-notifier-keyword-whitelist nil ;; good for testing org-wild-notifier--alert-severity 'high org-wild-notifier-display-time-format-string "%H:%M" alert-fade-time 50 ) #+end_src *** Org alert Actually works, but is too basic #+begin_src emacs-lispp ;; Org-alert configuration (use-package org-alert :ensure t :after org :config (progn ;; Setup (setq org-alert-interval 300 org-alert-notification-title "Org Agenda Reminder") (org-alert-enable) ) ) #+end_src *** Appt Internal emacs tool and [[https://github.com/jwiegley/alert][alert]]. Settings for alert are at the beginning of this file. This solution was found [[https://igormelo.org/you_dont_need_org_alert.html][here]]. #+begin_src emacs-lisp (use-package emacs :config ;; start warning 60 minutes before the appointment (setq appt-message-warning-time 60) ;; warn me every 5 minutes (setq appt-display-interval 15) (setq appt-disp-window-function (lambda (remaining new-time msg) (alert (format "In %s minutes" remaining) :title msg :severity 'moderate :category 'org-agenda :id (intern msg)))) (advice-add 'appt-check :before (lambda (&rest args) (org-agenda-to-appt t))) (appt-activate t)) (setq alert-fade-time 50) #+end_src **** Appt on modeline #+begin_src emacs-lisp (use-package org-upcoming-modeline :ensure t :after org :config (setq appt-display-mode-line nil) (org-upcoming-modeline-mode)) #+end_src * RAINBOW MODE Display the actual color as a background for any hex color value (ex. #ffffff). The code block below enables rainbow-mode in all programming modes (prog-mode) as well as org-mode, which is why rainbow works in this document. #+begin_src emacs-lisp (use-package rainbow-mode :ensure t :hook ((org-mode prog-mode) . rainbow-mode)) #+end_src * SHELLS AND TERMINALS ** Eshell Eshell is an Emacs 'shell' that is written in Elisp. #+begin_src emacs-lisp (use-package eshell-syntax-highlighting :ensure t :after esh-mode :config (eshell-syntax-highlighting-global-mode +1)) #+end_src ** Vterm Vterm is a terminal emulator within Emacs. The 'shell-file-name' setting sets the shell to be used in M-x shell, M-x term, M-x ansi-term and M-x vterm. By default, the shell is set to 'fish' but could change it to 'bash' or 'zsh' if you prefer. #+begin_src emacs-lisp (unless (string-equal system-type "android") ;;Fails to compile (use-package vterm :ensure t ;;:config )) #+end_src ** Vterm-Toggle [[https://github.com/jixiuf/vterm-toggle][vterm-toggle]] toggles between the vterm buffer and whatever buffer you are editing. #+begin_src emacs-lisp (unless (string-equal system-type "android") (use-package vterm-toggle :ensure t :after vterm :config (setq vterm-toggle-fullscreen-p nil) (setq vterm-toggle-scope 'project) (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-at-bottom) ;;(display-buffer-reuse-window display-buffer-in-direction) ;;display-buffer-in-direction/direction/dedicated is added in emacs27 ;;(direction . bottom) ;;(dedicated . t) ;dedicated is supported in emacs27 (reusable-frames . visible) (window-height . 0.3))))) #+end_src * SUDO EDIT [[https://github.com/nflath/sudo-edit][sudo-edit]] gives us the ability to open files with sudo privileges or switch over to editing with sudo privileges if we initially opened the file without such privileges. #+begin_src emacs-lisp (use-package sudo-edit :ensure t) #+end_src * Language support Emacs has built-in programming language modes for Lisp, Scheme, DSSSL, Ada, ASM, AWK, C, C++, Fortran, Icon, IDL (CORBA), IDLWAVE, Java, Javascript, M4, Makefiles, Metafont, Modula2, Object Pascal, Objective-C, Octave, Pascal, Perl, Pike, PostScript, Prolog, Python, Ruby, Simula, SQL, Tcl, Verilog, and VHDL. Other languages will require you to install additional modes. ** Nix #+begin_src emacs-lisp (use-package nix-mode :ensure t :mode ("\\.nix\\'" "\\.nix.in\\'")) (use-package nix-drv-mode :ensure nix-mode :mode "\\.drv\\'") (use-package nix-shell :ensure nix-mode :commands (nix-shell-unpack nix-shell-configure nix-shell-build)) (use-package nix-repl :ensure nix-mode :commands (nix-repl)) #+end_src * Nerd Icons #+begin_src emacs-lisp (use-package nerd-icons :ensure t ;; :custom ;; The Nerd Font you want to use in GUI ;; "Symbols Nerd Font Mono" is the default and is recommended ;; but you can use any other Nerd Font if you want ;; (nerd-icons-font-family "Symbols Nerd Font Mono") ) #+end_src ** Nerd Icons Completion [[https://github.com/rainstormstudio/nerd-icons-completion]] #+begin_src emacs-lisp (use-package nerd-icons-completion :ensure t :after marginalia :config (nerd-icons-completion-mode) (add-hook 'marginalia-mode-hook #'nerd-icons-completion-marginalia-setup)) #+end_src * Buffer Move Creating some functions to allow us to easily move windows (splits) around. The following block of code was taken from buffer-move.el found on the EmacsWiki: https://www.emacswiki.org/emacs/buffer-move.el #+begin_src emacs-lisp (require 'windmove) ;;;###autoload (defun buf-move-up () "Swap the current buffer and the buffer above the split. If there is no split, ie now window above the current one, an error is signaled." ;; "Switches between the current buffer, and the buffer above the ;; split, if possible." (interactive) (let* ((other-win (windmove-find-other-window 'up)) (buf-this-buf (window-buffer (selected-window)))) (if (null other-win) (error "No window above this one") ;; swap top with this one (set-window-buffer (selected-window) (window-buffer other-win)) ;; move this one to top (set-window-buffer other-win buf-this-buf) (select-window other-win)))) ;;;###autoload (defun buf-move-down () "Swap the current buffer and the buffer under the split. If there is no split, ie now window under the current one, an error is signaled." (interactive) (let* ((other-win (windmove-find-other-window 'down)) (buf-this-buf (window-buffer (selected-window)))) (if (or (null other-win) (string-match "^ \\*Minibuf" (buffer-name (window-buffer other-win)))) (error "No window under this one") ;; swap top with this one (set-window-buffer (selected-window) (window-buffer other-win)) ;; move this one to top (set-window-buffer other-win buf-this-buf) (select-window other-win)))) ;;;###autoload (defun buf-move-left () "Swap the current buffer and the buffer on the left of the split. If there is no split, ie now window on the left of the current one, an error is signaled." (interactive) (let* ((other-win (windmove-find-other-window 'left)) (buf-this-buf (window-buffer (selected-window)))) (if (null other-win) (error "No left split") ;; swap top with this one (set-window-buffer (selected-window) (window-buffer other-win)) ;; move this one to top (set-window-buffer other-win buf-this-buf) (select-window other-win)))) ;;;###autoload (defun buf-move-right () "Swap the current buffer and the buffer on the right of the split. If there is no split, ie now window on the right of the current one, an error is signaled." (interactive) (let* ((other-win (windmove-find-other-window 'right)) (buf-this-buf (window-buffer (selected-window)))) (if (null other-win) (error "No right split") ;; swap top with this one (set-window-buffer (selected-window) (window-buffer other-win)) ;; move this one to top (set-window-buffer other-win buf-this-buf) (select-window other-win)))) #+end_src * Completions ** Vertico [[https://github.com/minad/vertico][Vertico]] provides a performant and minimalistic vertical completion UI based on the default completion system. #+begin_src emacs-lisp ;; Enable vertico (use-package vertico :ensure t :custom ;; (vertico-scroll-margin 0) ;; Different scroll margin ;; (vertico-count 20) ;; Show more candidates ;; (vertico-resize t) ;; Grow and shrink the Vertico minibuffer (vertico-cycle t) ;; Enable cycling for `vertico-next/previous' :init (vertico-mode)) (vertico-mode t) ;; enable vertico for all buffers ;; Persist history over Emacs restarts. Vertico sorts by history position. (use-package savehist :init (savehist-mode)) ;; A few more useful configurations... (use-package emacs :custom ;; Support opening new minibuffers from inside existing minibuffers. (enable-recursive-minibuffers t) ;; Hide commands in M-x which do not work in the current mode. Vertico ;; commands are hidden in normal buffers. This setting is useful beyond ;; Vertico. (read-extended-command-predicate #'command-completion-default-include-p) :init ;; 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) ;; 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)) #+end_src ** Orderless Better searching #+begin_src emacs-lisp ;; Optionally use the `orderless' completion style. (use-package orderless :ensure t :custom ;; Configure a custom style dispatcher (see the Consult wiki) ;; (orderless-style-dispatchers '(+orderless-consult-dispatch orderless-affix-dispatch)) ;; (orderless-component-separator #'orderless-escapable-split-on-space) (completion-styles '(orderless flex basic partial-completion)) (completion-category-defaults nil) (completion-category-overrides '((file (styles partial-completion))))) #+end_src ** Fido-mode Enables fido completion in emacs. It's actually cool, but... Sometimes it hides the last result and it doesn't work good when using app launchers #+begin_src emacs-lispp (global-completion-preview-mode) (fido-mode t) (savehist-mode t) (fido-vertical-mode t) (setf completion-auto-select t ;; Show completion on first call completion-auto-help 'visible ;; Display *Completions* upon first request completions-format 'one-column ;; Use only one column completions-sort 'historical ;; Order based on minibuffer history completions-max-height 20 ;; Limit completions to 15 (completions start at line 5) completion-ignore-case t) (define-key icomplete-minibuffer-map (kbd "SPC") 'self-insert-command) ;; Allows to type spaces, if no completions available ;; Have TAB complete using the first option and continue, instead of popping up the *Completions* buffer (define-key icomplete-minibuffer-map [remap minibuffer-complete] 'icomplete-force-complete) #+end_src ** Descriptions *** Marginalia [[https://github.com/minad/marginalia/]] Descriptions for completions #+begin_src emacs-lisp ;; Enable rich annotations using the Marginalia package (use-package marginalia :ensure t ;; 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)) #+end_src * Communication ** Telegram [[https://github.com/zevlg/telega.el][Telega]] is a telegram client for Emacs. It doesn't work for unknown reason #+begin_src emacs-lispp (use-package telega :ensure t :init (setq telega-use-docker nil) (setq telega-use-images nil) :defer t) #+end_src * Theme [[https://emacsfodder.github.io/emacs-theme-editor/][Emacs Theme Editor]] ** Theme loading #+begin_src emacs-lisp (use-package doom-themes :ensure t :config ;; Global settings (defaults) (setq doom-themes-enable-bold t ; if nil, bold is universally disabled doom-themes-enable-italic t) ; if nil, italics is universally disabled (load-theme 'doom-one t) ;; Enable flashing mode-line on errors (doom-themes-visual-bell-config) ;; Enable custom neotree theme (nerd-icons must be installed!) (doom-themes-neotree-config) ;; or for treemacs users (setq doom-themes-treemacs-theme "doom-atom") ; use "doom-colors" for less minimal icon theme (doom-themes-treemacs-config) ;; Corrects (and improves) org-mode's native fontification. (doom-themes-org-config)) #+end_src * Transparency With Emacs version 29, true transparency has been added. #+begin_src emacs-lisp (unless (kylekrein/detect-wsl) (add-to-list 'default-frame-alist '(alpha-background . 90))) ; For all new frames henceforth #+end_src * WHICH-KEY #+begin_src emacs-lisp (use-package which-key :ensure t :init (which-key-mode 1) :config (setq which-key-side-window-location 'bottom which-key-sort-order #'which-key-key-order-alpha which-key-sort-uppercase-first nil which-key-add-column-padding 1 which-key-max-display-columns nil which-key-min-display-lines 6 which-key-side-window-slot -10 which-key-side-window-max-height 0.25 which-key-idle-delay 0.8 which-key-max-description-length 25 which-key-allow-imprecise-window-fit nil which-key-separator " → " )) #+end_src * Emacs on Android ** Fonts All fonts on Android must be in *~/fonts* directory ** Settings #+begin_src emacs-lisp (when (string-equal system-type "android") ;;Write all android settings here (setq touch-screen-keyboard-function t) (setq touch-screen-display-keyboard t) (menu-bar-mode 1) ;; Enable the menu bar (scroll-bar-mode 1) ;; Enable the scroll bar (tool-bar-mode 1) ;;Enable the tool bar (setq use-file-dialog t) ;; file dialog (setq use-dialog-box t) ;; dialog box (setq pop-up-windows t) ;; popup windows ) #+end_src * Emacs on WSL ** Clipboard fix Found the fix [[https://www.lukas-barth.net/blog/emacs-wsl-copy-clipboard/][here]] #+begin_src emacs-lisp (when (kylekrein/detect-wsl) (setq select-active-regions nil) (setq select-enable-clipboard 't) (setq select-enable-primary nil) (setq interprogram-cut-function #'gui-select-text) ) #+end_src