emacs-config/config-ext.org
2021-04-07 20:02:17 -05:00

32 KiB

Various tools

ledger

  (use-package ledger-mode
    :mode "\\.dat\\'"
    :config
    (defconst new-report-str (concat "%(binary) -f %(ledger-file) --cleared-format "
                                     "'%(justify(scrub(get_at(display_total, 0)), 16, 16 + int(prepend_width), true, color))"
                                     "  %(justify(scrub(get_at(display_total, 1)), 18, 36 + int(prepend_width), true, color))"
                                     "  %(justify(scrub(get_at(display_total, 0) - get_at(display_total, 1)), 18, 36 + int(prepend_width), true, color))"
                                     "    %(latest_cleared ? format_date(latest_cleared) : \"         \")"
                                     "    %(!options.flat ? depth_spacer : \"\")%-(ansify_if(partial_account(options.flat), blue if color))\\n"
                                   "%/%$1  %$2  %$3    %$4\\n"
                                   "%/%(prepend_width ? \" \" * int(prepend_width) : \"\")----------------    ----------------    ----------------    ---------\\n'"
                                   " cleared"))

    (setq ledger-reports
          `(("account" "%(binary) -f %(ledger-file) reg %(account)")
            ("bal" "%(binary) -f %(ledger-file) bal")
            ("reg" "%(binary) -f %(ledger-file) reg")
            ("equity" "%(binary) -f %(ledger-file) bal ^Exp ^RE ^Rev")
            ("cleared" ,new-report-str)
            ("last-superfluous" "%(binary) -f %(ledger-file) bal --limit='account =~ /^Exp:(Food|Luxury|NewTech|People)/ && date >= [last month]'")
            ("superfluous" "%(binary) -f %(ledger-file) reg --limit='account =~ /^Exp:(Food|Luxury|NewTech|People)/'")
            ("recurring" "%(binary) -f %(ledger-file) reg --limit='has_tag(\"RECURRING\")' ^Exp")
            ("expmonth" "%(binary) -f %(ledger-file) -M reg Expenses")
            ("owedmom" "%(binary) -f %(ledger-file) reg Liabilities")
            ("progress" "%(binary) -f %(ledger-file) reg Assets Equity Liabilities")
            ("payee" "%(binary) -f %(ledger-file) reg @%(payee)")))

    (setq dynamic-reports
          '(("budgetcal" "%(binary) -f ~/MEGA/org/entries/food.ledger --daily --add-budget reg Expenses")))

    (defun ledger-dynamic-report ()
      (interactive)
      (let* ((ledger-reports dynamic-reports)
             (report-name (ledger-report-read-name)))
        (ledger-report report-name nil)))

    (setq ledger-reconcile-buffer-line-format
          "%(date)s %-4(code)s %-30(payee)s %-30(account)s %15(amount)s\n")

    (defun ledger-account-check-dont-include-regexp (orig account)
      (when (= (aref account 0)
               ?^)
        (setq account
              (substring account 1))))

    (defun ledger-report-show-monthly-average ()
      (interactive)
      (let ((average-string "-A -M -n"))
        (unless (string-match-p average-string ledger-report-cmd)
          (setq ledger-report-cmd
                (concat ledger-report-cmd " " average-string))
          (ledger-report-redo))))

    (setq ledger-amount-regexp
          (concat "\\(  \\|\t\\| \t\\)[ \t]*-?"
            "\\(?:" "?-" ledger-commodity-regexp " *\\)?"
            ;; We either match just a number after the commodity with no
            ;; decimal or thousand separators or a number with thousand
            ;; separators.  If we have a decimal part starting with `,'
            ;; or `.', because the match is non-greedy, it must leave at
            ;; least one of those symbols for the following capture
            ;; group, which then finishes the decimal part.
            "\\(-?\\(?:[0-9]+\\|[0-9,.]+?\\)\\)"
            "\\([,.][0-9)]+\\)?"
            "\\(?: *" ledger-commodity-regexp "\\)?"
            "\\([ \t]*[@={]@?[^\n;]+?\\)?"
            "\\([ \t]+;.+?\\|[ \t]*\\)?$"))

    (define-key ledger-report-mode-map (kbd "M") #'ledger-report-show-monthly-average))

Credit Card Statement Macro

  (fset 'credit_card_statement
     [?\M-x ?o ?r ?g ?- ?m ?o ?d ?e return ?\M-x ?q backspace ?r ?e ?p ?l ?a ?c ?e ?- ?r ?e ?g ?e ?x ?p return ?^ ?\C-q tab return ?  ?  ?  ?  return ?\M-< ?\C-  ?\C-f ?\C-f ?\C-f ?\C-f ?\C-c ?m ?a ?\C-w ?- ?  ?\[ ?  ?\] ?  ?\C-e ?\C-k ?\C-c ?m ?  ?\C-q tab ?\C-q tab ?\C-e ?\C-j ?y ?\C-a ?_ ?_ ?_ ?_ backspace backspace backspace backspace ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?= ?\C-p ?\C-p ?\C-k ?\C-c ?m ?  ?\C-q tab ?\C-q tab ?\C-d ?\C-d return ?\C-n ?\C-n ?\C-n ?\C-n ?\C-n ?\C-n ?\C-n ?\C-n ?\C-n ?\C-n ?\C-n ?\C-n ?\C-n ?\C-n ?\C-n ?\C-n ?\C-n ?\C-n ?\C-n ?\C-n ?\C-n ?\C-n ?\C-n ?\C-n ?\C-n])

encryption

  (require 'epa-file)
  (epa-file-enable)
  (setq epa-pinentry-mode 'loopback)
  (setq epa-file-cache-passphrase-for-symmetric-encryption t)
  (setenv "GPG_AGENT_INFO" nil)

  (setq epg-gpg-program "gpg2")
  (setq auth-source-debug t)
  (setq auth-sources '((:source "~/.emacs.d/secrets/.authinfo.gpg")))

email

  (setq user-mail-address "bensonchu457@gmail.com"
        user-full-name "Benson Chu")

  (setq smtpmail-smtp-server "smtp.gmail.com"
        smtpmail-smtp-service 587
        send-mail-function 'smtpmail-send-it
        message-send-mail-function 'smtpmail-send-it)

  (mailcap-add "text/html" "/usr/bin/xdg-open %s ")

  (setq mail-specify-envelope-from t
        message-sendmail-envelope-from 'header
        mail-envelope-from 'header)

  (use-package notmuch
    :commands notmuch
    :bind (:map notmuch-message-mode-map
                ("C-c C-c" . #'my/choose-email-address-and-send)
                :map notmuch-search-mode-map
                ("z" . #'notmuch-search-tree-current-thread)
                ("A" . #'notmuch-search-show-all)
                ("<mouse-1>" . nil)
                :map notmuch-tree-mode-map
                ("<tab>" . #'notmuch-tree-explore-here)
                ("<down>" . #'notmuch-tree-next-message)
                ("<up>" . #'notmuch-tree-prev-message)
                ("d" . #'my/notmuch-delete-mail)
                ("U" . #'notmuch-tree-unfold-all)
                ("u" . #'notmuch-tree-up-thread)
                ("N" . #'notmuch-tree-next-sibling)
                ("P" . #'notmuch-tree-prev-sibling)
                ("t" . #'notmuch-tree-toggle-folding-thread)
                ("/" . #'notmuch-tree-undo-read)
                ("F" . #'notmuch-tree-focus)
                ("S-SPC" . #'notmuch-tree-scroll-message-window-back))
    :config
    (require 'notmuch-nav)
    (require 'notmuch-tree-hide)
    (require 'notmuch-tree)
    (require 'notmuch-fold)

    (setq notmuch-draft-tags '("+drafts"))

    (custom-set-faces
     '(notmuch-tree-match-tree-face ((t (:family "DejaVu Sans Mono"))) t)
     '(notmuch-tree-no-match-tree-face ((t (:family "DejaVu Sans Mono"))) t))
    (set-face-attribute 'notmuch-search-unread-face nil :foreground "white")
    (set-face-attribute 'notmuch-message-summary-face nil :background "steel blue" :foreground "snow")
    (add-to-list 'notmuch-search-line-faces
                 '("deleted" . font-lock-comment-face))

    (defun notmuch-search-show-all ()
      (interactive)
      (let* ((query (replace-regexp-in-string "date:[^ ]+" "" notmuch-search-query-string))
             (noand (replace-regexp-in-string "^ *and +" "" query))
             (noand2 (replace-regexp-in-string " +and *" "" query)))
        (notmuch-search noand2)))

    (defun notmuch-search-tree-current-thread (arg)
      (interactive "P")
      (let* ((thread-id (notmuch-search-find-thread-id))
             (input (notmuch-read-query (concat "Notmuch tree: " thread-id " and "))))
        (notmuch-tree thread-id (unless (zerop (length input)) input) nil nil nil nil nil (unless arg #'notmuch-tree-hide-dead-trees))))

    (defun notmuch-tree-focus (arg)
      (interactive "P")
      (notmuch-tree notmuch-tree-basic-query (notmuch-tree-get-message-id) nil nil nil nil nil (if (not arg) #'notmuch-tree-hide-dead-trees #'notmuch-tree-show-trail-and-alive-children)))

    (defun notmuch-tree-undo-read (arg)
      (interactive "P")
      (if arg
          (save-excursion
            (beginning-of-buffer)
            (while (text-property-search-forward
                    'face 'notmuch-tag-deleted
                    #'(lambda (value prop)
                        (if (consp prop)
                            (member value prop)
                          (eq value prop))))
              (notmuch-tree-add-tag '("+unread"))))
        (notmuch-tree-add-tag '("+unread"))
        (next-line)))

    (setq notmuch-search-oldest-first nil
          notmuch-saved-searches
          '((:name "inbox" :query "tag:inbox" :key "i")
            (:name "inbox today" :query "date:2020-07-25.. and tag:inbox" :key "t")
            (:name "work" :query "tag:work" :key "w")
            (:name "mailing lists" :query "tag:mlist" :key "m")
            (:name "emacs-devel" :query "tag:emacs-devel" :key "e")
            (:name "emacs bugs" :query "tag:bug-gnu-emacs and date:30d.." :key "E")
            (:name "emacs help" :query "tag:help-gnu-emacs and date:30d.." :key "h")
            (:name "org-mode" :query "tag:org-mode" :key "o")
            (:name "recruiting" :query "tag:recruiting" :key "r")
            (:name "unread" :query "tag:unread" :key "u")
            (:name "flagged" :query "tag:flagged" :key "f")
            (:name "cs" :query "tag:cs" :key "c")
            (:name "receipts" :query "tag:receipts" :key "R")
            (:name "voicemail" :query "from:vm@italkbb.com" :key "v")
            ;; (:name "sent" :query "tag:sent" :key "s")
            ;; (:name "drafts" :query "tag:draft" :key "d")
            (:name "all mail" :query "*" :key "a")))

    (defun my/choose-email-address-and-send ()
      (interactive)
      (let ((resp (completing-read "Which email? " '("bchu3@cougarnet.uh.edu" "bensonchu457@gmail.com") nil t "^")))
        (setq smtpmail-smtp-server
              (if (string= resp "bensonchu457@gmail.com")
                  "smtp.gmail.com"
                "smtp.office365.com"))
        (notmuch-mua-send-and-exit)))

    (add-to-list 'notmuch-tagging-keys
                 '("R" ("-inbox" "+recruiting") "Recruiting"))

    (setf (cdr (assoc "d" notmuch-tagging-keys))
          '(("+deleted") "Delete"))

    (advice-add #'notmuch-tag-jump :after #'(lambda (&rest args) (next-line))))


  (use-package exwm
    :config
    (defvar offlineimap-timer nil)
    (defvar offlineimap-process nil)

    (defun run-offlineimap ()
      (interactive)
      (if (and (processp offlineimap-process)
               (process-live-p offlineimap-process))
          (message "offlineimap already running...")
        (message "offlineimap starting...")
        (when (and (timerp offlineimap-timer)
                   (not (timer--triggered offlineimap-timer)))
          (cancel-timer offlineimap-timer))
        (set-process-sentinel
         (setq offlineimap-process
               (start-process-shell-command "offlineimap" "*offlineimap-output*" "offlineimap"))
         #'(lambda (process event)
             (when (string-match-p "exited abnormally with code 1" event)
               (with-current-buffer (process-buffer offlineimap-process)
                 (when (string-match-p "get_password_emacs"(buffer-string))
                   (erase-buffer)
                   (message "Oops, didn't grab a password. ")
                   (setq offlineimap-timer (run-with-timer 300 nil #'run-offlineimap)))))
             (when (string-match-p "^finished" event)
               (message "Offlineimap finished")
               (setq offlineimap-timer (run-with-timer 300 nil #'run-offlineimap)))))))

    (defun stop-offlineimap ()
      (interactive)
      (when (timerp offlineimap-timer)
        (cancel-timer offlineimap-timer))
      (when (processp offlineimap-process)
        (set-process-sentinel offlineimap-process
                              nil)))

    (add-to-list 'exwm-init-hook
                 #'run-offlineimap
                 t))

erc

  (use-package erc)
  (use-package erc-hl-nicks)
  (use-package erc-colorize)
  (require 'netrc)
  (erc-hl-nicks-mode)
  (erc-colorize-mode)
  (setq erc-user-full-name "Benson Chu")
  (setq erc-kill-buffer-on-part t)
  (setq erc-autojoin-channels-alist
        '(("freenode.net" "#emacs" "#org-mode"
           ;; "##linux" "#compilers" "#pltclub" 
           ;; "##cs" "##computerscience" "##programming" "#lisp" "##lisp"
           ;; "#sbcl" "#ecl"
           )))

  (defun get-authinfo (host port)
    (let* ((netrc (netrc-parse (expand-file-name "~/.emacs.d/secrets/.authinfo.gpg")))
           (hostentry (netrc-machine netrc host port)))
      (when hostentry (netrc-get hostentry "password"))))

  (defun freenode-connect (nick password)
    (erc :server "irc.freenode.net" :port 6667
         :password password :nick nick))

  (defun irc-connect ()
    (interactive)
    (when (y-or-n-p "Connect to IRC? ")
      (freenode-connect "pest-ctrl" (get-authinfo "irc.freenode.net" "6667"))))

pdf-tools use isearch

  (when (not (eq system-type 'windows-nt))
    (use-package pdf-tools)
    (pdf-tools-install)
    (define-key pdf-view-mode-map (kbd "C-s") 'isearch-forward)
    (define-key pdf-view-mode-map (kbd "d") (lambda () (interactive) (pdf-view-next-line-or-next-page 8)))
    (define-key pdf-view-mode-map (kbd "u") (lambda () (interactive) (pdf-view-previous-line-or-previous-page 8))))

freezing time

  (defvar my/frozen-time nil)

  (defvar my/format-time-string-function nil)

  (defun my/org-today ()
    (time-to-days my/frozen-time))

  (defun my/current-time ()
    my/frozen-time)

  (defun my/format-time-string (original format-string &optional time zone)
    (apply original
           format-string
           (if time
               time
             my/frozen-time)
           zone))

  (defun my/decode-time (original &optional time zone)
    (apply original
           (if time
               time
             my/frozen-time)
           zone))

  ;; Change and freeze time
  (defun za-warudo ()
    "Freeze `current-time' at the current active or inactive timestamp. If point
  is not on a timestamp, the function prompts for one. If time is not specified,
  either by the timstamp under point or prompt, the time defaults to the
  current HH:MM of today at the selected date."
    (interactive)
    (let* ((org-read-date-prefer-future nil)
           (time (org-read-date t 'totime nil "Input freeze time: ")))
      (setq my/frozen-time (append time '(0 0)))
      (advice-add #'current-time :override #'my/current-time)
      (advice-add #'format-time-string :around #'my/format-time-string)
      (advice-add #'decode-time :around #'my/decode-time)
      (advice-add #'org-today :override #'my/org-today)
      (set-face-background 'fringe "firebrick2")
      (message "Toki yo tomare")))

  (define-key *root-map* (kbd "C-z") 'za-warudo)

  ;; Release changed / frozen time
  (defun un-za-warudo ()
    "Release the time frozen by `freeze-time'."
    (interactive)
    (advice-remove #'current-time #'my/current-time)
    (advice-remove #'format-time-string #'my/format-time-string)
    (advice-remove #'decode-time #'my/decode-time)
    (advice-remove #'org-today #'my/org-today)
    (setq my/frozen-time nil)
    (set-face-background 'fringe nil)
    (message "Soshite, toki wa ugoki dasu"))

  (define-key *root-map* (kbd "C-r") 'un-za-warudo)

Programming stuff

lsp

  (use-package lsp-mode
    :commands lsp)

  (use-package lsp-ui
    :after lsp-mode
    :bind (:map lsp-mode-map
                ("M-." . #'lsp-ui-peek-find-definitions)
                ("M-?" . #'lsp-ui-peek-find-references)
                ("M-p" . #'lsp-ui-peek-jump-forward))
    :hook (lsp-mode . lsp-ui-mode)
    :config
    (setq lsp-ui-flycheck-enable t)
    (setq lsp-ui-flycheck-live-reporting t))

  (use-package dap-mode
    :hook ((java-mode . dap-mode)
           (java-mode . dap-ui-mode))
    :bind (:map dap-mode-map
                ("C-c h" . #'dap-hydra)
                ("C-c b" . #'dap-breakpoint-toggle)
                ("C-c d r" . #'dap-java-debug)
                ("C-c d m" . #'dap-java-debug-test-class)
                ("C-c r t" . #'mvn-test)))

c++

  (use-package ccls
    :hook
    ((c-mode c++-mode objc-mode) .
     (lambda () (let ((project-root (projectile-project-p)))
                  (when (and project-root
                             (file-readable-p (concat project-root "/compile_commands.json")))
                    (require 'ccls) (lsp)))))
    :config
    (setq ccls-sem-highlight-method 'font-lock-mode)
    (when-let (l (getenv "https_proxy"))
      (when (and (string-match-p "ti\.com" l)
                 (not (memq window-system '(mac ns))))
        (setq ccls-executable "/db/sds/packages2/ccls/ccls"))))

Projectile

  (use-package projectile
    :defer 5
    :bind-keymap ("C-c C-." . projectile-command-map)
    :init   (progn
              (setq projectile-enable-caching nil)
              (setq projectile-git-submodule-command nil)
              (setq projectile-completion-system 'ivy)

              ;; (setq counsel-projectile-switch-project-action 'projectile-vc)
              (setq projectile-switch-project-action 'projectile-dired)
              (setq projectile-require-project-root t))
    :config
    (projectile-mode)

    ;; Provide my own projectile-compile-project which uses
    ;; cca/projectile-compilation-dir instead of projectile-compilation-dir.
    (defvar cca/projectile-compilation-hash (make-hash-table :test 'equal)
      "Has of project roots to compilation directories")

    (defun cca/projectile-get-compilation-dir (key_dir)
      "Get the compilation directory associated with the specified root directory"
      (gethash key_dir cca/projectile-compilation-hash))

    (defun cca/projectile-set-compilation-dir (key_dir value)
      "Set the compilation directory for the specified root directory"
      (puthash key_dir value cca/projectile-compilation-hash))

    (defun cca/projectile-compilation-dir()
      "Prompts the user for a directory relative to the project root
  and returns the absolute path. It also stores the relative path
  from the current project root into projectile-compilation-dir."
      (let* ((root (projectile-project-root))
             (base-compilation-dir (or (cca/projectile-get-compilation-dir root) root))
             (full-compilation-dir (expand-file-name
                                    (read-directory-name "Build directory: " base-compilation-dir))))
        (setq projectile-project-compilation-dir (file-relative-name full-compilation-dir root))
        (cca/projectile-set-compilation-dir root full-compilation-dir)))

    (defun projectile-compile-project (arg)
      "Run project compilation command.

  Normally you'll be prompted for a compilation command, unless
  variable `compilation-read-command'.  You can force the prompt
  with a prefix ARG."
      (interactive "P")
      (let ((command (projectile-compilation-command (cca/projectile-compilation-dir))))
        (projectile--run-project-cmd command projectile-compilation-cmd-map
                                     :show-prompt arg
                                     :prompt-prefix "Compile command: "
                                     :save-buffers t))))

Slime mode

  (use-package slime
    :commands slime slime-switch-lisps
    :hook ((inferior-lisp-mode . inferior-slime-mode))
    :config
    (setq inferior-lisp-program "/usr/bin/sbcl")

    (defun slime-switch-lisps (lisp)
      (interactive (list (completing-read "Which lisp? "
                                          '("sbcl" "ecl" "cmucl" "clozure-cl"))))
      (setq inferior-lisp-program lisp))

    (let ((clhs-file "~/quicklisp/clhs-use-local.el"))
      (if (file-exists-p clhs-file)
          (load-file clhs-file)
        (warn "clhs not installed. Please install"))))

  (use-package slime-company
    :after slime company
    :config
    (slime-setup '(slime-fancy slime-asdf slime-company)))

rust

  (use-package cargo)
  (use-package rust-mode)
  (use-package rustic)

golang

  (use-package go-mode
    :hook (go-mode . (lambda ()
                       (add-hook 'before-save-hook 'gofmt-before-save nil t)
                       (setq indent-tabs-mode nil)))
    :config
    ;; This is for lsp to work
    (add-to-list 'exec-path "~/go/bin/"))

python

  (use-package elpy)
  (elpy-enable)
  (use-package ein)
  (add-to-list 'exec-path
               "/home/benson/anaconda3/bin/" t)

web stuff

  (use-package web-mode
    :commands web-mode
    :init
    (add-to-list 'auto-mode-alist '("\\.phtml\\'" . web-mode))
    (add-to-list 'auto-mode-alist '("\\.tpl\\.php\\'" . web-mode))
    (add-to-list 'auto-mode-alist '("\\.[agj]sp\\'" . web-mode))
    (add-to-list 'auto-mode-alist '("\\.as[cp]x\\'" . web-mode))
    (add-to-list 'auto-mode-alist '("\\.erb\\'" . web-mode))
    (add-to-list 'auto-mode-alist '("\\.mustache\\'" . web-mode))
    (add-to-list 'auto-mode-alist '("\\.djhtml\\'" . web-mode))
    (add-to-list 'auto-mode-alist '("\\.cshtml\\'" . web-mode))
    (add-to-list 'auto-mode-alist '("\\.html?\\'" . web-mode))
    :config
    (setq web-mode-auto-close-style 2))

  (use-package js2-mode
    :commands js2-mode
    :init
    (add-to-list 'auto-mode-alist '("\\.js$" . js2-mode)))

colorful compilation buffer

  (require 'ansi-color)
  (defun colorize-compilation-buffer ()
    (let ((buffer-read-only nil))
      (ansi-color-apply-on-region (point-min) (point-max))))
  (add-hook 'compilation-filter-hook 'colorize-compilation-buffer)

Various common files

  (use-package csv-mode
    :commands csv-mode
    :init
    (add-to-list 'auto-mode-alist
                 '("\\.csv$" . csv-mode)))

  (use-package yaml-mode
    :commands yaml-mode
    :init
    (add-to-list 'auto-mode-alist
                 '("\\.yaml$" . yaml-mode)
                 '("\\.yml$" . yaml-mode)))

New

transpose-frame

  (use-package transpose-frame)

e2wm

  (use-package e2wm
    :bind (("M-+" . e2wm:start-management)))

Youtube-dl

  (add-to-list 'load-path "~/.emacs.d/submodule/youtube-dl-emacs/")
  (require 'youtube-dl)

  (defun youtube-dl-song (url)
    (interactive
     (list (read-from-minibuffer
            "URL: " (or (thing-at-point 'url)
                        (when interprogram-paste-function
                          (funcall interprogram-paste-function))))))
    (async-shell-command (format "youtube-dl -x -f \"bestaudio[ext=m4a]\" \"%s\"; tageditor -s album=\"youtube-dl\" -f *.m4a" url)))

set-default-directory

  (defun set-default-directory (dir)
    (interactive "f")
    (setq default-directory dir))

World time include Taiwan

  (setq display-time-world-list
        '(("America/Chicago" "Houston")
          ("Asia/Taipei" "Taiwan")))

auto-save files in same directory

  (setq backup-directory-alist `(("." . "~/.emacs.d/backups/")))

  (setq make-backup-files t               ; backup of a file the first time it is saved.
        backup-by-copying t               ; don't clobber symlinks
        version-control t                 ; version numbers for backup files
        kept-old-versions 6               ; oldest versions to keep when a new numbered backup is made (default: 2)
        kept-new-versions 9               ; newest versions to keep when a new numbered backup is made (default: 2)
        auto-save-default t               ; auto-save every buffer that visits a file
        auto-save-timeout 20              ; number of seconds idle time before auto-save (default: 30)
        auto-save-interval 200            ; number of keystrokes between auto-saves (default: 300)
        )

Scroll interval

  (setq scroll-margin 1
        hscroll-margin 2
        hscroll-step 1
        scroll-conservatively 101
        scroll-preserve-screen-position t
        mouse-wheel-scroll-amount '(3)
        mouse-wheel-progressive-speed nil)

Setup convenient headers

  (setq auto-insert-alist
        '(((emacs-lisp-mode . "Emacs lisp mode") nil
           ";;; " (file-name-nondirectory buffer-file-name) " --- " _ " -*- lexical-binding: t -*-\n\n"

           ";; Copyright (C) " (format-time-string "%Y") " Benson Chu\n\n"

           ";; Author: Benson Chu <bensonchu457@gmail.com>\n"
           ";; Created: " (format-time-string "[%Y-%m-%d %H:%M]") "\n\n"

           ";; This file is not part of GNU Emacs\n\n"

           ";; This program is free software: you can redistribute it and/or modify\n"
           ";; it under the terms of the GNU General Public License as published by\n"
           ";; the Free Software Foundation, either version 3 of the License, or\n"
           ";; (at your option) any later version.\n\n"

           ";; This program is distributed in the hope that it will be useful,\n"
           ";; but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
           ";; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
           ";; GNU General Public License for more details.\n\n"

           ";; You should have received a copy of the GNU General Public License\n"
           ";; along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\n"

           ";;; Commentary:\n\n"

           ";;; Code:\n\n"

           "(provide '" (file-name-sans-extension (file-name-nondirectory buffer-file-name)) ")\n"
           ";;; " (file-name-nondirectory buffer-file-name) " ends here\n")
          ((lisp-mode . "Common Lisp") nil
           "(defpackage :" (file-name-sans-extension (file-name-nondirectory buffer-file-name)) "\n"
           "  (:use :cl :alexandria)\n"
           "  (:export))\n\n"

           "(in-package :" (file-name-sans-extension (file-name-nondirectory buffer-file-name)) ")")))

  (auto-insert-mode)

Profiler Keymap

  (define-prefix-command '*profiler-map*)

  (define-key *profiler-map* (kbd "s") #'profiler-start)
  (define-key *profiler-map* (kbd "r") #'profiler-report)
  (define-key *profiler-map* (kbd "S") #'profiler-stop)

  (define-key *root-map* (kbd "p") '*profiler-map*)

Open dev workspace

  (defun open-dev-workspace ()
    (interactive)
    (dired "~/big_files/workspace"))

Helpful view-mode

  (defun helpful--navigate-view-mode (orig button)
    (let ((w (window-parameter (selected-window) 'quit-restore)))
      (funcall orig button)
      (view-mode)
      (setq-local view-exit-action
                  `(lambda (&rest args)
                     (set-window-parameter (selected-window) 'quit-restore ',w)))))

  (advice-add #'helpful--navigate
              :around
              #'helpful--navigate-view-mode)

man select window

  (setq Man-notify-method 'aggressive)

find-file-view

  (defun view-file ()
    (interactive)
    (call-interactively #'ido-find-file)
    (view-mode))

  (global-set-key (kbd "C-c C-v") #'view-file)

rmsbolt

(use-package rmsbolt)

ivy-posframe

  (unless my/at-ti
    (use-package ivy-posframe
      :config
      (setq ivy-posframe-display-functions-alist
            '((swiper          . ivy-posframe-display-at-frame-center)
              (complete-symbol . ivy-posframe-display-at-point)
              (iwc-switch-to-wc . nil)
              (t               . ivy-posframe-display-at-window-top-center)))

      (defun ivy-posframe-display-at-window-top-center (str)
        (ivy-posframe--display str #'posframe-poshandler-window-top-center))

      (defun posframe-poshandler-window-top-center (info)
        "Posframe's position handler.

    Get a position which let posframe stay onto current window's
    center.  The structure of INFO can be found in docstring
    of `posframe-show'."
        (let* ((frame-width (plist-get info :parent-frame-width))
               (window-left (plist-get info :parent-window-left))
               (window-top (plist-get info :parent-window-top))
               (window-width (plist-get info :parent-window-width))
               (posframe-width (plist-get info :posframe-width)))
          (cons (min (- frame-width posframe-width)
                     (+ window-left (max 0
                                         (/ (- window-width posframe-width) 2))))
                (+ window-top 50))))

      (defun disable-ivy-posframe-on-exwm-windows (orig &rest args)
        (if (not (eq major-mode 'exwm-mode))
            (apply orig args)
          (letf (((symbol-function 'display-graphic-p) (lambda (&optional display) nil)))
            (apply orig args))))

      (advice-add #'ivy-posframe--read
                  :around
                  #'disable-ivy-posframe-on-exwm-windows))


    (unless (eq 'hash-table (type-of face-new-frame-defaults))

      ;; (def-face-copier my/posframe-faces (sym)
      ;;   (let ((name (symbol-name sym)))
      ;;     (string-match-p "^ivy-.*"
      ;;                     name)))

      ;;(setq ivy-posframe-min-height 0)

      ;; (setq ivy-posframe-height 24)

      ;; (setq ivy-height-alist
      ;;       '((t . 24)))
      ;; (setq ivy-posframe-height-alist
      ;;       '((counsel-M-x . 8)
      ;;         (t . 24)))
      ;; '((swiper . 24)))
      ))

  (use-package ivy-rich
    :requires ivy
    :config
    (ivy-rich-mode 1))

Elfeed

  (require 'elfeed)
  (setq elfeed-use-curl t)
  (elfeed-set-timeout 36000)
  (setq elfeed-curl-extra-arguments '("--insecure"))

  ;; enable elfeed-protocol
  (elfeed-protocol-enable)

pavucontrol switch speakers headphones

  (defvar laptop-sink-index 0)
  (defvar hdmi-pcie-interface nil)

  (defun setup-headphone-stuff ()
    (interactive)
    (let* ((result (shell-command-to-string "pactl list short sinks")))
      (when (string-match "\\([0-9]\\).*analog-stereo" result)
        (setq laptop-sink-index
              (string-to-number
               (match-string 1 result))))
      (when (string-match "[0-9].*\\(pci-.*\\)\\.hdmi-stereo" result)
        (setq hdmi-pcie-interface
              (match-string 1 result))))

    (when hdmi-pcie-interface
      (let* ((result (shell-command-to-string "pacmd list-modules"))
             (split (cdr (split-string result "index: "))))
        (loop for mod in split
              while (not
                     (string-match (format "\\([0-9]+\\)\n.*\n.*name=\"%s\"" hdmi-pcie-interface)
                                   mod))
              finally
              do (shell-command
                  (format "pactl unload-module %s"
                          (match-string 1 mod)))))))

  (defun current-speakers ()
    (let ((string (shell-command-to-string "pactl list sinks | grep 'Active Port: '")))
      (if (string-match-p "headphones" string)
          'headphones
        'speakers)))

  (defun toggle-audio-output ()
    (interactive)
    (if (eq (current-speakers)
            'headphones)
        (shell-command (format "pactl set-sink-port %d analog-output-speaker"
                               laptop-sink-index))
      (shell-command (format "pactl set-sink-port %d analog-output-headphones"
                             laptop-sink-index)))
    (message (format "Switched to: %s" (current-speakers))))

  (exwm-global-set-key (kbd "s-s") #'toggle-audio-output)

  (use-package exwm
    :config
    (add-hook 'exwm-init-hook #'setup-headphone-stuff))