Skip to content

Latest commit

 

History

History
2357 lines (2180 loc) · 73.7 KB

config.org

File metadata and controls

2357 lines (2180 loc) · 73.7 KB

Emacs Config

Given a vanilla emacs

Open this config file

I can use C-c I to open this config file and use C-c R to reload it. By the way, I can use C-c i to open system config file.

(defun find-config ()
  "Edit config.org"
  (interactive)
  (find-file "~/.emacs.d/config.org"))
(defun find-dotfiles ()
  "Edit dotfiles.org"
  (interactive)
  (find-file "~/.emacs.d/dotfiles.org"))
(defun reload-config()
  "Reload config.org"
  (interactive)
  (delete-file "~/.emacs.d/config.el")
  (org-babel-load-file (expand-file-name "~/.emacs.d/config.org")))

(global-set-key (kbd "C-c i") 'find-dotfiles)
(global-set-key (kbd "C-c I") 'find-config)
(global-set-key (kbd "C-c R") 'reload-config)

Identity who am I?

(setq user-full-name "Teddy Ma"
      user-mail-address "[email protected]")

Install packages

straight.el is a good tool to manage packages, but it is slow when pull all the packages, so I can use the command line : (async-shell-command "emacs -batch -l ~/.emacs.d/init.el -f straight-pull-all") when something wrong with ‘can not find package xxx’, try to delete the local repo of package and restart emacs

(defvar bootstrap-version)
(let ((bootstrap-file
       (expand-file-name
        "straight/repos/straight.el/bootstrap.el"
        user-emacs-directory))
      (bootstrap-version 5))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
        (url-retrieve-synchronously
         "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))

;;; Integration with use-package
(straight-use-package 'use-package)

(setq straight-use-package-by-default t)
(setq straight-vc-git-default-clone-depth 1)

Split custom settings config file

Set up the customize file to its own separate file, instead of saving customize settings in init.el. Try your best to make custom config clean.

(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(load custom-file)

Faster start

Less garbage collection

The default is 800 kilobytes. Measured in bytes.

(setq gc-cons-threshold 80000000)
(setq read-process-output-max (* 1024 1024))

How to measure it

(use-package esup
  :straight t)

Given an old school emacs user interface

Answer y or n instead of yes or no

(setq-default use-short-answers t)

Subpress warning buffer

(setq native-comp-async-report-warnings-errors nil)

The behavior of shell should be same when in emacs

Only macOS needs this config

(use-package exec-path-from-shell
  :straight t
  :if (memq window-system '(mac ns))
  :config
  (setq exec-path-from-shell-arguments '("-l"))
  (exec-path-from-shell-initialize)
  (exec-path-from-shell-copy-envs
   '("GOPATH" "GO111MODULE" "GOPROXY"
     "NPMBIN" "LC_ALL" "LANG" "LC_TYPE"
     "SSH_AGENT_PID" "SSH_AUTH_SOCK" "SHELL"
     "JAVA_HOME")))

No more asking whether to follow symlinks

(setq vc-follow-symlinks t)

No flash when error happens

(setq visible-bell nil)

Easily find where my cursor is

Highlight current line is a good idea

(global-hl-line-mode 1)

Light with shine when window scroll

(use-package beacon
  :straight t
  :init
  (beacon-mode 1))

Turn off the blinking cursor, I don’t need it

(blink-cursor-mode -1)

Avoid auto save and backup file which makes too many temp file

(setq make-backup-file nil)
(setq auto-save-default nil)
(setq backup-inhibited t)
(setq backup-directory-alist `(("." . "~/.saves")))

Move file to trash instead of delete it, it’s safer

(setq-default delete-by-moving-to-trash t)

After change opened file on other place, auto refresh it

(global-auto-revert-mode t)

Display all the font (Chinese, emoji and so on)

UTF8 everywhere

(setq utf-translate-cjk-mode nil) ; disable CJK coding/encoding (Chinese/Japanese/Korean characters)
(set-language-environment 'utf-8)
(set locale-coding-system 'utf-8)
(set-default-coding-systems 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-selection-coding-system
 (if (eq system-type 'windows-nt)
     'utf-16-le  ;; https://rufflewind.com/2014-07-20/pasting-unicode-in-emacs-on-windows
   'utf-8))
(prefer-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(set-clipboard-coding-system 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-buffer-file-coding-system 'utf-8)
(setq-default pathname-coding-system 'utf-8)
(set-file-name-coding-system 'utf-8)

Emoji support

Will automated download images for the first time

(use-package emojify
  :straight t
  :hook (after-init . global-emojify-mode))

Icon support

Run M-x all-the-icons-install-fonts to install fonts (only once)

;; Emoji: 😄, 🤦, 🏴󠁧󠁢󠁳󠁣󠁴󠁿
  (use-package all-the-icons
    :straight t
    :config
    (set-fontset-font t 'symbol "Apple Color Emoji")
    (set-fontset-font t 'symbol "Noto Color Emoji" nil 'append)
    (set-fontset-font t 'symbol "Segoe UI Emoji" nil 'append)
    (set-fontset-font t 'symbol "Symbola" nil 'append))

When code error happen, showing the cursor column is helpful

(column-number-mode t)

Long text in one line should be broken

(global-visual-line-mode 1)

Restore previous window layout

(use-package winner-mode
  :straight nil
  :hook (after-init . winner-mode))

Notify when package loading

(use-package package-loading-notifier
  :straight t
  :config
  (package-loading-notifier-mode 1))

What if I forgot some shortcut, only remember the prefix key, any hint?

Suggest next keys to me based on currently entered key combination.

(use-package which-key
  :straight t
  :init
  (which-key-mode 1)
  :config
  (which-key-setup-side-window-right-bottom)
  (setq which-key-sort-order 'which-key-key-order-alpha
        which-key-side-window-max-width 0.33
        which-key-idle-delay 2
        which-key-show-early-on-C-h t
        which-key-idle-secondary-delay 0.05)
  :diminish
  which-key-mode)

If more than one buffer have the same name, how to identify them?

(use-package uniquify
  :straight nil
  :config
  (setq uniquify-buffer-name-style 'reverse)
  (setq uniquify-separator "")
  (setq uniquify-after-kill-buffer-p t)
  (setq uniquify-ignore-buffers-re "^\\*"))

Meta key is too far on mac layout keyboard

make both command and option key to be meta key

(when (eq system-type 'darwin)
  (setq mac-command-modifier 'meta
        mac-option-modifier 'meta))
;; (when (eq system-type 'gnu/linux)
;; (setq  x-meta-keysym 'super
;;       x-super-keysym 'meta))

M-c may false triggering, disable it (I don’t use CUA)

(global-unset-key (kbd "M-c"))

Typing full command name is hard, why not make selection from list of items

(use-package vertico
  :straight t
  :init
  (vertico-mode))

Items order should be saved

(use-package savehist
  :config
  (setq history-length 25)
  (savehist-mode 1))

More items information

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

Exit cleanly

(defun teddy-ma/clean-exit ()
  "Exit Emacs cleanly.
If there are unsaved buffer, pop up a list for them to be saved
before existing. Replaces ‘save-buffers-kill-terminal’."
  (interactive)
  (if (frame-parameter nil 'client)
      (server-save-buffers-kill-terminal arg)
    (if-let ((buf-list (seq-filter (lambda (buf)
                                     (and (buffer-modified-p buf)
                                          (buffer-file-name buf)))
                                   (buffer-list))))
        (progn
          (pop-to-buffer (list-buffers-noselect t buf-list))
          (message "s to save, C-k to kill, x to execute"))
      (save-buffers-kill-emacs))))

I need power

(use-package power-mode
  :straight '(power-mode :host github
                         :branch "main"
                         :repo "elizagamedev/power-mode.el"))

Smooth Scrolling

;; Vertical Scroll
(setq scroll-step 1)
(setq scroll-margin 1)
(setq scroll-conservatively 101)
(setq scroll-up-aggressively 0.01)
(setq scroll-down-aggressively 0.01)
(setq auto-window-vscroll nil)
(setq fast-but-imprecise-scrolling nil)
(setq mouse-wheel-scroll-amount '(1 ((shift) . 1)))
(setq mouse-wheel-progressive-speed nil)
;; Horizontal Scroll
(setq hscroll-step 1)
(setq hscroll-margin 1)

Do not lock file

(setq-default create-lockfiles nil)

Given a file (buffer) when edit

Move effectively in one buffer

Move to beginning of line between head of line and head of text (head with blank space)

(use-package crux
  :straight t
  :bind (
         ("C-a" . crux-move-beginning-of-line)
         ))

Move to specific line (with preview and line number)

(use-package consult
  :straight t
  :bind
  (("M-g g" . consult-goto-line)))

Move to the beginning and end of buffer

M + < # beginning of buffer M + > # end of buffer

Search text in one buffer

M-s to search with key words

(use-package orderless
  :straight t
  :custom
  (completion-styles '(orderless)))

(use-package consult
  :straight t
  :bind
  (("M-s" . consult-line)))

Incremental find-and-replace

anzu-query-replace command, y to replace and n to skip

(use-package anzu
  :straight t
  :config
  (global-anzu-mode +1))

Select from buffers

C-x b can select buffers with preview (including bookmarks and files)

(use-package consult
  :straight t
  :bind
  (("C-x b" . consult-buffer)))

Rename or delete file with buffer

C-c r to rename file and buffer C-c D to delte file and buffer

(use-package crux
  :straight t
  :bind (
         ;;("C-c r" . crux-rename-file-and-buffer) ;; conflic with rails keybinding
         ("C-c D" . crux-delete-file-and-buffer)
         ))

Delete consecutive space at once

enchance default backspace and C-d

(use-package smart-hungry-delete
  :straight t
  :bind (("<backspace>" . smart-hungry-delete-backward-char)
         ("C-d" . smart-hungry-delete-forward-char))
  :defer nil ;; dont defer so we can add our functions to hooks
  :config (smart-hungry-delete-add-default-hooks))

Never leave whitespace at the end of lines, remove it on save.

(add-hook 'before-save-hook 'delete-trailing-whitespace)

Easily expand selection?

expand-region expands the region around the cursor semantically depending on mode. Hard to describe but a killer feature.

(use-package expand-region
  :straight t
  :bind ("C-=" . er/expand-region))

Zoom out && Zoom in

C-x C-= Zoom in C-x C-- Zoom out C-x 0 Reset

Multiple cursors

(use-package iedit
  :straight t)
(use-package multiple-cursors
  :straight t
  :config
  (global-unset-key (kbd "M-<down-mouse-1>"))
  (global-set-key (kbd "M-<mouse-1>") 'mc/add-cursor-on-click)
  (global-set-key (kbd "C-S-c C-S-c") 'mc/edit-lines)
  (global-set-key (kbd "C->") 'mc/mark-next-like-this)
  (global-set-key (kbd "C-<") 'mc/mark-previous-like-this))

Toggle between single and double quotes

(defun toggle-quotes ()
  "Toggle single quoted string to double or vice versa, and
  flip the internal quotes as well.  Best to run on the first
  character of the string."
  (interactive)
  (save-excursion
    (re-search-backward "[\"']")
    (let* ((start (point))
           (old-c (char-after start))
           new-c)
      (setq new-c
            (case old-c
              (?\" "'")
              (?\' "\"")))
      (setq old-c (char-to-string old-c))
      (delete-char 1)
      (insert new-c)
      (re-search-forward old-c)
      (backward-char 1)
      (let ((end (point)))
        (delete-char 1)
        (insert new-c)
        (replace-string new-c old-c nil (1+ start) end)))))

Swap two lines or two words location

M-<up> M-<down> to move line up and down

(use-package drag-stuff
  :straight t
  :diminish drag-stuff-mode
  :config
  (drag-stuff-global-mode t)
  (drag-stuff-define-keys)
  (add-to-list 'drag-stuff-except-modes 'org-mode))

Dumplicate current line or region

C-S-d if marked region, dumplicate region else current line

(use-package crux
  :straight t
  :bind (
         ("C-S-d" . crux-duplicate-current-line-or-region)
         ))

I remap capslock to ctrl, how can I simulate capslock key?

(use-package caps-lock
  :straight t)

By default emacs will not delete selection text when typing on it, let’s fix it

(delete-selection-mode t)

Quick copy target word (and other operation)

calling avy-goto-word-1 then press one key char and then press n, and select target word

(global-set-key (kbd “M-t”) ‘avy-goto-word-1) and a word that starts with a “w” and is select-able with “a”. Here’s what you can do now:

M-t w a to jump there M-t w x a - avy-action-kill-move: kill the word and move there, M-t w X a - avy-action-kill-stay: kill the word without moving the point, M-t w i a - avy-action-ispell: use ispell/flyspell to correct the word, M-t w y a - avy-action-yank: yank the word at point, M-t w t a - avy-action-teleport: kill the word and yank it at point, M-t w z a - avy-action-zap-to-char: kill from point up to selected point.

Undo many times and lost yourself?

Vundo (visual undo) displays the undo history as a tree and lets you move in the tree to go back to previous buffer states. To use vundo, type M-x vundo RET in the buffer you want to undo. An undo tree buffer should pop up. To move around, type:

f to go forward b to go backward

n to go to the node below when you at a branching point p to go to the node above

a to go back to the last branching point e to go forward to the end/tip of the branch

q to quit, you can also type C-g

(use-package vundo
  :straight '(vundo :host github
                    :branch "master"
                    :repo "casouri/vundo"))

Magnifying current line when showing content to other poeple

(defun magnifying-current-line ()
  (setq magnifying-overlay (make-overlay (line-beginning-position) (line-end-position)))
  (overlay-put magnifying-overlay 'font-lock-face '(:height 2.5)))

(defun magnifying-restore-line ()
  (dolist (o (overlays-in  (line-beginning-position) (line-end-position)))
    (delete-overlay o)))

(define-minor-mode magnifying-mode
  "Toggles global magnifying-mode."
  nil   ; Initial value, nil for disabled
  :global t
  :group 'magnifying
  :lighter " magnifying"
  :keymap
  (list
   (cons (kbd "C-c t") (lambda ()
                         (interactive)
                         (magnifying-current-line)))

   (cons (kbd "C-c m") (lambda ()
                         (interactive)
                         (magnifying-restore-line)))
   )

  (if magnifying-mode
      (message "magnifying-mode activated!")
    (message "magnifying-mode deactivated!")))

(add-hook 'magnifying-mode-hook (lambda () (message "magnifying hook was executed!")))
(add-hook 'magnifying-mode-on-hook (lambda () (message "magnifying turned on!")))
(add-hook 'magnifying-mode-off-hook (lambda () (message "magnifying turned off!")))

Hide current text for fun

(use-package redacted
  :straight t)

Given multiple files (buffers) when edit

Wgrep multi search result

use consult-grep to get global search result, C-S-a (embark-act) and E to export and C-c C-p to use wgrep to edit, after saved, use wgrep-save-all-buffers to confirm change

(use-package embark
  :straight t
  :bind
  (("C-S-a" . embark-act)       ;; pick some comfortable binding
   ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings'
  :init
  ;; Optionally replace the key help with a completing-read interface
  (setq prefix-help-command #'embark-prefix-help-command)
  :config
  ;; Hide the mode line of the Embark live/completions buffers
  (add-to-list 'display-buffer-alist
               '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
                 nil
                 (window-parameters (mode-line-format . none)))))

;; Consult users will also want the embark-consult package.
(use-package embark-consult
  :straight t
  :after (embark consult)
  :demand t ; only necessary if you have the hook below
  ;; if you want to have consult previews as you move around an
  ;; auto-updating embark collect buffer
  :hook
  (embark-collect-mode . consult-preview-at-point-mode))

;; wgrep allows you to edit a grep buffer and apply those changes to the file buffer like sed interactively.
;; No need to learn sed script, just learn Emacs.
(use-package wgrep
  :straight t)
(use-package consult
  :straight t
  :config
  (setq consult-project-root-function
        (lambda ()
          (when-let (project (project-current))
            (car (project-roots project)))))
  )

Given multiple windows

jump between windows

M-o if more than two windows, a leader key is needed

(use-package ace-window
  :straight t
  :config
  (setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l))
  (custom-set-faces
   '(aw-leading-char-face
     ((t (:inherit ace-jump-face-foreground :height 5.0)))))
  :bind
  ("M-o" . ace-window))

v/h layout toggle

(use-package transpose-frame :straight t)

swap window

ace-swap-window

save current windows layout

will lose after emacs restart window-configuration-to-register

change size of windows

C-x {(shrink-window-horizontally)
C-x }(enlarge-window-horizontally)

multiple windows show same file(buffer)

enable flow-mode and create new window (C-x 3)

kill selected window

press x when calling ace-window and select window many other actions https://github.com/abo-abo/ace-window#change-the-action-midway

Given an ugly default appearance

UI should be clear and modern

There are too many bars

no tool bar, no scroll bar, no menu bar, very clear

(tool-bar-mode -1)
(scroll-bar-mode -1)
(menu-bar-mode -1)

The icon in the titlebar on macOS looks not good

  (when (eq system-type 'darwin)
    (add-to-list 'default-frame-alist '(ns-transparent-titlebar . t))
    (add-to-list 'default-frame-alist '(ns-appearance . dark))
    (setq ns-use-proxy-icon  nil)
    (setq frame-title-format nil)
;;(setq-default frame-title-format '("M-EMACS - " user-login-name "@" system-name " - %b"))
    (setq frame-resize-pixelwise t))

I am not a newbie, no need to dispaly the help screen on startup

(setq inhibit-startup-screen t)

Make font a bit larger

Font on windows with 4k minitor is too small

(when (eq system-type 'windows-nt) (set-face-attribute 'default nil :height 100))

A dashboard on startup can clean my mind

(when (not (eq system-type 'windows-nt))
  (setq emacs-banner "~/Dropbox/Photos/emacs-banner.png")
  (use-package dashboard
    :straight t
    :config
    (dashboard-setup-startup-hook)
    (if (file-exists-p emacs-banner)
        (setq dashboard-startup-banner emacs-banner))

    (setq dashboard-items '((recents  . 5)
                            ;;(bookmarks . 5)
                            (projects . 5)
                            (agenda . 5))))
  )

Chinese and English font must align

It works fine with windows and org table in Chinese

(when (display-graphic-p)
  (when (eq system-type 'darwin)
    (setq fonts '("Monaco" "STKaiti"))
    (set-face-attribute 'default nil :font
                        (format "%s:pixelsize=%d" (car fonts) 15))
    (setq face-font-rescale-alist '(("STKaiti". 1.2))))

  (when (eq system-type 'windows-nt)
    (setq fonts '("Inconsolata" "华文楷体"))
    (set-face-attribute 'default nil :font
                        (format "%s:pixelsize=%d" (car fonts) 20))
    (setq face-font-rescale-alist '(("华文楷体". 1.0))))

  (when (eq system-type 'gnu/linux)
    (setq fonts '("Sarasa Mono SC Nerd" "STKaiti"))
    (set-face-attribute 'default nil :font
                        (format "%s:pixelsize=%d" (car fonts) 18))
    (setq face-font-rescale-alist '(("STKaiti". 1.0))))

  (dolist (charset '(kana han symbol cjk-misc bopomofo))
    (set-fontset-font (frame-parameter nil 'font) charset
                      (font-spec :family (car (cdr fonts)))))
  )

No need to display all the minor mode name

(use-package diminish
  :straight t)

In some case I want to hide the mode line

(use-package hide-mode-line
  :straight t)

Nyan Cat is lovely, it can live on mode line

(use-package nyan-mode
  :straight t
  :init
  (setq nyan-animate-nyancat t)
  (setq nyan-wavy-trail t)
  (setq nyan-minimum-window-width 80)
  (setq nyan-bar-length 20)
  (nyan-mode))

Mode line should be simple and customize

Custom mode line unitlity functions (align) https://gist.github.com/fhdhsni/990cba7794b4b6918afea94af0b30d66

(setq teddy-ma/current-mode-line-index 1)
(setq teddy-ma/mode-line-options `(""
                                   (eval `mode-line-modes)
                                   (teddy-ma/render-cyan)))
(setq teddy-ma/mode-line-content "")

(defun teddy-ma/toggle-mode-line-left-display()
  (interactive)
  (let ((idx (% teddy-ma/current-mode-line-index (length teddy-ma/mode-line-options))))
    (setq teddy-ma/mode-line-content (eval (nth idx teddy-ma/mode-line-options))))
  (setq teddy-ma/current-mode-line-index (+ teddy-ma/current-mode-line-index 1))
  (teddy-ma/refresh-mode-line))

(defun teddy-ma/render-mode-line-modes()
  teddy-ma/mode-line-content)

(defun mode-line-fill-right (face reserve)
  "Return empty space using FACE and leaving RESERVE space on the right."
  (unless reserve
    (setq reserve 20))
  (when (and window-system (eq 'right (get-scroll-bar-mode)))
    (setq reserve (- reserve 3)))
  (propertize " "
              'display `((space :align-to (- (+ right right-fringe right-margin) ,reserve)))
              'face face))

(defun mode-line-fill-center (face reserve)
  "Return empty space using FACE to the center of remaining space leaving RESERVE space on the right."
  (unless reserve
    (setq reserve 20))
  (when (and window-system (eq 'right (get-scroll-bar-mode)))
    (setq reserve (- reserve 3)))
  (propertize " "
              'display `((space :align-to (- (+ center (.5 . right-margin)) ,reserve
                                             (.5 . left-margin))))
              'face face))

(defconst RIGHT_PADDING 1)

(defun reserve-left/middle ()
  (/ (length (format-mode-line mode-line-align-middle)) 2))

(defun reserve-middle/right ()
  (+ RIGHT_PADDING (length (format-mode-line mode-line-align-right))))

Setup Model Line content

(defun teddy-ma/refresh-middle-mode-line ()
  (setq mode-line-align-middle ;; git status, modified status
        '(""
          (vc-mode vc-mode)
          "%3 "
          (:eval
           (when (eql (buffer-modified-p) t)
             ;; propertize adds metadata to text, so you can add colours and formatting, amongst other things
             (propertize "" 'face '(:foreground "black"))))
          " "
          (:eval
           (when (eql buffer-read-only t)
             (propertize "" 'face '(:foreground "pink"))))
          "")))

(defun teddy-ma/refresh-right-mode-line ()
  (setq mode-line-align-right ;; line number and point location
        '(""
          mode-line-misc-info
          "%2 "
          (:eval (format "%%l/%d : %%c " (line-number-at-pos (point-max))))))
  )

(defun teddy-ma/refresh-left-mode-line ()
  (setq mode-line-align-left ;; buffer name
        '(
          ""
          "%2 "
          (:propertize "%b" face mode-line-buffer-id) ;; buffer name
          "%2 "
          ;;mode-line-modes
          )))

Toggle mode line left content between blank, nyancat and mode names

(defun teddy-ma/render-cyan()
  '(:eval (list (nyan-create))))

(defun teddy-ma/refresh-mode-line ()
  (teddy-ma/refresh-left-mode-line)
  (teddy-ma/refresh-middle-mode-line)
  (teddy-ma/refresh-right-mode-line)
  (setq-default mode-line-format
                (list
                 (teddy-ma/render-mode-line-modes)
                 mode-line-align-left '(:eval (mode-line-fill-center 'mode-line (reserve-left/middle)))
                 mode-line-align-middle '(:eval (mode-line-fill-right 'mode-line (reserve-middle/right)))
                 mode-line-align-right
                 ))
  )
(teddy-ma/refresh-mode-line)

I want a light theme and a dark theme

(use-package kaolin-themes
  :straight t)

And can switch them (toggle dark and light theme)

(setq-default custom-enabled-themes '(kaolin-light))

(defun reapply-themes ()
  "Forcibly load the themes listed in `custom-enabled-themes'."
  (dolist (theme custom-enabled-themes)
    (unless (custom-theme-p theme)
      (disable-theme theme)
      (load-theme theme)))
  (custom-set-variables `(custom-enabled-themes (quote ,custom-enabled-themes))))

(add-hook 'after-init-hook 'reapply-themes)

(defun theme-light ()
  "Activate a light color theme."
  (interactive)
  (setq custom-enabled-themes '(kaolin-light))
  (reapply-themes))

(defun theme-dark ()
  "Activate a dark color theme."
  (interactive)
  (setq custom-enabled-themes '(kaolin-dark))
  (reapply-themes))

(defun theme-day ()
  "Activate a light day color theme."
  (interactive)
  (color-theme-sanityinc-tomorrow-day))

load-theme without annoying confirmation

;; (advice-add 'load-theme
;;             :around
;;             (lambda (fn theme &optional no-confirm no-enable)
;;               (funcall fn theme t)))

custom hl-line and mode-line bg color if necessary

;; (set-face-background 'hl-line "midnight blue")
;; (set-face-background hl-line-face "gray13")
;; (set-face-background 'mode-line "color-28")

Make emacs transparency

Only works on windowed emacs on macOS

(defun increase-transparency ()
  "Increase Transparency"
  (interactive)
  (seethru-relative 10))

(defun decrease-transparency ()
  "Decrease Transparency"
  (interactive)
  (seethru-relative -10))

(use-package seethru
  :straight t
  :bind
  (("C-c 9" . increase-transparency)
   ("C-c 8" . decrease-transparency))
  :config
  (seethru 95))

Long may the sun shine!

Solaire YYDS!

(use-package solaire-mode
  :straight t
  :hook (after-init . solaire-global-mode))

Given common programming work

Align non space

(defun align-non-space (BEG END)
  "Align non-space columns in region BEG END."
  (interactive "r")
  (align-regexp BEG END "\\(\\s-*\\)\\S-+" 1 1 t))

No tabs and

(setq-default indent-tabs-mode nil)

Show empty line

(setq-default indicate-empty-lines nil)

Don’t count two spaces after a period as the end of a sentence.

Just one space is needed.

(setq sentence-end-double-space nil)

Make matched parentheses colored

(use-package rainbow-delimiters
  :straight t
  :config
  (add-hook 'prog-mode-hook 'rainbow-delimiters-mode))

Keep parentheses balanced

(use-package smartparens
  :straight t
  :diminish
  smartparens-mode
  :hook
  (prog-mode . smartparens-mode))

Highlight strings which represent colors

I only want this in programming modes, and I don’t want colour names to be highlighted (x-colors).

(use-package rainbow-mode
  :straight t
  :config
  (setq rainbow-x-colors nil)
  :hook
  (prog-mode . rainbow-mode))

Auto indented code when edit

(use-package aggressive-indent
  :straight t)

Support editorconfig config file.

(use-package editorconfig
  :straight t
  :diminish editorconfig-mode
  :config
  (editorconfig-mode 1))

Format all

(use-package format-all
  :straight t
  :hook
  (prog-mode . format-all-mode))

Quickrun

(use-package quickrun
  :straight t)

Tree Sitter

(when (not (eq system-type 'windows-nt))
  (use-package tree-sitter
    :straight t
    :config
    (global-tree-sitter-mode)
    ;; you can easily see the difference tree-sitter-hl-mode makes for python, ts or tsx
    ;; by switching on and off
    (add-hook 'tree-sitter-after-on-hook #'tree-sitter-hl-mode))

  (use-package tree-sitter-langs
    :straight t
    :after tree-sitter))

Quick insert log

(use-package turbo-log
  :straight '(turbo-log :host github
                        :branch "master"
                        :repo "artawower/turbo-log.el")
  :bind (("C-s-l" . turbo-log-print)
         ("C-s-i" . turbo-log-print-immediately)
         ("C-s-h" . turbo-log-comment-all-logs)
         ("C-s-s" . turbo-log-uncomment-all-logs)
         ("C-s-[" . turbo-log-paste-as-logger)
         ("C-s-]" . turbo-log-paste-as-logger-immediately)
         ("C-s-d" . turbo-log-delete-all-logs))
  :config
  (setq turbo-log-msg-format-template "\"🚀: %s\"")
  (setq turbo-log-allow-insert-without-tree-sitter-p t))

View DevDocs

(use-package devdocs
  :straight t)

Underscore -> UPCASE -> CamelCase conversion

(defun my-string-inflection-cycle-auto ()
  "switching by major-mode"
  (interactive)
  (cond
   ((eq major-mode 'emacs-lisp-mode)
    (string-inflection-all-cycle))
   ((eq major-mode 'java-mode)
    (string-inflection-java-style-cycle))
   ((eq major-mode 'ruby-mode)
    (string-inflection-ruby-style-cycle))
   (t
    (string-inflection-all-cycle))))

(use-package string-inflection
  :straight t
  :config
  (global-set-key (kbd "C-M-j") 'my-string-inflection-cycle-auto))

Project management

Projectile handles folders which are in version control.

(use-package projectile
  :straight t
  :config
  (projectile-mode +1)
  (define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)
  (setq projectile-enable-caching t)
  (projectile-mode))

Neotree provide a sidebar of project files

(use-package neotree
  :straight t
  :config
  (defun neotree-project-tree-open ()
    (interactive)
    (let ((project-dir (ignore-errors (projectile-project-root)))
          (file-name (buffer-file-name)))
      (if project-dir
          (progn
            (neotree-dir project-dir)
            (neotree-find file-name))
        (neotree-find)))
    (neo-global--select-window))

  (defun neotree-project-tree-toggle ()
    (interactive)
    (if (neo-global--window-exists-p)
        (neotree-hide)
      (neotree-project-tree-open)))

  (global-set-key (kbd "C-<tab>") 'neotree-project-tree-toggle)

  (setq neo-show-hidden-files t)
  (setq neo-theme 'arrow)
  (setq neo-window-width 35)

  (defun custom-neotree-enter-hide ()
    (interactive)
    (neotree-enter)
    (let ((current (neo-buffer--get-filename-current-line)))
      (if (not (and current (file-accessible-directory-p current)))
          (neotree-hide))))

  (defun custom-neotree-peek ()
    (interactive)
    (let ((neo-window (neo-global--get-window)))
      (neotree-enter)
      (select-window neo-window)))

  (add-hook
   'neotree-mode-hook
   (lambda ()
     (define-key neotree-mode-map (kbd "RET") 'custom-neotree-enter-hide)))

  (add-hook
   'neotree-mode-hook
   (lambda ()
     (define-key neotree-mode-map (kbd "TAB") 'custom-neotree-peek))))

Consult integration for projectile

(use-package consult-projectile
  :straight (consult-projectile
             :type git
             :host gitlab
             :repo "OlMon/consult-projectile"
             :branch "master"))

Fuzzy search

fzf is a fuzzy file finder which is very quick.

(use-package fzf
  :straight t)

Deadgrep

You can now edit files directly from results buffers with M-x deadgrep-edit-mode.

(use-package deadgrep
  :straight t)

Open File with Line Number

Open files and goto lines like we see from g++ etc. i.e. Gemfile:12

(defadvice find-file (around find-file-line-number
                             (filename &optional wildcards)
                             activate)
  "Turn files like file.cpp:14 into file.cpp and going to the 14-th line."
  (save-match-data
    (let* ((matched (string-match "^\\(.*\\):\\([0-9]+\\):?$" filename))
           (line-number (and matched
                             (match-string 2 filename)
                             (string-to-number (match-string 2 filename))))
           (filename (if matched (match-string 1 filename) filename)))
      ad-do-it
      (when line-number
        ;; goto-line is for interactive use
        (goto-char (point-min))
        (forward-line (1- line-number))))))

Jump to source

Individual language packages often support IDE features like jump to source, but dumb-jump attempts to support many languages by simple searching. It’s quite effective even with dynamic libraries like JS and Python.

(use-package dumb-jump
  :straight t
  :diminish dumb-jump-mode
  :bind (("C-M-g" . dumb-jump-go)
         ("C-M-p" . dumb-jump-back)
         ("C-M-q" . dumb-jump-quick-look)))

Using Git inside Emacs

Magit is an awesome interface to git. Summon it with `C-x g`.

(use-package magit
  :straight t
  :commands (magit-status magit-file-dispatch)
  :config
  (setq magit-blame-styles
        '((margin
           (margin-format    . ("%a %A %s"))
           (margin-width     . 42)
           (margin-face      . magit-blame-margin)
           (margin-body-face . (magit-blame-dimmed)))
          (headings
           (heading-format   . "%-20a %C %s\n"))
          (highlight
           (highlight-face   . magit-blame-highlight))
          (lines
           (show-lines       . t)
           (show-message     . t))))
  :bind
  ("C-x g" . magit-status))

Display line changes in gutter based on git history. Enable it everywhere.

(use-package git-gutter
  :straight t
  :diminish git-gutter-mode
  :config
  (global-git-gutter-mode t))

TimeMachine lets us step through the history of a file as recorded in git.

(use-package git-timemachine
  :straight '(git-timemachine :host github
                              :branch "master"
                              :repo "emacsmirror/git-timemachine"))

bind M-p - to list all git repos in some folder

(setq magit-repository-directories '(("\~/code" . 4)))

(defun magit-status-with-prefix-arg ()
  "Call `magit-status` with a prefix."
  (interactive)
  (let ((current-prefix-arg '(4)))
    (call-interactively #'magit-status)))

(global-set-key (kbd "M-p p") 'magit-status-with-prefix-arg)

Syntax checking

Flycheck is a general syntax highlighting framework which other packages hook into. It’s an improvment on the built in flymake.

Setup is pretty simple - we just enable globally and turn on a custom eslint function, and also add a custom checker for proselint.

(use-package flycheck
  :straight t
  :config
  (add-hook 'after-init-hook 'global-flycheck-mode)
  (add-to-list 'flycheck-checkers 'proselint)
  (setq-default flycheck-highlighting-mode 'lines)
  ;; Define fringe indicator / warning levels
  (define-fringe-bitmap 'flycheck-fringe-bitmap-ball
    (vector #b00000000
            #b00000000
            #b00000000
            #b00000000
            #b00000000
            #b00000000
            #b00000000
            #b00011100
            #b00111110
            #b00111110
            #b00111110
            #b00011100
            #b00000000
            #b00000000
            #b00000000
            #b00000000
            #b00000000))
  (flycheck-define-error-level 'error
    :severity 2
    :overlay-category 'flycheck-error-overlay
    :fringe-bitmap 'flycheck-fringe-bitmap-ball
    :fringe-face 'flycheck-fringe-error)
  (flycheck-define-error-level 'warning
    :severity 1
    :overlay-category 'flycheck-warning-overlay
    :fringe-bitmap 'flycheck-fringe-bitmap-ball
    :fringe-face 'flycheck-fringe-warning)
  (flycheck-define-error-level 'info
    :severity 0
    :overlay-category 'flycheck-info-overlay
    :fringe-bitmap 'flycheck-fringe-bitmap-ball
    :fringe-face 'flycheck-fringe-info))

Color Identifier

(use-package color-identifiers-mode
  :straight t
  :commands color-identifiers-mode)

Autocomplete

Company mode provides good autocomplete options. Perhaps I should add company-quickhelp for documentation (https://github.com/expez/company-quickhelp)? It would also be good to improve integration with yasnippet as I don’t feel I’m making the best use there.

(use-package company
  :straight t
  :config
  (add-hook 'after-init-hook 'global-company-mode)

  (setq company-idle-delay t)

  (use-package company-anaconda
    :straight t
    :config
    (add-to-list 'company-backends 'company-anaconda)))

I don’t want suggestions from open files / buffers to be automatically lowercased as these are often camelcase function names.

(setq company-dabbrev-downcase nil)

Snippets

Type the shortcut and press TAB to complete, or M-/ to autosuggest a snippet. http://ergoemacs.org/emacs/yasnippet_templates_howto.html

yasnippet-snippets has to be loaded before yasnippet for user snippets to override the pre-built ones.

(use-package yasnippet-snippets
  :straight t)

(use-package yasnippet
  :straight t
  :diminish yas-minor-mode
  :config
  (add-to-list 'yas-snippet-dirs "~/.emacs.d/snippets")
  (yas-global-mode 1)
  (global-set-key (kbd "M-/") 'company-yasnippet))

(use-package consult-yasnippet
  :straight '(consult-yasnippet :host github
                                :branch "master"
                                :repo "mohkale/consult-yasnippet"))

Toggle side org file to take note

(defun teddy-ma/toggle-side-org-buffer ()
  "Toggle org file in a side buffer for quick note taking."
  (interactive)
  (teddy-ma/toggle-side-buffer-with-file "~/Documents/org/work.org"))

(defun teddy-ma/buffer-visible-p (buffer)
  "Check if given BUFFER is visible or not.  BUFFER is a string representing the buffer name."
  (or
   (eq buffer (window-buffer (selected-window)))
      (get-buffer-window buffer)))

(defun teddy-ma/display-buffer-in-side-window (buffer)
  "Just like `display-buffer-in-side-window' but only takes a BUFFER and rest of the parameters are for my taste."
  (select-window
   (display-buffer-in-side-window
    buffer
    (list (cons 'side 'right)
          (cons 'slot 0)
          (cons 'window-width 84)
          (cons 'window-parameters
                (list (cons 'no-delete-other-windows t)
                      (cons 'no-other-window nil)))))))

(defun teddy-ma/remove-window-with-buffer (the-buffer-name)
  "Remove window containing given THE-BUFFER-NAME."
  (mapc (lambda (window)
          (when (string-equal (buffer-name (window-buffer window)) the-buffer-name)
            (delete-window window)))
        (window-list (selected-frame))))

(defun teddy-ma/toggle-side-buffer-with-file (file-path)
  "Toggle FILE-PATH in a side buffer. The buffer is opened in side window so it can't be accidentaly removed."
  (let ((fname (file-name-nondirectory file-path)))
    (if (teddy-ma/buffer-visible-p fname)
        (teddy-ma/remove-window-with-buffer fname)
      (teddy-ma/display-buffer-in-side-window
       (save-window-excursion
         (find-file file-path)
         (current-buffer))))))

Given a programming language

LSP

Language Server Protocol

(use-package lsp-mode
   :straight t)

Common lisp

(use-package slime
  :straight t
  :config
  (setq inferior-lisp-program "sbcl")
  )

Quick run a programming file

(use-package quickrun
  :straight t)

Javascript

Indent 2 spaces.

(setq-default js-indent-level 2)

Enchance JS mode

JS2 mode improves on the built in JS mode.

(use-package js2-mode
  :straight t
  :mode "\\.js\\'"
  :config
  (setq-default js2-ignored-warnings '("msg.extra.trailing.comma")))

Refactor

js2-refactor supports some useful refactoring options and builds on top of js2-mode.

(use-package js2-refactor
  :straight t
  :config
  (js2r-add-keybindings-with-prefix "C-c C-m")
  :hook
  (after-js-mode . js-refactor-mode))

RJSX mode makes JSX work well.

(use-package rjsx-mode
  :straight t)

Prettier

Prettier-js autoformats JS code - much like `gofmt` - and we hook it into JS2 and RJSX modes.

(use-package prettier-js
  :straight t
  :config
  (setq prettier-js-args '(
                           "--trailing-comma" "es5"
                           "--single-quote" "true"
                           "--print-width" "100"
                           ))
  :hook
  (after-js2-mode . prettier-js-mode)
  (after-rjsx-mode . prettier-js-mode))

JSON

(use-package json-mode
  :straight t)

Typescript

https://vxlabs.com/2022/06/12/typescript-development-with-emacs-tree-sitter-and-lsp-in-2022/

(use-package typescript-mode
  :straight t
  :after tree-sitter)

Yaml

sytnax highlight

(use-package yaml-mode
  :straight t)

Toml

sytnax highlight

(use-package toml-mode
  :straight t)

Markdown

sytnax highlight

Markdown support isn’t built into Emacs, add it with markdown-mode.

(use-package markdown-mode
  :straight t
  :commands (markdown-mode gfm-mode)
  :mode (("README\\.md\\'" . gfm-mode)
         ("\\.md\\'" . markdown-mode)
         ("\\.markdown\\'" . markdown-mode))
  :init (setq markdown-command "multimarkdown"))

Lua

sytnax highlight

(use-package lua-mode
  :straight t)

fennel support

(use-package fennel-mode
  :straight t)

Haskell

sytnax highlight

(use-package haskell-mode
  :straight t)

format

(use-package hindent
  :straight t)

Elixir

sytnax highlight

Elixir highlighting is not built into emacs at present. Elixir-mode gives all the usual niceties, and alchemist improves interaction with tools like iex, mix and elixir-format.

(use-package elixir-mode
  :straight t)
(use-package alchemist
  :straight t)

Solidity

(use-package solidity-mode
  :straight t)

Ruby

Basic

(use-package rake
  :straight t)

;; (use-package robe
;;   :straight t
;;   :diminish
;;   :hook
;;   (after-ruby-mode . robe-mode))

(use-package ruby-hash-syntax
  :straight t
  :diminish
  :hook
  (after-ruby-mode . ruby-hash-syntax))

;; (use-package rubocop
;;   :straight t
;;   :diminish
;;   :hook
;;   (after-ruby-mode . rubocop-mode))

(use-package haml-mode
  :straight t)

(use-package minitest
  :straight t)

Rails

open latest migration file

(defun teddy-ma/visit-last-dired-file ()
  "Open the last file in an open dired buffer."
  (interactive)
  (end-of-buffer)
  (previous-line)
  (dired-find-file))

(defun teddy-ma/visit-last-migration ()
  "Open the last file in 'db/migrate/'."
  (interactive)
  (dired (expand-file-name "db/migrate" (projectile-project-root)))
  (teddy-ma/visit-last-dired-file)
  (kill-buffer "migrate"))

Rails route insert

https://github.com/otavioschwanck/rails-routes.el

(use-package rails-routes
    :straight '(rails-routes :host github
                          :branch "master"
                          :repo "otavioschwanck/rails-routes.el"))

Snippet

cls -> create class with file name

projectile extends

(use-package projectile-rails
  :straight t
  :config
  (define-key projectile-rails-mode-map
    (kbd "C-c r")
    'projectile-rails-command-map)
  (setq projectile-rails-vanilla-command "bin/rails"
        projectile-rails-spring-command "bin/spring"
        projectile-rails-zeus-command "bin/zeus")
  (projectile-rails-global-mode))

C

Emacs has a great built in C/C++ mode, but we can improve on it with irony-mode for code completion via libclang.

(use-package irony
  :straight t
  :hook (c-mode . irony-mode))

Add company mode support.

(use-package company-irony
  :straight t
  :config
  (add-to-list 'company-backends 'company-irony))

Add flycheck support.

(use-package flycheck-irony
  :straight t
  :hook (flycheck-mode . flycheck-irony-setup))

Go

(use-package go-mode
  :straight t)

Rust

https://robert.kra.hn/posts/2021-02-07_rust-with-emacs/

Syntax highlight and lsp support

(use-package rustic
  :straight t
  :bind (:map rustic-mode-map
              ("M-j" . lsp-ui-imenu)
              ("M-?" . lsp-find-references)
              ("C-c C-c l" . flycheck-list-errors)
              ("C-c C-c a" . lsp-execute-code-action)
              ("C-c C-c r" . lsp-rename)
              ("C-c C-c q" . lsp-workspace-restart)
              ("C-c C-c Q" . lsp-workspace-shutdown)
              ("C-c C-c s" . lsp-rust-analyzer-status)
              ("C-c C-c e" . lsp-rust-analyzer-expand-macro)
              ("C-c C-c d" . dap-hydra)
              ("C-c C-c h" . lsp-ui-doc-glance))
  :config
  (setq rustic-babel-format-src-block nil)
  )

Playground for creating / cleanup rust scratch projects quickly

(use-package rust-playground
  :straight t)

Web mode

sytnax highlight

Web mode handles html/css/js.

(use-package web-mode
  :straight t
  :mode (("\\.html\\'" . web-mode)
         ("\\.erb\\'" . web-mode))
  :config
  (setq web-mode-markup-indent-offset 2)
  )

close tag manually

C-c C-e /

jump between opening / closing HTML tags

C-c C-n

format

Web beautify prettifies html / css / js using js-beautify - install with npm install -g js-beautify.

(use-package web-beautify
  :straight t
  :bind (:map web-mode-map
              ("C-c b" . web-beautify-html)
              :map js2-mode-map
              ("C-c b" . web-beautify-js)))

HTML preview

(use-package impatient-mode
  :straight t)

Quick insert

Emmet mode, use C-j to expand it

;; (use-package emmet-mode
;;   :straight t
;;   :config
;;   (add-hook 'web-mode-hook  'emmet-mode)
;;   (add-hook 'css-mode-hook  'emmet-mode) ;; enable Emmet's css abbreviation.
;;   )

Highlight matching tag

web-mode-toggle-current-element-highlight

shortcuts

keydescription
C-c C-ftoggle folding on a tag/block
C-c C-ea select element content
C-c C-e kelement kill (useful when html struct complex)
C-c C-d dshow tag mismatch (check)
C-c C-e wwrap element
C-c C-e velement vanish(remove tag)
M-;un/comment block

Given an org file

Basic setup

(use-package org
  :straight (:type built-in)
  :diminish org-indent-mode
  :config
  (setq org-attach-use-inheritance t) ;; use parent node id as attachment folder
  (setq org-startup-indented 'f)
  (setq org-special-ctrl-a/e 't)
  (setq org-startup-folded t)
  (setq org-tags-column -77)
  (setq org-src-tab-acts-natively t)
  (setq org-src-fontify-natively t)
  (setq org-src-window-setup 'current-window)
  ;;(setq browse-url-browser-function 'browse-url-generic
        ;;browse-url-generic-program "Chromium")
  ;;(setq org-file-apps '((auto-mode . emacs)("\\.x?html?\\'" . "Chromium %s")))
  )

Navigate between headings

C-c C-nnext heading (can be difference level)
C-c C-pprevious heading (can be difference level)
C-c C-fnext heading (same level)
C-c C-bprevious heading (same level)
C-c C-uupper level

Change level recusively

M+S+<left/right>

Quick insert code block

eg. press `<s` and select shell or src block

(use-package company-org-block
  :straight t
  :custom
  (company-org-block-edit-style 'auto) ;; 'auto, 'prompt, or 'inline
  :hook ((org-mode . (lambda ()
                       (setq-local company-backends '(company-org-block))
                       (company-mode +1)))))

focus on one org node

org-narrow-to-subtree (bound in org-mode) C-x n s

Given a todo list

Custom todo sequence

(setq org-todo-keywords '((sequence "TODO" "DOING" "|" "DONE" "BLOCKED")))

nested check list

to use checkbox just typing - [] for parent check list, add [/] use C-c C-c to toggle finish checkbox M-S-RET to create new checkbox

Log time when a task is done

(setq org-log-into-drawer t)
(setq org-log-done 'time)

I need a pomodoro

C-c C-x ; start C-c C-x , pause or continue C-c C-x _ stop

;; (setq emacs-org-clock-sound-voice "~/Dropbox/Public/pomodoro-wei.wav")
;; (if (file-exists-p emacs-org-clock-sound-voice)
;;     (setq org-clock-sound emacs-org-clock-sound-voice))

After old task is done, archive (refiling) it

Save Org buffers after refiling!

 (setq org-refile-targets
	'(("~/Documents/org/archive.org" :maxlevel . 1)))

 (advice-add 'org-refile :after 'org-save-all-org-buffers)

Given an org workflow

Capture my task and idea

(use-package org-capture
  :straight nil
  :bind ("C-c c" . org-capture)
  :after org
  :config
  (setq org-capture-templates
        '(
          ("g" "General To-Do"
           entry (file+headline "~/Documents/org/todo.org" "General Tasks")
           "* TODO [#B] %?\n:Created: %T\n "
           :empty-lines 0)

          ("c" "Code To-Do"
           entry (file+headline "~/Documents/org/todo.org" "Code Related Tasks")
           "* TODO [#B] %?\n:Created: %T\n%i\n%a\nProposed Solution: "
           :empty-lines-before 0)

          ("j" "Work Log Entry"
           entry (file+datetree "~/Documents/org/work.org")
           "* %?"
           :empty-lines 0)
          )
        )
  )

Screenshots

require pngpaste installed

(use-package org-download
  :straight t
  :after org
  :custom
  (org-download-method 'attach)
  (org-download-image-dir "images")
  (org-download-heading-lvl nil)
  (org-download-timestamp "%Y%m%d-%H%M%S_")
  (org-image-actual-width 300)
  (org-download-screenshot-method "/usr/local/bin/pngpaste %s")
  :bind
  ("C-M-y" . org-download-screenshot)
  :config
  (require 'org-download))

Easily open common files

(defmacro defun-open-conf (config-name filepath)
  "Open conf macro.
  Argument CONFIG-NAME Alias name for your function.
  Argument FILEPATH Filepath of the file."
  `(defun ,(intern (format "open-conf-%s" config-name)) ()
     ,(format "Open %s: \"%s\"" config-name filepath)
     (interactive)
     (open-conf ,filepath)))

(defun open-conf (filepath)
  "Argument FILEPATH Filepath of the file."
  ;; (switch-to-buffer
  ;;  (find-file-noselect filepath))
  (find-file-other-frame filepath))

(defun-open-conf "work"      "~/Documents/org/work.org")
(defun-open-conf "blog"      "~/Documents/org/blog.org")
(defun-open-conf "todo"      "~/Documents/org/todo.org")
;;  (defun-open-conf "note"      "~/Documents/org/note.org")
(defun-open-conf "reminder"  "~/Documents/org/reminder.org")
;; (defun-open-conf "index"     "~/Documents/org/roam/index.org")
;; (set-register ?b (cons 'file "~/Documents/org/blog.org"))
;; (set-register ?t (cons 'file "~/Documents/org/todo.org"))
;; (set-register ?n (cons 'file "~/Documents/org/note.org"))
;; (set-register ?r (cons 'file "~/Documents/org/roam/index.org"))

Exported html should have no default style

I can style it myself

(setq org-html-head-include-default-style nil
      org-html-htmlize-output-type 'css)

Execute Org src block

in src block press C-c C-c to run code

(org-babel-do-load-languages
 'org-babel-load-languages
 '((emacs-lisp . t)
   (lisp . t)
   (ruby . t)
   (js .t )
   (lua .t )
   (shell . t)))

Insert org markup easily

(use-package wrap-region
  :straight t
  :config
  (wrap-region-add-wrappers
   '(("*" "*" nil org-mode)
     ("~" "~" nil org-mode)
     ("/" "/" nil org-mode)
     ("=" "=" "+" org-mode)
     ("_" "_" nil org-mode)
     ("$" "$" nil org-mode )))
  (add-hook 'org-mode-hook 'wrap-region-mode))

Insert table quickly

Use C-c | to fire create table function input ||| and press TAB to quick insert a three colum table

Agenda

Interactive agenda in the console https://github.com/rougier/agenda

(use-package org-agenda
  :straight nil
  :bind ("C-c a" . org-agenda)
  :config
  (setq org-agenda-files (directory-files-recursively "~/Documents/org/" "\\.org$"))
  ;; (setq org-agenda-files '(
  ;;                          "~/Documents/org/work.org"
  ;;                          "~/Documents/org/reminder.org"
  ;;                         ))
  (setq org-agenda-start-with-log-mode t)
  (setq org-agenda-prefix-format
        '((agenda . " %i %-24:c%?-16t%-10e% s")
          (todo   . " %i %-36:c %-16e")
          (tags   . " %i %-24:c")
          (search . " %i %-24:c")))

  ;; https://www.philnewton.net/blog/how-i-get-work-done-with-emacs/
  (setq org-agenda-custom-commands
        '(("d" "Today's Tasks"
           ((agenda "" ((org-agenda-span 1)
                        (org-agenda-overriding-header "Today's Tasks")))))))
  )

Org Roam

(use-package emacsql-sqlite
  :straight t)

(use-package org-roam
  :straight (:host github :repo "org-roam/org-roam" :files (:defaults "extensions/*"))
  :init
  (setq org-roam-v2-ack t)
  :custom
  (org-roam-directory (file-truename "~/Documents/org/roam/"))
  :bind (("C-c n l" . org-roam-buffer-toggle)
         ("C-c n f" . org-roam-node-find)
         ("C-c n g" . org-roam-graph)
         ("C-c n i" . org-roam-node-insert)
         ("C-c n c" . org-roam-capture)
         ;; Dailies
         ("C-c n j" . org-roam-dailies-capture-today))
  :config
  (org-roam-setup))

(use-package org-roam-ui
  :straight
  (:host github :repo "org-roam/org-roam-ui" :branch "main" :files ("*.el" "out"))
  :after org-roam
  :config
  (setq org-roam-ui-sync-theme t
        org-roam-ui-follow t
        org-roam-ui-update-on-save t
        org-roam-ui-open-on-start nil))

Turn heading into a node

move the cursor to the heading, and run: org-id-get-create

Given org UI

make headings colorful

;; (custom-set-faces
;;  '(default     ((t (:foreground "#BBC2CF"))))
;;  '(org-level-1 ((t (:foreground "#BF9D7A"))))
;;  '(org-level-2 ((t (:foreground "#E4E9CD"))))
;;  '(org-level-3 ((t (:foreground "#EBF2EA"))))
;;  '(org-level-4 ((t (:foreground "#0ABDA0"))))
;;  '(org-level-5 ((t (:foreground "#80ADD7")))))

Beautify Org heading symbol

(use-package org-superstar
  :straight t
  :hook (org-mode . org-superstar-mode))

Emoji Org tag

(use-package org-pretty-tags
  :diminish org-pretty-tags-mode
  :straight t
  :config
  ;;(setq org-pretty-tags-surrogate-strings
  ;; '(
  ;; ("work"  . "⚒")
  ;; ("@pc" . "🖥")
  ;; ("@ps5" . "🎮")
  ;; ("@switch" . "🕹")
  ;; ("@script" . "📝")
  ;; ))
  (org-pretty-tags-global-mode))

Colorful todo stags

(use-package hl-todo
  :straight t
  :hook ((prog-mode org-mode) . teddy-ma/hl-todo-init)
  :init
  (defun teddy-ma/hl-todo-init ()
    (setq-local hl-todo-keyword-faces '(("TODO" . "#ff9977")
                                        ("DOING" . "#FF00BC")
                                        ("DONE" . "#44bc44")
                                        ("BLOCKED" . "#003366")
                                        ))
    (hl-todo-mode))
  )

Org fancy Priorities

(use-package org-fancy-priorities
  :diminish
  :straight t
  :hook (org-mode . org-fancy-priorities-mode)
  :config
  (setq org-fancy-priorities-list '("🅰" "🅱" "🅲" "🅳" "🅴")))

Fixed pitch font

Set fixed pitch font for org (conflict with Chinese table)

;; conflict with chinese table in org
(defun teddy-ma/org-font-setup ()
  ;;   (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-table nil   :inherit '(shadow 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)
  )

Keep some padding each side

(use-package visual-fill-column
  :straight t
  :hook (org-mode . visual-fill-column-mode)
  :custom
  (visual-fill-column-center-text t)
  (visual-fill-column-width 120))

Completed tasks should be pierced through visually

to intensify the sense of achievement.

;; (setq org-fontify-done-headline t)
;; (set-face-attribute 'org-done nil :strike-through t)
;; (set-face-attribute 'org-headline-done nil
;;                     :strike-through t
;;                     :foreground "light gray")

Custom org block style

(defun teddy-ma/org-block-setup ()
  (let ((background-color (face-background 'default))
        (foreground-color (face-foreground 'default))
        (primary-green "#60a83d"))
    (set-face-attribute 'fringe nil :foreground foreground-color :background background-color)
    (set-face-attribute 'org-indent nil :background nil :foreground nil)
    (set-face-attribute 'org-block-begin-line nil :foreground primary-green :background nil)
    (set-face-attribute 'org-block-end-line nil   :foreground primary-green :background nil))
  )
;;(set-face-attribute 'org-block nil :background "red")))
;;(set-face-attribute 'org-level-2 nil :height 1.3 :background "#60a83d")

Ellipsis

(setq org-hide-emphasis-markers nil)
(setq org-ellipsis "") ;; ↴, ▼, ▶, ⤵, ▾

Beautify Org Checkbox Symbol

(defun teddy-ma/org-buffer-setup ()
  ;;(push '("[ ]" . "☐" ) prettify-symbols-alist)
  ;;(push '("☑" . "☑" ) prettify-symbols-alist)
  ;;(push '("[-]" . "❍" ) prettify-symbols-alist)
  (prettify-symbols-mode))

;; (setq-default prettify-symbols-alist '(("#+BEGIN_SRC" . "»") ("#+END_SRC" . "«")("#+begin_src" . "»") ("#+end_src" . "«") ("lambda"  . "λ") ("->" . "→") ("->>" . "↠")))

Apply Custom UI

(use-package org
  :straight (:type built-in)
  :diminish org-indent-mode
  :hook
  (org-mode . teddy-ma/org-font-setup)
  (org-mode . teddy-ma/org-block-setup)
  (org-mode . teddy-ma/org-buffer-setup))

Given a file explorer

Go to start or end

M - < and M - >

Go to parent directory

press ^

Open file with readonly mode

press v

Batch change file names

in dired mode, embark-export and Edit, change file names and save for project search, use find-name-dired select root folder path and wdired-change-to-wdired-mode

Use tab to expand folder

https://geekinney.com/post/emacs-workflow-file-management/

(use-package dired-subtree
  :straight t
  :after dired
  :config
  (setq dired-subtree-use-backgrounds nil)
  :bind (:map dired-mode-map
              ("TAB" . dired-subtree-toggle)))

Given a git repo

Clone repo from clipboard

https://xenodium.com/emacs-clone-git-repo-from-clipboard

(defun ar/git-clone-clipboard-url ()
  "Clone git URL in clipboard asynchronously and open in dired when finished."
  (interactive)
  (cl-assert (string-match-p "^\\(http\\|https\\|ssh\\)://" (current-kill 0)) nil "No URL in clipboard")
  (let* ((url (current-kill 0))
         (download-dir (expand-file-name "~/Downloads/"))
         (project-dir (concat (file-name-as-directory download-dir)
                              (file-name-base url)))
         (default-directory download-dir)
         (command (format "git clone %s" url))
         (buffer (generate-new-buffer (format "*%s*" command)))
         (proc))
    (when (file-exists-p project-dir)
      (if (y-or-n-p (format "%s exists. delete?" (file-name-base url)))
          (delete-directory project-dir t)
        (user-error "Bailed")))
    (switch-to-buffer buffer)
    (setq proc (start-process-shell-command (nth 0 (split-string command)) buffer command))
    (with-current-buffer buffer
      (setq default-directory download-dir)
      (shell-command-save-pos-or-erase)
      (require 'shell)
      (shell-mode)
      (view-mode +1))
    (set-process-sentinel proc (lambda (process state)
                                 (let ((output (with-current-buffer (process-buffer process)
                                                 (buffer-string))))
                                   (kill-buffer (process-buffer process))
                                   (if (= (process-exit-status process) 0)
                                       (progn
                                         (message "finished: %s" command)
                                         (dired project-dir))
                                     (user-error (format "%s\n%s" command output))))))
    (set-process-filter proc #'comint-output-filter)))

Delete local and remote branch

Open the Magit status buffer (C-x g) Press y to get a listing of all branches and tags in the git repo Navigate to the branch you want to delete and press k

Blame

(use-package blamer
  :straight
  (:host github :repo "artawower/blamer.el" :branch "master")
  :custom
  (blamer-idle-time 0.3)
  (blamer-min-offset 70)
  :custom-face
  (blamer-face ((t :foreground "#7a88cf"
                   :background nil
                   :height 140
                   :italic t)))
  )

Get current file git server remote url

(use-package git-link
    :straight
    (:host github :repo "sshaw/git-link" :branch "master"))

Only show one file commits history

  • Open (magit-status) C-x g
  • press l
  • limit to files --
  • press l again

Given a writting job

Spell and syntax checker

need aspell to ispell (brew install aspell)

(setq ispell-program-name "aspell")

Proselint is a syntax checker for English language. This defines a custom checker which will run in texty modes. Proselint is an external program, install it with pip install proselint for this to work.

(flycheck-define-checker proselint
  "A linter for prose."
  :command ("proselint" source-inplace)
  :error-patterns
  ((warning line-start (file-name) ":" line ":" column ": "
            (id (one-or-more (not (any " "))))
            (message (one-or-more not-newline)
                     (zero-or-more "\n" (any " ") (one-or-more not-newline)))
            line-end))
  :modes (text-mode markdown-mode gfm-mode org-mode))

Chinese input method

(use-package pyim
  :straight t
  :demand t
  :config
  (use-package pyim-basedict
    :straight t
    :config (pyim-basedict-enable))

  (setq default-input-method "pyim")

  (setq-default pyim-english-input-switch-functions
                '(pyim-probe-dynamic-english
                  pyim-probe-isearch-mode
                  pyim-probe-program-mode
                  pyim-probe-org-structure-template))

  (setq-default pyim-punctuation-half-width-functions
                '(pyim-probe-punctuation-line-beginning
                  pyim-probe-punctuation-after-punctuation))

  ;;(pyim-isearch-mode 1)
  (setq pyim-page-tooltip 'posframe)
  (setq pyim-page-length 5)

  (setq pyim-dicts
        '((:name "tsinghua"
                 :file "~/Dropbox/Config/pyim-tsinghua-dict.pyim")))

  :bind
  (("M-j" . pyim-convert-string-at-point)))

English Chinese Dictionary

(use-package youdao-dictionary
  :straight t
  :config
  (setq url-automatic-caching t))

Extras

Run shell in emacs

eshell with git https://github.com/elken/eshell-p10k optional theme

(use-package eshell-git-prompt
  :straight t)

(use-package eshell
  :straight t
  :config
  (eshell-git-prompt-use-theme 'powerline)
  (add-hook 'eshell-mode-hook (lambda ()
                                (setq-local global-hl-line-mode
                                            nil))))

vterm (need compile)

;; (when(not (eq system-type 'windows-nt))
;;   (use-package vterm
;;     :straight t
;;     :config
;;     (setq vterm-shell (executable-find "fish")
;;           vterm-max-scrollback 10000))

;;   (use-package vterm-toggle
;;     :straight t)

;;   (use-package shell-pop
;;     :straight t
;;     :custom
;;     (shell-pop-shell-type '("vterm" "*vterm*" (lambda () (vterm))))
;;     (shell-pop-full-span t))
;;   )

Hikarian

(defun hikarian-hello()
  (interactive)
  (message "hello"))

(defun hikarian-visit-last-migration ()
  "Open the last file in 'db/migrate/'."
  (interactive)
  (dired (expand-file-name "db/migrate" (projectile-project-root)))
  (end-of-buffer)
  (previous-line)
  (dired-find-file)
  (kill-buffer "migrate"))

;;(setq rails-routes-list (""))

(defun hikarian-insert-route ()
  "List all the routes and let user select one to insert"
  ;; (let* ((selected-value (split-string (completing-read "Route: " (rails-routes--get-routes-cached)) " +"))
  ;;        (selected-route (nth (if (eq (length selected-value) 5) 3 2) selected-value)))
  ;;   (when (not (rails-routes--guess-ignore-class)) (insert rails-routes-class-name))
  ;;   (rails-routes--insert-value selected-value)
  ;;   (when (or (string-match-p ":id" selected-route)
  ;;             (string-match-p ":[a-zA-Z0-9]+_id" selected-route))
  ;;     (progn (insert "()") (backward-char)))))
  ()
  )

(defun hikarian-get-routes-from-command ()
  "Run rails-routes-search-command and return it."
  (interactive)
  (message "Fetching routes.  Please wait.")
  (let ((command-result ((split-string (shell-command-to-string "rails routes") "\n"))))
    (message "rails routes no need to run in project root, the result is: %s" command-result)
    command-result))

(defun hikarian-list-resource-files(resource_name)
  "list relation resource"
  ())

(defun hikarian--auto-insert-init-template ()
  "init code template when file create."
  (interactive)
  ;; make sure auto inser mode active
  (auto-insert-mode t)
  (let* ((file-re (format "^%s.*\\.rb$" (projectile-project-root)))
         (current-project-cond `(,file-re . "hikarian")))
    (message "file re is %s" file-re)
    (define-auto-insert
      current-project-cond
      [
       (lambda ()
         (let ((snippet (hikarian-corresponding-snippet)))
           (when snippet
             (insert snippet))))
       hikarian-expand-yas-buffer ;; after buffer with template, then expand it
       ]
      )))

;; (string-match "app/models/\\(.+\\)\\.rb$" name)
;;                  (projectile-rails--snippet-for-model (match-string 1 name))

(defun hikarian-corresponding-snippet ()
  ;;"module %s\n  extend ActiveSupport::Concern\n  $0\nend")
  "cls")

(defun hikarian-expand-yas-buffer ()
  "Called right after buffer is populate with snippet by Auto-insert mode."
  (interactive)
  (yas-lookup-snippet (buffer-string) (point-min) (point-max) '((yas-indent-line 'nothing))))

(defun autoinsert-yas-expand()
  "Replace text in yasnippet template."
  (interactive)
  (yas-expand-snippet (buffer-string) (point-min) (point-max)))

(defun test-autoinsert()
  (interactive)
  (yas-expand-snippet (yas-lookup-snippet "class ... end")))


;;(add-hook 'projectile-rails-mode-hook #'projectile-rails-setup-auto-insert-maybe)
(provide 'hikarian)
;;; hikarian.el ends here