Skip to content

Latest commit

 

History

History
4184 lines (3478 loc) · 142 KB

config.org

File metadata and controls

4184 lines (3478 loc) · 142 KB

Yet Another Emacs Configuration

magit:~/matrix/tools/.emacs.d

https://github.com/yitang/.emacs.d

(setq yt-sphinx/proj-dir "~/matrix/tools/.emacs.d")
(setq yt-sphinx/docs-dir "~/matrix/tools/.emacs.d/docs")
(setq yt-sphinx/proj-badge-md (list "[![Documentation Status](https://readthedocs.org/projects/emacs/badge/?version=latest)](https://readthedocs.org/projects/emacs/?badge=latest)"))
(yt-sphinx/update-documentation)

Emacs Configuration

[2015-01-19 Mon 11:42]

General

Utilities

[2015-01-19 Mon 12:14]

Firstly, define a function for reloading Emacs configuration, need this function in debugging this configuration file.

(defun yt/reload-dot-emacs ()
  "Save the .emacs buffer if needed, then reload .emacs."
  (interactive)
  (let ((dot-emacs "~/.emacs"))
    (and (get-file-buffer dot-emacs)
         (save-buffer (get-file-buffer dot-emacs)))
    (load-file dot-emacs))
  (message "Re-initialized!"))
(setq confirm-kill-emacs 'y-or-n-p)

Life is too short to type “yes” or “no”. ‘y’ or ‘n’ is enough.

(fset 'yes-or-no-p 'y-or-n-p)

Remove Keybind

;; (global-unset-key (kbd "C-x b"))
;; (global-unset-key (kbd "C-x C-b"))
(global-set-key (kbd "C-x C-b") 'ibuffer)
(global-unset-key (kbd "C-x C-c"))  ;; save-buffers-kill-terminal
(global-unset-key (kbd "C-x o"))  ;; other window. replace by f2 - ace-window.

Assorted Pieces

[2015-01-19 Mon 12:21]

Automatically backup buffers/files into the working directory and the ~.emacs.d/backup// directory.

;; ref: http://stackoverflow.com/questions/151945/how-do-i-control-how-emacs-makes-backup-files
;; save all backup files (foo~) to this directory.
(setq make-backup-files nil) ; stop creating ~ files
(setq backup-directory-alist '(("." . "~/.emacs.d/backup"))
      backup-by-copying t    ; Don't delink hardlinks
      version-control t      ; Use version numbers on backups
      delete-old-versions t  ; Automatically delete excess backups
      kept-new-versions 20   ; how many of the newest versions to keep
      kept-old-versions 5    ; and how many of the old
      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)
      )

;; guide-key package 
;; (require 'guide-key)
;; (setq guide-key/guide-key-sequence t) ;; on for all key-bindings 
;; (guide-key-mode 1) 

;; use company for all except few modes
(use-package company
  :ensure t)
(add-hook 'after-init-hook 'global-company-mode)
;; Don't enable company-mode in below major modes, OPTIONAL
(setq company-global-modes '(not eshell-mode comint-mode erc-mode rcirc-mode))

;; config company mode
(setq company-selection-wrap-around t
      company-tooltip-align-annotations t
      company-idle-delay 0.36
      company-minimum-prefix-length 2
      company-tooltip-limit 10)

(setq company-ddabbrev-code-everywhere t)
(setq company-dabbrev-code-modes t)
(setq company-dabbrev-code-other-buffers 'all)
(setq company-dabbrev-ignore-buffers "\\`\\'")
(setq company-dabbrev-char-regexp "\\(\\sw\\|\\s_\\|_\\|-\\)")

;; config company for ESS mode
(defun yt/ess_company_mode_setup ()
  ;; this is really important. to source vairbales defined in the scripts.
     (make-local-variable 'company-backends)
     (add-to-list 'company-backends 'company-dabbrev-code)
     )
(add-hook 'ess-mode-hook 'yt/ess_company_mode_setup)


(defun text-mode-hook-setup ()
  (make-local-variable 'company-backends)
  (add-to-list 'company-backends 'company-ispell)
  ;; (setq company-ispell-dictionary (file-truename "~/matrix/tools/.emacs.d/english_words.txt"))
  )

(add-hook 'text-mode-hook 'text-mode-hook-setup)
(use-package company-quickhelp)
(company-quickhelp-mode 1)
(define-key company-active-map (kbd "M-h") #'company-quickhelp-manual-begin)
(define-key company-active-map (kbd "M-h") 'company-show-doc-buffer)

(setq company-dabbrev-downcase nil)
(setq company-show-numbers t)

Configure recent opened files.

(recentf-mode 1)
(setq recentf-max-saved-items 200
      recentf-max-menu-items 15)

Shows an notication for invalid operations.

(setq visible-bell nil) 
(setq ring-bell-function 'ignore)

Disable startup message

(setq inhibit-startup-message t)        

yasnippet is a powerful package that I’d like to explore in the future, and this stage, I turned if off since it will slow down the start-up.

(use-package yasnippet
  :ensure t)
(yas/global-mode 1)
(add-to-list 'yas/snippet-dirs "~/matrix/tools/.emacs.d/snippets" t)
(yas/reload-all)

Window Layout/Navigation

[2015-01-19 Mon 12:13]

I switched from using ace-window to using the build-in package movewind. It uses S+arrow keys to switch to the window adjacent to the current window.

It was disabled in the org-mode calendar model due to key conflicts. This is the only conflicts I’m aware of in this config.

(windmove-default-keybindings)
(define-key org-read-date-minibuffer-local-map (kbd "<left>") (lambda () (interactive) (org-eval-in-calendar '(calendar-backward-day 1))))
(define-key org-read-date-minibuffer-local-map (kbd "<right>") (lambda () (interactive) (org-eval-in-calendar '(calendar-forward-day 1))))
(define-key org-read-date-minibuffer-local-map (kbd "<up>") (lambda () (interactive) (org-eval-in-calendar '(calendar-backward-week 1))))
(define-key org-read-date-minibuffer-local-map (kbd "<down>") (lambda () (interactive) (org-eval-in-calendar '(calendar-forward-week 1))))

Instead of equally split the window size, it make a lot sense to have the current window, the one I am working one, has bigger size.

;; (require 'golden-ratio)
;; (golden-ratio-mode 1)
;; (add-to-list 'golden-ratio-extra-commands 'ace-window) ;; active golden ratio when using ace-window

Some actions will add/remove windows, and sometimes I’d like to cycle tough the window layout/changes. In the following settings, C-c <left> to undo window layout changes, and C-c <right> to redo.

(winner-mode 1)
;; winner-undo -> C-c <left>
;; winner-redo -> C-c <right>

I’d like to use two frames, one for doing and logging, and other for reference/searching.

(defun yt/ref-frame ()
  (interactive)
  ;;   (frame-parameter (car (frame-list)) 'name)
  (if (eq 1 (length (frame-list)))
      (new-frame '((name . "***********************REFERENCE*******************")))
    nil))
(global-set-key (kbd "M-`") 'other-frame)

System Path/Keyboard

[2015-01-19 Mon 12:15]

Solve the PATH issues for the software installed via Homebrew in OS X. Uncomment the setenv for CYGWIN since I am not using Windows any more.

(defun set-exec-path-from-shell-PATH ()
  (let ((path-from-shell 
         (replace-regexp-in-string "[[:space:]\n]*$" "" 
                                   (shell-command-to-string "$SHELL -l -c 'echo $PATH'"))))
    (setenv "PATH" path-from-shell)
    (setq exec-path (split-string path-from-shell path-separator))))
(when (equal system-type 'darwin) (set-exec-path-from-shell-PATH))
;; windows path convention
;; (setenv "CYGWIN" "nodosfilewarning")

Modify the Mac keyboard: unset the C-z just in case I run Emacs in terminal and C-z won’t stop the program without asking.

;; modify mac keyboard 
(cond ((eq system-type 'darwin)
       (setq mac-command-modifier 'meta)
       (fset 'insertPound "#")
       (global-set-key (kbd "M-3") 'insertPound)       
       (global-unset-key (kbd "M-`"))
       (global-set-key (kbd "M-`") 'other-frame)
       (global-set-key (kbd "C-Z") nil)
       ))

(prefer-coding-system 'utf-8)
(when (display-graphic-p)
  (setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)))

Open PDF files using external program.

;; (require 'openwith)
;; (openwith-mode t)
;; (if (string= system-type "darwin")
;;     (setq openwith-associations '(("\\.pdf\\'" "Skim" (file))))
;;   (setq openwith-associations '(("\\.pdf\\'" "evince" (file)))))

General Editing

There are a set of characters that are more likely to occur as a pair, for example, quote and brackets. smartparens mode allows me to define such set of pairing characters.

(use-package smartparens)
(smartparens-global-mode 1)
(sp-pair "(" ")" :wrap "C-(")
;; |foobar
;; hit C-(
;; becomes (|foobar)
(sp-pair "'" nil :actions :rem)

Modern display is widen. Like many of the Emacs users, I prefer to have the text wrapper inside a small region rather than have a stretch across the whole screen. It’s easier to read in this way.

A well accepted rule is to set the width of lines to 80 characters, and force a logical line breaks. This funcitonality is called auto-fill in Emacs, and I can do the filling by call fill-paragraph.

(add-hook 'text-mode-hook 'turn-on-auto-fill) ;; 

Just in case I need to reverse the auto-fill process.

[2016-06-20 Mon 21:47] Can’t remember when was the last time I use unfill. This snippet is not long used.

(defun yt/unfill-paragraph ()
  (interactive)
  (let ((fill-column (point-max)))
    (fill-paragraph nil)))
(defun yt/unfill-region ()
  (interactive)
  (let ((fill-column (point-max)))
    (fill-region (region-beginning) (region-end) nil)))

Minibuffer history

Let Emacs remember what I’ve typed, so I don’t need to tediously type the whole thing.

(setq savehist-file "~/matrix/tools/.emacs.d/local/emacs-history")
(savehist-mode 1)

highlight TODO, IMP in text mode

[2019-10-13 Sun 04:46]

;; ;; highlights FIXME: TODO: and BUG: in prog-mode 
;; (add-hook 'text-mode-hook
;;           (lambda ()
;;             (font-lock-add-keywords nil
;;                                     '(("\\<\\(YT\\|TODO\\|IMP\\):" 1 font-lock-warning-face t)))))

move line/region up/down

[2019-11-09 Sat 11:52]

(defun move-text-internal (arg)
   (cond
    ((and mark-active transient-mark-mode)
     (if (> (point) (mark))
            (exchange-point-and-mark))
     (let ((column (current-column))
              (text (delete-and-extract-region (point) (mark))))
	(forward-line arg)
	(move-to-column column t)
	(set-mark (point))
	(insert text)
	(exchange-point-and-mark)
	(setq deactivate-mark nil)))
    (t
     (beginning-of-line)
     (when (or (> arg 0) (not (bobp)))
	(forward-line)
	(when (or (< arg 0) (not (eobp)))
            (transpose-lines arg))
	(forward-line -1)))))

(defun move-text-down (arg)
   "Move region (transient-mark-mode active) or current line
  arg lines down."
   (interactive "*p")
   (move-text-internal arg))

(defun move-text-up (arg)
   "Move region (transient-mark-mode active) or current line
  arg lines up."
   (interactive "*p")
   (move-text-internal (- arg)))

(global-set-key [\M-\S-up] 'move-text-up)
(global-set-key [\M-\S-down] 'move-text-down)

GUI - Emacs Looks Cool

[2015-01-19 Mon 12:16]

Fonts

[2015-07-20 Mon 11:46]

I use the Adobe’s Source Code Pro font, it is Monospaced font and claimed to be suitable for coding environments but I use it for all modes.

(add-to-list 'default-frame-alist '(font . "Source Code Pro-16"))

Minimalists GUI

[2015-07-20 Mon 11:46]

I never click any buttons in the tool-bar, nor need the scroll-bar to tell me the cursor position the in the buffer, so I removed all of them to have minimalist GUI of Emacs.

Recently I found menu-bar is really useful, it shows commonly used functions for a particular mode. Occasionally I found something useful.

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

Theme

[2015-07-20 Mon 11:46]

I started using modus-operandi as the default theme.

(load-theme 'modus-operandi)

Mode Line

[2015-07-20 Mon 11:46]

The mode line is at the bottom of every Emacs Window aside from MiniBuffer windows. It has most of the relevant information about the buffer, including Git status, Major mode, clock info, etc.

The smart-mode-line packages can make mode-line “smart and sexy”. There are many options to tweak.

(setq sml/no-confirm-load-theme t)
(use-package smart-mode-line
  :ensure t)
(setq powerline-arrow-shape 'curve)
(setq powerline-default-separator-dir '(right . left))
(setq sml/theme 'respectful)
(sml/setup)

There are too much information cluttered at the bottom. I disable the display of minor modes, there are just too many and almost all are irrelevant.

(rich-minority-mode 1)
(setf rm-blacklist "")

This will leave empty spaces which can be removed by

(setq sml/mode-width 'full)
(setq sml/name-width 40)

Finally, show the current time in the mode-line.

(setq display-time-format "W%W %H:%M")
(display-time-mode)
;; (display-time)                 

running Emacs in terminal

[2019-04-01 Mon 21:43]

later i found that Emacs runs much faster in Terminal. for the reason i don’t know but I really enjoy the lightning speed. here’s a selection of confugratino for working Emacs in terminal.

(add-hook 'suspend-hook
	     (lambda () (or (y-or-n-p "Really suspend? ")
			    (error "Suspend canceled"))))
(add-hook 'suspend-resume-hook (lambda () (message "Resumed!")
				    (sit-for 2)))

then fg to bring back emacs.

Completion and Selection

[2015-01-23 Fri 18:44]

Multi-Cursor & Helm-swoop - Multiple Selection

[2015-01-19 Mon 12:10]

When refactoring code, I need to rename a variable or function names, the normal way to do that is via searching and replacing. multiple-cursors provides function to select all the words/symbols that is highlighted and then modify all of them at the same time.

(use-package multiple-cursors)
(global-set-key (kbd "C->") 'mc/mark-next-like-this)
(global-set-key (kbd "C-<") 'mc/mark-previous-like-this)

ace-jump

Instead of moving into the place I want, ace-jump provides a way to jump directly to there places, just by pressing 4-5 keys. The places can be a character, line, or word. Personally I found it is really efficient to jump to a word when editing.

(global-set-key (kbd "C-c w") 'ace-jump-word-mode)

Expand-Region - Incremental Selection

[2015-01-20 Tue 07:47]

expand-region provides smart way of sectioning, by expanding the scope one at a time. for example,

S = "A B C"

If the cursor in inside of the quote, I press C-=, everything inside of the quote is selected, press it again, the quotes are also selected, press it again, the whole line/region is selected. It saves a lot of keystrokes in highlighting the area.

It works well with smartparens mode, if I want to apply markup syntax around a word, I press C-= to select it, then insert quote or forward slash, the whole word will be warped inside of quote or forward flash.

(use-package expand-region)
(global-set-key (kbd "C-=") 'er/expand-region)

File Management

[2015-01-23 Fri 18:52]

Alternative to shell

[2015-01-28 Wed 07:46]

For the file management tasks like rename and delete, I’d like to wrapper it as a Lisp function and call it directly in Emacs.

Rename the buffer-visiting file, and also rename the buffer. Similar to the save as idea but will remove the older file.

;; rename current buffer-visiting file
(defun yt/rename-current-buffer-file ()
  "Renames current buffer and file it is visiting."
  (interactive)
  (let ((name (buffer-name))
        (filename (buffer-file-name)))
    (if (not (and filename (file-exists-p filename)))
        (error "Buffer '%s' is not visiting a file!" name)
      (let ((new-name (read-file-name "New name: " filename)))
        (if (get-buffer new-name)
            (error "A buffer named '%s' already exists!" new-name)
          (rename-file filename new-name 1)
          (rename-buffer new-name)
          (set-visited-file-name new-name)
          (set-buffer-modified-p nil)
          (message "File '%s' successfully renamed to '%s'"
                   name (file-name-nondirectory new-name)))))))

Another useful Lisp function is to copy the file path to clipboard for cross reference.

;; full path of current buffer
(defun yt/copy-full-path-to-kill-ring ()
  "copy buffer's full path to kill ring"
  (interactive)
  (when buffer-file-name
    (let* ((file-truename buffer-file-name))
      ;;(rel-name (file-relative-name file-truename "~/")))  ; BUG: if filename is not relative to home directory.
      ;; (kill-new (concat "~/" rel-name)))))
      (kill-new file-truename))))
(defun yt/copy-rel-path-to-kill-ring ()
  "copy buffer's relative path to project directory to kill ring"
  (interactive)
  (when (and buffer-file-name (magit-toplevel))
    (let* ((file-truename buffer-file-name))
      (kill-new (file-relative-name file-truename (magit-top-level))))))

Open a file as a root user in Emacs, very handy.

(defun yt/sudo-find-file (file-name)
  "Like find file, but opens the file as root."
  (interactive "FSudo Find File: ")
  (let ((tramp-file-name (concat "/sudo::" (expand-file-name file-name))))
    (find-file tramp-file-name))) 

Find out the last modified date for current buffer, I need this often when updating a blog post or documents.

(defun yt/last-updated-date ()
  "return modification time of current file-visitng buffer"
  (interactive)
  (let* ((mtime (visited-file-modtime))) 
    (unless (integerp mtime)
      (concat "/Last UPdated/: "
              (format-time-string "%d %b %Y" mtime)))))

Remove current buffer-visiting file, and kill the buffer. I use this function often in testing and trying out.

(defun yt/delete-this-buffer-and-file ()
  "Removes file connected to current buffer and kills buffer."
  (interactive)
  (let ((filename (buffer-file-name))
        (buffer (current-buffer))
        (name (buffer-name)))
    (if (not (and filename (file-exists-p filename)))
        (error "Buffer '%s' is not visiting a file!" name)
      (when (yes-or-no-p "Are you sure you want to remove this file? ")
        (delete-file filename)
        (kill-buffer buffer)
        (message "File '%s' successfully removed" filename)))))

Open the file manager at the default directory.

;; http://ergoemacs.org/emacs/emacs_dired_open_file_in_ext_apps.html
(defun yt/open-file-manager ()
  "Show current file in desktop (OS's file manager)."
  (interactive)
  (cond
   ((string-equal system-type "windows-nt")
    (w32-shell-execute "explore" (replace-regexp-in-string "/" "\\" default-directory t t)))
   ((string-equal system-type "darwin") (shell-command "open ."))
   ((string-equal system-type "gnu/linux")
    (let ((process-connection-type nil)) (start-process "" nil "xdg-open" "."))
    ;; (shell-command "xdg-open .") ;; 2013-02-10 this sometimes froze emacs till the folder is closed. ⁖ with nautilus
    )))

;; sort files in dired mode by datetime
(setq dired-listing-switches "-lsh")
(setq dired-recursive-copies 'always)
(setq dired-dwim-target t)

(add-hook 'dired-mode-hook (lambda () (dired-hide-details-mode)))

Projectile - Directory Access

[2015-01-19 Mon 12:08]

Projectile is an powerful Emacs package but I only use projectile to jump between different git folders.

(use-package projectile)
(projectile-mode +1)
(define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)

;; as of [2023-01-17 Tue 19:35], there's issue with
;; projectile-switch-project function. it cannot detect the right
;; project directory. so i have to use consult-projectile for this.
(use-package consult-projectile)
(setq consult-projectile-source-projectile-project-action #'(lambda (dir) (dired dir)))
(define-key projectile-command-map (kbd "p") 'consult-projectile-switch-project)

There are many things work out of box. For example, use C-p p to choose which project to jump to, but I can type M-g to invoke Magit or M-e to invoke Eshell for that project.

Remote (SSH)

[2015-01-22 Thu 23:11]

I can work on the remote files in Emacs via ssh or tramp, both are build-in packages.

(use-package tramp)
(use-package ssh)
(add-to-list 'tramp-remote-path 'tramp-own-remote-path)

(defun yt/tramp-abort ()
  ;; kill all tramp connections.
  (interactive)
  (recentf-cleanup)
  (tramp-cleanup-all-buffers)
  (tramp-cleanup-all-connections))

I’d like catch the password so that I don’t need to type it every time to open a file.

(setq password-cache-expiry nil)

I mainly run R on a remote machine. Sometimes I want to copy the charts I created to local to include them in my report. This workfow is suspended because it fails when the file size is large.

;; (defun yt/sync-local-remote ()
;;   (interactive)
;;   "copy all files in remote:~/LR_share to local:~/LR_share,
;; does not support the ther way"
;;   (find-file "/ssh:remote_host:/remote_directory")
;;   ;; (mark-whole-buffer)
;;   (dired-mark-subdir-files)
;;   ;; (find-file "~/LR_share")
;;   ;; (setq-local dirqed-dwim-target t)
;;   (dired-do-copy))

Testing Buffers

[2015-07-20 Mon 11:39]

scratch buffer is usually used for testing Emacs lisp functions. I also need temporary buffers for testing R code and org-mode. In the following settings, I can use F9-f to select temporal buffers.

(defvar yt/temp-dir "~/.tmp"
  "temporay folders")

(defun yt/open-tmp-R ()
  (interactive)
  (find-file (expand-file-name "tmp.R" yt/temp-dir)))
(defun yt/open-tmp-el ()
  (interactive)
  (find-file (expand-file-name "tmp.el" yt/temp-dir)))
(defun yt/open-tmp-org ()
  (interactive)
  (find-file (expand-file-name "tmp.org" yt/temp-dir)))
(global-set-key (kbd "<f9> f r") 'yt/open-tmp-R)
(global-set-key (kbd "<f9> f e") 'yt/open-tmp-el)
(global-set-key (kbd "<f9> f o") 'yt/open-tmp-org)

ESS - Emacs Speaks Statistics

As Statistician, coding in R and writing report is what I do most of the day. I have been though a long way of searching the perfect editor for me, tried Rstudio, SublimeText, TextMate and settled down happily with ESS/Emacs, for both coding and writing.

There three features that have me made the decision:

  1. Auto Formatting

    Scientists has reputation of being bad programmers, who wrote the code that is unreadable and therefore incomprehensible to others. I have intention to become top level programmer and followed a style guide strictly. It means I have to spent sometime in adding and removing space in the code.

    To my surprise, Emacs will do it for me automatically, just by hitting the TAB and it also indent smartly, which make me conformable to write long function call and split it into multiple lines. Here’s an example. Also if I miss placed a ‘)’ or ‘]’ the formatting will become strange and it reminders me to check.

    rainfall.subset <- data.table(rainfall.london,
                                 rainfall.pairs,
                                 rainfall.dublin)
        
  2. Search Command History

    I frequently search the command history. Imaging I was produce a plot and I realised there was something miss in the data, so I go back and fix the data first, then run the ggplot command again, I press Up/Down bottom many times, or just search once/two times. M-x ggplot( will gives me the most recent command I typed containing the keyword ggplot(, then I press RET to select the command, which might be ggplot(gg.df, aes(lon, lat, col = city)) + geom_line() + ...... If it is not I want, I press C-r again to choose the second most recent one and repeat until I find right one.

  3. Literate Programming

    I am an supporter of literate statistical analysis and believe we should put code, results and discoveries together in developing models. Rstudio provides an easy to use tool for this purpose, but it does not support different R sessions, so if I need to generate a report, I have to re-run all the code from beginning, which isn’t particle for me with volumes data because it will take quit long.

    ESS and org-mode works really well via Babel, which is more friendly to use. I can choose to run only part of the code and have the output being inserted automatically, no need to copy/paste. Also, I can choose where to execute the code, on my local machine or the remote server, or both at the same time.

    These are only the surface of ESS and there are lot more useful features like spell checking for comments and documentation templates, that makes me productive and I would recommend anyone use R to learn ESS/Emacs. The following is my current setting.

;; Adapted with one minor change from Felipe Salazar at
;; http://www.emacswiki.org/emacs/EmacsSpeaksStatistics
(use-package ess
  :ensure t
  :init (require 'ess-site))
(setq ess-ask-for-ess-directory nil) ;; start R on default folder
(setq ess-local-process-name "R")
(setq ansi-color-for-comint-mode 'filter) ;;
;; (setq comint-scroll-to-bottom-on-input t)
;; (setq comint-scroll-to-bottom-on-output nil)
;; (setq comint-move-point-for-output nil)
(setq ess-eval-visibly-p 'nowait) ;; no waiting while ess evalating
(defun my-ess-start-R ()
  (interactive)
  (if (not (member "*R*" (mapcar (function buffer-name) (buffer-list))))
      (progn
        (delete-other-windows)
        (setq w1 (selected-window))
        (setq w1name (buffer-name))
        (setq w2 (split-window w1))
        (R)
        (set-window-buffer w2 "*R*")
        (set-window-buffer w1 w1name))))
(defun my-ess-eval ()
  (interactive)
  (my-ess-start-R)
  (if (and transient-mark-mode mark-active)
      (call-interactively 'ess-eval-region)
    (call-interactively 'ess-eval-line-and-step)))
(add-hook 'ess-mode-hook
          '(lambda()
             (local-set-key [(shift return)] 'my-ess-eval)))
(add-hook 'ess-mode-hook
          (lambda ()
            (flyspell-prog-mode)
            (run-hooks 'prog-mode-hook)
            ))
(add-hook 'ess-R-post-run-hook (lambda () (smartparens-mode 1)))

;; REF: http://stackoverflow.com/questions/2901198/useful-keyboard-shortcuts-and-tips-for-ess-r
;; Control and up/down arrow keys to search history with matching what you've already typed:
(define-key comint-mode-map [C-up] 'comint-previous-matching-input-from-input)
(define-key comint-mode-map [C-down] 'comint-next-matching-input-from-input)
(setq ess-history-file "~/.Rhisotry")
(setq ess-indent-with-fancy-comments nil)


(define-key ess-r-mode-map "_" #'ess-insert-assign)
(define-key inferior-ess-r-mode-map "_" #'ess-insert-assign)

Syntax highlight

In Emacs, syntax highlighting is known as font-locking. You can customize the amount of syntax highlighting that you want to see. At the top of the Emacs window, click on the ESS menu and select “Font Lock”. This will display a menu of buttons corresponding to language elements that you can syntax highlight.

(setq ess-R-font-lock-keywords
    '((ess-R-fl-keyword:modifiers . t)
     (ess-R-fl-keyword:fun-defs . t)
     (ess-R-fl-keyword:keywords . t)
     (ess-R-fl-keyword:assign-ops)
     (ess-R-fl-keyword:constants . t)
     (ess-fl-keyword:fun-calls . t)
     (ess-fl-keyword:numbers)
     (ess-fl-keyword:operators)
     (ess-fl-keyword:delimiters)
     (ess-fl-keyword:=)
     (ess-R-fl-keyword:F&T)
     (ess-R-fl-keyword:%op%)))

use pretty mode

;; (add-hook 'ess-mode-hook 'turn-on-pretty-mode)

Documentation

[2015-01-23 Fri 17:53]

;; edit roxy template
;; ess-roxy-update-entry
(setq ess-roxy-template-alist '(("description" . " content for description")
                                ("details" . "content for details")
                                ("title" . "")
                                ("param" . "")
                                ("return" . "")
                                ("export" . "")
                                ("author" . "Yi Tang")))

R Style Check - Flycheck

[2015-01-20 Tue 10:49]

https://github.com/jimhester/lintr the default R-style is not meet my with current R project style, has to turn it off.

(use-package flycheck)
;; '(flycheck-lintr-caching nil) ;; need to customised it inside of Emacs
;; (add-hook 'ess-mode-hook
;;           (lambda () (flycheck-mode t)))

Scripts editing

[2015-06-25 Thu 10:02]

R programming

[2015-05-26 Tue 12:41]

clean up the messy R scripts buffer. it will

  1. remove comments lines start with ‘## ’
  2. remove blank lines,
  3. add one blank lines between sections, which defined by ‘#### ‘.
(defun yt/clean-R () 
  (interactive)
  (when (string= major-mode "ess-mode")
    (progn
      (goto-char (point-min))
      (flush-lines "^\\(\\|[[:space:]]+\\)[#]\\{1,3\\} ") ;; remove lines with only commenst and start with #, ##, or ###, but not #### for it's the section heading. 
      (flush-lines "^\\(\\|[[:space:]]+\\)$") ;; blank lines
      (replace-regexp "#### " "\n#### ") ;; add blank lines between sections. 
      (while (search-forward-regexp "##[^']" nil t) ;; remove inline comments start with ## 
        (kill-region (- (point) 3) (line-end-position)))
    (save-buffer))))

apply the clean scripts to the tangled file. also, preappend the date and my name on the tangled file.

;; add author info
(defun yt/ess-author-date ()
  (interactive)
  (when (string= major-mode "ess-mode")
    (goto-char (point-min))
    (insert "##' @author: Yi Tang\n")
    (insert "##' @date: ")
    (insert (format-time-string "%F %T"))
    (insert "\n\n")
    (save-buffer)))
(add-hook 'org-babel-post-tangle-hook 'yt/ess-author-date)
(add-hook 'org-babel-post-tangle-hook 'yt/clean-R)

increase readability

(defun yt/ess-chunk-args--line ()
  "sim.gc.table <- data.table(duration = sort(sim.duration, decreasing = TRUE), rp = 1e4 / seq(1, length(sim.duration))) becomes 


sim.gc.table <- data.table(duration = sort(sim.duration,
                                          decreasing = TRUE),
                          rp = 1e4 / seq(1, length(sim.duration)))
"
  (interactive)
  (save-excursion
    (let ((start-point (point)))
      (while (re-search-forward ", \\([a-z]+ =\\)" (line-end-position) t)
	(replace-match (concat ",\n    " (match-string 1))))
      (indent-region start-point (line-end-position))
      (goto-char start-point))))
      
(defun yt/ess-chunk-plus--line ()
  "ggplot(obs.gc.table, aes(rp, duration)) + geom_point() + scale_x_log10() + scale_y_log10() 

becomes 

ggplot(obs.gc.table, aes(rp, duration)) +
    geom_point() +
    scale_x_log10() +
    scale_y_log10()
"
  (interactive)
  (save-excursion
    (let ((start-point (point)))
      (replace-regexp " \\+ " " +\n    " nil (point) (line-end-position))
      (indent-region start-point (line-end-position))
      (goto-char start-point))))

testing

(defun yt/ess-script-variables ()
  (interactive)
  (let ((var-list '())
        (data-list '()))
    (save-excursion
      (while (search-forward-regexp "^[[:space:]]*\\([[:alpha:]]+\\) <- function\(" nil t)
        (add-to-list 'func-list (match-string-no-properties 1))))
    (save-excursion
      (while (search-forward-regexp "^[[:space:]]*\\([a-z\\.]+\\) <- " nil t)
        (add-to-list 'var-list (match-string-no-properties 1))))
    (append (set-difference var-list func-list) data-list)))

(defun yt/ess-remove-variables-not-in-scripts ()
  (interactive)
  (let* ((all-vars (yt/ess-script-variables))
         (all-vars-R (concat "c(\"" (mapconcat 'identity all-vars "\",\"")
                             "\")")))
    (kill-new (concat "rm(list = setdiff\(setdiff\(ls\(\), lsf.str\(\)\), " all-vars-R "\)\)"))))

Auto-complete

[2016-05-13 Fri 14:17]

Define in auto-complete section.

Run R script using subprocess

(defun yt/bash_run_R ()
     (interactive)
     (let* ((args (concat "R --no-save --no-restore < " (file-name-nondirectory (buffer-file-name))))
            (output-buf-name (concat "*R:" (file-name-nondirectory (buffer-file-name)) "*"))
            )
       (async-shell-command args output-buf-name)
       (with-current-buffer output-buf-name
         (inferior-ess-mode))
       ))

Code navigation

;; (visit-tags-table "~/R_tags")

Writing in Emacs

[2015-01-19 Mon 12:11]

Spell and Grammar

[2015-01-23 Fri 17:43]

Spell checking and correcting are essential in writing. Emacs need third party program do this. There are a couple of programs and I use aspell. It is part of GNU and can be easily installed in OS X and Ubuntu. The following snippet tells Emacs where aspell is installed and use British dictionary.

(if (eq system-type 'darwin)
    (setq ispell-program-name "/opt/homebrew/bin/aspell")   ;; this semes not necessary
  (setq ispell-program-name "/usr/bin/aspell"))
(setq ispell-dictionary "british"
      ispell-extra-args '() ;; TeX mode "-t"
      ispell-silently-savep t)

I have a personal spelling dictionary, most are abbreviations and jargon. I can tell aspell that they are not misspellings.

(setq ispell-personal-dictionary "~/matrix/tools/.emacs.d/local/ispell-dict") ;; add personal dictionary 
(add-to-list 'ispell-skip-region-alist '(":\\(PROPERTIES\\|LOGBOOK\\):" . ":END:"))
(add-to-list 'ispell-skip-region-alist '("#\\+BEGIN_SRC" . "#\\+END_SRC"))

Flyspell depends on ispell mode and enables on-the-fly spell checking/correcting. I enable the flyspell mode for text-mode and org-mode.

By default, I use C-, to move the cursor to the next misspelled word, and flycheck will provide a list of candidates for auto-correlection. I press C-. select the first one, and press it again to select the next one.

(use-package flyspell)
(add-hook 'text-mode-hook 'flyspell-mode)
(add-hook 'org-mode-hook 'flyspell-mode)
;; (define-key flyspell-mode-map (kbd "C-.") 'helm-flyspell-correct)

;; TODO - cannot get consult-flyspell working
(use-package consult-flyspell
  ;; :straight (consult-flyspell :type git :host gitlab :repo "OlMon/consult-flyspell" :branch "master")
  :config
  ;; default settings
  (setq consult-flyspell-select-function (lambda () (flyspell-correct-at-point) (consult-flyspell))
        consult-flyspell-set-point-after-word t
        consult-flyspell-always-check-buffer nil))

;; (require 'flyspell-correct)
;; (require 'flyspell-correct-ido)
;; (define-key flyspell-mode-map (kbd "C-;") 'flyspell-correct-wrapper)


(use-package flyspell-correct
  :after flyspell
  :bind (:map flyspell-mode-map ("C-." . flyspell-correct-wrapper)))

(use-package flyspell-correct-ivy
  :after flyspell-correct)

I need an grammar check to let me know that

Have you do ...

is wrong, and also tell me to change do to done, and also why. langtool can do be the job, but currently I don’t understand how to get it works, so I am not using it anymore.

;; ;; check grammar 
(use-package langtool)
(if (eq system-type 'darwin)
    ;; (setq langtool-java-bin "/opt/homebrew/opt/openjdk/bin/java")
    (setq langtool-language-tool-jar "~/Downloads/LanguageTool-6.3/languagetool-commandline.jar"))

(setq langtool-mother-tongue "en")

;; checkout this also; https://github.com/emacs-languagetool/flycheck-languagetool

;; https://github.com/PillFall/languagetool.el
;; not working 
;; (use-package languagetool
;;   :ensure t
;;   :defer t
;;   :commands (languagetool-check
;;              languagetool-clear-suggestions
;;              languagetool-correct-at-point
;;              languagetool-correct-buffer
;;              languagetool-set-language
;;              languagetool-server-mode
;;              languagetool-server-start
;;              languagetool-server-stop)
;;   :config
;;   (setq languagetool-java-arguments '("-Dfile.encoding=UTF-8")
;;         languagetool-console-command "/Users/yitang/Downloads/LanguageTool-6.3/languagetool-commandline.jar"
;;         languagetool-server-command "/Users/yitang/Downloads/LanguageTool-6.3/languagetool-server.jar"))

Abbreviation

I have been writing in Emacs/org-mode a lot, have been really tired of capitalise i to I, so I use abbrevitation table.

nameexpandCategory
iIwrite
amaxannual maximumstat
gmapgoogle mapwebsite
mailme[email protected]aboutme
twitterme@yi_tang_ukaboutme
eqtequivalent toenglish
iifif and only ifmaths
wrtwith respect toEnglish
stsuch thatEnglish
d/ndistributionStats
obsobservationstats
obssobservationsstats
(defun my-text-abbrev-expand-p ()
  "Return t if the abbrev is in a text context, which is: in
   comments and strings only when in a prog-mode derived-mode or
   src block in org-mode, and anywhere else."
  (if (or (derived-mode-p 'prog-mode)
          (and (eq major-mode 'org-mode)
               (org-in-src-block-p 'inside)))
      (nth 8 (syntax-ppss))
    t))

(define-abbrev-table 'my-text-abbrev-table ()
  "Abbrev table for text-only abbrevs. Expands only in comments and strings."
  :enable-function #'my-text-abbrev-expand-p)

(dolist (table (list text-mode-abbrev-table
                     prog-mode-abbrev-table))
  (abbrev-table-put table
                    :parents (list my-text-abbrev-table)))

(defun my-text-abbrev-table-init (abbrevs-org-list)
  "Parse 'name: expansion' pairs from an org list and insert into abbrev table."
  (message "Creating text-abbrev table...")
  (dolist (abbrev abbrevs-org-list)
    (let ((name (nth 0 abbrev))
          (expansion (nth 1 abbrev)))
      ;; (print (cons name expansion))
      (define-abbrev my-text-abbrev-table name expansion nil :system t))))
;;(my-text-abbrev-table-init my-text-abbrevs)  ; BUG: only work in org-mode

Style

[2015-05-26 Tue 12:13]

English is my second language, and I am trying to avoid certain guarding term in writing. The following snipts I get it from Sachua will highlight the word like shuold or I think, which reminds to confirm with what I am not sure about, and have more confidence in what I am saying.

(use-package artbollocks-mode)
(add-hook 'text-mode-hook 'artbollocks-mode)
(setq artbollocks-weasel-words-regex
      (concat "\\b" (regexp-opt
                     '("should"
                       "just"
                       "sort of"
                       "a lot"
                       "probably"
                       "maybe"
                       "perhaps"
                       "I think"
                       "really"
                       "nice") t) "\\b"))

add synosaurus

;; [2015-02-12 Thu 21:14]
;; https://github.com/rootzlevel/synosaurus
;; synosaurus-lookup
;; synosaurus-choose-and-replace
;; brew install wordnet
(require 'synosaurus)
(setq synosaurus-choose-method "popup")

;; synosaurus-lookup C-c s l
;; synosaurus-choose-and-replace C-c s r	
(setq synosaurus-backend 'synosaurus-backend-wordnet)
(setq synosaurus-choose-method 'popup)

Title Case

(defun xah-title-case-region-or-linebegin φend)
  "Title case text between nearest brackets, or current line, or text selection.
Capitalize first letter of each word, except words like {to, of, the, a, in, or, and, …}. If a word already contains cap letters such as HTTP, URL, they are left as is.

When called in a elisp program, φbegin φend are region boundaries.
URL `http://ergoemacs.org/emacs/elisp_title_case_text.html'
Version 2015-05-07"
  (interactive
   (if (use-region-p)
       (list (region-beginning) (region-end))
     (let (
           ξp1
           ξp2
           (ξskipChars "^\"<>(){}[]“”‘’‹›«»「」『』【】〖〗《》〈〉〔〕"))
       (progn
         (skip-chars-backward ξskipChars (line-beginning-position))
         (setq ξp1 (point))
         (skip-chars-forward ξskipChars (line-end-position))
         (setq ξp2 (point)))
       (list ξp1 ξp2))))
  (let* (
         (ξstrPairs [
                     [" A " " a "]
                     [" And " " and "]
                     [" At " " at "]
                     [" As " " as "]
                     [" By " " by "]
                     [" Be " " be "]
                     [" Into " " into "]
                     [" In " " in "]
                     [" Is " " is "]
                     [" It " " it "]
                     [" For " " for "]
                     [" Of " " of "]
                     [" Or " " or "]
                     [" On " " on "]
                     [" Via " " via "]
                     [" The " " the "]
                     [" That " " that "]
                     [" To " " to "]
                     [" Vs " " vs "]
                     [" With " " with "]
                     [" From " " from "]
                     ["'S " "'s "]
                     ]))
    (save-excursion 
      (save-restriction
        (narrow-to-region φbegin φend)
        (upcase-initials-region (point-min) (point-max))
        (let ((case-fold-search nil))
          (mapc
           (lambdax)
             (goto-char (point-min))
             (while
                 (search-forward (aref ξx 0) nil t)
               (replace-match (aref ξx 1) 'FIXEDCASE 'LITERAL)))
           ξstrPairs))))))

Org mode

Based on Bernt Hansen’s Org Mode - Organize Your Life In Plain Text!.

org-todos

[2015-07-20 Mon 14:57]

Define the TODO keywords and their colour.

(setq org-todo-keywords
      (quote ((sequence "TODO(t)" "NEXT(n)" "WIP(w)" "SOMEDAY" "|" "DONE(d)")
              (sequence "WAITING(W@/!)" "HOLD(h@/!)" "|" "CANCELLED(c@/!)" "MEETING"))))

(setq org-todo-keyword-faces
      (quote (("TODO" :foreground "red" :weight bold)
	      ("NEXT" :foreground "red" :weight bold)
              ("DONE" :foreground "forest green" :weight bold)
              ("WAITING" :foreground "orange" :weight bold)
              ("HOLD" :foreground "magenta" :weight bold)
              ("CANCELLED" :foreground "forest green" :weight bold)
              ("MEETING" :foreground "forest green" :weight bold))))

Define an event when a TODO status changes, for example, if changed to HOLD, add HOLD tag and remove WAITING tag. If changed to DONE, remove both HOLD and WAITING tags.

(setq org-todo-state-tags-triggers
      (quote (("CANCELLED" ("CANCELLED" . t))
              ("WAITING" ("WAITING" . t))
              ("HOLD" ("WAITING") ("HOLD" . t))
              (done ("WAITING") ("HOLD"))
              ("TODO" ("WAITING") ("CANCELLED") ("HOLD"))
              ("NEXT" ("WAITING") ("CANCELLED") ("HOLD"))
              ("DONE" ("WAITING") ("CANCELLED") ("HOLD")))))

Especially, when a task is marked as DONE, a timestamp is added to the LOGBOOK drawer.

  1. when a task is marked as DONE, a timestamp is added below the headline, for exmaple,
    CLOSED: [2023-03-19 Sun 08:54]
        
(setq org-log-done (quote time))
(setq org-log-into-drawer t)  ; t means LOGBOOK
(setq org-log-state-notes-insert-after-drawers nil)

Add a cross line for the headline with DONE status. Note currently it is disabled before of the performance issues in OS X.

(defun yt/modify-org-done-face ()
  (setq org-fontify-done-headline t)
  (set-face-attribute 'org-done nil :strike-through nil)
  (set-face-attribute 'org-headline-done nil
		      :strike-through t
		      :foreground "light gray"))
;; turn it off for now.
;; (add-hook 'org-mode-hook 'yt/modify-org-done-face)
;; (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)

org-capture

[2015-07-20 Mon 14:57]

Use C-c c anywhere to quickly create a org headline and save it to a default place.

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

org-refile

[2015-07-20 Mon 14:57]

Set the refile targets, they are all level 1 2 3 in current buffer and all the files in org-agenda-files.

(setq org-refile-targets
      '((nil :maxlevel . 3)
        (org-agenda-files :maxlevel . 3)))
(setq org-outline-path-complete-in-steps nil)

but exclude DONE state tasks from refile targets

(defun bh/verify-refile-target ()
  "Exclude todo keywords with a done state from refile targets"
  (not (member (nth 2 (org-heading-components)) org-done-keywords)))
(setq org-refile-target-verify-function 'bh/verify-refile-target)

Provide refile targets as paths. So a level 3 headline will be available as level1/level2/level3.

(setq org-refile-use-outline-path t)

Speed up the process by using cache.

(setq org-refile-use-cache t)

org-clock

[2015-07-20 Mon 14:57]

Save the running clock and all clock history when exiting Emacs, load it on startup. this is useful when i forget closing the clock, or crash.

(setq org-clock-persist t)

Resume clocking task when Emacs starts, and if continue to count on this task.

(org-clock-persistence-insinuate)
(setq org-clock-in-resume t)

;; Do not prompt to resume an active clock
;; (setq org-clock-persist-query-resume nil)
;; Save clock data and state changes and notes in the LOGBOOK drawer
(setq org-clock-into-drawer t)
;; Sometimes I change tasks I'm clocking quickly - this removes clocked tasks with 0:00 duration
(setq org-clock-out-remove-zero-time-clocks t)
;; Clock out when moving task to a done state
(setq org-clock-out-when-done t)

;; Enable auto clock resolution for finding open clocks
(setq org-clock-auto-clock-resolution (quote when-no-clock-is-running))
;; Include current clocking task in clock reports
(setq org-clock-report-include-clocking-task t)

highlight the clocking info in mode line.

(set-face-attribute 'org-mode-line-clock nil
		    :weight 'bold :box '(:line-width 1 :color "#FFBB00") :foreground "white" :background "#FF4040")

List recently clocked headline and clock in.

;; Show lot of clocking history so it's easy to pick items off the C-F11 list
(setq org-clock-history-length 23)
;; ;; http://stackoverflow.com/questions/6156286/emacs-lisp-call-function-with-prefix-argument-programmatically

When clock in to a TODO headline, turn the keywords into NEXT.

;; Change tasks to NEXT when clocking in
(setq org-clock-in-switch-to-state 'bh/clock-in-to-next)
(defun bh/clock-in-to-next (kw) 
  "Switch a task from TODO to NEXT when clocking in.
Skips capture tasks"
  (when (not (and (boundp 'org-capture-mode) org-capture-mode))
    (if (member (org-get-todo-state) (list "TODO"))
        "NEXT")))

punch-in into a default org-mode headline.

(defun yt/punch-in ()
  (interactive)
    (org-with-point-at (org-id-find "1b586ec1-fa8a-4bd1-a44c-faf3aa2adf51" 'marker)
    (org-clock-in)
     ))
(global-set-key (kbd "<f9> I") 'yt/punch-in)

remove empty clock entrys at checkout.

(add-hook 'org-clock-out-hook 'org-clock-remove-empty-clock-drawer 'append)

org-tags

[2015-07-20 Mon 14:57]

I don’t use org-tags.

(setq org-tag-alist (quote ((:startgroup)
                            ("@office" . ?O)
                            ("@home" . ?H)
                            (:endgroup)
                            ("WAITING" . ?w)
                            ("HOLD" . ?h)
                            ("PERSONAL" . ?P)
                            ("WORK" . ?W)
                            ("NOTE" . ?n)
                            ("READ" .?r)
                            ("CANCELLED" . ?c)
                            )))
;; Allow setting single tags without the menu
(setq org-fast-tag-selection-single-key (quote expert))
(setq org-agenda-tags-todo-honor-ignore-options t)

Agenda

[2015-01-23 Fri 16:54]
(global-set-key (kbd "<f12>") 'org-agenda)

;; Do not dim blocked tasks
(setq org-agenda-dim-blocked-tasks nil)
(setq org-agenda-dim-blocked-tasks 'invisible)

;; Compact the block agenda view
(setq org-agenda-compact-blocks nil)




;; Limit restriction lock highlighting to the headline only
(setq org-agenda-restriction-lock-highlight-subtree nil)

;; Always hilight the current agenda line
(add-hook 'org-agenda-mode-hook
          '(lambda () (hl-line-mode 1))
          'append)

  ;;;; * agenda ignore items 
;; Keep tasks with dates on the global todo lists
(setq org-agenda-todo-ignore-with-date nil)

;; Keep tasks with deadlines on the global todo lists
(setq org-agenda-todo-ignore-deadlines nil)

;; Keep tasks with scheduled dates on the global todo lists
(setq org-agenda-todo-ignore-scheduled nil)

;; Keep tasks with timestamps on the global todo lists
(setq org-agenda-todo-ignore-timestamp nil)

;; Remove completed deadline tasks from the agenda view
(setq org-agenda-skip-deadline-if-done t)

;; Remove completed scheduled tasks from the agenda view
(setq org-agenda-skip-scheduled-if-done t)

;; Remove completed items from search results
(setq org-agenda-skip-timestamp-if-done t)

(setq org-agenda-include-diary nil)


(setq org-agenda-insert-diary-extract-time t)

;; Include agenda archive files when searching for things
(setq org-agenda-text-search-extra-files (quote (agenda-archives)))

;; Show all future entries for repeating tasks
(setq org-agenda-repeating-timestamp-show-all t)

;; Show all agenda dates - even if they are empty
(setq org-agenda-show-all-dates t)

;; Sorting order for tasks on the agenda
(setq org-agenda-sorting-strategy
      (quote ((agenda habit-down time-up user-defined-up effort-up category-keep)
              (todo category-up effort-up)
              (tags category-up effort-up)
              (search category-up))))



;; (setq org-agenda-tags-column -102)
;; Use sticky agenda's so they persist
;; (setq qorg-agenda-sticky t)

Enable display of the time grid so we can see the marker for the current time

(setq org-agenda-time-grid (quote ((daily today require-timed)
 (600 630 700 730 800 830 900 930 1000 1030 1200 1400 1600 1800 2000)
 "......" "----------------")))

Start the weekly agenda on Monday.

(setq org-agenda-span 'week)
(setq org-agenda-start-on-weekday 1)

show the items 30 prior to their deadline. sounds bit too much. how about 7 days?

(setq org-deadline-warning-days 30)

highlight clock entries that are either too long (more than 4 hours) or too short (less than 5 mins). those entries requires manual checks.

(setq org-agenda-clock-consistency-checks
      (quote (:max-duration "4:00"                 ;; highligh clock entries longer than 5 hours.
			    :min-duration "00:05"  ;; highlight clock smaller than 5 mins 
			    :max-gap "00:05"       ;; highlight clock gap loger than 5 mins.
			    :gap-ok-around ("4:00")))) 
(setq org-read-date-prefer-future 'time)

Agenda reminder

;; Erase all reminders and rebuilt reminders for today from the agenda
(defun bh/org-agenda-to-appt ()
  (interactive)
  (setq appt-time-msg-list nil)
  (setq appt-display-format 'window) ;; YT: show notification in separate window
  (org-agenda-to-appt))

;; Rebuild the reminders everytime the agenda is displayed
(add-hook 'org-finalize-agenda-hook 'bh/org-agenda-to-appt 'append)

;; This is at the end of my .emacs - so appointments are set up when Emacs starts
;; (bh/org-agenda-to-appt)

Babel

[2015-01-29 Thu 14:16] define a list of supported language.

(org-babel-do-load-languages
 (quote org-babel-load-languages)
 (quote ((emacs-lisp . t) ;; TODO: simplifiy this list 
	 (R . t)
	 (shell . t)
	 (org . t)
	 (dot . t)
	 (python .t)
	 ;; (ipython .t)
	 ;; (bibtex .t)
	 (octave . t)
	 (latex . t)
	 (jupyter . t)
	 (sql . t))))
(setq org-confirm-babel-evaluate nil)


;; (use-package conda
;;   ;; :straight t
;;   :config
;;   (setq conda-anaconda-home (expand-file-name "/home/yitang/miniconda3/"))
;;   (setq conda-env-home-directory (expand-file-name "/home/yitang/miniconda3/"))
;;   (setq conda-env-subdirectory "envs"))

;; (unless (getenv "CONDA_DEFAULT_ENV")
;;   (conda-env-activate "latest"))


;; (defun my/jupyter-refresh-kernelspecs ()
;;   "Refresh Jupyter kernelspecs"
;;   (interactive)
;;   (jupyter-available-kernelspecs t))


;; (setq jupyter-use-zmq nil) 

define default header for all and specific language

(setq org-babel-default-header-args (append org-babel-default-header-args '((:colnames . "yes"))))

(setq org-babel-default-header-args:R
      '((:session . "R")
        (:colnames . "yes")))

(setq org-babel-default-header-args:bash
      '((:session . "*shell*")))

(setq org-src-window-setup 'current-window)
(setq org-src-fontify-natively t)
(setq org-src-preserve-indentation nil)
(setq org-edit-src-content-indentation 0)
(setq org-catch-invisible-edits 'error)
(setq org-export-coding-system 'utf-8)
(prefer-coding-system 'utf-8)
(set-charset-priority 'unicode)
(setq default-process-coding-system '(utf-8-unix . utf-8-unix))
(setq org-babel-results-keyword "results")

display inline image after execute src-block, useful for src block that generate inline images.

(defun bh/display-inline-images ()
  (condition-case nil
      (org-display-inline-images)
    (error nil)))

(add-hook 'org-babel-after-execute-hook 'bh/display-inline-images 'append)

when switch to the bash session, if does not exists, create a new one. a workaround would be to run a simple function, ls for example first, and then switch to the interactive session.

;; copy of org-sh-bash-initiate-session in ob-shell.el but with different name
;; per https://emacs.stackexchange.com/questions/55957/error-no-org-babel-initiate-session-function-for-bash
(defun org-babel-bash-initiate-session (&optional session _params)
  "Initiate a session named SESSION according to PARAMS."
  (when (and session (not (string= session "none")))
    (save-window-excursion
      (or (org-babel-comint-buffer-livep session)
          (progn
	    (shell session)
	    ;; Needed for Emacs 23 since the marker is initially
	    ;; undefined and the filter functions try to use it without
	    ;; checking.
	    (set-marker comint-last-output-start (point))
	    (get-buffer (current-buffer)))))))

use python3 in org-babel.

(setq org-babel-python-command "python3")

Export

[2015-02-04 Wed 12:23]

Add export back-end, I need HTML, PDF, MarkDown, and Ascii.

(setq org-export-backends '(ascii html latex md org))
(require 'ox-md) ;; somehow this does not work. i don't know why. 

General export options, it applys to all the export-backend.

(setq org-export-with-toc nil
      org-export-with-todo-keywords t
      org-export-with-sub-superscripts nil
      org-export-with-planning nil
      org-export-with-author t
      org-export-with-timestamps nil
      org-export-babel-evaluate t
      org-export-with-drawers nil)
(setq org-image-actual-width '(400))

Set the default format when exporting table to CSV.

(setq org-table-export-default-format "orgtbl-to-csv")

PDF Export

[2015-01-19 Mon 15:45]

define the latex export process, by default, it uses latexmk.

below i define the process as list of three shell commands, which will run in sequentially. multiple calls are neeed to ensure bibliograph etc works. i can also clean up the files, for example, remove the intermediary files. need to test those.

;; ;;;; comple pdf 
(setq org-latex-pdf-process
      '("xelatex -shell-escape -interaction=nonstopmode -output-directory %o %f"
        "xelatex -shell-escape -interaction=nonstopmode -output-directory %o %f"
        "xelatex -shell-escape -interaction=nonstopmode -output-directory %o %f"))

define customised latex-class which will be use when export org-mode to LaTeX. the latex-class can be defined somewhere else.

;; http://emacs-fu.blogspot.co.uk/2011/04/nice-looking-pdfs-with-org-mode-and.html
;; 'djcb-org-article' for export org documents to the LaTex 'article', using
;; XeTeX and some fancy fonts; requires XeTeX (see org-latex-to-pdf-process)
(require 'ox-latex)
(add-to-list 'org-latex-classes
             '("yt/org-article"
               "
\\documentclass[11pt,a4paper]{article}
\\usepackage{graphicx}    %% demo mode is a must when .img does not exists.
\\usepackage[T1]{fontenc}
\\usepackage{fontspec}
\\usepackage{hyperref}
\\hypersetup{
     colorlinks   = true,
     citecolor    = gray
}
\\usepackage{amsmath}
\\usepackage{amstext}
\\usepackage{amssymb} %% checkbox
\\usepackage{commath}
\\usepackage{physics}   %% \\pdv for derivative operators https://tex.stackexchange.com/questions/225523/how-to-write-partial-differential-equation-ex-dq-dt-ds-dt-with-real-partial-d
\\DeclareMathOperator*{\\argmin}{\\arg\\!\\min} %% use $\\argmin_{b}$
\\DeclareMathOperator*{\\argmax}{\\arg\\!\\max} 
%% \\DeclareMathOperator{\\E}{\\mathbb{E}}
\\newcommand{\\E}[1]{{\\mathbb E}\\left[ #1 \\right]}
\\newcommand{\\Var}{\\mathrm{Var}}
%% \\DeclareMathOperator{\\P}{\\mathbb{Pr}}

\\usepackage{minted}
\\defaultfontfeatures{Mapping=tex-text}
% \\setromanfont[BoldFont={Gentium Basic Bold},
%                 ItalicFont={Gentium Basic Italic}]{Gentium Plus}
\\setsansfont{Charis SIL}
\\setmonofont[Scale=0.8]{DejaVu Sans Mono}
\\usepackage{geometry}
%% \\geometry{a4paper, textwidth=6.5in, textheight=10in,
 %%  marginparsep=7pt,
 %%  marginparwidth=1.2in, %% make sure it less than right=1.5in,
  %% otherwise, will go out of the paper
 %% right=1.5in, left=0.6in}

\\geometry{a4paper, textwidth=6.5in, textheight=10in,
            marginparsep=7pt, marginparwidth=.6in}
\\pagestyle{empty}
 
%% package from org-latex-default-packages-alist
\\usepackage{setspace}
\\onehalfspacing
\\usepackage{textcomp}
\\usepackage{marvosym}
\\usepackage{wasysym}
\\usepackage{ulem}
\\usepackage{amsthm}

\\theoremstyle{definition}
\\newtheorem{definition}{Definition}[section]% Conjecture is numbered
                                % within \section
\\newtheorem{lemma}[definition]{Lemma}
\\newtheorem{theorem}[definition]{Theorem}

\\newcommand{\\twodots}{\\mathinner {\\ldotp \\ldotp}}

%% \\renewcommand\\texttt[1]{{\\mint{cl}|#1|}} 


\\usepackage{environ}
\\NewEnviron{note}{\\marginpar{\\footnotesize \\BODY}}

%% algorithm 
\\usepackage{xcolor}
\\usepackage[linesnumbered]{algorithm2e}
\\newcommand\\mycommfont[1]{\\footnotesize\\ttfamily\\textcolor{blue}{#1}}
\\makeatletter
\\renewcommand{\\@algocf@capt@plain}{above}% formerly {bottom}
\\makeatother


\\title{}
      [NO-DEFAULT-PACKAGES]
      [NO-PACKAGES]"
               ("\\section{%s}" . "\\section*{%s}")
               ("\\subsection{%s}" . "\\subsection*{%s}")
               ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
               ("\\paragraph{%s}" . "\\paragraph*{%s}")
               ("\\subparagraph{%s}" . "\\subparagraph*{%s}")))
(setq org-latex-default-class "yt/org-article")

(add-to-list 'org-latex-classes
             '("yt/beamer"
               "\\documentclass[aspectratio=169]{beamer}
\\usepackage[T1]{fontenc}
\\usepackage{fontspec}
\\usetheme[faculty=fi]{fibeamer}
\\usepackage[utf8]{inputenc}

\\usepackage[
  main=english, %% By using `czech` or `slovak` as the main locale
                %% instead of `english`, you can typeset the
                %% presentation in either Czech or Slovak,
                %% respectively.
  czech, slovak %% The additional keys allow foreign texts to be
]{babel}        %% typeset as follows:


[NO-DEFAULT-PACKAGES]
[NO-PACKAGES]
"
               ("\\section{%s}" . "\\section*{%s}")
               ("\\subsection{%s}" . "\\subsection*{%s}")
               ("\\subsubsection{%s}" . "\\subsubsection*{%s}")))

;; (use-package ox-beamer)

;; code highlights using minted package 
(add-to-list 'org-latex-packages-alist '("" "minted"))
(setq org-latex-listings 'minted)
(setq org-latex-minted-options
      '(("frame" "lines")
        ("fontsize" "\\scriptsize")))
;; ("linenos" "")))

Deep Configuration - remove this entry

Remove keys

;; remove C-TAB
(define-key org-mode-map (kbd "C-S-<right>") 'mc/mark-next-like-this)
(define-key org-mode-map (kbd "C-S-<left>") 'mc/mark-previous-like-this)
(org-defkey org-mode-map (kbd "C-c [") nil)
(org-defkey org-mode-map (kbd "C-c ]") nil)
(org-defkey org-mode-map (kbd "C-TAB") nil)
(org-defkey org-mode-map (kbd "<f8>") nil)

Show org-mode bullets as UTF-8 characters.

Add markup wrapper for org-mode. to turn a word into bold, wrapper in a selected region, by using expand-region, which is bound to C-= then type *.

(sp-local-pair 'org-mode "=" "=") ; select region, hit = then region -> =region= in org-mode
(sp-local-pair 'org-mode "*" "*") ; select region, hit * then region -> *region* in org-mode
(sp-local-pair 'org-mode "/" "/") ; select region, hit / then region -> /region/ in org-mode
(sp-local-pair 'org-mode "_" "_") ; select region, hit _ then region -> _region_ in org-mode
(sp-local-pair 'org-mode "+" "+") ; select region, hit + then region -> +region+ in org-mode
(sp-local-pair 'org-mode "$" "$") ; select region, hit $ then region -> $region$ in org-mode

Others Org Modules

[2015-07-20 Mon 14:57]

;;;; * Custom Key Bindings

(setq org-agenda-clockreport-parameter-plist
      (quote (:link t :maxlevel 5 :fileskip0 t :compact t :narrow 80)))
;; Set default column view headings: Task Effort Clock_Summary
(setq org-columns-default-format "%80ITEM(Task) %10Effort(Effort){:} %10CLOCKSUM")
;; global Effort estimate values
;; global STYLE property values for completion
(setq org-global-properties (quote (("Effort_ALL" . "0:05 0:15 0:30 0:45 1:00 2:00 3:00 4:00 5:00 6:00 0:00")
                                    ("STYLE_ALL" . "habit"))))
(setq org-agenda-log-mode-items (quote (clock)))

(setq org-use-speed-commands t)
(defun bh/insert-inactive-timestamp ()
  (interactive)
  (org-insert-time-stamp nil t t nil nil nil))
(global-set-key (kbd "<f9> t") 'bh/insert-inactive-timestamp)

(defun yt/insert-ts-as-file ()
    (interactive)
  (insert (format-time-string "%Y-%m-%d--%H-%M-%S"))
  )

(global-set-key (kbd "<f9> T") 'yt/insert-ts-as-file)

(defun bh/insert-heading-inactive-timestamp ()
  (save-excursion
    (org-return)
    (org-cycle)
    (bh/insert-inactive-timestamp)))
;; (add-hook 'org-insert-heading-hook 'bh/insert-heading-inactive-timestamp 'append)
(setq org-file-apps (quote ((auto-mode . emacs)
                            ("\\.png\\'" . emacs)
                            ("\\.svg\\'" . system)
                            ("\\.mm\\'" . system)
                            ("\\.x?html?\\'" . system)
                            ("\\.pdf\\'" . "evince %s"))))
                                        ; Overwrite the current window with the agenda
(setq org-agenda-window-setup 'current-window)

(setq org-time-clocksum-format
      '(:hours "%d" :require-hours t :minutes ":%02d" :require-minutes t))

(add-hook 'org-mode-hook (lambda () (abbrev-mode 1)))
(setq org-reverse-note-order t) ;; refiled headline will be the first under the taget

(setq org-archive-location "::* Archived Tasks") ;;in-file archive 

;; (setq org-habit-show-all-today t)
;; (setq org-habit-show-habits nil)
;; (setq org-habit-graph-column 80)
;; add the following 
(setq org-time-stamp-custom-formats '("<%A %d %B %Y>" . "<%A %d %B %Y %H:%M>"))
(setq org-agenda-tags-column 120)

(setq org-columns-default-format "%80ITEM(Task) %10Effort(Effort){:} %10CLOCKSUM %10Mindfullness")

Start up options

(setq org-startup-folded "showall"
      org-hide-block-startup t
      org-startup-indented nil)

C-c C-l org-insert-link by default. found it is easier to have C-c l as store link.

(global-set-key (kbd "C-c l") 'org-store-link)

trying to remove closed item in org-agenda. it does not work, might be a bug in org-mode.

;; https://emacs.stackexchange.com/questions/51755/org-mode-link-files-with-ids-and-not-filenames
;; create id property whenver link a headline -
;; see - https://emacs.stackexchange.com/questions/51755/org-mode-link-files-with-ids-and-not-filenames
(setq org-id-link-to-org-use-id t)

;; by defualt, agenda log mode shows when a taks is closed. i don't wanna that.
(setq org-agenda-log-mode-items '(clock))

add a created timestamp property to each header.

(defvar org-created-property-name "CREATED"
  "The name of the org-mode property that stores the creation date of the entry")

(defun org-set-created-property (&optional active NAME)
  "Set a property on the entry giving the creation time.

By default the property is called CREATED. If given the `NAME'
argument will be used instead. If the property already exists, it
will not be modified."
  (interactive)
  (let* ((created (or NAME org-created-property-name))
         (fmt (if active "<%s>" "[%s]"))
         (now  (format fmt (format-time-string "%Y-%m-%d %a %H:%M"))))
    (unless (org-entry-get (point) created nil)
      (org-set-property created now))))
(add-hook 'org-insert-heading-hook 'org-set-created-property 'append)
(add-hook 'org-capture-prepare-finalize-hook 'org-set-created-property 'append)

Jekyll in Emacs

I write blog posts in org-mode format, export to Markdown, and let Jekyll render it in HTML over the web.

I have the org/ folder to keep the blog posts and drafts. They are exporting to the blog/_posts and blog/_drafts folder.

(defvar jekyll-source-directory (expand-file-name "~/matrix/learning/mywebsite/org/")
  "root directory of org files.")

;; replace those two with jekyll-source-drafts-dir, jekyll-source-post-dir
(defvar jekyll-source-drafts-dir (file-name-concat jekyll-source-directory "_drafts/")
  "Relative path to drafts directory.")
(defvar jekyll-source-posts-dir (file-name-concat jekyll-source-directory  "_posts/")
  "Relative path to posts directory.")


(defvar jekyll-site-dir "~/matrix/learning/mywebsite/blog/"
  "root directory of the Jekyll site.")
;; TODO: remove this variable, use jekyll-sit-post-dir instead
;; (defvar jekyll-publish-dir (concat jekyll-site-dir "_posts/")
;;   "Relative path to posts directory.")
;; (defvar jekyll-assets-dir (file-name-concat jekyll-site-dir "assets/")
;;   "Relative path to assets directory.")

(defvar jekyll-site-posts-dir (file-name-concat jekyll-site-dir "_posts/")
  "Relative path to posts directory.")
(defvar jekyll-site-drafts-dir (file-name-concat jekyll-site-dir "_drafts/")
  "Relative path to posts directory.")
(defvar jekyll-site-assets-dir (file-name-concat jekyll-site-dir "assets/")
  "Relative path to assets directory.")

(defvar jekyll-post-ext ".org"
  "File extension of Jekyll posts.")

The jekyll-post-template contains the frontmatter Jekyll requires, a short HTML snippet for MathJax (might not need at all), and org-mode snippets to control the exporting.

This template is used when creating a post/draft in org-mode, the title will be populated.

(defvar jekyll-post-template
  " 
#+begin_export html
---
layout: post
title: %s
# date: add publish date when ready
excerpt: 
categories:
  -  
tags:
  -
comments: true
---
#+END_export

#+begin_export html
<script type=\"text/javascript\"
    src=\"http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML\">
</script>
#+end_export

# #+call: GetLastUpdatedDate[:exports none]()[:results org]
#+BIND: org-md-toplevel-hlevel 2
#+TOC: headlines 4



* COMMENT checklist 

- [ ] check title 
- [ ] check gramamr
- [ ] check tags,
- [ ] check dates, publish front matter
- [ ] promote in social media
"
  "Default template for Jekyll posts. %s will be replace by the post title.")

(defun jekyll-yaml-escape (s)
  "Escape a string for YAML."
  (if (or (string-match ":" s)
          (string-match "\"" s))
      (concat "\"" (replace-regexp-in-string "\"" "\\\\\"" s) "\"")
    s))

The jekly-ymal-escape function is used just to handle the quote and : characters in the title field.

Then in starting a blog post, I fill the title, and there file will be served in the org/_drafts folder, and insert the YMAL. Note in jeklly, the title be part of html file name and therefore part of the URL. So strange characters must been removed.

(defun blog-draft-post (title) 
  "Create a new Jekyll blog post."
  (interactive "sPost Title: ")
  (let ((draft-file (concat jekyll-source-drafts-dir
                            (jekyll-make-slug title)
                            jekyll-post-ext)))
    (if (file-exists-p draft-file)
        (find-file draft-file)
      (find-file draft-file)
      (insert (format jekyll-post-template (jekyll-yaml-escape title))))))

(defun jekyll-make-slug (s)
  "Turn a string into a slug."
  (replace-regexp-in-string
   " " "-" (downcase
            (replace-regexp-in-string
             "[^A-Za-z0-9 ]" "" s))))

Once I think the article is read to be publish, the blog-publish-post function will move the editing draft into org/_posts folder and prepend the file name with today’s date in %Y-%m-%d format. it is required by Jeklly.

(defun blog-publish-post ()
  "Move a draft post to the posts directory, and rename it so that it
 contains the date."
  (interactive)
  (cond
   ((not (yt/jekyll-is-draft-p))
    (message "This is not a draft post."))
   ((buffer-modified-p)
    (message "Can't publish post; buffer has modifications."))
   (t
    ;; TODO: use a utlity function to for `filename`, find publish file? 
    (let ((filename
           (concat jekyll-source-posts-dir
                   (format-time-string "%Y-%m-%d-")
                   (file-name-nondirectory
                    (buffer-file-name (current-buffer)))))

	  (draft-file-exported (yt/jekyll-find-export))

          (old-point (point)))
      (delete-file draft-file-exported)
      (rename-file (buffer-file-name (current-buffer))
                   filename)
      (kill-buffer nil)
      (find-file filename)
      (set-window-point (selected-window) old-point)))))

I bound C-c j n and C-c j P for making a draft and publishing a draft.

Then convert all the org files in org/_posts into Markdown and save in the _posts/ folder. I used to use the org-publish-project function, it was great. Now i switched to exporting explicitly blog post while working on it.

the yt/jekyll--export-to-md functions does that, for example, org/2023-12-31-example.org to blog/_posts/2023-12-31-example.md.

I prefer to Markdown format for its simplicity, and stopped using HTML format.

(defun yt/jekyll--export-to-md ()
  "export draft/post to markdown"
  (interactive)
  (let* ((export-file (yt/jekyll-find-export)))
    (message "Exporting to %s" export-file)
    (org-export-to-file 'jekyll-md export-file nil nil nil t)))


(defun yt/jekyll--export-to-html ()
  ;; export current posts in org-mode to html.
  (interactive)
  (message "not implemented. use yt/jekyll--export-to-md (markdown) instead."))


(defun yt/jekyll-is-draft-p ()
  "if the file is inside of the draft directory, it is a draft.

can also implmenet as  (file-name-directory (buffer-file-name (current-buffer))) equals to jekyll-source-drafts-dir
"
  (let ((draft-dir  (file-truename jekyll-source-drafts-dir))
	(filepath (file-truename (buffer-file-name))))
    (string-prefix-p draft-dir filepath)))
  

(defun yt/jekyll-find-export ()
  "find the full path to the exported file of the current post."
  (let* ((src-file (file-name-nondirectory (buffer-file-name)))
	 (dest-file (file-name-with-extension src-file ".md")))
    (if (yt/jekyll-is-draft-p)
	(file-name-concat jekyll-site-drafts-dir dest-file)
      (file-name-concat jekyll-site-posts-dir dest-file))))

transient

yt/jekyll is the command centre: it provides a entry point to all the functionalities I need while working on my personal blog project.

it is binds to F5 in a .dir-locals.el.

(transient-define-prefix yt/jekyll ()
  ""
  ["Jekyll Blog"
   [("n" "new draft" blog-draft-post)
    ("p" "publish post" blog-publish-post)
    ("dd" "Dired - drafts (org)" (lambda () (interactive) (find-file jekyll-source-drafts-dir)))
    ("dp" "Dired - posts (org)" (lambda () (interactive) (find-file jekyll-source-posts-dir)))
    ("dP" "Dired - published posts" (lambda () (interactive) (find-file jekyll-site-posts-dir)))
    ("m" "Export as Markdown" yt/jekyll--export-to-md)
    ("i" "Insert Image (Liquid Template)" yt/jekyll-insert-image)
    ;; ("eh" "Export as HTML" yt/jekyll--export-to-html)
    ;; ("u" "Update post title/date" yt/jekyll-update-post-name)
    ("s" "Jekyll Server" (lambda () (interactive) (yt/compile "*jekyll-sever*" "jekyll s  --watch --drafts" jekyll-site-dir)))
    ("S" "Sync website" (lambda () (interactive) (yt/compile "*jekyll-sync*" "bash script.sh" "~/matrix/learning/mywebsite")))
    ]])

code highlight

[2015-01-23 Fri 17:47]

wrap src block into highlight block in Jekyll. it search for src block, and replace the header with {% highlight python %} for example. run this as pre-export hook.

The org-mode documentation provides a elegant solution to this problem: simplify define a new export backend that inherent from the markdown backend, but only change the src code block.

(defun my-jekyll-src-block (src-block contents info)
  "Transcode a SRC-BLOCK element from Org to ASCII.
CONTENTS is nil.  INFO is a plist used as a communication
channel."
  (concat
   (format "{%% highlight %s %%} \n%s\n{%% endhighlight %%}"
           (org-element-property :language src-block)
           (org-element-normalize-string
            (org-export-format-code-default src-block info)))))

(require 'ox) ;; because blogging.el loadded first, before org-mode. need to load after org-mode.
(require 'ox-md) ;; 
(org-export-define-derived-backend 'jekyll-html 'html
  :translate-alist '((src-block . my-jekyll-src-block)))

(org-export-define-derived-backend 'jekyll-md 'md
  :translate-alist '((src-block . my-jekyll-src-block)))

Image Hyperlink

When publishing, the org-file is firstly exported to html file, and when Jekyll build the website, the html file will be saved in to some folder that depends on the YMAL. Then the relative path to image files are broken. To solve that, according to the Jeklyy web site, is to save all the image or downloade files in assets/ folder. Then those image files are referende by org-mode.

First, define a img link, that when exporting, the image a_img.png will be set to linked to /assets/a_img.png. when I click, it wil open the img file in Emacs.

(defun org-custom-link-img-follow (path)
  (org-open-file-with-emacs
   (format "../../assets/%s" path)))

(defun org-custom-link-img-export (path desc format)
  (cond
   ((eq format 'html)
    (format "<img src=\"/assets/%s\" alt=\"%s\"/>" path desc))))

(org-add-link-type "img" 'org-custom-link-img-follow 'org-custom-link-img-export)

i can also use liquid template for insetting images as follows:

{% include image.html src="/assets/temp.png"
caption="Threadripper 3970x vs i5-13600k: Train LightGBM Models on CPU" %}
src
is the path to the image i want to include.
caption
is the caption on the top of the image.
image.html
is the liquid template in _includes/image.html

this image is centred with caption on the top. i cannot control the image size here. image preview is lacking in org buffer.

(defvar  jekyll-insert-image-liquid-template
  " 
{%% include image.html
src=\"/assets/%s\"
caption=\"%s\"
width=450
%%}
"
  "insert image using liquid template.")

(defun yt/jekyll-insert-image (src caption)
  (interactive (list (read-file-name "images to include: " jekyll-site-assets-dir)
		     (read-string "Caption: ")))
  (insert (format jekyll-insert-image-liquid-template (file-name-nondirectory src) caption))
  )

(defun yt/jekyll-copy-to-assets-folder (file)
  "TODO: copy a file to the assets folder. if file not provided,
interactively select file, start with org-download-image-dir.

jekyll-make-slug to normalise the file name. jekyll does not like
_ in the file name.
"
  (interactive (list (read-file-name "file to copy: " org-download-image-dir)))
  (let* ((ext (file-name-extension file ))
	(base (file-name-base file))
	(dest-base (jekyll-make-slug base))
	(dest-file (expand-file-name (file-name-with-extension dest-base ext) jekyll-site-assets-dir)))
    (copy-file file dest-file)
    dest-file))

(defun yt/jekyll-copy-insert-image (file caption)
  (interactive (list (read-file-name "file to copy: " org-download-image-dir)
		     (read-string "Caption: ")))
  (yt/jekyll-insert-image (yt/jekyll-copy-to-assets-folder file) caption)
  )

Org-link

[2015-07-18 Sat 09:15]

magit

;; from https://lists.gnu.org/archive/html/emacs-orgmode/2009-08/msg00460.html
;; magit link in org-mode 
(defun org-magit-store-link ()
  "Store a link to a directory to open with magit."
  (when (eq major-mode 'magit-mode)
    (let* ((dir default-directory)
           (link (org-make-link "magit:" dir))
           (desc (abbreviate-file-name dir)))
      (org-store-link-props :type "magit" :link link :description desc)
      link)))
(defun org-magit-open (dir)
  "Follow a magit link to DIR."
  (magit-status dir))
(org-add-link-type "magit" 'org-magit-open nil)
(add-hook 'org-store-link-functions 'org-magit-store-link)

cross reference

[2015-07-21 Tue 10:59]

the idea is to include a link to post in org foramt, i.e.

2022-01-01-awesome-blog-post.org,

when export to jekyll, the link becomes

blog.yitang.uk/2022/01/01-awesome-blog-post

this works if category is null. to make it fully working, it needs to translate the filename into url. it is doable, but not strightfoward. because the url depends on few attributes of the blog setup, and front matter.

(defun org-jekyll-post-link-follow (path)
  (org-open-file-with-emacs path))

(defun org-jekyll-post-link-export (path desc format)
  (cond
   ((eq format 'html)
    (format "<a href=\"{%% post_url %s %%}\">%s</a>" path desc))))

(org-add-link-type "jekyll-post" 'org-jekyll-post-link-follow 'org-jekyll-post-link-export)

change the headline levels

I found the default font size for headline is too big. instead of tweaking the Jekyll/HTML, I did the trick of changing the level of headline while exporting to markdown. so the top level headline becomes 2nd, or 3rd, and therefore the fontsize is smaller.

use the bind keywords to set the variable org-md-toplevel-hlevel to either 2 or 3. it only effects the exporting.

according to the documentation, I also need to set org-export-allow-bind-keywords to true.

(setq org-export-allow-bind-keywords t)

See this question I asked: https://emacs.stackexchange.com/questions/76544/org-mode-export-to-markdown-respect-the-level-of-headlines

update post title and date

A handy function to update the title and date in the filename and also front matter, see http://yitang.uk/2023/12/18/jekyll-in-emacs-update-blog-post-title-and-date/

(defun yt/jekyll-update-post-title-and-date ()
  "it update the post filename with a new title and today's date.

it also update the font matter."
  (interactive)
  (let* ((title (read-string "new title: "))
	 (ext (file-name-extension (buffer-file-name)))  ;; as of now, the ext is always .org.

	 ;; the new filename is in the format of {date}-{new-title}.org
	 (filename (concat
		    (format-time-string "%Y-%m-%d-")
		    (file-name-with-extension (jekyll-make-slug title) ext)))

	 ;; normalise the filename. 
	 (filename (expand-file-name filename))

	 ;; keep the current point which we will go back to after editing.
	 (old-point (point))
	 )
    (rename-file (buffer-file-name) filename) ;; update the filename
    (kill-buffer nil)  ;; kill the current buffer, i.e. the old file.
    (find-file filename)  ;; open the new file.
    (set-window-point (selected-window) old-point)  ;; set the cursor to where i was in the old file.

    ;; udpate title field. 
    ;; note jekyll-yaml-escape is called to ensure the title field is yaml friendly.
    (yt/jekyll-update-frontmatter--title (jekyll-yaml-escape title))    
    )
  
  )

(defun yt/jekyll-update-frontmatter--title (title)
  "Update the title field in the front matter.

title case is used. 
"
  (let* ((old-point (point)))

    ;; ensure expand all the code/headers/drawers before editing.
    (org-show-all)

    ;; go to the first occurence of 'title:'.
    (goto-char (point-min))
    (search-forward "title: ")

    ;; update the title field with the new title.
    (beginning-of-line)
    (kill-line)
    (insert (format "title: %s" title))

    ;; ensure the title is in title case
    (xah-title-case-region-or-line (+ (line-beginning-position) 7) (line-end-position))

    ;; save and reset cursor back to where it started.
    (save-buffer)    
    (goto-char old-point)
    ))

update CUSTOM_ID property

so that the headline is used in URL instead of a randomly generated link, see my blog post: http://yitang.uk/2023/12/19/jekyll-in-emacs-align-headline-with-url/\[Jekyll in Emacs - Align URL with Headline]]

http://yitang.uk/2023/12/13/trx4-3970x/#org59a1ed6

vs.

http://yitang.uk/2023/12/13/trx4-3970x/#The%20Problem

alternatively, use NAME keyword, based on the documentation of org-html-prefer-user-labels, custom_id is used whenever it exists.

below I have a function to create/update the custom_id property whose value is literally the headline.

I tweaked the function API a bit so it is used as export hook just as an experiment. It is only ran when exporting using jekyll-md. it works on the exported buffer so it leaves the original org file unchanged.

(defun yt/jekyll--create-or-update-custom_id-field ()
  "so that the CUSTOM_ID property is the same as the headline and 
the URL reflects the headline.

by default, the URL to a section will be a random number."
  (org-entry-put nil "CUSTOM_ID" (org-entry-get nil "ITEM"))
  )

(defun yt/jekyll--create-or-update-custom_id-field-buffer (backend)
  (when (eq backend 'jekyll-md)
    (org-map-entries 'yt/jekyll--create-or-update-custom_id-field)
    ))

(add-hook 'org-export-before-processing-functions 'yt/jekyll--create-or-update-custom_id-field-buffer)

auctex

[2015-10-03 Sat 13:48]

http://tex.stackexchange.com/questions/50827/a-simpletons-guide-to-tex-workflow-with-emacs

http://tex.stackexchange.com/questions/29813/setup-synctex-with-emacs

http://www.stefanom.org/setting-up-a-nice-auctex-environment-on-mac-os-x/

;; AucTeX
(setq TeX-auto-save t)
(setq TeX-parse-self t)
(setq-default TeX-master nil)
(add-hook 'LaTeX-mode-hook 'visual-line-mode)
(add-hook 'LaTeX-mode-hook 'flyspell-mode)
(add-hook 'LaTeX-mode-hook 'LaTeX-math-mode)
(add-hook 'LaTeX-mode-hook 'turn-on-reftex)
(setq reftex-plug-into-AUCTeX t)
(setq TeX-PDF-mode t)
;; make latexmk available via C-c C-c
(add-hook 'LaTeX-mode-hook (lambda ()
                             (push
                              '("latexmk" "latexmk -pdf %s" TeX-run-TeX nil t
                                :help "Run latexmk on file")
                              TeX-command-list)))
(add-hook 'TeX-mode-hook '(lambda () (setq TeX-command-default "latexmk")))
(setq TeX-view-program-selection '((output-pdf "evince")))
(if (string= system-type "darwin")
    (setq TeX-view-program-selection '((output-dvi "open")
                                       (output-pdf "open")
                                       (output-html "open"))))

(add-hook 'LaTeX-mode-hook #'outline-minor-mode)
(use-package company-auctex)
(company-auctex-init)
;;; Brent.Longborough's .emacs

;; (global-visual-line-mode 1); Proper line wrapping
;; (global-hl-line-mode 1); Highlight current row
;; (show-paren-mode 1); Matches parentheses and such in every mode
;; (set-fringe-mode '(0 . 0)); Disable fringe because I use visual-line-mode
;; (set-face-background hl-line-face "#f2f1f0"); Same color as greyness in gtk
;; (setq inhibit-splash-screen t); Disable splash screen
;; (setq visible-bell t); Flashes on error
;; (setq calendar-week-start-day 1); Calender should start on Monday
;; (add-to-list 'default-frame-alist '(height . 59)); Default frame height.

;;; AUCTeX
;; Customary Customization, p. 1 and 16 in the manual, and http://www.emacswiki.org/emacs/AUCTeX#toc2
(setq TeX-parse-self t); Enable parse on load.
(setq TeX-auto-save t); Enable parse on save.
(setq-default TeX-master nil)

(setq TeX-PDF-mode t); PDF mode (rather than DVI-mode)

(add-hook 'TeX-mode-hook 'flyspell-mode); Enable Flyspell mode for TeX modes such as AUCTeX. Highlights all misspelled words.
(add-hook 'TeX-mode-hook
          (lambda () (TeX-fold-mode 1))); Automatically activate TeX-fold-mode.
(setq LaTeX-babel-hyphen nil); Disable language-specific hyphen insertion.

;; " expands into csquotes macros (for this to work babel must be loaded after csquotes).
(setq LaTeX-csquotes-close-quote "}"
      LaTeX-csquotes-open-quote "\\enquote{")

;; LaTeX-math-mode http://www.gnu.org/s/auctex/manual/auctex/Mathematics.html
(add-hook 'TeX-mode-hook 'LaTeX-math-mode)

;;; RefTeX
;; Turn on RefTeX for AUCTeX http://www.gnu.org/s/auctex/manual/reftex/reftex_5.html
(add-hook 'TeX-mode-hook 'turn-on-reftex)

(eval-after-load 'reftex-vars; Is this construct really needed?
  '(progn
     (setq reftex-cite-prompt-optional-args t); Prompt for empty optional arguments in cite macros.
     ;; Make RefTeX interact with AUCTeX, http://www.gnu.org/s/auctex/manual/reftex/AUCTeX_002dRefTeX-Interface.html
     (setq reftex-plug-into-AUCTeX t)
     ;; So that RefTeX also recognizes \addbibresource. Note that you
     ;; can't use $HOME in path for \addbibresource but that "~"
     ;; works.
     (setq reftex-bibliography-commands '("bibliography" "nobibliography" "addbibresource"))
                                        ;     (setq reftex-default-bibliography '("UNCOMMENT LINE AND INSERT PATH TO YOUR BIBLIOGRAPHY HERE")); So that RefTeX in Org-mode knows bibliography
     (setcdr (assoc 'caption reftex-default-context-regexps) "\\\\\\(rot\\|sub\\)?caption\\*?[[{]"); Recognize \subcaptions, e.g. reftex-citation
     (setq reftex-cite-format; Get ReTeX with biblatex, see http://tex.stackexchange.com/questions/31966/setting-up-reftex-with-biblatex-citation-commands/31992#31992
           '((?t . "\\textcite[]{%l}")
             (?a . "\\autocite[]{%l}")
             (?c . "\\cite[]{%l}")
             (?s . "\\smartcite[]{%l}")
             (?f . "\\footcite[]{%l}")
             (?n . "\\nocite{%l}")
             (?b . "\\blockcquote[]{%l}{}")))))

;; Fontification (remove unnecessary entries as you notice them) http://lists.gnu.org/archive/html/emacs-orgmode/2009-05/msg00236.html http://www.gnu.org/software/auctex/manual/auctex/Fontification-of-macros.html
(setq font-latex-match-reference-keywords
      '(
        ;; biblatex
        ("printbibliography" "[{")
        ("addbibresource" "[{")
        ;; Standard commands
        ;; ("cite" "[{")
        ("Cite" "[{")
        ("parencite" "[{")
        ("Parencite" "[{")
        ("footcite" "[{")
        ("footcitetext" "[{")
        ;; ;; Style-specific commands
        ("textcite" "[{")
        ("Textcite" "[{")
        ("smartcite" "[{")
        ("Smartcite" "[{")
        ("cite*" "[{")
        ("parencite*" "[{")
        ("supercite" "[{")
                                        ; Qualified citation lists
        ("cites" "[{")
        ("Cites" "[{")
        ("parencites" "[{")
        ("Parencites" "[{")
        ("footcites" "[{")
        ("footcitetexts" "[{")
        ("smartcites" "[{")
        ("Smartcites" "[{")
        ("textcites" "[{")
        ("Textcites" "[{")
        ("supercites" "[{")
        ;; Style-independent commands
        ("autocite" "[{")
        ("Autocite" "[{")
        ("autocite*" "[{")
        ("Autocite*" "[{")
        ("autocites" "[{")
        ("Autocites" "[{")
        ;; Text commands
        ("citeauthor" "[{")
        ("Citeauthor" "[{")
        ("citetitle" "[{")
        ("citetitle*" "[{")
        ("citeyear" "[{")
        ("citedate" "[{")
        ("citeurl" "[{")
        ;; Special commands
        ("fullcite" "[{")))

(setq font-latex-match-textual-keywords
      '(
        ;; biblatex brackets
        ("parentext" "{")
        ("brackettext" "{")
        ("hybridblockquote" "[{")
        ;; Auxiliary Commands
        ("textelp" "{")
        ("textelp*" "{")
        ("textins" "{")
        ("textins*" "{")
        ;; supcaption
        ("subcaption" "[{")))

(setq font-latex-match-variable-keywords
      '(
        ;; amsmath
        ("numberwithin" "{")
        ;; enumitem
        ("setlist" "[{")
        ("setlist*" "[{")
        ("newlist" "{")
        ("renewlist" "{")
        ("setlistdepth" "{")
        ("restartlist" "{")))

Hydra

[2015-06-22 Mon 14:11]

It is a good practise to group all the file management related commands together using hydra.

(use-package hydra)
(defhydra hydra-file-management (:color red
                                        :hint nil)
  "
_o_pen file
_O_pen file as Sudo user 
copy file _P_ath to kill ring
copy file _p_ath relative to project dir to kill ring
_r_ename buffer-visiting file 
_d_elete buffer-visiting file
open with _e_xternal application
_g_it sync"
  ("o" find-file)
  ("O" yt/sudo-find-file)
  ("P" yt/copy-full-path-to-kill-ring)
  ("p" yt/copy-rel-path-to-kill-ring)  
  ("r" yt/rename-current-buffer-file)
  ("c" yt/copy-file-to)
  ("d" yt/delete-this-buffer-and-file)
  ("e" prelude-open-with)
  ("g" yt/git-up))
(global-set-key [f3] 'hydra-file-management/body)
(defhydra yt-hydra/help (:color blue :hint nil)
  "
_f_unction: Documentation for a function
_v_ariable: Documentation for a variable
_i_nfo: info mode 
_d_ictionary: search meaning of a word
_V_ocabulary: add to or visit vocablary
"
  ("f" describe-function)
  ("v" describe-variable)
  ("d" osx-dictionary-search-input)
  ;; ("s" get-synonyms)
  ("i" helm-info)
  ;; ("G" helm-google-suggest)
  ("s" synosaurus-lookup)
  ;; ("d" voca-builder/search-popup)
  ("V" yt/add-vocabulary)
  )
(global-set-key (kbd "<f1>") 'yt-hydra/help/body)
;;; package ---  Query thesaurus.com for synonyms of a given word.

;;; Copyright (C) 2021 by Anselm Coogan
;;; URL: https://github.com/AnselmC/thesaurus
;;; Version: 0.1

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

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

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

;;; Commentary:

;;; Uses the Elisp request library (https://github.com/tkf/emacs-request) and the thesaurus.com API to fetch synonyms

;;; Code:

(require 'cl-lib)
(require 'request)

(defun parse-synonyms-in-response (payload)
  "Parse JSON PAYLOAD to extract synonyms from response."
  (let* ((data (assoc-default 'data payload))
         (definition-data (if data
                              (assoc-default 'definitionData data)
                            '()))
         (definitions-list (if definition-data
                               (assoc-default 'definitions definition-data)
                             '()))
         (definitions (if definitions-list
                          (aref definitions-list 0)
                        '()))
         (synonyms (if definitions
                       (cl-map 'vector
                               #'(lambda (e) (assoc-default 'term e))
                               (assoc-default 'synonyms definitions))
                     (vector))))
    synonyms))

(defun ask-thesaurus-for-synonyms (word)
  "Ask thesaurus.com for synonyms for WORD and return vector of synonyms (possibly empty)."
  (let* ((thesaurus-base-url "https://tuna.thesaurus.com/pageData/")
         (request-string (concat thesaurus-base-url word))
         (response (request-response-data (request request-string
                                            :parser 'json-read
                                            :sync t))))
    (if response
        (parse-synonyms-in-response response)
      (vector))))

(defun get-synonyms()
  "Interactively get synonyms for symbol at active region or point."
  (interactive)
  (let* ((bounds (if (use-region-p)
                     (cons (region-beginning) (region-end))
                     (bounds-of-thing-at-point 'symbol)))
         (word (buffer-substring-no-properties (car bounds) (cdr bounds)))
         (replace-text (completing-read
                        (format "Select synonym for %S: " word)
                        (append (ask-thesaurus-for-synonyms word) '()))))
    (when bounds
      (delete-region (car bounds) (cdr bounds))
      (insert replace-text))))

(provide 'thesaurus)
;;; thesaurus.el ends here

org-mode

[2021-06-07 Mon 12:40]

use hydra to define a function that use most frequently

;; https://github.com/abo-abo/hydra/wiki/Org-clock
(defhydra hydra-org-clock (:color blue :hint nil)
  "
Clock       In/out^     ^Edit^   ^Summary     Doc(_?_)
---------------------------------------------------
            _i_n         _e_dit   
_h_istory   _c_ontinue   _q_uit   _d_isplay
_g_oto      _o_ut        ^ ^      _r_eport
      "
  ("i" org-clock-in)
  ("o" org-clock-out)
  ("c" org-clock-in-last)
  ("e" org-clock-modify-effort-estimate)
  ("q" org-clock-cancel)
  ("g" org-clock-goto)
  ("d" org-clock-display)
  ("r" org-clock-report)
  ;; ("j" org-clock-jump-to-current-clock)
  ("h" yt/org-clock-in-select)
  ("?" (org-info "Clocking commands")))

(global-set-key (kbd "<f11>") 'hydra-org-clock/body)

(defun yt/org-clock-in-select ()
  (interactive)
  (setq current-prefix-arg '(4)) ;; C-u, 
  (call-interactively 'org-clock-in))

Emacs Lisp Programming

[2015-07-05 Sun 19:46]

Python

[2015-07-05 Sun 19:49]
(use-package pyvenv)
(add-hook 'python-mode-hook 'flyspell-prog-mode)
(add-hook 'python-mode-hook 'elpy-mode)
(setq python-fill-docstring-style 'django)

(use-package elpy
  :ensure t
  :init)

(elpy-enable)
;; ;; (elpy-use-ipython "ipython3")
;; (setq elpy-rpc-python-command "python3")
;; (global-set-key (kbd "M-*") 'pop-tag-mark)
;; ;; (setq elpy-test-discover-runner-command '("python3" "-m" "unittest"))
;; (setq elpy-test-pytest-runner-command '("py.test" "--maxfail=100" "-s"))
;; (setq elpy-rpc-backend "jedi")

;; make elpy more like ESS
(define-key elpy-mode-map (kbd "<C-return>") 'elpy-shell-send-statement-and-step)
(define-key elpy-mode-map (kbd "<C-c C-f>") 'python-shell-send-defun)
(define-key elpy-mode-map (kbd "<C-c C-b>") 'elpy-shell-send-region-or-buffer)

;; for new elpy version
(setq elpy-shell-starting-directory 'project-root)  ;; set to project directory.
(setq elpy-rpc-virtualenv-path 'current)   ;; rpc is in the python dev env

Use Jupyter for python interactive shell.

(setq python-shell-interpreter "jupyter"
      python-shell-interpreter-args "console --simple-prompt"
      python-shell-prompt-detect-failure-warning nil)
(require 'python)
(add-to-list 'python-shell-completion-native-disabled-interpreters
             "jupyter")
(setq python-shell-interpreter "ipython"
      python-shell-interpreter-args "-i --simple-prompt")
(add-to-list 'python-shell-completion-native-disabled-interpreters
             "ipython")

TODO: replace this by using native elpy function.

this allows me to run multiple python process and switch between them.

 (setq elpy-dedicated-shells nil)   ; Ensure no conflict with dedicated shells

 (defvar elpy-shell-python-shell-names '("Python")
	  "List of existing python shell names.")

 ;; (define-key elpy-mode-map (kbd "C-c C-s") 'elpy-shell-set-local-shell)

lsp for python

[2020-08-22 Sat 19:03]

replace elpy by LSP mode for python dev..

 ;; this requires pip install -U jedi-language-server
 (use-package lsp-mode
   :config
   (add-hook 'python-mode-hook 'lsp))
 ;; (use-package company-lsp)
 (use-package lsp-ui)


 ;; (use-package lsp-jedi
 ;;   :ensure t
 ;;   :config
 ;;   (with-eval-after-load "lsp-mode"
 ;;     (add-to-list 'lsp-disabled-clients 'pyls)
 ;;     (add-to-list 'lsp-enabled-clients 'jedi)))



 (use-package lsp-pyright
		:ensure t
		:hook (python-mode . (lambda ()
				       (require 'lsp-pyright)
				       (lsp))))  ; or lsp-deferred

Sphinx-doc for documentation

[2016-06-03 Fri 15:33]

(use-package sphinx-doc
  :ensure t
  :hook (python-mode . (lambda ()
                         (require 'sphinx-doc)
                         (sphinx-doc-mode t))))

(cl-defstruct sphinx-doc-doc
  (summary "FIXME: briefly describe function") ; summary line that fits on the first line
  before-fields                                ; list of comments before fields
  after-fields                                 ; list of comments after fields
  fields)                                      ; list of field objects

jupyter

(use-package jupyter)

Refile

quickly filter out non-work tasks in org-agenda.

(defun yt/filter-life-agenda (tag)
  (concat "-" "life"))
(defun yt/filter-office-agenda (tag)
  (concat "-" "@office"))
(if (eq system-type 'darwin)
    (setq org-agenda-auto-exclude-function 'yt/filter-office-agenda)
  (setq org-agenda-auto-exclude-function 'yt/filter-life-agenda))

open this gnome-terminal here

(defun yt/open-terminal ()
  (interactive)
  (shell-command (concat "gnome-terminal "
                         "--working-directory="
                         (file-truename default-directory)
                         )))
;; (global-set-key (kbd "<f5>") 'yt/open-terminal)

use consult-line to replace default isearch

(global-set-key "\C-s" 'consult-line)

use snakemake-mode for smake file.

(use-package snakemake-mode
  :ensure t
  :mode ("\\.sfile\\'" "\\.Snakemake\\'"))
(defun yt/sh-chunk-args ()
(interactive)
(replace-string " -" " \\\n -")
)

insert git sha1 value into current point.

(defun yt/insert-git-hash-value ()
  (interactive)
  (insert (shell-command-to-string (concat "git rev-parse HEAD"))))
(global-set-key (kbd "<f9> s") 'yt/insert-git-hash-value)
(use-package which-key)
(which-key-mode)

Treemacs

copied following configuration from https://github.com/Alexander-Miller/treemacs with no additional editing.

(use-package treemacs
  :ensure t
  :defer t
  :init
  (with-eval-after-load 'winum
    (define-key winum-keymap (kbd "M-0") #'treemacs-select-window))
  :config
  (progn
    (setq treemacs-collapse-dirs                   (if treemacs-python-executable 3 0)
          treemacs-deferred-git-apply-delay        0.5
          treemacs-directory-name-transformer      #'identity
          treemacs-display-in-side-window          t
          treemacs-eldoc-display                   'simple
          treemacs-file-event-delay                5000
          treemacs-file-extension-regex            treemacs-last-period-regex-value
          treemacs-file-follow-delay               0.2
          treemacs-file-name-transformer           #'identity
          treemacs-follow-after-init               t
          treemacs-expand-after-init               t
          treemacs-find-workspace-method           'find-for-file-or-pick-first
          treemacs-git-command-pipe                ""
          treemacs-goto-tag-strategy               'refetch-index
          treemacs-indentation                     2
          treemacs-indentation-string              " "
          treemacs-is-never-other-window           nil
          treemacs-max-git-entries                 5000
          treemacs-missing-project-action          'ask
          treemacs-move-forward-on-expand          nil
          treemacs-no-png-images                   nil
          treemacs-no-delete-other-windows         t
          treemacs-project-follow-cleanup          nil
          treemacs-persist-file                    (expand-file-name ".cache/treemacs-persist" "~/matrix/tools/.emacs.d/")
          treemacs-position                        'left
          treemacs-read-string-input               'from-child-frame
          treemacs-recenter-distance               0.1
          treemacs-recenter-after-file-follow      nil
          treemacs-recenter-after-tag-follow       nil
          treemacs-recenter-after-project-jump     'always
          treemacs-recenter-after-project-expand   'on-distance
          treemacs-litter-directories              '("/node_modules" "/.venv" "/.cask")
          treemacs-show-cursor                     nil
          treemacs-show-hidden-files               t
          treemacs-silent-filewatch                nil
          treemacs-silent-refresh                  nil
          treemacs-sorting                         'alphabetic-asc
          treemacs-select-when-already-in-treemacs 'move-back
          treemacs-space-between-root-nodes        t
          treemacs-tag-follow-cleanup              t
          treemacs-tag-follow-delay                1.5
          treemacs-text-scale                      nil
          treemacs-user-mode-line-format           nil
          treemacs-user-header-line-format         nil
          treemacs-wide-toggle-width               70
          treemacs-width                           35
          treemacs-width-increment                 1
          treemacs-width-is-initially-locked       t
          treemacs-workspace-switch-cleanup        nil)

    ;; The default width and height of the icons is 22 pixels. If you are
    ;; using a Hi-DPI display, uncomment this to double the icon size.
    ;;(treemacs-resize-icons 44)

    (treemacs-follow-mode t)
    (treemacs-filewatch-mode t)
    (treemacs-fringe-indicator-mode 'always)

    (pcase (cons (not (null (executable-find "git")))
                 (not (null treemacs-python-executable)))
      (`(t . t)
       (treemacs-git-mode 'deferred))
      (`(t . _)
       (treemacs-git-mode 'simple)))

    (treemacs-hide-gitignored-files-mode nil))
  ;; :bind
  ;; (:map global-map
  ;;       ("M-0"       . treemacs-select-window)
  ;;       ("C-x t 1"   . treemacs-delete-other-windows)
  ;;       ("C-x t t"   . treemacs)
  ;;       ("C-x t d"   . treemacs-select-directory)
  ;;       ("C-x t B"   . treemacs-bookmark)
  ;;       ("C-x t C-t" . treemacs-find-file)
  ;;       ("C-x t M-t" . treemacs-find-tag))
  )

(use-package treemacs-evil
  :after (treemacs evil)
  :ensure t)

(use-package treemacs-projectile
  :after (treemacs projectile)
  :ensure t)

(use-package treemacs-icons-dired
  :hook (dired-mode . treemacs-icons-dired-enable-once)
  :ensure t)

(use-package treemacs-magit
  :after (treemacs magit)
  :ensure t)

;; (use-package treemacs-persp ;;treemacs-perspective if you use perspective.el vs. persp-mode
;;   :after (treemacs persp-mode) ;;or perspective vs. persp-mode
;;   :ensure t
;;   :config (treemacs-set-scope-type 'Perspectives))


;; (use-package treemacs-tab-bar ;;treemacs-tab-bar if you use tab-bar-mode
;;   :after (treemacs)
;;   :ensure t
;;   :config (treemacs-set-scope-type 'Tabs))

nov-mode for reading epub in Emacs

https://depp.brause.cc/nov.el/

(add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode))

(setq nov-text-width t)
(setq visual-fill-column-center-text t)

(add-hook 'nov-mode-hook 'visual-line-mode)  ;; word-wrap and also use visual-line, not logical (actual) line.
(add-hook 'nov-mode-hook 'visual-fill-column-mode)  ;; center the text, looks good. have to call it again when change window size.
(add-hook 'nov-mode-hook '(lambda () (blink-cursor-mode 0)))  ;; blinking cursor is distracting.

(defun yt/read-mode ()
  (visual-line-mode)
  (visual-fill-column-mode))

(setq visual-fill-column-width 120)
(setq line-number-display-limit-width 2000000)  ;; https://emacs.stackexchange.com/questions/3824/what-piece-of-code-in-emacs-makes-line-number-mode-print-as-line-number-i

org-download

(use-package org-download)
(add-hook 'dired-mode-hook 'org-download-enable)
(setq-default org-download-image-dir "~/Pictures/org-download")
(setq-default org-download-heading-lvl nil)   ;; flat file systme, otherwise, would be confusing.

(if (eq system-type 'darwin)
    (setq org-download-screenshot-method "screencapture -i %s"))

org-roam

;; (setq emacsql-sqlite-executable "/usr/bin/sqlite3")
;; (use-package emacsql-sqlite-builtin)
;; ;; (setq org-roam-database-connector 'sqlite3)
;; (setq org-roam-database-connector 'sqlite-builtin)
;; (setq org-roam-database-connector 'sqlite)

(use-package org-roam
  :ensure t
  :init
  (setq org-roam-v2-ack t)
  :custom
  (org-roam-completion-everywhere t)
  (org-roam-dailies-capture-templates
    '(("d" "default" entry "* %<%H:%M>: %?"
       :if-new (file+head "%<%Y-%m-%d>.org" "#+title: %<%Y-%m-%d>\n"))))
  (org-roam-dailies-directory "daily")

  ;; ; change the timestmap aslightly..
  ;; (org-roam-capture-templates
  ;;  '(("d" "default" plain "%?"
  ;;    :target (file+head "%<%Y%m%d_%H%M%S>-${slug}.org"
  ;;                       "#+title: ${title}\n")
  ;;    :unnarrowed t)))

  :bind (("C-c n l" . org-roam-buffer-toggle)
         ("C-c n f" . org-roam-node-find)
         ("C-c n i" . org-roam-node-insert)
         :map org-mode-map
         ("C-M-i" . completion-at-point)
         :map org-roam-dailies-map
         ("Y" . org-roam-dailies-capture-yesterday)
         ("T" . org-roam-dailies-capture-tomorrow))
  :bind-keymap
  ("C-c n d" . org-roam-dailies-map)
  :config
  (require 'org-roam-dailies) ;; Ensure the keymap is available
  (org-roam-db-autosync-mode))

(defun yt/dailies ()
  "global function for creating daries"
  (interactive)
  (let ((org-roam-directory "~/git/org/diary"))
    (org-roam-dailies-capture-today)
    )
  )

gpg encryption

(use-package epa-file
    :ensure nil 
    :config
    (setq epa-file-encrypt-to '("[email protected]"))
    :custom
    (epa-file-select-keys nil)   ;; seems there's a bug in gpg. it always asks for which key.
    )

(setq epa-file-encrypt-to "[email protected]")

(setq epg-gpg-program "gpg2")
(require 'epa-file)
(epa-file-enable) ;; decrypt the buffer, may or may not ask for passphase.
;; ;; to open gpg file in binary mode, i.e. no decryptions, use
;; (epa-file-disable)
(setq password-cache-expiry (* 15 60))
;; loopback means to re-direct the dialog to the caller, here, it
;; means passpharse via Emacs's minibuffer instead of external
;; program.
(setq epa-pinentry-mode 'loopback)

.dir-locals.el

by default, ask for setting up local dir variables. this is useful in debugging.

(setq enable-local-variables t)

dired-rsync

called rsync in dired. useful in copy files between servers.

(use-package dired-rsync
  :demand t
  :after dired
  :bind (:map dired-mode-map ("r" . dired-rsync))
  :config (add-to-list 'mode-line-misc-info '(:eval dired-rsync-modeline-status 'append)))

yaml file

make yaml file a bit easier to work with.
(use-package yaml-mode
  :ensure t
  :mode ("\\.yaml\\'" "\\.yml\\'")
  :bind (("C-m" . newline-and-indent))
  )

if i do a lot of yaml file editing, use yaml-pro.

(use-package yaml-pro
  :ensure t)

org-mode

(setq org-export-with-broken-links t) ;; broken links are fine in exporting. 
;; (setq org-latex-prefer-user-labels t)  ;; fix labels, otherwise, randomly generated, not git friendly.

;; somehow relative path doesn't work in osx when export org to other format. so use abslute path.
(setq org-download-abbreviate-filename-function #'expand-file-name)
(setq org-link-file-path-type 'absolute)
(defun yt/execute-src-org-file (filename)
 "run the src babel block in a given file"
  (interactive)
  (with-current-buffer (get-buffer-create (concat "*buffer:" filename))
    (erase-buffer)
    (insert-file-contents filename)
    (let ((default-directory (file-name-directory filename)))
      (org-babel-execute-buffer))
    )
  )

compilation

for the visiting buffer, create a new compilation buffer if not exists, otherwise, switch to.

(defun yt/bind-to-compliation ()
  "bind current file-visiting buffer to a dedicated compilation buffer"
  (interactive)
  (let* ((filename (buffer-file-name))
	 (default-directory (file-name-directory filename))
	 (output-buffer-name (concat "*compilation*:" filename)))
    (compile "ls -l")
    (when (get-buffer output-buffer-name)
      (kill-buffer output-buffer-name))
    (with-current-buffer "*compilation*"
      (rename-buffer output-buffer-name)))
  )
(setq compilation-scroll-output 'first-error)


(defun yt/kill-buffer (name)
  "if buffer exists, kill, otherwise, do nothing"
  (when (get-buffer name)
    (kill-buffer name)
    ))

(defun yt/compile (output-id cmd &optional working-directory)
  (let ((default-directory (or working-directory default-directory))
	(buffer-name (concat "*compilation*" output-id)))
    (progn
      (compile cmd)
      (when (get-buffer buffer-name)
	(kill-buffer buffer-name))
      (with-current-buffer "*compilation*" 
	(rename-buffer buffer-name)))))

fix colour in compilation buffer - otherwise, there’s some strange characters in the compilation buffer

;;;; colorize output in compile buffer
(require 'ansi-color)
(defun colorize-compilation-buffer ()
  (ansi-color-apply-on-region compilation-filter-start (point-max)))
(add-hook 'compilation-filter-hook 'colorize-compilation-buffer)

ibuffer -

(global-set-key (kbd "M-i") 'ibuffer)
(setq ibuffer-saved-filter-groups
      '(("home"
	 ("shell" (mode . shell-mode))
	 ;; ("emacs-config" (or (filename . ".emacs.d")
	 ;; 		     (filename . "emacs-config")))
         ;; ("martinowen.net" (filename . "martinowen.net"))
	 ("compilation" (mode . compilation-mode))
	 ("dired" (mode . dired-mode))
	 ("planner" (or
		     (name . "^\\*Calendar\\*$")
		     (name . "^\\*Org Agenda\\*")))
	 ("python" (mode . python-mode))
	 ("Org" (or (mode . org-mode)
		    (filename . "OrgMode")))
         ;; ("code" (filename . "code"))
	 ;; ("Web Dev" (or (mode . html-mode)
	 ;; 		(mode . css-mode)))
	 ;; ("Subversion" (name . "\*svn"))
	 ("Magit" (name . "\\*magit"))
	 ;; ("Magit2" (name . "magit"))
	 ;; ("ERC" (mode . erc-mode))
	 ;; ("Help" (or (name . "\*Help\*")
	 ;; 	     (name . "\*Apropos\*")
	 ;; 	     (name . "\*info\*")))
	 )))

(setq ibuffer-expert t)
(setq ibuffer-show-empty-filter-groups nil)

(add-hook 'ibuffer-mode-hook
	  '(lambda ()
	     (ibuffer-auto-mode 1)
	     (ibuffer-switch-to-saved-filter-groups "home")))
(keymap-set ibuffer-mode-map "M-o" nil)


;; nearly all of this is the default layout
(setq ibuffer-formats 
      '((mark modified read-only " "
              (name 30 30 :left :elide) ; change: 30s were originally 18s
              " "
              (size 9 -1 :right)
              " "
              (mode 16 16 :left :elide)
              " " filename-and-process)
        (mark " "
              (name 16 -1)
              " " filename)))

bookmark

flush bookmarks immediately.

(setq bookmark-save-flag 1)  ; save bookmark file everytime.

Programming Mode

highlight TODO, FIXME or other in programming mode.

(add-hook 'prog-mode-hook
          (lambda ()
            (font-lock-add-keywords nil
                                    '(("\\<\\(YT\\|FIXME\\|TODO\\|BUG\\):" 1 font-lock-warning-face t)))))

we usually have long scripts, and in Subimetext, one cold folder and unfolder a function. in Emacs, this feature could be extended to furture, by define folder-characters. at this statge, I tented to used the deafault, I.e. folder functions only. in the folliwng setting, I could press F3 to folder/unfolder a function, C-F3 or S-F3 to folder/unfolder all functions.

One potentially solution is to use outshine package, to show/hide the whole section.

;; (add-hook 'prog-mode-hook 'hs-minor-mode)
;; (defalias 'fold-toggle 'hs-toggle-hiding)
;; (global-set-key (kbd "<f4>") 'hs-toggle-hiding)
;; (global-set-key (kbd "S-<f4>") 'hs-show-all) ;; S->show 
;; (global-set-key (kbd "C-<f4>") 'hs-hide-all) 
;; ;;   hs-hide-block                      C-c @ C-h
;; ;;   hs-show-block                      C-c @ C-s
;; ;;   hs-hide-all                        C-c @ C-M-h
;; ;;   hs-show-all                        C-c @ C-M-s
;; ;;   hs-hide-level                      C-c @ C-l
;; ;;   hs-toggle-hiding 
;; ;;   hs-mouse-toggle-hiding             [(shift mouse-2)]
;; ;;   hs-hide-initial-comment-block
(global-set-key (kbd "C-d") 'comment-region) ;; overwite delete-char 
(global-set-key (kbd "C-S-d") 'uncomment-region)

(defhydra hydra-fold (:pre (hs-minor-mode 1))
  "fold"
  ("t" hs-toggle-hiding "toggle")
  ("s" hs-show-all "hide-all")
  ("h" hs-hide-all "show-all")
  ("q" nil "quit"))
(global-set-key (kbd "<f4>") 'hydra-fold/body)

use subword-mode then ThisPhase has two word, and I can use C-DEL it will remove the Phase and left This. Very useful in CamerCase.

(subword-mode 1)

Highlights the text/code after 80 characters, a visual guide to avoid writing long code.

(use-package whitespace
  :ensure t
  :config
  (setq whitespace-line-column 120)
  (setq whitespace-style '(face lines-tail))
  :hook (prog-mode-hook . whitespace-mode))

Rainbow-delimiters. constantly have problem with package, and tired of fixing it, so I turned it off at this stage.

(use-package rainbow-delimiters
  :ensure t
  :hook (prog-mode-hook . rainbow-delimiters-mode))
(show-paren-mode t) ;; highlight matched parentheses

use f8 to remove the R process buffer.

(defun yt/prog-previous-output-region ()
  "return start/end points of previous output region"
  (save-excursion
    (beginning-of-line)
    (setq sp (point))
    (comint-previous-prompt 1)
    (next-line)
    (beginning-of-line)
    (setq ep (point))
    (cons sp ep)))
(defun yt/prog-kill-output-backwards ()
  (interactive)
  (save-excursion
    (let ((reg (yt/prog-previous-output-region)))
      (delete-region (car reg) (cdr reg))
      (goto-char (cdr reg))
      (insert "*** output flushed ***\n"))))
;; (global-set-key (kbd "<f8>") 'yt/prog-kill-output-backwards)

vertico, orderless etc.

(use-package vertico
  :bind (("C-x M-r" . vertico-repeat)
         :map vertico-map
         ("C-l" . vertico-directory-delete-word)
         ("M-g" . vertico-multiform-grid)
         ("M-q" . vertico-multiform-flat))
  :init (vertico-mode 1)
  :config (progn
            (add-hook 'minibuffer-setup-hook #'vertico-repeat-save)
            (vertico-mouse-mode 1)
            (vertico-multiform-mode 1)
            (setq vertico-multiform-categories '((consult-grep buffer))
                  vertico-multiform-commands '((tmm-menubar flat)
                                               (tmm-shortcut flat)))))

(use-package orderless
  :after vertico
  :config (progn
            (setq orderless-matching-styles '(orderless-regexp
                                              orderless-initialism
                                              orderless-prefixes)
                  orderless-component-separator #'orderless-escapable-split-on-space)

            ;; Use the built-in "partial-completion" style to complete
            ;; file inputs such as "/e/ni/co.nix" into
            ;; "/etc/nixos/configuration.nix".  The "basic" style is
            ;; needed to support the hostname completion in the TRAMP
            ;; inputs such as "/sshx:HOSTNAME".
            (setq completion-category-defaults nil
                  completion-category-overrides '((file (styles basic partial-completion))))

            (setq completion-styles '(orderless))

            (defun vifon/orderless-without-if-bang (pattern index total)
              (when (string-prefix-p "!" pattern)
                `(orderless-without-literal . ,(substring pattern 1))))
            (defun vifon/orderless-literal-if-equal (pattern index total)
              (when (string-suffix-p "=" pattern)
                `(orderless-literal . ,(substring pattern 0 -1))))
            (setq orderless-style-dispatchers '(vifon/orderless-without-if-bang
                                                vifon/orderless-literal-if-equal))))

(use-package embark
  :bind (("C-c o" . embark-act)
         ("C-."   . embark-act)
         :map minibuffer-local-map
         ("M-o"   . embark-act)
         :map embark-command-map
         ;; Unbind the dangerous `global-set-key' and `local-set-key'
         ;; actions.  It's far too easy to accidentally bind over some
         ;; `self-insert-command' binding or even over
         ;; \\[keyboard-quit].
         ("g" . nil)
         ("l" . nil))
  :config (progn
            (setq embark-mixed-indicator-delay 2)

            ;; Make the eval action editable.  Evaluating code
            ;; in-place is simple enough without Embark, if I invoke
            ;; it with Embark, I almost definitely want to edit the
            ;; expression beforehand.  And even if not, I can
            ;; just confirm.
            (cl-pushnew 'embark--allow-edit
                        (alist-get 'pp-eval-expression embark-target-injection-hooks))

            ;; Reload the project list after using
            ;; C-u `embark-act' with `project-forget-project'.
            (cl-pushnew 'embark--restart
                        (alist-get 'project-forget-project embark-post-action-hooks))

            (defun embark-act-with-eval (expression)
              "Evaluate EXPRESSION and call `embark-act' on the result."
              (interactive "sExpression: ")
              (with-temp-buffer
                (let ((expr-value (eval (read expression))))
                  (insert (if (stringp expr-value)
                              expr-value
                            (format "%S" expr-value))))
                (embark-act)))

            (dolist (keymap (list embark-variable-map embark-expression-map))
              (define-key keymap (kbd "v") #'embark-act-with-eval))

            ;; Source: https://github.com/oantolin/embark/wiki/Additional-Actions#attaching-file-to-an-email-message
            (autoload 'gnus-dired-attach "gnus-dired" nil t)
            (defun embark-attach-file (file)
              "Attach FILE to an email message."
              (interactive "fAttach: ")
              (gnus-dired-attach (list file)))
            (bind-key "a" #'embark-attach-file embark-file-map)))

(use-package embark-consult
  :after (embark consult))

(use-package marginalia
  :after vertico
  :demand t                     ; :demand applies to :bind but not
                                ; :after.  We want to eagerly load
                                ; marginalia once vertico is loaded.
  :bind (:map minibuffer-local-map
         ("C-o" . marginalia-cycle))
  :config (marginalia-mode 1))


(use-package corfu
  :init (global-corfu-mode 1))


;;; https://with-emacs.com/posts/tutorials/customize-completion-at-point/
(autoload 'ffap-file-at-point "ffap")
(add-hook 'completion-at-point-functions
          (defun complete-path-at-point+ ()
            (let ((fn (ffap-file-at-point))
                  (fap (thing-at-point 'filename)))
              (when (and (or fn (equal "/" fap))
                         (save-excursion
                           (search-backward fap (line-beginning-position) t)))
                (list (match-beginning 0)
                      (match-end 0)
                      #'completion-file-name-table :exclusive 'no))))
          'append)

;;; Add prompt indicator to `completing-read-multiple'.
;;; We display [CRM<separator>], e.g., [CRM,] if the separator is a comma.
;;;
;;; Taken from the Vertico docs.
(defun crm-indicator (args)
  (cons (format "[CRM%s] %s"
                (replace-regexp-in-string
                 "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
                 crm-separator)
                (car args))
        (cdr args)))
(advice-add #'completing-read-multiple :filter-args #'crm-indicator)

(setq enable-recursive-minibuffers t)
(minibuffer-depth-indicate-mode 1)

;;; Use the completing-read UI for the M-tab completion unless
;;; overridden (for example by `corfu').
(setq-default completion-in-region-function
              (lambda (&rest args)
                (apply (if vertico-mode
                           #'consult-completion-in-region
                         #'completion--in-region)
                       args)))

consult

;; Example configuration for Consult
(use-package consult
  ;; Replace bindings. Lazily loaded due by `use-package'.
  :bind (;; C-c bindings in `mode-specific-map'
         ("C-c M-x" . consult-mode-command)
         ("C-c h" . consult-history)
         ("C-c k" . consult-kmacro)
         ("C-c m" . consult-man)
         ("C-c i" . consult-info)
         ([remap Info-search] . consult-info)
         ;; C-x bindings in `ctl-x-map'
         ("C-x M-:" . consult-complex-command)     ;; orig. repeat-complex-command
         ("C-x b" . consult-buffer)                ;; orig. switch-to-buffer
         ("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window
         ("C-x 5 b" . consult-buffer-other-frame)  ;; orig. switch-to-buffer-other-frame
         ("C-x t b" . consult-buffer-other-tab)    ;; orig. switch-to-buffer-other-tab
         ("C-x r b" . consult-bookmark)            ;; orig. bookmark-jump
         ("C-x p b" . consult-project-buffer)      ;; orig. project-switch-to-buffer
         ;; Custom M-# bindings for fast register access
         ("M-#" . consult-register-load)
         ("M-'" . consult-register-store)          ;; orig. abbrev-prefix-mark (unrelated)
         ("C-M-#" . consult-register)
         ;; Other custom bindings
         ("M-y" . consult-yank-pop)                ;; orig. yank-pop
         ;; M-g bindings in `goto-map'
         ("M-g e" . consult-compile-error)
         ("M-g f" . consult-flymake)               ;; Alternative: consult-flycheck
         ("M-g g" . consult-goto-line)             ;; orig. goto-line
         ("M-g M-g" . consult-goto-line)           ;; orig. goto-line
         ("M-g o" . consult-outline)               ;; Alternative: consult-org-heading
         ("M-g m" . consult-mark)
         ("M-g k" . consult-global-mark)
         ("M-g i" . consult-imenu)
         ("M-g I" . consult-imenu-multi)
         ;; M-s bindings in `search-map'
         ("M-s d" . consult-find)                  ;; Alternative: consult-fd
         ("M-s c" . consult-locate)
         ("M-s g" . consult-grep)
         ("M-s G" . consult-git-grep)
         ("M-s r" . consult-ripgrep)
         ("M-s l" . consult-line)
         ("M-s L" . consult-line-multi)
         ("M-s k" . consult-keep-lines)
         ("M-s u" . consult-focus-lines)
         ;; Isearch integration
         ("M-s e" . consult-isearch-history)
         :map isearch-mode-map
         ("M-e" . consult-isearch-history)         ;; orig. isearch-edit-string
         ("M-s e" . consult-isearch-history)       ;; orig. isearch-edit-string
         ("M-s l" . consult-line)                  ;; needed by consult-line to detect isearch
         ("M-s L" . consult-line-multi)            ;; needed by consult-line to detect isearch
         ;; Minibuffer history
         :map minibuffer-local-map
         ("M-s" . consult-history)                 ;; orig. next-matching-history-element
         ("M-r" . consult-history))                ;; orig. previous-matching-history-element

  ;; Enable automatic preview at point in the *Completions* buffer. This is
  ;; relevant when you use the default completion UI.
  :hook (completion-list-mode . consult-preview-at-point-mode)

  ;; The :init configuration is always executed (Not lazy)
  :init

  ;; Optionally configure the register formatting. This improves the register
  ;; preview for `consult-register', `consult-register-load',
  ;; `consult-register-store' and the Emacs built-ins.
  (setq register-preview-delay 0.5
        register-preview-function #'consult-register-format)

  ;; Optionally tweak the register preview window.
  ;; This adds thin lines, sorting and hides the mode line of the window.
  (advice-add #'register-preview :override #'consult-register-window)

  ;; Use Consult to select xref locations with preview
  (setq xref-show-xrefs-function #'consult-xref
        xref-show-definitions-function #'consult-xref)

  ;; Configure other variables and modes in the :config section,
  ;; after lazily loading the package.
  :config

  ;; Optionally configure preview. The default value
  ;; is 'any, such that any key triggers the preview.
  ;; (setq consult-preview-key 'any)
  ;; (setq consult-preview-key "M-.")
  ;; (setq consult-preview-key '("S-<down>" "S-<up>"))
  ;; For some commands and buffer sources it is useful to configure the
  ;; :preview-key on a per-command basis using the `consult-customize' macro.
  (consult-customize
   consult-theme :preview-key '(:debounce 0.2 any)
   consult-ripgrep consult-git-grep consult-grep
   consult-bookmark consult-recent-file consult-xref
   consult--source-bookmark consult--source-file-register
   consult--source-recent-file consult--source-project-recent-file
   ;; :preview-key "M-."
   :preview-key '(:debounce 0.4 any))

  ;; Optionally configure the narrowing key.
  ;; Both < and C-+ work reasonably well.
  (setq consult-narrow-key "<") ;; "C-+"

  ;; Optionally make narrowing help available in the minibuffer.
  ;; You may want to use `embark-prefix-help-command' or which-key instead.
  ;; (define-key consult-narrow-map (vconcat consult-narrow-key "?") #'consult-narrow-help)

  ;; By default `consult-project-function' uses `project-root' from project.el.
  ;; Optionally configure a different project root function.
  ;;;; 1. project.el (the default)
  ;; (setq consult-project-function #'consult--default-project--function)
  ;;;; 2. vc.el (vc-root-dir)
  ;; (setq consult-project-function (lambda (_) (vc-root-dir)))
  ;;;; 3. locate-dominating-file
  ;; (setq consult-project-function (lambda (_) (locate-dominating-file "." ".git")))
  ;;;; 4. projectile.el (projectile-project-root)
  ;; (autoload 'projectile-project-root "projectile")
  ;; (setq consult-project-function (lambda (_) (projectile-project-root)))
  ;;;; 5. No project support
  ;; (setq consult-project-function nil)
)

smoothing scrolling

;; https://www.reddit.com/r/emacs/comments/tv022a/smooth_scrolling_on_emacs_29_is_a_dream_come_true/
(pixel-scroll-precision-mode 1)

smerge

(defhydra hydra/smerge ()
  "open file: "
  ("n" (smerge-next) "Jump to next confclit")
  ("p" (smerge-prev) "Jump to previous conflict")
  ("c" (smerge-keep-current) "keep current")
  ("u" (smerge-keep-upper) "keep upper version")
  ("l" (smerge-keep-lower) "keep lower version")
  ("a" (smerge-keep-all) "keep both versions")
  )
(global-set-key (kbd "<f7>") 'hydra/smerge/body)

Compare Files in dired

mark two files in dired, run ediff.

(setq ediff-keep-variants nil)  ;; ask before close the file.
(setq ediff-keep-variants t)    ;; show buffer after exit.


(defun mkm/ediff-marked-pair ()
  "Run ediff-files on a pair of files marked in dired buffer

copied from https://stackoverflow.com/questions/18121808/emacs-ediff-marked-files-in-different-dired-buffers"
  (interactive)
  (let* ((marked-files (dired-get-marked-files nil nil))
         (other-win (get-window-with-predicate
                     (lambda (window)
                       (with-current-buffer (window-buffer window)
                         (and (not (eq window (selected-window)))
                              (eq major-mode 'dired-mode))))))
         (other-marked-files (and other-win
                                  (with-current-buffer (window-buffer other-win)
                                    (dired-get-marked-files nil)))))
    (cond ((= (length marked-files) 2)
           (ediff-files (nth 0 marked-files)
                        (nth 1 marked-files)))
          ((and (= (length marked-files) 1)
                (= (length other-marked-files) 1))
           (ediff-files (nth 0 marked-files)
                        (nth 0 other-marked-files)))
          (t (error "mark exactly 2 files, at least 1 locally")))))


(define-key dired-mode-map (kbd "C-c e") 'mkm/ediff-marked-pair)

Scripting

[2021-06-05 Sat 12:00]

eshell

   (defalias 'open 'find-file)
   
   
   ;; from emacs-reddit
   ;; (defun eshell-here ()
   ;;   "Opens up a new shell in the directory associated with the current buffer's file."
   ;;   (interactive)
   ;;   (let* ((parent (file-name-directory (buffer-file-name)))
   ;;          (name   (car
   ;;                   (last
   ;;                    (split-string parent "/" t)))))
   ;;     (split-window-vertically)
   ;;     (other-window 1)
   ;;     (eshell "new")
   ;;     (rename-buffer (concat "*eshell: " name "*"))
   
   ;;     (insert (concat "ls"))
   ;;     (eshell-send-input)))
   
   (defun eshell-here ()
     "Opens up a new shell in the directory associated with the current buffer's file."
     (interactive)
     (let* ((parent (if (buffer-file-name)
			 (file-name-directory (buffer-file-name))
		       default-directory))
	     (name (car (last (split-string parent "/" t)))))
	(split-window-vertically)
	(other-window 1)
	(eshell "new")
	(rename-buffer (concat "*eshell: " name "*"))
   
	(insert (concat "ls"))
	(eshell-send-input)))
   
   (global-set-key (kbd "C-!") 'eshell-here)
   
   (defun delete-single-window (&optional window)
     "Remove WINDOW from the display.  Default is `selected-window'.
   If WINDOW is the only one in its frame, then `delete-frame' too."
     (interactive)
     (save-current-buffer
	(setq window (or window (selected-window)))
	(select-window window)
	(kill-buffer)
	(if (one-window-p t)
	    (delete-frame)
	  (delete-window (selected-window)))))
   
   (defun eshell/x (&rest args)
     (delete-single-window))
   
   
   

Git Sync

[2015-01-19 Mon 12:09]

Magit provides an interface to Git, and it is really pleasant to use. The reference card lists useful key-bindings and commands.

(use-package magit
  :ensure t
  :config
  (magit-auto-revert-mode nil)  ;; why disbale it? 
  (global-auto-revert-mode t)   ;; i think it's a good idea to have auto revert.
  :bind (("<f9> g" . magit-status)))

Occasionally my office machine goes down because I run R with big data, and it consumes all the memory. If that happens, I potentially lose the newsiest version of scripts, which is bit annoy. The following snippets will save all buffers in every hours.

(defun yt/save-all-buffers ()
  "save all files-visiting buffers without user confirmation"
  (interactive)
  (save-some-buffers t nil)
  (message "save all buffers... done"))
(run-at-time "05:59" 3600 'yt/save-all-buffers)

Sometimes I have to leave at the last minutes, then what I do is call a functions that commits and upload to the repo so that I can continue work at home.

The yt/git-up function will do

  1. pull from the remote repo, and make sure the local repo is always up-to-date.
  2. add everything and commit with a timesamp.
  3. push local changes to the remote repo.

Here is the snippets.

(defun yt/git-backup ()
  (let ((git-sh-scripts "
echo Start to Sync: $(date) 

REPOS=\"org\"
for REPO in $REPOS
do
    echo
    echo \"Repository: $REPO\"
    cd ~/git/$REPO
    # update
    git pull 
    # Remove deleted files
    git ls-files --deleted -z | xargs -0 git rm >/dev/null 2>&1
    # Add new files
    git add -A . >/dev/null 2>&1
    git commit -m \"$(date)\"
    git push origin main
done

echo Finished Sync: $(date)
"))
    (async-shell-command git-sh-scripts))
  (message "all git sync... done"))

(defun yt/git-up ()
  (interactive)
  (yt/save-all-buffers)
  (yt/git-backup))

Few times I did some important work over the weenend, but once I arrived office I realised I forgot uploading, These situations are quick frustrating. The following snippets will start to uploads once every three hours on my MacbookPro, but I don’t use it anymore, since I can get most of my work done in the office.

Note this workflow is suspended for it’s unsafe.

;; (cond ((eq system-type 'darwin)
;;        (run-at-time "05:59" 10800 'yt/git-up)))