Kostya's Emacs Configuration

Table of Contents

Setup

init.el Code

I use org-babel-tangle and this document, written in Org mode.

The code below extracts the elisp configuration code and creates/overwrites the ~/.emacs.d/init.el configuration file when the .org-file is saved. Therefore, changes are only done in the .org-file, where writing longer comments about how things work and why things are added is easier, hence the resulting init.el-file remains clean and without excessive comments.

This is what the init.el file should look like, prompting it to tangle the init.org file and replace itself with that code.

;; We need org in order to make use of the tangling functionality
 (require 'org)
 ;; Open the org-mode configuration
 (find-file (concat user-emacs-directory "init.org"))
 ;; Tangle the file
 (org-babel-tangle)
 ;; Load the tangled file
 (load-file (concat user-emacs-directory "init.el"))
 ;; Byte-compile it
(byte-compile-file (concat user-emacs-directory "init.el"))

Git tracking & practicalities

We also don't need to track the generated init.el file on Git, since it is directly derived from init.org.

This code makes Git ignore changes to init.el:

git update-index --assume-unchanged init.el

If you do want to start tracking the file again, you can use:

git update-index --no-assume-unchanged init.el

Lexical scoping

First, I want lexical scoping for the init-file, so I will add that to the top of the file.

;;; -*- lexical-binding: t -*-

Tangling

The init.el should (after the first run) mirror the source blocks in the init.org. We can use C-c C-v t to run org-babel-tangle, which extracts the code blocks from the current file into a source-specific file (in this case a .el-file).

To avoid doing this each time we can add a function to the after-save-hook ensuring that .org-document always tangles and byte-compiles after changes.

(defun tangle-init ()
   "If the current buffer is init.org, the code-blocks are
 tangled, and the tangled file is compiled."
   (when (equal (buffer-file-name)
                (expand-file-name (concat user-emacs-directory "init.org")))
     ;; Avoid running hooks when tangling.
     (let ((prog-mode-hook nil))
       (org-babel-tangle)
       (byte-compile-file (concat user-emacs-directory "init.el")))))

 (add-hook 'after-save-hook 'tangle-init)

Start-up

The line below is only for the KUT-lab that uses proxy!

(setq url-proxy-services
  '(("no_proxy" . "^\\(localhost\\|172.16.2.30\\)")
    ("http" . "proxy.noc.kochi-tech.ac.jp:3128")
    ("https" . "proxy.noc.kochi-tech.ac.jp:3128")))

Below are some tweaks to improve startup performance. First common tweak is to disable the garbage collector during initialization, and then resetting it afterwards.

(setq gc-cons-threshold (* 50 1000 1000))

;; Set and reset threshold
(let ((old-gc-treshold gc-cons-threshold))
  (setq gc-cons-threshold most-positive-fixnum)
  (add-hook 'after-init-hook
            (lambda () (setq gc-cons-threshold old-gc-treshold))))

(setq inhibit-startup-screen t) ;; disable startup screen

Fix IO bugs.

(setq process-adaptive-read-buffering nil)
(setq read-process-output-max (* 4 1024 1024))

Set Emacs user directory explicitly:

(setq user-emacs-directory "~/.emacs.d/")
(setq default-directory "~/")

Set UFT-8 as preferred coding system.

(set-language-environment "UTF-8")

Packages

To manage downloaded packages, Emacs comes with package.el installed. In addition, I want to use use-package, so let's make sure we have those loaded.

(require 'package)
(require 'use-package)
;; (setq use-package-always-ensure t)

Next, I'll set up my package sources. These are very common and well-maintained mirrors.

(setq package-archives
      '(("GNU ELPA"     . "https://elpa.gnu.org/packages/")
        ("MELPA"        . "https://melpa.org/packages/")
        ("ORG"          . "https://orgmode.org/elpa/")
        ("MELPA Stable" . "https://stable.melpa.org/packages/")
        ("nongnu"       . "https://elpa.nongnu.org/nongnu/"))
      package-archive-priorities
      '(("GNU ELPA"     . 20)
        ("MELPA"        . 15)
        ("ORG"          . 10)
        ("MELPA Stable" . 5)
        ("nongnu"       . 0)))

(package-initialize)

Defaults

General settings

(setq default-directory            "~/"      ;; Set default directory
        scroll-margin                0         ;; Space between top/bottom
        auto-revert-interval         1         ;; Refresh buffers fast
        echo-keystrokes              0.1       ;; Show keystrokes fast
        frame-inhibit-implied-resize 1         ;; Don't resize frame implicitly
        sentence-end-double-space    nil       ;; No double spaces
        recentf-max-saved-items      1000      ;; Show more recent files
        save-interprogram-paste-before-kill t  ;; Save copies between programs
        auto-fill-function 'do-auto-fill       ;; Auto-fill-mode everywhere
        warning-minimum-level :emergency       ;; Suppress startup warnings

       ; auto-fill-mode -1                     ;; visual fill
        fill-column 99999

        debug-on-error t
        split-width-threshold 0                ;; default vertical split for new buffers
        split-height-threshold nil
        )

Short answers

I want to only provide short answers to Emacs' prompts. E.g., I want to type "y" or "n" instead of out "yes" or "no".

(setq use-short-answers t)

Shortcuts for source-blocks

(require 'org-tempo)

(add-to-list 'org-structure-template-alist '("sh" . "src shell"))
(add-to-list 'org-structure-template-alist '("la" . "src latex"))
(add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
(add-to-list 'org-structure-template-alist '("py" . "src python"))
(add-to-list 'org-structure-template-alist '("ipy" . "src ipython"))
(add-to-list 'org-structure-template-alist '("ju" . "src jupyter-ipython"))
(add-to-list 'org-structure-template-alist '("r" . "src R"))
(add-to-list 'org-structure-template-alist '("ai" . "src ai"))
(add-to-list 'org-structure-template-alist '("pl" . "src plantuml"))
(add-to-list 'org-structure-template-alist '("gpt" . "src chatgpt-shell"))

Auto-saved directory

To avoid clutter, let's put all the auto-saved files into one and the same directory.

(defvar emacs-autosave-directory
      (concat user-emacs-directory "auto-save-list/")
      "This variable dictates where to put auto saves. It is set to a
      directory called autosaves located wherever your .emacs.d/ is
      located.")

    ;; Sets all files to be backed up and auto saved in a single directory.
  (setq backup-directory-alist
        `((".*" . ,emacs-autosave-directory))
        auto-save-file-name-transforms
        `((".*" ,emacs-autosave-directory t)))

Keys' directory

(setq auth-sources '("~/.emacs.d/secrets/.authinfo.gpg"))

(use-package exec-path-from-shell
  :ensure t)
(when (memq window-system '(mac ns x))
   (exec-path-from-shell-initialize))
(exec-path-from-shell-copy-env "OPENAI_API_KEY")

Editor configuration

I want to use the EditorConfig plugin, which helps maintain consistent coding styles across editors when collaborating.

(use-package editorconfig
  :ensure t
   :config
   (editorconfig-mode 1))

Smoother scrolling

I want scrolling to be a lot slower than it is by default.

(setq mouse-wheel-scroll-amount '(1 ((shift) . 1))) ;; one line at a time
(setq mouse-wheel-progressive-speed            nil) ;; don't accelerate scrolling
(setq mouse-wheel-follow-mouse                  't) ;; scroll window under mouse
(setq scroll-step                                1) ;; keyboard scroll one line at a time
(setq use-dialog-box                           nil) ;; Disable dialog

Visuals

Optimizing the look

(dolist (mode
         '(tool-bar-mode                          ;; Remove toolbar
           blink-cursor-mode))                    ;; Solid cursor, not blinking
  (funcall mode 0))

Frame settings

Emacs maximized and full-screen on a startup.

(set-frame-parameter (selected-frame) 'fullscreen 'maximized)
(add-to-list 'default-frame-alist  '(fullscreen . maximized))

Automatically resize frames your are working in

(use-package golden-ratio
  :defer t
  )

Delimiters, brackets, cursor

Bar cursor instead of a block cursor.

(setq-default cursor-type 'bar)

When coding, I want my delimiters (parentheses, brackets, etc.) to be colorized in pairs: rainbow-delimiters does exactly that.

(use-package rainbow-delimiters
  :ensure t
  :hook (prog-mode . rainbow-delimiters-mode))

;; Highlight-parentheses
(require 'highlight-parentheses)
 (global-highlight-parentheses-mode t)

Smart parentheses

(use-package smartparens
  :ensure smartparens  ;; install the package
  :hook (prog-mode text-mode markdown-mode) ;; add `smartparens-mode` to these hooks
  :config
  (require 'smartparens-config))

  ;; (add-hook 'LaTeX-mode-hook 'smartparens-mode)
  ;; (smartparens-global-mode t)
  ;; (sp-pair "\\[" "\\]")
  ;; (sp-pair "'" "")
  ;; (sp-pair "`" "")
  ;; (sp-pair "``" "''")
  ;; (show-smartparens-global-mode t)

Enable line numbers globally

;; (global-display-line-numbers-mode 1)

Font

(set-face-attribute 'default nil :font "DejaVu Sans Mono 15")

Theme

;; (use-package ef-themes
   ;;      :config
   ;;      (load-theme 'ef-tritanopia-dark t))

  ;; (use-package kaolin-themes
     ;;   :config
     ;;   (load-theme 'kaolin-galaxy t))

(use-package doom-themes
  :ensure t
  :config
  ;; Global settings (defaults)
  (setq doom-themes-enable-bold t    ; if nil, bold is universally disabled
        doom-themes-enable-italic t) ; if nil, italics is universally disabled
  (load-theme 'doom-monokai-pro t)

    ;; Enable flashing mode-line on errors
  (doom-themes-visual-bell-config)
    ;; Enable custom neotree theme (all-the-icons must be installed!)
  (doom-themes-neotree-config)
  ;; or for treemacs users
  (setq doom-themes-treemacs-theme "doom-colors") ; use "doom-colors" for less minimal icon theme
  (doom-themes-treemacs-config)
  ;; Corrects (and improves) org-mode's native fontification.
  (doom-themes-org-config))

Customize the color of the text selected by cursor

(set-face-attribute 'region nil :background "#d897f0" :foreground "#0b0309")

Add transparency

(add-to-list 'default-frame-alist '(alpha-background . 90))

Mode line

Configuration takes from here

;; Disables showing system load in modeline, useless anyway
(setq display-time-default-load-average nil)

(line-number-mode)
(column-number-mode)
(display-time-mode -1)
(size-indication-mode 0)

(use-package hide-mode-line
  :commands (hide-mode-line-mode))

 (use-package doom-modeline
   :ensure t
   :init
   (doom-modeline-mode 1)
   :config
   (setq doom-modeline-buffer-file-name-style 'truncate-upto-project
         doom-modeline-enable-word-count nil
         doom-modeline-buffer-encoding nil
         doom-modeline-icon t ;; Enable/disable all icons
         doom-modeline-modal-icon t ;; Icon for Evil mode
         doom-modeline-time t
         doom-modeline-major-mode-icon t
         doom-modeline-major-mode-color-icon t
         doom-modeline-buffer-modification-icon t
         doom-modeline-buffer-state-icon t
         doom-modeline-bar-width 3
         doom-modeline-text-height 140
         doom-modeline-height 30)) 

Adaptive wrap

Use adaptive-wrap to visually wrap lines.

(use-package adaptive-wrap
  :defer t
  :hook (visual-line-mode . adaptive-wrap-prefix-mode))

Minor modes for efficient writing

;; Text centering
(use-package olivetti
  :defer t
  :config
  (setq-default olivetti-body-width (+ fill-column 4)))

;; Dimming surrounding text
(use-package focus
  :defer t)

File browsing

Neotree

(use-package neotree
  :ensure t
   :config
   (global-set-key (kbd "C-c n e") 'neotree-toggle)
   (setq neo-window-fixed-size nil)
   (setq neo-theme (if (display-graphic-p) 'icons 'arrow)))

Switching between windows

(use-package ace-window
    :config
    (global-set-key (kbd "M-p") 'ace-window))

Dired

(use-package all-the-icons-dired
  :config
  (add-hook 'dired-mode-hook 'all-the-icons-dired-mode))

Dashboard

(use-package dashboard
  :config
  (dashboard-setup-startup-hook)
  :custom
  (dashboard-startup-banner 'logo)
  (dashboard-banner-logo-title nil)
  (dashboard-center-content t)
  (dashboard-icon-type 'all-the-icons)
  (dashboard-set-heading-icons t)
  (dashboard-set-file-icons t)
  (dashboard-set-navigator t)
  (dashboard-set-init-info t)
  (dashboard-page-separator "\n\n\n")
  (dashboard-projects-backend 'project-el)
  (dashboard-display-icons-p t)
  (dashboard-items '(
                     (recents . 5)
                     (agenda . 5)
                     (projects . 5)
                     (bookmarks . 5)
                     )))

Interaction

Tweaking default behavior

(dolist (mode
          '(column-number-mode        ;; Show current column number in mode line
            delete-selection-mode     ;; Replace selected text when yanking
            dirtrack-mode             ;; Directory tracking in shell
            ;; global-so-long-mode       ;; Mitigate performance for long lines
            global-visual-line-mode   ;; Break lines instead of truncating them
            global-auto-revert-mode   ;; Revert buffers automatically when they change
            recentf-mode              ;; Remember recently opened files
            savehist-mode             ;; Remember minibuffer prompt history
            save-place-mode           ;; Remember last cursor location in file
            show-paren-mode))         ;; Highlight matching parentheses
   (funcall mode 1))

 (setq history-length 25)        ;; Only save the last 25 minibuffer prompts
 (setq global-auto-revert-non-file-buffers t) ;; Revert Dired and other buffers

Terminal emulator

Make vterm not to double check before killing an instance of the terminal. One function is for toggling the vterm buffer with the other open buffer, and another binds a separate vterm instance to each M-n keystroke. The configuration is taken from here.

(use-package vterm
    :defer t

    :preface
    (let ((last-vterm ""))
      (defun toggle-vterm ()
        (interactive)
        (cond ((string-match-p "^\\vterm<[1-9][0-9]*>$" (buffer-name))
               (goto-non-vterm-buffer))
              ((get-buffer last-vterm) (switch-to-buffer last-vterm))
              (t (vterm (setq last-vterm "vterm<1>")))))

      (defun goto-non-vterm-buffer ()
        (let* ((r "^\\vterm<[1-9][0-9]*>$")
               (vterm-buffer-p (lambda (b) (string-match-p r (buffer-name b))))
               (non-vterms (cl-remove-if vterm-buffer-p (buffer-list))))
          (when non-vterms
            (switch-to-buffer (car non-vterms)))))

      (defun switch-vterm (n)
            (let ((buffer-name (format "vterm<%d>" n)))
              (setq last-vterm buffer-name)
              (cond ((get-buffer buffer-name)
                     (switch-to-buffer buffer-name))
                    (t (vterm buffer-name)
                       (rename-buffer buffer-name))))))

    :config
    ;; key to stop terminal process
   (define-key vterm-mode-map (kbd "C-c C-c") 'vterm--self-insert)
    ;; ;; Don't query about killing vterm buffers, just kill it
    (defadvice vterm (after kill-with-no-query nil activate)
      (set-process-query-on-exit-flag (get-buffer-process ad-return-value) nil)))

Spelling

See the instruction on setting up personal dictionary here.

;; find aspell and hunspell automatically
(cond
 ;; try hunspell at first
 ;; if hunspell does NOT exist, use aspell
 ((executable-find "hunspell")
  (setq ispell-program-name "hunspell")
  (setq ispell-local-dictionary "en_US")
  (setq ispell-local-dictionary-alist
        ;; Please note the list `("-d" "en_US")` contains ACTUAL parameters passed to hunspell
        ;; You could use `("-d" "en_US,en_US-med")` to check with multiple dictionaries
        '(("en_US" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "en_US") nil utf-8)))

  ;; new variable `ispell-hunspell-dictionary-alist' is defined in Emacs
  ;; If it's nil, Emacs tries to automatically set up the dictionaries.
  (when (boundp 'ispell-hunspell-dictionary-alist)
    (setq ispell-hunspell-dictionary-alist ispell-local-dictionary-alist)))

 ((executable-find "aspell")
  (setq ispell-program-name "aspell")
  ;; Please note ispell-extra-args contains ACTUAL parameters passed to aspell
  (setq ispell-extra-args '("--sug-mode=ultra" "--lang=en_US"))))
(setq ispell-personal-dictionary "~/MEGA/Job/Kochi/Emacs/Emacs_dictionary/.aspell.en.pws")

(add-to-list 'ispell-skip-region-alist '(":\\(PROPERTIES\\|LOGBOOK\\):" . ":END:")) ;; do not check code blocks
(add-to-list 'ispell-skip-region-alist '("#\\+begin_src" . "#\\+end_src"))

(add-hook 'text-mode-hook 'flyspell-mode)
(add-hook 'LaTeX-mode-hook 'flyspell-mode)
(add-hook 'prog-mode-hook 'flyspell-prog-mode)
(flyspell-mode 1)

harper (from here)

(use-package eglot
  :defer
  :hook
  (python-mode . eglot-ensure)
  ;; (markdown-mode . eglot-ensure)
  ;; (prog-mode . eglot-ensure)
  ;; (LaTeX-mode . eglot-ensure)
  ;; (org-mode . eglot-ensure)
  :bind (:map
         eglot-mode-map
         ("C-c e r" . eglot-rename)
         ("C-c e a" . eglot-code-actions)
         ("C-c e o" . eglot-code-action-organize-imports)
         ("C-c e d" . eldoc)
         ("C-c e f" . eglot-format)
         ("C-c e =" . eglot-format))
  ;; :config
  ;; (add-to-list 'eglot-server-programs
  ;;              '(markdown-mode . ("harper-ls" "--stdio"))
  ;; 	       '(prog-mode . ("harper-ls" "--stdio"))
             ;; '(LaTeX-mode . ("harper-ls" "--stdio"))
             ;; '(org-mode . ("harper-ls" "--stdio"))
         ;; )
  :custom
  (eglot-autoshutdown t) ;; default is to leave servers runing when last buffer exits
  (eglot-extend-to-xref nil)) ;; cover files found through xref (M-.)

(use-package highlight-indent-guides
  :ensure t
  :hook
  (prog-mode . highlight-indent-guides-mode)
  ;; (ess-mode . highlight-indent-guides-mode)
  :config
  (setq highlight-indent-guides-method 'character)
  ) 

Some flyspell enhancements

(use-package consult-flyspell
  :config
  ;; default settings
  (setq consult-flyspell-select-function nil
        consult-flyspell-set-point-after-word t
        consult-flyspell-always-check-buffer nil))

Auto-complete mode

(use-package corfu
  :custom
  (corfu-cycle t)                ;; Enable cycling for `corfu-next/previous'
  (corfu-auto t)                 ;; Enable auto completion
  (corfu-auto-delay 0)           ;; No delay
  (corfu-auto-prefix 2)          ;; Start when this many characters have been typed
  (corfu-popupinfo-delay 0.5)    ;; Short delay
  ;; (corfu-separator ?\s)          ;; Orderless field separator
  ;; (corfu-quit-at-boundary nil)   ;; Never quit at completion boundary
  ;; (corfu-quit-no-match nil)      ;; Never quit, even if there is no match
  ;; (corfu-preview-current nil)    ;; Disable current candidate preview
  (corfu-preselect 'prompt)      ;; Preselect the prompt
  ;; (corfu-on-exact-match nil)     ;; Configure handling of exact matches

  :init
  (global-corfu-mode))

(use-package emacs
  :init
  ;; TAB cycle if there are only few candidates
  (setq completion-cycle-threshold 3)

  ;; Hide commands in M-x which do not apply to the current mode.
  ;; Corfu commands are hidden, since they are not supposed to be used via M-x.
  (setq read-extended-command-predicate
        #'command-completion-default-include-p)

  ;; Enable indentation+completion using the TAB key.
  ;; `completion-at-point' is often bound to M-TAB.
  (setq tab-always-indent 'complete))

(use-package cape
  ;; Bind dedicated completion commands
  ;; Alternative prefix keys: C-c p, M-p, M-+, ...
  :bind (("C-c p p"  . completion-at-point) ;; capf
         ("C-c p t"  . complete-tag)        ;; etags
         ("C-c p d"  . cape-dabbrev)        ;; or dabbrev-completion
         ("C-c p h"  . cape-history)
         ("C-c p f"  . cape-file)
         ("C-c p k"  . cape-keyword)
         ("C-c p s"  . cape-symbol)
         ("C-c p a"  . cape-abbrev)
         ("C-c p l"  . cape-line)
         ("C-c p w"  . cape-dict)
         ("C-c p \\" . cape-tex)
         ("C-c p _"  . cape-tex)
         ("C-c p ^"  . cape-tex)
         ("C-c p &"  . cape-sgml)
         ("C-c p r"  . cape-rfc1345))
  :init
  ;; Add `completion-at-point-functions', used by `completion-at-point'.
  ;; NOTE: The order matters!
  (add-to-list 'completion-at-point-functions #'cape-dabbrev)
  (add-to-list 'completion-at-point-functions #'cape-file)
  (add-to-list 'completion-at-point-functions #'cape-elisp-block)
  (add-to-list 'completion-at-point-functions #'cape-history)
  (add-to-list 'completion-at-point-functions #'cape-keyword)
  (add-to-list 'completion-at-point-functions #'cape-tex)
  ;;(add-to-list 'completion-at-point-functions #'cape-sgml)
  ;;(add-to-list 'completion-at-point-functions #'cape-rfc1345)
  ;;(add-to-list 'completion-at-point-functions #'cape-abbrev)
  (add-to-list 'completion-at-point-functions #'cape-dict)
  ;;(add-to-list 'completion-at-point-functions #'cape-symbol)
  ;;(add-to-list 'completion-at-point-functions #'cape-line)
)

(use-package orderless
  :ensure t
  :config
  (setq completion-styles '(orderless basic partial-completion)
        completion-category-overrides '((file (styles basic partial-completion)))
        orderless-component-separator "[ |]"))

Which-key

This package enables display of key bindings

(use-package which-key
  :init (which-key-mode)
  :diminish which-key-mode
  :config
  (setq which-key-idle-delay 0.3))

Ivy mode

This package provides enhanced explanations to the functions. Configuration is taken from here.

  • While in an Ivy minibuffer, you can search within the current results by using S-Space.
  • To quickly jump to an item in the minibuffer, use C-' to get Avy line jump keys.
  • To see actions for the selected minibuffer item, use M-o and then press the action's key.
  • Super useful: Use C-c C-o to open ivy-occur to open the search results in a separate buffer. From there you can click any item to perform the ivy action.
(use-package ivy
     :diminish
     :bind (("C-s" . swiper)
            :map ivy-minibuffer-map
            ("TAB" . ivy-alt-done)
            ("C-f" . ivy-alt-done)
            ("C-l" . ivy-alt-done)
            ("C-j" . ivy-next-line)
            ("C-k" . ivy-previous-line)
            :map ivy-switch-buffer-map
            ("C-k" . ivy-previous-line)
            ("C-l" . ivy-done)
            ("C-d" . ivy-switch-buffer-kill)
            :map ivy-reverse-i-search-map
            ("C-k" . ivy-previous-line)
            ("C-d" . ivy-reverse-i-search-kill))
     :init
     (ivy-mode 1)
     :config
     (setq ivy-use-virtual-buffers t)
     (setq ivy-wrap t)
     (setq ivy-count-format "(%d/%d) ")
     (setq enable-recursive-minibuffers t)

;; Use different regex strategies per completion command
 (push '(completion-at-point . ivy--regex-fuzzy) ivy-re-builders-alist) ;; This doesn't seem to work...
 (push '(swiper . ivy--regex-ignore-order) ivy-re-builders-alist)
 (push '(counsel-M-x . ivy--regex-ignore-order) ivy-re-builders-alist)

 ;; Set minibuffer height for different commands
 (setf (alist-get 'counsel-projectile-ag ivy-height-alist) 15)
 (setf (alist-get 'counsel-projectile-rg ivy-height-alist) 15)
 (setf (alist-get 'swiper ivy-height-alist) 15)
 (setf (alist-get 'counsel-switch-buffer ivy-height-alist) 7))

(use-package ivy-hydra
:defer t
:after hydra)

  (use-package ivy-rich
    :ensure t
    :init
    (ivy-rich-mode 1)
    :config
    (setq ivy-format-function #'ivy-format-function-line)
    (setq ivy-rich-display-transformers-list
          (plist-put ivy-rich-display-transformers-list
                     'ivy-switch-buffer
                     '(:columns
                       ((ivy-rich-candidate (:width 40))
                        (ivy-rich-switch-buffer-indicators (:width 4 :face error :align right)); return the buffer indicators
                        (ivy-rich-switch-buffer-major-mode (:width 12 :face warning))          ; return the major mode info
                        (ivy-rich-switch-buffer-project (:width 15 :face success))             ; return project name using `projectile'
                        (ivy-rich-switch-buffer-path (:width (lambda (x) (ivy-rich-switch-buffer-shorten-path x (ivy-rich-minibuffer-width 0.3))))))
                                   ; return file path relative to project root or `default-directory' if project is nil
                       :predicate
                       (lambda (cand)
                         (if-let ((buffer (get-buffer cand)))
                             ;; Don't mess with EXWM buffers
                             (with-current-buffer buffer
                               (not (derived-mode-p 'exwm-mode)))))))))

(use-package all-the-icons-ivy-rich
  :ensure t
  :init (all-the-icons-ivy-rich-mode 1))

(use-package ivy-posframe
  :ensure t
  :init
  (ivy-posframe-mode 1)
  :config
  (setq ivy-posframe-display-functions-alist
        '((swiper                     . ivy-display-function-fallback)
          (complete-symbol . ivy-posframe-display-at-point)
          (counsel-M-x           . ivy-posframe-display-at-frame-center)
          (t                                . ivy-posframe-display)))
  )

Change the color of the searched text in the mini-buffer (taken from here)

(progn
  (set-face-attribute 'ivy-current-match nil :foreground "white"  :background "purple")
  (set-face-attribute 'ivy-minibuffer-match-face-2 nil :foreground "white" :background "red")
  (set-face-attribute 'ivy-minibuffer-match-face-3 nil :foreground "white" :background "darkgreen")
  (set-face-attribute 'ivy-minibuffer-match-face-4 nil :foreground "white" :background "blue")
  ;;
  (set-face-attribute 'swiper-match-face-2         nil :foreground "white" :background "red")
  (set-face-attribute 'swiper-match-face-3         nil :foreground "white" :background "darkgreen")
  (set-face-attribute 'swiper-match-face-4         nil :foreground "white" :background "blue"))

Counsel

     (use-package counsel
       :after ivy
       :demand t
       :bind (("M-x" . counsel-M-x)
              ("C-x b" . counsel-ibuffer)
              ("C-x C-f" . counsel-find-file)
              ("C-M-j" . counsel-switch-buffer)
              ("C-M-m" . counsel-imenu)
              :map minibuffer-local-map
              ("C-r" . 'counsel-minibuffer-history))
       :custom
       (counsel-linux-app-format-function #'counsel-linux-app-format-function-name-only)
       :config
       (setq ivy-initial-inputs-alist nil)) ;; Don't start searches with ^

  (use-package all-the-icons-ibuffer
    :ensure t
    :hook (ibuffer-mode . all-the-icons-ibuffer-mode))

(global-set-key (kbd "C-x C-b") 'ibuffer)

Helpful

Remap the help functions by referring to helpful package

(use-package helpful
   :custom
   (counsel-describe-function-function #'helpful-callable)
   (counsel-describe-variable-function #'helpful-variable)
   :bind
   ([remap describe-function] . counsel-describe-function)
   ([remap describe-command] . helpful-command)
   ([remap describe-variable] . counsel-describe-variable)
   ([remap describe-key] . helpful-key))

Vertico as alternative completion system

(use-package vertico
  :ensure t
  :init
  (vertico-mode 1)
  :config
  (setq vertico-count 25                       ; Show more candidates
                read-extended-command-predicate 'command-completion-default-include-p
                read-file-name-completion-ignore-case t  ; Ignore case of file names
                read-buffer-completion-ignore-case t     ; Ignore case in buffer completion
                completion-ignore-case t                 ; Ignore case in completion
                ))

Marginalia

Annotations in mini-buffer. Marginalia can be considered as ivy-rich replacement

(use-package marginalia
  :after vertico
  :ensure t
  :custom
  (marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil))
  :init
  (marginalia-mode))

Auto-save

Auto-save buffers except init.org

(use-package auto-save-buffers-enhanced
  :ensure t
  :config
  (auto-save-buffers-enhanced t)
  (setq auto-save-buffers-enhanced-exclude-regexps '("init.org")))

Version control

Magit is a Git client specifically for Emacs, and it's super powerful.

Let's first make sure we're highlighting uncommitted changes.

(use-package diff-hl
  :config
  (global-diff-hl-mode))

Then configure Magit.

(use-package magit
  :config
  (add-hook 'magit-pre-refresh-hook 'diff-hl-magit-pre-refresh)
  (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh))

Project management

Projectile provides a convenient project interaction interface. I keep most of my projects in a specific folder, so I'll set Projectile to check that path specifically.

(use-package projectile
  ; :bind (:map custom-bindings-map ("C-c p" . projectile-command-map))
  :config
  (setq projectile-project-search-path '("~/MEGA/Emacs-projects/")))

Org mode

General settings

Change the default programs that org uses for opening files

(add-to-list 'org-file-apps '("\\.html" . "brave-browser %s"))

Specify languages to be recognized by Org-mode

(org-babel-do-load-languages
 'org-babel-load-languages
  '((R . t)
    (latex . t)
    (shell . t)
    (python . t)
    (ipython . t)
    ;; (jupyter . t)
    (emacs-lisp . t)
    (plantuml . t)
    ))

(setq org-babel-python-command "/home/kostya/.local/bin/ipython3 --no-banner --classic --no-confirm-exit")

Specify the path to plantunl as instructed here

(setq org-plantuml-jar-path
      (expand-file-name "/usr/share/java/plantuml.jar"))

Taken from here:

(dolist (face '((org-level-1 . 1.2)
                (org-level-2 . 1.1)
                (org-level-3 . 1.05)
                (org-level-4 . 1.0)
                (org-level-5 . 1.1)
                (org-level-6 . 1.1)
                (org-level-7 . 1.1)
                (org-level-8 . 1.1)))
  (set-face-attribute (car face) nil :font "Cantarell" :weight 'regular :height (cdr face)))

;; Make sure org-indent face is available
;; (require 'org-indent)

;; Ensure that anything that should be fixed-pitch in Org files appears that way
(set-face-attribute 'org-block nil :foreground nil :inherit 'fixed-pitch)
(set-face-attribute 'org-code nil   :inherit '(shadow fixed-pitch))
(set-face-attribute 'org-indent nil :inherit '(org-hide fixed-pitch))
(set-face-attribute 'org-verbatim nil :inherit '(shadow fixed-pitch))
(set-face-attribute 'org-special-keyword nil :inherit '(font-lock-comment-face fixed-pitch))
(set-face-attribute 'org-meta-line nil :inherit '(font-lock-comment-face fixed-pitch))
(set-face-attribute 'org-checkbox nil :inherit 'fixed-pitch)

Add some setting to improve Org-mode's look

  ;; (add-hook 'org-mode-hook 'org-indent-mode)
  ;; (add-hook 'org-mode-hook 'turn-on-auto-fill)
  (add-hook 'org-mode-hook 'variable-pitch-mode)

(use-package org-bullets
  :ensure org-bullets
  :config
  :hook org-mode)

Styling

The part below is taken from here

(setq org-ellipsis " ▼")
;; ⤵ ▼ ⬎ ⮷  
(setq org-src-fontify-natively t) ;; Syntax highlighting in org src blocks 
(setq org-highlight-latex-and-related '(native)) ;; Highlight inline LaTeX
(setq org-startup-folded 'showeverything)
(setq org-image-actual-width 300)
(setq org-fontify-whole-heading-line t)
(setq org-pretty-entities t)

Colorize quote-blocks (taken from here)

(setq org-fontify-quote-and-verse-blocks t)

Colorize source-blocks (taken from here)

(custom-set-faces
 '(org-block-begin-line
   ((t (:underline "#A7A6AA" :foreground "#008ED1" :extend t))))
 '(org-block
   ((t (:background "#312c2c"))))
 '(org-block-end-line
   ((t (:underline "#A7A6AA" :foreground "#008ED1" :extend t))))
 '(org-quote
   ((t (:background "#312c2c"))))
)

Org-download lets us easily put copied screenshots into my org-documents.

(use-package org-download
  :after org
  :bind
  (:map org-mode-map
        (("s-t" . org-download-screenshot)
         ("s-y" . org-download-clipboard))))

Inline display of images, especially convenient for R

(add-hook 'org-babel-after-execute-hook 'org-display-inline-images 'append)   
(add-hook 'org-mode-hook 'org-display-inline-images)
(setq org-confirm-babel-evaluate nil)   ;don't prompt me to confirm everytime I want to evaluate a block

Org-modern

;; taken from [[https://github.com/jdtsmith/org-modern-indent][here]]
  (use-package org-modern
    :ensure t
    :custom
    (org-modern-hide-stars nil)		; adds extra indentation
    (org-modern-table nil)
    (org-modern-list 
     '(;; (?- . "-")
       (?* . "•")
       (?+ . "‣")))
    (org-modern-block-name '("" . "")) ; or other chars; so top bracket is drawn promptly
    :hook
    (org-mode . org-modern-mode)
    (org-agenda-finalize . org-modern-agenda))

      ;; Add frame borders and window dividers
      (modify-all-frames-parameters
       '(
         (bottom-divider-width .  0)
         (internal-border-width . 30)))
      (dolist (face '(window-divider
                      window-divider-first-pixel
                      window-divider-last-pixel))
        (face-spec-reset-face face)
        (set-face-foreground face (face-attribute 'default :background)))
      (set-face-background 'fringe (face-attribute 'default :background))

      (setq
       ;; Edit settings
       org-auto-align-tags nil
       org-tags-column 0
       org-catch-invisible-edits 'show-and-error
       org-special-ctrl-a/e t
       org-insert-heading-respect-content t

       ;; Org styling, hide markup etc.
       org-hide-emphasis-markers t
       org-pretty-entities t

       ;; Agenda styling
       org-agenda-tags-column 0
       org-agenda-block-separator ?─
       org-agenda-time-grid
       '((daily today require-timed)
         (800 1000 1200 1400 1600 1800 2000)
         " ┄┄┄┄┄ " "┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄")
       org-agenda-current-time-string
       "◀── now ─────────────────────────────────────────────────")

      ;; Ellipsis styling
      (setq org-ellipsis "…")
      (set-face-attribute 'org-ellipsis nil :inherit 'default :box nil)

      (global-org-modern-mode)

Visual tweaks taken from here

(defun my/prettify-symbols-setup ()

  ; Greek symbols
  ;; (push '("lambda" . ?λ) prettify-symbols-alist)
  ;; (push '("delta" .  ?Δ) prettify-symbols-alist)
  ;; (push '("gamma" .  ?Γ) prettify-symbols-alist)
  ;; (push '("phi" .    ?φ) prettify-symbols-alist)
  ;; (push '("psi" .    ?ψ) prettify-symbols-alist)

  ;; ;; org-abel
  (push '("#+BEGIN_SRC" . ?≫) prettify-symbols-alist)
  (push '("#+END_SRC" . ?≫) prettify-symbols-alist)
  (push '("#+begin_src" . ?≫) prettify-symbols-alist)
  (push '("#+end_src" . ?≫) prettify-symbols-alist)

  (push '("#+BEGIN_QUOTE" . ?❝) prettify-symbols-alist)
  (push '("#+END_QUOTE" . ?❞) prettify-symbols-alist)

  (prettify-symbols-mode))

(add-hook 'org-mode-hook        #'my/prettify-symbols-setup)
(add-hook 'org-agenda-mode-hook #'my/prettify-symbols-setup)

BibTeX integration

(defun org-mode-reftex-setup () (load-library "reftex")
       (and (buffer-file-name)(file-exists-p
                               (buffer-file-name))
            (reftex-parse-all)) (define-key org-mode-map (kbd "C-c [") 'reftex-citation))
(add-hook 'org-mode-hook 'org-mode-reftex-setup)

(use-package citar
  :custom
  (citar-bibliography '("~/MEGA/Emacs-projects/RoamNotes/references/Library.bib"))
  (org-cite-global-bibliography '("~/MEGA/Emacs-projects/RoamNotes/references/Library.bib"))
  (org-cite-insert-processor 'citar)
  (org-cite-follow-processor 'citar)
  (org-cite-activate-processor 'citar)
  (citar-bibliography org-cite-global-bibliography)
  :hook
  (org-mode . citar-capf-setup)
  (markdown-mode . citar-capf-setup)
  :bind
  (:map org-mode-map :package org ("C-c b" . #'org-cite-insert)))

(use-package citar-embark
  :after citar embark
  :no-require
  :config (citar-embark-mode))

(setq citar-at-point-function 'embark-act)

(defvar citar-indicator-notes-icons
  (citar-indicator-create
   :symbol (all-the-icons-material
            "speaker_notes"
            :face 'all-the-icons-blue
            :v-adjust -0.3)
   :function #'citar-has-notes
   :padding "  "
   :tag "has:notes"))

Polymode and ESS for reading and exporting .rmd files

(use-package poly-R
  :ensure t)

(use-package ess
  :ensure t)

(require 'ess-r-flymake)
(require 'ess-r-insert-obj)
(require 'ess-view-data)

(setq tab-always-indent t)

Apply the familiar (RStudio or similar) design to R with ESS (taken from here)

(add-hook 'ess-mode-hook
          (lambda ()
            (ess-set-style 'C++ 'quiet)
            (add-hook 'local-write-file-hooks
                      (lambda ()
                        (ess-nuke-trailing-whitespace)))))
(setq ess-nuke-trailing-whitespace-p 'ask)

Tweak the polymode exporter to avoid errors (taken from here and here) :

(defcustom polymode-exporter-output-file-format "%s"
  "Format of the exported files.
      %s is substituted with the current file name sans extension."
  :group 'polymode-export
  :type 'string)

;; associate the new polymode to Rmd files:
(add-to-list 'auto-mode-alist
             '("\\.[rR]md\\'" . poly-gfm+r-mode))

;; uses braces around code block language strings:
(setq markdown-code-block-braces t)

LaTeX export options

Specify the way orgmode exports LaTeX to PDF via the org-latex-pdf-process General export way suggested here is the one that works correctly for me. This one also works, plus thanks to the XeLaTeX, it correctly renders Japanese symbols and exports to LaTeX format C-c C-e l l smoothly.

;; (setq org-latex-pdf-process '("texi2dvi -p -b -V %f"))
(setq org-latex-pdf-process '("xelatex -interaction=nonstopmode -output-directory %o %f"
                              "xelatex -interaction=nonstopmode -output-directory %o %f"
                              "xelatex -interaction=nonstopmode -output-directory %o %f"))

Automatically process PDF on save (taken from here)

(defun org-export-as-pdf ()
  (interactive)
  (save-buffer)
  (org-latex-export-to-pdf))

(add-hook 
 'org-mode-hook
 (lambda()
   (define-key org-mode-map 
       (kbd "s-e") 'org-export-as-pdf)))

Instant preview of LaTeX components

(use-package org-fragtog
  :hook (org-mode . org-fragtog-mode)
  :config
  (setq org-latex-create-formula-image-program 'dvisvgm) ;; sharper
  (plist-put org-format-latex-options :scale 2)) ;; bigger

(require 'ox-latex)
(unless (boundp 'org-latex-classes)
  (setq org-latex-classes nil))

LaTeX templates for org-mode

(with-eval-after-load 'ox-latex
  (add-to-list 'org-latex-classes
               '("kochi-article"
                 "\\documentclass[12pt,a4paper]{article}
    \\usepackage{amsmath}
    \\usepackage[mathlines]{lineno}
    \\usepackage[margin=.9in]{geometry}
    \\usepackage[english]{babel}
    \\usepackage[T1]{fontenc}
    \\usepackage{adjustbox}
    \\usepackage{amsfonts}
    \\usepackage{amssymb}
    \\usepackage{amstext}
    \\usepackage{array}
    \\usepackage{arydshln}
    \\usepackage{booktabs}
    \\usepackage{boxedminipage}
    \\usepackage{caption}
    \\usepackage{colortbl}
    \\usepackage{csquotes}
    \\usepackage{dcolumn}
    \\usepackage{endfloat}
    \\usepackage{enumerate}
    \\usepackage{epsfig}
    \\usepackage{epstopdf}
    \\usepackage{etoolbox}
    \\usepackage{gensymb}
    \\usepackage{graphicx}
    \\usepackage{hyperref}
    \\usepackage{indentfirst}
    \\usepackage{latexsym}
    \\usepackage{multirow}
    \\usepackage{oldgerm}
    \\usepackage{rotating}
    \\usepackage{setspace}
    \\usepackage{siunitx}
    \\usepackage{subcaption}
    \\usepackage[noabbrev]{cleveref}
    \\usepackage[comma]{natbib}
    \\usepackage[normalem]{ulem}
    \\pagestyle{empty}
          [NO-DEFAULT-PACKAGES]
          [NO-PACKAGES]"
                 ("\\section{%s}" . "\\section*{%s}")
                 ("\\subsection{%s}" . "\\subsection*{%s}")
                 ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
                 ("\\paragraph{%s}" . "\\paragraph*{%s}")
                 ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))))

Handling PDFs

Use pdf-tools instead of a doc-view for PDFs=

(pdf-tools-install)
(add-hook 'pdf-tools-enabled-hook 'pdf-view-midnight-minor-mode) ;; dark theme by default for PDFs

org-noter for annotating PDFs

(setq org-noter-property-doc-file "NOTER_DOCUMENT"
      org-noter-property-note-location "NOTER_PAGE")

Enable linking of PDFs with org-mode (taken from here)

    (use-package org-noter
      :init
      (setq org-noter-enable-org-roam-integration t)
      :config
      ;; Your org-noter config ........
      (require 'org-noter-pdftools)
      (setq  org-noter-pdftools-markup-pointer-color "orange"))

    (use-package org-pdftools
      :hook (org-mode . org-pdftools-setup-link))

    (use-package org-noter-pdftools
      :after org-noter
      :config
      ;; Add a function to ensure precise note is inserted
      (defun org-noter-pdftools-insert-precise-note (&optional toggle-no-questions)
        (interactive "P")
        (org-noter--with-valid-session
         (let ((org-noter-insert-note-no-questions (if toggle-no-questions
                                                       (not org-noter-insert-note-no-questions)
                                                     org-noter-insert-note-no-questions))
               (org-pdftools-use-isearch-link t)
               (org-pdftools-use-freepointer-annot t))
           (org-noter-insert-note (org-noter--get-precise-info))))))

    ;; fix https://github.com/weirdNox/org-noter/pull/93/commits/f8349ae7575e599f375de1be6be2d0d5de4e6cbf
  (defun org-noter-set-start-location (&optional arg)
    "When opening a session with this document, go to the current location.
With a prefix ARG, remove start location."
    (interactive "P")
    (org-noter--with-valid-session
     (let ((inhibit-read-only t)
           (ast (org-noter--parse-root))
           (location (org-noter--doc-approx-location (when (called-interactively-p 'any) 'interactive))))
       (with-current-buffer (org-noter--session-notes-buffer session)
         (org-with-wide-buffer
          (goto-char (org-element-property :begin ast))
          (if arg
              (org-entry-delete nil org-noter-property-note-location)
            (org-entry-put nil org-noter-property-note-location
                           (org-noter--pretty-print-location location))))))))
  (with-eval-after-load 'pdf-annot
    (add-hook 'pdf-annot-activate-handler-functions #'org-noter-pdftools-jump-to-note))

Patch to select more than one line in PDF for inserting a note (taken from here)

;; Added patch function
(defun org-noter--check-and-convert-location (location)
  "If the location is an org-noter-pdftools-location, it transforms 
it into a (page . height) cons, otherwise it keeps the cons
unaltered"
  (if (org-noter-pdftools--location-p location)
      (cons (org-noter-pdftools--location-page location)
            (org-noter-pdftools--location-height location))
    location))

Customize TODO formatting in org PDF output (taken from here)

  (defun my-org-latex-format-headline-function
    (todo todo-type priority text tags _info)
  "Default format function for a headline.
See `org-latex-format-headline-function' for details."
  (concat
   (and todo (format "{\\bfseries\\sffamily\\color{%s} %s} "
                     (pcase todo-type
                       ('todo "orange")
                       ('done "green"))
                     todo))
   (and priority (format "\\framebox{\\#%c} " priority))
   text
   (and tags
    (format "\\hfill{}\\textsc{%s}"
        (mapconcat #'org-latex--protect-text tags ":")))))

(setq org-latex-format-headline-function 'my-org-latex-format-headline-function)

TOC in Org files

toc-org creates nice, Markdown compatible tables of content for your Org files.

(use-package toc-org
  :config
  (add-hook 'org-mode-hook 'toc-org-mode)

  ;; enable in markdown, too
  (add-hook 'markdown-mode-hook 'toc-org-mode))

Agenda

 (setq org-directory "~/MEGA/Emacs-projects/OrgFiles")

 ;; If you only want to see the agenda for today
 ;; (setq org-agenda-span 'day)

(setq org-agenda-start-with-log-mode t)
(setq org-log-done t)
(setq org-log-done 'time)
(setq org-log-into-drawer t)
(setq org-clock-continuously t)
(setq org-icalendar-include-todo t)

(add-to-list 'auto-mode-alist '("\\.org$" . org-mode))
(global-set-key (kbd "C-c s l") #'org-store-link)

Custom TODO states and Agendas (taken from here)

(setq org-todo-keywords
      '((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d!)")
        (sequence "BACKLOG(b)" "PLAN(p)" "READY(r)" "ACTIVE(a)" "REVIEW(v)" "WAIT(w@/!)" "HOLD(h)" "|" "COMPLETED(c)" "CANC(k@)")))

Large part regarding org-agenda (below) is taken from here and here

;; Configure custom agenda views
(setq org-agenda-custom-commands
  '(("d" "Dashboard"
     ((agenda "" ((org-deadline-warning-days 7)))
      (todo "NEXT"
        ((org-agenda-overriding-header "Next Tasks")))
      (tags-todo "agenda/ACTIVE" ((org-agenda-overriding-header "Active Projects")))))

    ("A" "Daily agenda and top priority tasks"
       ((tags-todo "*"
                   ((org-agenda-skip-function '(org-agenda-skip-if nil '(timestamp)))
                    (org-agenda-skip-function
                     `(org-agenda-skip-entry-if
                       'notregexp ,(format "\\[#%s\\]" (char-to-string org-priority-highest))))
                    (org-agenda-block-separator nil)
                    (org-agenda-overriding-header "Important tasks without a date\n")))
        (agenda "" ((org-agenda-span 1)
                    (org-deadline-warning-days 0)
                    (org-agenda-block-separator nil)
                    (org-scheduled-past-days 0)
                    ;; We don't need the `org-agenda-date-today'
                    ;; highlight because that only has a practical
                    ;; utility in multi-day views.
                    (org-agenda-day-face-function (lambda (date) 'org-agenda-date))
                    (org-agenda-format-date "%A %-e %B %Y")
                    (org-agenda-overriding-header "\nToday's agenda\n")))
        (agenda "" ((org-agenda-start-on-weekday nil)
                    (org-agenda-start-day "+1d")
                    (org-agenda-span 3)
                    (org-deadline-warning-days 0)
                    (org-agenda-block-separator nil)
                    (org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done))
                    (org-agenda-overriding-header "\nNext three days\n")))
        (agenda "" ((org-agenda-time-grid nil)
                    (org-agenda-start-on-weekday nil)
                    ;; We don't want to replicate the previous section's
                    ;; three days, so we start counting from the day after.
                    (org-agenda-start-day "+4d")
                    (org-agenda-span 14)
                    (org-agenda-show-all-dates nil)
                    (org-deadline-warning-days 0)
                    (org-agenda-block-separator nil)
                    (org-agenda-entry-types '(:deadline))
                    (org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done))
                    (org-agenda-overriding-header "\nUpcoming deadlines (+14d)\n")))))

    ("n" "Next Tasks"
     ((todo "NEXT"
        ((org-agenda-overriding-header "Next Tasks")))))


    ("W" "Work Tasks" tags-todo "+work")

    ;; Low-effort next actions
    ("e" tags-todo "+TODO=\"NEXT\"+Effort<15&+Effort>0"
     ((org-agenda-overriding-header "Low Effort Tasks")
      (org-agenda-max-todos 20)
      (org-agenda-files org-agenda-files)))

    ("w" "Workflow Status"
     ((todo "WAIT"
            ((org-agenda-overriding-header "Waiting on External")
             (org-agenda-files org-agenda-files)))
      (todo "REVIEW"
            ((org-agenda-overriding-header "In Review")
             (org-agenda-files org-agenda-files)))
      (todo "PLAN"
            ((org-agenda-overriding-header "In Planning")
             (org-agenda-todo-list-sublevels nil)
             (org-agenda-files org-agenda-files)))
      (todo "BACKLOG"
            ((org-agenda-overriding-header "Project Backlog")
             (org-agenda-todo-list-sublevels nil)
             (org-agenda-files org-agenda-files)))
      (todo "READY"
            ((org-agenda-overriding-header "Ready for Work")
             (org-agenda-files org-agenda-files)))
      (todo "ACTIVE"
            ((org-agenda-overriding-header "Active Projects")
             (org-agenda-files org-agenda-files)))
      (todo "COMPLETED"
            ((org-agenda-overriding-header "Completed Projects")
             (org-agenda-files org-agenda-files)))
      (todo "CANC"
            ((org-agenda-overriding-header "Cancelled Projects")
             (org-agenda-files org-agenda-files)))))))

(global-set-key (kbd "C-c a") #'org-agenda)

Google calendar integration

Based on this

(setq plstore-cache-passphrase-for-symmetric-encryption t)

(use-package org-gcal
    :ensure t
    :config
    (setq org-gcal-client-id ""
      org-gcal-client-secret ""
      org-gcal-file-alist '(("" .  "~/MEGA/Emacs-projects/OrgFiles/GoogleSchedule.org"))))

Org-clock

Taken from here

(use-package org-clock
  :after org
  :custom
  ;; Save clock history accross emacs sessions (read var for required info)
  (org-clock-persist t)
  ;; If idle for more than 15 mins, resolve by asking what to do with clock
  (org-clock-idle-time 15)
  ;; Don't show current clocked in task
  (org-clock-clocked-in-display nil)
  ;; Show more clocking history
  (org-clock-history-length 10)
  ;; Include running time in clock reports
  (org-clock-report-include-clocking-task t)
  ;; Put all clocking info int the "CLOCKING" drawer
  (org-clock-into-drawer "CLOCKING")
  ;; Setup default clocktable summary
  (org-clock-clocktable-default-properties
   '(:maxlevel 2 :scope file :formula % ;; :properties ("Effort" "Points")
               :sort (5 . ?t) :compact t :block today))
  :bind (:map global-map
              ("C-c j" . (lambda () (interactive) (org-clock-jump-to-current-clock)))
              :map org-mode-map
              ("C-c C-x r" . (lambda () (interactive) (org-clock-report)))))

Org-capture templates

Integrate org-journal, as specified here

(use-package org-journal
  :ensure t
  :defer t
  :init
  ;; Change default prefix key; needs to be set before loading org-journal
  (setq org-journal-prefix-key "C-c o")
  :config
  (setq org-journal-dir "~/MEGA/Emacs-projects/OrgFiles/Journal/"
        org-journal-date-format "%A, %d %B %Y"))

(defun org-journal-find-location ()
  ;; Open today's journal, but specify a non-nil prefix argument in order to
  ;; inhibit inserting the heading; org-capture will insert the heading.
  (org-journal-new-entry t)
  (unless (eq org-journal-file-type 'daily)
    (org-narrow-to-subtree))
  (goto-char (point-max)))

Include the org-capture, which works together with the browser extension. The custom function below is for capturing ArXiv entries

(defun transform-square-brackets-to-round-ones(string-to-transform)
  "Transforms [ into ( and ] into ), other chars left unchanged."
  (concat 
   (mapcar #'(lambda (c) (if (equal c ?[) ?\( (if (equal c ?]) ?\) c))) string-to-transform))
  )

;; Kill the frame if one was created for the capture
(defvar kk/delete-frame-after-capture 0 "Whether to delete the last frame after the current capture")

(defun kk/delete-frame-if-neccessary (&rest r)
  (cond
   ((= kk/delete-frame-after-capture 0) nil)
   ((> kk/delete-frame-after-capture 1)
    (setq kk/delete-frame-after-capture (- kk/delete-frame-after-capture 1)))
   (t
    (setq kk/delete-frame-after-capture 0)
    (delete-frame))))

(advice-add 'org-capture-finalize :after 'kk/delete-frame-if-neccessary)
(advice-add 'org-capture-kill :after 'kk/delete-frame-if-neccessary)
(advice-add 'org-capture-refile :after 'kk/delete-frame-if-neccessary)

Set up org-protocol required by org-capture

(server-start)
(add-to-list 'load-path "~/.local/share/applications/org-protocol.desktop")
(require 'org-protocol)

Based on this example

(setq org-capture-templates
         '(("t" "Tasks / Projects")
           ("tt" "Task" entry
            (file+olp "~/MEGA/Emacs-projects/OrgFiles/Tasks.org" "Inbox")
            "* TODO %?\n  %U\n  %a\n  %i" :empty-lines 1)
           ("ts" "Clocked Entry Subtask" entry (clock)
            "* TODO %?\n  %U\n  %a\n  %i" :empty-lines 1)

         ("a" "Appointment" entry
          (file  "~/MEGA/Emacs-projects/OrgFiles/GoogleSchedule.org")
          "* %?\n:PROPERTIES:\n:calendar-id:\tk.ovsiannikov@gmail.com\n:END:\n:org-gcal:\n%^T--%^T\n:END:\n\n"
          :jump-to-captured t
          :empty-lines 1)

           ("j" "Journal Entries")
           ("jj" "Journal" entry
            (file+olp+datetree "~/MEGA/Emacs-projects/OrgFiles/journal.org")
            "\n* %<%I:%M %p> - Journal :journal:\n\n%?\n\n"
            :clock-in :clock-resume
            :empty-lines 1)

           ("jo" "Org-Journal" plain
            (function org-journal-find-location)
            "** %(format-time-string org-journal-time-format)%^{Title}\n%i%?"
            :jump-to-captured t :immediate-finish t)

           ("jm" "Meeting" entry
            (file+olp+datetree "~/MEGA/Emacs-projects/OrgFiles/journal.org")
            "* %<%I:%M %p> - %a :meetings:\n\n%?\n\n"
            :clock-in :clock-resume
            :empty-lines 1)

           ("jl" "Liya" entry
            (file+datetree "~/MEGA/Liya/Liya-Milestones.org")
            "\n* %?\n %U\n  %i\n" :emty-lines 1)

          ("p" "Web quote" entry
            (file+headline "~/MEGA/Emacs-projects/OrgFiles/notes.org" "Web")
            ;; (file+headline (expand-file-name "notes.org" org-directory) "Web")
            "*  [[%:link][%:description]] \nCaptured On: %U\n #+BEGIN_QUOTE\n%i\n#+END_QUOTE\n%?")

           ("L" "Web link" entry
            (file+headline "~/MEGA/Emacs-projects/OrgFiles/notes.org" "Web")
            ;; (file+headline (expand-file-name "notes.org" org-directory) "Web")
            "* %? [[%:link][%(transform-square-brackets-to-round-ones \"%:description\")]] %(progn (setq kk/delete-frame-after-capture 2) \"\")\nCaptured On: %U"
            :emty-lines 1)
           ))


(global-set-key (kbd "C-c c") #'org-capture)


(with-eval-after-load 'org
  (add-hook 'org-mode-hook 'org-ref-prettify-mode))

Org-Roam

(use-package org-roam
  :ensure t
  :init
  (setq org-roam-v2-ack t)
  :custom
  (org-roam-directory "~/MEGA/Emacs-projects/RoamNotes/")
  (org-roam-completion-everywhere t)
  (org-roam-dailies-capture-templates
      '(
        ("d" "default" entry "* %<%I:%M %p>: %?"
         :if-new (file+head "%<%Y-%m-%d>.org" "#+title: %<%Y-%m-%d>\n"))))
  (org-roam-capture-templates
      '(
        ("m" "main" plain "%?"
         :if-new (file+head "main/%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n#+date: %U\n")
         :unnarrowed t)
        ("b" "bibliography notes" plain    
         (file "~/MEGA/Emacs-projects/RoamNotes/Templates/BibNoteTemplate.org")
         :target (file+head "references/notes/${citekey}.org"
                              "#+title: ${title}\n#+date: %U\n#+filetags: BibNote\n#+options: author:nil, date:nil, toc:nil, num:5, H:5, html-postamble:nil\n")
         :emty-lines 1)
        ("l" "programming language" plain
         "* Characteristics\n\n- Family: %?\n- Inspired by: \n\n* Reference:\n\n"
         :if-new (file+head "main/%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n")
         :unnarrowed t)
        ("n" "book notes" plain
         (file "~/MEGA/Emacs-projects/RoamNotes/Templates/BookNoteTemplate.org")
         :if-new (file+head "main/%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n")
         :unnarrowed t)
        ("p" "project" plain "* Goals\n\n%?\n\n* Tasks\n\n** TODO Add initial tasks\n\n* Dates\n\n"
         :if-new (file+head "main/%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n#+category: ${title}\n#+filetags: Project")
         :unnarrowed t)
        )
      )
  :bind (("C-c n l" . org-roam-buffer-toggle)
         ("C-c n f" . org-roam-node-find)
         ("C-c n i" . org-roam-node-insert)
         ("C-c n g" . org-roam-graph)
         :map org-roam-dailies-map
         ("Y" . org-roam-dailies-capture-yesterday)
         ("T" . org-roam-dailies-capture-tomorrow))
  :bind-keymap
  ("C-c n d" . org-roam-dailies-map)
  :config
  (setq org-roam-capture-ref-templates ;; based on: https://org-roam.discourse.group/t/passing-protocol-capture-text-with-quotations-inside/1970
        '(
          ("r" "ref" entry
           "* Quote\n#+BEGIN_QUOTE\n%(replace-regexp-in-string \"^\" \" \" \"${body}\")\n#+END_QUOTE\n%?"
           :if-new (file+head "webrefs/%<%Y%m%d%H%M%S>-webref.org"
                              "#+title: ${title}\n#+category: ${title}\n#+date: %U\n#+filetags: web-capture\n")
           :unnarrowed t
           :empty-lines 1)
          )
        )
  (setq org-roam-node-display-template (concat "${title:*} " (propertize "${tags:10}" 'face 'org-tag))) ;; for a more informative completion interface
  (org-roam-db-autosync-mode)
  (require 'org-roam-dailies) ;; Ensure the keymap is available
  (require 'org-roam-protocol) ;; If using org-roam-protocol
  )

Custom functions from System Crafters

Fast node insertion

;; Bind this to C-c n I
(defun org-roam-node-insert-immediate (arg &rest args)
  (interactive "P")
  (let ((args (cons arg args))
        (org-roam-capture-templates (list (append (car org-roam-capture-templates)
                                                  '(:immediate-finish t)))))
    (apply #'org-roam-node-insert args)))

Build Org-agenda with Org-roam notes

(defun my/org-roam-filter-by-tag (tag-name)
  (lambda (node)
    (member tag-name (org-roam-node-tags node))))

(defun my/org-roam-list-notes-by-tag (tag-name)
  (require 'org-roam-node) ;; this was missing from the source and has to be added!
  (mapcar #'org-roam-node-file
          (seq-filter
           (my/org-roam-filter-by-tag tag-name)
           (org-roam-node-list))))

(defun my/org-roam-refresh-agenda-list ()
  (interactive)
  (require 'org-roam-node)
  (setq org-agenda-files (my/org-roam-list-notes-by-tag "Project")))

;; Build the agenda list the first time for the session
(my/org-roam-refresh-agenda-list)

Selecting from a list of notes with a specific tag

(defun my/org-roam-project-finalize-hook ()
  "Adds the captured project file to `org-agenda-files' if the
capture was not aborted."
  ;; Remove the hook since it was added temporarily
  (remove-hook 'org-capture-after-finalize-hook #'my/org-roam-project-finalize-hook)

  ;; Add project file to the agenda list if the capture was confirmed
  (unless org-note-abort
    (with-current-buffer (org-capture-get :buffer)
      (add-to-list 'org-agenda-files (buffer-file-name)))))

(defun my/org-roam-find-project ()
  (interactive)
  ;; Add the project file to the agenda after capture is finished
  (add-hook 'org-capture-after-finalize-hook #'my/org-roam-project-finalize-hook)

  ;; Select a project file to open, creating it if necessary
  (org-roam-node-find
   nil
   nil
   (my/org-roam-filter-by-tag "Project")
   nil ;; added this based on the comment under the video - it's essential for the function to work!
   :templates
  '(
    ("p" "project" plain "* Goals\n\n%?\n\n* Tasks\n\n** TODO Add initial tasks\n\n* Dates\n\n"
    :if-new (file+head "main/%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n#+category: ${title}\n#+filetags: Project")
    :unnarrowed t)
   )))

(global-set-key (kbd "C-c n p") #'my/org-roam-find-project)

Streamlined custom capture for tasks and notes

(defun my/org-roam-capture-inbox ()
  (interactive)
  (org-roam-capture- :node (org-roam-node-create)
                     :templates '(("i" "inbox" plain "* %?"
                                   :if-new (file+head "Inbox.org" "#+title: Inbox\n")))))

(global-set-key (kbd "C-c n b") #'my/org-roam-capture-inbox)

Capture a task directly into a specific project

(defun my/org-roam-capture-task ()
  (interactive)
  ;; Add the project file to the agenda after capture is finished
  (add-hook 'org-capture-after-finalize-hook #'my/org-roam-project-finalize-hook)

  ;; Capture the new task, creating the project file if necessary
  (org-roam-capture- :node (org-roam-node-read
                            nil
                            (my/org-roam-filter-by-tag "Project"))
                     :templates '(("p" "project" plain "** TODO %?"
                                   :if-new (file+head+olp "main/%<%Y%m%d%H%M%S>-${slug}.org"
                                                          "#+title: ${title}\n#+category: ${title}\n#+filetags: Project"
                                                          ("Tasks"))))))

(global-set-key (kbd "C-c n t") #'my/org-roam-capture-task)

Automatically copy (or move) completed tasks to dailies

(defun my/org-roam-copy-todo-to-today ()
  (interactive)
  (let ((org-refile-keep t) ;; Set this to nil to delete the original!
        (org-roam-dailies-capture-templates
         '(("t" "tasks" entry "%?"
            :if-new (file+head+olp "%<%Y-%m-%d>.org" "#+title: %<%Y-%m-%d>\n" ("Tasks")))))
        (org-after-refile-insert-hook #'save-buffer)
        today-file
        pos)
    (save-window-excursion
      (org-roam-dailies--capture (current-time) t)
      (setq today-file (buffer-file-name))
      (setq pos (point)))

    ;; Only refile if the target file is different than the current file
    (unless (equal (file-truename today-file)
                   (file-truename (buffer-file-name)))
      (org-refile nil nil (list "Tasks" today-file nil pos)))))

(add-to-list 'org-after-todo-state-change-hook
             (lambda ()
               (when (equal org-state "DONE")
                 (my/org-roam-copy-todo-to-today))))

Creating the property "type" for the notes (taken from here)

(cl-defmethod org-roam-node-type ((node org-roam-node))
  "Return the TYPE of NODE."
  (condition-case nil
      (file-name-nondirectory
       (directory-file-name
        (file-name-directory
         (file-relative-name (org-roam-node-file node) org-roam-directory))))
    (error "")))

Modifying the display template to show the node “type”

(setq org-roam-node-display-template
      (concat "${type:15} ${title:*} " (propertize "${tags:10}" 'face 'org-tag)))

org-roam-bibtex and org-noter integration

Taken from here

helm-bibtex

 ;; IMP: Ensure 'latexmk' installed as a system package!
;; see also: http://www.jonathanleroux.org/bibtex-mode.html
(use-package helm-bibtex
  :config
  (setq bibtex-completion-bibliography '("~/MEGA/Emacs-projects/RoamNotes/references/Library.bib"))  ; location of .bib file containing bibliography entries
  (setq bibtex-completion-find-additional-pdfs t)                          ; support for multiple pdfs for one %citekey
  (setq bibtex-completion-pdf-field "File")                                ; in bib entry, file = {/path/to/file.pdf} could be set to locate the accompanying file
  ;; for multiple files use, file = {:/path/to/file0.pdf:PDF;:/path/to/file1.pdf:PDF}
  (setq bibtex-completion-library-path '("~/MEGA/Emacs-projects/RoamNotes/references/documents/"))  ; in this dir, %citekey-name(s).pdf would automatically attach pdf(s) to %citekey
  ;; if only !exist "file" field in bib entry
  (setq bibtex-completion-notes-path "~/MEGA/Emacs-projects/RoamNotes/references/")           ; dir to keep notes for the pdfs

  ;; BEGIN: Change insert citation (<f3>) behaviour of helm-bibtex for org-mode 
  (defun custom/bibtex-completion-format-citation-org (keys)
    "Custom cite definition for org-mode"
    (s-join ", "
            (--map (format "cite:&%s" it) keys)))

  (setq bibtex-completion-format-citation-functions
      '((org-mode      . custom/bibtex-completion-format-citation-org)
          (latex-mode    . bibtex-completion-format-citation-cite)
          (markdown-mode . bibtex-completion-format-citation-pandoc-citeproc)
          (python-mode   . bibtex-completion-format-citation-sphinxcontrib-bibtex)
          (rst-mode      . bibtex-completion-format-citation-sphinxcontrib-bibtex)
          (default       . bibtex-completion-format-citation-default))
      )
  ;; END: Change insert citation (<f3>) behaviour of helm-bibtex for org-mode

  (setq bibtex-autokey-year-length 4                          ; customisations for 'bibtex-generate-autokey'
      bibtex-autokey-name-year-separator "-"                ; press C-c C-c (bibtex-clean-entry) on a bib entry w/o %citekey
      bibtex-autokey-year-title-separator "-"               ; to automatically insert a %citekey based on meta data
      bibtex-autokey-titleword-separator "-"                ; use M-x crossref-add-bibtex-entry <ret>: to add an entry from
      bibtex-autokey-titlewords 2                           ; https://www.crossref.org/
      bibtex-autokey-titlewords-stretch 1
      bibtex-autokey-titleword-length 5
      bibtex-completion-pdf-symbol "⌘"
      bibtex-completion-notes-symbol "✎"))

org-roam-bibtex

(use-package org-roam-bibtex
  :after org-roam
  :config
   ;(setq bibtex-completion-edit-notes-function 'bibtex-completion-edit-notes-default) ; default to org-ref for notes
  (setq bibtex-completion-edit-notes-function 'orb-bibtex-completion-edit-note) ; use org-roam-capture-templates for notes
  (setq orb-preformat-keywords '("citekey" "title" "url" "author-or-editor" "keywords" "file") ; customisation for notes, org-noter integration
      orb-process-file-keyword t
      orb-attached-file-extensions '("pdf"))
  (require 'org-ref)) ; optional: if using Org-ref v2 or v3 citation links

org-noter integration

(setq org-noter-notes-search-path '("/home/kostya/MEGA/Emacs-projects/RoamNotes/references/notes/")) ; V IMPORTANT: SET FULL PATH!

Global key-binding

(global-set-key (kbd "C-c f r") 'helm-bibtex) 

Org-related extensions

org-reveal

(use-package ox-reveal
  :ensure t
  :config
  (setq org-reveal-root "file:///home/kostya/bin/reveal.js/"))

ox-report

(use-package ox-report
  :custom
  (ox-report-logo "/home/kostya/MEGA/Pictures/")
  )

Website with Org

(use-package simple-httpd
  :ensure t)

LLM

(use-package gptel
  :config
  ;; (setq
  ;;  gptel-model 'gemma3:latest
  ;;  gptel-backend (gptel-make-ollama "Ollama"
  ;;              :host "localhost:11434"
  ;;              :stream t
  ;;              :models '(gemma3:latest)))
  (setq gptel-api-key "")
  (setq gptel-model 'gpt-5-mini)
  (setq gptel-default-mode 'org-mode)
  (setq gptel-use-curl nil)
  (setq gptel-log-level "info"))

LaTeX

Auctex setup

(require 'auctex-latexmk)
(auctex-latexmk-setup)

(setq-default TeX-master nil)
(setq TeX-view-program-selection '((output-pdf "Evince")))
(setq TeX-save-query nil)
(setq TeX-PDF-mode t)
(setq TeX-parse-self t)
(setq TeX-auto-save t)
(setq TeX-source-correlate-method 'synctex)
(setq TeX-source-correlate-start-server t)
(setq auctex-latexmk-inherit-TeX-PDF-mode t)

(add-hook 'LaTeX-mode-hook 'TeX-toggle-debug-bad-boxes)
(add-hook 'LaTeX-mode-hook 'TeX-toggle-debug-warnings)
(add-hook 'LaTeX-mode-hook 'flyspell-mode)
(add-hook 'LaTeX-mode-hook 'prettify-symbols-mode)
(add-hook 'LaTeX-mode-hook 'LaTeX-math-mode)
(add-hook 'LaTeX-mode-hook 'TeX-source-correlate-mode)
(add-hook 'LaTeX-mode-hook 'TeX-interactive-mode)
(add-hook 'LaTeX-mode-hook #'TeX-fold-mode)
(add-hook 'LaTeX-mode-hook 'simpleclip-mode)
(add-hook 'LaTeX-mode-hook 'display-line-numbers-mode)
(add-hook 'prog-mode-hook 'display-line-numbers-mode)

Indentation

(defun LaTeX-indent-item ()
   "Provide proper indentation for LaTeX \"itemize\",\"enumerate\", and
 \"description\" environments.

   \"\\item\" is indented `LaTeX-indent-level' spaces relative to
   the the beginning of the environment.

   Continuation lines are indented either twice
   `LaTeX-indent-level', or `LaTeX-indent-level-item-continuation'
   if the latter is bound."
   (save-match-data
     (let* ((offset LaTeX-indent-level)
            (contin (or (and (boundp 'LaTeX-indent-level-item-continuation)
                             LaTeX-indent-level-item-continuation)
                        (* 1 LaTeX-indent-level)))
            (re-beg "\\\\begin{")
            (re-end "\\\\end{")
            (re-env "\\(itemize\\|\\enumerate\\|description\\)")
            (indent (save-excursion
                      (when (looking-at (concat re-beg re-env "}"))
                        (end-of-line))
                      (LaTeX-find-matching-begin)
                      (current-column))))
       (cond ((looking-at (concat re-beg re-env "}"))
              (or (save-excursion
                    (beginning-of-line)
                    (ignore-errors
                      (LaTeX-find-matching-begin)
                      (+ (current-column)
                         (if (looking-at (concat re-beg re-env "}"))
                             contin
                           offset))))
                  indent))
              ((looking-at (concat re-end re-env "}"))
               indent)
             ((looking-at "\\\\item")
              (+ offset indent))
             (t
              (+ contin indent))))))

 (defcustom LaTeX-indent-level-item-continuation 1
   "*Indentation of continuation lines for items in itemize-like
 environments."
   :group 'LaTeX-indentation
   :type 'integer)

 (eval-after-load "latex"
   '(setq LaTeX-indent-environment-list
          (nconc '(("itemize" LaTeX-indent-item)
                   ("enumerate" LaTeX-indent-item)
                   ("description" LaTeX-indent-item))
                 LaTeX-indent-environment-list)))
 (setq LaTeX-item-indent 0 LaTeX-indent-level 2)
 (setq LaTeX-indent-level-item-continuation 2)

RefTeX

(setq reftex-plug-into-AUCTeX t)
(setq reftex-format-ref-function `reftex-format-cref)
(add-hook 'LaTeX-mode-hook 'turn-on-reftex)
(add-hook 'latex-mode-hook 'turn-on-reftex)
(add-hook 'text-mode-hook 'turn-on-reftex)
(add-hook 'text-mode-hook 'turn-on-prettify-symbols-mode)
(eval-after-load
    "latex"
  '(TeX-add-style-hook
    "cleveref"
    (lambda ()
      (if (boundp 'reftex-ref-style-alist)
      (add-to-list
      'reftex-ref-style-alist
      '("Cleveref" "cleveref"
        (("\\cref" ?c) ("\\Cref" ?C) ("\\cpageref" ?d) ("\\Cpageref" ?D)))))
      (reftex-ref-style-activate "Cleveref")
      (TeX-add-symbols
      '("cref" TeX-arg-ref)
      '("Cref" TeX-arg-ref)
      '("cpageref" TeX-arg-ref)
      '("Cpageref" TeX-arg-ref)))))
(add-hook  'LaTeX-mode-hook
 (lambda ()
   (LaTeX-add-environments
    '("A" LaTeX-env-label)
    '("C" LaTeX-env-label)
    '("D" LaTeX-env-label)
    '("Ex" LaTeX-env-label)
    '("Prop" LaTeX-env-label)
    '("Q" LaTeX-env-label)
    '("R" LaTeX-env-label)
    '("T" LaTeX-env-label))
   (font-lock-add-keywords nil
       '(("\\(\\\\cref\\)\\>" 1 font-lock-warning-face t)))
   (font-lock-add-keywords nil
       '(("\\(\\\\Cref\\)\\>" 1 font-lock-warning-face t)))
   (font-lock-add-keywords nil
       '(("\\(\\\\glsxtrshort\\)\\>" 1 font-lock-warning-face t)))
   (font-lock-add-keywords nil
       '(("\\(\\\\glsxtrlong\\)\\>" 1 font-lock-warning-face t)))
   (font-lock-add-keywords nil
       '(("\\(\\\\glsxtrfull\\)\\>" 1 font-lock-warning-face t)))
   (add-to-list 'LaTeX-label-alist '("A" . "%f:a"))
   (add-to-list 'LaTeX-label-alist '("C" . "%f:c"))
   (add-to-list 'LaTeX-label-alist '("D" . "%f:d"))
   (add-to-list 'LaTeX-label-alist '("Ex" . "%f:x"))
   (add-to-list 'LaTeX-label-alist '("Prop" . "%f:p"))
   (add-to-list 'LaTeX-label-alist '("Q" . "%f:q"))
   (add-to-list 'LaTeX-label-alist '("R" . "%f:r"))
   (add-to-list 'LaTeX-label-alist '("T" . "%f:h"))))

(setq reftex-label-alist
'(("A" ?a "%f:a" "~\\ref{%s}" nil ("A" "a."))
("C" ?c "%f:c" "~\\ref{%s}" nil ("C" "c."))
("D" ?d "%f:d" "~\\ref{%s}" nil ("D" "d."))
("equation" ?e "%f:e" "~\\ref{%s}" nil ("equation" "e."))
("Ex" ?x "%f:x" "~\\ref{%s}" nil ("Ex" "x."))
("figure" ?f "%f:f" "~\\ref{%s}" nil ("figure" "f."))
("Prop" ?p "%f:p" "~\\ref{%s}" nil ("Prop" "p."))
("Q" ?q "%f:q" "~\\ref{%s}" nil ("Q" "q."))
("R" ?r "%f:r" "~\\ref{%s}" nil ("R" "r."))
("section" ?s "%f:s" "~\\ref{%s}" nil ("section" "s."))
("T" ?h "%f:h" "~\\ref{%s}" nil ("T" "h.") )
("table" ?t "%f:t" "~\\ref{%s}" nil ("table" "t."))))
(setq reftex-insert-label-flags '("sft" "sft"))

; table of contents
(setq reftex-toc-split-windows-horizontally t
        reftex-toc-split-windows-fraction     0.2)

Nomenclature

(eval-after-load "tex"
   '(add-to-list 'TeX-command-list
               '("Nomenclature" "makeindex %s.nlo -s nomencl.ist -o %s.nls"
                 TeX-run-command nil t :help "Create nomenclature file")))

Others

Instant preview

(add-hook 'latex-mode-hook #'xenops-mode)
(add-hook 'LaTeX-mode-hook #'xenops-mode)

Emacs Mail

Basic config

(use-package mu4e
  :ensure nil
  ;; :load-path "/usr/share/emacs/site-lisp/mu4e/"
  :defer 20 ; Wait until 20 seconds after startup
  :config

  ;; This is set to 't' to avoid mail syncing issues when using mbsync
  (setq mu4e-change-filenames-when-moving t)

  ;; Refresh mail using isync every 10 minutes
  (setq mu4e-update-interval (* 10 60))
  (setq mu4e-get-mail-command "mbsync -a")
  (setq mu4e-maildir "~/Mail")

  (setq mu4e-drafts-folder "/[Gmail]/Drafts")
  (setq mu4e-sent-folder   "/[Gmail]/Sent Mail")
  (setq mu4e-refile-folder "/[Gmail]/All Mail")
  (setq mu4e-trash-folder  "/[Gmail]/Trash")

  (setq mu4e-maildir-shortcuts
        '((:maildir "/Inbox"    :key ?i)
          (:maildir "/[Gmail]/Sent Mail" :key ?s)
          (:maildir "/[Gmail]/Trash"     :key ?t)
          (:maildir "/[Gmail]/Drafts"    :key ?d)
          (:maildir "/[Gmail]/All Mail"  :key ?a))))

Sending email

;; Now I set a list of 
(defvar my-mu4e-account-alist
  '(("Gmail"
     (user-mail-address "...@gmail.com")
     (smtpmail-smtp-user "...")
     (smtpmail-local-domain "gmail.com")
     (smtpmail-default-smtp-server "smtp.gmail.com")
     (smtpmail-smtp-server "smtp.gmail.com")
     (smtpmail-smtp-service 587)
     )
     ;; Include any other accounts here ...
    ))

(defun my-mu4e-set-account ()
  "Set the account for composing a message.
   This function is taken from: 
     https://www.djcbsoftware.nl/code/mu/mu4e/Multiple-accounts.html"
  (let* ((account
    (if mu4e-compose-parent-message
        (let ((maildir (mu4e-message-field mu4e-compose-parent-message :maildir)))
    (string-match "/\\(.*?\\)/" maildir)
    (match-string 1 maildir))
      (completing-read (format "Compose with account: (%s) "
             (mapconcat #'(lambda (var) (car var))
            my-mu4e-account-alist "/"))
           (mapcar #'(lambda (var) (car var)) my-mu4e-account-alist)
           nil t nil nil (caar my-mu4e-account-alist))))
   (account-vars (cdr (assoc account my-mu4e-account-alist))))
    (if account-vars
  (mapc #'(lambda (var)
      (set (car var) (cadr var)))
        account-vars)
      (error "No email account found"))))
(add-hook 'mu4e-compose-pre-hook 'my-mu4e-set-account)

Python

Taken from here and here:

(use-package lsp-mode
  :config
  (setq lsp-idle-delay 0.5
        lsp-enable-symbol-highlighting t
        lsp-enable-snippet nil  ;; Not supported by company capf, which is the recommended company backend
        lsp-pyls-plugins-flake8-enabled t)
  (lsp-register-custom-settings
   '(("pyls.plugins.pyls_mypy.enabled" t t)
     ("pyls.plugins.pyls_mypy.live_mode" nil t)
     ("pyls.plugins.pyls_black.enabled" t t)
     ("pyls.plugins.pyls_isort.enabled" t t)

     ;; Disable these as they're duplicated by flake8
     ("pyls.plugins.pycodestyle.enabled" nil t)
     ("pyls.plugins.mccabe.enabled" nil t)
     ("pyls.plugins.pyflakes.enabled" nil t)))
  :hook
  ((python-mode . lsp)
   (lsp-mode . lsp-enable-which-key-integration))
  )

(use-package lsp-ui
    :config
    (setq lsp-ui-sideline-show-hover t
          lsp-ui-sideline-delay 0.5
          lsp-ui-doc-delay 5
          lsp-ui-sideline-ignore-duplicates t
          lsp-ui-doc-position 'bottom
          lsp-ui-doc-alignment 'frame
          lsp-ui-doc-header nil
          lsp-ui-doc-include-signature t
          lsp-ui-doc-use-childframe t)
    )

Taken from here and here

(use-package elpy
  :init
  (elpy-enable)
  :config
  (setq
   python-shell-interpreter "ipython"
   ;; python-shell-interpreter-args "--profile=default"
   python-shell-prompt-regexp "In \\[[0-9]+\\]: "
   python-shell-prompt-output-regexp "Out\\[[0-9]+\\]: "
   python-shell-completion-setup-code
   "from IPython.core.completerlib import module_completion"
   python-shell-completion-module-string-code
   "';'.join(module_completion('''%s'''))\n"
   python-shell-completion-string-code
   "';'.join(get_ipython().Completer.all_completions('''%s'''))\n"
   ;; python-shell-interpreter-args
   ;; "--colors=Linux --profile=default"
   elpy-rpc-virtualenv-path 'current))

;; modify ob-ipython.el as follows: https://github.com/gregsexton/ob-ipython/issues/135#issuecomment-397463174

Taken from here:

(use-package pyvenv
  :ensure t
  :config
  (pyvenv-mode t)
  (pyvenv-tracking-mode 1)  ; Automatically use pyvenv-workon via dir-locals

  ;; Set correct Python interpreter
  (setq pyvenv-post-activate-hooks
          (list (lambda ()
                  (setq python-shell-interpreter (concat pyvenv-virtual-env "bin/python3")))))
  (setq pyvenv-post-deactivate-hooks
          (list (lambda ()
                  (setq python-shell-interpreter "python3"))))
  )

(require 'virtualenvwrapper)
(venv-initialize-interactive-shells) ;; if you want interactive shell support
(venv-initialize-eshell) ;; if you want eshell support
(setq venv-location "~/.virtualenvs/")

(use-package flycheck
  :ensure t
  :config
  (when (require 'flycheck nil t)
    (setq elpy-modules (delq 'elpy-module-flymake elpy-modules))
    (add-hook 'elpy-mode-hook 'flycheck-mode))
  )

(use-package flyover
  :ensure t
  :hook (flycheck-mode-hook . flyover-mode)
  :config
  (setq
   flyover-levels '(error warning info)
   flyover-wrap-messages t
   flyover-max-line-length 80
   flyover-use-theme-colors t
   flymake-show-diagnostics-at-end-of-line 'fancy
   flyover-virtual-line-type 'curved-dotted-arrow
   flyover-show-at-eol t
   flyover-hide-when-cursor-is-on-same-line t
   flyover-show-virtual-line t
   flyover-virtual-line-icon "╰──"
   flyover-info-icon "🛈"
   flyover-warning-icon "⚠"
   flyover-error-icon "✘"))

(use-package blacken
  :ensure t
  )

(use-package jupyter
  :ensure t
  :config
  (setq jupyter-use-zmq nil)
  :commands (jupyter-run-server-repl
             jupyter-run-repl
             jupyter-server-list-kernels))

To fix the jupyter 404 error, taken from here:

(defun gm/jupyter-api-request-xsrf-cookie-error-advice (func &rest args)
  (condition-case nil
      (apply func args)
    (jupyter-api-http-error nil)))
(advice-add 'jupyter-api-request-xsrf-cookie :around #'gm/jupyter-api-request-xsrf-cookie-error-advice)

Convert jupyter-notebooks to org-files (taken from here):

(setq code-cells-convert-ipynb-style '(
        ("pandoc" "--to" "ipynb" "--from" "org")
        ("pandoc" "--to" "org" "--from" "ipynb")
        org-mode))

Snippets

(use-package yasnippet
  :diminish yas-minor-mode
  :defer 5
  :config
  (setq yas-snippet-dirs '("~/.emacs.d/snippets/"))
  (yas-global-mode 1)) ;; or M-x yas-reload-all if you've started YASnippet already.

;; Silences the warning when running a snippet with backticks (runs a command in the snippet)
(require 'warnings)
(add-to-list 'warning-suppress-types '(yasnippet backquote-change)) 

(use-package yasnippet-snippets
  :ensure t)

Emacs 29.3 (Org mode 9.6.15)