diff --git a/.emacs.d/init.el b/.emacs.d/init.el index 1672976..dabd221 100644 --- a/.emacs.d/init.el +++ b/.emacs.d/init.el @@ -16,9727 +16,262 @@ (straight-use-package 'use-package) (eval-when-compile (require 'use-package)) -(setq my/remote-server - (or (string= (getenv "IS_REMOTE") "true") - (string= (system-name) "dev-digital") - (string= (system-name) "viridian"))) +(defun my/get-env () + (or (getenv "EMACS_ENV") + (when (member (system-name) '("dev-digital" "viridian")) + "remote") + (when (string-match-p (rx (* nonl) "com.termux" (* nonl)) (getenv "HOME")) + "termux") + "normal")) -(setq my/is-termux (string-match-p (rx (* nonl) "com.termux" (* nonl)) (getenv "HOME"))) +(setq my/env (my/get-env)) + +(setq my/nested-emacs (and (getenv "IS_EMACS") t)) +(setenv "IS_EMACS" "true") (defun my/system-name () (or (getenv "ANDROID_NAME") (system-name))) -(setq my/nested-emacs (and (getenv "IS_EMACS") t)) -(setenv "IS_EMACS" "true") +(setq my/is-termux (equal (my/get-env) "termux")) +(setq my/remote-server (equal (my/get-env) "remote")) -(setq my/emacs-started nil) +(setq my/modules-dir (file-name-as-directory + (expand-file-name "modules" user-emacs-directory))) -(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 my/emacs-started t))) +(setq my/modules-prefix "sqrt-") -(setq use-package-verbose nil) +(defun my/modules--refresh-and-list () + (let (modules-list + headlines) + (org-element-map (org-element-parse-buffer) 'headline + (lambda (elem) (push elem headlines))) + (mapc + (lambda (elem) + (when-let ((module-name (org-element-property :MODULE_NAME elem))) + (save-excursion + (goto-char (org-element-property :begin elem)) + (org-set-property + "header-args:emacs-lisp" + (concat ":tangle " my/modules-dir my/modules-prefix module-name ".el" + " :comments links")) + (push module-name modules-list)))) + headlines) + (seq-uniq modules-list))) -(setq use-package-compute-statistics 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)))) - -(defun my/get-ram-usage-async (callback) - (let* ((temp-buffer (generate-new-buffer "*ps*")) - (proc (start-process "ps" temp-buffer "ps" - "-p" (number-to-string (emacs-pid)) "-o" "rss"))) - (set-process-sentinel - proc - (lambda (process _msg) - (when (eq (process-status process) 'exit) - (let* ((output (with-current-buffer temp-buffer - (buffer-string))) - (usage (string-to-number (nth 1 (split-string output "\n"))))) - (ignore-errors - (funcall callback usage))) - (kill-buffer temp-buffer)))))) - -(defun my/ram-usage () - (interactive) - (my/get-ram-usage-async - (lambda (data) - (message "%f Gb" (/ (float data) 1024 1024))))) - -(use-package micromamba - :straight t - :if (executable-find "micromamba") - :config - (micromamba-activate "general")) - -(setq custom-file (concat user-emacs-directory "custom.el")) -(load custom-file 'noerror) - -(setq auth-source-debug nil) -(setq auth-sources '("~/.authinfo.gpg")) - -(let ((private-file (expand-file-name "private.el" user-emacs-directory))) - (when (file-exists-p private-file) - (load-file private-file))) - -(use-package no-littering - :straight t) - -(defun my/run-in-background (command) - (let ((command-parts (split-string command "[ ]+"))) - (apply #'call-process `(,(car command-parts) nil 0 nil ,@(cdr command-parts))))) - -(defun my/quit-window-and-buffer () - (interactive) - (quit-window t)) - -(setq confirm-kill-emacs 'y-or-n-p) - -(setq initial-major-mode 'fundamental-mode) -(setq initial-scratch-message "Hello there <3\n\n") - -(use-package general - :straight t - :config - (general-evil-setup)) - -(use-package which-key - :config - (setq which-key-idle-delay 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 buffer) - (dolist (key (which-key--get-bindings (kbd prefix))) - (with-current-buffer buffer - (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)) - buffer)))) - -(defun my/dump-bindings (prefix) - "Dump keybindings starting with PREFIX in a tree-like form." - (interactive "sPrefix: ") - (let ((buffer (get-buffer-create "bindings"))) - (with-current-buffer buffer - (erase-buffer)) - (my/dump-bindings-recursive prefix 0 buffer) - (with-current-buffer buffer - (goto-char (point-min)) - (setq-local buffer-read-only t)) - (switch-to-buffer-other-window buffer))) - -(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) - (when (fboundp #'undo-tree-undo) - (evil-set-undo-system 'undo-tree)) - (when (fboundp #'general-define-key) - (general-define-key - :states '(motion)))) - -(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 - :disabled - :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)) +(defun my/modules--read-table () (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 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 forge deadgrep vc-annonate telega doc-view gnus outline))) - -(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) - "" 'beginning-of-line - "" '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) - -(general-def :states '(insert) - " 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-" 'evil-window-right - "C-" 'evil-window-left - "C-" 'evil-window-up - "C-" '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) - -(defun my/lisp-interaction-buffer () - (interactive) - (let ((buf (get-buffer-create "*lisp-interaction*"))) - (with-current-buffer buf - (lisp-interaction-mode)) - (switch-to-buffer buf))) - -(my-leader-def - :infix "b" - "" '(:which-key "buffers") - "s" '(my/lisp-interaction-buffer - :which-key "*lisp-interaction*") - "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 - "b" #'persp-switch-to-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) - -(use-package xref - :straight (:type built-in)) - -(require 'hideshow) -(general-define-key - :keymaps '(hs-minor-mode-map outline-minor-mode-map outline-mode-map) - :states '(normal motion) - "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) - -(when (and my/is-termux (not (equal (my/system-name) "snow"))) - (define-key key-translation-map (kbd "`") (kbd "")) - (define-key key-translation-map (kbd "") (kbd "`"))) - -(when my/is-termux - (setq split-width-threshold 90)) - -(unless (or my/remote-server my/nested-emacs) - (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 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) - (evil-set-undo-system 'undo-tree) - (setq undo-tree-visualizer-diff t) - (setq undo-tree-visualizer-timestamps t) - (setq undo-tree-auto-save-history nil) - - (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 - :disabled - :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) - (my-leader-def - :keymaps 'yas-minor-mode-map - :infix "es" - "" '(:wk "yasnippet") - "n" #'yas-new-snippet - "s" #'yas-insert-snippet - "v" #'yas-visit-snippet-file)) - -(general-imap "M-TAB" 'company-yasnippet) - -(setq default-input-method "russian-computer") - -(defun my/toggle-input-method () - (interactive) - (if (derived-mode-p 'exwm-mode) - (my/run-in-background "xkb-switch -n") - (if (or - (not (executable-find "xkb-switch")) - (equal (string-trim - (shell-command-to-string "xkb-switch -p")) - "us")) - (toggle-input-method) - (my/run-in-background "xkb-switch -s us")))) - -(general-define-key - :keymaps 'global - "M-\\" #'my/toggle-input-method) - -(use-package smartparens - :straight t) - -(use-package visual-fill-column - :straight t - :commands (visual-fill-column-mode) - :config - ;; How did it get here? - ;; (add-hook 'visual-fill-column-mode-hook - ;; (lambda () (setq visual-fill-column-center-text t))) - ) - -(defvar my/default-accents - '((a . ä) - (o . ö) - (u . ü) - (s . ß) - (A . Ä) - (O . Ö) - (U . Ü) - (S . ẞ))) - -(defun my/accent (arg) - (interactive "P") - (require 'accent) - (message "%s" arg) - (let* ((after? (eq accent-position 'after)) - (char (if after? (char-after) (char-before))) - (curr (intern (string char))) - (default-diac (cdr (assoc curr my/default-accents)))) - (if (and default-diac (not arg)) - (progn - (delete-char (if after? 1 -1)) - (insert (format "%c" default-diac))) - (call-interactively #'accent-company)))) - -(use-package accent - :straight (:host github :repo "eliascotto/accent") - :init - (general-define-key - :states '(normal) - "gs" #'accent-company) - (general-define-key - :states '(normal insert) - "M-n" #'my/accent) - :commands (accent-menu) - :config - (general-define-key - :keymaps 'popup-menu-keymap - "C-j" #'popup-next - "C-k" #'popup-previous - "M-j" #'popup-next - "M-k" #'popup-previous) - (setq accent-custom '((a (ā)) - (A (Ā))))) - -(defun my/round-number-at-point (word signs) - (interactive - (list (or (when (region-active-p) - (buffer-substring-no-properties - (region-beginning) - (region-end))) - (thing-at-point 'number 'no-properties)) - (read-number "Decimal signs: " 2))) - (when (stringp word) - (setq word (string-to-number word))) - (let ((number (/ (float (round (* (expt 10 signs) word))) - (expt 10 signs)))) - (save-excursion - (replace-string-in-region - (number-to-string word) - (number-to-string number) - (line-beginning-position) - (line-end-position))))) - -(use-package projectile - :straight t - :config - (projectile-mode +1) - (setq projectile-project-search-path '("~/Code" "~/Documents")) - (general-define-key - :keymaps 'projectile-command-map - "b" #'consult-project-buffer)) - -(my-leader-def - "p" '(:keymap projectile-command-map :which-key "projectile")) - -(general-nmap "C-p" #'projectile-find-file) - -(use-package magit - :straight t - :commands (magit-status magit-file-dispatch) - :init - (my-leader-def - "m" 'magit - "M" 'magit-file-dispatch) - :config - (require 'forge) - (setq magit-blame-styles - '((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 - :config - (global-git-gutter-mode +1)) - -(use-package git-timemachine - :straight t - :commands (git-timemachine)) - -(use-package difftastic - :straight t - :commands (difftastic-magit-diff - difftastic-magit-show - difftastic-files - difftastic-buffers) - :init - (with-eval-after-load 'magit-diff - (transient-append-suffix 'magit-diff '(-1 -1) - [("D" "Difftastic diff (dwim)" difftastic-magit-diff) - ("S" "Difftastic show" difftastic-magit-show)]) - (general-define-key - :keymaps 'magit-blame-read-only-mode-map - :states 'normal - "D" #'difftastic-magit-show - "S" #'difftastic-magit-show)) - :config - (setq difftastic-executable (executable-find "difft")) - (general-define-key - :keymaps 'difftastic-mode-map - :states '(normal) - "gr" #'difftastic-rerun - "q" #'kill-buffer-and-window)) - -(defun my/difftastic-pop-at-bottom (buffer-or-name _requested-width) - (let ((window (split-window-below))) - (select-window window) - (evil-move-window 'below)) - (set-window-buffer (selected-window) buffer-or-name)) - -(setq difftastic-display-buffer-function #'my/difftastic-pop-at-bottom) - -(setq difftastic-requested-window-width-function - (lambda () (- (frame-width) 4))) - -(use-package forge - :after magit - :straight t - :config - (add-to-list 'forge-alist '("gitlab.etu.ru" - "gitlab.etu.ru/api/v4" - "gitlab.etu.ru" - forge-gitlab-repository))) - -(defun my/password-store-get-field (entry field) - (if-let (field (password-store-get-field entry field)) - field - (my/password-store-get-field entry field))) - -(defun my/ghub--token (host username package &optional nocreate forge) - (cond ((and (or (equal host "gitlab.etu.ru/api/v4") - (equal host "gitlab.etu.ru/api")) - (equal username "pvkorytov")) - (my/password-store-get-field - "Job/Digital/Infrastructure/gitlab.etu.ru" - (format "%s-token" package))) - (t (error "Don't know token: %s %s %s" host username package)))) - -(with-eval-after-load 'ghub - (advice-add #'ghub--token :override #'my/ghub--token)) - -(use-package code-review - :straight (:host github :repo "phelrine/code-review" :branch "fix/closql-update") - :after forge - :config - (setq code-review-auth-login-marker 'forge) - (setq code-review-gitlab-base-url "gitlab.etu.ru") - (setq code-review-gitlab-host "gitlab.etu.ru/api") - (setq code-review-gitlab-graphql-host "gitlab.etu.ru/api") - (general-define-key - :states '(normal visual) - :keymaps '(code-review-mode-map) - "RET" #'code-review-comment-add-or-edit - "gr" #'code-review-reload - "r" #'code-review-transient-api - "s" #'code-review-comment-code-suggestion - "d" #'code-review-submit-single-diff-comment-at-point - "TAB" #'magit-section-toggle) - (general-define-key - :states '(normal) - :keymaps '(forge-topic-mode-map) - "M-RET" #'code-review-forge-pr-at-point)) - -(defun my/code-review-comment-quit () - "Quit the comment window." - (interactive) - (magit-mode-quit-window t) - (with-current-buffer (get-buffer code-review-buffer-name) - (goto-char code-review-comment-cursor-pos) - (code-review-comment-reset-global-vars))) - -(with-eval-after-load 'code-review - (advice-add #'code-review-comment-quit :override #'my/code-review-comment-quit)) - -(use-package editorconfig - :straight t - :config - (add-to-list 'editorconfig-indentation-alist - '(emmet-mode emmet-indentation)) - (editorconfig-mode)) - -(recentf-mode 1) - -(save-place-mode nil) - -(defun my/deadgrep-fix-buffer-advice (fun &rest args) - (let ((buf (apply fun args))) - (with-current-buffer buf - (toggle-truncate-lines 1)) - buf)) - -(use-package deadgrep - :straight t - :commands (deadgrep) - :config - (advice-add #'deadgrep--buffer :around #'my/deadgrep-fix-buffer-advice)) - -(defun my/register-clear (register) - (interactive (list (register-read-with-preview "Clear register: "))) - (setq register-alist (delq (assoc register register-alist) register-alist))) - -(setq register-preview-delay which-key-idle-delay) - -(my-leader-def - :infix "g" - "" '(:wk "registers & marks") - "y" #'copy-to-register - "p" #'insert-register - "o" #'point-to-register - "c" #'my/register-clear - "r" #'jump-to-register - "R" #'consult-register - "w" #'window-configuration-to-register) - -(defun my/push-mark-no-activate () - "Pushes `point' to `mark-ring' and does not activate the region - Equivalent to \\[set-mark-command] when \\[transient-mark-mode] is disabled" - (interactive) - (push-mark (point) t nil) - (message "Pushed mark to ring")) - -(defun my/mark-ring-clear () - (interactive) - (setq mark-ring nil)) - -(my-leader-def - :infix "g" - "G" #'consult-global-mark - "g" #'consult-mark - "C" #'my/mark-ring-clear - "m" #'my/push-mark-no-activate) - -(general-define-key - :keymaps 'global - "C-SPC" #'my/push-mark-no-activate) - -(use-package avy - :straight t - :config - (setq avy-timeout-seconds 0.5) - (setq avy-ignored-modes - '(image-mode doc-view-mode pdf-view-mode exwm-mode)) - (general-define-key - :states '(normal motion) - "-" #'avy-goto-char-timer)) - -(defun avy-action-embark (pt) - (unwind-protect - (save-excursion - (goto-char pt) - (embark-act)) - (select-window - (cdr (ring-ref avy-ring 0)))) - t) - -(with-eval-after-load 'avy - (setf (alist-get ?. avy-dispatch-alist) 'avy-action-embark)) - -(use-package ace-link - :straight t - :commands (ace-link-info ace-link-help ace-link-woman ace-link-eww)) - -(use-package vertico - :straight t - :config - (setq enable-recursive-minibuffers t) - (general-define-key - :keymaps '(vertico-map) - "M-j" #'vertico-next - "M-k" #'vertico-previous - "TAB" #'minibuffer-complete) - (vertico-mode)) - -(defun crm-indicator (args) - (cons (format "[CRM%s] %s" - (replace-regexp-in-string - "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" "" - crm-separator) - (car args)) - (cdr args))) -(with-eval-after-load 'crm - (advice-add #'completing-read-multiple :filter-args #'crm-indicator)) - -(use-package savehist - :init - (savehist-mode)) - -(use-package vertico-directory - :after (vertico) - :config - (general-define-key - :keymaps '(vertico-map) - "RET" #'vertico-directory-enter - "DEL" #'vertico-directory-delete-char) - (add-hook 'rfn-eshadow-update-overlay-hook #'vertico-directory-tidy)) - -(use-package vertico-grid - :after (vertico)) - -(defun my/sort-directories-first (files) - (setq files (vertico-sort-alpha files)) - (nconc (seq-filter (lambda (x) (string-suffix-p "/" x)) files) - (seq-remove (lambda (x) (string-suffix-p "/" x)) files))) - -(use-package vertico-multiform - :after vertico - :config - (vertico-multiform-mode) - (general-define-key - :keymap 'vertico-multiform-map - "M-b" #'vertico-multiform-buffer - "M-g" #'vertico-multiform-grid) - (setq vertico-multiform-categories - '((file (vertico-sort-function . my/sort-directories-first)) - (password-store-pass grid))) - (setq vertico-multiform-commands - '((eshell-atuin-history (vertico-sort-function . nil)) - (my/index-nav (vertico-sort-function . nil)) - (org-ql-view (vertico-sort-function . nil)) - (my/consult-line (vertico-sort-function . nil)) - (telega-msg-add-reaction grid)))) - -(use-package vertico-quick - :after vertico - :config - (general-define-key - :keymaps '(vertico-map) - "M-q" #'vertico-quick-insert - "C-q" #'vertico-quick-exit)) - -(use-package orderless - :straight t - :config - (setq completion-styles '(orderless basic)) - (setq completion-category-defaults nil) - (setq completion-category-overrides - '((file (styles partial-completion)))) - (setq orderless-matching-styles - '(orderless-literal orderless-initialism orderless-regexp))) - -(defun company-completion-styles (capf-fn &rest args) - (let ((completion-styles '(basic partial-completion))) - (apply capf-fn args))) - -(with-eval-after-load 'company - (advice-add 'company-capf :around #'company-completion-styles)) - -(use-package consult - :straight t - :config - (setq consult-preview-excluded-files - `("\\`/[^/|:]+:" - ,(rx "html" eos)))) - -(use-package marginalia - :straight t - :config - (marginalia-mode) - (push '(projectile-find-file . file) - marginalia-command-categories)) - -(use-package embark - :straight t - :commands (embark-act embark-dwim embark-bindings) - :init - (general-define-key - "M-e" #'embark-act)) - -(use-package embark-consult - :straight t - :after (embark) - :config - (add-hook 'embark-collect-mode #'consult-preview-at-point-mode)) - -(defun embark-which-key-indicator () - "An embark indicator that displays keymaps using which-key. -The which-key help message will show the type and value of the -current target followed by an ellipsis if there are further -targets." - (lambda (&optional keymap targets prefix) - (if (null keymap) - (which-key--hide-popup-ignore-command) - (which-key--show-keymap - (if (eq (plist-get (car targets) :type) 'embark-become) - "Become" - (format "Act on %s '%s'%s" - (plist-get (car targets) :type) - (embark--truncate-target (plist-get (car targets) :target)) - (if (cdr targets) "…" ""))) - (if prefix - (pcase (lookup-key keymap prefix 'accept-default) - ((and (pred keymapp) km) km) - (_ (key-binding prefix 'accept-default))) - keymap) - nil nil t (lambda (binding) - (not (string-suffix-p "-argument" (cdr binding)))))))) - -(defun embark-hide-which-key-indicator (fn &rest args) - "Hide the which-key indicator immediately when using the completing-read prompter." - (which-key--hide-popup-ignore-command) - (let ((embark-indicators - (remq #'embark-which-key-indicator embark-indicators))) - (apply fn args))) - -(with-eval-after-load 'embark - (advice-add #'embark-completing-read-prompter - :around #'embark-hide-which-key-indicator) - (setq embark-indicators (delq #'embark-mixed-indicator embark-indicators)) - (push #'embark-which-key-indicator embark-indicators)) - -(my-leader-def - :infix "f" - "" '(:which-key "various completions")' - "b" #'persp-switch-to-buffer* - "e" 'micromamba-activate - "f" 'project-find-file - "c" 'consult-yank-pop - "a" 'consult-ripgrep - "d" 'deadgrep) - -(general-define-key - :states '(insert normal) - "C-y" 'consult-yank-pop) - -(defun my/consult-line () - (interactive) - (if current-prefix-arg - (call-interactively #'consult-line-multi) - (consult-line nil t))) - -;; (my-leader-def "SPC SPC" 'ivy-resume) -(my-leader-def "s" 'my/consult-line) - -(use-package company - :straight t - :config - (global-company-mode) - (setq company-idle-delay 0.2) - (setq company-dabbrev-downcase nil) - (setq company-show-numbers t)) - -(general-imap "C-SPC" 'company-complete) - -(use-package company-box - :straight t - :if (display-graphic-p) - :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")) - -(my-leader-def - :infix "h" - "" '(:which-key "help") - "h" '(:keymap help-map :which-key "help-map") - "f" 'helpful-function - "k" 'helpful-key - "v" 'helpful-variable - "o" 'helpful-symbol - "i" 'info) - -(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/remote-server)) - :config - (setq wakatime-ignore-exit-codes '(0 1 102 112)) - (advice-add 'wakatime-init :after - (lambda () - (setq wakatime-cli-path (or - (executable-find "wakatime-cli") - (expand-file-name "~/bin/wakatime-cli"))))) - (when (file-exists-p "~/.wakatime.cfg") - (setq wakatime-api-key - (string-trim - (shell-command-to-string "awk '/api-key/{print $NF}' ~/.wakatime.cfg")))) - ;; (setq wakatime-cli-path (executable-find "wakatime")) - (global-wakatime-mode)) - -(use-package request - :straight t - :defer 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)) - -(when my/is-termux - (menu-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 use-dialog-box nil) - -(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 olivetti - :straight t - :if (display-graphic-p) - :commands (olivetti-mode) - :config - (setq-default olivetti-body-width 86)) - -(use-package keycast - :straight t - :init - (define-minor-mode keycast-mode - "Keycast mode" - :global t - (if keycast-mode - (progn - (add-to-list 'global-mode-string '("" keycast-mode-line " ")) - (add-hook 'pre-command-hook 'keycast--update t) ) - (remove-hook 'pre-command-hook 'keycast--update) - (setq global-mode-string (delete '("" keycast-mode-line " ") global-mode-string)))) - :commands (keycast--update)) - -(use-package doom-themes - :straight t - ;; Not deferring becuase I want `doom-themes-visual-bell-config' - :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)) - -(use-package modus-themes - :straight t) - -(use-package ef-themes - :straight t - :config - (setq ef-duo-light-palette-overrides - '((constant green)))) - -(use-package ct - :straight t) - -(defun my/doom-p () - (seq-find (lambda (x) (string-match-p (rx bos "doom") (symbol-name x))) - custom-enabled-themes)) - -(defun my/modus-p () - (seq-find (lambda (x) (string-match-p (rx bos "modus") (symbol-name x))) - custom-enabled-themes)) - -(defun my/ef-p () - (seq-find (lambda (x) (string-match-p (rx bos "ef") (symbol-name x))) - custom-enabled-themes)) - -(defun my/light-p () - (ct-light-p (my/color-value 'bg))) - -(defun my/dark-p () - (not (my/light-p))) - -(defconst my/theme-override - '((doom-palenight - (red . "#f07178")))) - -(defvar my/alpha-for-light 7) - -(defun my/doom-color (color) - (when (doom-color 'bg) - (let ((override (alist-get (my/doom-p) my/theme-override)) - (color-name (symbol-name color)) - (is-light (ct-light-p (doom-color 'bg)))) - (or - (alist-get color override) - (cond - ((eq 'black color) - (if is-light (doom-color 'fg) (doom-color 'bg))) - ((eq 'white color) - (if is-light (doom-color 'bg) (doom-color 'fg))) - ((eq 'border color) - (if is-light (doom-color 'base0) (doom-color 'base8))) - ((string-match-p (rx bos "light-") color-name) - (ct-edit-hsl-l-inc (my/doom-color (intern (substring color-name 6))) - my/alpha-for-light)) - ((string-match-p (rx bos "dark-") color-name) - (or (doom-color color) - (ct-edit-hsl-l-dec (my/doom-color (intern (substring color-name 5))) - my/alpha-for-light))) - (t (doom-color color))))))) - -(defun my/modus-get-base (color) - (let ((base-value (string-to-number (substring (symbol-name color) 4 5))) - (base-start (cadr (assoc 'bg-main (modus-themes--current-theme-palette)))) - (base-end (cadr (assoc 'fg-dim (modus-themes--current-theme-palette))))) - (nth base-value (ct-gradient 9 base-start base-end t)))) - -(defun my/prot-color (color palette) - (let ((is-light (ct-light-p (cadr (assoc 'bg-main palette))))) - (cond - ((member color '(black white light-black light-white)) - (let ((bg-main (cadr (assoc 'bg-main palette))) - (fg-main (cadr (assoc 'fg-main palette)))) - (pcase color - ('black (if is-light fg-main bg-main)) - ('white (if is-light bg-main fg-main)) - ('light-black (ct-edit-hsl-l-inc - (if is-light fg-main bg-main) - 15)) - ('light-white (ct-edit-hsl-l-inc - (if is-light bg-main fg-main) - 15))))) - ((or (eq color 'bg)) - (cadr (assoc 'bg-main palette))) - ((or (eq color 'fg)) - (cadr (assoc 'fg-main palette))) - ((eq color 'bg-alt) - (cadr (assoc 'bg-dim palette))) - ((eq color 'violet) - (cadr (assoc 'magenta-cooler palette))) - ((string-match-p (rx bos "base" digit) (symbol-name color)) - (my/modus-get-base color)) - ((string-match-p (rx bos "dark-") (symbol-name color)) - (cadr (assoc (intern (format "%s-cooler" (substring (symbol-name color) 5))) - palette))) - ((eq color 'grey) - (my/modus-get-base 'base5)) - ((string-match-p (rx bos "light-") (symbol-name color)) - (or - (cadr (assoc (intern (format "%s-intense" (substring (symbol-name color) 6))) palette)) - (cadr (assoc (intern (format "bg-%s-intense" (substring (symbol-name color) 6))) palette)))) - (t (cadr (assoc color palette)))))) - -(defun my/modus-color (color) - (my/prot-color color (modus-themes--current-theme-palette))) - -(defun my/ef-color (color) - (my/prot-color color (ef-themes--current-theme-palette))) - -(defconst my/test-colors-list - '(black red green yellow blue magenta cyan white light-black - dark-red dark-green dark-yellow dark-blue dark-magenta dark-cyan - light-red light-green light-yellow light-blue light-magenta - light-cyan light-white bg bg-alt fg fg-alt violet grey base0 base1 - base2 base3 base4 base5 base6 base7 base8 border)) - -(defun my/test-colors () - (interactive) - (let ((buf (generate-new-buffer "*colors-test*"))) - (with-current-buffer buf - (insert (format "%-20s %-10s %-10s %-10s" "Color" "Doom" "Modus" "Ef") "\n") - (cl-loop for color in my/test-colors-list - do (insert - (format "%-20s %-10s %-10s %-10s\n" - (prin1-to-string color) - (my/doom-color color) - (my/modus-color color) - (my/ef-color color)))) - (special-mode) - (rainbow-mode)) - (switch-to-buffer buf))) - -(defun my/color-value (color) - (cond - ((stringp color) (my/color-value (intern color))) - ((eq color 'bg-other) - (or (my/color-value 'bg-dim) - (let ((color (my/color-value 'bg))) - (if (ct-light-p color) - (ct-edit-hsl-l-dec color 2) - (ct-edit-hsl-l-dec color 3))))) - ((eq color 'modeline) - (or - (my/color-value 'bg-mode-line-active) - (my/color-value 'bg-mode-line) - (if (my/light-p) - (ct-edit-hsl-l-dec (my/color-value 'bg-alt) 10) - (ct-edit-hsl-l-inc (my/color-value 'bg-alt) 15)))) - ((my/doom-p) (my/doom-color color)) - ((my/modus-p) (my/modus-color color)) - ((my/ef-p) (my/ef-color color)))) - -(deftheme my-theme-1) - -(defvar my/my-theme-update-color-params nil) - -(defmacro my/use-colors (&rest data) - `(progn - ,@(cl-loop for i in data collect - `(setf (alist-get ',(car i) my/my-theme-update-color-params) - (list ,@(cl-loop for (key value) on (cdr i) by #'cddr - append `(,key ',value))))) - (when (and (or (my/doom-p) (my/modus-p)) my/emacs-started) - (my/update-my-theme)))) - -(defun my/update-my-theme (&rest _) - (interactive) - (cl-loop for (face . values) in my/my-theme-update-color-params - do (custom-theme-set-faces - 'my-theme-1 - `(,face ((t ,@(cl-loop for (key value) on values by #'cddr - collect key - collect (eval value))))))) - (enable-theme 'my-theme-1)) - -(unless my/is-termux - (advice-add 'load-theme :after #'my/update-my-theme) - (add-hook 'emacs-startup-hook #'my/update-my-theme)) - -(my/use-colors - (tab-bar-tab :background (my/color-value 'bg) - :foreground (my/color-value 'yellow) - :underline (my/color-value 'yellow)) - (tab-bar :background 'unspecified :foreground 'unspecified) - (magit-section-secondary-heading :foreground (my/color-value 'blue) - :weight 'bold)) - -(defun my/switch-theme (theme) - (interactive - (list (intern (completing-read "Load custom theme: " - (mapcar #'symbol-name - (custom-available-themes)))))) - (cl-loop for enabled-theme in custom-enabled-themes - if (not (or (eq enabled-theme 'my-theme-1) - (eq enabled-theme theme))) - do (disable-theme enabled-theme)) - (load-theme theme t) - (when current-prefix-arg - (my/regenerate-desktop))) - -(if my/is-termux - (progn - (my/switch-theme 'modus-operandi-tinted)) - (my/switch-theme 'ef-duo-light)) - -(with-eval-after-load 'transient - (my/use-colors - (transient-key-exit :foreground (my/color-value 'dark-red)) - (transient-key-noop :foreground (my/color-value 'grey)) - (transient-key-return :foreground (my/color-value 'yellow)) - (transient-key-stay :foreground (my/color-value 'green)))) - -(use-package auto-dim-other-buffers - :straight t - :if (display-graphic-p) - :config - (auto-dim-other-buffers-mode t) - (my/use-colors - (auto-dim-other-buffers-face - :background (my/color-value 'bg-other)))) - -(with-eval-after-load 'ansi-color - (my/use-colors - (ansi-color-black - :foreground (my/color-value 'base2) :background (my/color-value 'base0)) - (ansi-color-red - :foreground (my/color-value 'red) :background (my/color-value 'red)) - (ansi-color-green - :foreground (my/color-value 'green) :background (my/color-value 'green)) - (ansi-color-yellow - :foreground (my/color-value 'yellow) :background (my/color-value 'yellow)) - (ansi-color-blue - :foreground (my/color-value 'dark-blue) :background (my/color-value 'dark-blue)) - (ansi-color-magenta - :foreground (my/color-value 'violet) :background (my/color-value 'violet)) - (ansi-color-cyan - :foreground (my/color-value 'dark-cyan) :background (my/color-value 'dark-cyan)) - (ansi-color-white - :foreground (my/color-value 'base8) :background (my/color-value 'base8)) - (ansi-color-bright-black - :foreground (my/color-value 'base5) :background (my/color-value 'base5)) - (ansi-color-bright-red - :foreground (my/color-value 'orange) :background (my/color-value 'orange)) - (ansi-color-bright-green - :foreground (my/color-value 'teal) :background (my/color-value 'teal)) - (ansi-color-bright-yellow - :foreground (my/color-value 'yellow) :background (my/color-value 'yellow)) - (ansi-color-bright-blue - :foreground (my/color-value 'blue) :background (my/color-value 'blue)) - (ansi-color-bright-magenta - :foreground (my/color-value 'magenta) :background (my/color-value 'magenta)) - (ansi-color-bright-cyan - :foreground (my/color-value 'cyan) :background (my/color-value 'cyan)) - (ansi-color-bright-white - :foreground (my/color-value 'fg) :background (my/color-value 'fg)))) - -(when (display-graphic-p) - (if (x-list-fonts "JetBrainsMono Nerd Font") - (let ((font "-JB -JetBrainsMono Nerd Font-medium-normal-normal-*-17-*-*-*-m-0-iso10646-1")) - (set-frame-font font nil t) - (add-to-list 'default-frame-alist `(font . ,font))) - (message "Install JetBrainsMono Nerd Font!"))) - -(when (display-graphic-p) - (set-face-attribute 'variable-pitch nil :family "Cantarell" :height 1.0) - (set-face-attribute - 'italic nil - :family "JetBrainsMono Nerd Font" - :weight 'regular - :slant 'italic)) - -(use-package ligature - :straight (:host github :repo "mickeynp/ligature.el") - :if (display-graphic-p) - :config - (ligature-set-ligatures - '( - typescript-mode - typescript-ts-mode - js2-mode - javascript-ts-mode - vue-mode - svelte-mode - scss-mode - php-mode - python-mode - python-ts-mode - js-mode - markdown-mode - clojure-mode - go-mode - sh-mode - haskell-mode - web-mode) - '("--" "---" "==" "===" "!=" "!==" "=!=" "=:=" "=/=" "<=" - ">=" "&&" "&&&" "&=" "++" "+++" "***" ";;" "!!" "??" - "?:" "?." "?=" "<:" ":<" ":>" ">:" "<>" "<<<" ">>>" - "<<" ">>" "||" "-|" "_|_" "|-" "||-" "|=" "||=" "##" - "###" "####" "#{" "#[" "]#" "#(" "#?" "#_" "#_(" "#:" - "#!" "#=" "^=" "<$>" "<$" "$>" "<+>" "<+" "+>" "<*>" - "<*" "*>" "" "/>" "" "->" "->>" - "<<-" "<-" "<=<" "=<<" "<<=" "<==" "<=>" "<==>" "==>" "=>" - "=>>" ">=>" ">>=" ">>-" ">-" ">--" "-<" "-<<" ">->" "<-<" - "<-|" "<=|" "|=>" "|->" "<->" "<~~" "<~" "<~>" "~~" "~~>" - "~>" "~-" "-~" "~@" "[||]" "|]" "[|" "|}" "{|" "[<" - ">]" "|>" "<|" "||>" "<||" "|||>" "<|||" "<|>" "..." ".." - ".=" ".-" "..<" ".?" "::" ":::" ":=" "::=" ":?" ":?>" - "//" "///" "/*" "*/" "/=" "//=" "/==" "@_" "__")) - (global-ligature-mode t)) - -(use-package nerd-icons - :straight t) - -(use-package indent-bars - :straight (:host github :repo "jdtsmith/indent-bars") - :if (not (or my/remote-server)) - :hook ((prog-mode . indent-bars-mode) - (LaTeX-mode . indent-bars-mode)) - :config - (require 'indent-bars-ts) - (setopt indent-bars-no-descend-lists t - indent-bars-treesit-support t - indent-bars-width-frac 0.3)) - -(use-package rainbow-delimiters - :straight t - :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) - (setq doom-modeline-display-misc-in-all-mode-lines nil) - (when my/is-termux - (setopt doom-modeline-icon nil)) - :config - (setq doom-modeline-minor-modes nil) - (setq doom-modeline-irc nil) - (setq doom-modeline-buffer-state-icon nil) - (doom-modeline-mode 1)) - -(defun my/tab-bar-mode-line--format () - (unless (derived-mode-p 'company-box-mode) - (cl-letf (((symbol-function 'window-pixel-width) - 'frame-pixel-width) - ((symbol-function 'window-margins) - (lambda (&rest _) - (list nil)))) - (let ((doom-modeline-window-width-limit nil) - (doom-modeline--limited-width-p nil)) - (format-mode-line - '("%e" - (:eval - (doom-modeline-format--main)))))))) - -(defun my/hide-mode-line-if-only-window () - (let* ((windows (window-list)) - (hide-mode-line-p (length= windows 1))) - (dolist (win windows) - (with-current-buffer (window-buffer win) - (unless (eq hide-mode-line-p hide-mode-line-mode) - (hide-mode-line-mode - (if hide-mode-line-p +1 -1))))))) - -(define-minor-mode my/tab-bar-mode-line-mode - "Use tab-bar as mode line mode." - :global t - (if my/tab-bar-mode-line-mode - (progn - (tab-bar-mode +1) - (setq tab-bar-format '(my/tab-bar-mode-line--format)) - (set-face-attribute 'tab-bar nil :inherit 'mode-line) - (add-hook 'window-configuration-change-hook #'my/hide-mode-line-if-only-window) - - (dolist (buf (buffer-list)) - (with-current-buffer buf - (doom-modeline-set-modeline 'minimal))) - (doom-modeline-set-modeline 'minimal 'default) - - (dolist (frame (frame-list)) - (with-selected-frame frame - (my/hide-mode-line-if-only-window)) - (when-let (cb-frame (company-box--get-frame frame)) - (set-frame-parameter cb-frame 'tab-bar-lines 0))) - (setenv "POLYBAR_BOTTOM" "false") - (when (fboundp #'my/exwm-run-polybar) - (my/exwm-run-polybar))) - (tab-bar-mode -1) - (setq tab-bar-format - '(tab-bar-format-history tab-bar-format-tabs tab-bar-separator tab-bar-format-add-tab)) - (set-face-attribute 'tab-bar nil :inherit 'default) - (remove-hook 'window-configuration-change-hook #'my/hide-mode-line-if-only-window) - (global-hide-mode-line-mode -1) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (doom-modeline-set-modeline 'main))) - (doom-modeline-set-modeline 'main 'default) - (setenv "POLYBAR_BOTTOM" "true") - (when (fboundp #'my/exwm-run-polybar) - (my/exwm-run-polybar)))) - -(use-package perspective - :straight t - :init - ;; (setq persp-show-modestring 'header) - (setq persp-sort 'created) - (setq persp-suppress-no-prefix-key-warning t) - :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-switch-to-buffer - "x" 'persp-switch-to-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))) - -(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)) - -(setq my/perspective-assign-alist '()) - -(defvar my/perspective-assign-ignore nil - "If non-nil, ignore `my/perspective-assign'") - -(defun my/perspective-assign () - (when-let* ((_ (not my/perspective-assign-ignore)) - (rule (alist-get major-mode my/perspective-assign-alist))) - (let ((workspace-index (car rule)) - (persp-name (cadr rule)) - (buffer (current-buffer))) - (if (fboundp #'perspective-exwm-assign-window) - (progn - (perspective-exwm-assign-window - :workspace-index workspace-index - :persp-name persp-name) - (when workspace-index - (exwm-workspace-switch workspace-index)) - (when persp-name - (persp-switch persp-name))) - (with-perspective persp-name - (persp-set-buffer buffer)) - (persp-switch-to-buffer buffer))))) - -(defun my/perspective-assign-ignore-advice (fun &rest args) - (let ((my/perspective-assign-ignore t)) - (apply fun args))) - -(add-hook 'after-change-major-mode-hook #'my/perspective-assign) - -(defmacro my/persp-add-rule (&rest body) - (declare (indent 0)) - (unless (= (% (length body) 3) 0) - (error "Malformed body in my/persp-add-rule")) - (let (result) - (while body - (let ((major-mode (pop body)) - (workspace-index (pop body)) - (persp-name (pop body))) - (push - `(add-to-list 'my/perspective-assign-alist - '(,major-mode . (,workspace-index ,persp-name))) - result))) - `(progn - ,@result))) - -(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 treemacs - :straight t - :defer t - :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 - "q" #'treemacs-quit) - (general-define-key - :keymaps 'treemacs-mode-map - :states '(normal emacs) - "q" 'treemacs-quit)) - -(use-package treemacs-evil - :after (treemacs evil) - :straight t) - -(use-package lsp-mode - :straight t - :if (not (or 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) - (setq lsp-volar-take-over-mode 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 all-the-icons - :straight t) - -(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) - -(defun my/lsp--progress-status () - "Returns the status of the progress for the current workspaces." - (-let ((progress-status - (s-join - "|" - (-keep - (lambda (workspace) - (let ((tokens (lsp--workspace-work-done-tokens workspace))) - (unless (ht-empty? tokens) - (mapconcat - (-lambda ((&WorkDoneProgressBegin :message? :title :percentage?)) - (concat (if percentage? - (if (numberp percentage?) - (format "%.0f%%%% " percentage?) - (format "%s%%%% " percentage?)) - "") - (let ((msg (url-unhex-string (or message\? title)))) - (if (string-match-p "\\`file:///" msg) - (file-name-nondirectory msg))))) - (ht-values tokens) - "|")))) - (lsp-workspaces))))) - (unless (s-blank? progress-status) - (concat lsp-progress-prefix progress-status)))) - -(with-eval-after-load 'lsp-mode - (advice-add 'lsp--progress-status :override #'my/lsp--progress-status)) - -(setq my/lsp--vue-diagnostics-last-update (make-hash-table :test #'equal)) - -(defun my/lsp--on-diagnostics (fn workspace params) - (if (equal (gethash 'vue-semantic-server lsp-clients) - (lsp--workspace-client workspace)) - (progn - (let* ((is-empty (seq-empty-p (gethash "diagnostics" params))) - (uri (gethash "uri" params)) - (last-update (gethash uri my/lsp--vue-diagnostics-last-update)) - (current-update (time-convert nil #'integer))) - (unless is-empty - (puthash uri current-update my/lsp--vue-diagnostics-last-update)) - (when (or (not is-empty) - (not last-update) - (> (- current-update (or last-update 0)) 5)) - (funcall fn workspace params)))) - (funcall fn workspace params))) - -(with-eval-after-load 'lsp - (advice-add #'lsp--on-diagnostics :around #'my/lsp--on-diagnostics)) - -(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/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 treesit - :straight (:type built-in) - :if (featurep 'treesit) - :config - (setq treesit-language-source-alist - (mapcar - (lambda (item) - (let ((lang (nth 0 item)) - (url (nth 1 item)) - (rev (nth 2 item)) - (source-dir (nth 3 item))) - `(,lang ,url ,rev ,source-dir - ,(executable-find "gcc") ,(executable-find "c++")))) - '((bash "https://github.com/tree-sitter/tree-sitter-bash") - (cmake "https://github.com/uyha/tree-sitter-cmake") - (css "https://github.com/tree-sitter/tree-sitter-css") - (elisp "https://github.com/Wilfred/tree-sitter-elisp") - (go "https://github.com/tree-sitter/tree-sitter-go") - (html "https://github.com/tree-sitter/tree-sitter-html") - (javascript "https://github.com/tree-sitter/tree-sitter-javascript" "master" "src") - (json "https://github.com/tree-sitter/tree-sitter-json") - (make "https://github.com/alemuller/tree-sitter-make") - (markdown "https://github.com/ikatyang/tree-sitter-markdown") - (python "https://github.com/tree-sitter/tree-sitter-python") - (toml "https://github.com/tree-sitter/tree-sitter-toml") - (tsx "https://github.com/tree-sitter/tree-sitter-typescript" "master" "tsx/src") - (typescript "https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src") - (yaml "https://github.com/ikatyang/tree-sitter-yaml")))) - (setq treesit-font-lock-level 4) - (setq major-mode-remap-alist - '((typescript-mode . typescript-ts-mode) - (js-mode . javascript-ts-mode) - (python-mode . python-ts-mode) - (json-mode . json-ts-mode))) - (cl-loop for (old-mode . new-mode) in major-mode-remap-alist - do (my/set-smartparens-indent new-mode) - do (set (intern (concat (symbol-name new-mode) "-hook")) - (list - (eval `(lambda () - (run-hooks - ',(intern (concat (symbol-name old-mode) "-hook"))))))))) - -(use-package treesit-fold - :straight (treesit-fold :type git :host github :repo "emacs-tree-sitter/treesit-fold") - :commands (treesit-fold-mode)) - -(defun my/treesit-fold--get-nodes-to-fold () - (when-let* - ((node (ignore-errors (treesit-buffer-root-node))) - (patterns (seq-mapcat (lambda (fold-range) `((,(car fold-range)) @name)) - (alist-get major-mode treesit-fold-range-alist))) - (query (ignore-errors - (treesit-query-compile (treesit-node-language node) - patterns))) - (nodes-to-fold (treesit-query-capture node query)) - (mode-ranges (alist-get major-mode treesit-fold-range-alist)) - (nodes-to-fold - (cl-remove-if (lambda (node) - (treesit-fold--non-foldable-node-p (cdr node) mode-ranges)) - nodes-to-fold))) - nodes-to-fold)) - -(defun my/treesit-fold-hide-children () - (interactive) - (let* ((current-node (treesit-fold--foldable-node-at-pos)) - (all-nodes-to-fold (my/treesit-fold--get-nodes-to-fold)) - ;; Find foldable children of `current-node' - (target-nodes-to-fold - (seq-filter - (lambda (n) - (cl-block tree-iter - (while n - (setq n (treesit-node-parent n)) - (when (equal n current-node) - (cl-return-from tree-iter t))))) - (mapcar #'cdr all-nodes-to-fold)))) - (dolist (node target-nodes-to-fold) - (treesit-fold-close node)))) - -(defun my/evil-fold-hide-level () - (interactive) - (cond - (hs-minor-mode (hs-hide-level)) - (treesit-fold-mode (my/treesit-fold-hide-children)))) - -(with-eval-after-load 'treesit-fold - (general-define-key - :states '(normal) - "ze" #'my/evil-fold-hide-level) - (keymap-unset evil-motion-state-map "z e" t)) - -(use-package combobulate - :straight (:host github :repo "mickeynp/combobulate") - :commands (combobulate)) - -(use-package dap-mode - :straight t - :if (not (or my/remote-server my/is-termux)) - :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) - (require 'dap-php) - - (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-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 ( . ) 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/copilot-tab () - (interactive) - (or (copilot-accept-completion) - (when (my/should-run-emmet-p) (my/emmet-or-tab)) - (when (and (eq evil-state 'normal) - (or hs-minor-mode treesit-fold-mode outline-minor-mode)) - (evil-toggle-fold) - t) - (indent-for-tab-command))) - -(use-package copilot - :straight (:host github :repo "copilot-emacs/copilot.el") - :commands (copilot-mode) - :disabled t - :init - (add-hook 'emacs-startup-hook - (lambda () - (add-hook 'prog-mode-hook #'copilot-mode))) - :config - (push '(copilot) warning-suppress-types) - (setq copilot-node-executable "/home/pavel/.guix-extra-profiles/dev/dev/bin/node") - (general-define-key - :keymaps 'company-active-map - "" #'my/copilot-tab) - (general-define-key - :keymaps 'copilot-mode-map - "" #'my/copilot-tab - "M-j" #'copilot-accept-completion-by-line - "M-l" #'copilot-accept-completion-by-word)) - -(defun my/should-run-emmet-p () - (and (bound-and-true-p emmet-mode) - (or (and (derived-mode-p 'web-mode) - (member (web-mode-language-at-pos) '("html" "css"))) - (not (derived-mode-p 'web-mode))))) - -(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 - (defun my/emmet-or-tab (&optional arg) - (interactive) - (if (my/should-run-emmet-p) - (or (emmet-expand-line arg) - (emmet-go-to-edit-point 1) - (indent-for-tab-command arg)) - (indent-for-tab-command arg))) - (general-imap :keymaps 'emmet-mode-keymap - "TAB" 'my/emmet-or-tab - "" '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 - typescript-ts-mode-map - vue-mode-map - svelte-mode-map) - "rr" #'prettier-prettify)) - -(use-package typescript-mode - :straight t - :mode "\\.ts\\'" - :init - (add-hook 'typescript-mode-hook #'smartparens-mode) - (add-hook 'typescript-mode-hook #'rainbow-delimiters-mode) - (add-hook 'typescript-mode-hook #'treesit-fold-mode) - :config - (my/set-smartparens-indent 'typescript-mode)) - -(add-hook 'js-mode-hook #'smartparens-mode) -(add-hook 'js-mode-hook #'treesit-fold-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 - "d" #'jest-test-debug-run-at-point - "r" #'jest-test-run - "a" #'jest-test-run-all-tests) - (defmacro my/jest-test-with-debug-flags (form) - "Execute FORM with debugger flags set." - (declare (indent 0)) - `(let ((jest-test-options (seq-concatenate 'list jest-test-options (list "--runInBand") )) - (jest-test-npx-options (seq-concatenate 'list jest-test-npx-options (list "--node-options" "--inspect-brk")))) - ,form)) - (defun my/jest-test-debug () - "Run the test with an inline debugger attached." - (interactive) - (my/jest-test-with-debug-flags - (jest-test-run))) - (defun my/jest-test-debug-rerun-test () - "Run the test with an inline debugger attached." - (interactive) - (my/jest-test-with-debug-flags - (jest-test-rerun-test))) - (defun my/jest-test-debug-run-at-point () - "Run the test with an inline debugger attached." - (interactive) - (my/jest-test-with-debug-flags - (jest-test-run-at-point))) - (advice-add #'jest-test-debug :override #'my/jest-test-debug) - (advice-add #'jest-test-debug-rerun-test :override #'my/jest-test-debug-rerun-test) - (advice-add #'jest-test-debug-run-at-point - :override #'my/jest-test-debug-run-at-point)) - -(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-unit-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) - (with-eval-after-load 'editorconfig - (push - 'standard-indent - (alist-get 'web-mode editorconfig-indentation-alist))) - (setq web-mode-auto-pairs nil)) - -(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-file-name))) - my/web-mode-lsp-extensions) - (lsp-deferred))) - -(add-hook 'web-mode-hook #'my/web-mode-lsp) - -(defun my/web-mode-vue-setup (&rest _) - (let ((filename (buffer-file-name))) - (when (and (stringp filename) - (string-match-p (rx ".vue" eos) filename)) - (setq-local web-mode-script-padding 0) - (setq-local web-mode-style-padding 0) - (setq-local create-lockfiles nil) - (setq-local web-mode-enable-auto-pairing nil)))) - -(add-hook 'web-mode-hook 'my/web-mode-vue-setup) -(add-hook 'editorconfig-after-apply-functions '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\\'" - :config - (add-hook 'php-mode-hook #'smartparens-mode) - (add-hook 'php-mode-hook #'lsp) - (my/set-smartparens-indent 'php-mode)) - -(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)) - -(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 + (goto-char (point-min)) + (re-search-forward (rx bol "#+NAME: modules-settings")) + (forward-line) + (let* ((table (org-element-at-point)) + (table-contents + (mapcar + (lambda (elem) + (if (listp elem) + (mapcar #'substring-no-properties elem) + elem)) + (org-table-to-lisp))) + (envs-list (cdar table-contents))) (mapcar (lambda (elem) - `(,(car elem) - ,(cdr elem) - ,(progn - (string-match "[a-z]+" (cdr elem)) - (match-string 0 (cdr elem))))) - my/latex-section-snippets)) + (cons (car elem) + (cl-loop for sign in (cdr elem) + for env in envs-list + collect (cons env (not (string-empty-p sign)))))) + (cddr table-contents))))) -(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-" 'markdown-promote - "M-" 'markdown-demote)) - -;; (use-package livedown -;; :straight (:host github :repo "shime/emacs-livedown") -;; :commands livedown-preview -;; :config -;; (setq livedown-browser "qutebrowser")) - -(use-package adoc-mode - :mode (rx (| ".asciidoc") eos) - :straight t) - -(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") - :build (:not native-compile)) - ;; (cons (rx (| "srt" "vtt" "ass") eos) #'subed-mode) - :mode ("\\(?:ass\\|\\(?:sr\\|vt\\)t\\)\\'" . subed-mode) - :config - (general-define-key - :keymaps '(subed-mode-map subed-vtt-mode-map) - :states '(normal) - "gp" #'subed-mpv-toggle-pause)) - -(use-package lsp-ltex - :straight t - :after (lsp) - :init - (setq lsp-ltex-version "15.2.0") - (setq lsp-ltex-check-frequency "save")) - -(defun my/ltex-lang () - (interactive) - (setq lsp-ltex-language (completing-read - "Language: " - '("en-GB" "ru-RU" "de-DE"))) - (lsp-workspace-restart (lsp--read-workspace))) - -(defun my/ltex-need-p () - (let ((file-name (buffer-file-name))) - (cond - (my/is-termux nil) - ((null file-name) nil) - ((string-match-p (rx "/home/pavel/" (+ alnum) ".org" eos) file-name) nil) - ((string-match-p (rx (literal org-directory) "/" (or "roam" "inbox-notes" "literature-notes" "journal")) file-name) t) - ((string-match-p (rx (literal org-directory)) file-name) nil) - ((string-match-p (rx (literal (expand-file-name user-emacs-directory))) file-name) nil) - (t t)))) - -(defun my/text-mode-lsp-maybe () - (when (my/ltex-need-p) - (lsp))) - -;; (add-hook 'text-mode-hook #'my/text-mode-lsp-maybe) - -(use-package langtool - :straight t - :commands (langtool-check) - :config - (setq langtool-language-tool-server-jar "/home/pavel/bin/LanguageTool-6.4/languagetool-server.jar") - (setq langtool-mother-tongue "ru") - (setq langtool-default-language "ru-RU")) - -(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 reverso - :straight (:host github :repo "SqrtMinusOne/reverso.el") - :init - (my-leader-def "ar" #'reverso) - :commands (reverso) - :config - (setq reverso-languages '(russian english german)) - (reverso-history-mode)) - -(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 - :defer t - :init - (defun my/flycheck-package-setup () - (require 'flycheck-package) - (flycheck-package-setup) - (remove-hook 'emacs-lisp-mode-hook #'my/flycheck-package-setup)) - (add-hook 'emacs-lisp-mode-hook #'my/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) - -(defun advice-unadvice (sym) - "Remove all advices from symbol SYM." - (interactive "aFunction symbol: ") - (advice-mapc (lambda (advice _props) (advice-remove sym advice)) sym)) - -(add-hook 'inferior-emacs-lisp-mode-hook #'smartparens-mode) -(my-leader-def "bi" #'ielm) - -(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 - :after clojure-mode - :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 - :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\\'" - :disabled t - :config - (add-hook 'clips-mode 'lispy-mode)) - -(use-package ein - :commands (ein:run) - :straight t) - -(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 - :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 #'treesit-fold-mode) - -(use-package pipenv - :straight t - :hook (python-mode . pipenv-mode) - :init - (setq - pipenv-projectile-after-switch-function - #'pipenv-projectile-after-switch-extended)) - -(use-package yapfify - :straight (:repo "JorisE/yapfify" :host github) - :disabled - :commands (yapfify-region - yapfify-buffer - yapfify-region-or-buffer - yapf-mode)) - -(use-package python-black - :straight t - :commands (python-black-buffer) - :config - (setq python-black-command "black")) - -(use-package py-isort - :straight t - :commands (py-isort-buffer py-isort-region)) - -(my-leader-def - :keymaps '(python-mode-map python-ts-mode-map) - "rr" (lambda () - (interactive) - (unless (and (fboundp #'org-src-edit-buffer-p) (org-src-edit-buffer-p)) - (py-isort-buffer)) - (python-black-buffer))) - -(use-package numpydoc - :straight t - :commands (numpydoc-generate) - :init - (my-leader-def - :keymaps 'python-ts-mode-map - "rd" #'numpydoc-generate) - :config - (setq numpydoc-insertion-style 'prompt) - (setq numpydoc-insert-return-without-typehint nil)) - -(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 code-cells-convert-ipynb)) - -(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-hook #'smartparens-mode) - (add-hook 'json-mode-hook #'treesit-fold-mode) - (my/set-smartparens-indent 'json-mode)) - -(use-package csv-mode - :straight t - :disabled - :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 jenkinsfile-mode - :straight t - :mode "Jenkinsfile\\'" - :config - (add-hook 'jenkinsfile-mode-hook #'smartparens-mode) - (my/set-smartparens-indent 'jenkinsfile-mode)) - -(use-package crontab-mode - :mode "/crontab\\(\\.X*[[:alnum:]]+\\)?\\'" - :straight t) - -(use-package nginx-mode - :straight t - :config - (my/set-smartparens-indent 'nginx-mode)) - -(use-package hcl-mode - :mode "\\.hcl\\'" - :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)) - -(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)) - -(my-leader-def - :keymaps '(sql-mode-map) - "rr" #'sqlformat-buffer) - -(use-package sparql-mode - :mode "\\.sparql\\'" - :straight t) - -(use-package graphql-mode - :mode (rx (| "gql" "grapql") eos) - :straight t) - -(defun my/doc-view-setup () - (display-line-numbers-mode -1) - (undo-tree-mode -1)) - -(use-package doc-view - :straight (:type built-in) - :config - (setq doc-view-resolution 300) - (add-hook 'doc-view-mode-hook #'my/doc-view-setup) - (general-define-key - :states '(normal) - :keymaps '(doc-view-mode-map) - "j" #'doc-view-next-line-or-next-page - "k" #'doc-view-previous-line-or-previous-page)) - -(use-package gnuplot - :straight t - :commands (gnuplot-mode gnuplot-make-buffer) - :init - (add-to-list 'auto-mode-alist '("\\.gp\\'" . gnuplot-mode)) - :config - (general-define-key - :keymaps 'gnuplot-mode-map - "C-c C-c" #'gnuplot-send-buffer-to-gnuplot) - (general-define-key - :states '(normal) - :keymaps 'gnuplot-mode-map - "RET" #'gnuplot-send-buffer-to-gnuplot) - (add-hook 'gnuplot-mode-hook #'smartparens-mode)) - -(use-package x509-mode - :commands (x509-dwim) - :straight (:host github :repo "jobbflykt/x509-mode" - :build (:not native-compile))) - -(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 #'treesit-fold-mode)) - -(use-package csharp-mode - :straight t - :mode "\\.cs\\'" - :disabled t - :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 #'treesit-fold-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 nix-mode - :straight t - :mode "\\.nix\\'" - :config - (add-hook 'nix-mode-hook #'smartparens-mode) - (my/set-smartparens-indent 'nix-mode)) - -(use-package lua-mode - :straight t - :mode "\\.lua\\'" - :hook (lua-mode . smartparens-mode)) - -(my/set-smartparens-indent 'lua-mode) - -(setq org-directory (expand-file-name "~/30-39 Life/32 org-mode")) - -(use-package org - :straight (:type built-in) - :if (not my/remote-server) - :defer t - :init - (unless (file-exists-p org-directory) - (mkdir org-directory t)) - :config - (setq org-startup-indented (not my/is-termux)) - (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)))) - -(with-eval-after-load 'org - (require 'org-crypt) - (org-crypt-use-before-save-magic) - (setq org-tags-exclude-from-inheritance '("crypt")) - (setq org-crypt-key "C1EC867E478472439CC82410DE004F32AFA00205")) - -(with-eval-after-load 'epg - (setq epg-gpg-program "gpg") - (setq epg-config--program-alist - `((OpenPGP - epg-gpg-program - ;; ("gpg2" . ,epg-gpg2-minimum-version) - ("gpg" . ((,epg-gpg-minimum-version . "2.0") - ,epg-gpg2-minimum-version))) - (CMS - epg-gpgsm-program - ("gpgsm" . "2.0.4"))))) - -(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)) - -(unless my/remote-server - (setq epa-file-encrypt-to '("DE004F32AFA00205"))) - -(use-package org-contrib - :straight (org-contrib - :type git - :repo "https://git.sr.ht/~bzg/org-contrib" - :build t) - :after (org) - :if (not my/remote-server) - :config - (require 'ox-extra) - (ox-extras-activate '(latex-header-blocks ignore-headlines))) - -(unless (or my/remote-server my/is-termux) - (use-package ol-notmuch - :straight t - :after (org notmuch))) - -(with-eval-after-load 'org - (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"))) - -(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)) - -(defun my/export-rel-url (path desc format) - (cl-case format - (html (format "%s" path (or desc path))) - (latex (format "\\href{%s}{%s}" path (or desc path))) - (otherwise path))) - -(with-eval-after-load 'org - (org-link-set-parameters "rel" :follow #'browse-url :export #'my/export-rel-url)) - -(defun my/outline-prev-or-up-heading () - (interactive) - (if (outline-on-heading-p) - (outline-up-heading 1) - (outline-previous-visible-heading 1))) - -(with-eval-after-load 'org - (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-" #'org-next-visible-heading - "S-" #'org-previous-visible-heading - "M-0" #'org-next-visible-heading - "M-9" #'org-previous-visible-heading - "C-0" #'org-forward-heading-same-level - "C-9" #'org-backward-heading-same-level - "(" #'my/outline-prev-or-up-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-nmap :keymaps 'org-mode-map "RET" 'org-ctrl-c-ctrl-c)) - -(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)))) - -(with-eval-after-load 'org - (general-nmap :keymaps 'org-mode-map - "C-x C-l" 'my/org-link-copy)) - -(defun my/org-babel-next-visible-src-block (arg) - "Move to the next visible source block. - -With ARG, repeats or can move backward if negative." - (interactive "p") - (let ((regexp org-babel-src-block-regexp)) - (if (< arg 0) - (beginning-of-line) - (end-of-line)) - (while (and (< arg 0) (re-search-backward regexp nil :move)) - (unless (bobp) - (while (pcase (get-char-property-and-overlay (point) 'invisible) - (`(outline . ,o) - (goto-char (overlay-start o)) - (re-search-backward regexp nil :move)) - (_ nil)))) - (cl-incf arg)) - (while (and (> arg 0) (re-search-forward regexp nil t)) - (while (pcase (get-char-property-and-overlay (point) 'invisible) - (`(outline . ,o) - (goto-char (overlay-end o)) - (re-search-forward regexp nil :move)) - (_ (end-of-line) nil))) - (re-search-backward regexp nil :move) - (cl-decf arg)) - (if (> arg 0) (goto-char (point-max)) (beginning-of-line)))) - -(defun my/org-babel-previous-visible-src-block (arg) - "Move to the prevous visible source block. - -With ARG, repeats or can move backward if negative." - (interactive "p") - (my/org-babel-next-visible-src-block (- arg))) - -(with-eval-after-load 'org - (general-define-key - :keymaps 'org-mode-map - :states '(normal emacs) - "M-]" #'my/org-babel-next-visible-src-block - "M-[" #'my/org-babel-previous-visible-src-block)) - -(defun my/org-file-open () - (interactive) - (let* ((files - (thread-last - '("projects" "misc" "learning") - (mapcar (lambda (f) - (directory-files (concat org-directory "/" f) t (rx ".org" eos)))) - (apply #'append) - (mapcar (lambda (file) - (string-replace (concat org-directory "/") "" file))) - (append - '("inbox.org" "contacts.org" "recurring.org"))))) - (find-file - (concat org-directory "/" - (completing-read "Org file: " files))))) - -(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 "'")) - -(with-eval-after-load 'org - (setq my/org-latex-scale 1.75) - (setq org-format-latex-options (plist-put org-format-latex-options :scale my/org-latex-scale))) - -(with-eval-after-load 'org - (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))) - -(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)) - -(unless (display-graphic-p) - (add-hook 'org-mode-hook #'org-indent-mode)) - -(defun my/org-no-ellipsis-in-headlines () - (remove-from-invisibility-spec '(outline . t)) - (add-to-invisibility-spec 'outline)) - -(with-eval-after-load 'org-bars - (add-hook 'org-mode-hook #'my/org-no-ellipsis-in-headlines) - (when (eq major-mode 'org-mode) - (my/org-no-ellipsis-in-headlines))) - -(my/use-colors - (org-block :background (my/color-value 'bg-other)) - (org-block-begin-line :background (my/color-value 'bg-other) - :foreground (my/color-value 'grey))) - -(use-package org-appear - :after (org) - :straight t) - -(use-package org-fragtog - :after (org) - :straight t) - -(use-package jupyter - :straight t - :after (org) - :if (not (or my/remote-server my/is-termux))) - -(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)) - -(defun my/org-load-jupyter () - (interactive) - (org-babel-do-load-languages - 'org-babel-load-languages - '((jupyter . t))) - (my/jupyter-refesh-langs)) - -(use-package ob-hy - :after (org) - :if (not my/remote-server) - :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)))))) - -(with-eval-after-load 'org - (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))) - -(use-package restclient - :if (not my/remote-server) - :straight t - :mode ("\\.http\\'" . restclient-mode) - :config - (general-define-key - :keymaps 'restclient-mode-map - :states '(normal visual) - "RET" #'restclient-http-send-current - "M-RET" #'restclient-http-send-current-stay-in-window - "y" nil - "M-y" #'restclient-copy-curl-command) - (general-define-key - :keymaps 'restclient-response-mode-map - :states '(normal visual) - "q" #'quit-window)) - -(use-package ob-restclient - :after (org restclient) - :if (not my/remote-server) - :straight t) - -(with-eval-after-load 'org - (org-babel-do-load-languages - 'org-babel-load-languages - `((emacs-lisp . t) - (python . t) - (sql . t) - (sqlite . t) - ;; (typescript .t) - (hy . t) - (shell . t) - (plantuml . t) - (octave . t) - (sparql . t) - (gnuplot . t))) - - (add-hook 'org-babel-after-execute-hook 'org-redisplay-inline-images)) - -(with-eval-after-load 'ob-jupyter - (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) - )) - -(use-package ob-async - :straight t - :after (org) - :config - (setq ob-async-no-async-languages-alist '("python" "hy" "jupyter-python" "jupyter-octave" "restclient"))) - -(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))) - -(with-eval-after-load 'jupyter - (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/babel-ansi () - (when-let ((beg (org-babel-where-is-src-block-result nil nil))) - (save-excursion - (goto-char beg) - (when (looking-at org-babel-result-regexp) - (let ((end (org-babel-result-end)) - (ansi-color-context-region nil)) - (ansi-color-apply-on-region beg end)))))) - -(define-minor-mode org-babel-ansi-colors-mode - "Apply ANSI color codes to Org Babel results." - :global t - :after-hook - (if org-babel-ansi-colors-mode - (add-hook 'org-babel-after-execute-hook #'my/babel-ansi) - (remove-hook 'org-babel-after-execute-hook #'my/babel-ansi))) - -(defun my/org-babel-execute-buffer-below (&optional arg) - (interactive "P") - (org-babel-eval-wipe-error-buffer) - (let ((point (point))) - (org-save-outline-visibility t - (org-babel-map-executables nil - (when (>= (point) point) - (if (memq (org-element-type (org-element-context)) - '(babel-call inline-babel-call)) - (org-babel-lob-execute-maybe) - (org-babel-execute-src-block arg))))))) - -(defun my/org-babel-execute-buffer-above (&optional arg) - (interactive "P") - (org-babel-eval-wipe-error-buffer) - (let ((point (point))) - (org-save-outline-visibility t - (org-babel-map-executables nil - (when (<= (point) point) - (if (memq (org-element-type (org-element-context)) - '(babel-call inline-babel-call)) - (org-babel-lob-execute-maybe) - (org-babel-execute-src-block arg))))))) - -(defun my/org-babel-execute-marked (&optional arg) - (interactive "P") - (let (markers) - (org-element-map (org-element-parse-buffer) 'src-block - (lambda (elem) - (let ((params (org-element-property :parameters elem))) - (when (and params - (string-match-p (rx "startup t") params)) - (let ((m (make-marker))) - (set-marker m (org-element-property :begin elem)) - (set-marker-insertion-type m t) - (push m markers)))))) - (setq markers (nreverse markers)) - (when arg - (setq markers - (seq-filter - (lambda (m) (> (marker-position m) (point))) - markers))) - (dolist (m markers) - (goto-char m) - (ignore-errors - (org-babel-execute-src-block))))) - -(with-eval-after-load 'org - (general-define-key - :keymaps 'org-babel-map - "B" #'my/org-babel-execute-buffer-below - "A" #'my/org-babel-execute-buffer-above) - - (my-leader-def - :keymaps 'org-mode-map - "SPC b" '(:wk "org-babel") - "SPC b" org-babel-map)) - -(defun my/org-prj-dir (path) - (expand-file-name path (org-entry-get nil "PRJ-DIR" t))) - -(use-package hide-mode-line - :straight t - :commands (hide-mode-line-mode)) - -(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 - "" 'my/present-next-with-latex - "" 'my/present-prev-with-latex) - (setq org-present-mode-hook - (list (lambda () - (blink-cursor-mode 0) - (org-present-big) - (org-bars-mode -1) - ;; (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)) - ;; TODO ^somehow this stucks at running LaTeX^ - (setq-local olivetti-body-width 60) - (olivetti-mode 1)))) - (setq org-present-mode-quit-hook - (list (lambda () - (blink-cursor-mode 1) - (org-present-small) - (org-bars-mode 1) - ;; (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)) - (olivetti-mode -1) - (setq-local olivetti-body-width (default-value 'olivetti-body-width)))))) - -(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 - :config - (setq org-attach-screenshot-auto-refresh 'never)) - -(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)) - -(use-package edraw-org - :straight (:host github :repo "misohena/el-easydraw") - :if (and (not my/is-termux) (not my/remote-server)) - :after (org) - :config - (edraw-org-setup-default)) - -(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 phscroll - :straight (:host github :repo "misohena/phscroll") - :commands (org-phscroll-mode) - :config - (with-eval-after-load 'org - (require 'org-phscroll) - (org-phscroll-deactivate))) - -(defun my/update-org-agenda () - (interactive) - (let ((project-files - (when (file-directory-p (concat org-directory "/projects")) - (thread-last "/projects" - (concat org-directory) - (directory-files) - (mapcar (lambda (f) - (concat - org-directory "/projects/" f))) - (seq-filter (lambda (f) - (not (file-directory-p f)))))))) - (setq org-agenda-files - (seq-filter #'file-exists-p - (append - project-files - (mapcar (lambda (f) - (concat org-directory "/" f)) - '("inbox.org" - "misc/habit.org" - "contacts.org"))))) - (setq org-refile-targets - `(,@(mapcar - (lambda (f) `(,f . (:tag . "refile"))) - project-files) - ,@(mapcar - (lambda (f) `(,f . (:regexp . "Tasks"))) - project-files))) - (when (file-exists-p (concat org-directory "/scripts/refile.el")) - (load-file (concat org-directory "/scripts/refile.el")) - (run-hooks 'my/org-refile-hooks)))) - -(setq org-roam-directory (concat org-directory "/roam")) -(with-eval-after-load 'org - (require 'seq) - (my/update-org-agenda)) - -(setq org-refile-use-outline-path 'file) -(setq org-outline-path-complete-in-steps nil) - -(setq org-extend-today-until 4) - -(defun my/generate-inbox-note-name () - (format - "%s/inbox-notes/%s%s.org" - org-directory - (format-time-string "%Y%m%d%H%M%S") - (let ((note-name (read-string "Note name: "))) - (if (not (string-empty-p note-name)) - (string-replace " " "-" (concat "-" (downcase note-name))) - "")))) - -(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" plain (file my/generate-inbox-note-name) - ,(concat "#+TODO: PROCESSED(p)\n" - "\n" - "* %?\n" - "/Entered on/ %U")))) - -(use-package org-clock-agg - :straight (:host github :repo "SqrtMinusOne/org-clock-agg") - :commands (org-clock-agg) - :init - (with-eval-after-load 'org - (my-leader-def "ol" #'org-clock-agg)) - :config - (setq org-clock-agg-node-format - "%-%(+ title-width)t %20c %8z %s/%S") - (setq org-clock-agg-node-title-width-delta 47) - (push - (cons "Agenda+Archive" - (append - (org-agenda-files) - (thread-last "/projects/archive" - (concat org-directory) - (directory-files) - (mapcar (lambda (f) - (concat - org-directory "/projects/archive/" f))) - (seq-filter (lambda (f) - (not (file-directory-p f))))))) - org-clock-agg-files-preset)) - -(with-eval-after-load 'org - (setq org-clock-persist 'clock) - (org-clock-persistence-insinuate)) - -(with-eval-after-load 'org - (add-to-list - 'org-global-properties - '("Effort_ALL" . "0 0:05 0:10 0:15 0:30 0:45 1:00 1:30 2:00 4:00 8:00"))) - -(setq org-log-done 'time) - -(defun my/org-clock-in--fix-mode-line () - (when (memq 'org-mode-line-string global-mode-string) - (let (new-global-mode-string - appended - (is-first t)) - (dolist (item global-mode-string) - (cond - ((or (equal item '(:eval (exwm-modeline-segment))) - (equal item '(:eval (persp-mode-line)))) - (unless appended - (when is-first - (push "" new-global-mode-string)) - (push 'org-mode-line-string new-global-mode-string) - (setq appended t)) - (push item new-global-mode-string)) - ((equal item 'org-mode-line-string)) - (t - (push item new-global-mode-string))) - (setq is-first nil)) - (unless appended - (push 'org-mode-line-string new-global-mode-string)) - (setq global-mode-string (nreverse new-global-mode-string))))) - -(add-hook 'org-clock-in-hook #'my/org-clock-in--fix-mode-line) - -(defun my/org-clock-in-prompt-time (&optional select) - (interactive "P") - (org-clock-in - select - (encode-time - (org-parse-time-string - (org-read-date t))))) - -(with-eval-after-load 'org - (my-leader-def - :keymaps 'org-mode-map - :infix "SPC" - "I" #'my/org-clock-in-prompt-time)) - -(defun my/org-clock-get-total-minutes-at-point () - "Get total clocked time for heading at point." - (let* ((element (org-element-at-point-no-context)) - (s (buffer-substring-no-properties - (org-element-property :begin element) - (org-element-property :end element)))) - (with-temp-buffer - (insert s) - (org-clock-sum) - org-clock-file-total-minutes))) - -(defconst my/org-clock-total-prop :CLOCK_TOTAL) - -(defun my/org-clock-set-total-clocked () - "Set total clocked time for heading at point." - (interactive) +(defun my/modules--refresh-table (modules-list) (save-excursion - (org-back-to-heading t) - (org-set-property - (substring - (symbol-name my/org-clock-total-prop) - 1) - (org-duration-from-minutes - (my/org-clock-get-total-minutes-at-point))))) - -(add-hook 'org-clock-in-hook #'my/org-clock-set-total-clocked) -(add-hook 'org-clock-out-hook #'my/org-clock-set-total-clocked) -(add-hook 'org-clock-cancel-hook #'my/org-clock-set-total-clocked) - -(defun my/org-clock-recent () - (interactive) - (let* ((entries (org-ql-query - :select #'element-with-markers - :from (org-agenda-files) - :where '(clocked :from -1))) - (entries-data (mapcar (lambda (e) - (cons (org-element-property :raw-value e) e)) - entries))) - (unless entries - (user-error "No recently clocked entries!")) - entries-data - (let* ((entry (alist-get (completing-read "Entry: " entries-data) - entries-data nil nil #'equal)) - (marker (org-element-property :org-marker entry))) - (pop-to-buffer-same-window (marker-buffer marker)) - (goto-char marker)))) - -(with-eval-after-load 'org - (my-leader-def - :keymaps 'org-mode-map - :infix "SPC" - "C" #'my/org-clock-recent)) - -(defun my/org-fix-task-kind () - (interactive) - (let ((entries (org-ql-query - :select #'element-with-markers - :from (current-buffer) - :where '(and (olp "Tasks") - (not (property "TASK_KIND")) - (clocked))))) - (org-fold-show-all) - (dolist (entry entries) - (let ((marker (org-element-property :org-marker entry))) - (org-with-point-at marker - (let ((value (org-read-property-value "TASK_KIND"))) - (org-set-property "TASK_KIND" value))))))) - -(use-package org-super-agenda - :straight t - :after (org) - :config - ;; Alphapapa doesn't like evil - (general-define-key - :keymaps '(org-super-agenda-header-map) - "h" nil - "j" nil - "k" nil - "l" nil) - (org-super-agenda--def-auto-group outline-path-file "their outline paths & files" - :key-form - (org-super-agenda--when-with-marker-buffer (org-super-agenda--get-marker item) - ;; org-ql depends on f and s anyway - (s-join "/" (cons - (f-filename (buffer-file-name)) - (org-get-outline-path)))))) - -(defun my/org-super-agenda--make-agenda-header-around (fun name) - (remove-text-properties 0 (length name) '(line-prefix nil) name) - (remove-text-properties 0 (length name) '(wrap-prefix nil) name) - (funcall fun (substring-no-properties name))) - -(with-eval-after-load 'org-super-agenda - (advice-add 'org-super-agenda--make-agenda-header :around #'my/org-super-agenda--make-agenda-header-around)) - -(use-package org-ql - :after (org) - :if (not my/remote-server) - :straight t - :config - (setq org-ql-ask-unsafe-queries nil) - :init - ;; See https://github.com/alphapapa/org-ql/pull/237 - (setq org-ql-regexp-part-ts-time - (rx " " (repeat 1 2 digit) ":" (repeat 2 digit) - (optional "-" (repeat 1 2 digit) ":" (repeat 2 digit)))) - (my-leader-def - :infix "o" - "v" #'org-ql-view - "q" #'org-ql-search)) - -(cl-defun my/org-ql-view-recent-items - (&key num-days (type 'ts) - (files (org-agenda-files)) - (groups '((:auto-outline-path-file t) - (:auto-todo t)))) - "Show items in FILES from last NUM-DAYS days with timestamps of TYPE. -TYPE may be `ts', `ts-active', `ts-inactive', `clocked', or -`closed'." - (interactive (list :num-days (read-number "Days: ") - :type (->> '(ts ts-active ts-inactive clocked closed) - (completing-read "Timestamp type: ") - intern))) - ;; It doesn't make much sense to use other date-based selectors to - ;; look into the past, so to prevent confusion, we won't allow them. - (-let* ((query (pcase-exhaustive type - ((or 'ts 'ts-active 'ts-inactive) - `(,type :from ,(- num-days) :to 0)) - ((or 'clocked 'closed) - `(,type :from ,(- num-days) :to 0))))) - (org-ql-search files query - :title "Recent items" - :sort '(todo priority date) - :super-groups groups))) - -(defun my/org-ql-all-todo () - (interactive) - ;; The hack I borrowed from notmuch to make " " a separator - (let* ((crm-separator " ") - (crm-local-completion-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map crm-local-completion-map) - (define-key map " " 'self-insert-command) - map)) - (vertico-sort-function nil) - (categories (completing-read-multiple - "Categories: " - '("TEACH" "EDU" "JOB" "LIFE" "COMP")))) - (org-ql-search (org-agenda-files) - `(and (todo) - ,@(unless (seq-empty-p categories) - `((category ,@categories)))) - :sort '(priority todo deadline) - :super-groups '((:auto-outline-path-file t))))) - -(defun my/org-ql-clocked-today () - (interactive) - (let ((today (format-time-string - "%Y-%m-%d" - (days-to-time - (- (org-today) (time-to-days 0)))))) - (org-ql-search (org-agenda-files) `(clocked :from ,today) - :title "Clocked today" - :sort '(todo priority date) - :super-groups '((:auto-outline-path-file t) - (:auto-todo t))))) - -(defun my/org-ql-closed-today () - (interactive) - (let ((today (format-time-string - "%Y-%m-%d" - (days-to-time - (- (org-today) (time-to-days 0)))))) - (org-ql-search (org-agenda-files) `(closed :from ,today) - :title "Closed today" - :sort '(todo priority date) - :super-groups '((:auto-outline-path-file t) - (:auto-todo t))))) - -(setq org-ql-views - (list - (cons "Overview: All TODO" #'my/org-ql-all-todo) - (cons "Review: Stale tasks" - (list :buffers-files #'org-agenda-files - :query '(and (todo) - (not (tags "nots")) - (not (ts :from -14))) - :title "Review: Stale tasks" - :sort '(todo priority date) - :super-groups '((:auto-outline-path-file t)))) - (cons "Review: Unclocked tasks" - (list :buffers-files #'org-agenda-files - :query '(and (done) - (ts :from -14) - (not (clocked)) - (not (tags "nots"))) - :title "Review: Unclocked tasks" - :sort '(todo priority date) - :super-groups '((:auto-outline-path-file t)))) - (cons "Review: Recently timestamped" #'my/org-ql-view-recent-items) - (cons "Review: Clocked today" #'my/org-ql-clocked-today) - (cons "Review: Closed today" #'my/org-ql-closed-today) - (cons "Fix: tasks without TASK_KIND" - (lambda () - (interactive) - (org-ql-search (current-buffer) - '(and (olp "Tasks") - (not (property "TASK_KIND")) - (clocked)) - :super-groups '((:auto-outline-path-file t))))))) - -(defun my/org-ql-view--format-element-override (element) - "Format ELEMENT for `org-ql-view'. - -Check `org-ql-view--format-element' for the original implementation -and lots of comments which are too long for my Emacs config." - (if (not element) - "" - (setf element (org-ql-view--resolve-element-properties element)) - (let* ((properties (cadr element)) - (properties (cl-loop for (key val) on properties by #'cddr - for symbol = (intern (cl-subseq (symbol-name key) 1)) - unless (member symbol '(parent)) - append (list symbol val))) - (title (--> (org-ql-view--add-faces element) - (org-element-property :raw-value it) - (org-link-display-format it))) - (todo-keyword (-some--> (org-element-property :todo-keyword element) - (org-ql-view--add-todo-face it))) - (tag-list (if org-use-tag-inheritance - (if-let ((marker (or (org-element-property :org-hd-marker element) - (org-element-property :org-marker element)))) - (with-current-buffer (marker-buffer marker) - (org-with-wide-buffer - (goto-char marker) - (cl-loop for type in (org-ql--tags-at marker) - unless (or (eq 'org-ql-nil type) - (not type)) - append type))) - (display-warning 'org-ql (format "No marker found for item: %s" title)) - (org-element-property :tags element)) - (org-element-property :tags element))) - (tag-string (when tag-list - (--> tag-list - (s-join ":" it) - (s-wrap it ":") - (org-add-props it nil 'face 'org-tag)))) - ;; (category (org-element-property :category element)) - (priority-string (-some->> (org-element-property :priority element) - (char-to-string) - (format "[#%s]") - (org-ql-view--add-priority-face))) - (clock-string (let ((effort (org-element-property :EFFORT element)) - (clocked (org-element-property my/org-clock-total-prop element))) - (cond - ((and clocked effort) (format "[%s/%s]" clocked effort)) - ((and clocked (not effort) (format "[%s]" clocked))) - ((and (not clocked) effort) (format "[EST: %s]" effort))))) - (habit-property (org-with-point-at (or (org-element-property :org-hd-marker element) - (org-element-property :org-marker element)) - (when (org-is-habit-p) - (org-habit-parse-todo)))) - (due-string (pcase (org-element-property :relative-due-date element) - ('nil "") - (string (format " %s " (org-add-props string nil 'face 'org-ql-view-due-date))))) - (string (s-join " " (-non-nil (list todo-keyword priority-string title due-string clock-string tag-string))))) - (remove-list-of-text-properties 0 (length string) '(line-prefix) string) - (--> string - (concat " " it) - (org-add-props it properties - 'org-agenda-type 'search - 'todo-state todo-keyword - 'tags tag-list - 'org-habit-p habit-property))))) - -(with-eval-after-load 'org-ql - (advice-add #'org-ql-view--format-element :override #'my/org-ql-view--format-element-override)) - -(use-package org-habit-stats - :straight (:host github :repo "ml729/org-habit-stats") - :after (org) - :config - (general-define-key - :keymaps '(org-habit-stats-mode-map) - :states '(normal emacs) - "q" #'org-habit-stats-exit - "<" #'org-habit-stats-calendar-scroll-left - ">" #'org-habit-stats-calendar-scroll-right - "[" #'org-habit-stats-scroll-graph-left - "]" #'org-habit-stats-scroll-graph-right - "{" #'org-habit-stats-scroll-graph-left-big - "}" #'org-habit-stats-scroll-graph-right-big - "." #'org-habit-stats-view-next-habit - "," #'org-habit-stats-view-previous-habit) - (add-hook 'org-after-todo-state-change-hook 'org-habit-stats-update-properties)) - -(defun my/org-match-at-point-p (match) - "Return non-nil if headline at point matches MATCH. -Here MATCH is a match string of the same format used by -`org-tags-view'." - (funcall (cdr (org-make-tags-matcher match)) - (org-get-todo-state) - (org-get-tags-at) - (org-reduced-level (org-current-level)))) - -(defun my/org-agenda-skip-without-match (match) - "Skip current headline unless it matches MATCH. - -Return nil if headline containing point matches MATCH (which -should be a match string of the same format used by -`org-tags-view'). If headline does not match, return the -position of the next headline in current buffer. - -Intended for use with `org-agenda-skip-function', where this will -skip exactly those headlines that do not match." - (save-excursion - (unless (org-at-heading-p) (org-back-to-heading)) - (let ((next-headline (save-excursion - (or (outline-next-heading) (point-max))))) - (if (my/org-match-at-point-p match) nil next-headline)))) - -(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" "refile" "proj" "habit"))) - -(setq org-agenda-custom-commands - `(("p" "My outline" - ((agenda "" ((org-agenda-skip-function '(my/org-agenda-skip-without-match "-habit")))) - (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)"))) - (tags-todo "habit+SCHEDULED<=\"<+0d>\"" - ((org-agenda-overriding-header "Habits") - (org-agenda-prefix-format " %i %-12:c") - (org-agenda-hide-tags-regexp "."))))))) - -(use-package org-yaap - :straight (org-yaap :type git :host gitlab :repo "SqrtMinusOne/org-yaap") - :after (org) - :if (not my/nested-emacs) - :disabled t - :config - (org-yaap-mode 1) - (setq org-yaap-alert-before '(10 1)) - (setq org-yaap-alert-title "PROXIMITY ALERT") - (setq org-yaap-todo-keywords-only '("FUTURE"))) - -(setq my/org-alert-notify-times '(600 60)) - -(setq my/org-alert--alerts (make-hash-table :test #'equal)) - -(defun my/org-alert--is-scheduled (label time) - "Check if LABEL is scheduled to be shown an TIME." - (gethash (cons label time) - my/org-alert--alerts nil)) - -(defun my/org-alert--schedule (label time) - "Schedule LABEL to be shown at TIME, unless it's already scheduled." - (unless (my/org-alert--is-scheduled label time) - (puthash (cons label time) - (run-at-time time - nil - (lambda () - (alert label - :title "PROXIMITY ALERT"))) - my/org-alert--alerts))) - -(defun my/org-alert-cleanup (&optional keys) - "Unschedule items that do not appear in KEYS. - -KEYS is a list of cons cells like (