1318 lines
39 KiB
EmacsLisp
Executable file
1318 lines
39 KiB
EmacsLisp
Executable file
;; -------- 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)
|
|
(setq mode-line-end-spaces 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
|
|
:diminish which-key-mode
|
|
: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 "<leader>f") 'find-file)
|
|
(evil-define-key 'normal 'global (kbd "<leader>d") 'dired-jump)
|
|
(evil-define-key 'normal 'global (kbd "<leader>b") 'switch-to-buffer)
|
|
(evil-define-key 'normal 'global (kbd "<leader>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-modern-star 'replace)
|
|
)
|
|
|
|
(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><html><body><p>Hello ! I am the html signature which can contains anything in html !</p></body></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
|