在使用 Emacs 进行编辑的时候,每个人都有自己独特的编辑习惯和需求,我整理了一些自己的“奇思妙想”(或许是我的脑子不生产灵感,它只是 ELisp 库的搬运工,但这不重要,重要的是享受编辑):
需求:
某些命令(如 highlight-regexp、occur、find-grep、query-replace)需要先进行 minibuffer 输入内容再执行,若输入的内容往往是光标处的符号,先选中复制再粘贴,何其麻烦?
思路:
Talk is cheap, show you my code!
代码:
(define-key minibuffer-local-map (kbd "<your-lovely-key>")
(lambda ()
(interactive)
(let* ( (buf (other-buffer (current-buffer) t))
(sym (unless (minibufferp buf)
(with-current-buffer buf (thing-at-point 'symbol)))))
(if (and (stringp sym) (> (length sym) 0))
(insert sym)
(error "No symbol found")))))
需求:
某些命令会打开文件,而它选择的窗口非常狂野而捉摸不透;
思路:
这个需要自己 hack 命令的源码;
代码:
(defun select-window-dwim ()
;; 通过数值前缀在指定编号的窗口打开(不指定则在最后一个窗口打开)
(let* ( (all-wins (window-list nil 0 (frame-first-window)))
(en-wins
(delq nil
(mapcar
(lambda (w) (unless (window-dedicated-p w) w))
(delete (selected-window) all-wins)))))
(or (and current-prefix-arg (nth (1- current-prefix-arg) all-wins))
(car (last en-wins)))))
occur 打开文件
(defun occur-mode-goto-occurrence (&optional event)
"Go to the occurrence specified by EVENT, a mouse click.
If not invoked by a mouse click, go to occurrence on the current line."
(interactive (list last-nonmenu-event))
(let ( (buffer (when event (current-buffer)))
(pos
(if (null event)
(occur-mode-find-occurrence)
(with-current-buffer (window-buffer (posn-window (event-end event)))
(save-excursion
(goto-char (posn-point (event-end event)))
(occur-mode-find-occurrence)))))
(win (select-window-dwim)) ; Hack
)
;; 注释以下代码
;; (pop-to-buffer (marker-buffer pos))
;; Hack Start
(if (not win)
(pop-to-buffer (marker-buffer pos))
(select-window win)
(switch-to-buffer (marker-buffer pos)))
;; Hack End
(goto-char pos)
(when buffer (next-error-found buffer (current-buffer)))
(run-hooks 'occur-mode-find-occurrence-hook)))
help 打开文件
(defun help-function-def--button-function (fun &optional file type)
(or file
(setq file (find-lisp-object-file-name fun type)))
(if (not file)
(message "Unable to find defining file")
(require 'find-func)
(when (eq file 'C-source)
(setq file
(help-C-file-name (indirect-function fun) 'fun)))
(let* ( (location (find-function-search-for-symbol fun type file))
(position (cdr location))
(win (select-window-dwim)) ; Hack
)
;; 注释以下代码
;; (pop-to-buffer (car location))
;; Hack Start
(if (not win)
(pop-to-buffer (car location))
(select-window win)
(switch-to-buffer (car location)))
;; Hack End
(run-hooks 'find-function-after-hook)
(if position
(progn
(when (or (< position (point-min))
(> position (point-max)))
(widen))
(goto-char position))
(message "Unable to find location in file")))))
dired 打开文件
(defun dired-find-file ()
"In Dired, visit the file or directory named on this line."
(interactive)
(let ( (find-file-run-dired t)
(switch-to-buffer-preserve-window-point
(if dired-auto-revert-buffer
nil switch-to-buffer-preserve-window-point))
(win (select-window-dwim)) ; Hack
(file (dired-get-file-for-visit))
)
;; Hack Start
(and win (select-window win))
;; Hack End
(find-file file)))
需求:
在 mode-line 显示 overwrite 的状态?您的屏幕真大!
思路:
像其他编辑器一样,高亮一下吧!
代码:
(defvar-local hl-overwrite-overlay nil)
(defsubst hl-overwrite-clear-ov ()
(if (and (overlayp hl-overwrite-overlay)
(bufferp (overlay-buffer hl-overwrite-overlay)))
(delete-overlay hl-overwrite-overlay)))
(defsubst hl-overwrite-make-ov ()
(hl-overwrite-clear-ov)
(if overwrite-mode
(let* ( (start (point))
(ov (if (not (or (eobp)
(ignore-errors
(and (string-match-p "[\n\t]" (char-to-string (char-after (point)))) t))))
(make-overlay start (1+ start)))))
(when ov
(setq hl-overwrite-overlay ov)
(overlay-put ov 'face 'region)
(overlay-put ov 'window (selected-window))))))
(advice-add 'overwrite-mode :after
(lambda (arg)
(if overwrite-mode
(add-hook 'post-command-hook #'hl-overwrite-make-ov nil t)
(hl-overwrite-clear-ov)
(remove-hook 'post-command-hook #'hl-overwrite-make-ov t))))
需求:
经常使用的字符串,为何不保存下来,随时增加或删除?
思路:
手动记录、手动增加、手动删除;
代码:
(defcustom insert-string-file (locate-user-emacs-file "insert-string")
"File to save the string list.")
(defvar insert-string nil)
(defsubst insert-string-load ()
(if (file-exists-p insert-string-file)
(load insert-string-file nil t)))
(defsubst insert-string-save ()
(with-temp-message ""
(with-temp-buffer
(let ( (inhibit-modification-hooks t)
(message-log-max nil))
(erase-buffer)
(insert ";; -*- mode: emacs-lisp; coding: utf-8-unix -*-\n"
"(setq insert-string\n '(\n")
(dolist (e insert-string)
(insert " \"" e "\"\n"))
(insert " ))\n")
(write-region (point-min) (point-max) insert-string-file)))))
(defun insert-string-remove ()
(interactive)
(insert-string-load)
(setq insert-string
(delete (completing-read "Delete string: " insert-string nil t)
insert-string))
(insert-string-save))
(defun insert-string-add ()
(interactive)
(insert-string-load)
(push (read-string "Add string: " nil nil "") insert-string)
(insert-string-save))
(defun insert-string ()
(interactive)
(insert-string-load)
(let ((content (completing-read "Insert: " insert-string)))
(insert content)
(setq insert-string
(cons content (delq content insert-string))))
(insert-string-save))
需求:
山顶洞人一直喜欢使用 emacs 开发,而他的摩登同事们都是 VSCoce 迷,井水不犯河水。直到有一天,他邀请同事来帮他检查一段代码,于是抱怨声四起 “为什么不能复制?”、“为什么我粘贴代码,屏幕在滚动?”
思路:
为你我打开 VS!
代码:
(defun betray-emacs-and-run (command)
;; 依赖 nohup,调用外部进程,允许脱离当前 Emacs 运行
(with-temp-buffer
(ignore-errors
(call-process-shell-command (format "nohup %s >/dev/null 2>&1 &" command) nil t))
(let ((process (get-buffer-process (current-buffer))))
(and (processp process) (set-process-query-on-exit-flag process nil)))))
(defun open-vscode-for-you ()
(interactive)
(let ((files ""))
(dolist (x (delq nil
(mapcar (lambda (b) (if (buffer-file-name b) b))
(buffer-list))))
(let ((path (buffer-file-name x)))
(when (and path (file-exists-p path) (not (file-remote-p path)))
(setq files (concat files " " path)))))
(if files
(betray-emacs-and-run (concat "code -n" files)) ; cmd for vscode in archlinux
(error "No files selected"))))