Emacs notes

Helper functions

Git quick commit for magit

The following function stages all unstaged changes and commits them with a commit message that only contains a list of the modified files.

Resulting commit message will look like:

changed: foo.txt, bar.txt

I use this function when updating this blog quite often, because I do a lot of small commits and do not need a hand written message for commits.

(defun my/git-quick-commit ()
  "Create a commit with list of changed files as the message."
  (interactive)
  (let ((changed
         (mapconcat
          #'(lambda (x)
              ; Extract file part of the path
              (substring x (string-match-p "\\([^\/]*\\)$" x)))
          (magit-git-lines "diff" "--name-only") ", ")))
    (if (string= "" changed)
        (user-error "No unstaged changes"))
    (magit-stage-modified)
    (magit-call-git "commit" "-m" (concat "changed: " changed))
    (magit-refresh)
    (message "Changes committed.")))

To bind it globally, use following (I'm using M-g m q)

(global-set-key (kbd "M-g m q") 'my/git-quick-commit)

Vterm

This function makes it possible to use counsel to quickly navigate directories in the shell running vterm buffer. I prefer this to using e.g. zsh's directory autocompletion. Requires ivy and counsel to be installed.

(defun my/vterm-cd (&optional initial-input initial-directory)
  (interactive)
  (let ((default-directory (or initial-directory default-directory))
        (counsel--find-file-predicate #'file-directory-p))
    (counsel--find-file-1 "Find directory: " initial-input
                          #'my/vterm-cd-action
                          'my/vterm-cd)))

(defun my/vterm-cd-action (dir)
  (vterm-send-string (format " cd %s" dir) t)
  (vterm-send-return))

To bind it to a key, use following (I'm using C-c .)

(define-key vterm-mode-map (kbd "C-c .") 'my/vterm-cd)

Camelize string

Converts a string such as "foo bar baz" to fooBarBaz.

(defun camelize-string (str)
  (let ((words (split-string str "[-_ ]+")))
    (concat (downcase (car words))
            (mapconcat 'capitalize (cdr words) ""))))

Does the conversion above and puts it in the kill ring.

(defun camelize-region (start end)
  "Convert the region to camelCase and copy it to the kill ring."
  (interactive "r")
  (let ((value (buffer-substring start end)))
    (kill-new (camelize-string value)))
  (deactivate-mark t))

Peek in other window

Opens the current buffer in a new window in read only mode, to allow glancing other parts of the buffer without losing track of current location. 'q' quits the window. I sometimes use this to look at parts of a long function or look up function headers.

(defun my/clone-buffer-in-new-window-readonly ()
  "Clone the current buffer in a new window, make it readonly, and set up a 
keybinding to close the window."
  (interactive)
  (let ((clone-buffer (clone-indirect-buffer (buffer-name) t)))
    (with-current-buffer clone-buffer
      (read-only-mode t)
      (let ((map (make-sparse-keymap)))
        (define-key map (kbd "q") (lambda ()
                                    (interactive)
                                    (kill-buffer-and-window)))
        (use-local-map map)))
    (switch-to-buffer-other-window clone-buffer)))

To bind it globally, use following (I'm using C-x 9)

(global-set-key (kbd "C-x 9") 'my/clone-buffer-in-new-window-readonly)

dired

Dired is pretty plain in its default configuration.

Coloring by file type by using dired-rainbow:

(use-package dired-rainbow
  :config
  (progn
    (dired-rainbow-define-chmod directory "#6cb2eb" "d.*")
    (dired-rainbow-define html "#eb5286" ("css" "less" "sass" "scss" "htm" "html" "jhtm" "mht" "eml" "mustache" "xhtml"))
    (dired-rainbow-define xml "#f2d024" ("xml" "xsd" "xsl" "xslt" "wsdl" "bib" "json" "msg" "pgn" "rss" "yaml" "yml" "rdata"))
    (dired-rainbow-define document "#9561e2" ("docm" "doc" "docx" "odb" "odt" "pdb" "pdf" "ps" "rtf" "djvu" "epub" "odp" "ppt" "pptx"))
    (dired-rainbow-define markdown "#ffed4a" ("org" "etx" "info" "markdown" "md" "mkd" "nfo" "pod" "rst" "tex" "textfile" "txt"))
    (dired-rainbow-define database "#6574cd" ("xlsx" "xls" "csv" "accdb" "db" "mdb" "sqlite" "nc"))
    (dired-rainbow-define media "#de751f" ("mp3" "mp4" "MP3" "MP4" "avi" "mpeg" "mpg" "flv" "ogg" "mov" "mid" "midi" "wav" "aiff" "flac"))
    (dired-rainbow-define image "#f66d9b" ("tiff" "tif" "cdr" "gif" "ico" "jpeg" "jpg" "png" "psd" "eps" "svg"))
    (dired-rainbow-define log "#c17d11" ("log"))
    (dired-rainbow-define shell "#f6993f" ("awk" "bash" "bat" "sed" "sh" "zsh" "vim"))
    (dired-rainbow-define interpreted "#38c172" ("py" "ipynb" "rb" "pl" "t" "msql" "mysql" "pgsql" "sql" "r" "clj" "cljs" "scala" "js"))
    (dired-rainbow-define compiled "#4dc0b5" ("asm" "cl" "lisp" "el" "c" "h" "c++" "h++" "hpp" "hxx" "m" "cc" "cs" "cp" "cpp" "go" "f" "for" "ftn" "f90" "f95" "f03" "f08" "s" "rs" "hi" "hs" "pyc" ".java"))
    (dired-rainbow-define executable "#8cc4ff" ("exe" "msi"))
    (dired-rainbow-define compressed "#51d88a" ("7z" "zip" "bz2" "tgz" "txz" "gz" "xz" "z" "Z" "jar" "war" "ear" "rar" "sar" "xpi" "apk" "xz" "tar"))
    (dired-rainbow-define packaged "#faad63" ("deb" "rpm" "apk" "jad" "jar" "cab" "pak" "pk3" "vdf" "vpk" "bsp"))
    (dired-rainbow-define encrypted "#ffed4a" ("gpg" "pgp" "asc" "bfe" "enc" "signature" "sig" "p12" "pem"))
    (dired-rainbow-define fonts "#6cb2eb" ("afm" "fon" "fnt" "pfb" "pfm" "ttf" "otf"))
    (dired-rainbow-define partition "#e3342f" ("dmg" "iso" "bin" "nrg" "qcow" "toast" "vcd" "vmdk" "bak"))
    (dired-rainbow-define vc "#0074d9" ("git" "gitignore" "gitattributes" "gitmodules"))
    (dired-rainbow-define-chmod executable-unix "#38c172" "-.*x.*")))