dotfiles/.emacs.d/init.el

4551 lines
141 KiB
EmacsLisp

(defvar bootstrap-version)
(let ((bootstrap-file
(expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
(bootstrap-version 5))
(unless (file-exists-p bootstrap-file)
(with-current-buffer
(url-retrieve-synchronously
"https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
'silent 'inhibit-cookies)
(goto-char (point-max))
(eval-print-last-sexp)))
(load bootstrap-file nil 'nomessage))
(straight-use-package 'use-package)
(eval-when-compile (require 'use-package))
(setq my/lowpower (string= (system-name) "azure"))
(setq my/slow-ssh
(or
(string= (getenv "IS_TRAMP") "true")
(string= (system-name) "dev-digital")
(string= (system-name) "violet")))
(setq my/remote-server
(or (string= (getenv "IS_REMOTE") "true")
(string= (system-name) "dev-digital")
(string= (system-name) "violet")))
(setq my/is-termux (string-match-p (rx (* nonl) "com.termux" (* nonl)) (getenv "HOME")))
(setenv "IS_EMACS" "true")
(add-hook 'emacs-startup-hook
(lambda ()
(message "*** Emacs loaded in %s with %d garbage collections."
(format "%.2f seconds"
(float-time
(time-subtract after-init-time before-init-time)))
gcs-done)))
;; (setq use-package-verbose t)
(setq gc-cons-threshold 80000000)
(setq read-process-output-max (* 1024 1024))
(add-hook 'emacs-startup-hook
(lambda ()
(if (boundp 'after-focus-change-function)
(add-function :after after-focus-change-function
(lambda ()
(unless (frame-focus-state)
(garbage-collect))))
(add-hook 'after-focus-change-function 'garbage-collect))))
(when my/lowpower
(setq comp-async-jobs-number 1))
(use-package conda
:straight t
:if (executable-find "conda")
:config
(setq conda-anaconda-home (string-replace "/bin/conda" "" (executable-find "conda")))
(setq conda-env-home-directory (expand-file-name "~/.conda/"))
(setq conda-env-subdirectory "envs")
(setenv "INIT_CONDA" "true")
(advice-add 'conda-env-activate :after
(lambda (&rest _) (setenv "EMACS_CONDA_ENV" conda-env-current-name)))
(unless (getenv "CONDA_DEFAULT_ENV")
(conda-env-activate "general")))
(setq custom-file (concat user-emacs-directory "custom.el"))
(load custom-file 'noerror)
(let ((private-file (expand-file-name "private.el" user-emacs-directory)))
(load-file private-file))
(use-package no-littering
:straight t)
(setq confirm-kill-emacs 'y-or-n-p)
(use-package general
:straight t
:config
(general-evil-setup))
(use-package which-key
:config
(setq which-key-idle-delay (if my/lowpower 1 0.3))
(setq which-key-popup-type 'frame)
(which-key-mode)
(which-key-setup-side-window-bottom)
(set-face-attribute 'which-key-local-map-description-face nil
:weight 'bold)
:straight t)
(defun my/dump-bindings-recursive (prefix &optional level)
(dolist (key (which-key--get-bindings (kbd prefix)))
(when level
(insert (make-string level ? )))
(insert (apply #'format "%s%s%s\n" key))
(when (string-match-p
(rx bos "+" (* nonl))
(substring-no-properties (elt key 2)))
(my/dump-bindings-recursive
(concat prefix " " (substring-no-properties (car key)))
(+ 2 (or level 0))))))
(defun my/dump-bindings (prefix)
"Dump keybindings starting with PREFIX in a tree-like form."
(interactive "sPrefix: ")
(with-current-buffer (get-buffer-create "bindings")
(point-max)
(erase-buffer)
(save-excursion
(my/dump-bindings-recursive prefix)))
(switch-to-buffer-other-window "bindings"))
(use-package evil
:straight t
:init
(setq evil-want-integration t)
(setq evil-want-C-u-scroll t)
(setq evil-want-keybinding nil)
(setq evil-search-module 'evil-search)
(setq evil-split-window-below t)
(setq evil-vsplit-window-right t)
(unless (display-graphic-p)
(setq evil-want-C-i-jump nil))
:config
(evil-mode 1)
;; (setq evil-respect-visual-line-mode t)
(evil-set-undo-system 'undo-tree))
(use-package evil-surround
:straight t
:after evil
:config
(global-evil-surround-mode 1))
(use-package evil-commentary
:straight t
:after evil
:config
(evil-commentary-mode))
(use-package evil-quickscope
:straight t
:after evil
:config
:hook ((prog-mode . turn-on-evil-quickscope-mode)
(LaTeX-mode . turn-on-evil-quickscope-mode)
(org-mode . turn-on-evil-quickscope-mode)))
(use-package evil-numbers
:straight t
:commands (evil-numbers/inc-at-pt evil-numbers/dec-at-pt)
:init
(general-nmap
"g+" 'evil-numbers/inc-at-pt
"g-" 'evil-numbers/dec-at-pt))
(use-package evil-lion
:straight t
:config
(setq evil-lion-left-align-key (kbd "g a"))
(setq evil-lion-right-align-key (kbd "g A"))
(evil-lion-mode))
(use-package evil-matchit
:straight t
:config
(global-evil-matchit-mode 1))
(defun my/evil-ex-search-word-forward-other-window (count &optional symbol)
(interactive (list (prefix-numeric-value current-prefix-arg)
evil-symbol-word-search))
(save-excursion
(evil-ex-start-word-search nil 'forward count symbol))
(other-window 1)
(evil-ex-search-next))
(general-define-key
:states '(normal)
"&" #'my/evil-ex-search-word-forward-other-window)
(use-package evil-collection
:straight t
:after evil
:config
(evil-collection-init
'(eww
devdocs
proced
emms
pass
calendar
dired
ivy
debug
guix
calc
docker
ibuffer
geiser
pdf
info
elfeed
edebug
bookmark
company
vterm
flycheck
profiler
cider
explain-pause-mode
notmuch
custom
xref
eshell
helpful
compile
comint
git-timemachine
magit
prodigy
slime)))
(defun minibuffer-keyboard-quit ()
"Abort recursive edit.
In Delete Selection mode, if the mark is active, just deactivate it;
then it takes a second \\[keyboard-quit] to abort the minibuffer."
(interactive)
(if (and delete-selection-mode transient-mark-mode mark-active)
(setq deactivate-mark t)
(when (get-buffer "*Completions*") (delete-windows-on "*Completions*"))
(abort-recursive-edit)))
(defun my/escape-key ()
(interactive)
(evil-ex-nohighlight)
(keyboard-quit))
(general-define-key
:keymaps '(normal visual global)
[escape] #'my/escape-key)
(general-define-key
:keymaps '(minibuffer-local-map
minibuffer-local-ns-map
minibuffer-local-completion-map
minibuffer-local-must-match-map
minibuffer-local-isearch-map)
[escape] 'minibuffer-keyboard-quit)
(general-def :states '(normal insert visual)
"<home>" 'beginning-of-line
"<end>" 'end-of-line)
(general-create-definer my-leader-def
:keymaps 'override
:prefix "SPC"
:states '(normal motion emacs))
(general-def :states '(normal motion emacs)
"SPC" nil
"M-SPC" (general-key "SPC"))
(general-def :states '(insert)
"M-SPC" (general-key "SPC" :state 'normal))
(my-leader-def "?" 'which-key-show-top-level)
(my-leader-def "E" 'eval-expression)
(my-leader-def
"a" '(:which-key "apps"))
(general-def
:keymaps 'universal-argument-map
"M-u" 'universal-argument-more)
(general-def
:keymaps 'override
:states '(normal motion emacs insert visual)
"M-u" 'universal-argument)
(my-leader-def
:infix "P"
"" '(:which-key "profiler")
"s" 'profiler-start
"e" 'profiler-stop
"p" 'profiler-report)
(general-define-key
:keymaps 'override
"C-<right>" 'evil-window-right
"C-<left>" 'evil-window-left
"C-<up>" 'evil-window-up
"C-<down>" 'evil-window-down
"C-h" 'evil-window-left
"C-l" 'evil-window-right
"C-k" 'evil-window-up
"C-j" 'evil-window-down
"C-x h" 'previous-buffer
"C-x l" 'next-buffer)
(general-define-key
:keymaps 'evil-window-map
"x" 'kill-buffer-and-window
"d" 'kill-current-buffer)
(winner-mode 1)
(general-define-key
:keymaps 'evil-window-map
"u" 'winner-undo
"U" 'winner-redo)
(my-leader-def
:infix "b"
"" '(:which-key "buffers")
"s" '((lambda () (interactive) (switch-to-buffer (persp-scratch-buffer)))
:which-key "*scratch*")
"m" '((lambda () (interactive) (persp-switch-to-buffer "*Messages*"))
:which-key "*Messages*")
"l" 'next-buffer
"h" 'previous-buffer
"k" 'kill-buffer
"b" 'persp-ivy-switch-buffer
"r" 'revert-buffer
"u" 'ibuffer)
(general-nmap
"gD" 'xref-find-definitions-other-window
"gr" 'xref-find-references
"gd" 'evil-goto-definition)
(my-leader-def
"fx" 'xref-find-apropos)
(general-nmap :keymaps '(hs-minor-mode-map outline-minor-mode-map)
"ze" 'hs-hide-level
"TAB" 'evil-toggle-fold)
(defun my/zoom-in ()
"Increase font size by 10 points"
(interactive)
(set-face-attribute 'default nil
:height
(+ (face-attribute 'default :height) 10)))
(defun my/zoom-out ()
"Decrease font size by 10 points"
(interactive)
(set-face-attribute 'default nil
:height
(- (face-attribute 'default :height) 10)))
;; change font size, interactively
(global-set-key (kbd "C-+") 'my/zoom-in)
(global-set-key (kbd "C-=") 'my/zoom-out)
(add-hook 'after-init-hook #'server-start)
(defmacro i3-msg (&rest args)
`(start-process "emacs-i3-windmove" nil "i3-msg" ,@args))
(defun my/emacs-i3-windmove (dir)
(let ((other-window (windmove-find-other-window dir)))
(if (or (null other-window) (window-minibuffer-p other-window))
(i3-msg "focus" (symbol-name dir))
(windmove-do-window-select dir))))
(defun my/emacs-i3-direction-exists-p (dir)
(cl-some (lambda (dir)
(let ((win (windmove-find-other-window dir)))
(and win (not (window-minibuffer-p win)))))
(pcase dir
('width '(left right))
('height '(up down)))))
(defun my/emacs-i3-move-window (dir)
(let ((other-window (windmove-find-other-window dir))
(other-direction (my/emacs-i3-direction-exists-p
(pcase dir
('up 'width)
('down 'width)
('left 'height)
('right 'height)))))
(cond
((and other-window (not (window-minibuffer-p other-window)))
(window-swap-states (selected-window) other-window))
(other-direction
(evil-move-window dir))
(t (i3-msg "move" (symbol-name dir))))))
(defun my/emacs-i3-resize-window (dir kind value)
(if (or (one-window-p)
(not (my/emacs-i3-direction-exists-p dir)))
(i3-msg "resize" (symbol-name kind) (symbol-name dir)
(format "%s px or %s ppt" value value))
(setq value (/ value 2))
(pcase kind
('shrink
(pcase dir
('width
(evil-window-decrease-width value))
('height
(evil-window-decrease-height value))))
('grow
(pcase dir
('width
(evil-window-increase-width value))
('height
(evil-window-increase-height value)))))))
(use-package transpose-frame
:straight t
:commands (transpose-frame))
(defun my/emacs-i3-integration (command)
(pcase command
((rx bos "focus")
(my/emacs-i3-windmove
(intern (elt (split-string command) 1))))
((rx bos "move")
(my/emacs-i3-move-window
(intern (elt (split-string command) 1))))
((rx bos "resize")
(my/emacs-i3-resize-window
(intern (elt (split-string command) 2))
(intern (elt (split-string command) 1))
(string-to-number (elt (split-string command) 3))))
("layout toggle split" (transpose-frame))
("split h" (evil-window-split))
("split v" (evil-window-vsplit))
("kill" (evil-quit))
(- (i3-msg command))))
(use-package aggressive-indent
:commands (aggressive-indent-mode)
:straight t)
(setq my/trailing-whitespace-modes '(markdown-mode))
(require 'cl-extra)
(add-hook 'before-save-hook
(lambda ()
(unless (cl-some #'derived-mode-p my/trailing-whitespace-modes)
(delete-trailing-whitespace))))
(setq tab-always-indent nil)
(setq-default default-tab-width 4)
(setq-default tab-width 4)
(setq-default evil-indent-convert-tabs nil)
(setq-default indent-tabs-mode nil)
(setq-default tab-width 4)
(setq-default evil-shift-round nil)
(setq scroll-conservatively scroll-margin)
(setq scroll-step 1)
(setq scroll-preserve-screen-position t)
(setq scroll-error-top-bottom t)
(setq mouse-wheel-progressive-speed nil)
(setq mouse-wheel-inhibit-click-time nil)
(setq select-enable-clipboard t)
(setq mouse-yank-at-point t)
(setq backup-inhibited t)
(setq auto-save-default nil)
(use-package undo-tree
:straight t
:config
(global-undo-tree-mode)
(setq undo-tree-visualizer-diff t)
(setq undo-tree-visualizer-timestamps t)
(my-leader-def "u" 'undo-tree-visualize)
(fset 'undo-auto-amalgamate 'ignore)
(setq undo-limit 6710886400)
(setq undo-strong-limit 100663296)
(setq undo-outer-limit 1006632960))
(use-package yasnippet-snippets
:straight t)
(use-package yasnippet
:straight t
:config
(setq yas-snippet-dirs
`(,(concat (expand-file-name user-emacs-directory) "snippets")
yasnippet-snippets-dir))
(setq yas-triggers-in-field t)
(yas-global-mode 1))
(general-imap "M-TAB" 'company-yasnippet)
(use-package smartparens
:straight t)
(use-package expand-region
:straight t
:commands (er/expand-region)
:init
(general-nmap "+" 'er/expand-region))
(use-package visual-fill-column
:straight t
:config
(add-hook 'visual-fill-column-mode-hook
(lambda () (setq visual-fill-column-center-text t))))
(use-package treemacs
:straight t
:commands (treemacs treemacs-switch-workspace treemacs-edit-workspace)
:config
(setq treemacs-follow-mode nil)
(setq treemacs-follow-after-init nil)
(setq treemacs-space-between-root-nodes nil)
(treemacs-git-mode 'extended)
(add-to-list 'treemacs-pre-file-insert-predicates #'treemacs-is-file-git-ignored?)
(general-define-key
:keymaps 'treemacs-mode-map
[mouse-1] #'treemacs-single-click-expand-action
"M-l" #'treemacs-root-down
"M-h" #'treemacs-root-up))
(use-package treemacs-evil
:after (treemacs evil)
:straight t)
(use-package treemacs-magit
:after (treemacs magit)
:straight t)
(use-package treemacs-perspective
:after (treemacs perspective)
:straight t
:config
(treemacs-set-scope-type 'Perspectives))
(general-define-key
:keymaps '(normal override global)
"C-n" 'treemacs)
(general-define-key
:keymaps '(treemacs-mode-map) [mouse-1] #'treemacs-single-click-expand-action)
(my-leader-def
:infix "t"
"" '(:which-key "treemacs")
"w" 'treemacs-switch-workspace
"e" 'treemacs-edit-workspaces)
(defun my/treemacs-open-dired ()
"Open dired at given treemacs node"
(interactive)
(let (path (treemacs--prop-at-point :path))
(dired path)))
(defun my/treemacs-open-vterm ()
"Open vterm at given treemacs node"
(interactive)
(let ((default-directory (file-name-directory (treemacs--prop-at-point :path))))
(vterm)))
(with-eval-after-load 'treemacs
(general-define-key
:keymaps 'treemacs-mode-map
:states '(treemacs)
"gd" 'my/treemacs-open-dired
"gt" 'my/treemacs-open-vterm
"`" 'my/treemacs-open-vterm))
;; (treemacs-define-custom-icon (concat " " (all-the-icons-fileicon "typescript")) "spec.ts")
;; (setq treemacs-file-extension-regex (rx "." (or "spec.ts" (+ (not "."))) eos))
(use-package projectile
:straight t
:config
(projectile-mode +1)
(setq projectile-project-search-path '("~/Code" "~/Documents"))
(defadvice projectile-project-root (around ignore-remote first activate)
(unless (file-remote-p default-directory) ad-do-it)))
(use-package counsel-projectile
:after (counsel projectile)
:straight t)
(use-package treemacs-projectile
:after (treemacs projectile)
:straight t)
(my-leader-def
"p" '(:keymap projectile-command-map :which-key "projectile"))
(general-nmap "C-p" 'counsel-projectile-find-file)
(use-package magit
:straight t
:commands (magit-status magit-file-dispatch)
:config
(setq magit-blame-styles
'((margin
(margin-format . ("%a %A %s"))
(margin-width . 42)
(margin-face . magit-blame-margin)
(margin-body-face . (magit-blame-dimmed)))
(headings
(heading-format . "%-20a %C %s\n"))
(highlight
(highlight-face . magit-blame-highlight))
(lines
(show-lines . t)
(show-message . t)))))
(use-package git-gutter
:straight t
:if (not my/slow-ssh)
:config
(global-git-gutter-mode +1))
(use-package git-timemachine
:straight t
:commands (git-timemachine))
(my-leader-def
"m" 'magit
"M" 'magit-file-dispatch)
(use-package editorconfig
:straight t
:config
(unless my/slow-ssh (editorconfig-mode 1))
(add-to-list 'editorconfig-indentation-alist
'(emmet-mode emmet-indentation)))
(use-package ivy
:straight t
:config
(setq ivy-use-virtual-buffers t)
(ivy-mode))
(use-package counsel
:straight t
:after ivy
:config
(counsel-mode))
(use-package swiper
:defer t
:straight t)
(use-package ivy-rich
:straight t
:after ivy
:config
(ivy-rich-mode 1)
(setcdr (assq t ivy-format-functions-alist) #'ivy-format-function-line))
(use-package ivy-prescient
:straight t
:after counsel
:config
(ivy-prescient-mode +1)
(setq ivy-prescient-retain-classic-highlighting t)
(prescient-persist-mode 1)
(setq ivy-prescient-sort-commands
'(:not swiper
swiper-isearch
ivy-switch-buffer
;; ivy-resume
;; ivy--restore-session
lsp-ivy-workspace-symbol
dap-switch-stack-frame
my/dap-switch-stack-frame
dap-switch-session
dap-switch-thread
counsel-grep
;; counsel-find-file
counsel-git-grep
counsel-rg
counsel-ag
counsel-ack
counsel-fzf
counsel-pt
counsel-imenu
counsel-yank-pop
counsel-recentf
counsel-buffer-or-recentf
proced-filter-interactive
proced-sort-interactive
perspective-exwm-switch-perspective
my/persp-ivy-switch-buffer-other-window
lsp-execute-code-action))
;; Do not use prescient in find-file
(ivy--alist-set 'ivy-sort-functions-alist #'read-file-name-internal #'ivy-sort-file-function-default))
(my-leader-def
:infix "f"
"" '(:which-key "various completions")'
;; "b" 'counsel-switch-buffer
"b" 'persp-ivy-switch-buffer
"e" 'conda-env-activate
"f" 'project-find-file
"c" 'counsel-yank-pop
"a" 'counsel-rg
"A" 'counsel-ag)
(general-define-key
:states '(insert normal)
"C-y" 'counsel-yank-pop)
(my-leader-def "SPC" 'ivy-resume)
(my-leader-def "s" 'swiper-isearch
"S" 'swiper-all)
(general-define-key
:keymaps '(ivy-minibuffer-map swiper-map)
"M-j" 'ivy-next-line
"M-k" 'ivy-previous-line
"<C-return>" 'ivy-call
"M-RET" 'ivy-immediate-done
[escape] 'minibuffer-keyboard-quit)
(use-package company
:straight t
:config
(global-company-mode)
(setq company-idle-delay (if my/lowpower 0.5 0.125))
(setq company-dabbrev-downcase nil)
(setq company-show-numbers t))
(general-imap "C-SPC" 'company-complete)
(use-package company-box
:straight t
:if (and (display-graphic-p) (not my/lowpower))
:after (company)
:hook (company-mode . company-box-mode))
(use-package helpful
:straight t
:commands (helpful-callable
helpful-variable
helpful-key
helpful-macro
helpful-function
helpful-command))
(my-leader-def
"h" '(:keymap help-map :which-key "help"))
(general-define-key
:keymaps 'help-map
"f" 'helpful-function
"k" 'helpful-key
"v" 'helpful-variable
"o" 'helpful-symbol)
(use-package wakatime-mode
:straight (:host github :repo "SqrtMinusOne/wakatime-mode")
:if (not (or my/is-termux my/remote-server))
:config
(setq wakatime-ignore-exit-codes '(0 1 102))
(advice-add 'wakatime-init :after (lambda () (setq wakatime-cli-path "/home/pavel/bin/wakatime-cli")))
;; (setq wakatime-cli-path (executable-find "wakatime"))
(global-wakatime-mode))
(use-package request
:straight t)
(use-package activity-watch-mode
:straight t
:if (not (or my/is-termux my/remote-server))
:config
(global-activity-watch-mode))
(unless my/is-termux
(tool-bar-mode -1)
(menu-bar-mode -1)
(scroll-bar-mode -1))
;; (set-frame-parameter (selected-frame) 'alpha '(90 . 90))
;; (add-to-list 'default-frame-alist '(alpha . (90 . 90)))
;; (global-prettify-symbols-mode)
(setq inhibit-startup-screen t)
(setq visible-bell 0)
(defalias 'yes-or-no-p 'y-or-n-p)
(setq make-pointer-invisible t)
(show-paren-mode 1)
(global-hl-line-mode 1)
(global-display-line-numbers-mode 1)
(line-number-mode nil)
(setq display-line-numbers-type 'visual)
(column-number-mode)
(setq word-wrap 1)
(global-visual-line-mode 1)
(setq-default frame-title-format
'(""
"emacs"
;; (:eval
;; (let ((project-name (projectile-project-name)))
;; (if (not (string= "-" project-name))
;; (format ":%s@%s" project-name (system-name))
;; (format "@%s" (system-name)))))
))
(use-package auto-dim-other-buffers
:straight t
:if (display-graphic-p)
:config
(auto-dim-other-buffers-mode t))
(use-package doom-themes
:straight t
:if (not my/is-termux)
:config
(setq doom-themes-enable-bold t
doom-themes-enable-italic t)
(if my/remote-server
(load-theme 'doom-gruvbox t)
(load-theme 'doom-palenight t))
(doom-themes-visual-bell-config)
(setq doom-themes-treemacs-theme "doom-colors")
(doom-themes-treemacs-config))
(unless my/is-termux
(deftheme my-theme-1)
(defun my/update-my-theme (&rest _)
(custom-theme-set-faces
'my-theme-1
`(tab-bar-tab ((t (
:background ,(doom-color 'bg)
:foreground ,(doom-color 'yellow)
:underline ,(doom-color 'yellow)))))
`(org-block ((t (:background ,(color-darken-name (doom-color 'bg) 3)))))
`(org-block-begin-line ((t (
:background ,(color-darken-name (doom-color 'bg) 3)
:foreground ,(doom-color 'grey)))))
`(auto-dim-other-buffers-face ((t (:background ,(color-darken-name (doom-color 'bg) 3)))))
`(aweshell-alert-buffer-face ((t (:foreground ,(doom-color 'red) :weight bold))))
`(aweshell-alert-command-face ((t (:foreground ,(doom-color 'yellow) :weight bold))))
`(epe-pipeline-delimiter-face ((t (:foreground ,(doom-color 'green)))))
`(epe-pipeline-host-face ((t (:foreground ,(doom-color 'blue)))))
`(epe-pipeline-time-face ((t (:foreground ,(doom-color 'yellow)))))
`(epe-pipeline-user-face ((t (:foreground ,(doom-color 'red)))))
`(elfeed-search-tag-face ((t (:foreground ,(doom-color 'yellow)))))
`(notmuch-wash-cited-text ((t (:foreground ,(doom-color 'yellow)))))
`(spaceline-evil-emacs ((t :background ,(doom-color 'bg)
:foreground ,(doom-color 'fg))))
`(spaceline-evil-insert ((t :background ,(doom-color 'green)
:foreground ,(doom-color 'base0))))
`(spaceline-evil-motion ((t :background ,(doom-color 'magenta)
:foreground ,(doom-color 'base0))))
`(spaceline-evil-normal ((t :background ,(doom-color 'blue)
:foreground ,(doom-color 'base0))))
`(spaceline-evil-replace ((t :background ,(doom-color 'yellow)
:foreground ,(doom-color 'base0))))
`(spaceline-evil-visual ((t :background ,(doom-color 'grey)
:foreground ,(doom-color 'base0)))))
(custom-theme-set-variables
'my-theme-1
`(aweshell-invalid-command-color ,(doom-color 'red))
`(aweshell-valid-command-color ,(doom-color 'green)))
(enable-theme 'my-theme-1))
(advice-add 'load-theme :after #'my/update-my-theme)
(when (fboundp 'doom-color)
(my/update-my-theme)))
(set-frame-font "JetBrainsMono Nerd Font 10" nil t)
(use-package ligature
:straight (:host github :repo "mickeynp/ligature.el")
:if (display-graphic-p)
:config
(ligature-set-ligatures
'(
typescript-mode
js2-mode
vue-mode
svelte-mode
scss-mode
php-mode
python-mode
js-mode
markdown-mode
clojure-mode
go-mode
sh-mode
haskell-mode
web-mode)
'("--" "---" "==" "===" "!=" "!==" "=!=" "=:=" "=/=" "<="
">=" "&&" "&&&" "&=" "++" "+++" "***" ";;" "!!" "??"
"?:" "?." "?=" "<:" ":<" ":>" ">:" "<>" "<<<" ">>>"
"<<" ">>" "||" "-|" "_|_" "|-" "||-" "|=" "||=" "##"
"###" "####" "#{" "#[" "]#" "#(" "#?" "#_" "#_(" "#:"
"#!" "#=" "^=" "<$>" "<$" "$>" "<+>" "<+" "+>" "<*>"
"<*" "*>" "</" "</>" "/>" "<!--" "<#--" "-->" "->" "->>"
"<<-" "<-" "<=<" "=<<" "<<=" "<==" "<=>" "<==>" "==>" "=>"
"=>>" ">=>" ">>=" ">>-" ">-" ">--" "-<" "-<<" ">->" "<-<"
"<-|" "<=|" "|=>" "|->" "<->" "<~~" "<~" "<~>" "~~" "~~>"
"~>" "~-" "-~" "~@" "[||]" "|]" "[|" "|}" "{|" "[<"
">]" "|>" "<|" "||>" "<||" "|||>" "<|||" "<|>" "..." ".."
".=" ".-" "..<" ".?" "::" ":::" ":=" "::=" ":?" ":?>"
"//" "///" "/*" "*/" "/=" "//=" "/==" "@_" "__"))
(global-ligature-mode t))
(use-package all-the-icons
:if (display-graphic-p)
:straight t)
(use-package highlight-indent-guides
:straight t
:if (not (or my/lowpower my/remote-server))
:hook (
(prog-mode . highlight-indent-guides-mode)
(vue-mode . highlight-indent-guides-mode)
(LaTeX-mode . highlight-indent-guides-mode))
:config
(setq highlight-indent-guides-method 'bitmap)
(setq highlight-indent-guides-bitmap-function 'highlight-indent-guides--bitmap-line))
(use-package rainbow-delimiters
:straight t
:if (not my/lowpower)
:hook ((prog-mode . rainbow-delimiters-mode)))
(use-package rainbow-mode
:commands (rainbow-mode)
:straight t)
(use-package hl-todo
:hook (prog-mode . hl-todo-mode)
:straight t)
(use-package doom-modeline
:straight t
;; :if (not (display-graphic-p))
:init
(setq doom-modeline-env-enable-python nil)
(setq doom-modeline-env-enable-go nil)
(setq doom-modeline-buffer-encoding 'nondefault)
(setq doom-modeline-hud t)
(setq doom-modeline-persp-icon nil)
(setq doom-modeline-persp-name nil)
:config
(setq doom-modeline-minor-modes nil)
(setq doom-modeline-buffer-state-icon nil)
(doom-modeline-mode 1))
(use-package perspective
:straight t
:init
;; (setq persp-show-modestring 'header)
(setq persp-sort 'created)
:config
(persp-mode)
(my-leader-def "x" '(:keymap perspective-map :which-key "perspective"))
(general-define-key
:keymaps 'override
:states '(normal emacs)
"gt" 'persp-next
"gT" 'persp-prev
"gn" 'persp-switch
"gN" 'persp-kill)
(general-define-key
:keymaps 'perspective-map
"b" 'persp-ivy-switch-buffer
"x" 'persp-ivy-switch-buffer
"u" 'persp-ibuffer))
(defun my/persp-move-window-and-switch ()
(interactive)
(let* ((buffer (current-buffer)))
(call-interactively #'persp-switch)
(persp-set-buffer (buffer-name buffer))
(switch-to-buffer buffer)))
(defun my/persp-copy-window-and-switch ()
(interactive)
(let* ((buffer (current-buffer)))
(call-interactively #'persp-switch)
(persp-add-buffer (buffer-name buffer))
(switch-to-buffer buffer)))
(defun my/persp-ivy-switch-buffer-other-window (arg)
(interactive "P")
(declare-function ivy-switch-buffer-other-window "ivy.el")
(persp--switch-buffer-ivy-counsel-helper
arg
(lambda ()
(ivy-read "Switch to buffer in other window: " #'internal-complete-buffer
:keymap ivy-switch-buffer-map
:preselect (buffer-name (other-buffer (current-buffer)))
:action #'ivy--switch-buffer-other-window-action
:matcher #'ivy--switch-buffer-matcher
:caller 'ivy-switch-buffer))))
(with-eval-after-load 'perspective
(general-define-key
:keymaps 'perspective-map
"m" #'my/persp-move-window-and-switch
"f" #'my/persp-copy-window-and-switch))
(defmacro my/command-in-persp (command-name persp-name workspace-index &rest args)
`'((lambda ()
(interactive)
(when (and ,workspace-index (fboundp #'exwm-workspace-switch-create))
(exwm-workspace-switch-create ,workspace-index))
(persp-switch ,persp-name)
(delete-other-windows)
,@args)
:wk ,command-name))
(use-package lsp-mode
:straight t
:if (not (or my/slow-ssh my/is-termux my/remote-server))
:hook (
(typescript-mode . lsp)
(js-mode . lsp)
(vue-mode . lsp)
(go-mode . lsp)
(svelte-mode . lsp)
;; (python-mode . lsp)
(json-mode . lsp)
(haskell-mode . lsp)
(haskell-literate-mode . lsp)
(java-mode . lsp)
;; (csharp-mode . lsp)
)
:commands lsp
:init
(setq lsp-keymap-prefix nil)
:config
(setq lsp-idle-delay 1)
(setq lsp-eslint-server-command '("node" "/home/pavel/.emacs.d/.cache/lsp/eslint/unzipped/extension/server/out/eslintServer.js" "--stdio"))
(setq lsp-eslint-run "onSave")
(setq lsp-signature-render-documentation nil)
;; (lsp-headerline-breadcrumb-mode nil)
(setq lsp-headerline-breadcrumb-enable nil)
(setq lsp-modeline-code-actions-enable nil)
(setq lsp-modeline-diagnostics-enable nil)
(add-to-list 'lsp-language-id-configuration '(svelte-mode . "svelte")))
(use-package lsp-ui
:straight t
:commands lsp-ui-mode
:config
(setq lsp-ui-doc-delay 2)
(setq lsp-ui-sideline-show-hover nil))
;; (use-package helm-lsp
;; :straight t
;; :commands helm-lsp-workspace-symbol)
;; (use-package origami
;; :straight t
;; :hook (prog-mode . origami-mode))
;; (use-package lsp-origami
;; :straight t
;; :config
;; (add-hook 'lsp-after-open-hook #'lsp-origami-try-enable))
(use-package lsp-treemacs
:after (lsp)
:straight t
:commands lsp-treemacs-errors-list)
(my-leader-def
:infix "l"
"" '(:which-key "lsp")
"d" 'lsp-ui-peek-find-definitions
"r" 'lsp-rename
"u" 'lsp-ui-peek-find-references
"s" 'lsp-ui-find-workspace-symbol
"l" 'lsp-execute-code-action
"e" 'list-flycheck-errors)
(use-package flycheck
:straight t
:config
(global-flycheck-mode)
(setq flycheck-check-syntax-automatically '(save idle-buffer-switch mode-enabled))
;; (add-hook 'evil-insert-state-exit-hook
;; (lambda ()
;; (if flycheck-checker
;; (flycheck-buffer))
;; ))
(advice-add 'flycheck-eslint-config-exists-p :override (lambda() t))
(add-to-list 'display-buffer-alist
`(,(rx bos "*Flycheck errors*" eos)
(display-buffer-reuse-window
display-buffer-in-side-window)
(side . bottom)
(reusable-frames . visible)
(window-height . 0.33))))
(defun my/tree-sitter-if-not-mmm ()
(when (not (and (boundp 'mmm-temp-buffer-name)
(string-equal mmm-temp-buffer-name (buffer-name))))
(tree-sitter-mode)
(tree-sitter-hl-mode)))
(use-package tree-sitter
:straight t
:if (not my/remote-server)
:hook ((typescript-mode . my/tree-sitter-if-not-mmm)
(js-mode . my/tree-sitter-if-not-mmm)
(python-mode . tree-sitter-mode)
(python-mode . tree-sitter-hl-mode)
(csharp-mode . tree-sitter-mode)))
(use-package tree-sitter-langs
:straight t
:after tree-sitter)
(use-package dap-mode
:straight t
:commands (dap-debug)
:init
(setq lsp-enable-dap-auto-configure nil)
:config
(setq dap-ui-variable-length 100)
(setq dap-auto-show-output nil)
(require 'dap-node)
(dap-node-setup)
(require 'dap-chrome)
(dap-chrome-setup)
(require 'dap-python)
(dap-mode 1)
(dap-ui-mode 1)
(dap-tooltip-mode 1)
(tooltip-mode 1))
(with-eval-after-load 'dap-mode
(defmacro my/define-dap-ui-window-toggler (name)
`(defun ,(intern (concat "my/dap-ui-toggle-" name)) ()
,(concat "Toggle DAP " name "buffer")
(interactive)
(if-let (window (get-buffer-window ,(intern (concat "dap-ui--" name "-buffer"))))
(quit-window nil window)
(,(intern (concat "dap-ui-" name))))))
(my/define-dap-ui-window-toggler "locals")
(my/define-dap-ui-window-toggler "expressions")
(my/define-dap-ui-window-toggler "sessions")
(my/define-dap-ui-window-toggler "breakpoints")
(my/define-dap-ui-window-toggler "repl"))
(defhydra my/dap-hydra (:color pink :hint nil :foreign-keys run)
"
^Stepping^ ^UI^ ^Switch^ ^Breakpoints^ ^Debug^ ^Expressions
^^^^^^^^------------------------------------------------------------------------------------------------------------------------------------------
_n_: Next _uc_: Controls _ss_: Session _bb_: Toggle _dd_: Debug _ee_: Eval
_i_: Step in _ue_: Expressions _st_: Thread _bd_: Delete _dr_: Debug recent _er_: Eval region
_o_: Step out _ul_: Locals _sf_: Stack frame _ba_: Add _dl_: Debug last _es_: Eval thing at point
_c_: Continue _ur_: REPL _su_: Up stack frame _bc_: Set condition _de_: Edit debug template _ea_: Add expression
_r_: Restart frame _uo_: Output _sd_: Down stack frame _bh_: Set hit count _Q_: Disconnect _ed_: Remove expression
_us_: Sessions _sF_: Stack frame filtered _bl_: Set log message _eu_: Refresh expressions
_ub_: Breakpoints "
("n" dap-next)
("i" dap-step-in)
("o" dap-step-out)
("c" dap-continue)
("r" dap-restart-frame)
("uc" dap-ui-controls-mode)
("ue" my/dap-ui-toggle-expressions)
("ul" my/dap-ui-toggle-locals)
("ur" my/dap-ui-toggle-repl)
("uo" dap-ui-go-to-output-buffer)
("us" my/dap-ui-toggle-sessions)
("ub" my/dap-ui-toggle-breakpoints)
("ss" dap-switch-session)
("st" dap-switch-thread)
("sf" dap-switch-stack-frame)
("sF" my/dap-switch-stack-frame)
("su" dap-up-stack-frame)
("sd" dap-down-stack-frame)
("bb" dap-breakpoint-toggle)
("ba" dap-breakpoint-add)
("bd" dap-breakpoint-delete)
("bc" dap-breakpoint-condition)
("bh" dap-breakpoint-hit-condition)
("bl" dap-breakpoint-log-message)
("dd" dap-debug)
("dr" dap-debug-recent)
("dl" dap-debug-last)
("de" dap-debug-edit-template)
("ee" dap-eval)
("ea" dap-ui-expressions-add)
("er" dap-eval-region)
("es" dap-eval-thing-at-point)
("ed" dap-ui-expressions-remove)
("eu" dap-ui-expressions-refresh)
("q" nil "quit" :color blue)
("Q" dap-disconnect :color red))
(my-leader-def "d" #'my/dap-hydra/body)
(defvar my/dap-mode-buffer-fixed nil)
(with-eval-after-load 'dap-mode
(defmacro my/define-dap-tree-buffer-fixer (buffer-var buffer-name)
`(defun ,(intern (concat "my/fix-dap-ui-" buffer-name "-buffer")) (&rest _)
(with-current-buffer ,buffer-var
(unless my/dap-mode-buffer-fixed
(toggle-truncate-lines 1)
(doom-modeline-set-modeline 'info)
(setq-local my/dap-mode-buffer-fixed t)))))
(my/define-dap-tree-buffer-fixer dap-ui--locals-buffer "locals")
(my/define-dap-tree-buffer-fixer dap-ui--expressions-buffer "expressions")
(my/define-dap-tree-buffer-fixer dap-ui--sessions-buffer "sessions")
(my/define-dap-tree-buffer-fixer dap-ui--breakpoints-buffer "breakpoints")
(advice-add 'dap-ui-locals :after #'my/fix-dap-ui-locals-buffer)
(advice-add 'dap-ui-expressions :after #'my/fix-dap-ui-expressions-buffer)
(advice-add 'dap-ui-sessions :after #'my/fix-dap-ui-sessions-buffer)
(advice-add 'dap-ui-breakpoints :after #'my/fix-dap-ui-breakpoints-buffer))
(defun my/clear-bad-window-parameters ()
"Clear window parameters that interrupt my workflow."
(interactive)
(let ((window (get-buffer-window (current-buffer))))
(set-window-parameter window 'no-delete-other-windows nil)))
(defun my/dap-yank-value-at-point (node)
(interactive (list (treemacs-node-at-point)))
(kill-new (message (plist-get (button-get node :item) :value))))
(defun my/dap-display-value (node)
(interactive (list (treemacs-node-at-point)))
(let ((value (plist-get (button-get node :item) :value)))
(when value
(let ((buffer (generate-new-buffer "dap-value")))
(with-current-buffer buffer
(insert value))
(select-window (display-buffer buffer))))))
(with-eval-after-load 'dap-mode
(setq my/dap-stack-frame-filters
`(("node_modules,node:internal" . ,(rx (or "node_modules" "node:internal")))
("node_modules" . ,(rx (or "node_modules")))
("node:internal" . ,(rx (or "node:internal")))))
(setq my/dap-stack-frame-current-filter (cdar my/dap-stack-frame-filters))
(defun my/dap-stack-frame-filter-set ()
(interactive)
(setq my/dap-stack-frame-current-filter
(cdr
(assoc
(completing-read "Filter: " my/dap-stack-frame-filters)
my/dap-stack-frame-filters))))
(defun my/dap-stack-frame-filter (frame)
(when-let (path (dap--get-path-for-frame frame))
(not (string-match my/dap-stack-frame-current-filter path)))))
(defun my/dap-switch-stack-frame ()
"Switch stackframe by selecting another stackframe stackframes from current thread."
(interactive)
(when (not (dap--cur-session))
(error "There is no active session"))
(-if-let (thread-id (dap--debug-session-thread-id (dap--cur-session)))
(-if-let (stack-frames
(gethash
thread-id
(dap--debug-session-thread-stack-frames (dap--cur-session))))
(let* ((index 0)
(stack-framces-filtered
(-filter
#'my/dap-stack-frame-filter
stack-frames))
(new-stack-frame
(dap--completing-read
"Select active frame: "
stack-framces-filtered
(-lambda ((frame &as &hash "name"))
(if-let (frame-path (dap--get-path-for-frame frame))
(format "%s: %s (in %s)"
(cl-incf index) name frame-path)
(format "%s: %s" (cl-incf index) name)))
nil
t)))
(dap--go-to-stack-frame (dap--cur-session) new-stack-frame))
(->> (dap--cur-session)
dap--debug-session-name
(format "Current session %s is not stopped")
error))
(error "No thread is currently active %s" (dap--debug-session-name (dap--cur-session)))))
(defun my/exwm-perspective-find-buffer (path)
"Find a buffer with PATH in all EXWM perspectives.
Returns (<buffer> . <workspace-index>) or nil."
(let* ((buf (cl-loop for buf being buffers
if (and (buffer-file-name buf)
(f-equal-p (buffer-file-name buf) path))
return buf))
(target-workspace
(and buf
(cl-loop for frame in exwm-workspace--list
if (with-selected-frame frame
(cl-loop for persp-name being the hash-keys of (perspectives-hash)
if (member buf (persp-buffers
(gethash persp-name (perspectives-hash))))
return persp-name))
return (cl-position frame exwm-workspace--list)))))
(when target-workspace (cons buf target-workspace))))
(defun my/dap--go-to-stack-frame-override (debug-session stack-frame)
"Make STACK-FRAME the active STACK-FRAME of DEBUG-SESSION."
(with-lsp-workspace (dap--debug-session-workspace debug-session)
(when stack-frame
(-let* (((&hash "line" line "column" column "name" name) stack-frame)
(path (dap--get-path-for-frame stack-frame)))
(setf (dap--debug-session-active-frame debug-session) stack-frame)
;; If we have a source file with path attached, open it and
;; position the point in the line/column referenced in the
;; stack trace.
(if (and path (file-exists-p path))
(progn
(let ((exwm-target (my/exwm-perspective-find-buffer path)))
(if exwm-target
(progn
(unless (= (cdr exwm-target) exwm-workspace-current-index)
(exwm-workspace-switch (cdr exwm-target)))
(persp-switch-to-buffer (car exwm-target)))
(select-window (get-mru-window (selected-frame) nil))
(find-file path)))
(goto-char (point-min))
(forward-line (1- line))
(forward-char column))
(message "No source code for %s. Cursor at %s:%s." name line column))))
(run-hook-with-args 'dap-stack-frame-changed-hook debug-session)))
(with-eval-after-load 'exwm
(with-eval-after-load 'dap-mode
(advice-add #'dap--go-to-stack-frame :override #'my/dap--go-to-stack-frame-override)))
;; (advice-remove #'dap--go-to-stack-frame #'my/dap--go-to-stack-frame-override)
(with-eval-after-load 'dap-mode
(dap-register-debug-template
"Node::Nest.js"
(list :type "node"
:request "attach"
:name "Node::Attach"
:port 9229
:outFiles ["${workspaceFolder}/dist/**/*.js"]
:sourceMaps t
:program "${workspaceFolder}/src/app.ts"))
(dap-register-debug-template
"Node::Babel"
(list :type "node"
:request "attach"
:name "Node::Attach"
:port 9229
:program "${workspaceFolder}/dist/bin/www.js")))
(use-package reformatter
:straight t)
(defun my/set-smartparens-indent (mode)
(sp-local-pair mode "{" nil :post-handlers '(("|| " "SPC") ("||\n[i]" "RET")))
(sp-local-pair mode "[" nil :post-handlers '(("|| " "SPC") ("||\n[i]" "RET")))
(sp-local-pair mode "(" nil :post-handlers '(("|| " "SPC") ("||\n[i]" "RET"))))
(defun my/set-flycheck-eslint()
"Override flycheck checker with eslint."
(setq-local lsp-diagnostic-package :none)
(setq-local flycheck-checker 'javascript-eslint))
(use-package emmet-mode
:straight t
:hook ((vue-html-mode . emmet-mode)
(svelte-mode . emmet-mode)
(web-mode . emmet-mode)
(html-mode . emmet-mode)
(css-mode . emmet-mode)
(scss-mode . emmet-mode))
:config
;; (setq emmet-indent-after-insert nil)
(setq my/emmet-mmm-submodes '(vue-html-mode css-mode))
(defun my/emmet-or-tab (&optional arg)
(interactive)
(if (and
(boundp 'mmm-current-submode)
mmm-current-submode
(not (member mmm-current-submode my/emmet-mmm-submodes)))
(indent-for-tab-command arg)
(or (emmet-expand-line arg)
(emmet-go-to-edit-point 1)
(indent-for-tab-command arg))))
(general-imap :keymaps 'emmet-mode-keymap
"TAB" 'my/emmet-or-tab
"<backtab>" 'emmet-prev-edit-point))
(use-package prettier
:commands (prettier-prettify)
:straight t
:init
(my-leader-def
:keymaps '(js-mode-map web-mode-map typescript-mode-map vue-mode-map svelte-mode-map)
"rr" #'prettier-prettify))
(use-package typescript-mode
:straight t
:mode "\\.ts\\'"
:config
(add-hook 'typescript-mode-hook #'smartparens-mode)
(add-hook 'typescript-mode-hook #'rainbow-delimiters-mode)
(add-hook 'typescript-mode-hook #'hs-minor-mode)
(my/set-smartparens-indent 'typescript-mode))
(add-hook 'js-mode-hook #'smartparens-mode)
(add-hook 'js-mode-hook #'hs-minor-mode)
(my/set-smartparens-indent 'js-mode)
(use-package jest-test-mode
:straight t
:hook ((typescript-mode . jest-test-mode)
(js-mode . jest-test-mode))
:config
(my-leader-def
:keymaps 'jest-test-mode-map
:infix "t"
"t" 'jest-test-run-at-point
"r" 'jest-test-run
"a" 'jest-test-run-all-tests))
(defun my/jest-test-run-at-point-copy ()
"Run the top level describe block of the current buffer's point."
(interactive)
(let ((filename (jest-test-find-file))
(example (jest-test-example-at-point)))
(if (and filename example)
(jest-test-from-project-directory filename
(let ((jest-test-options (seq-concatenate 'list jest-test-options (list "-t" example))))
(kill-new (jest-test-command filename))))
(message jest-test-not-found-message))))
(use-package web-mode
:straight t
:commands (web-mode)
:init
(add-to-list 'auto-mode-alist '("\\.svelte\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.vue\\'" . web-mode))
:config
(add-hook 'web-mode-hook 'smartparens-mode)
(add-hook 'web-mode-hook 'hs-minor-mode)
(my/set-smartparens-indent 'web-mode))
(setq my/web-mode-lsp-extensions
`(,(rx ".svelte" eos)
,(rx ".vue" eos)))
(defun my/web-mode-lsp ()
(when (seq-some
(lambda (regex) (string-match-p regex (buffer-name)))
my/web-mode-lsp-extensions)
(lsp-deferred)))
(add-hook 'web-mode-hook #'my/web-mode-lsp)
(defun my/web-mode-vue-setup ()
(when (string-match-p (rx ".vue" eos) (buffer-name))
(setq-local web-mode-script-padding 0)))
(add-hook 'web-mode-hook 'my/web-mode-vue-setup)
(add-hook 'scss-mode-hook #'smartparens-mode)
(add-hook 'scss-mode-hook #'hs-minor-mode)
(my/set-smartparens-indent 'scss-mode)
(use-package php-mode
:straight t
:mode "\\.php\\'")
(use-package tex
:straight auctex
:defer t
:config
(setq-default TeX-auto-save t)
(setq-default TeX-parse-self t)
(TeX-PDF-mode)
;; Use XeLaTeX & stuff
(setq-default TeX-engine 'xetex)
(setq-default TeX-command-extra-options "-shell-escape")
(setq-default TeX-source-correlate-method 'synctex)
(TeX-source-correlate-mode)
(setq-default TeX-source-correlate-start-server t)
(setq-default LaTeX-math-menu-unicode t)
(setq-default font-latex-fontify-sectioning 1.3)
;; Scale preview for my DPI
(setq-default preview-scale-function 1.4)
(when (boundp 'tex--prettify-symbols-alist)
(assoc-delete-all "--" tex--prettify-symbols-alist)
(assoc-delete-all "---" tex--prettify-symbols-alist))
(add-hook 'LaTeX-mode-hook
(lambda ()
(TeX-fold-mode 1)
(outline-minor-mode)))
(add-to-list 'TeX-view-program-selection
'(output-pdf "Zathura"))
;; Do not run lsp within templated TeX files
(add-hook 'LaTeX-mode-hook
(lambda ()
(unless (string-match "\.hogan\.tex$" (buffer-name))
(lsp))
(setq-local lsp-diagnostic-package :none)
(setq-local flycheck-checker 'tex-chktex)))
(add-hook 'LaTeX-mode-hook #'rainbow-delimiters-mode)
(add-hook 'LaTeX-mode-hook #'smartparens-mode)
(add-hook 'LaTeX-mode-hook #'prettify-symbols-mode)
(my/set-smartparens-indent 'LaTeX-mode)
(require 'smartparens-latex)
(general-nmap
:keymaps '(LaTeX-mode-map latex-mode-map)
"RET" 'TeX-command-run-all
"C-c t" 'orgtbl-mode)
(setq my/greek-alphabet
'(("a" . "\\alpha")
("b" . "\\beta" )
("g" . "\\gamma")
("d" . "\\delta")
("e" . "\\epsilon")
("z" . "\\zeta")
("h" . "\\eta")
("o" . "\\theta")
("i" . "\\iota")
("k" . "\\kappa")
("l" . "\\lambda")
("m" . "\\mu")
("n" . "\\nu")
("x" . "\\xi")
("p" . "\\pi")
("r" . "\\rho")
("s" . "\\sigma")
("t" . "\\tau")
("u" . "\\upsilon")
("f" . "\\phi")
("c" . "\\chi")
("v" . "\\psi")
("g" . "\\omega")))
(setq my/latex-greek-prefix "'")
;; The same for capitalized letters
(dolist (elem my/greek-alphabet)
(let ((key (car elem))
(value (cdr elem)))
(when (string-equal key (downcase key))
(add-to-list 'my/greek-alphabet
(cons
(capitalize (car elem))
(concat
(substring value 0 1)
(capitalize (substring value 1 2))
(substring value 2)))))))
(yas-define-snippets
'latex-mode
(mapcar
(lambda (elem)
(list (concat my/latex-greek-prefix (car elem)) (cdr elem) (concat "Greek letter " (car elem))))
my/greek-alphabet))
(setq my/english-alphabet
'("a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"))
(dolist (elem my/english-alphabet)
(when (string-equal elem (downcase elem))
(add-to-list 'my/english-alphabet (upcase elem))))
(setq my/latex-mathbb-prefix "`")
(yas-define-snippets
'latex-mode
(mapcar
(lambda (elem)
(list (concat my/latex-mathbb-prefix elem) (concat "\\mathbb{" elem "}") (concat "Mathbb letter " elem)))
my/english-alphabet))
(setq my/latex-math-symbols
'(("x" . "\\times")
("." . "\\cdot")
("v" . "\\forall")
("s" . "\\sum_{$1}^{$2}$0")
("p" . "\\prod_{$1}^{$2}$0")
("d" . "\\partial")
("e" . "\\exists")
("i" . "\\int_{$1}^{$2}$0")
("c" . "\\cap")
("u" . "\\cup")
("0" . "\\emptyset")
("^" . "\\widehat{$1}$0")
("_" . "\\overline{$1}$0")
("~" . "\\sim")
("|" . "\\mid")
("_|" . "\\perp")))
(setq my/latex-math-prefix ";")
(yas-define-snippets
'latex-mode
(mapcar
(lambda (elem)
(let ((key (car elem))
(value (cdr elem)))
(list (concat my/latex-math-prefix key) value (concat "Math symbol " value))))
my/latex-math-symbols))
(setq my/latex-section-snippets
'(("ch" . "\\chapter{$1}")
("sec" . "\\section{$1}")
("ssec" . "\\subsection{$1}")
("sssec" . "\\subsubsection{$1}")
("par" . "\\paragraph{$1}}")))
(setq my/latex-section-snippets
(mapcar
(lambda (elem)
`(,(car elem)
,(cdr elem)
,(progn
(string-match "[a-z]+" (cdr elem))
(match-string 0 (cdr elem)))))
my/latex-section-snippets))
(dolist (elem my/latex-section-snippets)
(let* ((key (nth 0 elem))
(value (nth 1 elem))
(desc (nth 2 elem))
(star-index (string-match "\{\$1\}" value)))
(add-to-list 'my/latex-section-snippets
`(,(concat key "*")
,(concat
(substring value 0 star-index)
"*"
(substring value star-index))
,(concat desc " with *")))
(add-to-list 'my/latex-section-snippets
`(,(concat key "l")
,(concat value "%\n\\label{sec:$2}")
,(concat desc " with label")))))
(dolist (elem my/latex-section-snippets)
(setf (nth 1 elem) (concat (nth 1 elem) "\n$0")))
(yas-define-snippets
'latex-mode
my/latex-section-snippets))
(use-package ivy-bibtex
:commands (ivy-bibtex)
:straight t
:init
(my-leader-def "fB" 'ivy-bibtex))
(add-hook 'bibtex-mode 'smartparens-mode)
(defun my/list-sty ()
(reverse
(sort
(seq-filter
(lambda (file) (if (string-match ".*\.sty$" file) 1 nil))
(directory-files
(seq-some
(lambda (dir)
(if (and
(f-directory-p dir)
(seq-some
(lambda (file) (string-match ".*\.sty$" file))
(directory-files dir))
) dir nil))
(list "./styles" "../styles/" "." "..")) :full))
(lambda (f1 f2)
(let ((f1b (file-name-base f1))
(f1b (file-name-base f2)))
(cond
((string-match-p ".*BibTex" f1) t)
((and (string-match-p ".*Locale" f1) (not (string-match-p ".*BibTex" f2))) t)
((string-match-p ".*Preamble" f2) t)
(t (string-lessp f1 f2))))))))
(defun my/import-sty ()
(interactive)
(insert
(apply #'concat
(cl-mapcar
(lambda (file) (concat "\\usepackage{" (file-name-sans-extension (file-relative-name file default-directory)) "}\n"))
(my/list-sty)))))
(defun my/import-sty-org ()
(interactive)
(insert
(apply #'concat
(cl-mapcar
(lambda (file) (concat "#+LATEX_HEADER: \\usepackage{" (file-name-sans-extension (file-relative-name file default-directory)) "}\n"))
(my/list-sty)))))
(setq my/greek-alphabet
'(("a" . "\\alpha")
("b" . "\\beta" )
("g" . "\\gamma")
("d" . "\\delta")
("e" . "\\epsilon")
("z" . "\\zeta")
("h" . "\\eta")
("o" . "\\theta")
("i" . "\\iota")
("k" . "\\kappa")
("l" . "\\lambda")
("m" . "\\mu")
("n" . "\\nu")
("x" . "\\xi")
("p" . "\\pi")
("r" . "\\rho")
("s" . "\\sigma")
("t" . "\\tau")
("u" . "\\upsilon")
("f" . "\\phi")
("c" . "\\chi")
("v" . "\\psi")
("g" . "\\omega")))
(setq my/latex-greek-prefix "'")
;; The same for capitalized letters
(dolist (elem my/greek-alphabet)
(let ((key (car elem))
(value (cdr elem)))
(when (string-equal key (downcase key))
(add-to-list 'my/greek-alphabet
(cons
(capitalize (car elem))
(concat
(substring value 0 1)
(capitalize (substring value 1 2))
(substring value 2)))))))
(yas-define-snippets
'latex-mode
(mapcar
(lambda (elem)
(list (concat my/latex-greek-prefix (car elem)) (cdr elem) (concat "Greek letter " (car elem))))
my/greek-alphabet))
(setq my/english-alphabet
'("a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"))
(dolist (elem my/english-alphabet)
(when (string-equal elem (downcase elem))
(add-to-list 'my/english-alphabet (upcase elem))))
(setq my/latex-mathbb-prefix "`")
(yas-define-snippets
'latex-mode
(mapcar
(lambda (elem)
(list (concat my/latex-mathbb-prefix elem) (concat "\\mathbb{" elem "}") (concat "Mathbb letter " elem)))
my/english-alphabet))
(setq my/latex-math-symbols
'(("x" . "\\times")
("." . "\\cdot")
("v" . "\\forall")
("s" . "\\sum_{$1}^{$2}$0")
("p" . "\\prod_{$1}^{$2}$0")
("d" . "\\partial")
("e" . "\\exists")
("i" . "\\int_{$1}^{$2}$0")
("c" . "\\cap")
("u" . "\\cup")
("0" . "\\emptyset")
("^" . "\\widehat{$1}$0")
("_" . "\\overline{$1}$0")
("~" . "\\sim")
("|" . "\\mid")
("_|" . "\\perp")))
(setq my/latex-math-prefix ";")
(yas-define-snippets
'latex-mode
(mapcar
(lambda (elem)
(let ((key (car elem))
(value (cdr elem)))
(list (concat my/latex-math-prefix key) value (concat "Math symbol " value))))
my/latex-math-symbols))
(setq my/latex-section-snippets
'(("ch" . "\\chapter{$1}")
("sec" . "\\section{$1}")
("ssec" . "\\subsection{$1}")
("sssec" . "\\subsubsection{$1}")
("par" . "\\paragraph{$1}}")))
(setq my/latex-section-snippets
(mapcar
(lambda (elem)
`(,(car elem)
,(cdr elem)
,(progn
(string-match "[a-z]+" (cdr elem))
(match-string 0 (cdr elem)))))
my/latex-section-snippets))
(dolist (elem my/latex-section-snippets)
(let* ((key (nth 0 elem))
(value (nth 1 elem))
(desc (nth 2 elem))
(star-index (string-match "\{\$1\}" value)))
(add-to-list 'my/latex-section-snippets
`(,(concat key "*")
,(concat
(substring value 0 star-index)
"*"
(substring value star-index))
,(concat desc " with *")))
(add-to-list 'my/latex-section-snippets
`(,(concat key "l")
,(concat value "%\n\\label{sec:$2}")
,(concat desc " with label")))))
(dolist (elem my/latex-section-snippets)
(setf (nth 1 elem) (concat (nth 1 elem) "\n$0")))
(yas-define-snippets
'latex-mode
my/latex-section-snippets)
(use-package markdown-mode
:straight t
:mode "\\.md\\'"
:config
(setq markdown-command
(concat
"pandoc"
" --from=markdown --to=html"
" --standalone --mathjax --highlight-style=pygments"
" --css=pandoc.css"
" --quiet"
))
(setq markdown-live-preview-delete-export 'delete-on-export)
(setq markdown-asymmetric-header t)
(setq markdown-open-command "/home/pavel/bin/scripts/chromium-sep")
(add-hook 'markdown-mode-hook #'smartparens-mode)
(general-define-key
:keymaps 'markdown-mode-map
"M-<left>" 'markdown-promote
"M-<right>" 'markdown-demote))
;; (use-package livedown
;; :straight (:host github :repo "shime/emacs-livedown")
;; :commands livedown-preview
;; :config
;; (setq livedown-browser "qutebrowser"))
(use-package plantuml-mode
:straight t
:mode "(\\.\\(plantuml?\\|uml\\|puml\\)\\'"
:config
(setq plantuml-executable-path "/home/pavel/.guix-extra-profiles/emacs/emacs/bin/plantuml")
(setq plantuml-default-exec-mode 'executable)
(setq plantuml-indent-level 2)
(setq my/plantuml-indent-regexp-return "^\s*return\s+.+$")
(add-to-list
'plantuml-indent-regexp-end
my/plantuml-indent-regexp-return)
(add-to-list 'auto-mode-alist '("\\.plantuml\\'" . plantuml-mode))
(add-to-list 'auto-mode-alist '("\\.uml\\'" . plantuml-mode))
(add-hook 'plantuml-mode-hook #'smartparens-mode))
(general-nmap
:keymaps 'plantuml-mode-map
"RET" 'plantuml-preview)
(use-package subed
:straight (:host github :repo "rndusr/subed" :files ("subed/*.el"))
:mode (rx (| "srt" "vtt" "ass") eos))
(use-package langtool
:straight t
:commands (langtool-check)
:config
(setq langtool-language-tool-server-jar "/home/pavel/bin/LanguageTool-5.4/languagetool-server.jar")
(setq langtool-mother-tongue "ru")
(setq langtool-default-language "en-US"))
(my-leader-def
:infix "L"
"" '(:which-key "languagetool")
"c" 'langtool-check
"s" 'langtool-server-stop
"d" 'langtool-check-done
"n" 'langtool-goto-next-error
"p" 'langtool-goto-previous-error
"l" 'langtool-correct-buffer)
(use-package lispy
:commands (lispy-mode)
:straight t)
(use-package lispyville
:hook (lispy-mode . lispyville-mode)
:straight t)
(sp-with-modes sp-lisp-modes
(sp-local-pair "'" nil :actions nil))
(use-package flycheck-package
:straight t
:after flycheck
:config
(flycheck-package-setup))
(add-hook 'emacs-lisp-mode-hook #'aggressive-indent-mode)
;; (add-hook 'emacs-lisp-mode-hook #'smartparens-strict-mode)
(add-hook 'emacs-lisp-mode-hook #'lispy-mode)
(use-package slime
:straight t
:commands (slime)
:config
(setq inferior-lisp-program "sbcl")
(add-hook 'slime-repl-mode 'smartparens-mode))
(add-hook 'lisp-mode-hook #'aggressive-indent-mode)
;; (add-hook 'emacs-lisp-mode-hook #'smartparens-strict-mode)
(add-hook 'lisp-mode-hook #'lispy-mode)
(use-package clojure-mode
:straight t
:mode "\\.clj[sc]?\\'"
:config
;; (add-hook 'clojure-mode-hook #'smartparens-strict-mode)
(add-hook 'clojure-mode-hook #'lispy-mode)
(add-hook 'clojure-mode-hook #'aggressive-indent-mode))
(use-package cider
:mode "\\.clj[sc]?\\'"
:straight t)
(use-package hy-mode
:straight t
:mode "\\.hy\\'"
:config
(add-hook 'hy-mode-hook #'lispy-mode)
(add-hook 'hy-mode-hook #'aggressive-indent-mode))
(use-package geiser
:straight t
:if (not my/lowpower)
:commands (geiser run-geiser)
:config
(setq geiser-default-implementation 'guile))
(use-package geiser-guile
:straight t
:after geiser)
(add-hook 'scheme-mode-hook #'aggressive-indent-mode)
(add-hook 'scheme-mode-hook #'lispy-mode)
(use-package clips-mode
:straight t
:mode "\\.cl\\'"
:config
(add-hook 'clips-mode 'lispy-mode))
(setq my/pipenv-python-alist '())
(defun my/get-pipenv-python ()
(let ((default-directory (projectile-project-root)))
(if (file-exists-p "Pipfile")
(let ((asc (assoc default-directory my/pipenv-python-alist)))
(if asc
(cdr asc)
(let ((python-executable
(string-trim (shell-command-to-string "PIPENV_IGNORE_VIRTUALENVS=1 pipenv run which python 2>/dev/null"))))
(if (string-match-p ".*not found.*" python-executable)
(message "Pipfile found, but not pipenv executable!")
(message (format "Found pipenv python: %s" python-executable))
(add-to-list 'my/pipenv-python-alist (cons default-directory python-executable))
python-executable))))
"python")))
(use-package lsp-pyright
:straight t
:defer t
:if (not my/slow-ssh)
:hook (python-mode . (lambda ()
(require 'lsp-pyright)
(setq-local lsp-pyright-python-executable-cmd (my/get-pipenv-python))
(lsp))))
(add-hook 'python-mode-hook #'smartparens-mode)
(add-hook 'python-mode-hook #'hs-minor-mode)
(use-package pipenv
:straight t
:hook (python-mode . pipenv-mode)
:if (not my/slow-ssh)
:init
(setq
pipenv-projectile-after-switch-function
#'pipenv-projectile-after-switch-extended))
(use-package yapfify
:straight (:repo "JorisE/yapfify" :host github)
:commands (yapfify-region
yapfify-buffer
yapfify-region-or-buffer
yapf-mode))
(use-package py-isort
:straight t
:commands (py-isort-buffer py-isort-region))
(my-leader-def
:keymaps 'python-mode-map
"rr" (lambda ()
(interactive)
(unless (and (fboundp #'org-src-edit-buffer-p) (org-src-edit-buffer-p))
(py-isort-buffer))
(yapfify-buffer)))
(use-package sphinx-doc
:straight t
:hook (python-mode . sphinx-doc-mode)
:config
(my-leader-def
:keymaps 'sphinx-doc-mode-map
"rd" 'sphinx-doc))
(defun my/set-pipenv-pytest ()
(setq-local
python-pytest-executable
(concat (my/get-pipenv-python) " -m pytest")))
(use-package python-pytest
:straight t
:commands (python-pytest-dispatch)
:init
(my-leader-def
:keymaps 'python-mode-map
:infix "t"
"t" 'python-pytest-dispatch)
:config
(cl-defun python-pytest--run-as-comint (&key command)
"Run a pytest comint session for COMMAND."
(let* ((buffer (python-pytest--get-buffer))
(process (get-buffer-process buffer)))
(with-current-buffer buffer
(when (comint-check-proc buffer)
(unless (or compilation-always-kill
(yes-or-no-p "Kill running pytest process?"))
(user-error "Aborting; pytest still running")))
(when process
(delete-process process))
(let ((inhibit-read-only t))
(erase-buffer))
(unless (eq major-mode 'python-pytest-mode)
(python-pytest-mode))
(compilation-forget-errors)
(display-buffer buffer)
(setq command (format "export COLUMNS=%s; %s"
(- (window-width (get-buffer-window buffer)) 5)
command))
(insert (format "cwd: %s\ncmd: %s\n\n" default-directory command))
(setq python-pytest--current-command command)
(when python-pytest-pdb-track
(add-hook
'comint-output-filter-functions
'python-pdbtrack-comint-output-filter-function
nil t))
(run-hooks 'python-pytest-setup-hook)
(make-comint-in-buffer "pytest" buffer "bash" nil "-c" command)
(run-hooks 'python-pytest-started-hook)
(setq process (get-buffer-process buffer))
(set-process-sentinel process #'python-pytest--process-sentinel))))
(add-hook 'python-mode-hook #'my/set-pipenv-pytest)
(when (derived-mode-p 'python-mode)
(my/set-pipenv-pytest)))
(use-package code-cells
:straight t
:commands (code-cells-mode))
(setq my/tensorboard-buffer "TensorBoard-out")
(defun my/tensorboard ()
(interactive)
(start-process
"tensorboard"
my/tensorboard-buffer
"tensorboard"
"serve"
"--logdir"
(car (find-file-read-args "Directory: " t)))
(display-buffer my/tensorboard-buffer))
(use-package json-mode
:straight t
:mode "\\.json\\'"
:config
(add-hook 'json-mode #'smartparens-mode)
(add-hook 'json-mode #'hs-minor-mode)
(my/set-smartparens-indent 'json-mode))
(use-package csv-mode
:straight t
:mode "\\.csv\\'")
(use-package yaml-mode
:straight t
:mode "\\.yml\\'"
:config
(add-hook 'yaml-mode-hook 'smartparens-mode)
(add-hook 'yaml-mode-hook 'highlight-indent-guides-mode)
(add-to-list 'auto-mode-alist '("\\.yml\\'" . yaml-mode)))
(use-package dotenv-mode
:straight t
:mode "\\.env\\..*\\'")
(use-package gitignore-templates
:straight t
:commands (gitignore-templates-insert
gitignore-templates-new-file))
(use-package dockerfile-mode
:mode "Dockerfile\\'"
:straight t
:config
(add-hook 'dockerfile-mode 'smartparens-mode))
(use-package crontab-mode
:straight t)
(add-hook 'sh-mode-hook #'smartparens-mode)
(use-package fish-mode
:straight t
:mode "\\.fish\\'"
:config
(add-hook 'fish-mode-hook #'smartparens-mode))
(use-package lsp-java
:straight t
:after (lsp)
:config
(setq lsp-java-jdt-download-url "https://download.eclipse.org/jdtls/milestones/0.57.0/jdt-language-server-0.57.0-202006172108.tar.gz"))
(add-hook 'java-mode-hook #'smartparens-mode)
;; (add-hook 'java-mode-hook #'hs-minor-mode)
(my/set-smartparens-indent 'java-mode)
(use-package go-mode
:straight t
:mode "\\.go\\'"
:config
(my/set-smartparens-indent 'go-mode)
(add-hook 'go-mode-hook #'smartparens-mode)
(add-hook 'go-mode-hook #'hs-minor-mode))
(use-package csharp-mode
:straight t
:mode "\\.cs\\'"
:config
(setq lsp-csharp-server-path (executable-find "omnisharp-wrapper"))
(add-hook 'csharp-mode-hook #'csharp-tree-sitter-mode)
(add-hook 'csharp-tree-sitter-mode-hook #'smartparens-mode)
(add-hook 'csharp-mode-hook #'hs-minor-mode)
(my/set-smartparens-indent 'csharp-tree-sitter-mode))
(use-package csproj-mode
:straight t
:mode "\\.csproj\\'"
:config
(add-hook 'csproj-mode #'smartparens-mode))
(use-package haskell-mode
:straight t
:mode "\\.hs\\'")
(use-package lsp-haskell
:straight t
:after (lsp haskell-mode))
(use-package lua-mode
:straight t
:mode "\\.lua\\'"
:hook (lua-mode . smartparens-mode))
(my/set-smartparens-indent 'lua-mode)
(setq my/sqlformatter-dialect-choice
'("db2" "mariadb" "mysql" "n1ql" "plsql" "postgresql" "redshift" "spark" "sql" "tsql"))
(setq my/sqlformatter-dialect "postgresql")
(defun my/sqlformatter-set-dialect ()
"Set dialect for sql-formatter"
(interactive)
(setq my/sqlformatter-dialect
(completing-read "Dialect: " my/sqlformatter-dialect-choice)))
(reformatter-define sqlformat
:program (executable-find "sql-formatter")
:args `("-l" ,my/sqlformatter-dialect, "-u"))
(my-leader-def
:keymaps '(sql-mode-map)
"rr" #'sqlformat-buffer)
(use-package sparql-mode
:straight t)
(use-package org
:straight t
:if (not my/remote-server)
:defer t
:init
(setq org-directory (expand-file-name "~/Documents/org-mode"))
:config
(setq org-startup-indented t)
(setq org-return-follows-link t)
(setq org-src-tab-acts-natively nil)
(add-hook 'org-mode-hook 'smartparens-mode)
(add-hook 'org-agenda-mode-hook
(lambda ()
(visual-line-mode -1)
(toggle-truncate-lines 1)
(display-line-numbers-mode 0)))
(add-hook 'org-mode-hook
(lambda ()
(rainbow-delimiters-mode -1)))
(require 'org-tempo)
(add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
(add-to-list 'org-structure-template-alist '("py" . "src python"))
(add-to-list 'org-structure-template-alist '("sq" . "src sql"))
(require 'org-crypt)
(org-crypt-use-before-save-magic)
(setq org-tags-exclude-from-inheritance (quote ("crypt")))
(setq org-crypt-key "C1EC867E478472439CC82410DE004F32AFA00205")
(unless my/is-termux
(use-package jupyter
:straight t
:if (not my/is-termux)
:init
(my-leader-def "ar" 'jupyter-run-repl))
(use-package ob-hy
:straight t)
(setq org-plantuml-executable-path "/home/pavel/.guix-extra-profiles/emacs/emacs/bin/plantuml")
(setq org-plantuml-exec-mode 'plantuml)
(add-to-list 'org-src-lang-modes '("plantuml" . plantuml))
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(python . t)
(sql . t)
;; (typescript .t)
(hy . t)
(shell . t)
(plantuml . t)
(octave . t)
(jupyter . t)
(sparql . t)))
(add-hook 'org-babel-after-execute-hook 'org-redisplay-inline-images)
(org-babel-jupyter-override-src-block "python")
(org-babel-jupyter-override-src-block "hy")
(add-hook 'org-src-mode-hook
(lambda ()
;; (hs-minor-mode -1)
;; (electric-indent-local-mode -1)
;; (rainbow-delimiters-mode -1)
(highlight-indent-guides-mode -1))))
(setq my/org-latex-scale 1.75)
(setq org-format-latex-options (plist-put org-format-latex-options :scale my/org-latex-scale))
(setq my/latex-preview-header "\\documentclass{article}
\\usepackage[usenames]{color}
\\usepackage{graphicx}
\\usepackage{grffile}
\\usepackage{longtable}
\\usepackage{wrapfig}
\\usepackage{rotating}
\\usepackage[normalem]{ulem}
\\usepackage{amsmath}
\\usepackage{textcomp}
\\usepackage{amssymb}
\\usepackage{capt-of}
\\usepackage{hyperref}
\\pagestyle{empty}")
(setq org-preview-latex-process-alist
(mapcar
(lambda (item)
(cons
(car item)
(plist-put (cdr item) :latex-header my/latex-preview-header)))
org-preview-latex-process-alist))
(general-define-key
:keymaps 'org-mode-map
"C-c d" 'org-decrypt-entry
"C-c e" 'org-encrypt-entry
"M-p" 'org-latex-preview
"M-o" 'org-redisplay-inline-images)
(general-define-key
:keymaps 'org-mode-map
:states '(normal emacs)
"L" 'org-shiftright
"H" 'org-shiftleft
"S-<next>" 'org-next-visible-heading
"S-<prior>" 'org-previous-visible-heading
"M-0" 'org-next-visible-heading
"M-9" 'org-previous-visible-heading
"M-]" 'org-babel-next-src-block
"M-[" 'org-babel-previous-src-block)
(general-define-key
:keymaps 'org-agenda-mode-map
"M-]" 'org-agenda-later
"M-[" 'org-agenda-earlier)
;; (general-imap :keymaps 'org-mode-map "RET" 'evil-org-return)
(general-nmap :keymaps 'org-mode-map "RET" 'org-ctrl-c-ctrl-c)
;; (my-leader-def "aa" 'org-agenda)
(defun my/org-link-copy (&optional arg)
"Extract URL from org-mode link and add it to kill ring."
(interactive "P")
(let* ((link (org-element-lineage (org-element-context) '(link) t))
(type (org-element-property :type link))
(url (org-element-property :path link))
(url (concat type ":" url)))
(kill-new url)
(message (concat "Copied URL: " url))))
(general-nmap :keymaps 'org-mode-map
"C-x C-l" 'my/org-link-copy)
(setq org-roam-directory (concat org-directory "/roam"))
(setq org-agenda-files '("inbox.org"))
;; (setq org-default-notes-file (concat org-directory "/notes.org"))
(add-to-list 'org-global-properties
'("Effort_ALL" . "0 0:05 0:10 0:15 0:30 0:45 1:00 2:00 4:00"))
(setq org-log-done 'time)
(use-package org-ql
:straight (:fetcher github
:repo "alphapapa/org-ql"
:files (:defaults (:exclude "helm-org-ql.el")))))
(require 'org-crypt)
(org-crypt-use-before-save-magic)
(setq org-tags-exclude-from-inheritance (quote ("crypt")))
(setq org-crypt-key "C1EC867E478472439CC82410DE004F32AFA00205")
(defun my/epa--select-keys-around (fun prompt keys)
(if (= (seq-length keys) 1)
keys
(funcall fun prompt keys)))
(with-eval-after-load 'epa
(advice-add #'epa--select-keys :around #'my/epa--select-keys-around))
(setq epa-file-encrypt-to '("DE004F32AFA00205"))
(use-package org-contrib
:straight (org-contrib
:type git
:host nil
:repo "https://git.sr.ht/~bzg/org-contrib"
:build t)
:after (org)
:config
(require 'ox-extra)
(require 'ol-notmuch)
(ox-extras-activate '(latex-header-blocks ignore-headlines)))
(use-package evil-org
:straight t
:hook (org-mode . evil-org-mode)
:config
(add-hook 'evil-org-mode-hook
(lambda ()
(evil-org-set-key-theme '(navigation insert textobjects additional calendar todo))))
(add-to-list 'evil-emacs-state-modes 'org-agenda-mode)
(require 'evil-org-agenda)
(evil-org-agenda-set-keys))
(use-package jupyter
:straight t
:if (not my/is-termux)
:init
(my-leader-def "ar" 'jupyter-run-repl))
(defun my/jupyter-refresh-kernelspecs ()
"Refresh Jupyter kernelspecs"
(interactive)
(jupyter-available-kernelspecs t))
(defun my/jupyter-refesh-langs ()
"Refresh Jupyter languages"
(interactive)
(org-babel-jupyter-aliases-from-kernelspecs t))
(use-package ob-hy
:straight t)
(setq my/org-view-html-tmp-dir "/tmp/org-html-preview/")
(use-package f
:straight t)
(defun my/org-view-html ()
(interactive)
(let ((elem (org-element-at-point))
(temp-file-path (concat my/org-view-html-tmp-dir (number-to-string (random (expt 2 32))) ".html")))
(cond
((not (eq 'export-block (car elem)))
(message "Not in an export block!"))
((not (string-equal (plist-get (car (cdr elem)) :type) "HTML"))
(message "Export block is not HTML!"))
(t (progn
(f-mkdir my/org-view-html-tmp-dir)
(f-write (plist-get (car (cdr elem)) :value) 'utf-8 temp-file-path)
(start-process "org-html-preview" nil "xdg-open" temp-file-path))))))
(use-package ob-async
:straight t
:after (org)
:config
(setq ob-async-no-async-languages-alist '("python" "hy" "jupyter-python" "jupyter-octave")))
(setq my/jupyter-runtime-folder (expand-file-name "~/.local/share/jupyter/runtime"))
(defun my/get-open-ports ()
(mapcar
#'string-to-number
(split-string (shell-command-to-string "ss -tulpnH | awk '{print $5}' | sed -e 's/.*://'") "\n")))
(defun my/list-jupyter-kernel-files ()
(mapcar
(lambda (file) (cons (car file) (cdr (assq 'shell_port (json-read-file (car file))))))
(sort
(directory-files-and-attributes my/jupyter-runtime-folder t ".*kernel.*json$")
(lambda (x y) (not (time-less-p (nth 6 x) (nth 6 y)))))))
(defun my/select-jupyter-kernel ()
(let ((ports (my/get-open-ports))
(files (my/list-jupyter-kernel-files)))
(completing-read
"Jupyter kernels: "
(seq-filter
(lambda (file)
(member (cdr file) ports))
files))))
(defun my/insert-jupyter-kernel ()
"Insert a path to an active Jupyter kernel into the buffer"
(interactive)
(insert (my/select-jupyter-kernel)))
(defun my/jupyter-connect-repl ()
"Open an emacs-jupyter REPL, connected to a Jupyter kernel"
(interactive)
(jupyter-connect-repl (my/select-jupyter-kernel) nil nil nil t))
(defun my/jupyter-qtconsole ()
"Open Jupyter QtConsole, connected to a Jupyter kernel"
(interactive)
(start-process "jupyter-qtconsole" nil "setsid" "jupyter" "qtconsole" "--existing"
(file-name-nondirectory (my/select-jupyter-kernel))))
(defun my/jupyter-cleanup-kernels ()
(interactive)
(let* ((ports (my/get-open-ports))
(files (my/list-jupyter-kernel-files))
(to-delete (seq-filter
(lambda (file)
(not (member (cdr file) ports)))
files)))
(when (and (length> to-delete 0)
(y-or-n-p (format "Delete %d files?" (length to-delete))))
(dolist (file to-delete)
(delete-file (car file))))))
(defun my/jupyter-org-scalar (value)
(cond
((stringp value) value)
(t (jupyter-org-scalar value))))
(define-minor-mode my/emacs-jupyter-raw-output
"Make emacs-jupyter do raw output")
(defun my/jupyter-org-scalar-around (fun value)
(if my/emacs-jupyter-raw-output
(my/jupyter-org-scalar value)
(funcall fun value)))
(advice-add 'jupyter-org-scalar :around #'my/jupyter-org-scalar-around)
(defun my/org-strip-results (data)
(replace-regexp-in-string ":\\(RESULTS\\|END\\):\n" "" data))
(defun my/org-caption-wrap (data &optional name caption attrs strip-drawer src-wrap)
(let* ((data-s (if (and strip-drawer (not (string-empty-p strip-drawer)))
(my/org-strip-results data)
data))
(drawer-start (if (string-match-p "^:RESULTS:.*" data-s) 10 0)))
(concat
(substring data-s 0 drawer-start)
(and name (not (string-empty-p name)) (concat "#+NAME:" name "\n"))
(and caption (not (string-empty-p caption)) (concat "#+CAPTION:" caption "\n"))
(and attrs (not (string-empty-p attrs)) (concat "#+ATTR_LATEX:" attrs "\n"))
(if (and src-wrap (not (string-empty-p src-wrap)))
(concat "#+begin_src " src-wrap "\n"
(substring data-s drawer-start)
(when (not (string-match-p ".*\n" data-s)) "\n")
"#+end_src")
(substring data-s drawer-start)))))
(defun my/org-prj-dir (path)
(expand-file-name path (org-entry-get nil "PRJ-DIR" t)))
(use-package hide-mode-line
:straight t
:after (org-present))
(defun my/present-next-with-latex ()
(interactive)
(org-present-next)
(org-latex-preview '(16)))
(defun my/present-prev-with-latex ()
(interactive)
(org-present-prev)
(org-latex-preview '(16)))
(use-package org-present
:straight (:host github :repo "rlister/org-present")
:if (not my/remote-server)
:commands (org-present)
:config
(general-define-key
:keymaps 'org-present-mode-keymap
"<next>" 'my/present-next-with-latex
"<prior>" 'my/present-prev-with-latex)
(add-hook 'org-present-mode-hook
(lambda ()
(blink-cursor-mode 0)
(org-present-big)
;; (org-display-inline-images)
(org-present-hide-cursor)
(org-present-read-only)
(display-line-numbers-mode 0)
(hide-mode-line-mode +1)
(setq-local org-format-latex-options
(plist-put org-format-latex-options
:scale (* org-present-text-scale my/org-latex-scale 0.5)))
(org-latex-preview '(16))))
(add-hook 'org-present-mode-quit-hook
(lambda ()
(blink-cursor-mode 1)
(org-present-small)
;; (org-remove-inline-images)
(org-present-show-cursor)
(org-present-read-write)
(display-line-numbers-mode 1)
(hide-mode-line-mode 0)
(setq-local org-format-latex-options (plist-put org-format-latex-options :scale my/org-latex-scale))
(org-latex-preview '(64)))))
(use-package org-make-toc
:after (org)
:if (not my/remote-server)
:commands
(org-make-toc
org-make-toc-insert
org-make-toc-set
org-make-toc-at-point)
:straight t)
(use-package org-attach-screenshot
:commands (org-attach-screenshot)
:straight t)
(use-package org-transclusion
:after org
:straight (:host github :repo "nobiot/org-transclusion")
:config
(add-to-list 'org-transclusion-extensions 'org-transclusion-indent-mode)
(require 'org-transclusion-indent-mode)
(general-define-key
:keymaps '(org-transclusion-map)
:states '(normal)
"RET" #'org-transclusion-open-source
"gr" #'org-transclusion-refresh)
(general-define-key
:keymaps '(org-mode-map)
:states 'normal
"C-c t a" #'org-transclusion-add
"C-c t A" #'org-transclusion-add-all
"C-c t t" #'org-transclusion-mode))
(my-leader-def
:infix "o"
"" '(:which-key "org-mode")
"c" 'org-capture
"a" 'org-agenda)
(setq org-refile-targets
'())
(setq org-refile-use-outline-path 'file)
(setq org-outline-path-complete-in-steps nil)
(defun my/generate-inbox-note-name ()
(format
"%s/inbox-notes/%s.org"
org-directory
(format-time-string "%Y%m%d%H%M%S")))
(setq org-capture-templates
`(("i" "Inbox" entry (file "inbox.org")
,(concat "* TODO %?\n"
"/Entered on/ %U"))
("e" "email" entry (file "inbox.org")
,(concat "* TODO %:from %:subject \n"
"/Entered on/ %U\n"
"/Received on/ %:date-timestamp-inactive\n"
"%a\n"))
("f" "elfeed" entry (file "inbox.org")
,(concat "* TODO %:elfeed-entry-title\n"
"/Entered on/ %U\n"
"%a\n"))
("n" "note" entry (file my/generate-inbox-note-name)
,(concat "* %?\n"
"/Entered on/ %U"))))
(setq org-trello-files
(thread-last (concat org-directory "/trello")
(directory-files)
(seq-filter
(lambda (f) (string-match-p (rx ".org" eos) f)))
(mapcar
(lambda (f) (concat org-directory "/trello/" f)))))
(use-package org-trello
:straight (:build (:not native-compile))
:commands (org-trello-mode)
:init
(setq org-trello-current-prefix-keybinding "C-c o")
(setq org-trello-add-tags nil)
(add-hook 'org-mode-hook
(lambda ()
(when (string-match-p (rx "trello") (or (buffer-file-name) ""))
(org-trello-mode))))
:config
(eval
`(my-leader-def
:infix "o t"
:keymaps '(org-trello-mode-map)
"" '(:which-key "trello")
,@(mapcan
(lambda (b) (list (nth 1 b) (macroexp-quote (nth 0 b))))
org-trello-interactive-command-binding-couples))))
(defun my/org-scheduled-get-time ()
(let ((scheduled (org-get-scheduled-time (point))))
(if scheduled
(format-time-string "%Y-%m-%d" scheduled)
"")))
(setq org-agenda-hide-tags-regexp (rx (or "org" "log" "log_here")))
(setq org-agenda-custom-commands
`(("p" "My outline"
((agenda "")
(todo "NEXT"
((org-agenda-prefix-format " %i %-12:c [%e] ")
(org-agenda-overriding-header "Next tasks")))
(org-ql-block
`(and
(regexp ,(rx ":orgtrello_users:" (* nonl) "sqrtminusone"))
(todo)
(deadline))
((org-agenda-files ',org-trello-files)
(org-ql-block-header "Trello assigned")))
(tags-todo "inbox"
((org-agenda-overriding-header "Inbox")
(org-agenda-prefix-format " %i %-12:c")
(org-agenda-hide-tags-regexp ".")))
(tags-todo "+waitlist+SCHEDULED<=\"<+14d>\""
((org-agenda-overriding-header "Waitlist")
(org-agenda-hide-tags-regexp "waitlist")
(org-agenda-prefix-format " %i %-12:c %-12(my/org-scheduled-get-time)")))))
("tp" "Personal tasks"
((tags-todo "personal"
((org-agenda-prefix-format " %i %-12:c [%e] ")))))))
(use-package org-journal
:straight t
:if (not my/remote-server)
:after org
:config
(setq org-journal-dir (concat org-directory "/journal"))
(setq org-journal-file-type 'weekly)
(setq org-journal-file-format "%Y-%m-%d.org")
(setq org-journal-date-format "%A, %Y-%m-%d")
(setq org-journal-enable-encryption t))
(my-leader-def
:infix "oj"
"" '(:which-key "org-journal")
"j" 'org-journal-new-entry
"o" 'org-journal-open-current-journal-file
"s" 'org-journal-search)
(defun my/set-journal-header ()
(org-set-property "Emacs" emacs-version)
(org-set-property "Hostname" system-name)
(when (boundp 'my/location)
(org-set-property "Location" my/location))
(when (fboundp 'emms-playlist-current-selected-track)
(let ((track (emms-playlist-current-selected-track)))
(when track
(let ((album (cdr (assoc 'info-album track)))
(artist (or (cdr (assoc 'info-albumartist track))
(cdr (assoc 'info-album track))))
(title (cdr (assoc 'info-title track)))
(string ""))
(when artist
(setq string (concat string "[" artist "] ")))
(when album
(setq string (concat string album " - ")))
(when title
(setq string (concat string title)))
(when (> (length string) 0)
(org-set-property "EMMS_Track" string)))))))
(add-hook 'org-journal-after-entry-create-hook
#'my/set-journal-header)
(use-package emacsql-sqlite
:defer t
:straight (:type built-in))
(use-package org-roam
:straight (:host github :repo "org-roam/org-roam"
:files (:defaults "extensions/*.el"))
:if (not my/remote-server)
:after org
:init
(setq org-roam-file-extensions '("org"))
(setq org-roam-v2-ack t)
(setq orb-insert-interface 'ivy-bibtex)
:config
(org-roam-setup)
(require 'org-roam-protocol))
(setq my/org-roam-project-template
`("p" "project" plain ,(string-join
'("%?"
"* Tasks"
"** TODO Add initials tasks"
"* Log :log_here:")
"\n")
:if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org"
,(string-join
'("#+title: ${title}"
"#+category: ${title}"
"#+filetags: :org:log_here:"
"#+TODO: TODO(t) NEXT(n) HOLD(h) | NO(q) DONE(d)"
"#+TODO: FUTURE(f) | PASSED(p)"
"#+STARTUP: logdone overview")
"\n"))
:unnarrowed t))
(setq org-roam-capture-templates
`(("d" "default" plain "%?"
:if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n")
:unnarrowed t)
("e" "encrypted" plain "%?"
:if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org.gpg" "#+title: ${title}\n")
:unnarrowed t)
,my/org-roam-project-template))
(defun my/make-daily-header-track ()
(when (fboundp 'emms-playlist-current-selected-track)
(let ((track (emms-playlist-current-selected-track)))
(when track
(let ((album (cdr (assoc 'info-album track)))
(artist (or (cdr (assoc 'info-albumartist track))
(cdr (assoc 'info-album track))))
(title (cdr (assoc 'info-title track)))
(string ""))
(when artist
(setq string (concat string "[" artist "] ")))
(when album
(setq string (concat string album " - ")))
(when title
(setq string (concat string title)))
(when (> (length string) 0)
string))))))
(defun my/make-daily-header ()
(string-join
(seq-filter
#'identity
`(":PROPERTIES:"
,(format ":Emacs: %s" emacs-version)
,(format ":Hostname: %s" system-name)
,(when (boundp 'my/location)
(format ":Location: %s" my/location))
,(when-let (track (my/make-daily-header-track))
(format ":EMMS_Track: %s" track))
":END:"))
"\n"))
(setq org-roam-dailies-capture-templates
`(("d" "default" entry
,(string-join
'("* %<%H:%M>"
"%(my/make-daily-header)"
"%?")
"\n")
:target
(file+head "%<%Y-%m-%d>.org.gpg"
,(string-join
'("#+TITLE: %<%Y-%m-%d, %A>"
"#+FILETAGS: log"
"")
"\n")))))
(cl-defmacro my/org-roam-filter-by-tag (&optional &key (include nil) (exclude nil))
`(lambda (node)
(let ((tags (org-roam-node-tags node)))
(and
,(if include
`(or
,@(mapcar (lambda (tag)
`(member ,tag tags))
include))
t)
,@(mapcar (lambda (tag)
`(not (member ,tag tags)))
exclude)))))
(defun my/org-roam-list-notes-by-tag (tag-name)
(mapcar #'org-roam-node-file
(seq-filter
(my/org-roam-filter-by-tag :include (tag-name))
(org-roam-node-list))))
(defun my/org-roam-refresh-agenda-list ()
(interactive)
(let ((project-files (my/org-roam-list-notes-by-tag "org")))
(setq org-agenda-files
(seq-uniq
`(,@org-agenda-files
,@project-files)))
(dolist (file project-files)
(add-to-list 'org-refile-targets
`(,file :maxlevel . 2)))))
(with-eval-after-load 'org-roam
(my/org-roam-refresh-agenda-list))
(defun my/org-roam-find-project ()
(interactive)
(org-roam-node-find
nil
nil
(my/org-roam-filter-by-tag :include ("org"))
:templates
`(,my/org-roam-project-template)))
(defun my/org-roam-daily-extract-target-links ()
(save-excursion
(goto-char (point-min))
(cl-loop while (not (eobp))
do (forward-line 1)
for match = (save-excursion
(search-forward-regexp
(rx "[" (* nonl) "]")
(line-end-position)
t))
for node = (when-let (link (org-element-link-parser))
(when (string-equal (plist-get (cadr link) :type) "id")
(when-let (node (org-roam-node-from-id
(plist-get (cadr link) :path)))
(and (member "log_here" (org-roam-node-tags node))
node))))
if (and node match)
collect (list
node
(line-number-at-pos)
;; Hardcoded for now
1
(save-excursion
(org-back-to-heading)
(org-element-property :title (org-element-at-point)))))))
(defun my/org-roam-node-insert-log ()
(interactive)
(beginning-of-line)
(org-roam-node-insert
(my/org-roam-filter-by-tag :include ("log_here")))
(insert ": "))
(defun my/org-roam-daily-get-transclude-header ()
(let ((kws (org-collect-keywords '("TITLE"))))
(unless kws
(error "No title found!"))
(cadar kws)))
(defun my/org-roam-daily-find-log-header ()
(let ((log-header nil))
(org-map-entries
(lambda ()
(let* ((headline (org-element-at-point))
(tags (mapcar #'substring-no-properties
(org-element-property :tags headline))))
(when (member "log_here" tags)
(setq log-header headline)))))
(unless log-header
(error "Header with :log_here: tag not found"))
log-header))
(defun my/org-insert-alphabetical-header (root-header target-header)
(let ((target-headline)
(last-header "")
(last-headline)
(first-headline))
(goto-char (org-element-property :begin root-header))
;; Map the tree under root-header
(org-map-tree
(lambda ()
(let* ((headline (org-element-at-point))
(header (org-element-property :title headline)))
(when (/= (point) (org-element-property :begin root-header))
;; Try to find a heading with title equal to target-header
(when (string-equal target-header header)
(setq target-headline headline))
;; Or try to find a heading < target-header
(when (and (string-lessp header target-header)
(string-greaterp header last-header))
(setq last-header header)
(setq last-headline headline))
(unless first-headline
(setq first-headline headline))))))
(if target-headline
;; If a matching header is found, clear its contents
(let ((content-start (save-excursion
(goto-char
(org-element-property :begin target-headline))
(forward-line 1)
(point)))
(content-end (org-element-property :end target-headline)))
(if (<= content-start content-end)
(progn
(delete-region content-start content-end)
(goto-char (org-element-property :begin target-headline))
(end-of-line)
(insert "\n"))
(goto-char content-end)))
;; If a heading < target-header is found, insert the new one just after it
(if last-headline
(progn
(goto-char (org-element-property :begin last-headline))
(org-insert-heading-respect-content)
(insert target-header)
(insert "\n"))
;; If neither is found, insert a new heading before the first headline
(if first-headline
(progn
(goto-char (org-element-property :begin first-headline))
(org-insert-heading)
(insert target-header)
(insert "\n"))
;; If there is not even a first heading, that means the target header is empty
(goto-char (org-element-property :begin root-header))
(org-insert-heading-respect-content)
(org-do-demote)
(insert target-header)
(insert "\n"))))))
(defun my/org-roam-daily-format-target-links (links path)
(string-trim
(cl-loop for i from 0 to (length links)
for link in links
for line-number = (nth 1 link)
for line-count = (nth 2 link)
for title = (nth 3 link)
for prev-title = (if (> i 0) (nth 3 (nth (1- i) links)) "")
concat (string-join
(seq-filter
#'identity
`(,(unless (string-equal title prev-title)
(format "%s:" title))
,(format "#+transclude: [[file:%s]] :lines %d-%d"
path
line-number
(+ line-number line-count -1))
""
""))
"\n"))))
(defun my/org-roam-daily-dispatch-transclusions ()
(interactive)
(let* ((targets (my/org-roam-daily-extract-target-links))
(target-groups
(seq-group-by
(lambda (item)
(org-roam-node-file (nth 0 item)))
targets))
(header (my/org-roam-daily-get-transclude-header))
(path (buffer-file-name)))
(dolist (group target-groups)
(with-temp-file (car group)
(insert-file-contents (car group))
(org-mode)
(my/org-insert-alphabetical-header
(my/org-roam-daily-find-log-header)
header)
(insert (my/org-roam-daily-format-target-links (cdr group) path))))))
(defun my/org-roam-daily-transclusions-hook ()
(when (org-roam-dailies--daily-note-p)
(my/org-roam-daily-dispatch-transclusions)
(message "Tranclusions dispatched!")))
(with-eval-after-load 'org-roam
(add-hook 'after-save-hook #'my/org-roam-daily-transclusions-hook))
(defun my/org-roam-find-zk ()
(interactive)
(org-roam-node-find
nil
nil
(my/org-roam-filter-by-tag :exclude ("log" "org"))))
(defun my/org-roam-find-daily ()
(interactive)
(org-roam-node-find
nil
nil
(my/org-roam-filter-by-tag :include ("log"))))
(my-leader-def
:infix "or"
"" '(:which-key "org-roam")
"i" 'org-roam-node-insert
"r" '(my/org-roam-find-zk :wk "ZK")
"p" '(my/org-roam-find-project :wk "Projects")
"g" 'org-roam-graph
"c" 'org-roam-capture
"b" 'org-roam-buffer-toggle)
(my-leader-def
:infix "od"
"" '(:which-key "org-roam-dailies")
"d" #'org-roam-dailies-capture-today
"o" #'org-roam-dailies-goto-today
"f" #'my/org-roam-find-daily
"i" #'my/org-roam-node-insert-log)
(with-eval-after-load 'org-roam
(general-define-key
:keymaps 'org-roam-mode-map
:states '(normal)
"TAB" #'magit-section-toggle
"q" #'quit-window
"k" #'magit-section-backward
"j" #'magit-section-forward
"gr" #'revert-buffer
"RET" #'org-roam-buffer-visit-thing))
(with-eval-after-load 'org
(my-leader-def
:keymap 'org-mode-map
:infix "or"
"t" 'org-roam-tag-add
"T" 'org-toam-tag-remove)
(general-define-key
:keymap 'org-mode-map
"C-c i" 'org-id-get-create
"C-c l o" 'org-roam-node-insert))
(use-package org-roam-ui
:straight (:host github :repo "org-roam/org-roam-ui" :branch "main" :files ("*.el" "out"))
:after org-roam
;; :hook (org-roam . org-roam-ui-mode)
:init
(my-leader-def "oru" #'org-roam-ui-mode))
(setq my/git-diff-status
'(("A" . added)
("C" . copied)
("D" . deleted)
("M" . modified)
("R" . renamed)
("T" . type-changed)
("U" . unmerged)))
(defun my/get-files-status (rev)
(let ((files (shell-command-to-string (concat "git diff --name-status " rev))))
(mapcar
(lambda (file)
(let ((elems (split-string file "\t")))
(cons
(cdr (assoc (car elems) my/git-diff-status))
(nth 1 elems))))
(split-string files "\n" t))))
(defun my/org-changed-files-since-date (date)
(let ((default-directory org-directory))
(my/get-files-status (format "@{%s}" date))))
(setq my/org-review-roam-queries
'((:status added
:tags (:include ("org"))
:title "New Project Entries")
(:status changed
:tags (:include ("org"))
:title "Changed Project Entries")
(:status added
:tags (:include ("log") :exclude ("org" "log_here"))
:title "New Dailies")
(:status added
:tags (:exclude ("log" "org"))
:title "New Zettelkasten Entries")
(:status changed
:tags (:exclude ("log" "org"))
:title "Changed Zettelkasten Entries")))
(defun my/org-review-format-roam (changes)
(cl-loop for query in my/org-review-roam-queries
with nodes = (org-roam-node-list)
with node-tags = (mapcar #'org-roam-node-tags nodes)
for include-tags = (plist-get (plist-get query :tags) :include)
for exclude-tags = (plist-get (plist-get query :tags) :exclude)
;; List of nodes filtered by :tags in query
for filtered-nodes =
(cl-loop for node in nodes
for tags in node-tags
if (and
(or (seq-empty-p include-tags)
(seq-intersection include-tags tags))
(or (seq-empty-p exclude-tags)
(not (seq-intersection exclude-tags tags))))
collect node)
;; List of changes filtered by :status in query
for filtered-changes =
(cl-loop for change in changes
if (and (eq (car change) (plist-get query :status))
(string-match-p (rx bos "roam") (cdr change)))
collect (cdr change))
;; Intersection of the two filtered lists
for final-nodes =
(cl-loop for node in filtered-nodes
for path = (file-relative-name (org-roam-node-file node)
org-directory)
if (member path filtered-changes)
collect node)
;; If the intersction list is not empty, format it to the result
if final-nodes
concat (format "** %s\n" (plist-get query :title))
;; FInal list of links, sorted by title
and concat (cl-loop for node in (seq-sort
(lambda (node1 node2)
(string-lessp
(org-roam-node-title node1)
(org-roam-node-title node2)))
final-nodes)
concat (format "- [[id:%s][%s]]\n"
(org-roam-node-id node)
(org-roam-node-title node)))))
(setq my/org-ql-review-queries
`(("Waitlist" scheduled scheduled
(and
(done)
(tags-inherited "waitlist")))
("Personal tasks done" closed ,nil
(and
(tags-inherited "personal")
(todo "DONE")))
("Attended meetings" closed scheduled
(and
(tags-inherited "meeting")
(todo "PASSED")))
("Done project tasks" closed deadline
(and
(todo "DONE")
(ancestors
(heading "Tasks"))))))
(defun my/org-review-exec-ql (saved rev-date)
(let ((query `(and
(,(nth 1 saved) :from ,rev-date)
,(nth 3 saved))))
(org-ql-query
:select #'element
:from (org-agenda-files)
:where query
:order-by (nth 2 saved))))
(defun my/org-review-format-element (elem)
(concat
(string-pad
(plist-get (cadr elem) :raw-value)
40)
(when-let (scheduled (plist-get (cadr elem) :scheduled))
(concat " [SCHEDULED: " (plist-get (cadr scheduled) :raw-value) "]"))
(when-let (deadline (plist-get (cadr elem) :deadline))
(concat " [DEADLINE: " (plist-get (cadr deadline) :raw-value) "]"))))
(defun my/org-review-format-queries (rev-date)
(mapconcat
(lambda (results)
(concat "** " (car results) "\n"
(string-join
(mapcar (lambda (r) (concat "- " r)) (cdr results))
"\n")
"\n"))
(seq-filter
(lambda (result)
(not (seq-empty-p (cdr result))))
(mapcar
(lambda (saved)
(cons
(car saved)
(mapcar
#'my/org-review-format-element
(my/org-review-exec-ql saved rev-date))))
my/org-ql-review-queries))
"\n"))
(setq my/org-review-directory "review")
(defun my/get-last-review-date ()
(substring
(or
(-max-by
'string-greaterp
(-filter
(lambda (f) (not (or (string-equal f ".") (string-equal f ".."))))
(directory-files (f-join org-roam-directory my/org-review-directory))))
(format-time-string
"%Y-%m-%d"
(time-subtract
(current-time)
(seconds-to-time (* 60 60 24 14)))))
0 10))
(setq my/org-review-capture-template
`("r" "Review" plain
,(string-join
'("#+title: %<%Y-%m-%d>: REVIEW"
"#+category: REVIEW"
"#+filetags: log review"
"#+STARTUP: overview"
""
"Last review date: %(org-timestamp-translate (org-timestamp-from-string (format \"<%s>\" (my/get-last-review-date))))"
""
"* Roam"
"%(my/org-review-format-roam (my/org-changed-files-since-date (my/get-last-review-date)))"
"* Agenda"
"%(my/org-review-format-queries (my/get-last-review-date))"
"* Thoughts"
"%?")
"\n")
:if-new (file "review/%<%Y-%m-%d>.org.gpg")))
(defun my/org-roam-capture-review ()
(interactive)
(org-roam-capture- :node (org-roam-node-create)
:templates `(,my/org-review-capture-template)))
(use-package org-ref
:straight (:files (:defaults (:exclude "*helm*")))
:if (not my/remote-server)
:init
(setq org-ref-completion-library 'org-ref-ivy-cite)
(setq bibtex-dialect 'biblatex)
(setq org-ref-default-bibliography '("~/Documents/org-mode/bibliography.bib"))
(setq reftex-default-bibliography org-ref-default-bibliography)
(setq bibtex-completion-bibliography org-ref-default-bibliography)
:after (org)
:config
(general-define-key
:keymaps 'org-mode-map
"C-c l" #'org-ref-insert-link-hydra/body)
(general-define-key
:keymaps 'bibtex-mode-map
"M-RET" 'org-ref-bibtex-hydra/body)
;; (add-to-list 'orhc-candidate-formats
;; '("online" . " |${=key=}| ${title} ${url}"))
)
(defun my/org-ref-select-bibliograhy ()
(interactive)
(setq-local org-ref-default-bibliography
`(,(read-file-name "Bibliograhy: " nil nil t)))
(setq-local reftex-default-bibliography org-ref-default-bibliography)
(setq-local bibtex-completion-bibliography org-ref-default-bibliography))
(use-package org-roam-bibtex
:straight (:host github :repo "org-roam/org-roam-bibtex")
:after (org-roam org-ref)
:disabled
:config
(org-roam-bibtex-mode))
(defun my/export-org-tables-to-csv ()
(interactive)
(org-table-map-tables
(lambda ()
(when-let
(name
(plist-get (cadr (org-element-at-point)) :name))
(org-table-export
(concat
(file-name-directory
(buffer-file-name))
name ".csv")
"orgtbl-to-csv")))))
(use-package org-latex-impatient
:straight (:repo "yangsheng6810/org-latex-impatient"
:branch "master"
:host github)
:hook (org-mode . org-latex-impatient-mode)
:disabled
:init
(setq org-latex-impatient-tex2svg-bin
"/home/pavel/Programs/miniconda3/lib/node_modules/mathjax-node-cli/bin/tex2svg")
(setq org-latex-impatient-scale 1.75)
(setq org-latex-impatient-delay 1)
(setq org-latex-impatient-border-color "#ffffff"))
(defun my/enable-org-latex ()
(interactive)
(customize-set-variable 'org-highlight-latex-and-related '(native))
(add-hook 'org-mode-hook (lambda () (yas-activate-extra-mode 'LaTeX-mode)))
(sp-local-pair 'org-mode "$" "$")
(sp--remove-local-pair "'"))
(use-package org-superstar
:straight t
:disabled
:hook (org-mode . org-superstar-mode))
(use-package org-bars
:straight (:repo "tonyaldon/org-bars" :host github)
:if (display-graphic-p)
:hook (org-mode . org-bars-mode))
(defun my/org-no-ellipsis-in-headlines ()
(remove-from-invisibility-spec '(outline . t))
(add-to-invisibility-spec 'outline))
(add-hook 'org-mode-hook #'my/org-no-ellipsis-in-headlines)
;; (setq org-export-backends '(md html latex beamer org))
(use-package ox-hugo
:straight t
:after ox)
(use-package ox-ipynb
:straight (:host github :repo "jkitchin/ox-ipynb")
:after ox)
(use-package htmlize
:straight t
:after ox
:config
(setq org-html-htmlize-output-type 'css))
(defun my/setup-org-latex ()
(setq org-latex-prefer-user-labels t)
(setq org-latex-compiler "xelatex") ;; Probably not necessary
(setq org-latex-pdf-process '("latexmk -outdir=%o %f")) ;; Use latexmk
(setq org-latex-listings 'minted) ;; Use minted to highlight source code
(setq org-latex-minted-options ;; Some minted options I like
'(("breaklines" "true")
("tabsize" "4")
("autogobble")
("linenos")
("numbersep" "0.5cm")
("xleftmargin" "1cm")
("frame" "single")))
;; Use extarticle without the default packages
(add-to-list 'org-latex-classes
'("org-plain-extarticle"
"\\documentclass{extarticle}
[NO-DEFAULT-PACKAGES]
[PACKAGES]
[EXTRA]"
("\\section{%s}" . "\\section*{%s}")
("\\subsection{%s}" . "\\subsection*{%s}")
("\\subsubsection{%s}" . "\\subsubsection*{%s}")
("\\paragraph{%s}" . "\\paragraph*{%s}")
("\\subparagraph{%s}" . "\\subparagraph*{%s}")))
;; Use beamer without the default packages
(add-to-list 'org-latex-classes
'("org-latex-beamer"
"\\documentclass{beamer}
[NO-DEFAULT-PACKAGES]
[PACKAGES]
[EXTRA]"
("beamer" "\\documentclass[presentation]{beamer}"
("\\section{%s}" . "\\section*{%s}")
("\\subsection{%s}" . "\\subsection*{%s}")
("\\subsubsection{%s}" . "\\subsubsection*{%s}")))))
;; Make sure to eval the function when org-latex-classes list already exists
(with-eval-after-load 'ox-latex
(my/setup-org-latex))
(defun my/org-link-copy (&optional arg)
"Extract URL from org-mode link and add it to kill ring."
(interactive "P")
(let* ((link (org-element-lineage (org-element-context) '(link) t))
(type (org-element-property :type link))
(url (org-element-property :path link))
(url (concat type ":" url)))
(kill-new url)
(message (concat "Copied URL: " url))))
(general-nmap :keymaps 'org-mode-map
"C-x C-l" 'my/org-link-copy)
(defun my/org-file-open ()
(interactive)
(let* ((default-directory org-directory)
(project-files
(seq-filter
(lambda (f)
(and
(string-match-p (rx (* nonl) ".org" eos) f)
(not (string-match-p (rx (| "journal" "roam" "review" "archive")) f))))
(projectile-current-project-files))))
(find-file
(concat org-directory "/" (completing-read "Org file: " project-files)))))
(my-leader-def
"o o" 'my/org-file-open)
(defun my/extract-guix-dependencies (&optional category)
(let ((dependencies '()))
(org-table-map-tables
(lambda ()
(let* ((table
(seq-filter
(lambda (q) (not (eq q 'hline)))
(org-table-to-lisp)))
(dep-name-index
(cl-position
nil
(mapcar #'substring-no-properties (nth 0 table))
:test (lambda (_ elem)
(string-match-p "[G|g]uix.*dep" elem))))
(category-name-index
(cl-position
nil
(mapcar #'substring-no-properties (nth 0 table))
:test (lambda (_ elem)
(string-match-p ".*[C|c]ategory.*" elem))))
(disabled-name-index
(cl-position
nil
(mapcar #'substring-no-properties (nth 0 table))
:test (lambda (_ elem)
(string-match-p ".*[D|d]isabled.*" elem)))))
(when dep-name-index
(dolist (elem (cdr table))
(when
(and
;; Category
(or
;; Category not set and not present in the table
(and
(or (not category) (string-empty-p category))
(not category-name-index))
;; Category is set and present in the table
(and
category-name-index
(not (string-empty-p category))
(string-match-p category (nth category-name-index elem))))
;; Not disabled
(or
(not disabled-name-index)
(string-empty-p (nth disabled-name-index elem))))
(add-to-list
'dependencies
(substring-no-properties (nth dep-name-index elem)))))))))
dependencies))
(defun my/format-guix-dependencies (&optional category)
(mapconcat
(lambda (e) (concat "\"" e "\""))
(my/extract-guix-dependencies category)
"\n"))
(setq my/org-config-files
'("/home/pavel/Emacs.org"
"/home/pavel/Desktop.org"
"/home/pavel/Console.org"
"/home/pavel/Guix.org"
"/home/pavel/Mail.org"))
(add-hook 'org-mode-hook
(lambda ()
(when (member (buffer-file-name) my/org-config-files)
(setq-local org-confirm-babel-evaluate nil))))
(use-package dired
:ensure nil
:custom ((dired-listing-switches "-alh --group-directories-first"))
:commands (dired)
:config
(setq dired-dwim-target t)
(setq wdired-allow-to-change-permissions t)
(setq wdired-create-parent-directories t)
(setq dired-recursive-copies 'always)
(setq dired-recursive-deletes 'always)
(setq dired-kill-when-opening-new-dired-buffer t)
(add-hook 'dired-mode-hook
(lambda ()
(setq truncate-lines t)
(visual-line-mode nil)))
(general-define-key
:states '(normal)
:keymaps 'dired-mode-map
"h" 'dired-up-directory
"l" 'dired-find-file
"=" 'dired-narrow
"-" 'dired-create-empty-file
"~" 'vterm
"<left>" 'dired-up-directory
"<right>" 'dired-find-file
"M-<return>" 'dired-open-xdg))
(defun my/dired-home ()
"Open dired at $HOME"
(interactive)
(dired (expand-file-name "~")))
(my-leader-def
"ad" #'dired
"aD" (my/command-in-persp "dired $HOME" "dired" nil (dired (expand-file-name "~"))))
(use-package diredfl
:straight t
:after dired
:config
(diredfl-global-mode 1))
(use-package dired-single
:after dired
:disabled
:straight t)
(use-package all-the-icons-dired
:straight t
:if (not (or my/lowpower my/slow-ssh (not (display-graphic-p))))
:hook (dired-mode . (lambda ()
(unless (string-match-p "/gnu/store" default-directory)
(all-the-icons-dired-mode))))
:config
(advice-add 'dired-add-entry :around #'all-the-icons-dired--refresh-advice)
(advice-add 'dired-remove-entry :around #'all-the-icons-dired--refresh-advice)
(advice-add 'dired-kill-subdir :around #'all-the-icons-dired--refresh-advice))
(use-package dired-open
:straight t
:commands (dired-open-xdg))
(use-package dired-narrow
:straight t
:commands (dired-narrow)
:config
(general-define-key
:keymaps 'dired-narrow-map
[escape] 'keyboard-quit))
(use-package dired-git-info
:straight t
:after dired
:if (not my/slow-ssh)
:config
(general-define-key
:keymap 'dired-mode-map
:states '(normal emacs)
")" 'dired-git-info-mode))
(defun my/dired-open-this-subdir ()
(interactive)
(dired (dired-current-directory)))
(defun my/dired-kill-all-subdirs ()
(interactive)
(let ((dir dired-directory))
(kill-buffer (current-buffer))
(dired dir)))
(with-eval-after-load 'dired
(general-define-key
:states '(normal)
:keymaps 'dired-mode-map
"s" nil
"ss" 'dired-maybe-insert-subdir
"sl" 'dired-maybe-insert-subdir
"sq" 'dired-kill-subdir
"sk" 'dired-prev-subdir
"sj" 'dired-next-subdir
"sS" 'my/dired-open-this-subdir
"sQ" 'my/dired-kill-all-subdirs
(kbd "TAB") 'dired-hide-subdir))
(setq tramp-verbose 1)
(setq remote-file-name-inhibit-cache nil)
(setq vc-ignore-dir-regexp
(format "\\(%s\\)\\|\\(%s\\)"
vc-ignore-dir-regexp
tramp-file-name-regexp))
(when (or my/remote-server my/slow-ssh)
(setq explicit-shell-file-name "/bin/bash"))
(with-eval-after-load 'tramp
(setq tramp-remote-path
(append tramp-remote-path
'(tramp-own-remote-path))))
(defun my/dired-bookmark-open ()
(interactive)
(let ((bookmarks
(mapcar
(lambda (el) (cons (format "%-30s %s" (car el) (cdr el)) (cdr el)))
my/dired-bookmarks)))
(dired
(cdr
(assoc
(completing-read "Dired: " bookmarks nil nil "^")
bookmarks)))))
(use-package vterm
;; :straight t
:commands (vterm vterm-other-window)
:config
(setq vterm-kill-buffer-on-exit t)
(add-hook 'vterm-mode-hook
(lambda ()
(setq-local global-display-line-numbers-mode nil)
(display-line-numbers-mode 0)))
(advice-add 'evil-collection-vterm-insert
:before (lambda (&rest args)
(ignore-errors
(apply #'vterm-reset-cursor-point args))))
(general-define-key
:keymaps 'vterm-mode-map
"M-q" 'vterm-send-escape
"C-h" 'evil-window-left
"C-l" 'evil-window-right
"C-k" 'evil-window-up
"C-j" 'evil-window-down
"C-<right>" 'evil-window-right
"C-<left>" 'evil-window-left
"C-<up>" 'evil-window-up
"C-<down>" 'evil-window-down
"M-<left>" 'vterm-send-left
"M-<right>" 'vterm-send-right
"M-<up>" 'vterm-send-up
"M-<down>" 'vterm-send-down)
(general-define-key
:keymaps 'vterm-mode-map
:states '(normal insert)
"<home>" 'vterm-beginning-of-line
"<end>" 'vterm-end-of-line)
(general-define-key
:keymaps 'vterm-mode-map
:states '(insert)
"C-r" 'vterm-send-C-r
"C-k" 'vterm-send-C-k
"C-j" 'vterm-send-C-j
"M-l" 'vterm-send-right
"M-h" 'vterm-send-left
"M-k" 'vterm-send-up
"M-j" 'vterm-send-down))
(add-to-list 'display-buffer-alist
`(,"vterm-subterminal.*"
(display-buffer-reuse-window
display-buffer-in-side-window)
(side . bottom)
(reusable-frames . visible)
(window-height . 0.33)))
(defun my/toggle-vterm-subteminal ()
"Toogle subteminal."
(interactive)
(let
((vterm-window
(seq-find
(lambda (window)
(string-match
"vterm-subterminal.*"
(buffer-name (window-buffer window))))
(window-list))))
(if vterm-window
(if (eq (get-buffer-window (current-buffer)) vterm-window)
(kill-buffer (current-buffer))
(select-window vterm-window))
(vterm-other-window "vterm-subterminal"))))
(unless my/slow-ssh
(general-nmap "`" 'my/toggle-vterm-subteminal)
(general-nmap "~" 'vterm))
(defun my/vterm-get-pwd ()
(if vterm--process
(file-truename (format "/proc/%d/cwd" (process-id vterm--process)))
default-directory))
(defun my/vterm-dired-other-window ()
"Open dired in vterm pwd in other window"
(interactive)
(dired-other-window (my/vterm-get-pwd)))
(defun my/vterm-dired-replace ()
"Replace vterm with dired"
(interactive)
(let ((pwd (my/vterm-get-pwd)))
(kill-process vterm--process)
(dired pwd)))
(with-eval-after-load 'vterm
(general-define-key
:keymaps 'vterm-mode-map
:states '(normal)
"gd" #'my/vterm-dired-other-window
"gD" #'my/vterm-dired-replace))
(use-package with-editor
:straight t
:after (vterm)
:config
(add-hook 'vterm-mode-hook 'with-editor-export-editor))
(defun my/configure-eshell ()
(add-hook 'eshell-pre-command-hook 'eshell-save-some-history)
(add-to-list 'eshell-output-filter-functions 'eshell-truncate-buffer)
(setq eshell-history-size 10000)
(setq eshell-hist-ingnoredups t)
(setq eshell-buffer-maximum-lines 10000)
(evil-define-key '(normal insert visual) eshell-mode-map (kbd "<home>") 'eshell-bol)
(evil-define-key '(normal insert visual) eshell-mode-map (kbd "C-r") 'counsel-esh-history)
(general-define-key
:states '(normal)
:keymaps 'eshell-mode-map
(kbd "C-h") 'evil-window-left
(kbd "C-l") 'evil-window-right
(kbd "C-k") 'evil-window-up
(kbd "C-j") 'evil-window-down))
(use-package eshell
:ensure nil
:after evil-collection
:commands (eshell)
:config
(add-hook 'eshell-first-time-mode-hook 'my/configure-eshell 90)
(when my/slow-ssh
(add-hook 'eshell-mode-hook
(lambda ()
(setq-local company-idle-delay 1000))))
(setq eshell-banner-message ""))
(use-package aweshell
:straight (:repo "manateelazycat/aweshell" :host github)
:after eshell
:config
(setq eshell-highlight-prompt nil)
(setq eshell-prompt-function 'epe-theme-pipeline))
(use-package eshell-info-banner
:defer t
:if (not my/slow-ssh)
:straight (eshell-info-banner :type git
:host github
:repo "phundrak/eshell-info-banner.el")
:hook (eshell-banner-load . eshell-info-banner-update-banner))
(when my/slow-ssh
(general-nmap "`" 'aweshell-dedicated-toggle)
(general-nmap "~" 'eshell))
(general-define-key
"C-c c" (my/command-in-persp "Emacs.org" "conf" 1 (find-file "~/Emacs.org")))
(my-leader-def
:infix "c"
"" '(:which-key "configuration")
"c" (my/command-in-persp "Emacs.org" "conf" 1 (find-file "~/Emacs.org")))
(with-eval-after-load 'tramp
(add-to-list 'tramp-methods
`("yadm"
(tramp-login-program "yadm")
(tramp-login-args (("enter")))
(tramp-login-env (("SHELL") "/bin/sh"))
(tramp-remote-shell "/bin/sh")
(tramp-remote-shell-args ("-c")))))
(defun my/yadm-magit ()
(interactive)
(magit-status "/yadm::"))
(my-leader-def "cm" 'my/yadm-magit)
(defun my/open-yadm-file ()
"Open a file managed by yadm"
(interactive)
(find-file
(concat
(file-name-as-directory (getenv "HOME"))
(completing-read
"yadm files: "
(split-string
(shell-command-to-string "yadm ls-files $HOME --full-name") "\n")))))
(general-define-key
"C-c f" (my/command-in-persp
"yadm file" "conf" 1
(my/open-yadm-file)))
(my-leader-def
"cf" (my/command-in-persp
"yadm file" "conf" 1
(my/open-yadm-file)))
(unless (or my/is-termux my/remote-server)
(load-file (expand-file-name "mail.el" user-emacs-directory)))
(use-package elfeed
:straight (:repo "SqrtMinusOne/elfeed" :host github)
:if (not my/remote-server)
:commands (elfeed)
:init
(my-leader-def "ae" (my/command-in-persp "elfeed" "elfeed" 0 (elfeed)))
:config
(setq elfeed-db-directory "~/.elfeed")
(setq elfeed-enclosure-default-dir (expand-file-name "~/Downloads"))
(advice-add #'elfeed-insert-html
:around
(lambda (fun &rest r)
(let ((shr-use-fonts nil))
(apply fun r))))
(general-define-key
:states '(normal)
:keymaps 'elfeed-search-mode-map
"o" #'my/elfeed-search-filter-source
"c" #'elfeed-search-clear-filter
"gl" (lambda () (interactive) (elfeed-search-set-filter "+later")))
(general-define-key
:states '(normal)
:keymaps 'elfeed-show-mode-map
"ge" #'my/elfeed-show-visit-eww))
(use-package elfeed-org
:straight t
:after (elfeed)
:config
(setq rmh-elfeed-org-files '("~/.emacs.d/private.org"))
(elfeed-org))
(defun my/elfeed-search-filter-source (entry)
"Filter elfeed search buffer by the feed under cursor."
(interactive (list (elfeed-search-selected :ignore-region)))
(when (elfeed-entry-p entry)
(elfeed-search-set-filter
(concat
"@6-months-ago "
"+unread "
"="
(replace-regexp-in-string
(rx "?" (* not-newline) eos)
""
(elfeed-feed-url (elfeed-entry-feed entry)))))))
(defun my/elfeed-show-visit-eww ()
"Visit the current entry in eww"
(interactive)
(let ((link (elfeed-entry-link elfeed-show-entry)))
(when link
(eww link))))
(defface elfeed-videos-entry
`((t :foreground ,(doom-color 'red)))
"Face for the elfeed entries with tag \"videos\"")
(defface elfeed-twitter-entry
`((t :foreground ,(doom-color 'blue)))
"Face for the elfeed entries with tah \"twitter\"")
(defface elfeed-emacs-entry
`((t :foreground ,(doom-color 'magenta)))
"Face for the elfeed entries with tah \"emacs\"")
(defface elfeed-music-entry
`((t :foreground ,(doom-color 'green)))
"Face for the elfeed entries with tah \"music\"")
(defface elfeed-podcasts-entry
`((t :foreground ,(doom-color 'yellow)))
"Face for the elfeed entries with tag \"podcasts\"")
(defface elfeed-blogs-entry
`((t :foreground ,(doom-color 'orange)))
"Face for the elfeed entries with tag \"blogs\"")
(with-eval-after-load 'elfeed
(setq elfeed-search-face-alist
'((twitter elfeed-twitter-entry)
(podcasts elfeed-podcasts-entry)
(music elfeed-music-entry)
(videos elfeed-videos-entry)
(emacs elfeed-emacs-entry)
(blogs elfeed-blogs-entry)
(unread elfeed-search-unread-title-face))))
(defun my/update-my-theme-elfeed (&rest _)
(custom-theme-set-faces
'my-theme-1
`(elfeed-videos-entry ((t :foreground ,(doom-color 'red))))
`(elfeed-twitter-entry ((t :foreground ,(doom-color 'blue))))
`(elfeed-emacs-entry ((t :foreground ,(doom-color 'magenta))))
`(elfeed-music-entry ((t :foreground ,(doom-color 'green))))
`(elfeed-podcasts-entry ((t :foreground ,(doom-color 'yellow))))
`(elfeed-blogs-entry ((t :foreground ,(doom-color 'orange)))))
(enable-theme 'my-theme-1))
(advice-add 'load-theme :after #'my/update-my-theme-elfeed)
(when (fboundp 'doom-color)
(my/update-my-theme-elfeed))
(defun my/elfeed-toggle-score-sort ()
(interactive)
(setq elfeed-search-sort-function
(if elfeed-search-sort-function
nil
#'elfeed-score-sort))
(message "Sorting by score: %S" (if elfeed-search-sort-function "ON" "OFF"))
(elfeed-search-update--force))
(use-package elfeed-score
:straight t
:after (elfeed)
:init
(setq elfeed-score-serde-score-file "~/.emacs.d/elfeed.score")
:config
(elfeed-score-enable)
(setq elfeed-search-print-entry-function #'elfeed-score-print-entry)
(general-define-key
:states '(normal)
:keymaps '(elfeed-search-mode-map)
"=" elfeed-score-map)
(general-define-key
:keymaps '(elfeed-score-map)
"=" #'my/elfeed-toggle-score-sort))
(defun my/get-youtube-url (entry)
(let ((watch-id (cadr
(assoc "watch?v"
(url-parse-query-string
(substring
(url-filename
(url-generic-parse-url (elfeed-entry-link entry)))
1))))))
(when watch-id
(concat "https://www.youtube.com/watch?v=" watch-id))))
(defun my/get-enclosures-url (entry)
(caar (elfeed-entry-enclosures entry)))
(with-eval-after-load 'emms
(define-emms-source elfeed (entry)
(let ((url (or (my/get-enclosures-url entry)
(my/get-youtube-url entry))))
(unless url
(error "URL not found"))
(let ((track (emms-track 'url url)))
(emms-track-set track 'info-title (elfeed-entry-title entry))
(emms-playlist-insert-track track)))))
(defun my/elfeed-add-emms ()
(interactive)
(emms-add-elfeed elfeed-show-entry)
(elfeed-tag elfeed-show-entry 'watched)
(elfeed-show-refresh))
(with-eval-after-load 'elfeed
(general-define-key
:states '(normal)
:keymaps 'elfeed-show-mode-map
"gm" #'my/elfeed-add-emms))
(use-package emms
:straight t
:if (not my/remote-server)
:commands (emms-smart-browse
emms-browser
emms-add-url
emms-add-file
emms-add-find)
:if (not my/is-termux)
:init
(my-leader-def
:infix "as"
"" '(:which-key "emms")
"s" (my/command-in-persp "emms" "EMMS" 0 (emms-smart-browse))
"b" 'emms-browser
"p" 'emms-pause
"q" 'emms-stop
"h" 'emms-previous
"l" 'emms-next
"u" 'emms-player-mpd-connect)
(setq emms-mode-line-icon-enabled-p nil)
:config
(require 'emms-setup)
(require 'emms-player-mpd)
(require 'emms-player-mpv)
(emms-all)
;; MPD setup
(setq emms-source-file-default-directory (expand-file-name "~/Music/"))
(add-to-list 'emms-info-functions 'emms-info-mpd)
(add-to-list 'emms-player-list 'emms-player-mpd)
(setq emms-player-mpd-server-name "localhost")
(setq emms-player-mpd-server-port "6600")
(setq emms-player-mpd-music-directory "~/Music")
(emms-player-mpd-connect)
(add-hook 'emms-playlist-cleared-hook 'emms-player-mpd-clear)
(emms-player-set emms-player-mpd
'regex
(rx (or (: "https://" (* nonl) (or "acast.com") (* nonl))
(+ (? (or "https://" "http://"))
(* nonl)
(regexp (eval (emms-player-simple-regexp
"m3u" "ogg" "flac" "mp3" "wav" "mod" "au" "aiff")))))))
;; MPV setup
(add-to-list 'emms-player-list 'emms-player-mpv)
(emms-player-set emms-player-mpv
'regex
(rx (or (: "https://" (* nonl) "youtube.com" (* nonl))
(+ (? (or "https://" "http://"))
(* nonl)
(regexp (eval (emms-player-simple-regexp
"mp4" "mov" "wmv" "webm" "flv" "avi" "mkv")))))))
(setq my/youtube-dl-quality-list
'("bestvideo[height<=720]+bestaudio/best[height<=720]"
"bestvideo[height<=480]+bestaudio/best[height<=480]"
"bestvideo[height<=1080]+bestaudio/best[height<=1080]"))
(setq my/default-emms-player-mpv-parameters
'("--quiet" "--really-quiet" "--no-audio-display"))
(defun my/set-emms-mpd-youtube-quality (quality)
(interactive "P")
(unless quality
(setq quality (completing-read "Quality: " my/youtube-dl-quality-list nil t)))
(setq emms-player-mpv-parameters
`(,@my/default-emms-player-mpv-parameters ,(format "--ytdl-format=%s" quality))))
(my/set-emms-mpd-youtube-quality (car my/youtube-dl-quality-list))
;; evil-lion and evil-commentary shadow some gX bindings
;; (add-hook 'emms-browser-mode-hook
;; (lambda ()
;; (evil-lion-mode -1)
;; (evil-commentary-mode -1)
;; ))
;; I have everything I need in polybar
(emms-mode-line-mode -1)
(emms-playing-time-display-mode -1)
(defun emms-info-mpd-process (track info)
(dolist (data info)
(let ((name (car data))
(value (cdr data)))
(setq name (cond ((string= name "artist") 'info-artist)
((string= name "albumartist") 'info-albumartist)
((string= name "composer") 'info-composer)
((string= name "performer") 'info-performer)
((string= name "title") 'info-title)
((string= name "album") 'info-album)
((string= name "track") 'info-tracknumber)
((string= name "disc") 'info-discnumber)
((string= name "date") 'info-year)
((string= name "genre") 'info-genre)
((string= name "time")
(setq value (string-to-number value))
'info-playing-time)
(t nil)))
(when name
(emms-track-set track name value)))))
(defun emms-player-mpd-get-alists (info)
"Turn the given parsed INFO from MusicPD into an list of alists.
The list will be in reverse order."
(when (and info
(null (car info)) ; no error has occurred
(cdr info)) ; data exists
(let ((alists nil)
(alist nil)
cell)
(dolist (line (cdr info))
(when (setq cell (emms-player-mpd-parse-line line))
(if (member (car cell) '("file" "directory" "playlist"))
(setq alists (cons alist alists)
alist (list cell))
(setq alist (cons cell alist)))))
(when alist
(setq alists (cons alist alists)))
alists))))
(defun my/emms-cleanup-urls ()
(interactive)
(let ((keys-to-delete '()))
(maphash (lambda (key value)
(when (eq (cdr (assoc 'type value)) 'url)
(add-to-list 'keys-to-delete key)))
emms-cache-db)
(dolist (key keys-to-delete)
(remhash key emms-cache-db)))
(setq emms-cache-dirty t))
(my-leader-def "asc" #'my/emms-cleanup-urls)
(use-package lyrics-fetcher
:straight t
:after (emms)
:init
(my-leader-def
"ast" #'lyrics-fetcher-show-lyrics
"asT" #'lyrics-fetcher-show-lyrics-query)
:config
(setq lyrics-fetcher-genius-access-token
(password-store-get "My_Online/APIs/genius.com"))
(general-define-key
:states '(emacs normal)
:keymaps 'emms-browser-mode-map
"gl" 'lyrics-fetcher-emms-browser-show-at-point
"gC" 'lyrics-fetcher-emms-browser-fetch-covers-at-point
"go" 'lyrics-fetcher-emms-browser-open-large-cover-at-point))
(with-eval-after-load 'emms-browser
(general-define-key
:states '(normal)
:keymaps 'emms-browser-mode-map
"q" 'quit-window))
(with-eval-after-load 'emms
(general-define-key
:states '(normal)
:keymaps 'emms-playlist-mode-map
"q" 'quit-window))
(use-package ytel
:straight t
:commands (ytel)
:config
(setq ytel-invidious-api-url "https://invidio.xamh.de/")
(general-define-key
:states '(normal)
:keymaps 'ytel-mode-map
"q" #'ytel-quit
"s" #'ytel-search
"L" #'ytel-search-next-page
"H" #'ytel-search-previous-page
"RET" #'my/ytel-add-emms))
(with-eval-after-load 'emms
(define-emms-source ytel (video)
(let ((track (emms-track
'url (concat "https://www.youtube.com/watch?v="
(ytel-video-id video)))))
(emms-track-set track 'info-title (ytel-video-title video))
(emms-track-set track 'info-artist (ytel-video-author video))
(emms-playlist-insert-track track))))
(defun my/ytel-add-emms ()
(interactive)
(emms-add-ytel (ytel-get-current-video)))
(defun my/toggle-shr-use-fonts ()
"Toggle the shr-use-fonts variable in buffer"
(interactive)
(setq-local shr-use-fonts (not shr-use-fonts)))
(my-leader-def "aw" 'eww)
(general-define-key
:keymaps 'eww-mode-map
"+" 'text-scale-increase
"-" 'text-scale-decrease)
(use-package erc
:commands (erc erc-tls)
:straight (:type built-in)
:init
(my-leader-def "ai" (my/command-in-persp "erc" "ERC" 0 (erc-tls)))
:config
;; Logging
(setq erc-log-channels-directory "~/.erc/logs")
(setq erc-save-buffer-on-part t)
;; Config of my ZNC instance.
(setq erc-server "sqrtminusone.xyz")
(setq erc-port 1984)
(setq erc-nick "sqrtminusone")
(setq erc-user-full-name "Pavel Korytov")
(setq erc-password (password-store-get "Selfhosted/ZNC"))
(setq erc-kill-buffer-on-part t)
(setq erc-track-shorten-start 8))
(setq erc-track-exclude-types '("NICK" "JOIN" "LEAVE" "QUIT" "PART"
"301" ; away notice
"305" ; return from awayness
"306" ; set awayness
"324" ; modes
"329" ; channel creation date
"332" ; topic notice
"333" ; who set the topic
"353" ; Names notice
))
(use-package erc-hl-nicks
:hook (erc-mode . erc-hl-nicks-mode)
:after (erc)
:straight t)
(use-package znc
:straight t
:after (erc))
(use-package google-translate
:straight t
:functions (my-google-translate-at-point google-translate--search-tkk)
:custom
(google-translate-backend-method 'curl)
:config
(require 'facemenu)
(defun google-translate--search-tkk ()
"Search TKK."
(list 430675 2721866130))
(defun my-google-translate-at-point()
"reverse translate if prefix"
(interactive)
(if current-prefix-arg
(google-translate-at-point)
(google-translate-at-point-reverse)))
(setq google-translate-translation-directions-alist
'(("en" . "ru")
("ru" . "en"))))
(my-leader-def
:infix "at"
"" '(:which-key "google translate")
"p" 'google-translate-at-point
"P" 'google-translate-at-point-reverse
"q" 'google-translate-query-translate
"Q" 'google-translate-query-translate-reverse
"t" 'google-translate-smooth-translate)
(use-package tldr
:straight t
:commands (tldr)
:config
(setq tldr-source-zip-url "https://github.com/tldr-pages/tldr/archive/refs/heads/main.zip")
(defun tldr-update-docs ()
(interactive)
(shell-command-to-string (format "curl -L %s --output %s" tldr-source-zip-url tldr-saved-zip-path))
(when (file-exists-p "/tmp/tldr")
(delete-directory "/tmp/tldr" t))
(shell-command-to-string (format "unzip -d /tmp/tldr/ %s" tldr-saved-zip-path))
(when (file-exists-p tldr-directory-path)
(delete-directory tldr-directory-path 'recursive 'no-trash))
(shell-command-to-string (format "mv %s %s" "/tmp/tldr/tldr-main" tldr-directory-path))))
(my-leader-def "hT" 'tldr)
(setq Man-width-max 180)
(my-leader-def "hM" 'man)
(general-define-key
:states '(normal)
:keymaps 'Info-mode-map
(kbd "RET") 'Info-follow-nearest-node)
(defun my/man-fix-width (&rest _)
(setq-local Man-width (- (window-width) 4)))
(advice-add #'Man-update-manpage :before #'my/man-fix-width)
(use-package devdocs
:straight t
:commands (devdocs-install devdocs-lookup)
:init
(my-leader-def
"he" #'devdocs-lookup
"hE" #'devdocs-install))
(use-package pass
:straight t
:commands (pass)
:init
(my-leader-def "ak" #'pass)
:config
(setq pass-show-keybindings nil))
(use-package docker
:straight t
:commands (docker)
:init
(my-leader-def "ao" 'docker))
(setq my/selected-docker-directory nil)
(defun my/docker-override-dir (fun &rest args)
(let ((default-directory (or my/selected-docker-directory default-directory)))
(setq my/selected-docker-directory nil)
(apply fun args)))
(with-eval-after-load 'docker
(advice-add #'docker-compose-run-docker-compose-async :around #'my/docker-override-dir)
(advice-add #'docker-compose-run-docker-compose :around #'my/docker-override-dir)
(advice-add #'docker-run-docker-async :around #'my/docker-override-dir)
(advice-add #'docker-run-docker :around #'my/docker-override-dir))
(defun my/docker-from-dir ()
(interactive)
(when (not (boundp 'my/docker-directories))
(load (concat user-emacs-directory "prodigy-config")))
(let* ((directories
(mapcar
(lambda (el) (cons (format "%-30s %s" (car el) (cdr el)) (cdr el)))
my/docker-directories))
(selected-directory
(cdr (assoc (completing-read "Docker: " directories nil nil "^")
directories))))
(setq my/selected-docker-directory selected-directory)
(docker)))
(my-leader-def "aO" 'my/docker-from-dir)
(use-package prodigy
:straight t
:commands (prodigy)
:init
(my-leader-def "aP" 'prodigy)
:config
(when (not (boundp 'my/docker-directories))
(load (concat user-emacs-directory "prodigy-config")))
(general-define-key
:states '(normal)
:keymaps 'prodigy-view-mode-map
"C-h" 'evil-window-left
"C-l" 'evil-window-right
"C-k" 'evil-window-up
"C-j" 'evil-window-down))
(defun my/get-apps-on-ports ()
(mapcar
(lambda (line)
(let* ((split (split-string line (rx (| (+ " ") (+ "\t")))))
(process (elt split 6)))
`((netid . ,(elt split 0))
(state . ,(elt split 1))
(recv-q . ,(elt split 2))
(send-q . ,(elt split 3))
,@(let ((data (elt split 4)))
(save-match-data
(string-match (rx (group-n 1 (* nonl)) ":" (group-n 2 (or (+ num) "*"))) data)
`((local-address . ,(match-string 1 data))
(local-port . ,(match-string 2 data)))))
,@(unless (string-empty-p process)
`((pid . ,(save-match-data
(string-match (rx "pid=" (+ num)) process)
(string-to-number (substring (match-string 0 process) 4)))))))))
(seq-filter
(lambda (s) (not (string-empty-p s)))
(split-string
(shell-command-to-string "ss -tulpnH | grep LISTEN") "\n"))))
(defun my/kill-app-on-port (port &optional signal)
(let ((apps (my/get-apps-on-ports)))
(dolist (app apps)
(when (string-equal (cdr (assoc 'local-port app)) port)
(signal-process (cdr (assoc 'pid app)) (or signal 15))
(message "Sent %d to %d" (or signal 15) (cdr (assoc 'pid app)))))))
(use-package screenshot
:straight (:repo "tecosaur/screenshot" :host github :files ("screenshot.el") :commit "f8204e82dc0c1158c401735d36a143e6f6d24cf5")
:if (display-graphic-p)
:commands (screenshot)
:init
(my-leader-def "S" 'screenshot))
(my-leader-def "ah" 'proced)
(setq proced-auto-update-interval 1)
(add-hook 'proced-mode-hook (lambda ()
(visual-line-mode -1)
(setq-local truncate-lines t)
(proced-toggle-auto-update 1)))
(use-package guix
:straight t
:commands (guix)
:init
(my-leader-def "ag" 'guix))
(use-package pomm
:straight (:host github :repo "SqrtMinusOne/pomm.el" :files (:defaults "resources"))
;; :straight (:local-repo "~/Code/Emacs/pomm" :files (:defaults "resources"))
:commands (pomm)
:init
(my-leader-def "ap" #'pomm)
:config
(setq alert-default-style 'libnotify)
(add-hook 'pomm-on-tick-hook 'pomm-update-mode-line-string)
(add-hook 'pomm-on-status-changed-hook 'pomm-update-mode-line-string))
(setq calendar-date-style 'iso) ;; YYYY/mm/dd
(setq calendar-week-start-day 1)
(setq calendar-time-display-form '(24-hours ":" minutes))
(setq calendar-latitude 59.9375)
(setq calendar-longitude 30.308611)
(defun my/elcord-mask-buffer-name (name)
(cond
((string-match-p (rx bos (? "CAPTURE-") (= 14 num) "-" (* not-newline) ".org" eos) name)
"<ORG-ROAM>")
((string-match-p (rx bos (+ num) "-" (+ num) "-" (+ num) ".org" eos) name)
"<ORG-JOURNAL>")
((string-match-p (rx bos "EXWM") name)
"<EXWM>")
(t name)))
(defun my/elcord-buffer-details-format-functions ()
(format "Editing %s" (my/elcord-mask-buffer-name (buffer-name))))
(defun my/elcord-update-presence-mask-advice (r)
(list (my/elcord-mask-buffer-name (nth 0 r)) (nth 1 r)))
(use-package elcord
:straight t
:if (and (or
(string= (system-name) "indigo")
(string= (system-name) "eminence"))
(not my/slow-ssh)
(not my/remote-server))
:config
(setq elcord-buffer-details-format-function #'my/elcord-buffer-details-format-functions)
(advice-add 'elcord--try-update-presence :filter-args #'my/elcord-update-presence-mask-advice)
(elcord-mode))
(use-package snow
:straight (:repo "alphapapa/snow.el" :host github)
:commands (snow))
(use-package power-mode
:straight (:host github :repo "elizagamedev/power-mode.el")
:disabled
:commands (power-mode))
(use-package redacted
:straight (:host github :repo "bkaestner/redacted.el"))
(use-package zone
:ensure nil
:config
(setq original-zone-programs (copy-sequence zone-programs)))
(defun my/zone-with-select ()
(interactive)
(ivy-read "Zone programs"
(cl-pairlis
(cl-mapcar 'symbol-name original-zone-programs)
original-zone-programs)
:action (lambda (elem)
(setq zone-programs (vector (cdr elem)))
(zone))))
(defun my/ytel-kill-url ()
(interactive)
(kill-new
(concat
"https://www.youtube.com/watch?v="
(ytel-video-id (ytel-get-current-video)))))