;; -------- Speed up load time ------- ;; (package-initialize) ;; I don't use emacs-server, so startup times are very important to me. ;; Garbage collection is triggered very often during start up, and it ;; slows the whole thing down. It is safe to increase threshold ;; temporarily to prevent aggressive GC, and then re-enable it at the ;; end. (setq gc-cons-threshold 402653184 gc-cons-percentage 0.6) ;; There are special ways to handle files (via SSH or in archives), ;; but this is not necessary during startup, and it also slows down ;; the load significantly, as emacs is going through lots of files. (defvar saved--file-name-handler-alist file-name-handler-alist) (setq file-name-handler-alist nil) ;; Restore defaults after initialization has completed (add-hook 'after-init-hook #'(lambda () (setq gc-cons-threshold 16777216 gc-cons-percentage 0.1) (setq file-name-handler-alist saved--file-name-handler-alist))) ;; Required by use-package :bind in compiled mode (require 'bind-key) (setq package-enable-at-startup nil) (setq package-archives nil) (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) (add-to-list 'package-archives '("gnu" . "http://elpa.gnu.org/packages/") t) ;;(setq initial-major-mode 'text-mode) ;; -------- Default directories -------- (setq default-directory "~/") ;;(setq command-line-default-directory "~/") ;; -------- State files -------- ;; By default emacs leaves lots of trash around your filesystem while ;; you are editing. This section cleans up the basics. ;; Don't leave =yourfile~= temporary files nearby, and put them to a ;; separate directory instead. (setq backup-directory-alist '(("." . "~/.emacs.d/backups"))) (setq auto-save-file-name-transforms '((".*" "~/.emacs.d/backups" t))) ;; -------- Command history -------- ;; Save command history so that when emacs is restarted, the history ;; is preserved. (setq savehist-file "~/.emacs.d/savehist") (use-package savehist-mode :defer 1 :config (setq savehist-save-minibuffer-history +1) (setq savehist-additional-variables '(kill-ring search-ring regexp-search-ring)) (savehist-mode +1) ) ;; -------- Clipboard --------- ;; This plugin enabling yanking directly into the OS clipboard when ;; editing from terminal (with help of OSC-52 escape sequence) (use-package clipetty :ensure t :defer 1 :diminish clipetty-mode :config (global-clipetty-mode) ) ;; -------- Recent files -------- ;; Recent files are convenient to record because you can use them to ;; quickly jump to what you've been editing recently. (use-package recentf-mode :defer 1 :config (setq recentf-save-file "~/.emacs.d/recentf" recentf-max-menu-items 0 recentf-max-saved-items 300 recentf-filename-handlers '(file-truename) recentf-exclude (list "^/tmp/" "^/ssh:" "\\.?ido\\.last$" "\\.revive$" "/TAGS$" "^/var/folders/.+$" )) (recentf-mode 1) ) ;; -------- De-clutter -------- ;; Toolbar and scrollbars are only useful to novices. The same for ;; startup screen and menu bar. (tool-bar-mode -1) (scroll-bar-mode -1) (setq inhibit-start-screen 1) (setq inhibit-startup-screen t) (setq inhibit-splash-screen t) ;; Emacs is very persistent about showing you a welcome message in ;; the echo area. The only way to disable it is to pass your user ;; name in this magic variable (put 'inhibit-startup-echo-area-message 'saved-value t) (setq inhibit-startup-echo-area-message (user-login-name)) (if (not (eq window-system 'mac)) (menu-bar-mode -1)) ;; More reliable inter-window border ;; The native border "consumes" a pixel of the fringe on righter-most splits, ;; ~window-divider~ does not. Available since Emacs 25.1. (setq-default window-divider-default-places t window-divider-default-bottom-width 0 window-divider-default-right-width 1) (use-package window-divider-mode :defer 1 :config (window-divider-mode +1) ) ;; Remove continuation arrow on right fringe (setq fringe-indicator-alist (delq (assq 'continuation fringe-indicator-alist) fringe-indicator-alist)) ;; No more typing the whole yes or no. Just y or n will do. (fset 'yes-or-no-p 'y-or-n-p) ;; Makes *scratch* empty. (setq initial-scratch-message "") ;; Hide modeline in the vterm mode (use-package hide-mode-line :ensure t :defer t :hook (vterm-mode . hide-mode-line-mode) ) ;; Disable "when done with this frame..." message when running ;; emacsclient (setq server-client-instructions nil) ;; -------- Cursor and movement -------- ;; On emacs mac port use Alt as meta key (if (eq window-system 'mac) (progn (setq mac-option-modifier 'meta) (setq mac-command-modifier nil) (setq mac-pass-command-to-system 't))) ;; Blinking cursor is inconvenient (blink-cursor-mode -1) (unless (display-graphic-p) (setq visible-cursor nil)) ;; Disable bell ring when moving outside of available area (setq ring-bell-function 'ignore) ;; Disable annoying blink-matching-paren (setq blink-matching-paren nil) ;; -------- Window decoration -------- ;; This makes the header transparent on Emacs 26.1+ under OS X (add-to-list 'default-frame-alist '(ns-transparent-titlebar . t)) (add-to-list 'default-frame-alist '(ns-appearance . dark)) (setq ns-use-proxy-icon nil) ;; -------- Minor modes -------- ;; Hide some miror modes from sight to not clutter the modeline (use-package diminish :ensure t) ;; -------- Theme -------- ;; - I don't like that fringes are visible, so I set them to regular ;; background color ;; - Panels look better without outset/inset shadows (use-package modus-themes :ensure t :config (load-theme 'modus-operandi t) ) (set-face-attribute 'fringe nil :foreground (face-foreground 'default) :background (face-background 'default)) ;; On many OSs the modeline has an outset border (lighter on top and ;; darker on the bottom). This doesn't look pretty on a flat theme. (set-face-attribute 'mode-line nil :box nil) (set-face-attribute 'mode-line-inactive nil :box nil) ;; -------- Font -------- ;; Some time ago I've purchased a great font called Pragmata Pro, ;; which is easy on the eyes and tailored for programmers. It may ;; not be available everywhere though, hence conditional load. (when window-system (if (not (null (x-list-fonts "PragmataPro"))) (add-to-list 'default-frame-alist '(font . "PragmataPro-15")) (add-to-list 'default-frame-alist '(font . "Source Code Pro-11")) )) (add-to-list 'default-frame-alist '(font . "Source Code Pro-11")) ;; Configure fonts when running in daemon mode (defun my-configure-font (frame) "Configure font given initial non-daemon FRAME. Intended for `after-make-frame-functions'." (add-to-list 'default-frame-alist '(font . "Source Code Pro-11")) (remove-hook 'after-make-frame-functions #'my-configure-font)) (add-hook 'after-make-frame-functions #'my-configure-font) ;; -------- Packages -------- ;; -------- Navigation -------- ;; Quickly find my way around emacs ;; Default scheme for uniquifying buffer names is not convenient. ;; It's better to have a regular path-like structure. ;;(use-package 'uniquify) (setq uniquify-buffer-name-style 'forward) (setq uniquify-separator "/") (setq uniquify-after-kill-buffer-p t) ; rename after killing uniquified (setq uniquify-ignore-buffers-re "^\\*") ; don't muck with special buffers ;; If you stop after typing a part of keybinding, shows available ;; options in minibuffer. (use-package which-key :ensure t :defer 2 :config (which-key-mode) (which-key-setup-side-window-bottom) ) ;; persp-mode allows you to have tagged workspaces akin to ;; Linux tiled-window managers. ;;(use-package persp-mode ;;:ensure t ;;:bind ;;("C-x x s" . persp-switch) ;;:init ;;;; persp-mode clashes with corfu ;;(setq persp-auto-resume-time 0) ;;:config ;;(persp-mode) ;;) ;; vertico-mode allows for easy navigation between buffers and files (use-package vertico :ensure t :defer 0.1 :config (require 'bind-key) (setq completion-ignore-case t) (setq completion-styles '(basic substring partial-completion flex)) (vertico-mode) ) (use-package marginalia :ensure t :defer 0.1 :config (marginalia-mode) ) ;; ripgrep search with consult (use-package consult :ensure t :defer 0.1 :config (global-set-key (kbd "M-s r") 'consult-ripgrep) (setq completion-in-region-function 'consult-completion-in-region) ) ;; Navigation when in russian layout (cl-loop for from across "йцукенгшщзхъфывапролджэячсмитьбюЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖ\ЭЯЧСМИТЬБЮ№" for to across "qwertyuiop[]asdfghjkl;'zxcvbnm,.QWERTYUIOP{}ASDFGHJKL:\"ZXCVBNM<>#" do (eval `(define-key key-translation-map (kbd ,(concat "C-" (string from))) (kbd ,(concat "C-" (string to))))) (eval `(define-key key-translation-map (kbd ,(concat "M-" (string from))) (kbd ,(concat "M-" (string to)))))) ;; -------- Editor basics -------- ;; Use 4 spaces to indent by default (setq-default indent-tabs-mode nil) (setq-default tab-width 4) ;; Clean up trailing whitespace on file save (add-hook 'before-save-hook 'whitespace-cleanup) ;; But use editorconfig to guess proper project-wide indentation rules (use-package editorconfig :ensure t :defer t :diminish editorconfig-mode :hook (prog-mode . editorconfig-mode) ) ;; Speed up comint buffers by disabling bidirectional language support (setq-default bidi-display-reordering nil) ;; -------- Evil mode -------- ;; Evil mode is a vi/vim compatibility layer for Emacs that provides roughly ;; equivalent keybindings for editing. (use-package evil :ensure t :defer .1 ;; don't block emacs when starting, load evil immediately after startup :init ;;(setq evil-want-integration nil) ;; required by evil-collection (setq evil-want-keybinding nil) (setq evil-want-C-u-scroll t) :config (evil-mode) (evil-set-leader 'normal " ") (evil-set-undo-system 'undo-redo) (evil-define-key 'normal 'global (kbd "f") 'find-file) (evil-define-key 'normal 'global (kbd "b") 'switch-to-buffer) (evil-define-key 'normal 'global (kbd "r") 'consult-ripgrep) ;; Make :q close the current buffer, and not the whole emacs process (global-set-key [remap evil-quit] 'kill-buffer-and-window) (use-package evil-collection :after evil :diminish evil-collection-unimpaired-mode :config (evil-collection-init)) ) (use-package evil-terminal-cursor-changer :ensure t :if (not (display-graphic-p)) :after evil :defer t :init (evil-terminal-cursor-changer-activate)) (use-package evil-snipe :ensure t :defer t :after evil :config (setq evil-snipe-scope 'buffer) (evil-snipe-mode +1) (evil-snipe-override-mode 1) ) ;; -------- Tools and environment -------- ;; By default, Emacs doesn't add system path to its search places (use-package exec-path-from-shell :ensure t :defer 1 :config (setenv "PATH" (concat "/usr/local/bin:" (getenv "PATH"))) ;; On a mac, this will set up PATH and MANPATH from your environment (when (memq window-system '(mac ns x pgtk)) (exec-path-from-shell-initialize)) ) ;; -------- Org roam -------- (use-package org-roam :ensure t :defer t :bind ("C-c n l" . org-roam-buffer-toggle) ("C-c n i" . org-roam-node-insert) ("C-c n f" . org-roam-node-find) ("C-c n r" . consult-org-roam-search) ("C-c n d" . org-roam-dailies-goto-today) ("C-c n w" . org-roam-week) :config (use-package consult-org-roam :ensure t ) (setq org-roam-directory "~/notes") (setq org-roam-node-default-sort 'file-mtime) (setq consult-org-roam-grep-func #'consult-ripgrep) (setq org-roam-capture-templates `( ("d" "default" plain "%?" :target (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n") :unnarrowed t) ("p" "post" plain "%?" :target (file+head "%<%Y%m%d%H%M%S>-${slug}.org" ,(concat "#+date: %<%Y-%m-%dT%H:%M:%S>Z\n" "#+slug: ${slug}\n" "#+title: ${title}\n" "#+filetags: Post\n" "\n")) :unnarrowed t) ("i" "interview" plain "%?" :target (file+head "%<%Y%m%d%H%M%S>-${slug}.org" ,(concat "#+title: ${title}\n" "#+filetags: Interview\n" "\n")) :unnarrowed t) )) (setq org-roam-node-display-template (concat "${title:80} " (propertize "${tags:20}" 'face 'org-tag)) org-roam-node-annotation-function (lambda (node) (marginalia--time (org-roam-node-file-mtime node)))) (defun roam-extra:get-filetags () (split-string (or (org-roam-get-keyword "filetags") ""))) (defun roam-extra:add-filetag (tag) (let* ((new-tags (cons tag (roam-extra:get-filetags))) (new-tags-str (combine-and-quote-strings new-tags))) (org-roam-set-keyword "filetags" new-tags-str))) (defun roam-extra:del-filetag (tag) (let* ((new-tags (seq-difference (roam-extra:get-filetags) `(,tag))) (new-tags-str (combine-and-quote-strings new-tags))) (org-roam-set-keyword "filetags" new-tags-str))) (defun roam-extra:todo-p () "Return non-nil if current buffer has any TODO entry. TODO entries marked as done are ignored, meaning the this function returns nil if current buffer contains only completed tasks." (org-element-map (org-element-parse-buffer 'headline) 'headline (lambda (h) (eq (org-element-property :todo-type h) 'todo)) nil 'first-match)) (defun roam-extra:update-todo-tag () "Update TODO tag in the current buffer." (when (and (not (active-minibuffer-window)) (org-roam-file-p)) (org-with-point-at 1 (let* ((tags (roam-extra:get-filetags)) (is-todo (roam-extra:todo-p))) (cond ((and is-todo (not (seq-contains-p tags "todo"))) (roam-extra:add-filetag "todo")) ((and (not is-todo) (seq-contains-p tags "todo")) (roam-extra:del-filetag "todo"))))))) (defun roam-extra:todo-files () "Return a list of roam files containing todo tag." (org-roam-db-sync) (let ((todo-nodes (seq-filter (lambda (n) (seq-contains-p (org-roam-node-tags n) "todo")) (org-roam-node-list)))) (seq-uniq (seq-map #'org-roam-node-file todo-nodes)))) (defun roam-extra:update-todo-files (&rest _) "Update the value of `org-agenda-files'." (setq org-agenda-files (roam-extra:todo-files))) (add-hook 'find-file-hook #'roam-extra:update-todo-tag) (add-hook 'before-save-hook #'roam-extra:update-todo-tag) (advice-add 'org-agenda :before #'roam-extra:update-todo-files) (defun org-roam-week (&optional other-window) "Opens or creates a weekly note." (interactive) (let* ((title (format-time-string "Week %U %Y")) (nodes (org-roam-node-list)) (node (seq-find (lambda (n) (string-equal (org-roam-node-title n) title)) nodes))) (if (and node (org-roam-node-file node)) (org-roam-node-visit node other-window) (org-roam-capture- :node (org-roam-node-create :title title) :templates `(("w" "week" plain "* Goals\n\n%?\n\n* Tasks\n\n" :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n#+category: ${title}\n#+filetags: Week") :unnarrowed t)) :props '(:finalize find-file)) ) ) ) (org-roam-setup) ) ;; -------- Org mode --------- (use-package org :ensure t :mode (("\\.org$" . org-mode)) :config (use-package org-contrib :ensure t) (use-package org-modern :ensure t :config (global-org-modern-mode) ) (setq org-modules '(org-w3m org-bbdb org-bibtex org-docview org-gnus org-info org-irc org-mhe org-rmail org-checklist org-mu4e)) ;; Enable line wrap by default in org buffers (setq org-startup-truncated nil) ;; Use svg export for latex fragments (setq org-latex-create-formula-image-program 'dvisvgm) ;; Sometimes I sit at night until 4 AM, and I still want org to treat it ;; as "today" (setq org-extend-today-until 4) ;; navigate with (org-goto) by offering the full list of targets in ido-mode (setq org-goto-interface 'outline-path-completion) ;; don't search incrementally (setq org-outline-path-complete-in-steps nil) ;; see whole path at once ;; avoid inadvertently editing hidden text (setq org-catch-invisible-edits 'show-and-error) ;; hide empty spaces between folded subtrees (setq org-cycle-separator-lines 0) ;; use hours in clocktable instead of days and hours (setq org-time-clocksum-format "%d:%02d") ;; After a recurring task is marked as done, reset it to TODO. This ;; is important because I have the "INBOX" state first in the sequence ;; of states. (setq org-todo-repeat-to-state "TODO") ;; Default format for column view (setq org-columns-default-format "%38ITEM(Details) %TAGS(Context) %7TODO(To Do) %5Effort(Time){:} %6CLOCKSUM(Total){:}") ;; Better source code editing (setq org-src-fontify-natively t org-src-window-setup 'current-window org-src-strip-leading-and-trailing-blank-lines t org-src-preserve-indentation t org-src-tab-acts-natively t) ;; Default tags (setq org-tag-alist '((:startgroup . nil) ("WORK" . ?w) ("HOME" . ?h) (:endgroup . nil) ("PROJECT" . ?p) ("PHONE" . ?n) ("MEETING" . ?m) ("DOC" . ?d) ("GOOGLE" . ?g) ("QUICK" . ?q))) ;; Default todo sequence (setq org-todo-keywords '((sequence "INBOX(i)" "TODO(t)" "ERRAND(k)" "SOMEDAY(s)" "WAITING(w@/!)" "APPT(a)" "|" "DONE(d!)" "CANCELLED(c!)"))) ;; Babel and code block embedding (setq org-confirm-babel-evaluate nil) (org-babel-do-load-languages 'org-babel-load-languages '((emacs-lisp . t) (python . t) (shell . t))) ;; Log TODO state changes and clock-ins into the LOGBOOK drawer (setq org-clock-into-drawer t) (setq org-log-into-drawer t) ;; Quickly creating new tasks (global-set-key (kbd "\C-c r") 'org-capture) (setq org-capture-templates `(("t" "Todo" entry (file+headline "~/org/gtd.org" "Tasks") "* TODO %?\n %U\n %a") ("i" "Inbox" entry (file+headline "~/org/gtd.org" "Tasks") "* INBOX %?\n %U") ("f" "Follow-up" entry (file+headline "~/org/gtd.org" "Tasks") ,(concat "* TODO %? :EMAIL:\n" " %U\n" " %a")) ("c" "Contacts" entry (file "~/org/contacts.org") (concat "* %(org-contacts-template-name)\n" ":PROPERTIES:\n" ":EMAIL: %(org-contacts-template-email)\n" ":END:")) ("q" "Org capture template" entry (file+headline "~/org/capture.org" "Notes") "* %:description\n\n Source: %u, %:link\n\n %i" :empty-lines 1) ) ) ;; org-protocol allows you to capture stuff into your system from web ;; browsers (require 'org-protocol) ;; Refiling allows you to quickly move an element with its children to ;; another location. ;; By default, refile works up to 2-level sections, which is not very ;; convenient if you have project-based organization ;; (/Projects/ProjectName). (setq org-refile-targets '((org-agenda-files :maxlevel . 3))) ;; Then, it's nice to have a full path to the target element appear in ;; completion (setq org-refile-use-outline-path 'file) ;; But, when using helm, we also need to tell org mode to present the ;; whole list of possible completions right away, and not use ;; incremental search: (setq org-outline-path-complete-in-steps nil) ;; It may also be useful to be able to create elements, if the refile ;; target doesn't already exist. (setq org-refile-allow-creating-parent-nodes 'confirm) ;; Agenda (global-set-key "\C-ca" 'org-agenda) (setq org-agenda-files '("~/org/gtd.org" "~/org/weeklyreview.org" )) (setq org-agenda-custom-commands nil) (add-to-list 'org-agenda-custom-commands '("h" "Work todos" tags-todo "-personal-doat={.+}-dowith={.+}/!-ERRAND" ((org-agenda-todo-ignore-scheduled t)))) (add-to-list 'org-agenda-custom-commands '("H" "All work todos" tags-todo "-personal/!-ERRAND-MAYBE" ((org-agenda-todo-ignore-scheduled nil)))) (add-to-list 'org-agenda-custom-commands '("A" "Work todos with doat or dowith" tags-todo "-personal+doat={.+}|dowith={.+}/!-ERRAND" ((org-agenda-todo-ignore-scheduled nil)))) (add-to-list 'org-agenda-custom-commands '("P" "Projects" tags "+PROJECT-TODO=\"SOMEDAY\"")) (add-to-list 'org-agenda-custom-commands '("i" "Inbox" todo "INBOX")) (add-to-list 'org-agenda-custom-commands '("o" "Someday" todo "SOMEDAY")) (add-to-list 'org-agenda-custom-commands '("c" "Simple agenda view" ( (agenda "" ) (todo "" ( (org-agenda-overriding-header "\nUnscheduled TODO") (org-agenda-skip-function '(org-agenda-skip-entry-if 'timestamp 'todo '("SOMEDAY" "ERRAND"))) (org-agenda-sorting-strategy (quote ((agenda time-up priority-down tag-up)))) )) ) ((org-agenda-overriding-columns-format "%38ITEM(Details) %TAGS(Context) %7TODO(To Do) %5Effort(Time){:} %6CLOCKSUM_T(Total){:}") (org-agenda-view-columns-initially t)) ) ) (setq org-todo-keyword-faces '(("ERRAND" . (:foreground "light sea green" :weight bold)) ("INBOX" . (:foreground "DarkGoldenrod" :weight bold)))) ;;(set-face-foreground 'org-scheduled-previously "DarkGoldenrod") (setq org-tags-exclude-from-inheritance '("PROJECT") org-stuck-projects '("+PROJECT/-MAYBE-DONE-SOMEDAY" ("TODO" "ERRAND" "WAITING") () ())) ) (use-package org-tree-slide :ensure t :defer t :config (defun my-tree-slide-start () (set-window-margins (get-buffer-window (current-buffer)) 10 10) ) (defun my-tree-slide-end () (set-window-margins (get-buffer-window (current-buffer)) 0 0) ) (setq org-tree-slide-slide-in-effect nil) (add-hook 'org-tree-slide-play-hook 'my-tree-slide-start) (add-hook 'org-tree-slide-stop-hook 'my-tree-slide-end) ) ;; -------- Email -------- (use-package mu4e :defer t :commands (mu4e) :config (setq sendmail-program (executable-find "msmtp")) (autoload 'mu4e "mu4e" "\ If mu4e is not running yet, start it. Then, show the main window, unless BACKGROUND (prefix-argument) is non-nil. " t nil) (setq mu4e-update-interval 600) ;; refresh every X seconds (setq message-citation-line-format "On %d %b %Y at %R, %f wrote:\n") (setq message-citation-line-function 'message-insert-formatted-citation-line) (setq mu4e-attachment-dir "~/Downloads") (setq mu4e-html2text-command 'mu4e-shr2text) (setq mu4e-user-mail-address-list '("mail@knazarov.com" "k.nazarov@corp.mail.ru")) ;; exlude myself from the email replies (setq mu4e-compose-dont-reply-to-self t) ;; set mu4e as a default mail agent (setq mail-user-agent 'mu4e-user-agent) (setq mu4e-maildir "/home/knazarov/Maildir") (setq mu4e-view-show-images t mu4e-image-max-width 800 mu4e-view-prefer-html t mu4e-change-filenames-when-moving t ;; prevent duplicate UIDs mu4e-get-mail-command "mbsync -a -q" mu4e-headers-include-related nil) (setq mu4e-sent-folder "/personal/Sent" mu4e-drafts-folder "/personal/Drafts" mu4e-trash-folder "/personal/Trash" mu4e-refile-folder "/personal/Archive" user-full-name "Konstantin Nazarov" user-mail-address "mail@knazarov.com" smtpmail-default-smtp-server "smtp.fastmail.com" smtpmail-local-domain "knazarov.com" smtpmail-smtp-server "smtp.fastmail.com" smtpmail-stream-type 'starttls smtpmail-smtp-service 587 message-send-mail-function 'message-send-mail-with-sendmail ;;message-sendmail-extra-arguments '("--read-envelope-from") ) (setq mu4e-compose-signature "<#part type=text/html>

Hello ! I am the html signature which can contains anything in html !

<#/part>" ) (defvar my-mu4e-account-alist `(("personal" (mu4e-sent-folder "/personal/Sent") (mu4e-drafts-folder "/personal/Drafts") (mu4e-trash-folder "/personal/Trash") (mu4e-refile-folder "/personal/Archive") (user-mail-address "mail@knazarov.com") (message-sendmail-envelope-from "mail@knazarov.com") ;;(mu4e-compose-signature-auto-include nil) (mu4e-compose-signature ,(if (file-exists-p "~/.mail-sig.txt") (with-temp-buffer (insert-file-contents "~/.mail-sig.txt") (buffer-string)) "")) (message-signature-file "~/.mail-sig.txt") (message-cite-reply-position above) (message-cite-style message-cite-style-outlook)) ("work" (mu4e-sent-folder "/work/Sent") (mu4e-drafts-folder "/work/Drafts") (mu4e-trash-folder "/work/Trash") (mu4e-refile-folder "/work/Archive") (user-mail-address "k.nazarov@corp.mail.ru") (message-sendmail-envelope-from "k.nazarov@corp.mail.ru") (mu4e-compose-signature-auto-include nil) (message-signature-file "~/.mail-sig.txt") (mu4e-compose-signature ,(if (file-exists-p "~/.mail-sig.txt") (with-temp-buffer (insert-file-contents "~/.mail-sig.txt") (buffer-string)) "")) (message-cite-reply-position above) (message-cite-style message-cite-style-outlook)) )) (defun my-mu4e-set-account () "Set the account for composing a message." (let* ((account (if mu4e-compose-parent-message (let ((maildir (mu4e-message-field mu4e-compose-parent-message :maildir))) (string-match "/\\(.*?\\)/" maildir) (match-string 1 maildir)) (completing-read (format "Compose with account: (%s) " (mapconcat #'(lambda (var) (car var)) my-mu4e-account-alist "/")) (mapcar #'(lambda (var) (car var)) my-mu4e-account-alist) nil t nil nil (caar my-mu4e-account-alist)))) (account-vars (cdr (assoc account my-mu4e-account-alist)))) (if account-vars (mapc #'(lambda (var) (set (car var) (cadr var))) account-vars) (error "No email account found")))) (defun my-mu4e-refile-folder-function (msg) (let ((mu4e-accounts my-mu4e-account-alist) (current-message msg) (account)) (setq account (catch 'found (dolist (candidate mu4e-accounts) (if (string-match (car candidate) (mu4e-message-field current-message :maildir)) (throw 'found candidate) )))) (if account (cadr (assoc 'mu4e-refile-folder account)) (throw 'account_not_found (mu4e-message-field current-message :maildir)) ) ) ) (setq mu4e-refile-folder 'my-mu4e-refile-folder-function) (add-hook 'mu4e-compose-pre-hook 'my-mu4e-set-account) ;; Be smart about inserting signature for either cite-reply-position used (defun insert-signature () "Insert signature where you are replying" ;; Do not insert if already done - needed when switching modes back/forth (unless (save-excursion (message-goto-signature)) (save-excursion (if (eq message-cite-reply-position 'below) (goto-char (point-max)) (message-goto-body)) (insert-file-contents message-signature-file) (save-excursion (insert "\n-- \n"))))) (add-hook 'mu4e-compose-mode-hook 'insert-signature) ;;(add-to-list 'mu4e-bookmarks ;; '("maildir:/work/INBOX" "work inbox" ?w)) ;;(add-to-list 'mu4e-bookmarks ;; '("maildir:/knazarov/INBOX" "personal inbox" ?p)) (setq mu4e-bookmarks '(("maildir:/personal/INBOX OR maildir:/work/INBOX" "inbox" ?i))) ) ;;(add-to-list 'load-path "/usr/share/emacs/site-lisp/mu4e") ;; -------- Programming -------- (use-package eldoc :defer t :diminish eldoc-mode :hook (prog-mode . eldoc-mode) :init ;;(setq eldoc-idle-delay 0.1) ) ;; Rainbow delimeters highlight matching pairs of braces in different colors (use-package rainbow-delimiters :ensure t :hook (prog-mode . rainbow-delimiters-mode) ) ;; Flycheck is an on-the-fly syntax checker for emacs with pluggable backengs. (use-package flycheck :ensure t :hook (prog-mode . rainbow-delimiters-mode) :diminish flycheck-mode ) (use-package flycheck-eglot :ensure t :after (flycheck eglot) :config (global-flycheck-eglot-mode 1)) ;; Not usable for every single mode, so need to be selective ;;(add-hook 'prog-mode-hook #'flycheck-mode) ;; Scroll compilation buffer with the output (setq compilation-scroll-output t) ;; Projectile auto-detects projects and allows to run project-wide commands (use-package projectile :ensure t :defer 0.1 :diminish projectile-mode :config (setq projectile-cache-file "~/.emacs.d/projectile.cache") (projectile-mode +1) (define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map) ) ;; Corfu mode is a simple auto completion framework with ;; pluggable backends ;;(setq company-backends '(company-capf (company-dabbrev-code) company-dabbrev)) ;;(add-hook 'prog-mode-hook #'company-mode) ;;(add-hook 'mu4e-compose-mode-hook #'company-mode) (use-package corfu :ensure t :defer 1 :init (setq corfu-auto t) (setq tab-always-indent 'complete) (setq corfu-quit-no-match 'separator) :config (global-corfu-mode) (corfu-popupinfo-mode) ) (use-package corfu-terminal :ensure t :after corfu :config (unless (display-graphic-p) (corfu-terminal-mode +1)) ) ;; (setq corfu-auto t) ;;(setq tab-always-indent 'complete) ;;(setq corfu-quit-no-match 'separator) ;;(global-corfu-mode) ;;(corfu-popupinfo-mode) (use-package cape :ensure t :defer 0.1 :config (add-to-list 'completion-at-point-functions #'cape-dabbrev) (add-to-list 'completion-at-point-functions #'cape-file) ) ;; Can't live without magit. It makes working with git sooo much easie (use-package magit :ensure t :bind ("C-x g" . magit-status) ) ;;(setq magit-completing-read-function 'magit-ido-completing-read) ;; Tree-sitter (setq major-mode-remap-alist '((yaml-mode . yaml-ts-mode) (bash-mode . bash-ts-mode) (js2-mode . js-ts-mode) (typescript-mode . typescript-ts-mode) (json-mode . json-ts-mode) (css-mode . css-ts-mode) (python-mode . python-ts-mode) (cpp-mode . cpp-ts-mode) (c-mode . c-ts-mode) )) (setq treesit-font-lock-level 4) ;; vterm ;; Vterm is a fully featured terminal emulator, that works inside ;; emacs buffers. It is miles ahead of "term" and "eshell" both in ;; speed and features. (setq vterm-always-compile-module t) (use-package vterm :ensure t :defer t :commands vterm :config (defun vterm-less (content) (let ((less-buffer (get-buffer-create (make-temp-name "vterm-less-")))) (with-current-buffer less-buffer (switch-to-buffer less-buffer) (special-mode) (insert (base64-decode-string content))) ) ) (defun my-vterm-mode-hook () (add-to-list 'vterm-eval-cmds (list "less" #'vterm-less)) (with-editor-export-editor) ) (add-hook 'vterm-mode-hook #'my-vterm-mode-hook) (defun vterm-new () (interactive) (setq current-prefix-arg '(4)) ; C-u (call-interactively 'vterm)) ) ;;(add-to-list 'load-path (expand-file-name "~/dev/emacs-libvterm/")) (use-package with-editor :ensure t :defer 1 ) (put 'upcase-region 'disabled nil) ;; lua (use-package lua-mode :ensure t :mode "\\.lua$" :config (setq lua-indent-level 4) ) ;; common lisp ;;(use-package sly) ;;(use-package sly-asdf) ;;(use-package aggressive-indent) ;;(add-to-list 'sly-contribs 'sly-asdf 'append) ;;(autoload 'sly "sly" ;;"Start an inferior^_superior Lisp and connect to its Swank server." ;;t nil) ;;(define-key lisp-mode-map (kbd "C-c v") ;;'sly-asdf-test-system) ;autoload 'sly-lisp-mode-hook "sly" ; "Set up sly for a lisp buffer." ; t nil) ;;(add-hook 'lisp-mode-hook #'aggressive-indent-mode) ;(add-hook 'lisp-mode-hook 'sly-lisp-mode-hook) ;(setq slime-contribs '(slime-fancy slime-company)) ;;(setq inferior-lisp-program "sbcl") ;; Markdown (use-package markdown-mode :ensure t :mode "\\.md$" ) ;; Dockerfiles (use-package dockerfile-mode :ensure t :mode "Dockerfile$" ) ;; Yaml (use-package yaml-mode :ensure t :mode "\\.(yaml|yml)$" ) ;; CMake (use-package cmake-mode :ensure t :mode "CMakeLists.txt$" ) ;; C/C++ (defun eldoc-short (arg) "`eldoc' but uses the echo area by default and a prefix will swap to a buffer." ;; https://github.com/joaotavora/eglot/discussions/1328 (interactive "P") (let (_) (defvar eldoc-display-functions) (setq eldoc-display-functions (if arg '(eldoc-display-in-buffer) '(eldoc-display-in-echo-area))) (eldoc t))) (defun nix-clangd (int) (let ((curproj (project-current nil))) (if (and curproj (file-exists-p (format "%s/flake.nix" (project-root curproj)))) (list "nix" "develop" (format "%s#" (project-root curproj)) "--command" "bash" "-c" "clangd") (list "clangd") )) ) (defun nix-gopls (int) (let ((curproj (project-current nil))) (if (and curproj (file-exists-p (format "%s/flake.nix" (project-root curproj)))) (list "nix" "develop" (format "%s#" (project-root curproj)) "--command" "bash" "-c" "gopls") (list "gopls") )) ) (defun nix-build () (let ((curproj (project-current nil))) (when (and curproj (file-exists-p (format "%s/flake.nix" (project-root curproj))) (file-exists-p (format "%s/CMakeLists.txt" (project-root curproj))) (file-exists-p (format "%s/build" (project-root curproj))) ) (list "nix" "develop" (format "%s#" (project-root curproj)) "--command" "bash" "-c" (format "cd %s/build && eval \"$buildPhase\"" (project-root curproj))) )) ) (with-eval-after-load 'eglot (add-hook 'eglot-managed-mode-hook (lambda () ;; we want eglot to setup callbacks from eldoc, but we don't want eldoc ;; running after every command. As a workaround, we disable it after we just ;; enabled it. Now calling `M-x eldoc` will put the help we want in the eldoc ;; buffer. Alternatively we could tell eglot to stay out of eldoc, and add ;; the hooks manually, but that seems fragile to updates in eglot. ;; (eldoc-mode -1) )) (add-to-list 'eglot-server-programs `(c++-mode . ,#'nix-clangd)) (add-to-list 'eglot-server-programs `(c++-ts-mode . ,#'nix-clangd)) (add-to-list 'eglot-server-programs `(c-ts-mode . ,#'nix-clanngd)) (add-to-list 'eglot-server-programs `(go-ts-mode . ,#'nix-gopls)) (add-to-list 'eglot-server-programs `(go-mode . ,#'nix-gopls)) ) (add-hook 'c++-mode-hook 'eglot-ensure) (add-hook 'c++-ts-mode-hook 'eglot-ensure) (add-hook 'c-ts-mode-hook 'eglot-ensure) (add-hook 'go-ts-mode-hook 'eglot-ensure) (add-hook 'go-mode-hook 'eglot-ensure) (use-package clang-format :ensure t :defer 2 :config ) (use-package clang-format+ :ensure t :defer 2 :config (add-hook 'c-mode-hook 'clang-format+-mode) (add-hook 'c-ts-mode-hook 'clang-format+-mode) (add-hook 'c++-mode-hook 'clang-format+-mode) (add-hook 'c++-ts-mode-hook 'clang-format+-mode) (setq clang-format+-context 'buffer) ) ;; Go (use-package go-mode :ensure t :mode "\\.go$" :config (setq gofmt-show-errors 'echo) (add-hook 'before-save-hook #'gofmt-before-save) ) ;; Zig (use-package zig-mode :ensure t :mode "\\.zig$" ) ;; Terraform (use-package terraform-mode :ensure t :mode "\\.tf$" ) ;; Bash (add-hook 'sh-mode-hook (lambda () (setq indent-tabs-mode t))) ;; Nix (use-package nix-mode :ensure t :mode "\\.nix$" ) (use-package sudo-edit :ensure t :bind ("C-c C-r" . sudo-edit) ) ;; Solidity (use-package solidity-mode :ensure t :mode "\\.sol$" ) ;; Valeri (use-package lisp-mode :ensure nil :mode "\\.vli$" ) ;; LLM (use-package gptel :ensure t :config (setq gptel-model 'fastgpt) (setq gptel-kagi-api-key (lambda () (with-temp-buffer (insert-file-contents "/var/run/secrets/kagi_api_key") (buffer-string)) ) ) (setq gptel-backend (gptel-make-kagi "Kagi" :key gptel-kagi-api-key)) (setq gptel-org-branching-context t) ) (provide 'init) ;;; init.el ends here