#+STARTUP: overview
#+PROPERTY: header-args:emacs-lisp :results silent :tangle config.el
* Spacemacs like setup
** Base variables
#+begin_src emacs-lisp 
(setq mv/nixos-base-folder "/etc/nixos")
(setq mv/nextcloud-base "/home/misha/Nextcloud")
(setq org-export-with-broken-links t)
(setq filemv-download "/home/misha/Downloads")
#+end_src
** Custom
#+begin_src emacs-lisp 
(custom-set-variables
 ;; custom-set-variables was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 '(custom-safe-themes
   '("cdad4e5bc718111deb1f1a2a35e9781e11724144a1eb2064732c337160946760"
     "cfb0bb44259cf576124b900613628f4b589df1c3860185d24fffde6666bc88cf"
     default))
 '(package-selected-packages nil))
(custom-set-faces
 ;; custom-set-faces was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 '(org-level-1 ((t (:inherit outline-1 :height 1.3))))
 '(org-level-2 ((t (:inherit outline-2 :height 1.2))))
 '(org-level-3 ((t (:inherit outline-3 :height 1.1))))
 '(org-level-4 ((t (:inherit outline-4 :height 1.0))))
 '(org-level-5 ((t (:inherit outline-5 :height 1.0)))))
#+end_src
** Basic tricks Daniel
#+begin_src emacs-lisp 
(define-prefix-command 'spc-root 'spc-leader-map)
(global-set-key (kbd "M-m") 'spc-root)
;; Uncouple C-i and C-m keys
(define-key input-decode-map "\C-m" [C-m])

;; For defining keybindings Emacs provides the =define-key= command to set a single key binding at a time. If we want to define multiple key bindings at once, we must define a custom function for that.
(defun spc-define-keys (map key def &rest bindings)
  "Define multiple key bindings at once."
  (while key
    (define-key map (kbd key) def)
    (setq key (pop bindings) def (pop bindings))))
(put 'spc-define-keys 'lisp-indent-function 'defun)
#+end_src
** Server
This starts Emacsclient.
#+begin_src emacs-lisp
(require 'server)
(unless (server-running-p)
  (server-start))
#+end_src
** Evil

#+begin_src emacs-lisp
(setq evil-want-integration t ;; This is optional since it's already set to t by default.
      evil-want-keybinding nil
      evil-want-minibuffer t
      evil-want-C-i-jump t
      evil-respect-visual-line-mode t
      evil-undo-system 'undo-redo
      evil-shift-width 2
      evil-symbol-word-search t)

(evil-mode)

(global-set-key (kbd "C-u") 'evil-scroll-up)
(global-set-key (kbd "C-d") 'evil-scroll-down)

;; not needed when using evil-collection
(add-to-list 'evil-motion-state-modes 'special-mode)

(evil-global-set-key 'motion (kbd "SPC") 'spc-root)

;; Added by me
;; (package-install 'evil-surround) ;;nixos
(global-evil-surround-mode 1)
(setq evil-shift-width 2)
#+end_src
*** Evil collection
#+BEGIN_SRC emacs-lisp  
;; (package-install 'evil-collection) ;;nixos
(require 'evil-collection)
(evil-collection-init)
(evil-collection-help-setup)

(evil-collection-define-key 'normal 'prog-mode-map [tab] 'indent-region)

#+END_SRC

** ivy
#+begin_src emacs-lisp 

;; (package-install 'ivy) ;;nixos
(require 'ivy)
(ivy-mode 1)
;; (package-install 'counsel) ;;nixos
(require 'counsel)
;; (package-install 'ivy-prescient) ;;nixos
(require 'ivy-prescient)
(ivy-prescient-mode 1)
(prescient-persist-mode 1)

#+end_src
*** Work with ivy-prescient-mode
- <2023-11-20 ma>
- Setting ivy-prescient-mode to -1 (turning it off) means that lists are shown in their natural order: recently chosen items do not get shown above.
- In general I like recently chosen things to show up first. But sometimes I want to turn this off.
- If I put this in the beginning of a function:
  (add-hook 'minibuffer-exit-hook 'turn-on-prescient-mode-mv)
  (ivy-prescient-mode -1)
- ... and I turn ivy-prescient-mode on at the end of a function..
- ... it works. Which makes sense.
#+begin_src emacs-lisp
(defun turn-on-prescient-mode-mv ()
  (interactive)
  (ivy-prescient-mode 1)
  (remove-hook 'minibuffer-exit-hook 'turn-on-prescient-mode-mv))
#+end_src

** Window management
#+begin_src emacs-lisp 
;; (package-install 'winum) ;;nixos
(require 'winum)
(winum-mode)
(setq frame-title-format
      (list (format "%s %%S: %%j " (system-name))
            '(buffer-file-name "%f" (dired-directory dired-directory "%b"))))

;; windows
(global-set-key (kbd "M-1") 'winum-select-window-1)
(global-set-key (kbd "M-2") 'winum-select-window-2)
(global-set-key (kbd "M-3") 'winum-select-window-3)
(global-set-key (kbd "M-4") 'winum-select-window-4)
(global-set-key (kbd "M-5") 'winum-select-window-5)
(global-set-key (kbd "M-6") 'winum-select-window-6)
(global-set-key (kbd "M-7") 'winum-select-window-7)
(global-set-key (kbd "M-8") 'winum-select-window-8)
(global-set-key (kbd "M-9") 'winum-select-window-9)

#+end_src

** Disabling bars
#+begin_src emacs-lisp 
;; Disable bars
(menu-bar-mode -1)
(tool-bar-mode -1)
(scroll-bar-mode -1)
#+end_src

** Basic keybindings
#+begin_src emacs-lisp
(spc-define-keys spc-leader-map

  ;; Basics
  "SPC"   'execute-extended-command
  "<tab>" 'evil-switch-to-windows-last-buffer
  ";"           'comment-or-uncomment-region

  "b"  '("buffer" keymap)
  "bb" 'switch-to-buffer
  "bd" 'kill-current-buffer
  "bm" 'pop-to-messages
  "bs" 'scratch-buffer
  "bt" 'find-todo
  "bp" 'previous-buffer
  "bn" 'next-buffer

  "c"  '("compile" keymap)
  "cc" 'recompile

  "e"  '("edit" keymap)

  "f"   '("files" keymap)
  "fe"  '("emacs" keymap)
  "fD" 'delete-current-buffer-file
  "fed" 'find-init-file
  "feR" 'load-init-file
  "ff"  'find-file
  "fl"  'find-library
  "fL"  'counsel-locate
  "fr"  'counsel-recentf
  "fR"  'rename-mv
  "fs"  'save-buffer
  "fS" 'evil-write-all
  "Fn" 'make-frame
  "Fd" 'delete-frame

  "g" '("git" keymap)

  "h"  '("help" keymap)
  "hdc" 'describe-char
  "hdf" 'describe-function
  "hj" 'info-display-manual
  "hdk" 'describe-key
  "hdv" 'describe-variable
  "hdK" 'describe-keymap
  "hdm" 'describe-mode

  "j"  '("jump" keymap)
  "jf" 'find-function
  ;;"ji" 'imenu ;might get overwritten by ivy/vertico/helm section
  "jl" 'find-library
  "ji" 'ji-mv

  "l" '("lisp" keymap)
  "ll" 'ielm

  "o"  '("org" keymap)
  "oe" 'open-chatgtp
  "p"  '("projects" keymap)
  "pf" 'project-find-file
  "pp" 'project-switch-project

  "s"  '("search" keymap)
  "ss" 'swiper
  "si" 'swiper-thing-at-point
  "sl" 'locate

  "t"  '("toggles" keymap)
  "tl" 'display-line-numbers-mode

  "u" 'universal-argument

  "w"  '("windows" keymap)
  "w/" 'split-window-right
  "w-" 'split-window-below
  "wd" 'delete-window
  "w1" 'delete-other-windows
  "wl" 'evil-window-move-far-right
  "wh" 'evil-window-move-far-left
  
  ;;"z" 'text-scale-adjust
  ;;"ws" 'shrink-window-horizontally
  ;;"wl" 'enlarge-window-horizontally
  "w]" 'evil-window-increase-width
  "w[" 'evil-window-decrease-width

  "q"  '("quit" keymap)
  "qq" 'save-buffers-kill-emacs

  "gs" 'magit-status
  "eof" 'edebug-defun
  "om" 'pdf-outline
  "ew" 'google-translate-mv
  "pc" 'comint-clear-buffer
  "pr" 'run-region-mv 
  "pB" 'python-shell-send-buffer
  "pb" 'run-block)

(add-hook 'custom-mode-hook
          (lambda ()
            (evil-define-key 'normal custom-mode-map (kbd "SPC") nil)))


(global-set-key (kbd "C-S-c") (lambda () (interactive) (insert (format-time-string "<%Y-%m-%d %a>"))))
(global-set-key (kbd "C-S-t") (lambda () (interactive) (insert (format-time-string "<%Y-%m-%d %a %z %H:%M>"))))
#+end_src
** nix
#+begin_src emacs-lisp
(require 'nix-mode)
(add-to-list 'auto-mode-alist '("\\.nix\\'" . nix-mode))

#+end_src
** Font
#+begin_src emacs-lisp 
(set-face-attribute 'default nil :height 110)

(defun text-scale-normal ()
  (interactive)
  (text-scale-set 0.1))

(spc-define-keys spc-leader-map
  "z+" 'text-scale-increase
  "z-" 'text-scale-decrease
  "z0" 'text-scale-normal)

(setq org-emphasis-alist
      '(("*" (bold :foreground "dim gray" ))
        ("/" (italic :foreground "dim gray" ))
        ("_" underline)
        ("=" (:background "wheat" :foreground "black"))
        ("~" (:background "wheat" :foreground "black" :weight bold))
        ("+" (:strike-through t))))

(defun mv/font-normal ()
  (interactive)
  (set-face-attribute 'default nil :height 110))

(defun mv/font-medium ()
  (interactive)
  (set-face-attribute 'default nil :height 150))

(defun mv/font-large ()
  (interactive)
  (set-face-attribute 'default nil :height 190))

(defun mv/font-huge ()
  (interactive)
  (set-face-attribute 'default nil :height 240))

(spc-define-keys spc-leader-map
  "z0" 'mv/font-normal
  "z1" 'mv/font-medium
  "z2" 'mv/font-large
  "z3" 'mv/font-huge
  )

#+end_src
** Theme and global formatting
#+begin_src emacs-lisp 
(load-theme 'standard-dark)
(add-to-list 'initial-frame-alist '(fullscreen . maximized))
#+end_src
*** Line numbers
#+begin_src emacs-lisp
(add-hook 'org-mode-hook '(lambda () (display-line-numbers-mode t)))
(add-hook 'LaTeX-mode-hook '(lambda () (display-line-numbers-mode t)))
#+end_src
** Fix indent
#+begin_src emacs-lisp 
(defun fix-indent-mv ()
  (interactive)
  (save-excursion
    (indent-region (point-min) (point-max))))
#+end_src
* Finding files functions

#+begin_src emacs-lisp
(defun find-init-file ()
  (interactive)
  (find-file "~/.emacs.d/README.org"))


(defun file-search-mv (paths-mv)
  (interactive)
  ;; this function removes itself from the hook 
  (add-hook 'minibuffer-exit-hook 'turn-on-prescient-mode-mv)
  (ivy-prescient-mode -1)
  (let* ((listfiles (split-string
                     (shell-command-to-string
                      (format "find %s -maxdepth 1 -type f -printf '%%T@ %%CY-%%Cm-%%Cd %%CH:%%CM:%%.2CS %%h %%f\n' | sort -nrk1 | awk '{$1=\"\"; print}' | grep -v pdf-view-restore | grep -Ev '[#~]$' | grep -v '.html$'" (string-join paths-mv " "))
                      ;;(format "find %s -maxdepth 1 -type f -printf '%%T@ %%c %%h %%f\\n' | sort -nr | awk '{$1=\"\";print}'" (string-join paths-mv " "))
                      ) "\n"))
         (filenamem
          (string-join
           (split-string
            (ivy-read "open: " listfiles) " ") " " )))
    (let ((output (format "%s/%s" (nth 3 (split-string filenamem " ")) (string-join (nthcdr 4 (split-string filenamem " ")) " "))))
      ;; (print filenamem)
      ;; (print output)
      (find-file output)
      ;;(remove-hook 'minibuffer-exit-hook 'turn-on-prescient-mode)
      ))
  (ivy-prescient-mode 1)
  )

(defun mnotessearch-simple ()
  (interactive)
  ;; this function removes itself from the hook 
  (add-hook 'minibuffer-exit-hook 'turn-on-prescient-mode-mv)
  (ivy-prescient-mode -1)
  (let* ((listfiles (split-string
                     (shell-command-to-string
                      (format "find /home/misha/Nextcloud/mnotes/ -type f -printf '%%T@ %%CY-%%Cm-%%Cd %%CH:%%CM:%%.2CS %%h %%f\n' | sort -nrk1 | awk '{$1=\"\"; print}' | grep -v pdf-view-restore | grep -Ev '[#~]$' | grep -v '.html$' | grep -v 'images'")
                      ;;(format "find %s -maxdepth 1 -type f -printf '%%T@ %%c %%h %%f\\n' | sort -nr | awk '{$1=\"\";print}'" (string-join paths-mv " "))
                      ) "\n"))
         (filenamem
          (string-join
           (split-string
            (ivy-read "open: " listfiles) " ") " " )))
    (let ((output (format "%s/%s" (nth 3 (split-string filenamem " ")) (string-join (nthcdr 4 (split-string filenamem " ")) " "))))
      ;; (print filenamem)
      ;; (print output)
      (find-file output)
      ;;(remove-hook 'minibuffer-exit-hook 'turn-on-prescient-mode)
      ))
  (ivy-prescient-mode 1)
  )


(defun mnotessearch ()
  (interactive)
  (file-search-mv '(
                    (format "%s/mnotes/" mv/nextcloud-base)
                    (format "%s/mnotes/orgzly/" mv/nextcloud-base)
                    (format "%s/mnotes/org-roam/" mv/nextcloud-base)
                    (format "%s/mnotes/org-roam/pdf_notes/" mv/nextcloud-base)
                    (format "%s/mnotes/org-roam/blog/drafts" mv/nextcloud-base)
                    (format "%s/mnotes/org-roam/blog/posts" mv/nextcloud-base)
                    (format "%s/mnotes/org-roam/presentations/2024-2025/semester1/system-earth" mv/nextcloud-base)
   		    (format "%s/mnotes/org-roam/presentations/2024-2025/semester1/intro-energy-transition" mv/nextcloud-base)
                    )))

(defun pdf_library_search ()
  (interactive)
  (file-search-mv (list (format "%s/pdf_library/" mv/nextcloud-base))))

(defun books_search ()
  (interactive)
  (file-search-mv (list (format "%s/pdf_library/books" mv/nextcloud-base))))
(defun extra_search ()
  (interactive)
  (file-search-mv (list (format "%s/pdf_library/extra" mv/nextcloud-base))))

#+end_src
** Keybindings
#+begin_src emacs-lisp
(spc-define-keys spc-leader-map
  "onn" 'create_mnotes
  "oom" 'mnotessearch-simple
  "oob" 'books_search
  "oop" 'pdf_library_search
  "ooe" 'extra_search
  )

#+end_src
* Dirvish
https://github.com/alexluigit/dirvish/blob/main/docs/CUSTOMIZING.org
** Basics
#+begin_src emacs-lisp
;; (package-install 'dirvish) ;;nixos
(require 'dirvish)
(dirvish-override-dired-mode)
(setq dired-listing-switches "-lta")
(add-hook 'dirvish-find-entry-hook
          (lambda (&rest _) (setq-local truncate-lines t)))
;; To also disable truncated lines in the directory preview (which is in fundamental mode):
(add-hook 'after-change-major-mode-hook
          (lambda ()
	    (if (eq major-mode 'fundamental-mode)
                (setq truncate-lines t))))

(setq dirvish-reuse-session 'resume)

;; (setq dirvish-preview-dispatchers
;;       (cl-substitute 'pdf-preface 'pdf dirvish-preview-dispatchers))

;; (package-install 'all-the-icons) ;;nixos
(require 'all-the-icons)
(setq dirvish-attributes
      '(file-time file-size))

(setq dirvish-default-layout '(1 0.11 0.35))
(setq dirvish-yank-overwrite-existing-files 'backup)
(setq dirvish-yank-new-name-style 'append-to-filename)
#+end_src
** Functions
#+begin_src emacs-lisp
(defun mv/open-external-application ()
  (interactive)
  (let* ((file (dired-get-file-for-visit))
         (applications (directory-files "/usr/bin/" t))
         (chosen-app (ivy-read "Choose application: " applications)))
    (start-process "dirvish-open-with" nil chosen-app file)))


(defun mv/open-external-application ()
  (interactive)
  (let* ((file (dired-get-file-for-visit))
         (applications (directory-files (file-name-directory (executable-find "env")) t))
         (chosen-app (ivy-read "Choose application: " applications)))
    (start-process "dirvish-open-with" nil chosen-app file)))


(defun mv/check-mail-buffer ()
  (if (eq major-mode 'mu4e-compose-mode)
      (setq compose-buffer (buffer-name))
    (setq compose-buffer nil)))

(defun dirvish-nix-home ()
  (interactive)
  (mv/check-mail-buffer)
  (dirvish "/etc/nixos/"))

(defun dirvish-home ()
  (interactive)
  (mv/check-mail-buffer)
  (dirvish "/home/misha/"))

(defun dirvish-other-config ()
  (interactive)
  (mv/check-mail-buffer)
  (dirvish "/etc/nixos/other-config/"))

(defun dirvish-nextcloud ()
  (interactive)
  (mv/check-mail-buffer)
  (dirvish "/home/misha/Nextcloud/"))

(defun dirvish-presentations ()
  (interactive)
  (mv/check-mail-buffer)
  (dirvish "/home/misha/Nextcloud/mnotes/org-roam/presentations/2024-2025/semester2"))

(defun dirvish-website ()
  (interactive)
  (mv/check-mail-buffer)
  (dirvish "/home/misha/Nextcloud/mishathings-website/content"))

(defun dirvish-images ()
  (interactive)
  (mv/check-mail-buffer)
  (dirvish "/home/misha/Nextcloud/mnotes/org-roam/presentations/images"))

(defun dirvish-auc ()
  (interactive)
  (mv/check-mail-buffer)
  (dirvish "/home/misha/Nextcloud/UvA/AUC/2024-2025/semester2/"))

(defun dirvish-mnotes ()
  (interactive)
  (mv/check-mail-buffer)
  (dirvish "/home/misha/Nextcloud/mnotes/"))

(defun dirvish-pdf-library ()
  (interactive)
  (mv/check-mail-buffer)
  (dirvish "/home/misha/Nextcloud/pdf_library/"))

(defun dirvish-git ()
  (interactive)
  (mv/check-mail-buffer)
  (dirvish "/home/misha/git/"))

(defun dirvish-books ()
  (interactive)
  (mv/check-mail-buffer)
  (dirvish "/home/misha/Nextcloud/pdf_library/books"))

(defun dirvish-extra ()
  (interactive)
  (mv/check-mail-buffer)
  (dirvish "/home/misha/Nextcloud/pdf_library/extra"))

(defun dirvish-download ()
  (interactive)
  (mv/check-mail-buffer)
  (dirvish "/home/misha/Downloads"))

(defun dirvish-send-attach ()
  (interactive)
  (mv/check-mail-buffer)
  (dirvish "/home/misha/Documents/attach"))

(defun dirvish-any-dir-Nextcloud ()
  (interactive)
  (mv/check-mail-buffer)
  (dirvish (ivy-read "Pick dir: " (split-string (shell-command-to-string "find /home/misha/Nextcloud -type d") "\n"))))
;; (defun dirvish-fd-ask-nextcloud ()
;;   (interactive)
;;   (dirvish-fd-ask "/home/misha/Nextcloud/" (read-from-minibuffer "Pattern: ")))

(defun dirvish-resume ()
  (interactive)
  (mv/check-mail-buffer)
  (setq dirvish-reuse-session 'resume)
  (dirvish))

(defun dirvish-open-folder-of-file ()
  (interactive)
  (setq dirvish-reuse-session t)
  (if (eq major-mode 'shell-mode)
      (dirvish (string-trim-right (dirs)))
    (dirvish)))
(defun dirvish-shell ()
  (interactive)
  ;; kill open shells (processes and buffers)
  (let ((kill-buffer-query-functions
         (remq 'process-kill-buffer-query-function
               kill-buffer-query-functions)))
    (dolist (process (process-list))
      (when (string-match-p "shell" (process-name process))
        (let ((buffer (process-buffer process)))
          (when buffer
            (kill-buffer buffer)))
        (delete-process process))))
  (if (eq major-mode 'dired-mode)
      (progn
        (shell)
        (delete-other-windows))
    (shell default-directory)))
;; This gives the pdf preview the familiar keybindings when I want to walk through the preview
(defun dirvish-switch-window () 
  (interactive)
  "i2r"
  (if (or (eq major-mode 'dired-mode) (eq major-mode 'wdired-mode))
      (progn
        (other-window 1)
        (load-keys))
    (progn 
      (save-buffer)
      (other-window 1)
      (if (eq major-mode 'pdf-view-mode)
	  (progn
	    (if mv-midnight-mode
		(progn
		  (pdf-view-midnight-minor-mode)
		  (pdf-view-midnight-minor-mode))))
	
	;; (if (not (eq major-mode 'wdired-mode))
	;; (save-buffer))
	))))
(defun dirvish-layout-switch-mv ()
  (interactive)
  (dirvish-layout-switch '(0 0 0.8)))

;; (defun dirvish-open-xdg ()
;;   (interactive)
;;   (shell-command (format "xdg-open \"%s\"" (dired-get-file-for-visit))))

;; (defun dirvish-open-nautilusf ()
;;   (interactive)
;;   (if (eq major-mode 'dired-mode)
;;       (progn 
;; 	(let* ((path (dired-get-file-for-visit))
;; 	       (path (if (file-exists-p path)
;; 			 (file-name-directory path) path)))
;; 	  (async-shell-command (format "nautilus \"%s\" &" path))))
;;     (async-shell-command (format "nautilus \"%s\" &" (buffer-file-name)))))

(defun dirvish-open-nautilus ()
  (interactive)
  (if (eq major-mode 'dired-mode)
      (progn 
        (let* ((path (dired-get-file-for-visit))
	       (path (if (file-exists-p path)
                         (file-name-directory path) path)))
          (async-shell-command (format "nautilus \"%s\" &" path)
			       (generate-new-buffer-name "*nautilus-async*"))))
    (async-shell-command (format "nautilus \"%s\" &" (buffer-file-name))
                         (generate-new-buffer-name "*nautilus-async*"))))

(defun dirvish-history-jump ()
  "Open a target directory from `dirvish--history'."
  (interactive)
  (unless dirvish--history (user-error "Dirvish[error]: no history entries"))
  (let* ((result (ivy-read "Recently visited: " dirvish--history :sort nil)))
    (when result (dirvish-find-entry-a result))))

(defalias 'rename-in-dirvish
  (kmacro "i 0 w w v t . c"))

(defalias 'change-name-dirvish
  (kmacro "0 t . l i"))
(defalias 'change-name-dirvish-folder
  (kmacro "A"))

(defun change-name-dirvish2 ()
  (interactive)
  (wdired-change-to-wdired-mode)
  (beginning-of-line)
  (let ((eol (save-excursion (end-of-line) (point))))
    (if (search-forward "." eol t)
        (change-name-dirvish)
      (change-name-dirvish-folder))))

#+end_src
** Keybindings
#+begin_src emacs-lisp
(evil-define-key 'normal dired-mode-map (kbd "SPC") nil)
(evil-define-key 'normal dired-mode-map (kbd "q") 'dirvish-quit)
(evil-define-key 'normal dired-mode-map (kbd "h") 'dired-up-directory)
(evil-define-key 'normal dired-mode-map (kbd "l") 'dired-find-file)
(evil-define-key 'normal dired-mode-map (kbd "M") 'dirvish-move)
(evil-define-key 'normal dired-mode-map (kbd "Y") 'dirvish-yank)
(evil-define-key 'normal dired-mode-map (kbd "C") 'dired-create-directory)
(evil-define-key 'normal dired-mode-map (kbd "T") 'dired-create-empty-file)
(evil-define-key 'normal dired-mode-map (kbd "yp") 'dirvish-copy-file-path)
(evil-define-key 'normal dired-mode-map (kbd "yn") 'dirvish-copy-file-name)
(evil-define-key 'normal dired-mode-map (kbd "F") 'browse-url-of-dired-file)
(evil-define-key 'normal dired-mode-map (kbd "c") nil)
(evil-define-key 'normal dired-mode-map (kbd "cw") 'rename-in-dirvish)
(evil-define-key 'normal dired-mode-map (kbd "H") 'dirvish-open-nautilus)
(evil-define-key 'normal dired-mode-map (kbd "a") 'change-name-dirvish2)
(evil-define-key 'normal dired-mode-map (kbd "r") nil)
(evil-define-key 'normal dired-mode-map (kbd "r") 'revert-buffer)
(evil-define-key 'normal dired-mode-map (kbd "e") 'mv/process-marked-files-as-attachments)

(spc-define-keys spc-leader-map
  "dn" 'dirvish-nextcloud
  "dy" 'dirvish-presentations
  "di" 'dirvish-images
  "dt" 'dirvish-website
  "du" 'dirvish-auc
  "dd" 'dirvish-download
  "dm" 'dirvish-mnotes
  "dp" 'dirvish-pdf-library
  "dg" 'dirvish-git
  "db" 'dirvish-books
  "de" 'dirvish-extra
  "do" 'dirvish-any-dir-Nextcloud
  "dh" 'dirvish-history-jump
  "df" 'dirvish-fd-ask-nextcloud
  "d." 'dirvish-open-folder-of-file
  "dr" 'dirvish-resume
  "ts" 'dirvish-shell
  "tp" 'run-python
  "d/" 'dirvish-narrow
  "dw" 'dirvish-switch-window
  "d0" 'dirvish-nix-home
  "d1" 'dirvish-home
  "d2" 'dirvish-other-config
  "sas" 'dirvish-send-attach
  )
#+end_src
** Open with external program
#+begin_src emacs-lisp
(setq dirvish-open-with-programs '((("ape" "stm" "s3m" "ra" "rm" "ram" "wma" "wax" "m3u" "med" "669"
                                     "mtm" "m15" "uni" "ult" "mka" "flac" "axa" "kar" "midi" "mid" "s1m"
                                     "smp" "smp3" "rip" "multitrack" "ecelp9600" "ecelp7470" "ecelp4800"
                                     "vbk" "pya" "lvp" "plj" "dtshd" "dts" "mlp" "eol" "uvva" "uva"
                                     "koz" "xhe" "loas" "sofa" "smv" "qcp" "psid" "sid" "spx" "opus"
                                     "ogg" "oga" "mp1" "mpga" "m4a" "mxmf" "mhas" "l16" "lbc" "evw"
                                     "enw" "evb" "evc" "dls" "omg" "aa3" "at3" "atx" "aal" "acn" "awb"
                                     "amr" "ac3" "ass" "aac" "adts" "726" "abs" "aif" "aifc" "aiff" "au"
                                     "mp2" "mp3" "mp2a" "mpa" "mpa2" "mpega" "snd" "vox" "wav")
                                    #1="/usr/bin/mpv" "--profile=builtin-pseudo-gui" "%f")
                                   (("f4v" "rmvb" "wvx" "wmx" "wmv" "wm" "asx" "mk3d" "mkv" "fxm" "flv"
                                     "axv" "webm" "viv" "yt" "s1q" "smo" "smov" "ssw" "sswf" "s14" "s11"
                                     "smpg" "smk" "bk2" "bik" "nim" "pyv" "m4u" "mxu" "fvt" "dvb" "uvvv"
                                     "uvv" "uvvs" "uvs" "uvvp" "uvp" "uvvu" "uvu" "uvvm" "uvm" "uvvh"
                                     "uvh" "ogv" "m2v" "m1v" "m4v" "mpg4" "mp4" "mjp2" "mj2" "m4s"
                                     "3gpp2" "3g2" "3gpp" "3gp" "avi" "mov" "movie" "mpe" "mpeg" "mpegv"
                                     "mpg" "mpv" "qt" "vbs")
                                    #1# "%f")
                                   (("odt" "odp" "docx" "doc" "ods" "xlsx" "pptx" "csv") #1="xdg-open" "%f")
                                   ))
(dirvish-define-preview exa (file)
  "Use `exa' to generate directory preview."
  :require ("exa") ; tell Dirvish to check if we have the executable
  (when (file-directory-p file) ; we only interest in directories here
    `(shell . ("exa" "-lr" "--color=always" "--sort=newest" ,file))))
(add-to-list 'dirvish-preview-dispatchers 'exa)
;;(setq-default truncate-lines t)
#+end_src

* PDF 
** Basics
#+begin_src emacs-lisp
;; (package-install 'pdf-tools) ;;nixos
(pdf-tools-install) 

;; this disables the useless visual state when viewing a pdf
(with-eval-after-load 'pdf-view
  (evil-define-key 'visual pdf-view-mode-map
    "y" 'pdf-view-kill-ring-save
    (kbd "<C-down-mouse-1>") 'pdf-view-mouse-extend-region
    (kbd "<M-down-mouse-1>") 'pdf-view-mouse-set-region-rectangle
    (kbd "<down-mouse-1>")  'pdf-view-mouse-set-region))


(evil-set-initial-state 'pdf-view-mode 'emacs)
(add-hook 'pdf-view-mode-hook
          (lambda ()
            (set (make-local-variable 'evil-emacs-state-cursor) (list nil))
	    (blink-cursor-mode -1)))
(add-hook 'pdf-view-mode-hook (lambda () (run-with-timer 0.1 nil #'evil-emacs-state)))
#+end_src
** Functions
*** translate
#+begin_src emacs-lisp
(defun mvr/translate-pdf ()
  (interactive)
  (pdf-view-kill-ring-save)
  (let ((to-translate (current-kill 0)))    ; get last entry from kill ring
    (google-translate-translate "auto" "en" to-translate 'help))
  (switch-to-buffer-other-window "*Help*"))

#+end_src
*** other
#+begin_src emacs-lisp
(defun red_highlight (arg)
  (interactive (list (pdf-view-active-region t)))
  (pdf-annot-add-markup-annotation arg 'highlight '"red"))

;; Daniel made this one for me in the beginning to search pdfs
(defun pdf-occur (string &optional regexp-p)
  "List lines matching STRING or PCRE.

           Interactively search for a regexp. Unless a prefix arg was given,
           in which case this functions performs a string search.

           If `pdf-occur-prefer-string-search' is non-nil, the meaning of
           the prefix-arg is inverted."
  (interactive
   (progn
     (pdf-util-assert-pdf-buffer)
     (list
      (pdf-occur-read-string
       (pdf-occur-want-regexp-search-p))
      (pdf-occur-want-regexp-search-p))))
  (pdf-util-assert-pdf-buffer)
  (pdf-view-fit-width-to-window)
  (pdf-occur-search (list (current-buffer)) string regexp-p)
  (select-window (get-buffer-window "*PDF-Occur*"))
  (next-error-follow-minor-mode)
  (sit-for 1)
  (pdf-occur-view-occurrence)
  )

(defun pdf-view-next-line-or-next-page-and-switcht-to-emacs ()
  (interactive)
  (evil-emacs-state nil)
  (pdf-view-next-line-or-next-page 1))

(defun pdf-view-next-line-or-next-page-and-switch-to-emacs ()
  (interactive)
  (evil-emacs-state nil)
  (pdf-view-next-line-or-next-page 1))

(defun pdf-view-scroll-up-or-next-page-and-switch-to-emacs ()
  (interactive)
  (evil-emacs-state nil)
  (pdf-view-scroll-up-or-next-page 1))

(defun pdf_link_page ()
  (interactive)
  (let ((page (pdf-view-current-page))
        (path (buffer-file-name)))
    (kill-new (format "[[pdf:%s::%s][]]" path page))
    ))

(defun evil-collection-pdf-view-goto-last-page ()
  (interactive)
  (pdf-view-goto-page (pdf-cache-number-of-pages)))

#+end_src
*** Use doi
#+begin_src emacs-lisp
(defun substring-from-first-number (str)
  (interactive)
  (when (string-match "[0-9]" str)
    (substring str (match-beginning 0))))

(defun rename-from-doi ()
  (interactive)
  (let* ((doi (if mark-active (progn (pdf-view-kill-ring-save) (car kill-ring)) (ivy-read "select doi: " (split-string (shell-command-to-string (format "pdftotext -f %s -l %s \"%s\" /tmp/pdftotext.txt && cat /tmp/pdftotext.txt | grep -i doi" (pdf-view-current-page) (pdf-view-current-page) (buffer-file-name))) "\n"))))
         (doi (read-from-minibuffer "Adjust doi: " doi))
         (doi (if (not (string= (substring doi 0 4) "http")) (format "http://dx.doi.org/%s" (substring-from-first-number doi)) doi)))
    (shell-command-to-string (format "curl -LH \"Accept: application/json\" %s > /tmp/article-json" doi))     
    (let* ((names (split-string (shell-command-to-string "jq -r '.. | objects | select(.family) | .family' /tmp/article-json") "\n"))
           ;;(names (butlast names))
           (name (if (> (length names) 3)
                     (format "%s et al." (car names))
                   (if (> (length names) 2)
                       (format "%s & %s" (car names) (nth 1 names))
                     (format "%s" (car names)))))
           (title (replace-regexp-in-string ":" "-" (shell-command-to-string "jq -r .title /tmp/article-json | tr -d \"\n\"")))
           (year (shell-command-to-string "cat /tmp/article-json | jq -r '.license[0].start[\"date-time\"]' | cut -f1 -d \"-\" | tr -d \"\n\"")))
      (rename-mv (format "%s - %s - %s.pdf" name year title))
      )))

(defun rename-from-doi ()
  (interactive)
  (let* ((doi
          (if mark-active
              (progn (pdf-view-kill-ring-save) (car kill-ring))
            (ivy-read "select doi: " (split-string (shell-command-to-string (format "pdftotext -f %s -l %s \"%s\" /tmp/pdftotext.txt && cat /tmp/pdftotext.txt | grep -i doi" (pdf-view-current-page) (pdf-view-current-page) (buffer-file-name))) "\n"))))
         (doi (read-from-minibuffer "Adjust doi: " doi))
         (doi (if (not (string= (substring doi 0 4) "http"))
                  (format "http://dx.doi.org/%s" (substring-from-first-number doi))
                doi)))
    (shell-command-to-string (format "curl -LH \"Accept: application/json\" %s > /tmp/article-json" doi))     
    (let* ((names (split-string (shell-command-to-string "jq -r '.. | objects | select(.family) | .family' /tmp/article-json") "\n"))
           ;;(names (butlast names))
           (name (if (> (length names) 3)
                     (format "%s et al." (car names))
                   (if (> (length names) 2)
                       (format "%s & %s" (car names) (nth 1 names))
                     (format "%s" (car names)))))
           (title (replace-regexp-in-string ":" "-" (shell-command-to-string "jq -r .title /tmp/article-json | tr -d \"\n\"")))
           (year (shell-command-to-string "cat /tmp/article-json | jq -r '.license[0].start[\"date-time\"]' | cut -f1 -d \"-\" | tr -d \"\n\"")))
      (rename-mv (format "%s - %s - %s.pdf" name year title)))))

(defun rename-from-doi-manual ()
  (interactive)
  (let* ((doi (read-from-minibuffer "doi: "))
         (doi (if (not (string= (substring doi 0 4) "http")) (format "http://dx.doi.org/%s" (substring-from-first-number doi)) doi)))
    (shell-command-to-string (format "curl -LH \"Accept: application/json\" %s > /tmp/article-json" doi))     
    (let* ((names (split-string (shell-command-to-string "jq -r '.. | objects | select(.family) | .family' /tmp/article-json") "\n"))
           ;;(names (butlast names))
           (name (if (> (length names) 3)
                     (format "%s et al." (car names))
                   (if (> (length names) 2)
                       (format "%s & %s" (car names) (nth 1 names))
                     (format "%s" (car names)))))
           (title (replace-regexp-in-string ":" "-" (shell-command-to-string "jq -r .title /tmp/article-json | tr -d \"\n\"")))
           (year (shell-command-to-string "cat /tmp/article-json | jq -r '.license[0].start[\"date-time\"]' | cut -f1 -d \"-\" | tr -d \"\n\"")))
      (rename-mv (format "%s - %s - %s.pdf" name year title))
      )))

(defun get-citation-from-doi-manual-input ()
  (interactive)
  (let* ((doi (read-from-minibuffer "doi: "))
         (doi (if (not (string= (substring doi 0 4) "http")) (format "http://dx.doi.org/%s" (substring-from-first-number doi)) doi)))

    (shell-command-to-string (format "curl -LH \"Accept: text/x-bibliography; style=apa\" %s > /tmp/cite" doi))     
    (let ((cite (shell-command-to-string "cat /tmp/cite")))
      (message cite)
      (kill-new cite))))
#+end_src
** Keybindings 
#+begin_src emacs-lisp
;; this makes SPC available
;; (add-hook 'pdf-view-mode-hook
;; 	  (lambda ()
;; 	    (evil-define-key 'normal pdf-view-mode-map (kbd "SPC") nil)))
;; (add-hook 'pdf-view-mode-hook
;; 	  (lambda ()
;; 	    (evil-define-key 'emacs pdf-view-mode-map (kbd "SPC") nil)))

(defun load-keys ()
  (interactive)
  (define-key evil-emacs-state-local-map "e" 'pdf-view-previous-line-or-previous-page)
  (define-key evil-normal-state-local-map "w" 'pdf-view-next-line-or-next-page-and-switch-to-emacs)
  (define-key evil-emacs-state-local-map "w" 'pdf-view-next-line-or-next-page)
  (define-key evil-emacs-state-local-map "k" 'pdf-view-previous-line-or-previous-page)
  (define-key evil-emacs-state-local-map "j" 'pdf-view-next-line-or-next-page)
  (define-key evil-emacs-state-local-map "1" 'pdf-annot-add-underline-markup-annotation)
  (define-key evil-emacs-state-local-map "2" 'pdf-annot-add-highlight-markup-annotation)
  (define-key evil-emacs-state-local-map "4" 'pdf-annot-delete)
  (define-key evil-emacs-state-local-map "5" 'red_highlight)
  (define-key evil-emacs-state-local-map "s" 'org-noter-insert-note)
  (define-key evil-emacs-state-local-map "r" 'pdf-view-scroll-down-or-previous-page)
  (define-key evil-emacs-state-local-map "q" 'pdf-view-scroll-up-or-next-page)
  (define-key evil-emacs-state-local-map "y" 'pdf-view-kill-ring-save)
  (define-key evil-normal-state-local-map "q" 'pdf-view-scroll-up-or-next-page-and-switch-to-emacs)
  (define-key evil-emacs-state-local-map "f" 'save-noter)
  (define-key evil-emacs-state-local-map "gt" 'pdf-view-goto-page)
  (define-key evil-emacs-state-local-map "aa" 'mvr/translate-pdf)
  (define-key evil-emacs-state-local-map "o" 'pdf-outline)
  (define-key evil-emacs-state-local-map "9" 'close-noter)
  (define-key evil-emacs-state-local-map "d" 'define-word-at-point)
  (define-key evil-emacs-state-local-map "gg" 'evil-collection-pdf-view-goto-first-page)
  (define-key evil-emacs-state-local-map "G" 'evil-collection-pdf-view-goto-last-page)
  (define-key evil-emacs-state-local-map "[" 'evil-collection-pdf-jump-backward)
  )

(add-hook 'pdf-view-mode-hook 'load-keys)
(define-key pdf-view-mode-map (kbd "SPC") nil)
(define-key pdf-view-mode-map (kbd "SPC") 'spc-root)

;; (define-key evil-emacs-state-map "e" 'pdf-view-previous-line-or-previous-page)
;; (define-key evil-emacs-state-map "w" 'pdf-view-next-line-or-next-page)
;; (define-key evil-emacs-state-map "j" 'pdf-view-previous-line-or-previous-page)
;; (define-key evil-emacs-state-map "k" 'pdf-view-next-line-or-next-page)
;; (define-key evil-emacs-state-map "1" 'pdf-annot-add-underline-markup-annotation)
;; (define-key evil-emacs-state-map "4" 'pdf-annot-delete)
;; (define-key evil-emacs-state-map "5" 'red_highlight)
;; (define-key evil-emacs-state-map "s" 'org-noter-insert-note)
;; (define-key evil-emacs-state-map "q" 'pdf-view-scroll-down-or-previous-page)
;; (define-key evil-emacs-state-map "r" 'pdf-view-scroll-up-or-next-page)

(spc-define-keys spc-leader-map
  "mss" 'pdf-occur
  "fh" 'pdf-view-fit-height-to-window
  "fw" 'pdf-view-fit-width-to-window
  "1" 'pdf-annot-add-underline-markup-annotation
  "2" 'pdf-annot-add-highlight-markup-annotation
  "4" 'pdf-annot-delete
  "5" 'red_highlight
  "." 'sync-note-mv)

(add-hook 'pdf-view-mode-hook
          (lambda ()
            (local-set-key (kbd "C-c 1") 'pdf-annot-add-underline-markup-annotation)
            (local-set-key (kbd "C-c 4") 'pdf-annot-delete)
            (local-set-key (kbd "C-c 5") 'red_highlight)
            (local-set-key "j" nil)
            (local-set-key "j" 'pdf-view-next-line-or-next-page)
            (local-set-key (kbd "C-c s") 'org-noter-insert-note)))

#+end_src
** Adjust mode line
#+begin_src emacs-lisp
(defvar old-mode-line-format mode-line-format)
(defun my-pdf-view-page-count ()
  (if (eq major-mode 'pdf-view-mode)
      (let ((my-window-number (winum-get-number-string))
            (my-file-name (buffer-name))
            (my-state (cond ((eq evil-state 'normal) "N")
                            ((eq evil-state 'emacs) "E")
                            (t (symbol-name evil-state)))))
        (format "%s | <%s> | %d/%d | %s"
                my-window-number
                my-state
                (pdf-view-current-page)
                (pdf-cache-number-of-pages)
                my-file-name))
    old-mode-line-format))

(setq-default mode-line-format
              '(:eval (my-pdf-view-page-count)))
#+end_src

* ORG
#+begin_src emacs-lisp
(require 'org)
#+end_src
** Settings
*** Auto update inline images
For example for when you're running code and immediately want to check the output.
#+begin_src emacs-lisp
(eval-after-load 'org
  (add-hook 'org-babel-after-execute-hook 'org-redisplay-inline-images))
#+end_src
*** Manual image resize in org available
#+begin_src emacs-lisp
(setq org-image-actual-width nil)
#+end_src
*** Indentation
#+begin_src emacs-lisp
(setq org-indent-indentation-per-level 2)
#+end_src
*** Babel
#+begin_src emacs-lisp
(org-babel-do-load-languages
 'org-babel-load-languages
 '(
   ;;(racket . t)
   (python . t)
   (shell . t)
   ;;(jupyter .t)
   ))
#+end_src
*** Indent and visual line mode  
#+begin_src emacs-lisp
(add-hook 'org-mode-hook
          (lambda ()
            (org-indent-mode)
            (visual-line-mode)))    
#+end_src
*** Python in org
#+begin_src emacs-lisp
(setq org-babel-python-command "python3")
#+end_src
*** Opening links with RET
#+begin_src emacs-lisp
(with-eval-after-load 'evil-maps
  (define-key evil-motion-state-map (kbd "RET") nil))
(setq org-return-follows-link t)
#+end_src
*** Source blocks
**** Fix indentation in source blocks
#+begin_src emacs-lisp
(defun python-org-mode ()
  (setq-local indent-line-function 'python-indent-line-function))
(add-hook 'org-mode-hook 'python-org-mode)
#+end_src
*** Open org-edit-special in split window
#+begin_src emacs-lisp
(defun my/org-edit-special-in-split ()
  "Open `org-edit-special` in a split window at the bottom."
  (interactive)
  (let ((org-src-window-setup 'split-window-below))
    (org-edit-src-code)))

(define-key org-mode-map (kbd "C-c '") 'my/org-edit-special-in-split)
#+end_src
** Functions
*** search headers (ji)
#+begin_src emacs-lisp
(defun ji-mv ()
  (interactive)
  (add-hook 'minibuffer-exit-hook 'turn-on-prescient-mode-mv)
  (ivy-prescient-mode -1)
  (if (eq major-mode 'org-mode)
      (counsel-org-goto)
    (if (eq major-mode 'mu4e-headers-mode)
        (select-mu4e-box)
      (if (eq major-mode 'latex-mode)
          (swiper "section ")))))
#+end_src
*** org themes
**** create-theme
#+begin_src emacs-lisp
(defun mv/create-theme ()
  (interactive)
  (if (bolp)
      (insert (format "tt_%s" (replace-regexp-in-string " " "_" (read-from-minibuffer "Enter theme: "))))
    (insert (format " tt_%s" (replace-regexp-in-string " " "_" (read-from-minibuffer "Enter theme: "))))))
#+end_src
**** get-themes
#+begin_src emacs-lisp
(defun mv/get-themes ()
  (let ((re "tt_\\S-+") ; updated regex to avoid trailing spaces
        forms
        matches)
    (save-excursion
      (goto-char (point-min))
      (while (re-search-forward re nil t)
        (push (match-string-no-properties 0) forms)))
    (setq theme-matches (delete-dups forms))))
#+end_src
**** insert-themes
#+begin_src emacs-lisp
(defun mv/insert-themes ()
  (interactive)
  (mv/get-themes)
  (if (bolp) 
      (insert (format "%s" (ivy-read "Matches: " theme-matches)))
    (insert (format " %s" (ivy-read "Matches: " theme-matches)))))
#+end_src
**** filter-themes
#+begin_src emacs-lisp
(defun mv/filter-themes ()
  (interactive)
  (mv/get-themes)
  (org-occur (ivy-read "Matches: " theme-matches)))
#+end_src
*** Paste link
#+begin_src emacs-lisp 
(spc-define-keys spc-leader-map "ol" 'paste-link)
(defun paste-link ()
  (interactive)
  (if (or (string= major-mode "org-mode") (string= major-mode "mu4e-compose-mode"))
      (progn
        (if (= (length (buffer-substring-no-properties (line-beginning-position) (line-end-position))) 0)
            (progn
              (insert (format "[[%s][]]" (substring-no-properties (current-kill 0 t))))
              (evil-backward-char 2)
              (evil-append 1)
              (print "yes"))
          (progn
            (insert (format " [[%s][]]" (substring-no-properties (current-kill 0 t))))
            (evil-backward-char 3)
            (evil-append 1)
            )
          ))
    (if (string= major-mode "mail-mode")
        (progn
          (insert (format " [](%s)" (substring-no-properties (current-kill 0 t))))
          ;; (evil-find-char-backward 1 ?\[)
          ;; (evil-append 1)
          ))))
#+end_src
*** Paste image
#+begin_src emacs-lisp
(defun image-enable ()
  (interactive)
  (org-display-inline-images t t))

(defun mv/buffer-has-path ()
  "Check if the open buffer has a specific path in its full path."
  (let ((buffer-path (buffer-file-name)))
    (if buffer-path
	(string-match-p "/home/misha/Nextcloud/mnotes/org-roam/presentations" buffer-path)
      nil)))

(defun insert-image-mv ()
  (interactive)
  (let* ((path (substring-no-properties (current-kill 0 t)))
         (filename (file-name-nondirectory path)))
    (if (mv/buffer-has-path)
        (insert (format "#+ATTR_HTML: :width 350\n[[./../../../images/%s]]" filename))
      (insert (format "#+ATTR_HTML: :width 350\n[[%s]]" path)))))

(defun insert-image-stretch-mv ()
  (interactive)
  (let ((var (file-name-nondirectory (substring-no-properties (current-kill 0 t)))))
    (if (string-match-p "images" var)
        (insert (format "#+REVEAL_HTML: <img class=\"r-stretch\" src=\"%s\">" var))
      (insert (format "#+REVEAL_HTML: <img class=\"r-stretch\" src=\"./../../../images/%s\">" var)))))
#+end_src
*** Internal reference
#+begin_src emacs-lisp
(defun buffer-contains-substring (string)
  (save-excursion
    (save-match-data
      (goto-char (point-min))
      (search-forward string nil t))))
(defun internal-reference-mv ()
  (interactive)
  (let ((num 1))
    (while (buffer-contains-substring (format "i%sr" (number-to-string num)))
      (message (format "i%sr" (number-to-string num)))
      (setq num (+ num 1)))
    (insert (format "i%sr" (number-to-string num)))
    (kill-new (format "i%sr" (number-to-string num)))))
#+end_src
*** org-meta-return
**** Check if dash or number
#+begin_src emacs-lisp
(defun first-char-match ()
  (interactive)
  (save-excursion
    (beginning-of-line)
    (skip-chars-forward "[:space:]")
    (looking-at "[-0-9]")))
#+end_src
**** down
#+begin_src emacs-lisp
(defun org-meta-return-mv-down ()
  (interactive)
  (if (first-char-match)
      (progn
        (if (or (eq major-mode 'org-mode) (eq major-mode 'org-msg-edit-mode))
            (progn
              (end-of-line)
              (evil-append-line 1)
              (org-meta-return)
              (evil-insert 1))))
    (if (eq major-mode 'LaTeX-mode)
        (progn 
          (beginning-of-line)
          (if (looking-at "^\\s-*\\\\item")
              (progn
                (end-of-line)
                (insert "\n\\item ")
                (fix-indent-mv)
                (evil-append 1))
            (evil-open-below 1)))
      (if (org-at-table-p)
          (progn (org-table-next-row)
                 (if (bolp) (evil-forward-char 2))
                 (evil-insert 1))
        (progn
          (evil-append-line 1)
          (newline))))))
#+end_src 
**** up
#+begin_src emacs-lisp
(defun org-meta-return-mv-up ()
  (interactive)
  (if (first-char-match)
      (progn
        (if (or (eq major-mode 'org-mode) (eq major-mode 'org-msg-edit-mode))
            (progn
              (beginning-of-line)
              (evil-insert-line 1)
              (org-meta-return)
              (evil-insert 1))))
    (if (eq major-mode 'latex-mode)
        (progn 
          (beginning-of-line)
          (if (looking-at "^\\s-*\\\\item")
              (progn
                (previous-line)
                (end-of-line)
                (insert "\n\\item ")
                (fix-indent-mv)
                (evil-append 1))
            (evil-open-below 1)))
      (if (org-at-table-p)
          (progn (org-table-insert-row)
                 (if (bolp) (evil-forward-char 2))
                 (evil-insert 1))
        (progn
          (beginning-of-line)
          (insert "\n")
          (previous-line)
          (evil-insert 1))))))
#+end_src
**** COMMENT Next/previous bullet
#+begin_src emacs-lisp
(defun paragraph-first-char-is-dash-p ()
  (interactive)
  (save-excursion
    ;;(evil-backward-sentence-begin)
    (beginning-of-line)
    (skip-chars-forward "[:space:]")
    (looking-at "-")))
(defun org-next-item-mv ()
  (interactive)
  (if (eq major-mode 'latex-mode)
      (progn 
        (beginning-of-line)
        (if (looking-at "^\\s-*\\\\item")
            (progn
              (end-of-line)
                                        ;(TeX-newline)
              (insert "\n\\item ")
              (fix-indent-mv)
              (evil-append 1)
              )
          (evil-open-below 1)
          ))
    (progn
      (if (paragraph-first-char-is-dash-p)
          (progn
            (end-of-line)
            (evil-append 1)
            (org-meta-return))
        (if (org-at-table-p)
            (progn (org-table-next-row)
                   (if (bolp) (evil-forward-char 2))
                   (evil-insert 1))
          (evil-open-below 1))))))


(defun org-previous-item-mv ()
  (interactive)
  (if (paragraph-first-char-is-dash-p)
      (progn
        (beginning-of-line)
        (org-insert-item)
        (evil-append 1)
        )
    (if (org-at-table-p)
        (progn
          (org-table-insert-row)
          (evil-insert 1))
      (evil-open-above 1))))
#+end_src
*** Delete to eol
#+begin_src emacs-lisp
(defun delete-point-eol ()
  (interactive)
  (kill-region (point) (line-end-position))
  (evil-insert 1))
#+end_src
*** Archive done tasks
#+begin_src emacs-lisp
(defun org-archive-done-tasks-subtree ()
  (interactive)
  (org-map-entries
   (lambda ()
     (org-archive-subtree)
     (setq org-map-continue-from (org-element-property :begin (org-element-at-point))))
   "/DONE" 'tree))
(defun org-archive-done-tasks-file ()
  (interactive)
  (org-map-entries
   (lambda ()
     (org-archive-subtree)
     (setq org-map-continue-from (org-element-property :begin (org-element-at-point))))
   "*/DONE" 'file))
#+end_src
*** Update modified attribute
#+begin_src emacs-lisp
(load-file "/etc/nixos/other-config/emacs/update_date_modified.el")
#+end_src
** Keybindings
#+begin_src emacs-lisp
(evil-define-key 'visual org-mode-map "s" 'evil-surround-region)
(spc-define-keys spc-leader-map
  "is" 'org-insert-subheading
  "ih" 'org-insert-heading
  "osl" 'org-store-link
  "oil" 'org-insert-link
  "oit" 'mv/insert-themes
  "oft" 'mv/filter-themes
  "oct" 'mv/create-theme
  "oir" 'internal-reference-mv
  "ii"  'insert-image-mv
  "iy" 'insert-image-stretch-mv
  "it" 'org-toggle-inline-images
  "ie" 'image-enable
  )

(with-eval-after-load 'org
  (setq org-src--preserve-indentation t)
  (setq org-edit-src-content-indentation 0))

;; (define-key evil-normal-state-map "H" 'dirvish-open-nautilus)
(define-key evil-normal-state-map "o" nil)
;;(define-key evil-normal-state-map "o" 'org-next-item-mv)
(define-key evil-normal-state-map "o" 'org-meta-return-mv-down)
(define-key evil-normal-state-map "O" nil)
;;(define-key evil-normal-state-map "O" 'org-previous-item-mv)
(define-key evil-normal-state-map "O" 'org-meta-return-mv-up)
(define-key evil-normal-state-map "C" nil)
(define-key evil-normal-state-map "C" 'delete-point-eol)
(define-key evil-normal-state-map "}" nil)
(define-key evil-normal-state-map "}" 'org-next-link)
(define-key evil-normal-state-map "{" nil)
(define-key evil-normal-state-map "{" 'org-previous-link)
(define-key evil-normal-state-map "[" nil)
(define-key evil-normal-state-map (kbd "SPC [") 'flyspell-goto-previous-error)
(define-key evil-normal-state-map (kbd "SPC ]") 'flyspell-goto-next-error)
#+end_src
* ORG-ROAM 
** Basics
#+begin_src emacs-lisp
;; (package-install 'org-roam) ;;nixos
(require 'org-roam)
;; (package-install 'org-roam-ui) ;;nixos
(require 'org-roam-ui)

(setq org-roam-db-update-on-save nil)
(setq org-roam-ui-browser-function 'browse-url-firefox)

(require 'websocket)

(require 'org-roam-ui)
(setq org-roam-ui-sync-theme t
      org-roam-ui-follow t
      org-roam-ui-update-on-save t
      org-roam-ui-open-on-start t)

(require 'org-roam-protocol)

(setq org-roam-directory "~/Nextcloud/mnotes/org-roam/")
(setq org-roam-file-exclude-regexp ".*drill\.org")

(setq org-roam-dailies-directory "daily/")

(add-to-list 'display-buffer-alist
             '("\\*org-roam\\*"
               (display-buffer-in-side-window)
               (side . right)
               (slot . 0)
               (window-width . 0.33)
               (window-parameters . ((no-other-window . t)
                                     (no-delete-other-windows . t)))
               ))

(setq org-roam-capture-templates '(("d" "default" plain "%?"
                                    :if-new (file+head "${slug}.org"
                                                       "#+TITLE: ${title} \n#+CREATED: %U\n#+LAST_MODIFIED: %U\n\n")
                                    :unnarrowed t)))
(setq org-roam-mode-section-functions
      (list #'org-roam-backlinks-section
            #'org-roam-reflinks-section
            #'org-roam-unlinked-references-section
            ))

(setq org-roam-node-display-template
      ;; (concat "${title:*} " (propertize "${tags:20}" 'face 'org-tag)))
      (concat "${title:100} " (propertize "${tags:20}" 'face 'org-tag)))


(advice-add #'org-roam-fontify-like-in-org-mode :around (lambda (fn &rest args) (save-excursion (apply fn args))))
#+end_src
** Functions
#+begin_src emacs-lisp
(defun find-org-roam-mv ()
  (interactive)
  (add-hook 'minibuffer-exit-hook 'turn-on-prescient-mode-mv)
  (ivy-prescient-mode -1)
  (org-roam-node-find)
  (ivy-prescient-mode 1))

(defun insert-org-roam-mv ()
  (interactive)
  (add-hook 'minibuffer-exit-hook 'turn-on-prescient-mode-mv)
  (ivy-prescient-mode -1)
  (when (not (bolp))
    (insert " "))
  (org-roam-node-insert)
  (ivy-prescient-mode 1))
#+end_src
** Keybindings 
#+begin_src emacs-lisp
(spc-define-keys spc-leader-map
  "aordt" 'org-roam-dailies-find-today
  "aora" 'add-annotation-roam
  "aorA" 'org-roam-alias-add
  "aorf" 'find-org-roam-mv
  "aorl" 'org-roam-buffer-toggle
  "aori" 'insert-org-roam-mv
  "aort" 'org-roam-tag-add
  "aoru" 'org-roam-db-sync
  )
#+end_src

* ORG-NOTER
** Loading files
#+begin_src emacs-lisp
(load-file (format "%s/other-config/emacs/org-noter/org-noter.el" mv/nixos-base-folder))
(load-file (format "%s/other-config/emacs/org-noter/org-pdftools.el" mv/nixos-base-folder))
(load-file (format "%s/other-config/emacs/org-noter/org-noter-pdftools.el" mv/nixos-base-folder))
(load-file (format "%s/other-config/emacs/org-noter/org-noter-embed-misha.el" mv/nixos-base-folder))
#+end_src
** Defining functions
#+begin_src emacs-lisp 
(defun mv/add-general-notes ()
  (interactive)
  (evil-goto-first-line)
  (search-forward "NOTER_DOCUMENT")
  (org-next-visible-heading 1)
  (evil-open-above 0)
  (insert "** General notes\n")
  (save-excursion
    (insert "** Annotations")
    ;; (org-next-visible-heading 1)
    ;; (evil-visual-line (point) (point-max))
    (set-mark (point))
    (goto-char (point-max))
    (activate-mark)
    (org-do-demote))
  (evil-open-above 1)
  )

(defun add-annotation-roam ()
  (interactive)
  (org-entry-delete nil "ID")
  (org-entry-delete nil "ROAM_EXCLUDE")
  (org-id-get-create)
  (save-buffer)
  (org-roam-db-sync))

(defun sync-note-mv ()
  (interactive)
  (save-excursion
    ;; (org-previous-visible-heading 1)
    ;; (if (string= (car (last (split-string (what-cursor-position) "="))) "0")
    (if (string= (substring (thing-at-point 'line t) 0 1 ) "*")
        ;; (message "yes")
        (org-open-at-point 1)
      (progn
        (search-backward-regexp "^\*")
        (org-open-at-point 1)))
    (evil-window-next 2)
    ))

(defun close-noter ()
  (interactive)
  (save-buffer)
  (other-window 1)
  (save-buffer)
  ;; (org-noter-kill-session org-noter--session)
  (org-noter-kill-session)
  (kill-buffer))

(setq mv-midnight-mode nil)
(defun toggle-mv-midnight-mode ()
  (interactive)
  "Toggle the value of VAR between t and nil. i2r"
  (if mv-midnight-mode
      (progn 
	(setq mv-midnight-mode nil)
	(command-execute 'pdf-view-midnight-minor-mode))
    (progn
      (setq mv-midnight-mode t)
      (pdf-view-midnight-minor-mode nil)
      (pdf-view-midnight-minor-mode)))
  (message "mv-midnight-mode: %s" mv-midnight-mode))

(defun save-noter ()
  (interactive)
  (save-buffer)
  (other-window 1)
  (save-buffer)
  (other-window 1)
  (if mv-midnight-mode
      (progn
	(pdf-view-midnight-minor-mode)
	(pdf-view-midnight-minor-mode))))

(defun fix-10-noter ()
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (while (search-forward "++9" nil t)
      (replace-match "++9" nil t))
    (goto-char (point-min))
    (while (search-forward "++9" nil t)
      (replace-match "++9" nil t))))
#+end_src
** Set bindings, add hooks
#+begin_src emacs-lisp 
(add-hook 'org-noter-doc-mode-hook
          (lambda ()
            (local-set-key (kbd "C-c a") 'org-noter-insert-note)))
;; This is necessary for going from org to pdf
(add-hook 'org-mode-hook #'org-pdftools-setup-link)

;; This enables clicking from pdf to org
(with-eval-after-load 'pdf-annot
  (add-hook 'pdf-annot-activate-handler-functions #'org-noter-pdftools-jump-to-note))

(spc-define-keys spc-leader-map
  "oaa" 'org-noter
  "9" 'close-noter
  )

#+end_src
** Setting variables
#+begin_src emacs-lisp 
(setq org-noter-default-notes-file-names nil)
(setq org-noter-notes-search-path (list (format "%s/mnotes/org-roam/pdf_notes/" mv/nextcloud-base)))
(setq org-noter-pdftools-markup-pointer-color "#93f086")
(setq org-noter-doc-split-fraction '(0.66 . 0.66))
(setq org-noter-always-create-frame t)
(setq org-noter-notes-window-location 'horizontal-split)
(setq org-noter-closest-tipping-point 0)
(setq org-noter-insert-note-no-questions t)
(setq org-noter-hide-other nil)
#+end_src

* GPTEL
#+begin_src emacs-lisp
;; (package-install 'gptel) ;; nixos
(require 'gptel)
#+end_src
** COMMENT Use UvA-chat
#+begin_src emacs-lisp
(gptel-make-azure "UvAchat"             ;Name, whatever you'd like
  :protocol "https"                     ;Optional -- https is the default
  :host "ai-research-proxy.azurewebsites.net"
  :endpoint "/openai/deployments/gpt4o/chat/completions"
  :stream t                             ;Enable streaming responses
  :key #'gptel-api-key-uva-chat
  :models '(gpt4o))
(setq gptel-log-level 'debug)

(setq gptel-api-key-uva-chat (string-trim-right (shell-command-to-string "pass misc/uvachat")))
#+end_src
** COMMENT Use ollama
#+begin_src emacs-lisp
(setq
 gptel-model 'deepseek-r1:latest
 gptel-backend (gptel-make-ollama "Ollama"
                 :host "localhost:11434"
                 :stream t
                 :models '(deepseek-r1:latest)))
#+end_src
** Use OpenAI
#+begin_src emacs-lisp
(setq gptel-api-key (string-trim-right (shell-command-to-string "pass mail/chatgpt-api-key-emacs")))
(setq gptel-model 'gpt-4o)
#+end_src
** General
#+begin_src emacs-lisp
;; I think this has to be loaded again because markdown is not loaded yet when this is defined? 
(setq gptel-default-mode 'org-mode)
;; window can only split above-below if the window has more than # lines
(setq split-height-threshold nil)
;; window can only split left-right if the window has more than # columns
(setq split-width-threshold 100)
;; (setq gptel-model "gpt-4") (I've set this with customized)
(spc-define-keys spc-leader-map


"ot" 'send-mv)

;; (setq gptel--system-message "You are a large language model living in Emacs and a helpful assistant. Respond concisely. Also, you are very knowledgeable. An expert. Think and respond with confidence.")
#+end_src
** Open window
#+begin_src emacs-lisp
(defun open-chatgtp ()
  (interactive)
  (if (or (string= (buffer-name) "*ChatGPT*")  (string= (buffer-name) "*UvAchat*"))
      (progn 
        (delete-region (point-min) (point-max)) 
	(insert "*** ")
	(evil-append 1))
    (call-interactively 'gptel)))
#+end_src

* ORG-AGENDA
** Disable space
#+begin_src emacs-lisp
(add-hook 'org-agenda-mode-hook
          (lambda ()
            (evil-define-key 'normal org-agenda-mode-map (kbd "SPC") nil)))
(evil-set-initial-state 'org-agenda-mode 'normal)

#+end_src
** Keybindings
#+begin_src emacs-lisp
(add-hook 'org-agenda-mode-hook
          (lambda ()
            (local-set-key (kbd "C-c f")  'org-agenda-later)
            (local-set-key (kbd "C-c b")  'org-agenda-earlier)
            (local-set-key (kbd "C-c .") 'org-agenda-goto-today)
            (local-set-key (kbd "C-c q") 'org-agenda-quit)
            ))
(evil-define-key 'normal org-agenda-mode-map "n" 'org-agenda-later-mv)
(evil-define-key 'normal org-agenda-mode-map "p" 'org-agenda-earlier-mv)
(evil-define-key 'normal org-agenda-mode-map "." 'org-agenda-goto-today)
(evil-define-key 'normal org-agenda-mode-map "q" 'org-agenda-quit)
(evil-define-key 'normal org-agenda-mode-map "J" 'org-agenda-goto-date)
(evil-define-key 'normal org-agenda-mode-map "w" 'org-agenda-week-view)
(evil-define-key 'normal org-agenda-mode-map "d" 'org-agenda-day-view)
(evil-define-key 'normal org-agenda-mode-map "t" 'org-agenda-todo)
(evil-define-key 'normal org-agenda-mode-map "m" 'org-agenda-month-view)
(evil-define-key 'normal org-agenda-mode-map "r" 'org-agenda-redo)
(evil-define-key 'normal org-agenda-mode-map "s" 'org-search-view)

(spc-define-keys spc-leader-map
  "occ" 'my-org-capture
  "oc1" 'org-capture
  "mau" 'mv/caldav-async
  "maq" 'open-cal-mv-quick
  "mad" 'open-cal-mv-thorough
  )

#+end_src
** Setting variables
#+begin_src emacs-lisp

(require 'color)
(setq org-agenda-span 'month)
(setq calendar-week-start-day 1)
(setq org-deadline-warning-days 0)
(setq org-read-date-popup-calendar nil)

(setq org-agenda-files-basic
      (mapcar (lambda (file)
                (format "/home/misha/Nextcloud/calendar/%s/%s" (system-name) file))
              '("personal.org" "work.org" "teaching.org" "bij1.org" "reminders.org" "amor.org")))

#+end_src
** Defining functions  
*** agenda week later and sooner
#+begin_src emacs-lisp
(defun org-agenda-later-mv ()
  (interactive)
  (org-agenda-later 1)
  (run-with-timer 0.1 nil #' evil-goto-first-line))

(defun org-agenda-earlier-mv ()
  (interactive)
  (org-agenda-earlier 1)
  (run-with-timer 0.1 nil #' evil-goto-first-line))
#+end_src
*** Colombia functions
#+begin_src emacs-lisp 
(setq in-colombia nil)
(defun toggle-colombia ()
  (interactive)
  "Toggle the value of VAR between t and nil."
  (if in-colombia
      (setq in-colombia nil)
    (setq in-colombia t))
  (message "in-colombia: %s" in-colombia))
(defun timezone-diff-mv ()
  (interactive)
  (let* ((hours1 (string-to-number (substring (shell-command-to-string "echo -n $(TZ=\"Europe/Amsterdam\" date +%z)") 1 3)))
         (hours2 (string-to-number (substring (shell-command-to-string "echo -n $(TZ=\"America/Bogota\" date +%z)") 1 3)))
         (diff (+ hours1 hours2)))
    diff))
(defun timezone-mv ()
  (interactive)
  (evil-write-all nil)
  (mapc (lambda (var)
          (let* ((name1 (car (split-string (file-name-nondirectory var) "\\.")))
                 (name2 (format "%s_col.org" name1))
                 (file2 (format "/home/misha/caltemp/%s" name2))
                 ;; (file2 (format "%s%s" (file-name-directory var) name2))
                 (revert-without-query '(".*"))
                 (revert-without-query '(".*")))
            (copy-file var file2 t)
            (find-file file2)
            (goto-char (point-min))
            (while (re-search-forward (org-re-timestamp 'all) nil t)
              ;; don't apply the timestamp change when it's just a day.
              (if (string-match ":" (thing-at-point 'line t))
                  (org-timestamp-change (* -1 (timezone-diff-mv)) 'hour)))
            (org-map-entries
             (lambda ()
               (org-delete-property "ID")
               'file))
            (save-buffer)
            (kill-buffer name2)
            )) org-agenda-files-basic))
#+end_src
*** Main functions
**** open-cal-mv-thorough
#+begin_src emacs-lisp 
(defun open-cal-mv-thorough ()
  (interactive)
  (if in-colombia (progn
                    (setq org-agenda-files (mapcar (lambda (var)
                                                     (let* ((name1 (car (split-string (file-name-nondirectory var) "\\.")))
                                                            (name2 (format "%s_col.org" name1))
                                                            ;; (file2 (format "%s%s" (file-name-directory var) name2))
                                                            (file2 (format "/home/misha/caltemp/%s" name2))
                                                            ) file2)) org-agenda-files-basic))
                    (timezone-mv))
    (setq org-agenda-files org-agenda-files-basic))
  (org-agenda nil "a")
  (org-agenda-week-view)
  ;; (org-agenda-month-view)
  )
#+end_src
**** open-cal-mv-quick
#+begin_src emacs-lisp 
(defun open-cal-mv-quick ()
  (interactive)
  ;;(refresh-theme)
  ;;(mv/caldav-async)
  (if in-colombia (progn
                   (setq org-agenda-files
                    (mapcar (lambda (var)
                             (let* ((name1
                                     (car (split-string (file-name-nondirectory var) "\\.")))
                                    (name2 (format "%s_col.org" name1))
                                    ;; (file2 (format "%s%s" (file-name-directory var) name2))
                                    (file2 (format "/home/misha/caltemp/%s" name2))) file2)) org-agenda-files-basic)))
   (setq org-agenda-files org-agenda-files-basic))

(if (and (not (one-window-p)) (not (org-calendar-window-open-p)))
 (progn
  (let ((org-agenda-window-setup 'current-window))
    (split-window-below) ;; Split the window horizontally
    (other-window 1)    ;; Move to the new window
    (org-agenda nil "a")
    (org-agenda-week-view)
    ;; (org-agenda-list)
    ;; (org-agenda-week-view)
   ))

(progn
       (message "There is only one window open.")
       (org-agenda nil "a")
       (org-agenda-week-view)
(calendar-basic-setup nil t))))


(defun org-calendar-window-open-p ()
  (interactive)
  (if (seq-some (lambda (win)
                  (string= (buffer-name (window-buffer win)) "*Org Agenda*"))
                (window-list))
      (message "An Org Agenda window is open.")
    nil))


#+end_src
**** set org capture templates
#+begin_src emacs-lisp 
(setq org-capture-templates
      `(
        ("w" "Work agenda" entry (file ,(format "~/Nextcloud/calendar/%s/work.org" (system-name)))
         "* %?")
        ("t" "time track" entry (file+headline "/home/misha/Nextcloud/mnotes/Tracking_January_2023.org" "Entries")
         "* %? %^g" :clock-in t :prepend t)
        ("p" "Personal agenda" entry (file ,(format "~/Nextcloud/calendar/%s/personal.org" (system-name)))
         "* %?")
        ("r" "Reminders" entry (file ,(format "~/Nextcloud/calendar/%s/reminders.org" (system-name)))
         "* %?")
        ("a" "Amor" entry (file ,(format "~/Nextcloud/calendar/%s/amor.org" (system-name)))
         "* %?")
        ("b" "BIJ1 agenda" entry (file ,(format "~/Nextcloud/calendar/%s/bij1.org" (system-name)))
         "* %?")
        ("u" "Todo Uni" entry (file+headline "~/Nextcloud/mnotes/todo.org" "Uni")
         "** TODO %?" :prepend t)
        ("o" "Todo overig" entry (file+headline "~/Nextcloud/mnotes/todo.org" "Overig")
         "** TODO %?" :prepend t)
        ("c" "Todo computer" entry (file+headline "~/Nextcloud/mnotes/todo.org" "Computer")
         "** TODO %?" :prepend t)
        ("m" "Add spatial memory" plain (file "~/Nextcloud/mnotes/spatial_memory.org")
         "* %?")
        ("i" "BIJ1 todo" entry (file+headline "~/Nextcloud/mnotes/todo.org" "Bij1")
         "** TODO %?" :prepend t)
        ("l" "Watching list" entry (file+headline "~/Nextcloud/mnotes/todo.org" "Watching list")
         "** %?")
        ("s" "Add Spanish word" entry (file "/home/misha/Nextcloud/mnotes/orgzly/EspaƱol.org")
         "* %?" :prepend t)
        ))
#+end_src
**** my-org-capture
#+begin_src emacs-lisp
(defun get-date-from-agenda ()
  (interactive)
  (let* ((date (calendar-gregorian-from-absolute (get-text-property (min (1- (point-max)) (point)) 'day)))
         (day (format "%02d" (nth 0 date)))
         (month (format "%02d" (nth 1 date)))
         (year (nth 2 date)))
    (format "<%s-%s-%s>" year day month)))
(defun my-org-capture ()
  (interactive)
  (let ((org-agenda-files-basic '("/home/misha/Nextcloud/calendar/personal.org"
                                  "/home/misha/Nextcloud/calendar/work.org"
                                  "/home/misha/Nextcloud/calendar/teaching.org"
                                  "/home/misha/Nextcloud/calendar/bij1.org"
                                  "/home/misha/Nextcloud/calendar/reminders.org"
                                  "/home/misha/Nextcloud/calendar/amor.org"
                                  )))
    (let ((agenda-date (if (derived-mode-p 'org-agenda-mode) (get-date-from-agenda) nil)))
      (if (and (> (count-windows) 1) (not (org-calendar-window-open-p)))
          (split-window-below))
      (org-capture)
      (if (not (string-match "CAPTURE-todo" (buffer-name)))
          (progn
            (evil-open-below 1)
            (if (not (eq agenda-date nil))
                (progn
                  (let ((time (if in-colombia (read-from-minibuffer "Time in Colombia: ") (read-from-minibuffer "Time: "))))
                    (if (not (string= "" time))
                        (progn
                          (insert (format agenda-date))
                          (left-char)
                          (insert (format " %s" time))
                          (org-set-property "TIMEZONE" "Europe/Amsterdam")
                          (if in-colombia (org-timestamp-change (* 1 (timezone-diff-mv)) 'hour)))
                      (progn
                        (insert agenda-date)))
                    (evil-goto-first-line)))
              (progn
                (let ((time (org-time-stamp nil)))
                  (other-window 1)
                  (open-agenda-in-buffer time)
                  (other-window -1)
                  (evil-goto-first-line)))))))))
(defun close-extra-org-agenda-window ()
  "If more than one window is open displaying the *Org Agenda* buffer, close down one."
  (let ((agenda-buf (get-buffer "*Org Agenda*")))
    (when agenda-buf
      (let ((agenda-windows (get-buffer-window-list agenda-buf)))
        (when (> (length agenda-windows) 1)
          (delete-window (car agenda-windows))))))
  (org-agenda-redo))
(add-hook 'org-capture-after-finalize-hook 'close-extra-org-agenda-window)
#+end_src
**** get-time-appointment
***** Using chatgpt
#+begin_src emacs-lisp
(defun chatgpt-input-output ()
  (interactive)
  (let* ((input (replace-regexp-in-string "\n" "" (buffer-substring (region-beginning) (region-end))))
         (setup (replace-regexp-in-string "\n" "" "This is an email about a meeting. Give me the title of the meeting, the time and the location if there is any. Give me the output like a string, without newlines following this setup: 'topic, date, time, location'. Use this format for the date '2023-10-31' and this format for time 22:10'"))
         (output (shell-command-to-string (format "bash /home/misha/Nextcloud/scripts_misha/misc_useful/openai-basic-prompt-input-output.sh \"%s\" \"%s\"" setup input)))
         (topic (car (split-string output ",")))
         (date (nth 1 (split-string output ",")))
         (time (nth 2 (split-string output ",")))
         (location (nth 3 (split-string output ","))))
    (message "test: %s, %s, %s, %s" topic date time location)))
#+end_src
***** manually
#+begin_src emacs-lisp 
(defun get-time-appointment (&optional col)
  (interactive)
  (save-excursion)
  (goto-char (point-min))
  (let* ((summary (progn
                    (search-forward "Summary: ")
                    (evil-forward-word-begin 1)
                    (let ((beg (point)))
                      (evil-end-of-line)
                      (buffer-substring-no-properties beg (+ 1 (point))))))

         (message-link (format "[[mu4e:msgid:%s][link to mail]]" (mu4e-message-field-at-point :message-id)))
         (location (progn
                     (goto-char (point-min))
                     (search-forward "Location: ")
                     ;; If location is empty give this variable the value nil
                     (if (not (looking-at "[[:space:]\n]*$"))
                         (progn
                           (evil-forward-word-begin 1)
                           (let ((beg (point)))
                             (evil-end-of-line)
                             (buffer-substring-no-properties beg (+ 1 (point)))))
                       nil)))
         (time (progn
                 (goto-char (point-min))
                 (search-forward "Time: ")
                 (search-forward "<")
                 (backward-char 1)
                 (let ((beg (point)))
                   (search-forward ">")
                   (buffer-substring-no-properties beg (point)))))
         (time (if col
                   (let* ((subday (cdr (split-string (car (split-string time " ")) "<")))
			  (subtime1 (car (split-string (car (cdr (split-string time " "))) "-")))
			  (subtime2 (car (split-string (car (cdr (split-string (car (cdr (split-string time " "))) "-"))) ">")))
			  (subtime1-cor (shell-command-to-string (format "python3 /etc/nixos/other-config/emacs/timezone/timezone-basis.py %s %s %s %s" subtime1 "America/Bogota" "Europe/Amsterdam" (car subday))))
			  (subtime2-cor (shell-command-to-string (format "python3 /etc/nixos/other-config/emacs/timezone/timezone-basis.py %s %s %s %s" subtime2 "America/Bogota" "Europe/Amsterdam" (car subday))))
			  (subtime1-cor (replace-regexp-in-string "h" "" subtime1-cor))
			  (subtime2-cor (replace-regexp-in-string "h" "" subtime2-cor)))
                     (format "<%s>-<%s>" subtime1-cor subtime2-cor))
                 time)))

    (open-cal-mv-quick)
    (let ((agenda-window (get-buffer-window "*Org Agenda")))
      (when agenda-window
        (select-window agenda-window)))
    (org-agenda-goto-date time)
    (org-capture t "w")
    (insert summary)
    ;;(evil-org-open-below 1)
    (evil-open-below 1)
    (insert time)
    ;; Only if the variable location is not nil
    (if location
        (org-set-property "LOCATION" location))
    (evil-open-below 1)
    (org-set-property "TIMEZONE" "Europe/Amsterdam")
    (insert message-link)))
(defun get-time-appointment-col ()
  (interactive)
  (get-time-appointment t))
#+end_src
**** Todo overview
#+begin_src emacs-lisp
(defun open-tasks-mv ()
  (interactive)
  (setq org-agenda-custom-commands
        '(("1" "n" agenda "Todos and scheduled"
           ((org-agenda-window-setup 'current-window)
            (org-agenda-entry-types '(:deadline :scheduled))
            (org-agenda-files
             '("/home/misha/Nextcloud/mnotes/auc_tutees_keeping_track.org"
               "/home/misha/Nextcloud/mnotes/auc_capstone_projects.org"
               "/home/misha/Nextcloud/mnotes/orgzly/todo.org"
               "/home/misha/Nextcloud/calendar/work.org"
               ))))))
  (org-agenda nil "1")
  (org-agenda-day-view))
(spc-define-keys spc-leader-map
  "mat" 'open-tasks-mv
  )

#+end_src
**** Recur-mv
#+begin_src emacs-lisp
(defun recur-mv ()
  (interactive)
  (org-insert-subheading 1)
  (insert (format "%s\n" (read-string "Title: ")))
  (org-time-stamp nil)
  (org-clone-subtree-with-time-shift (- (string-to-number (read-string "Number or recurrences: ")) 1) "+1w"))
#+end_src
**** Rest?
#+begin_src emacs-lisp 
(defun his-tracing-function (orig-fun &rest args)
  (let ((res (apply orig-fun args)))
    (print (file-name-nondirectory (car args)))
    (mapcar (lambda (s)
              ;; (add-face-text-property 1 (string-match-p ":" s) 'warning nil s)
              (add-face-text-property 1 5 'warning nil s)
              )
            res)
    res)
(let ((res (apply orig-fun args)))
    (mapcar (lambda (s)
              (add-face-text-property 1 (length s)
                                      ;;(string-match-p ":" s)
                                      (pcase (file-name-nondirectory (car args))
                                        ("personal.org" 'all-the-icons-green)
                                        ("personal_col.org" 'all-the-icons-green)
                                        ("work.org" 'all-the-icons-dorange)
                                        ("work_col.org" 'all-the-icons-dorange)
                                        ("teaching.org" 'all-the-icons-blue)
                                        ("teaching_col.org" 'all-the-icons-blue)
                                        ("bij1.org" 'all-the-icons-lorange)
                                        ("bij1_col.org" 'help-argument-name)
                                        ("reminders.org" 'all-the-icons-dsilver)
                                        ("amor.org" 'font-lock-property-use-face)
                                        )

                                      nil s))
            res)
    res))



(advice-add 'org-agenda-get-day-entries :around #'his-tracing-function)


(defun my-org-capture-place-template-dont-delete-windows (oldfun &rest args)
  (cl-letf (((symbol-function 'delete-other-windows) 'ignore))
    (apply oldfun args)))
(with-eval-after-load "org-capture"
  (advice-add 'org-capture-place-template :around 'my-org-capture-place-template-dont-delete-windows))

(defun close-extra-org-agenda-window ()
  (interactive)
  "If more than one window is open displaying the *Org Agenda* buffer, close down one."
  (let ((agenda-buf (get-buffer "*Org Agenda*")))
    (when agenda-buf
      (let ((agenda-windows (get-buffer-window-list agenda-buf)))
        (when (> (length agenda-windows) 1)
          (delete-window (car agenda-windows))))))
  (org-agenda-redo))

(add-hook 'org-capture-after-finalize-hook 'close-extra-org-agenda-window)
#+end_src
** CALDAV
#+begin_src emacs-lisp 
(load-file "/etc/nixos/other-config/emacs/caldav/org-caldav.el")
(setq org-caldav-calendars-prep
      `(
        (:calendar-id "personal-2" :files ,(list (format "/home/misha/Nextcloud/calendar/%s/personal.org" (system-name)))
                      :inbox ,(format "/home/misha/Nextcloud/calendar/%s/personal.org" (system-name))) 
        (:calendar-id "work" :files ,(list (format "/home/misha/Nextcloud/calendar/%s/work.org" (system-name)))
                      :inbox ,(format "/home/misha/Nextcloud/calendar/%s/work.org" (system-name)))
        (:calendar-id "teaching" :files ,(list (format "/home/misha/Nextcloud/calendar/%s/teaching.org" (system-name)))
                      :inbox ,(format "/home/misha/Nextcloud/calendar/%s/teaching.org" (system-name)))
        (:calendar-id "bij1-2" :files ,(list (format "/home/misha/Nextcloud/calendar/%s/bij1.org" (system-name)))
    		      :inbox ,(format "/home/misha/Nextcloud/calendar/%s/bij1.org" (system-name)))
        (:calendar-id "reminders-1" :files ,(list (format "/home/misha/Nextcloud/calendar/%s/reminders.org" (system-name)))
		      :inbox ,(format "/home/misha/Nextcloud/calendar/%s/reminders.org" (system-name)))
        (:calendar-id "amor-1" :files ,(list (format "/home/misha/Nextcloud/calendar/%s/amor.org" (system-name)))
		      :inbox ,(format "/home/misha/Nextcloud/calendar/%s/amor.org" (system-name)))
	))
(setq org-caldav-calendars org-caldav-calendars-prep)

(setq org-caldav-url "https://nextcloud.mishathings.com/remote.php/dav/calendars/misha2")

(defun caldav-save-and-sync ()
  (interactive)
  (evil-write-all nil)

  (let ((use-dialog-box nil))
    (advice-add 'yes-or-no-p :override (lambda (_prompt) t))
    (org-caldav-sync)
    (advice-remove 'yes-or-no-p (lambda (_prompt) t))))

(defun mv/caldav-async ()
 (interactive)
(make-thread
 (lambda ()
  (caldav-save-and-sync))))

;; ;; Run at startup
;; (mv/caldav-async)

(setq org-caldav-sync-changes-to-org 'all)
(setq org-caldav-delete-calendar-entries 'always)
(setq org-caldav-delete-org-entries 'always)
(require 'auth-source)
(setq auth-sources
      '((:source "~/.authinfo.gpg")))

#+end_src

** Colors
#+begin_src emacs-lisp 
;; (defun his-tracing-function (orig-fun &rest args)
;;   (let ((res (apply orig-fun args)))
;;     (mapcar (lambda (s)
;;               (add-face-text-property 1 (length s)
;;                                       ;;(string-match-p ":" s)
;;                                       (pcase (file-name-nondirectory (car args))
;;                                         ("personal.org" 'all-the-icons-green)
;;                                         ("personal_col.org" 'all-the-icons-green)
;;                                         ("work.org" 'all-the-icons-dorange)
;;                                         ("work_col.org" 'all-the-icons-dorange)
;;                                         ("teaching.org" 'all-the-icons-blue)
;;                                         ("teaching_col.org" 'all-the-icons-blue)
;;                                         ("bij1.org" 'help-argument-name)
;;                                         ("bij1_col.org" 'help-argument-name)
;;                                         ("reminders.org" 'gnus-cite-2)
;;                                         ("amor.org" 'nxml-attribute-local-name)
;;                                         )

;;                                       nil s))
;;             res)
;;     res))
#+end_src

* MU4E
#+begin_src emacs-lisp 
(require 'mu4e)
#+end_src
** Contexts
#+begin_src emacs-lisp
(setq mu4e-contexts
      `( 
	,(make-mu4e-context
          :name "uva"
          :enter-func (lambda () (mu4e-message "Entering uva context"))
          :leave-func (lambda () (mu4e-message "Leaving uva context"))
          ;; we match based on the contact-fields of the message
          :match-func (lambda (msg)
                        (when msg
                          (mu4e-message-contact-field-matches msg
                                                              :to "m.velthuis@uva.nl")))
          ;;:to "work@mishavelthuis.nl")))
          :vars '( ( user-mail-address	    . "m.velthuis@uva.nl"  )
                   ( user-full-name	    . "Misha Velthuis" )
                   ( mu4e-sent-folder . "/uva/INBOX" )
                   ;;( mu4e-drafts-folder . "/../external-mail-folder/unnecessary-drafts/for-mu4e/uva")
                   ( mu4e-drafts-folder . "/uva/Concepten")
                   ( mu4e-trash-folder . "/../external-mail-folder/mail-trash/for-mu4e/uva")
                   ;;( mu4e-refile-folder . "/uva/archive-March252024-onwards" )
                   ( mu4e-refile-folder . "/work/Archive-5Feb2025-onwards" )

                   ))
        ,(make-mu4e-context
          :name "work"
          :enter-func (lambda () (mu4e-message "Entering work context"))
          :leave-func (lambda () (mu4e-message "Leaving work context"))
          :match-func (lambda (msg)
                        (when msg
                          (mu4e-message-contact-field-matches msg
                                                              :to "work@mishavelthuis.nl")))
          :vars '( ( user-mail-address	    . "work@mishavelthuis.nl"  )
                   ( user-full-name	    . "Misha Velthuis" )
                   ( mu4e-sent-folder . "/work/INBOX" )
                   ;;( mu4e-drafts-folder . "/../external-mail-folder/unnecessary-drafts/for-mu4e/work")
                   ( mu4e-drafts-folder . "/work/Drafts")
                   ( mu4e-trash-folder . "/../external-mail-folder/mail-trash/for-mu4e/work")
                   ( mu4e-refile-folder . "/work/Archive-5Feb2025-onwards" )
                   ))
        
        ,(make-mu4e-context
          :name "transip"
          :enter-func (lambda () (mu4e-message "Entering work context"))
          :leave-func (lambda () (mu4e-message "Leaving work context"))
          ;; we match based on the contact-fields of the message
          :match-func (lambda (msg)
                        (when msg
                          (mu4e-message-contact-field-matches msg
                                                              :to "mail@mishavelthuis.nl")))
          :vars '( ( user-mail-address	    . "mail@mishavelthuis.nl" )
                   ( user-full-name	    . "Misha Velthuis" )
                   ( mu4e-sent-folder . "/transip/INBOX" )
                   ;;( mu4e-drafts-folder . "/../external-mail-folder/unnecessary-drafts/for-mu4e/transip")
                   ( mu4e-drafts-folder . "/transip/Drafts")
                   ( mu4e-trash-folder . "/../external-mail-folder/mail-trash/for-mu4e/transip")
                   ( mu4e-refile-folder . "/transip/Archive-5Feb2025-onwards" )
                   ))
        ,(make-mu4e-context
          :name "bij1"
          :enter-func (lambda () (mu4e-message "Entering bij1 context"))
          :leave-func (lambda () (mu4e-message "Leaving bij1 context"))
          ;; we match based on the contact-fields of the message
          :match-func (lambda (msg)
                        (when msg
                          (mu4e-message-contact-field-matches msg
                                                              :to "misha@bij1.org")))
          :vars '( ( user-mail-address	    . "misha@bij1.org" )
                   ( user-full-name	    . "Misha Velthuis" )
                   ( mu4e-sent-folder . "/bij1/INBOX" )
                   ( mu4e-drafts-folder . "/bij1/Drafts" )
                   ( mu4e-trash-folder . "/../mail-trash/for-mu4e" )
                   ( mu4e-refile-folder . "/bij1/Archief" )))

        ))

(mu4e-context-switch nil "uva")
(mu4e t)
#+end_src
** Sending & pulling
#+begin_src emacs-lisp 
(setq message-sendmail-f-is-evil t
      message-sendmail-extra-arguments '("--read-envelope-from")
      sendmail-program "msmtp"
      send-mail-function 'smtpmail-send-it
      message-send-mail-function 'message-send-mail-with-sendmail)

(defun check-canvas ()
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (let ((end (progn (forward-line 15) (point))))
      (goto-char (point-min))
      (if (search-forward "via Canvas Notifications" end t)
          (progn
            (adsi)
            (evil-goto-first-line 1))
        (progn 
          (goto-char (point-min))
          (if (search-forward "notifications@instructure.com" end t)
              (progn 
                (adsi)
                (evil-goto-first-line 1))
            (export-buffer-content-to-html-and-replace)
	    (message-send-and-exit)
	    ;;(org-ctrl-c-ctrl-c)
	    ;;(message "send")

            ))))))



(defun mv/delete-cc ()
  (interactive)
  (goto-char (point-min))
  (while (re-search-forward "^Cc:.*" nil t)
    (beginning-of-line)
    (kill-line 1)))

(defun adsi ()
  (interactive)
  (let* ((line (ivy-read "select: "
                         (split-string (shell-command-to-string "python3 /etc/nixos/other-config/emacs/mu4e/new_script.py") "\n")
                         :initial-input (progn
                                          (goto-char (point-min))
                                          (search-forward "To: ")
                                          (let ((var (thing-at-point 'word t)))
                                            (message var)))))
         (line2 (replace-regexp-in-string "'" "" line)))
    (insert (shell-command-to-string (format "bash  /etc/nixos/other-config/emacs/mu4e/extract_email.sh '%s'" line2)))
    (evil-change-line (point) (line-end-position)))
  (mv/delete-cc))

#+end_src
** Get email addresses
#+begin_src emacs-lisp
;; (defun ads ()
;;   (interactive)
;;   (insert (shell-command-to-string (format "bash /etc/nixos/other-config/mail/extract_email.sh '%s'" (ivy-read "select: "(split-string (shell-command-to-string (format "python3 %s/new_script.py" filemv-mu4e)) "\n"))))))

;; get email of groups
(defun adg ()
  (interactive)
  (insert (shell-command-to-string (format "cat /home/misha/Nextcloud/Addresses/groups/%s" (ivy-read "select" (directory-files "/home/misha/Nextcloud/Addresses/groups"))))))

(spc-define-keys spc-leader-map
  "oas" 'adsi
  "oag" 'adg)

#+end_src
** Folding
See also [[*Theming][Theming]]
#+begin_src emacs-lisp 
;; I am using this thread folding script
(load-file "/etc/nixos/other-config/emacs/mu4e/mu4e-thread-folding.el")
(require 'mu4e-thread-folding)
(mu4e-thread-folding-mode t)
(setq mu4e-thread-folding-keep-faces t)

(add-to-list 'mu4e-header-info-custom
             '(:empty . (:name "Empty"
                               :shortname ""
                               :function (lambda (msg) " "))))
(setq mu4e-headers-fields '((:empty         .    0)
                            (:human-date    .   19)
                            (:flags         .    3)
                            (:from          .   22)
                            (:to            .   20)
                            (:subject       .   nil)))

(setq mu4e-headers-date-format "%a %d %b %H:%M %y")

(setq mu4e-thread-folding-root-folded-prefix-string "")
(setq mu4e-thread-folding-root-unfolded-prefix-string "")

(define-key mu4e-headers-mode-map (kbd "<tab>") 'mu4e-headers-toggle-at-point)
#+end_src
** Defining functions
*** search-mail
#+begin_src emacs-lisp
(setq ag-reuse-buffers 't)
;;(setq ag-arguments "--smart-case" "--ignore-dir=/home/misha/Maildir/transip")

(defun mv/focus-ag-search-buffer ()
  "Focus the first buffer that starts with *ag search."
  (let ((buf (catch 'found
               (dolist (b (buffer-list))
                 (when (string-prefix-p "*ag search" (buffer-name b))
                   (throw 'found b))))))
    (if buf
        (switch-to-buffer buf)
      (message "No *ag search buffer found."))))


(setq ag-arguments '("--smart-case" "--stats" "--ignore-dir" "transip/"))
(defun mv/mail-rg ()
  (interactive)
  ;;(rg nil "/home/misha/Maildir/" "-g !/home/misha/Maildir/transip/ -i --sortr=modified -B 2 -A 2" "Type: ")
  (ag (read-from-minibuffer "Keyword: ") "/home/misha/Maildir")
  (mv/focus-ag-search-buffer)
  (delete-other-windows))

;; (defun mv/open-mail-with-id ()
;;   (interactive)
;;   (save-excursion
;;     (compile-goto-error)
;;     (goto-char (point-min))  ; Start searching from the beginning of the buffer
;;     (when
;; 	;;(re-search-forward "message-id: *<\\([^>]+\\)>" nil t)
;; 	;;(re-search-forward "message-id: *\\(?:<\\([^>]+\\)>\\|\\([^<>\n]+\\)\\)" nil t)
;;         (re-search-forward "message-id: *<\\([^>\n]+\\)\\|message-id: *\n *<\\([^>\n]+\\)" nil t)
;;       (let ((input (match-string 1)))  ; Get the captured group
;; 	(message (format "Opening mail with id: %s" input))
;; 	(mu4e-headers-search (format "msgid:%s" input)))
;;       (run-with-timer 0.2 nil 'mu4e-headers-view-message-mv)
;;      )))

(defun mv/open-mail-with-id ()
  "Open mail with the message ID found in the current buffer.
Only counts if the message ID is followed by '<' on the same line 
or on the next line."
  (interactive)
  (save-excursion
    (compile-goto-error)
    (goto-char (point-min))  ; Start searching from the beginning of the buffer
    (let ((case-fold-search t)  ; Optional: make the search case-insensitive
          (found-id nil))
      (while (re-search-forward "message-id: *<\\([^>\n]+\\)\\|message-id: *\n *<\\([^>\n]+\\)" nil t)
        (setq found-id (or (match-string 1) (match-string 2))))
      (when found-id
        (message "Opening mail with id: %s" found-id)
        (mu4e-headers-search (format "msgid:%s" found-id))
        (run-with-timer 0.2 nil 'mu4e-headers-view-message-mv)))))



(evil-define-key 'normal ag-mode-map "o" 'mv/open-mail-with-id)
#+end_src
*** Show thread in all modes
#+begin_src emacs-lisp
(defun mv/show-thread-all-modes ()
  (interactive)

  ;; (delete-other-windows)
  ;; (split-window-right)
  (if (or (eq major-mode 'mu4e-headers-mode) (eq major-mode 'mu4e-view-mode))
      (let ((mu4e-headers-include-related t))
	(mu4e-headers-search (format "msgid:%s" (mu4e-message-field-at-point :message-id))))

    (let* ((current-file (buffer-file-name))
           (temp-buffer (generate-new-buffer "*temp-email*")))
      (with-temp-buffer
	(insert-file-contents current-file)
	(goto-char (point-min))
	(let ((in-reply-to-regex "In-Reply-To: *<\\([^>]+\\)>"))
          (if (re-search-forward in-reply-to-regex nil t)
              (let ((in-reply-to (match-string 1))) ; Extract without <>
		(message "In-Reply-To: %s" in-reply-to)
		;; (setq my-in-reply-to-variable in-reply-to) ; Save in variable if needed

		;; (delete-other-windows)
		;; (split-window-right)
		(other-window 1)
		(message (concat "msgid:" in-reply-to))
		(let ((mu4e-headers-include-related t))
		  (mu4e-headers-search (format "msgid:%s" in-reply-to))))))))))

(spc-define-keys spc-leader-map
  "ww" 'mv/show-thread-all-modes)

#+end_src
*** read-mail-mv
**** New
#+begin_src emacs-lisp
(defun mv/read-mail ()
  "Open the current file in a temporary buffer, extract the In-Reply-To email address without <>, and then close the buffer."
  (interactive)
  (save-buffer "/tmp/mail-tmp")
  (save-excursion 
    (message "read-mail is running")
    (let* ((current-file (buffer-file-name))
           (temp-buffer (generate-new-buffer "*temp-email*")))
      (with-temp-buffer
	(insert-file-contents current-file)
	(goto-char (point-min))
	(let ((in-reply-to-regex "In-Reply-To: *<\\([^>]+\\)>"))
          (if (re-search-forward in-reply-to-regex nil t)
              (let ((in-reply-to (match-string 1))) ; Extract without <>
		(message "In-Reply-To: %s" in-reply-to)
		;; (setq my-in-reply-to-variable in-reply-to) ; Save in variable if needed

		(delete-other-windows)
		(split-window-right)
		(message (concat "msgid:" in-reply-to))
		(mu4e-headers-search (concat "msgid:" in-reply-to))
		(mu4e-view-message-with-message-id in-reply-to)
		(let ((buf (get-buffer "*mu4e-headers*")))
		  (when buf
		    (let ((window (get-buffer-window buf)))
		      (when window
			(delete-window window))))))
	    (other-window 1)
            (message "No In-Reply-To found.")))))))
#+end_src
**** COMMENT Old (not working anymore)
<2025-01-08 wo>
#+begin_src emacs-lisp 

(defun read-mail-mv ()
  (interactive)
  (save-excursion
    (delete-other-windows)
    (goto-char (point-min))
    (if
        (re-search-forward "reply-to:" nil t)
        (progn 
          (let
              ((id (car (split-string (nth 1 (split-string (thing-at-point 'line t) "<")) ">")))
               (mu4e-search-include-related t))
            (split-window-right)
            (other-window 1)
            (switch-to-buffer "*mu4e-headers*")
            (evil-window-set-width 80)
            (mu4e-headers-search (format "msgid:%s" id))
            ;;(mu4e-headers-toggle-include-related)
            (run-with-timer 0.2 nil #'open-mail-no-split)))
      (progn
        (if (get-buffer "*mu4e-article*")
            (switch-to-buffer "*mu4e-article*")
          (go-to-inbox)
          )))))
#+end_src
*** Remove trash
#+begin_src emacs-lisp
;; (defun mv/move-files-from-x-to-y (x y)
;;   "Move all files from directory X to directory Y."
;;   (let ((files (directory-files x t "\\`[^.]"))) ;; Exclude `.` and `..`
;;     (when files
;;       (dolist (file files)
;;         (when (file-regular-p file)
;;          (rename-file file (expand-file-name (file-name-nondirectory file) y)))))))

(defun mv/move-files-from-x-to-y (x y)
  "Move all files from directory X to directory Y, overwriting if they exist."
  (let ((files (directory-files x t "\\`[^.]"))) ;; Exclude `.` and `..`
    (when files
      (dolist (file files)
        (when (file-regular-p file)
          (rename-file file (expand-file-name (file-name-nondirectory file) y) t))))))

#+end_src
*** search without threads
#+begin_src emacs-lisp
(defun turn-off-threads ()
  (interactive)
  (setq mu4e-headers-show-threads nil))
(defun search-mv ()
  (interactive)
  (turn-off-threads)
  (mu4e-search))
#+end_src
*** Toggle the organization of mail in threads
#+begin_src emacs-lisp
(defun toggle-threads-mv ()
  (interactive)
  (if mu4e-headers-show-threads
      (setq mu4e-headers-show-threads nil)
    (setq mu4e-headers-show-threads t)))
#+end_src
*** Select inbox, archives, trash, concepts
#+begin_src emacs-lisp
(defun select-mu4e-box ()
  (interactive)
  (let* ((folders-alist `(("mu4e-drafts-folder" . ,mu4e-drafts-folder)
                          ("mu4e-trash-folder" . ,mu4e-trash-folder)
                          ("mu4e-refile-folder" . ,mu4e-refile-folder)))
         (choice (ivy-read "Select:" folders-alist)))
    (mu4e~headers-jump-to-maildir (cdr (assoc choice folders-alist)))))

#+end_src
*** go-to-headers
#+begin_src emacs-lisp
(defun go-to-headers ()
  (interactive)
  (mv/move-files-from-x-to-y (format "/home/misha/external-mail-folder/mail-trash/for-mu4e/%s/cur/" (mu4e-context-name (mu4e-context-current))) (format "/home/misha/external-mail-folder/mail-trash/real/%s/" (mu4e-context-name (mu4e-context-current))))
  (mv/move-files-from-x-to-y (format "/home/misha/Maildir%s/cur/" mu4e-drafts-folder) (format "/home/misha/external-mail-folder/unnecessary-drafts/real/%s/" (mu4e-context-name (mu4e-context-current))))
  (if (get-buffer "*mu4e-headers*")
      (switch-to-buffer "*mu4e-headers*")
    (progn
      (mu4e t)
      (show-combined-inbox))))
#+end_src
*** go-to-inbox
#+begin_src emacs-lisp 
(defun go-to-inbox ()
  (interactive)
  ;; (when mv/draft-name
  ;;   (mv/remove-drafts))
  (setq mu4e-headers-show-threads t)
  (let ((current-context (mu4e-context-current)))
    (if (and current-context
             (string= (mu4e-context-name current-context) "work"))
        (show-combined-inbox)
      (mu4e~headers-jump-to-maildir mu4e-sent-folder)))
  ;; (when mu4e-headers-include-related (mu4e-headers-toggle-include-related))
  ;;(org-msg-mode t)
  (mv/move-files-from-x-to-y (format "/home/misha/external-mail-folder/mail-trash/for-mu4e/%s/cur/" (mu4e-context-name (mu4e-context-current))) (format "/home/misha/external-mail-folder/mail-trash/real/%s/" (mu4e-context-name (mu4e-context-current))))
  (mv/move-files-from-x-to-y (format "/home/misha/Maildir%s/cur" mu4e-drafts-folder) (format "/home/misha/external-mail-folder/unnecessary-drafts/real/%s/" (mu4e-context-name (mu4e-context-current))))
  (refresh-theme)
  (mv/close-duplicate-mu4e-headers)
  (mv/empty-directory "/home/misha/Documents/attach")
  )
#+end_src
*** mu4e-update-index-mv
#+begin_src emacs-lisp
(defun mu4e-quit-open ()
  (interactive)
  (mu4e-quit)
  (go-to-inbox)
  (mu4e-update-index))
#+end_src
*** show-single-thread
#+begin_src emacs-lisp 
(defun show-single-thread ()
  (interactive)
  (let ((mu4e-headers-include-related t))
    (mu4e-headers-search (format "msgid:%s" (mu4e-message-field-at-point :message-id)))))
#+end_src
*** search-sent-mv
#+begin_src emacs-lisp 
(defun search-sent-mv ()
  (interactive) 
  (setq mu4e-headers-show-threads nil)
  (if (string= (mu4e-context-name (mu4e-context-current)) "work")
      (mu4e-headers-search "from:m.velthuis@uva.nl or from:work@mishavelthuis.nl")
    (mu4e-headers-search (format "from:%s and (maildir:\"%s\" or maildir:\"%s\")" user-mail-address mu4e-sent-folder mu4e-refile-folder) nil nil)))
#+end_src
*** show-combined-inbox
#+begin_src emacs-lisp
(defun show-combined-inbox ()
  (interactive) 
  (mu4e-headers-search "maildir:\"/uva/INBOX\" or maildir:\"/work/INBOX\""))

#+end_src
*** show-archive-mv
Adding other archives does not make that much sense if the result is limited to 500 messages.
#+begin_src emacs-lisp 
(defun show-archive-mv ()
  (interactive)

  (let ((current-context (mu4e-context-current)))
    (if (and current-context
             (string= (mu4e-context-name current-context) "work"))

	(mu4e-headers-search "maildir:\"/uva/archive-March252024-onwards\" or maildir:\"/transip/uva.archive-uva2024\" or \"/transip/Archive-5Feb2025-onwards\" or \"/work/Archive-5Feb2025-onwards\"")
      (mu4e~headers-jump-to-maildir mu4e-refile-folder))))
#+end_src
*** show-drafts-mv
#+begin_src emacs-lisp
(defun show-drafts-mv ()
  (interactive) 
  (dirvish (format "/home/misha/Maildir/%s/cur" mu4e-drafts-folder)))
#+end_src
*** archive-mv-thread
#+begin_src emacs-lisp 
(defun archive-mv-thread ()
  (interactive)
  (let ((mu4e-refile-folder "/work/Archive-5Feb2025-onwards"))
    (mu4e-headers-mark-thread nil '(refile))
    ;;(mu4e-update-index)
    ))
#+end_src
*** open-mail-no-split
#+begin_src emacs-lisp 
(defun open-mail-no-split ()
  (interactive)
  (setq mu4e-split-view 'single-window)
  (mu4e-headers-view-message)
  (sleep-for 0.1)
  (setq mu4e-split-view 'vertical))
#+end_src
*** mu4e-headers-view-message-mv
#+begin_src emacs-lisp 
(defun mu4e-headers-view-message-mv ()
  (interactive)
  (if (< (length (window-list)) 2)
      (mu4e-headers-view-message)
    (open-mail-no-split)))

#+end_src
*** tomail
#+begin_src emacs-lisp 
(defun tomail ()
  (interactive)
  (delete-other-windows)
  (let ((buffernames nil))

    ;; (dolist (name (mapcar #'buffer-name (buffer-list)))
    ;;   (when (or (string-match "-draft*" name) (string-match "Re:" name) (string-match "Fwd:" name))
    ;;    (push name buffernames)))
    
    (dolist (buffer (buffer-list))
      (when (with-current-buffer buffer (derived-mode-p 'mu4e-compose-mode))
	(push (buffer-name buffer) buffernames)))
    
    (if (> (length buffernames) 1)
        (progn
          (dolist (x (cdr buffernames))
            (split-window-right))
          (winum-select-window-1)
          (setq count 2)
          (dolist (bufferm buffernames)
            (switch-to-buffer bufferm)
            (evil-window-next count)
            (setq count (+ count 1))))
      (switch-to-buffer (car buffernames)))))
#+end_src
*** show-archief
#+begin_src emacs-lisp 
(defun show-archief ()
  (interactive)
  (mu4e-search-maildir "/uva/Archief.2022"))
#+end_src
*** execute-mark-mv
#+begin_src emacs-lisp 
(defun execute-mark-mv ()
  (interactive)
  (mu4e-mark-execute-all t))
#+end_src
*** check if source block is python
#+begin_src emacs-lisp
(defun inside-python-src-block-p ()
  "Check if the cursor is in a Python source block in org-mode."
  (interactive)
  (let ((element (org-element-context)))
    (and (eq (org-element-type element) 'src-block)
         (string= (org-element-property :language element) "python"))))
#+end_src
*** send-mv
**** Check if cursor is in a source block
Made by ChatGPT
#+begin_src emacs-lisp
(defun is-cursor-in-source-block-p ()
  "Check if cursor is in a source block."
  (interactive)
  (let ((element (org-element-at-point)))
    (if (eq (car element) 'src-block) t nil)))
#+end_src
**** Actual script
#+begin_src emacs-lisp 
(defun send-mv ()
  (interactive)
  (if (string= major-mode "mu4e-compose-mode")
      (check-canvas)
    (if (string= major-mode "org-mode")
	(if (is-cursor-in-source-block-p)

	    (cl-letf (((symbol-function 'yes-or-no-p) (lambda (prompt) t))
		      ((symbol-function 'y-or-n-p) (lambda (prompt) t)))
	      (org-ctrl-c-ctrl-c))

          ;;(org-ctrl-c-ctrl-c)
          (progn (evil-end-of-visual-line) (gptel-send))))))
#+end_src

*** mail-view-move-up/ mail-view-move-down
#+begin_src emacs-lisp 
(defun mail-view-move-down ()
  (interactive)
  (mu4e-view-quit)
  (winum-select-window-1)
  (evil-next-line 1)
  (mu4e-headers-view-message-mv))

(defun mail-view-move-up ()
  (interactive)
  (mu4e-view-quit)
  (winum-select-window-1)
  (evil-previous-line 1)
  (mu4e-headers-view-message-mv))
#+end_src

*** Closing buffers
**** Close mu4e-article buffer
#+begin_src emacs-lisp
(defun mv/close-article-buffer ()
  (interactive)
  (switch-to-buffer "*mu4e-article*")
  (mu4e-view-quit))
#+end_src

**** Close headers windows
#+begin_src emacs-lisp
;; Sometimes, after closing the export buffer (and maybe the *mu4e-article*?)
;; I end up with two windows with the same mu4e-headers. This makes sure that
;; it is only one.
(defun mv/close-duplicate-mu4e-headers ()
  (interactive)
  "Close one duplicate *mu4e-headers* window if multiple exist."
  (sleep-for 0.1)
  (let ((headers-windows (cl-loop for win in (window-list)
                                  if (string= (buffer-name (window-buffer win)) "*mu4e-headers*")
                                  collect win)))
    (when (> (length headers-windows) 1)
      (delete-window (car headers-windows))))
  (message "mv/close-duplicate-mu4e-headers has run"))
#+end_src

** Keybindings & hooks
Zie ook [[*ORG-MSG (compose and save attachment)][ORG-MSG]] voor hooks:
#+begin_src emacs-lisp 
;; Prevents two mu4e windows from being open
;; (add-hook 'mu4e-headers-mode-hook 'close-duplicate-mu4e-headers)
;; This turns of (hopefully) the constant creation of new drafts
(add-hook 'mu4e-compose-mode-hook #'(lambda () (auto-save-mode -1)))
(add-hook 'org-msg-edit-mode-hook #'(lambda () (auto-save-mode -1)))

;; This frees up the space bar
(add-hook 'mu4e-view-mode-hook
          (lambda ()
            (evil-define-key 'normal mu4e-view-mode-map (kbd "SPC") nil)))

(spc-define-keys spc-leader-map
  "mu" 'go-to-headers
  "mm" 'tomail
  "nn" 'mv/read-mail
  "aa" 'add-extra-attach
  )

(add-hook 'mu4e-headers-mode-hook
          (lambda ()
            (visual-line-mode -1)
            (toggle-truncate-lines)
            ))

;; This stops the autosaving of messages hopefully (<2024-04-06 Sat +0200 12:42>)
(add-hook 'mu4e-compose-mode-hook #'(lambda () (auto-save-mode -1)))

(evil-define-key 'normal mu4e-headers-mode-map "o" 'mu4e-headers-view-message-mv)
(evil-define-key 'normal mu4e-headers-mode-map "O" 'open-mail-no-split)
(evil-define-key 'normal mu4e-headers-mode-map "a" 'archive-mv-thread)
(evil-define-key 'normal mu4e-headers-mode-map "t" 'add-tag-mv)
(evil-define-key 'normal mu4e-headers-mode-map "i" 'go-to-inbox)
(evil-define-key 'normal mu4e-headers-mode-map "q" 'mu4e-quit)
(evil-define-key 'normal mu4e-headers-mode-map "I" 'mu4e-update-index)
(evil-define-key 'normal mu4e-headers-mode-map "r" nil)
(evil-define-key 'normal mu4e-headers-mode-map "r" 'mu4e-compose-wide-reply)
(evil-define-key 'normal mu4e-headers-mode-map "R" 'mu4e-compose-reply)
(evil-define-key 'normal mu4e-headers-mode-map "F" 'mu4e-compose-forward) 
(evil-define-key 'normal mu4e-view-mode-map "F" 'mu4e-compose-forward) 
(evil-define-key 'normal mu4e-headers-mode-map "w" 'show-single-thread)
(evil-define-key 'normal mu4e-headers-mode-map "S" 'search-sent-mv)
(evil-define-key 'normal mu4e-headers-mode-map "s" 'search-mv)
(evil-define-key 'normal mu4e-headers-mode-map "D" 'show-drafts-mv)
(evil-define-key 'normal mu4e-headers-mode-map "A" 'show-archive-mv)
(evil-define-key 'normal mu4e-headers-mode-map "x" (lambda () (interactive) (mu4e-mark-execute-all t)))
(evil-define-key 'normal mu4e-view-mode-map "f" 'mu4e-view-go-to-url)
(evil-define-key 'normal mu4e-view-mode-map "r" 'mu4e-compose-wide-reply)
(evil-define-key 'normal mu4e-view-mode-map (kbd "C-k") 'mail-view-move-up)
(evil-define-key 'normal mu4e-view-mode-map (kbd "C-j") 'mail-view-move-down)
(evil-define-key 'normal mu4e-view-mode-map "o" 'mu4e-view-save-attachments)

(add-hook 'org-msg-edit-mode-hook
          (lambda ()
            (turn-on-visual-line-mode)
            (flyspell-mode t)
            ))

(add-hook 'mu4e-view-mode-hook
          (lambda ()
            (turn-on-visual-line-mode)
            (display-line-numbers-mode -1)
            ))
(setq mu4e-headers-include-related nil)

(with-eval-after-load "mm-decode"
  (add-to-list 'mm-discouraged-alternatives "text/html")
  (add-to-list 'mm-discouraged-alternatives "text/richtext"))

(add-hook 'message-sent-hook 'mv/close-article-buffer)
(add-hook 'message-sent-hook 'mv/close-duplicate-mu4e-headers)

#+end_src
** Defining basic variables
#+begin_src emacs-lisp 
(setq mu4e-confirm-quit nil) ; don't ask for yes or no when quitting
(setq mu4e-headers-open-after-move nil) ; i1r (I have to set it there again)

(setq message-kill-buffer-on-exit t)
(setq org-export-show-temporary-export-buffer nil)

(setq mu4e-split-view 'vertical)
(setq mu4e-attachment-dir "~/Downloads")
(setq shr-use-fonts nil)
(setq mu4e-headers-visible-columns 60)
(setq mail-user-agent 'mu4e-user-agent) ;; 
;;(setq mail-user-agent 'message-user-agent)
(setq mu4e-compose-dont-reply-to-self t) ;; the old one
(setq message-dont-reply-to-names t) ;; the new one
#+end_src
** Message Compose
#+begin_src emacs-lisp
(add-hook 'mu4e-compose-mode-hook 'turn-off-auto-fill) ;; this prevents the mu4e-compose-mode from truncating
(setq message-citation-line-function nil) ;; this turns off the "bla bla writes ..." at the beginning

(defun mv/update-buffer-text ()

  (interactive)
  (goto-char (point-min))

  ;; ;; For some reason, when replying, mu4e always defaults to the :From address that the original email was addressed to.
  ;; (while (re-search-forward "From: Misha Velthuis <m\\.velthuis@uva\\.nl>" nil t)
  ;;   (replace-match "From: Misha Velthuis <work@mishavelthuis.nl>" nil nil))
  ;; (goto-char (point-min))
  ;; ;; (while (re-search-forward "Misha Velthuis <m\\.velthuis@uva\\.nl>" nil t)
  ;; ;;  (replace-match "" nil nil))

  ;; (while (re-search-forward "\\(Misha Velthuis <m\\.velthuis@uva\\.nl>\\|Cc: \"m\\.velthuis@uva\\.nl\" <m\\.velthuis@uva\\.nl>\\|\"dr\\. Misha Velthuis\" <m\\.velthuis@uva\\.nl>\\,\\)" nil t)
  ;;   (replace-match "" nil nil))
  
  (while (re-search-forward "From: Misha Velthuis <work@mishavelthuis\\.nl>" nil t)
    (replace-match "From: Misha Velthuis <m.velthuis@uva.nl>" nil nil))
  (goto-char (point-min))

  (while (re-search-forward "\\(Misha Velthuis <work@mishavelthuis\\.nl>\\|Cc: \"work@mishavelthuis\\.nl\" <work@mishavelthuis\\.nl>\\|\"dr\\. Misha Velthuis\" <work@mishavelthuis\\.nl>\\,\\)" nil t)
    (replace-match "" nil nil))

  
  ;; (goto-char (point-min))
  (when (re-search-forward "--text follows this line--" nil t)
    (delete-region (point) (point-max))
    (insert "\n\n\n\nGroetjes,\n\nMisha\n\n---\n\n"
     "#+html:<div class=\"signature\">\n\n"
     "/Lecturer Sciences/\n\n"
     "Amsterdam University College\n\n"
     "Science Park 113 | 1098 XG Amsterdam | The Netherlands\n\n"
     "[[https://mishathings.org][Blog]] [[https://social.edu.nl/@MishaVelthuis][Mastodon]] [[https://matrix.to/#/@misha:pub.solar][Matrix]]"
     "\n\n#+html:</div>\n"
	    ))
  (message "update-buffer-text is running now")
  (run-with-timer 1 nil '(lambda ()
                             (other-window 1)
                             (goto-char (point-max))
                             (previous-logical-line 19)))
  (save-excursion (mv/read-mail))
  )


(setq mu4e-compose-context-policy 'pick-first)

(add-hook 'mu4e-compose-mode-hook 'mv/update-buffer-text)
(add-hook 'mu4e-compose-mode-hook 'visual-line-mode)

;;(setq mail-default-reply-to "Misha Velthuis <work@mishavelthuis.nl>")
(setq mail-default-reply-to "Misha Velthuis <m.velthuis@uva.nl>")

(setq mu4e-compose-reply-ignore-address '("work@mishavelthuis.nl"))
(setq user-mail-address "m.velthuis@uva.nl")

(setq gnus-ignored-from-addresses "work@mishavelthuis.nl")
#+end_src
*** mail html mails
**** Set-up 2
#+begin_src emacs-lisp
(defun export-buffer-content-to-html-and-replace ()
  (interactive)
  (let* ((split-point (save-excursion
                        (goto-char (point-min))
                        (when (search-forward "--text follows this line--" nil t)
                          (line-end-position))))
         (content (when split-point
                    (buffer-substring-no-properties (1+ split-point) (point-max))))
         (org-export-show-temporary-export-buffer nil))
    (when content
     (with-temp-buffer
      (insert
       ;; "#+HTML_HEAD: <style type=\"text/css\">
       ;; #+HTML_HEAD: signature {
       ;; #+HTML_HEAD:   background-color:  #d9fcd4 ;
       ;; #+HTML_HEAD:   border-left: 5px solid  #95ac92 ;
       ;; #+HTML_HEAD:   margin: 3em 1em;
       ;; #+HTML_HEAD:   padding: 3em 10px;
       ;; #+HTML_HEAD: }
       ;; #+HTML_HEAD: </style>"
       "#+HTML_HEAD: <style>
       #+HTML_HEAD: .signature {
       #+HTML_HEAD:   background-color:  #defada ; /* Light green background */
       #+HTML_HEAD:   border-left: 5px solid   #9cdb93  ; /* Dark green left border */
       #+HTML_HEAD:   margin: 1em 0;
       #+HTML_HEAD:   padding: 0.5em 10px;
       #+HTML_HEAD: }
       #+HTML_HEAD: blockquote {
       #+HTML_HEAD:   background-color: #f0f0f0; /* Light gray background */
       #+HTML_HEAD:   border-left: 5px solid #ccc; /* Gray left border */
       #+HTML_HEAD:   margin: 1em 0;
       #+HTML_HEAD:   padding: 0.5em 10px;
       #+HTML_HEAD: }
       #+HTML_HEAD: </style>"
       )
        (insert content)
        (write-file "/tmp/mail.org")
        (let ((org-export-with-author nil)
              (org-export-with-date nil)
              (org-html-postamble nil))
          (org-html-export-to-html nil nil nil nil nil)
          (rename-file (concat default-directory (file-name-sans-extension "mail.org") ".html") "/tmp/mail.html" t))))
    (when split-point
      (save-excursion
        (goto-char (1+ split-point))
        (delete-region (point) (point-max))
        (insert "<#part filename=\"/tmp/mail.html\"><#/part>")))
    (insert-part-for-files-in-attach-directory)
    ))
#+end_src

**** send attachments

#+begin_src emacs-lisp
(defun insert-part-for-files-in-attach-directory ()
  "Insert a part tag for each file in the '/home/misha/Documents/attach' directory into the current buffer."
  (interactive)
  (goto-char (point-max))
  (insert "\n")
  (let ((dir-path "/home/misha/Documents/attach")
        (files (directory-files "/home/misha/Documents/attach" t "^[^.].*"))) ; List files, excluding '.' and '..'
    (dolist (file files)
      (when (file-regular-p file) ; Check if it's a regular file
        (let ((filename (file-name-nondirectory file)))
          (insert (format "<#part filename=\"/home/misha/Documents/attach/%s\"><#/part>\n" filename)))))))

#+end_src

**** Set-up 1
- Create mail.
- And then simply use mv/process-marked-files-as-attachments
#+begin_src emacs-lisp
(defun mv/create-html-mail ()
  (interactive)
  (let ((file-path "/home/misha/html-mail/mail.org"))
    (with-temp-buffer
      (insert "#+OPTIONS: toc:nil num:nil author:nil timestamp:nil\n#+HTML_HEAD: <style>\n#+HTML_HEAD: body { background-color: beige; font-family: monospace; }\n#+HTML_HEAD: </style>\n\n\n")
      (write-file file-path)))
  (find-file "/home/misha/html-mail/mail.org")
  (goto-line 6))

(setq org-html-validation-link nil)
#+end_src
** Attachments
*** Getting other people's attachments
#+begin_src emacs-lisp 
;; From /usr/local/share/emacs/site-lisp/mu4e/mu4e-mime-parts.el 
;; Rewritten to remove forward slashes
(defun mu4e-view-save-attachments (&optional ask-dir)
  "Save files from the current view buffer.
    This applies to all MIME-parts that are \"attachment-like\" (have a filename),
    regardless of their disposition.

    With ASK-DIR is non-nil, user can specify the target-directory; otherwise
    one is determined using `mu4e-attachment-dir'."
  (interactive "P")
  (let* ((parts (mu4e-view-mime-parts))
         (candidates  (seq-map
                       (lambda (fpart)
                         (cons ;; (filename . annotation)
                          (plist-get fpart :filename)
                          fpart))
                       (seq-filter
                        (lambda (part) (plist-get part :attachment-like))
                        parts)))
         (candidates (or candidates
                         (mu4e-warn "No attachments for this message")))
         (files (mu4e--completing-read "Save file(s): " candidates
                                       'attachment 'multi))
         (custom-dir (when ask-dir (read-directory-name
                                    "Save to directory: "))))
    ;; we have determined what files to save, and where.
    (seq-do (lambda (fname)
              (let* ((part (cdr (assoc fname candidates)))
                     (path (mu4e--uniqify-file-name
                            (mu4e-join-paths
                             (or custom-dir (plist-get part :target-dir))
                             (replace-regexp-in-string "\/" "-" (plist-get part :filename))))))
                (mm-save-part-to-file (plist-get part :handle) path)))
            files)))


;; (load-file "/usr/local/share/emacs/site-lisp/mu4e/mu4e-mime-parts.el")
;; I need to load both these files for the attachment process to work
;;(load-file (format "%s/mu4e-folders_oud.el" filemv-mu4e))
;; (load-file (format "%s/mu4e-folders.el" filemv-mu4e))
;; I asked ChatGPT to modify the code above to produce something that saves all files
(defun save-files-custom-dir (&optional ask-dir)
  "Save files from the current view buffer.
    This applies to all MIME-parts that are \"attachment-like\" (have a filename),
    regardless of their disposition.

    With ASK-DIR is non-nil, user can specify the target-directory; otherwise
    one is determined using `mu4e-attachment-dir'."
  (interactive "P")
  (let* ((parts (mu4e-view-mime-parts))
         (candidates  (seq-map
                       (lambda (fpart)
                         (cons ;; (filename . annotation)
                          (plist-get fpart :filename)
                          fpart))
                       (seq-filter
                        (lambda (part) (plist-get part :attachment-like))
                        parts)))
         (candidates (or candidates
                         (mu4e-warn "No attachments for this message")))
         (files (mapcar 'car candidates)) ; save all files without prompting
         (targets (split-string (shell-command-to-string (format "find %s -type d" mv/nextcloud-base)) "\n"))
         (custom-dir (ivy-read "Pick dir: " targets)))

    ;; we have determined what files to save, and where.
    (seq-do (lambda (fname)
              (let* ((part (cdr (assoc fname candidates)))
                     (path (mu4e--uniqify-file-name
                            (mu4e-join-paths
                             (or custom-dir (plist-get part :target-dir))
                             (replace-regexp-in-string "\/" "-" fname)))))
                (mm-save-part-to-file (plist-get part :handle) path)
                ))
            files)
    (dirvish custom-dir)
    ))
(defun save-files-downloads (&optional ask-dir)
  "Save files from the current view buffer.
    This applies to all MIME-parts that are \"attachment-like\" (have a filename),
    regardless of their disposition.

    With ASK-DIR is non-nil, user can specify the target-directory; otherwise
    one is determined using `mu4e-attachment-dir'."
  (interactive "P")
  (if (not (eq major-mode 'mu4e-view-mode))
      (dirvish (format "%s/mail" filemv-download))
    (progn

      (let* ((parts (mu4e-view-mime-parts))
             (candidates  (seq-map
                           (lambda (fpart)
                             (cons ;; (filename . annotation)
                              (plist-get fpart :filename)
                              fpart))
                           (seq-filter
                            (lambda (part) (plist-get part :attachment-like))
                            parts)))
             (candidates (or candidates
                             (mu4e-warn "No attachments for this message")))
             (files (mapcar 'car candidates)) ; save all files without prompting
             (custom-dir "/home/misha/Downloads/mail"))
        (shell-command (format "rm -rf %s/mail/*" filemv-download))
        (shell-command (format "mkdir %s/mail" filemv-download))

        ;; we have determined what files to save, and where.
        (seq-do (lambda (fname)
                  (let* ((part (cdr (assoc fname candidates)))
                         (path (mu4e--uniqify-file-name
                                (mu4e-join-paths
                                 (or custom-dir (plist-get part :target-dir))
                                 (replace-regexp-in-string "\/" "-" fname)))))
                    (mm-save-part-to-file (plist-get part :handle) path)
                    ))
                files)
        (when (file-directory-p custom-dir) (dirvish custom-dir))
        ))))

(spc-define-keys spc-leader-map
  "sac" 'save-files-custom-dir
  "sad" 'save-files-downloads)

#+end_src
*** Saving own attachments
Adjusted from [[https://gist.github.com/rtrppl/1ffdc842a9b536c8f3a9ec583e4bca09][this]].
#+begin_src emacs-lisp
(defun mv/process-marked-files-as-attachments ()
  "Processes all marked files in the Dired buffer as attachments for mu4e."
  (interactive)
  (if (dired-get-marked-files)
      (progn
	(let ((marked-files (dired-get-marked-files)))
	  (dired-unmark-all-marks)
	  (with-temp-buffer
	    (dolist (file marked-files)
	      (insert "<\#part filename=\"" file "\">\n" "<\#\/part>"))
	    (setq attachments (buffer-string))))
	(dirvish-quit)
	(if compose-buffer
	    (mv/return-to-mu4e-draft)
          (message "no mail buffer selected")))
    (message "No files selected.")))

(defun mv/return-to-mu4e-draft ()
  "Returns to the mu4e draft and inserts all attachments at point."
  ;;(revert-buffer)
  (when compose-buffer 
    (switch-to-buffer compose-buffer)
    (save-excursion (goto-char (point-max)) 
		    (insert "\n\n" attachments)))
  (setq compose-buffer nil))
#+end_src
** COMMENT ORG-MSG (compose and save attachment)
*** Basis
#+begin_src emacs-lisp 
(require 'org-msg)
(add-hook 'mu4e-headers-mode-hook
          (lambda () (org-msg-mode t)))
#+end_src
*** Add signature
#+begin_src emacs-lisp
(setq
 org-msg-greeting-fmt "Hi%s,\n\n\n\n--\n\nBlog: https://mishathings.org\n\nMastodon: https://social.edu.nl/@MishaVelthuis\n\nMatrix (chat): https://matrix.to/#/@misha:pub.solar\n"
 org-msg-default-alternatives '((new		. (text html))
                                (reply-to-html	. (text html))
                                (reply-to-text	. (text))))
#+end_src
*** COMMENT Other stuff
#+begin_src emacs-lisp 
;; (package-install 'org-msg) ;;nixos
(setq org-msg-options "html-postamble:nil H:5 num:nil ^:{} toc:nil author:nil email:nil \\n:t"
      org-msg-startup "hidestars indent inlineimages"
      org-msg-greeting-fmt "Hi%s,\n\n\n\n--\n\nBlog: https://mishathings.org\n\nMastodon: https://social.edu.nl/@MishaVelthuis\n\nMatrix (chat): https://matrix.to/#/@misha:pub.solar\n"
                                        ;org-msg-signature "--\n\nBlog: https://mishathings.org\n\nMastodon: https://social.edu.nl/@MishaVelthuis\n\nMatrix (chat): https://matrix.to/#/@misha:pub.solar"
      org-msg-greeting-name-limit 3
      org-msg-default-alternatives '((new		. (text html))
                                     (reply-to-html	. (text html))
                                     (reply-to-text	. (text)))
      org-msg-convert-citation t)

#+end_src
*** close buffers and remove drafts
**** Setting things up
***** Save buffer
#+begin_src emacs-lisp
;; This closes the org export buffer (from org-msg)
;; It also closes the *mu4e-artcle* to which I replied
;; and it closes a duplicate mu4e-header.
(setq mv/draft-name nil)
(defun mv/save-mail-buffer ()
  (interactive)
  (save-buffer)
  (setq mv/mail-buffer (replace-regexp-in-string "<[0-9]+>$" "" (buffer-name)))
  (setq mv/draft-name (file-name-nondirectory (buffer-file-name)))
  (message (format "Mail buffer name: %s" mv/mail-buffer)))

#+end_src
***** Save hook
#+begin_src emacs-lisp
(add-hook 'message-send-hook 'mv/save-mail-buffer)
#+end_src
**** Remove drafts
- Depends on variable mv/draft-name
- I remove the drafts after sending (bound to go-to-inbox). Adding it to the sent-hook caused errors ("draft not found" etc.)
#+begin_src emacs-lisp 
(defun mv/print-directory-contents (dir)
  (interactive)
  (let ((directory "/path/to/directory/"))  ; Replace with your directory path
    (dolist (file (directory-files dir t))
      (message "file: %s" file))))

(mv/print-directory-contents "/home/misha/Downloads/")

(defun extract-up-to-ml1 (str)
  "Return the part of STR up to '.ml1'."
  (if (string-match "\\(.+?\\)\\.ml1" str)
      (match-string 1 str)
    str))
(defun remove-files-starting-with (directory prefix)
  "Remove all files in DIRECTORY that start with PREFIX."
  (dolist (file (directory-files directory t (concat "^" (regexp-quote prefix))))
    (when (file-exists-p file)
      (progn
	(message (format "%s delted" file))
	(delete-file file))))
  (mv/print-directory-contents directory))

(defun remove-broken-symlinks (directory)
  "Remove all broken symlinks from the specified DIRECTORY."
  (interactive "DDirectory: ")
  (let ((default-directory directory))
    (dolist (file (directory-files default-directory t))
      (when (and (file-symlink-p file) (not (file-exists-p file)))
        (delete-file file)
        (message "Removed broken symlink: %s" file)))))

(defun mv/remove-drafts ()
  (interactive)
  ;; Delete drafts ...
  (remove-files-starting-with (format "/home/misha/Maildir%s/cur" mu4e-drafts-folder) (extract-up-to-ml1 mv/draft-name))
  (remove-broken-symlinks (format "/home/misha/Maildir%s/cur" mu4e-drafts-folder)))

#+end_src
**** Close buffers and windows after sending
***** Function for closing double mu4e-header windows
#+begin_src emacs-lisp
;; Sometimes, after closing the export buffer (and maybe the *mu4e-article*?)
;; I end up with two windows with the same mu4e-headers. This makes sure that
;; it is only one.
(defun close-duplicate-mu4e-headers ()
  (interactive)
  "Close one duplicate *mu4e-headers* window if multiple exist."
  (let ((headers-windows (cl-loop for win in (window-list)
                                  if (string= (buffer-name (window-buffer win)) "*mu4e-headers*")
                                  collect win)))
    (when (> (length headers-windows) 1)
      (delete-window (car headers-windows)))))
#+end_src
***** Close buffers
#+begin_src emacs-lisp

(defun mv/close-mail-export ()
  (interactive)
  
  ;;kill the export buffer
  (let ((mail-buffer-string mv/mail-buffer))
    (dolist (buffer (buffer-list))
      (with-current-buffer buffer
	(when (and (eq major-mode 'org-msg-edit-mode)
                   (string-match-p (concat (regexp-quote mail-buffer-string) ".*") (buffer-name)))
	  (message (format "killing %s" buffer))
	  (kill-buffer buffer)))))

  ;; close duplicate mu4e headers window
  (close-duplicate-mu4e-headers)

  ;; kill mu4e-article
  (switch-to-buffer "*mu4e-article*")
  (mu4e-view-quit)

  ;; close duplicate mu4e headers window
  (close-duplicate-mu4e-headers))

#+end_src
***** Close stuff hook
#+begin_src emacs-lisp
(defun mv/run-remove-drafts ()
  (run-at-time "0.5 sec" nil 'mv/remove-drafts))
(defun mv/run-close-mail-export ()
  (run-at-time "0.5 sec" nil 'mv/remove-drafts))
(add-hook 'message-sent-hook 'mv/run-close-mail-export)
(add-hook 'message-sent-hook 'mv/run-remove-drafts)
#+end_src
*** Move cursor to beginning email
#+begin_src emacs-lisp 
(defun move-mail-to-writing-area ()
  (interactive)
  ;;(evil-scroll-line-down 12)
                                        ;(sleep-for 0.5)
  (search-forward ":END:")
  (evil-next-visual-line 3)
  (evil-insert 1)
  )
(add-hook 'org-msg-edit-mode-hook (lambda () (run-with-timer 0.8 nil #'move-mail-to-writing-area)))

#+end_src
*** Attachments
**** Attaching stuff myself
#+begin_src emacs-lisp 
;; ChatGPT made this. Allows me to save what is in between brackets
(defun get-between-brackets ()
  (interactive)
  (let ((bounds (bounds-of-thing-at-point 'list)))
    (when bounds
      (buffer-substring-no-properties (1+ (car bounds)) (cdr bounds)))))

(defun add-extra-attach ()
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (search-forward ":attachment: ")
    (if (not (string-equal (char-to-string (char-after (point))) "("))
        (progn
          (delete-char (- (line-end-position) (point)))
          (insert (prin1-to-string (split-string (current-kill 0) "\n"))))
      (progn
        (let* ((my-variable (get-between-brackets))
               (listmv (mapcar (lambda (in)
                                 ;; Add quotes
                                 (format "\"%s\""
                                         ;; Remove trailing ")"
                                         (replace-regexp-in-string ")\\'" ""
                                                                   ;; Remove quotes
                                                                   (replace-regexp-in-string "\"" ""
                                                                                             ;; Not sure what substring does
                                                                                             (substring in)))))
                               ;; Split strings on spaces, but only when they are in between quotes? (Came from ChatGPT)
                               (split-string (string-trim my-variable) "\"? +\""))))
          (mapc (lambda (var) (add-to-list 'listmv (format "\"%s\"" var))) (split-string (current-kill 0) "\n"))
          (delete-char (- (line-end-position) (point)))
          (insert (format "%s" listmv)))))))
#+end_src
** Responding to outlook invites
#+begin_src emacs-lisp
(require 'mu4e-icalendar)
(mu4e-icalendar-setup)
#+end_src
** mu4e-goodies
#+begin_src emacs-lisp
(load-file "/etc/nixos/other-config/emacs/mu4e/mu4e-goodies-utils.el")
(load-file "/etc/nixos/other-config/emacs/mu4e/mu4e-goodies-tags.el")
(defun add-tag-mv ()
  (interactive)
  (setq mu4e-action-tags-completion-list (split-string (shell-command-to-string "python3 /etc/nixos/other-config/emacs/mu4e/get-tags.py") ","))
  (mu4e-action-retag-message (mu4e-message-at-point)))
#+end_src
  
* Theming
See [[*Folding][Folding]]
#+begin_src emacs-lisp 
;; (package-install 'standard-themes) ;;nixos
(require 'color)
(load-file "/etc/nixos/other-config/emacs/mu4e/mu4e-headers.el")
;; I have to set this back to nil
(setq mu4e-headers-open-after-move nil) ; i1r
#+end_src
** Theme light and dark
- [["/home/misha/Nextcloud/scripts_misha/klad/mu4e-headers.el"][This]] file defines some new faces.
- Only the color of the faces changes with dark-light
- The bold font for the unread (flagged or not flagged) remains
*** Dark
#+begin_src emacs-lisp
(defun make-theme-dark-mv ()
  (interactive)
  (load-theme 'standard-dark)

  ;; set background
  (set-background-color "gray12")
  ;;(set-face-foreground 'default "gray60")

  ;; The "normal" thread child face (background + foreground) (inactive when mu4e-thread-folding-keep-faces is t)
  (set-face-background 'mu4e-thread-folding-child-face "gray20")
  (set-face-foreground 'mu4e-thread-folding-child-face "#a6a6a6")

  ;; foreground unread + flagged (outside thread)
  (set-face-foreground 'mu4e-unread-flagged-face "#ff6f60")

  ;; background + foreground unread + flagged (inside thread)
  (set-face-background 'mu4e-unread-flagged-child-face "gray20")
  (set-face-foreground 'mu4e-unread-flagged-child-face "#ff6f60")


  ;; background + foreground unread child face
  (set-face-background 'mu4e-unread-child-face "gray20")
  (set-face-foreground 'mu4e-unread-child-face "white")

  ;; background flagged child face
  (set-face-background 'mu4e-flagged-child-face "gray20")
  (set-face-foreground 'mu4e-flagged-child-face "#ff6f60")

  ;; background (inside thread)
  (set-face-background 'mu4e-child-face "gray20")
  (set-face-foreground 'mu4e-child-face "#a6a6a6")

  ;; root background (inactive when mu4e-thread-folding-keep-faces is t)
  (set-face-background 'mu4e-thread-folding-root-unfolded-face "gray10")
  (set-face-background 'mu4e-thread-folding-root-unfolded-face "gray10")
  ;; root foreground (inactive when mu4e-thread-folding-keep-faces is t)
  (set-face-foreground 'mu4e-thread-folding-root-folded-face "#a6a6a6")
  (set-face-foreground 'mu4e-thread-folding-root-unfolded-face "#a6a6a6")

  ;; set replied face
  (set-face-foreground 'mu4e-replied-face "#a6a6a6")

  ;; org block
  (set-face-attribute 'org-block nil :background "gray20")

  ;; colors of agenda
  (defun his-tracing-function (orig-fun &rest args)
    (let ((res (apply orig-fun args)))
      (mapcar (lambda (s)
                (add-face-text-property 1 (length s)
                                        ;;(string-match-p ":" s)
                                        (pcase (file-name-nondirectory (car args))
                                          ("personal.org" 'all-the-icons-green)
                                          ("personal_col.org" 'all-the-icons-green)
                                          ("work.org" 'all-the-icons-dorange)
                                          ("work_col.org" 'all-the-icons-dorange)
                                          ("teaching.org" 'all-the-icons-blue)
                                          ("teaching_col.org" 'all-the-icons-blue)
                                          ("bij1.org" 'help-argument-name)
                                          ("bij1_col.org" 'help-argument-name)
                                        ("amor.org" 'font-lock-property-use-face)
                                          )

                                        nil s))
              res)
      res))

  ;; <2023-12-12 Tue> I needed this to keep this face from being overwritten by this definition
  (set-face-foreground 'mu4e-flagged-face "#ff6f60")
  ;;(add-hook 'mu4e-headers-mode-hook 'make-theme-dark-mv)
  )

#+end_src
*** Light
#+begin_src emacs-lisp

(defun make-theme-light-mv ()
  (interactive)
  (load-theme 'standard-light)
  ;; The "normal" thread child face (background + foreground) (inactive when mu4e-thread-folding-keep-faces is t)
  (set-face-background 'mu4e-thread-folding-child-face "white smoke")
  (set-face-foreground 'mu4e-thread-folding-child-face "black")
  (set-face-foreground 'mu4e-forwarded-face "gray90")

  ;; foreground unread + flagged (outside thread)
  (set-face-foreground 'mu4e-unread-flagged-face "#e00033")
  (set-face-foreground 'mu4e-flagged-face "#e00033")

  ;; background + foreground, unread + flagged (inside thread)
  (set-face-background 'mu4e-unread-flagged-child-face "gray90")
  (set-face-extend 'mu4e-unread-flagged-child-face t)

  (set-face-foreground 'mu4e-unread-flagged-child-face "#e00033")

  (set-face-background 'mu4e-flagged-child-face "gray90")
  (set-face-foreground 'mu4e-flagged-child-face "#e00033")

  ;; background + foreground unread (inside thread)
  (set-face-background 'mu4e-unread-child-face "gray90")
  (set-face-foreground 'mu4e-unread-child-face "black")

  ;; background (inside thread)
  (set-face-background 'mu4e-child-face "gray90")
  (set-face-foreground 'mu4e-child-face "#7f7f7f")

  ;; root background (inactive when mu4e-thread-folding-keep-faces is t)
  (set-face-background 'mu4e-thread-folding-root-unfolded-face "gainsboro")
  (set-face-foreground 'mu4e-thread-folding-root-unfolded-face "black")
  ;; root background (inactive when mu4e-thread-folding-keep-faces is t)
  (set-face-background 'mu4e-thread-folding-root-folded-face "gainsboro")
  (set-face-foreground 'mu4e-thread-folding-root-folded-face "black")

  ;; set replied face
  (set-face-foreground 'mu4e-replied-face "#7f7f7f")

  ;; org agenda date
  (set-face-foreground 'org-agenda-date "black")

  ;; org blocks
  (set-face-attribute 'org-block nil :background
                      (color-darken-name (face-attribute 'default :background) 15))

  ;; set background
  (set-background-color "linen")

  ;; Colors of agenda
  ;; (defun his-tracing-function (orig-fun &rest args)
  ;;   (let ((res (apply orig-fun args)))
  ;;     (mapcar (lambda (s)
  ;;               (add-face-text-property 1 (length s)
  ;;                                       ;;(string-match-p ":" s)
  ;;                                       (pcase (file-name-nondirectory (car args))
  ;;                                         ("personal.org" 'bookmark-face)
  ;;                                         ("personal_col.org" 'bookmark-face)
  ;;                                         ("work.org" 'font-lock-comment-delimiter-face)
  ;;                                         ("work_col.org" 'font-lock-comment-delimiter-face)
  ;;                                         ("teaching.org" 'company-tooltip-common-selection)
  ;;                                         ("teaching_col.org" 'company-tooltip-common-selection)
  ;;                                         ("bij1.org" 'gnus-group-mail-1-empty)
  ;;                                         ("bij1_col.org" 'gnus-group-mail-1-empty)
  ;;                                         )

  ;;                                       nil s))
  ;;             res)
  ;;     res))

  ;; <2023-12-12 Tue> I needed this to keep this face from being overwritten by this definition
  (set-face-foreground 'mu4e-flagged-face "#e00033")
  )

#+end_src
*** Toggle
#+begin_src emacs-lisp
;; This script allows me to toggle between light and dark
(defun refresh-theme ()
  (interactive)
  (set-face-bold 'mu4e-unread-flagged-child-face t)
  (let* ((current-time (current-time))
         (current-hour (string-to-number (format-time-string "%H" current-time))))
    (if (or (< current-hour 6) (>= current-hour 17))
        (make-theme-dark-mv)
      (make-theme-light-mv)
      )))

(refresh-theme)

#+end_src
* Reveal
** Auto export to html at save
#+begin_src emacs-lisp
(defun mv/after-save-hook ()
  (interactive)
  (require 'f)
  (when (string-match-p "\\.org$" (buffer-file-name))
    (when (f-descendant-of? (file-truename buffer-file-name) "/home/misha/Nextcloud/mnotes/org-roam/presentations")
      (org-reveal-export-to-html-mv))))
;;(add-hook 'after-save-hook 'mv/after-save-hook)
#+end_src
** Fix image in reveal presentation
Made largely by ChatGPT
#+begin_src emacs-lisp 
(defun mv/replace-under-cursor ()
  (interactive)
  (let ((line (thing-at-point 'line t)))
    (when (string-match "\./images" line)
      (setq line (replace-match "\./../../../images" t t line))
      (delete-region (line-beginning-position) (line-end-position))
      (insert line))))
#+end_src
    
#+begin_src emacs-lisp
(defun update-image-reveal ()
  (interactive)
  (let* ((full-string (save-excursion
                        (beginning-of-line)
                        (buffer-substring-no-properties
                         (line-beginning-position)
                         (line-end-position)
			 )))
         (name-of-file (or (when (string-match "\\[\\[\\(.*/\\)\\(.*?\\)\\]\\]" full-string)
                             (match-string-no-properties 2 full-string))
			   (when (string-match "src=\"\\(.*\\)\"" full-string)
			     (file-name-nondirectory (match-string 1 full-string)))))


         (origin-path (cond
                       ((string-match "system-earth" (buffer-name))
                        (when name-of-file
                          (concat "/home/misha/Nextcloud/mnotes/org-roam/presentations/2023-2024/system-earth2023/images/"
                                  name-of-file)))
                       ((string-match "system-earth" (buffer-name))
                        (when name-of-file
                          (concat "/home/misha/Nextcloud/mnotes/org-roam/presentations/2023-2024/system-earth2023/images/"
                                  name-of-file)))
                       ((string-match "energy-transition" (buffer-name))
                        (when name-of-file
                          (concat "/home/misha/Nextcloud/mnotes/org-roam/presentations/2023-2024/intro-to-energy-transition/images/"
				  name-of-file)))
                       ((string-match "climate-sustainability" (buffer-name))
                        (when name-of-file
                          (concat "/home/misha/Nextcloud/mnotes/org-roam/presentations/2023-2024/intro-to-climate-and-sustainability2023/images/"
				  name-of-file)))
                       ((string-match "BigTime" (buffer-name))
                        (when name-of-file
                          (concat "/home/misha/Nextcloud/mnotes/org-roam/presentations/2023-2024/BigTime/images/"
				  name-of-file)))
                       ((string-match "ECS" (buffer-name))
                        (when name-of-file
                          (concat "/home/misha/Nextcloud/mnotes/org-roam/presentations/2023-2024/ECS/images/"
				  name-of-file)))
                       ))
         
         (destination (when origin-path
                        (expand-file-name (file-name-nondirectory origin-path) 
                                          "/home/misha/Nextcloud/mnotes/org-roam/presentations/images/"))))
    (when (and origin-path destination (file-exists-p origin-path))
      (copy-file origin-path destination t)
      (message "'%s' copied to '%s'" origin-path destination)
      (let ((org-image-regex "\\[\\[\\(\\.\\./\\)*\\./images/\\(.*\\)\\]\\]")
	    (html-image-regex "#\\+REVEAL_HTML: <img class=\"r-stretch\" src=\"\\(.*\\)\">"))
	(mv/replace-under-cursor)
        ))))
#+end_src

#+begin_src emacs-lisp
(defun mv/update-image-reveal-buffer ()
  (interactive)
  (save-excursion
    (while (search-forward "./images" nil t)
      (update-image-reveal))))
#+end_src 
  
#+begin_src emacs-lisp
(require 'ox-reveal)
(setq org-reveal-root "/home/misha/git/reveal.js")
(setq org-export-html-date-format-string "%d-%m-%Y")
(setq org-export-date-timestamp-format "%d-%m-%Y")
(setq org-reveal-plugins '(markdown notes))


(defun org-reveal-export-to-html-open-mv ()
  (interactive)
  (org-set-custom-id)
  (org-reveal-export-to-html)
  (rename-file (concat (file-name-sans-extension (buffer-file-name)) ".html") "./../" t)
  (shell-command (format "xdg-open %s" (concat "./../" (file-name-sans-extension (buffer-name)) ".html"))))

(defun org-reveal-export-to-html-mv ()
  (interactive)

  (let* ((current-dir (directory-file-name (expand-file-name default-directory)))
	 (last-two-dirs (mapconcat 'identity (last (split-string current-dir "/") 2) "/")))
    (if (string-equal last-two-dirs "PYW/org")
	;;(message "You're not in a directory ending with PYW/org")
	(org-html-export-to-html)
      (progn
	
	(org-set-custom-id)
	
	(org-reveal-export-to-html))

      ))

  (rename-file (concat (file-name-sans-extension (buffer-file-name)) ".html") "./../" t)
  )

(spc-define-keys spc-leader-map
  "oga" 'org-reveal-export-to-html-mv)
(spc-define-keys spc-leader-map
  "ogb" 'org-reveal-export-to-html-open-mv)
(spc-define-keys spc-leader-map
  "ogp" 'reveal-sync-presentations-exclude-drafts)

;; From ChatGPT
(defun org-set-custom-id ()
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (let ((count-top-level 0) 
          (count-sub-level 0) 
          parent-level headline-level)
      (while (re-search-forward org-heading-regexp nil t)
        (setq headline-level (length (match-string-no-properties 1)))
        (if (= headline-level 1)
            (save-excursion
              (setq count-top-level (1+ count-top-level))
              (setq count-sub-level 0)
              (org-entry-put (point) "CUSTOM_ID" (number-to-string count-top-level)))
          (save-excursion
            (setq count-sub-level (1+ count-sub-level))
            (org-entry-put (point) "CUSTOM_ID" (format "%d-%d" count-top-level count-sub-level))))))))

(defun reveal-sync-presentations-exclude-drafts ()
  (interactive)
  "Sync 'presentations' directories using rsync, excluding drafts."
  ;;(replace-reveal-root)
  (save-buffer)
  (shell-command
   "rsync -avm --delete --include='*/' --include='ltximg/***' --include='images/***' --exclude='*draft*.html' --include='*.html' --exclude='*' /home/misha/Nextcloud/mnotes/org-roam/presentations /home/misha/Nextcloud/mishathings-website/static/public/")
  (shell-command "bash /etc/nixos/other-config/emacs/update_website.sh")
  )

(defun reveal-toggle-speaker-notes ()
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (if (search-forward "#+REVEAL_INIT_OPTIONS: width:1100, slideNumber:true, hash:true, showNotes:'separate-page'" nil t)
        (progn
          (goto-char (point-min))
          (while (search-forward "#+REVEAL_INIT_OPTIONS: width:1100, slideNumber:true, hash:true, showNotes:'separate-page'" nil t)
            (replace-match "#+REVEAL_INIT_OPTIONS: width:1100, slideNumber:true, hash:true" nil t))
          (message "speaker notes off"))
      (while (search-forward "#+REVEAL_INIT_OPTIONS: width:1100, slideNumber:true, hash:true" nil t)
        (replace-match "#+REVEAL_INIT_OPTIONS: width:1100, slideNumber:true, hash:true, showNotes:'separate-page'" nil t)
        (message "speaker notes on")))))

(defun insert-pdf-link-reveal ()
  (interactive)
  (let* ((buffer-file-path (buffer-file-name))
         (replacement "/home/misha/Nextcloud/mnotes/mnotes_folders/")
         (modified-path (replace-regexp-in-string replacement "https://mishathings.org/public/" buffer-file-path))
         (html-path (replace-regexp-in-string "\.org$" ".html" modified-path)))
    (insert (format "[[%s?print-pdf][click]]" html-path))))
#+end_src
** push presentation
#+begin_src emacs-lisp
(defun push-presentation ()
  (interactive)
  (shell-command-to-string "ssh misha@$(pass ip/transip) 'rm /home/misha/git/reveal.js-remote/presentations/index.html'")

  (org-reveal-export-to-html-mv)

  (let ((new-content "		
                <script src=\"dist/reveal.js\"></script>
                <script src=\"plugin/notes/notes.js\"></script>
                <script src=\"plugin/search/search.js\"></script>
                <script src=\"plugin/markdown/markdown.js\"></script>
                <script src=\"plugin/highlight/highlight.js\"></script>

                <!--
                reveal.js-remote:
                the next two dependencies are required
                -->
                <script src=\"../socket.io/socket.io.js\"></script>
                <script src=\"../_remote/plugin.js\"></script>
                <script src=\"../_remote/remotezoom.js\"></script>

                <script>

                        // Also available as an ES module, see:
                        // https://revealjs.com/initialization/
                        Reveal.initialize({
                                controls: true,
                                progress: true,
                                center: true,
                                hash: true,

                                /*
                                        reveal.js-remote:
                                        optional configuration (with default values)
                                ,*/
                                remote: {
                                        // enable remote control
                                        remote: true,

                                        // enable multiplexing
                                        multiplex: true,

                                        // server address
                                        // change this if you do not serve the presentation from the same domain
                                        // example: https://presentations.jowisoftware.de
                                        server: window.location.protocol + \"//\" + window.location.host + \"/\",

                                        // path to socket.io
                                        // change this if the basepath of the server is not \"/\"
                                        path: \"/socket.io\",

                    // url of the presentation to share
                    shareUrl: window.location.href
                                },

                                // Learn about plugins: https://revealjs.com/plugins/
                                plugins: [ RevealRemoteZoom, RevealNotes, RevealSearch, RevealMarkdown, RevealHighlight, RevealRemote ]
                        });

                </script>

</body>
</html>"))
    (find-file (concat "./../" (file-name-sans-extension (buffer-name)) ".html"))
    (goto-char (point-min))
    (when (search-forward "<script src=\"https://cdn.jsdelivr.net/npm/reveal.js/dist/reveal.js\">" nil t)
      (delete-region (line-beginning-position) (point-max))
      (insert new-content))
    
    (save-buffer)
    (kill-buffer))
  
  (shell-command-to-string (format "rsync -rv --delete %s misha@$(pass ip/transip)://home/misha/git/reveal.js-remote/presentations/index.html" (concat "./../" (file-name-sans-extension (buffer-name)) ".html")))

  (shell-command-to-string "rsync -rv --delete /home/misha/Nextcloud/mnotes/org-roam/presentations/images/ misha@$(pass ip/transip)://home/misha/git/reveal.js-remote/presentations/images/")
  

  (shell-command "firefox http://mishathings.org:8080")

  )
#+end_src

* Latex 
** Normal latex
#+begin_src emacs-lisp
;; (package-install 'auctex) ;; nixos
;; Use pdf-tools to open PDF files
(setq TeX-view-program-selection '((output-pdf "PDF Tools"))
      TeX-source-correlate-start-server t)

;; Update PDF buffers after successful LaTeX runs
(add-hook 'TeX-after-compilation-finished-functions
          #'TeX-revert-document-buffer)

(add-hook 'LaTeX-mode-hook #'visual-line-mode)

(spc-define-keys spc-leader-map
  "lb" 'TeX-command-run-all
  )

(setq Latex-command "latex --synctex=1")
(setq TeX-source-correlate-mode t)
#+end_src
** Latex fragments
*** Functions and variables
#+begin_src emacs-lisp
(setq org-format-latex-options (plist-put org-format-latex-options :scale 1.5))
(setq constants-snip '(
                       ("Stefan Boltzmann Constant: 5.56*10**-8" . "5.67*10**-8")
                       ("Euler's number: 2.718281828459" . "2.718281828459")
                       ("Avogadro constant: 6.02214076*10**23" .  "6.02214076*10**23")
                       ("Constant in Wien's law: 2898" .  "2898")
                       ))

(defun constants_insert ()
  (interactive)
  (insert (format "%s" (alist-get (completing-read "Choose: " constants-snip) constants-snip nil nil #'string=)))
  (evil-insert 1))

(defun clear_latex_fragments_buffer ()
  (interactive)
  (org-clear-latex-preview (point-min) (point-max)))

(setq org-startup-latex-with-latex-preview t)
(defun insert_latex_fragments ()
  (interactive)
  (insert " \\(\\)")
  (evil-backward-char 2)
  (evil-insert 1))
(defun clear_latex_fragments_buffer ()
  (interactive)
  (org-clear-latex-preview (point-min) (point-max)))
(defun show_latex_fragments_buffer ()
  (interactive)
  (org--latex-preview-region (point-min) (point-max)))

;; This allows me to make the permil symbol
;; (add-to-list 'org-latex-packages-alist '("AUTO" "wasysym" t))

(setq latex-snip '(
                   ("CH4 (methane)" . "$CH_4")
                   ("double arrow" . "\\leftrightarrow")
                   ("Mg2+ (magnesium)" . "$Mg^{2+}")
                   ("pi" . "\\pi")
                   ("Pi" . "\\Pi")
                   ("micrometer" . "\\mu $m")
                   ("alpha" . "\\alpha")
                   ("beta" . "\\beta")
                   ("mu" . "\\mu")
                   ("Fe2+ (ferrous)" . "$Fe^{2+}")
                   ("Fe3+ (ferric)" . "$Fe^{3+}")
                   ("permil" . "\\permil")
                   ("approx" . "\\approx")
                   ("plusminus" . "\\pm")
                   ("sigma" . "\\sigma")
                   ("Sigma" . "\\Sigma")
                   ("NH4+ (ammonium)" . "$NH_4^+")
                   ("rho (density)" . "\\rho")
                   ("tau (stress)" . "\\tau")
                   ("N2 (nitrogen)" . "$N_2")
                   ("NH3 (ammonia)" . "$NH_3")
                   ("NO3- (nitrate)" . "$NO_3^-")
                   ("NO2- (nitrite)" . "$NO_2^-")
                   ("NO2 (nitrogen dioxide)" . "$NO_2")
                   ("NO (nitric oxide)" . "$NO")
                   ("N2O (nitrous oxide)" . "$N_2$O")
                   ("HNO3 (nitric acid)" . "$HNO_3")
                   ("SO42- (Sulfate ion)" . "$SO_4^{2-}")
                   ("lambda" . "\\lambda")
                   ("Delta" . "\\Delta")
                   ("H+ (hydrogen)" . "\\textrm{H}^+")
                   ("H3O+" . "$H_3$O^+")
                   ("H2O (water)" . "$H_2$O")
                   ("H2CO3 (carbonic acid)" . "$H_2$CO_3")
                   ("HCO3- (bicarbonate)" . "$HCO_3^-")
                   ("CO2 (CO2)" . "$CO_2")
                   ("CO32- (carbonate)" . "$CO_3^{2-}")
                   ("Ca2+ (calcium)" . "$Ca^{2+}")
                   ("O2 (Oxygen)" . "$O_2")
                   ("C6H12O6 (Glucose)" . "$C_6$H_12$O_6")
                   ("CaCO3 (calcium carbonate)" . "$CaCO_3")
                   ("MgCO3 (magnesium carbonate)" . "$MgCO_3")
                   ("Stefan Boltzmann equation" . "F = \\sigma T^4")
                   ))
(defun latex-insert-separate ()
  (interactive)
  (insert (format " \\(%s\\)" (alist-get (completing-read "Choose: " latex-snip) latex-snip nil nil #'string=)))
  (org-latex-preview)
  (evil-insert 1))
(defun latex-insert-add ()
  (interactive)
  (insert (alist-get (completing-read "Choose: " latex-snip) latex-snip nil nil #'string=)))

#+end_src
*** Keybindings
#+begin_src emacs-lisp
(spc-define-keys spc-leader-map
  "mli" 'insert_latex_fragments
  "ml1" 'latex-insert-separate
  "ml2" 'latex-insert-add
  "mlt" 'org-latex-preview
  "mlc" 'clear_latex_fragments_buffer
  "mls" 'show_latex_fragments_buffer
  )
#+end_src
* Python mode
#+begin_src emacs-lisp
(add-hook 'python-mode-hook (lambda () (eldoc-mode -1)))
#+end_src
* Yasnippet
#+begin_src emacs-lisp 
;; (package-install 'yasnippet) ;;nixos
;; (package-install 'yasnippet-snippets) ;; nixos
(require 'yasnippet)
(yas-global-mode 1)
(spc-define-keys spc-leader-map "ys" 'yas-insert-snippet)
#+end_src

* Org-mapping-notes 
** Add org-coor
#+begin_src emacs-lisp
(defun add-org-coor ()
  (interactive)
  (evil-paste-after 1)
  (evil-undo 1)
  (let ((var (car kill-ring)))
    (org-set-property "org-coor" var)
    (org-set-property "CREATED" (format-time-string "<%Y-%m-%d %a %z %H:%M>"))))
#+end_src
** The program
#+begin_src emacs-lisp
(load-file "/etc/nixos/other-config/emacs/org-mapping/org-mapping-notes.el")

(setq org-mapping-skip-files '(
                               "/home/misha/Nextcloud/mnotes/spatial_memory_project.org"
                               "/home/misha/Nextcloud/git/codeberg/org-mapping-notes/README.md"
                               "/home/misha/Nextcloud/git/pub-solar/org-mapping-notes/README.md"
                               "/home/misha/.emacs.d/history"
                               "/home/misha/emacs-troep/.emacs.default-28.28/history"
                               "/home/misha/Nextcloud/mnotes/spanish_practice.org"
                               "/home/misha/Nextcloud/mnotes/practicing_geological_timescales.org"
                               "/home/misha/Nextcloud/rc_files/emacs/README.org"
                               "/home/misha/Nextcloud/website/mapm/org-mapping-notes-docs/data.js"
                               "/home/misha/Nextcloud/mishathings-website/static/mapm/org-mapping-notes-docs/data.js"
                               "/home/misha/Nextcloud/mishathings-website/public/mapm/org-mapping-notes-docs/data.js"
                               ))
(setq org-mapping-base-folder "/home/misha/Nextcloud/mishathings-website/static/mapm")
(setq org-mapping-image-folder "/home/misha/Nextcloud/mishathings-website/static/mapm/imagesmv/")
(setq recoll-search-folder "/home/misha/Nextcloud/")
(setq spatial-memory-folder "/home/misha/Nextcloud/mnotes/org-roam/spatial_memory.org")
(setq mapping-code-folder "/etc/nixos/other-config/emacs/org-mapping")
(setq org-export-with-broken-links t)

(defun org-mapping-notes-build-map-and-sync ()
  (interactive)
  (prefer-coding-system 'utf-8)
  (org-mapping-notes-build-map)
  (shell-command "bash /etc/nixos/other-config/emacs/update_website.sh"))

#+end_src 

* COMMENT Nixos
#+begin_src emacs-lisp
(shell-command "sudo nixos-rebuild switch")
#+end_src

* Spelling check
#+begin_src emacs-lisp
(defun dutch_dic ()
  (interactive)
  (ispell-change-dictionary "nl_NL")
  (flyspell-buffer)
  )

(defun span_dic ()
  (interactive)
  (ispell-change-dictionary "spanish")
  (flyspell-buffer)
  )

(defun english_dic ()
  (interactive)
  (ispell-change-dictionary "en_US")
  (flyspell-buffer)
  )


;; <2024-01-02 Tue> I took this from /usr/local/share/emacs/30.0.50/lisp/textmodes/flyspell.el.gz I couldn't invoke it interactively because it's not available like that. So I took it and added "interactive"
(defun flyspell--mode-off ()
  "Turn Flyspell mode off."
  (interactive)
  (remove-hook 'post-command-hook (function flyspell-post-command-hook) t)
  (remove-hook 'pre-command-hook (function flyspell-pre-command-hook) t)
  (remove-hook 'after-change-functions 'flyspell-after-change-function t)
  (remove-hook 'hack-local-variables-hook
               (function flyspell-hack-local-variables-hook) t)
  (flyspell-delete-all-overlays)
  (setq flyspell-pre-buffer nil)
  (setq flyspell-pre-point  nil)
  (setq flyspell-mode nil))


(spc-define-keys spc-leader-map
  "osn" 'dutch_dic
  "ose" 'english_dic
  "oso" 'flyspell--mode-off
  "oss" 'span_dic
  "osc" 'flyspell-correct-word-before-point)

(defun flyspell-goto-previous-error (arg)
  "Go to arg previous spelling error."
  (interactive "p")
  (while (not (= 0 arg))
    (let ((pos (point))
          (min (point-min)))
      (if (and (eq (current-buffer) flyspell-old-buffer-error)
               (eq pos flyspell-old-pos-error))
          (progn
            (if (= flyspell-old-pos-error min)
                ;; goto end of buffer
                (progn
                  (message "Restarting from end of buffer")
                  (goto-char (point-max)))
              (backward-word 1))
            (setq pos (point))))
      ;; seek the previous error
      (while (and (> pos min)
                  (let ((ovs (overlays-at pos))
                        (r '()))
                    (while (and (not r) (consp ovs))
                      (if (flyspell-overlay-p (car ovs))
                          (setq r t)
                        (setq ovs (cdr ovs))))
                    (not r)))
        (backward-word 1)
        (setq pos (point)))
      ;; save the current location for next invocation
      (setq arg (1- arg))
      (setq flyspell-old-pos-error pos)
      (setq flyspell-old-buffer-error (current-buffer))
      (goto-char pos)
      (if (= pos min)
          (progn
            (message "No more miss-spelled word!")
            (setq arg 0))))))
#+end_src

* Google translate
#+begin_src emacs-lisp
;; (package-install 'google-translate) ;; nixos
(require 'google-translate)
;; the normal functions don't work with pdfs (but I can fix that)
(defun google-translate-mv ()
  (interactive)
  (if (eq major-mode 'pdf-view-mode)
      (pdf-view-kill-ring-save))
  (let* ((input (current-kill 0)))
    (google-translate-translate "auto" "en" input 'kill-ring)
    (shell-command-to-string (format "dunstify -t 70000 'Original text: %s\n\nTranslation: %s'" input (current-kill 0)))))

(defun tr_en_nl ()
  (interactive)
  (setq google-translate-default-source-language "en")
  (setq google-translate-default-target-language "nl")
  (let ((display-buffer-overriding-action '(display-buffer-in-direction . (direction right))))
    (google-translate-at-point))
  )

;; Google translate
(defun tr_en_es ()
  (interactive)
  (setq google-translate-default-source-language "en")
  (setq google-translate-default-target-language "es")
  (let ((display-buffer-overriding-action '(display-buffer-in-direction . (direction right))))
    (google-translate-at-point))
  )

(defun tr_es_en ()
  (interactive)
  (setq google-translate-default-source-language "es")
  (setq google-translate-default-target-language "en")
  (let ((display-buffer-overriding-action '(display-buffer-in-direction . (direction right))))
    (google-translate-at-point))
  )

(defun tr_nl_en ()
  (interactive)
  (setq google-translate-default-source-language "nl")
  (setq google-translate-default-target-language "en")
  (let ((display-buffer-overriding-action '(display-buffer-in-direction . (direction right))))
    (google-translate-at-point))
  )

(defun tr_en_nl ()
  (interactive)
  (setq google-translate-default-source-language "en")
  (setq google-translate-default-target-language "nl")
  (let ((display-buffer-overriding-action '(display-buffer-in-direction . (direction right))))
    (google-translate-at-point))
  )

(defun tr_nl_es ()
  (interactive)
  (setq google-translate-default-source-language "nl")
  (setq google-translate-default-target-language "es")
  (let ((display-buffer-overriding-action '(display-buffer-in-direction . (direction right))))
    (google-translate-at-point))
  )

(defun tr_es_nl ()
  (interactive)
  (setq google-translate-default-source-language "es")
  (setq google-translate-default-target-language "nl")
  (let ((display-buffer-overriding-action '(display-buffer-in-direction . (direction right))))
    (google-translate-at-point))
  )

(defun tr_en_en ()
  (interactive)
  (setq google-translate-default-source-language "en")
  (setq google-translate-default-target-language "en")
  (let ((display-buffer-overriding-action '(display-buffer-in-direction . (direction right))))
    (google-translate-at-point))
  )
#+end_src
* Misc
** Remove folder
#+begin_src emacs-lisp
(defun mv/empty-directory (dir)
  "Delete all files and directories in DIR."
  (when (and (file-exists-p dir) (file-directory-p dir))
    (dolist (file (directory-files dir t directory-files-no-dot-files-regexp))
      (if (file-directory-p file)
          (delete-directory file t) ; t for recursive
        (delete-file file)))))
#+end_src
** Remove-annotations
#+begin_src emacs-lisp
(defun remove-annotations ()
  (interactive)
  (let ((file-path (buffer-file-name)))
    (if file-path
        (shell-command (format "bash $HOME/Nextcloud/scripts_misha/misc_useful/remove-annotations.sh \"%s\"" file-path))
      (message "Buffer is not associated with a file."))
    (dirvish "/home/misha/Nextcloud/pdf_library/misc/no_annot")))
#+end_src
** Clean directory
#+begin_src emacs-lisp
(defun clean-directory (directory)
  "Recursively delete files ending with #, starting with .#, or ending with ~ in DIRECTORY."
  (let ((files (directory-files-recursively directory ".*")))
    (dolist (file files)
      (when (or (string-match-p "#$" file)
                (string-match-p "~$" file)
                (string-prefix-p (expand-file-name ".#" (file-name-directory file)) file))
        (message "Deleting file: %s" file)
        (delete-file file)))))

(defun clean-current-dirvish-directory ()
  "Recursively delete files ending with #, starting with .#, or ending with ~ in the current Dired directory."
  (interactive)
  (let ((directory (dired-current-directory)))
    (clean-directory directory)
    (revert-buffer)))
#+end_src
** Store file history recentf
#+begin_src emacs-lisp  
(recentf-mode)
(setq
 recentf-save-file "~/.emacs.d/recentf"
 recentf-max-saved-items 100000
 recentf-max-menu-items 5000
 )
(run-at-time (current-time) 300 'recentf-save-list)


(defun recentmv ()
  (interactive)
  (add-hook 'minibuffer-exit-hook 'turn-on-prescient-mode-mv)
  (ivy-prescient-mode -1)
  (find-file (ivy-read  "Select: " recentf-list))
  (ivy-prescient-mode 1))

(spc-define-keys spc-leader-map
  "fr" 'recentmv)

#+end_src
** Find folders locate
These function use dirfilter, which is a script that Daniel made to replace the following:
~locate -i --nul %s | xargs -r0 sh -c 'for i do [ -d \"$i\" ] && printf \"%%s\\n\" \"$i\"; done' sh {} +~
The script looks as follows, and is in my /usr/local/bin
#!/usr/bin/env python

import sys
import os

for line in sys.stdin:
if os.path.isdir(line[:-1]):
sys.stdout.write(line)

*** Find folders with locate
#+begin_src emacs-lisp
(defun find-folders-mv ()
  (interactive)
  (dirvish (ivy-read "Search folder and open in Dirvish: "
                     (lambda (str)
                       (let ((shell-command 
			      (format "locate -i %s | /etc/nixos/other-config/emacs/dirfilter.py" str)))
                         (split-string (shell-command-to-string shell-command) "\n")))
                     :dynamic-collection t)))

#+end_src
*** Find folders with locate + nautilus (non-fuzzy)
#+begin_src emacs-lisp
(defun find-folders-mv-nautilus ()
  (interactive)
  (call-process-shell-command (format "nautilus \"%s\" &" (ivy-read "Open Nautilus of folder: "
                                                                    (lambda (str)
                                                                      (let ((shell-command 
                                                                             (format "locate -i %s | /etc/nixos/other-config/emacs/dirfilter.py" str)))
                                                                        (split-string (shell-command-to-string shell-command) "\n")))
                                                                    :dynamic-collection t
                                                                    :action (lambda (x)
                                                                              (setq current-str ivy-text)))) nil 0 ))

#+end_src
*** Find folder of specific file and move focus to file
- The script below requires a bit of abacadabra (Daniel helped) to get the focus on the file.
- It worked for Daniel but it didn't work for me. The solution: the run-with-timer in the hook function.
#+begin_src emacs-lisp

(defvar dired-file nil)

(defun dired-goto-initial-pos ()
  (when dired-file
    (evil-goto-first-line)
    (search-forward dired-file)
    (evil-beginning-of-line))
  (setq dired-file nil))

(add-hook 'dired-initial-position-hook #'(lambda () (run-with-timer nil 1 #'dired-goto-initial-pos)))

(defun dirvish-open-folder-of-specified-file ()
  (interactive)
  (let* ((path (ivy-read "Open folder of file: "
                         (lambda (str)
                           (let ((shell-command 
                                  (format "locate -i %s" str)))
                             (split-string (shell-command-to-string shell-command) "\n")))
                         :dynamic-collection t))
         (folder (file-name-directory path))
         (file (file-name-nondirectory path)))
    (setq dired-file (if (> (length file) 20)
                         (substring-no-properties file 0 20)
                       file))
    (setq dired-file file)
    (dirvish folder)))

#+end_src
*** Set shortcuts
#+begin_src emacs-lisp
(spc-define-keys spc-leader-map
  "ds" 'find-folders-mv
  "df" 'find-folders-mv-nautilus
  "dl" 'dirvish-open-folder-of-specified-file)
#+end_src
** Select between spaces
Built by ChatGPT
#+begin_src emacs-lisp
(defun select-between-spaces ()
  "Select text between the nearest two spaces, or from the beginning of line 
to the first space, or from the last space to the end of line."
  (interactive)
  (let ((start (save-excursion
                 (search-backward " " (line-beginning-position) t)))
        (end (save-excursion
               (search-forward " " (line-end-position) t))))
    (cond
     ;; If both start and end are found and surrounded by spaces
     ((and start end)
      (goto-char (1+ start)) ; Move past the first space
      (set-mark (1- end))    ; Move to just before the second space
      (message "Selected: %s" (buffer-substring-no-properties (mark) (point))))
     
     ;; If at the start of the line, select to the first space
     (start
      (goto-char (1+ start)) ; Move past the space
      (set-mark (line-end-position)) ; Select to the end of the line
      (message "Selected: %s" (buffer-substring-no-properties (mark) (point))))
     
     ;; If at the end of the line, select from the last space
     (end
      (goto-char (line-beginning-position)) ; Move to the start of the line
      (set-mark (1- end)) ; Move to just before the end space
      (message "Selected: %s" (buffer-substring-no-properties (mark) (point))))    
     
     (t
      (message "No spaces found on this line.")))))

(define-key evil-inner-text-objects-map " " 'select-between-spaces)
#+end_src
** Time zone converter
#+begin_src emacs-lisp
(load-file "/etc/nixos/other-config/emacs/timezone/timezones.el" )
#+end_src
** Markdown-mode
#+begin_src emacs-lisp
(add-hook 'markdown-mode-hook #'visual-line-mode)
#+end_src

** Create file functions
*** Make klad
#+begin_src emacs-lisp
(defun make_klad ()
  (interactive)
  (counsel-find-file nil "/home/misha/Nextcloud/scripts_misha/klad/"))
#+end_src
*** Make mnotes
#+begin_src emacs-lisp
(defun create_mnotes ()
  (interactive)
  (let* ((filename1 (read-from-minibuffer "Enter name of file: "))
         (filename (replace-regexp-in-string " " "_" filename1))
         (filepath (format "~/Nextcloud/mnotes/%s.org" filename))
         (date (format-time-string "[%Y-%m-%d %a %H:%M]")))
    (if (file-exists-p filepath) (message "file already exists")
      (with-temp-file filepath
        (insert (format "#+TITLE: %s \n#+CREATED: %s\n#+LAST_MODIFIED: %s\n\n" filename1 date date)))
      (find-file filepath)
      (goto-line 5)
      (evil-insert 0))))

#+end_src

#+end_src
** Run block
*** Run all blocks
#+begin_src emacs-lisp
(defun mv/run-all-python-src-blocks ()
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (while (search-forward "#+begin_src python" nil t)
      (when (org-in-src-block-p)
        (run-block)))))
#+end_src
*** Python
This removes
#+begin_src emacs-lisp
(defun run-block ()
  (interactive)
  (if (inside-python-src-block-p)
      (progn 
        (setq display-buffer-alist
              ;; remove the "open in the same buffer" 
              (delq (assoc "^\\*Python\\*$" display-buffer-alist)
                    display-buffer-alist))
        (run-python nil nil t)
        (sleep-for 0.1)
        (org-babel-mark-block)
        (call-interactively 'python-shell-send-region)
        ;; add the "open in the same buffer" 
        (add-to-list 'display-buffer-alist 
                     '("^\\*Python\\*$" . (display-buffer-same-window))))
    (cl-letf (((symbol-function 'yes-or-no-p) (lambda (&rest args) t))
              ((symbol-function 'y-or-n-p) (lambda (&rest args) t)))
      (org-ctrl-c-ctrl-c))))

#+end_src
** Run region
*** Select region
#+begin_src emacs-lisp
(defun mv-get-region (&optional start end)
  "Send the region delimited by START and END to inferior Python process."
  (interactive)
  (let ((start (cond (start start)
                     ((region-active-p) (region-beginning))
                     (t (point-min))))
        (end (cond (end end)
                   ((region-active-p) (region-end))
                   (t (point-max)))))
    (list start end)))
#+end_src
*** Bash
#+begin_src emacs-lisp
(defun run-bash-region ()
  (interactive)
  (shell-command-on-region (region-beginning) (region-end) "bash"))
#+end_src
*** Run bash or python
#+begin_src emacs-lisp
(defun run-region-mv ()
  (interactive)
  (if (eq major-mode 'sh-mode) (run-bash-region) 
    (if (eq major-mode 'python-mode)
        (python-shell-send-region (region-beginning) (+ (region-end) 1))
      ;; (python-shell-send-region (car (mv-get-region)) (cdr (mv-get-region)))
      (if (eq major-mode 'racket-mode) (racket-send-region (region-beginning) (+ (region-end) 1) )))))
#+end_src
** Calc
#+begin_src emacs-lisp
(defun calc-mv ()
  (interactive)
  (evil-append 1)
  (insert (format "%.2f" (string-to-number (calc-eval (buffer-substring-no-properties
						       (save-excursion
							 (if (not (re-search-backward "[a-z]" (line-beginning-position) t))
							     (move-beginning-of-line nil))
							 (1- (re-search-forward "[0-9(]"))) (- (point) 1)))))))
(spc-define-keys spc-leader-map "/" 'calc-mv)
#+end_src
** Rename files
#+begin_src emacs-lisp
;; I prefer this rename function because the other shows the files in the folder in an expanding minibuffer
(defun rename-mv (&optional path)
  (interactive)
  (let* (
	 (fil buffer-file-name)
	 (dir (file-name-directory (buffer-file-name)))
	 (newn (if (not path)
		   (read-from-minibuffer "Enter name of file: ")
		 (read-from-minibuffer "Enter name of file: " path)))
	 (newp (format "%s%s" dir newn))
	 )
    (if (file-exists-p newp) (message "file already exists")
      (progn (rename-file fil newp)
	     (kill-this-buffer)
	     (find-file newp)))))
#+end_src
** y/n instead of yes/no
#+begin_src emacs-lisp  
(defalias 'yes-or-no-p 'y-or-n-p)
#+end_src
** Buffer bookmarks
#+begin_src emacs-lisp
(spc-define-keys spc-leader-map
  "bi" 'bookmark-set
  "bj" 'bookmark-jump
  "bD" 'bookmarks-delete
  "b1" 'mv/bookmark1
  "b2" 'mv/bookmark2
  "b3" 'mv/bookmark3
  "b4" 'mv/bookmark4
  "b5" 'mv/bookmark5
  "b6" 'mv/bookmark6
  "p1" 'mv/bookmarkgo1
  "p2" 'mv/bookmarkgo2
  "p3" 'mv/bookmarkgo3
  "p4" 'mv/bookmarkgo4
  "p5" 'mv/bookmarkgo5
  "p6" 'mv/bookmarkgo6
  )

(defun mv/bookmark1 ()
  (interactive)
  (bookmark-set "1"))
(defun mv/bookmark2 ()
  (interactive)
  (bookmark-set "2"))
(defun mv/bookmark3 ()
  (interactive)
  (bookmark-set "3"))
(defun mv/bookmark4 ()
  (interactive)
  (bookmark-set "4"))
(defun mv/bookmark5 ()
  (interactive)
  (bookmark-set "5"))
(defun mv/bookmark6 ()
  (interactive)
  (bookmark-set "6"))


(defun mv/bookmarkgo1 ()
  (interactive)
  (bookmark-jump "1"))
(defun mv/bookmarkgo2 ()
  (interactive)
  (bookmark-jump "2"))
(defun mv/bookmarkgo3 ()
  (interactive)
  (bookmark-jump "3"))
(defun mv/bookmarkgo4 ()
  (interactive)
  (bookmark-jump "4"))
(defun mv/bookmarkgo5 ()
  (interactive)
  (bookmark-jump "5"))
(defun mv/bookmarkgo6 ()
  (interactive)
  (bookmark-jump "6"))

(defun bookmarks-delete ()
  (interactive)
  (dolist (bookmark (bookmark-all-names))
    (bookmark-delete bookmark))
  (message "bookmarks deleted"))

;; This is to make sure that the mail opens in one window, and not half.
(defun mu4e--jump-to-bookmark (bookmark)
  "View the message referred to by BOOKMARK."
  (let ((msgid (bookmark-prop-get bookmark 'message-id)))
    ;;(message msgid)
    ;; Perform the search
    (mu4e-headers-search (concat "msgid:" msgid))
    ;; Delay the call to open-mail-no-split
    (run-with-timer 0.1 nil 'open-mail-no-split)))


#+end_src
** Copy buffer filename
#+begin_src emacs-lisp
(defun copy-buffer-filename ()
  (interactive)
  (kill-new buffer-file-name))
(spc-define-keys spc-leader-map
  "fyy" 'copy-buffer-filename
  )
#+end_src
** Delete org ids
#+begin_src emacs-lisp
(defun remove-org-ids-from-headlines ()
  "Remove all :ID: properties from all headlines in the current Org buffer."
  (interactive)
  (org-map-entries
   (lambda ()
     (when (org-at-heading-p)
       (org-delete-property "ID")))))


(defun remove-org-custom_ids-from-headlines ()
  "Remove all :ID: properties from all headlines in the current Org buffer."
  (interactive)
  (org-map-entries
   (lambda ()
     (when (org-at-heading-p)
       (org-delete-property "CUSTOM_ID")))))
#+end_src