EMACS-DOCUMENT

=============>随便,谢谢

Emacs:更多专业技巧

这是我第一篇 Emacs pro-tips 的后续.

避免“加载旧字节码而不是新的源代码”的陷阱

(setq load-prefer-newer t)

用于本地存放包和开发内容的site Lisp文件夹

我们需要编写自己的函数,因此我们希望把本地的site文件夹放在 load-path 的前面,然而 normal-top-level-add-subdirs-to-load-path 会把它添加到 load-path 的最后.

(defun ambrevar/package-refresh-load-path (path)
  "Add every non-hidden sub-folder of PATH to `load-path'."
  (when (file-directory-p path)
    (dolist (dir (directory-files path t "^[^\.]"))
      (when (file-directory-p dir)
        (setq load-path (add-to-list 'load-path dir))
        (dolist (subdir (directory-files dir t "^[^\.]"))
          (when (file-directory-p subdir)
            (setq load-path (add-to-list 'load-path subdir))))))))
(let ((site-lisp (expand-file-name "site-lisp/" "~/.local/share/emacs/")))
  (add-to-list 'load-path site-lisp)
  (ambrevar/package-refresh-load-path site-lisp))

Flyspell 以及 whitespace-mode

(defun ambrevar/flyspell-and-whitespace-mode ()
  "Toggle `flyspell-mode' and `whitespace-mode'."
  (interactive)
  (if (derived-mode-p 'prog-mode)
      (flyspell-prog-mode)
    (flyspell-mode)
    (when flyspell-mode
      (flyspell-buffer)))
  (whitespace-mode 'toggle))

(global-set-key (kbd "<f9>") #'ambrevar/flyspell-and-whitespace-mode)

下载光标所在的视频网址

下面代码需要 youtube-dl 程序的支持。

(defun ambrevar/youtube-dl-at-point (&optional url)
  "Run 'youtube-dl' over the URL at point.
If URL is non-nil, use that instead."
  (interactive)
  (setq url (or url (thing-at-point-url-at-point)))
  (let ((eshell-buffer-name "*youtube-dl*"))
    (eshell)
    (when (eshell-interactive-process)
      (eshell t))
    (eshell-interrupt-process)
    (insert "cd ~/temp && youtube-dl " url)
    (eshell-send-input)))

参见 youtube-dl-emacs.

列出当前的minor模式

(defun ambrevar/current-minor-modes ()
  "Return the list of minor modes enabled in the current buffer."
  (interactive)
  (delq nil
        (mapcar (lambda (mode)
                  (if (and (boundp mode) (symbol-value mode))
                      mode))
                minor-mode-list)))

窗口管理

由于我使用 EXWM 作为窗口管理器, 因此我将 super 键用于进行窗口管理.

下面是一些简单而有效的规则:

  • s-TAB: 切换到最后一个缓冲区。
  • s-<箭头> (或在Evil中的 s-<hjkl>): 在选定的方向上选择窗口。
  • S-s-<arrows> (or S-s-<hjkl> with Evil): swap current window with window in
  • S-s-<箭头> (或在Evil中的 S-s-<hjkl>): 与选择方向的窗口进行互换
  • s-: 切换水平或垂直分隔。
  • s-o: 切换是否隐藏所有其他窗口。

With Helm, I use C-c o (or my custom binding S-RET) to find a file or a 在Helm中,我使用 C-c o (或这我自定义的快捷键 S-RET)来在新分割的窗口上打开文件或buffer

我需要一些额外的函数来实现上述工作流程:

(defun ambrevar/swap-windows (&optional w1 w2)
  "If 2 windows are up, swap them.
Else if W1 is a window, swap it with current window.
If W2 is a window too, swap both."
  (interactive)
  (unless (or (= 2 (count-windows))
              (windowp w1)
              (windowp w2))
    (error "Ambiguous window selection"))
  (let* ((w1 (or w1 (car (window-list))))
         (w2 (or w2
                 (if (eq w1 (car (window-list)))
                     (nth 1 (window-list))
                   (car (window-list)))))
         (b1 (window-buffer w1))
         (b2 (window-buffer w2))
         (s1 (window-start w1))
         (s2 (window-start w2)))
    (with-temp-buffer
      ;; Some buffers like EXWM buffers can only be in one live buffer at once.
      ;; Switch to a dummy buffer in w2 so that we don't display any buffer twice.
      (set-window-buffer w2 (current-buffer))
      (set-window-buffer w1 b2)
      (set-window-buffer w2 b1))
    (set-window-start w1 s2)
    (set-window-start w2 s1))
  (select-window w1))
(global-set-key (kbd "C-x \\") 'swap-windows)

(defun ambrevar/swap-windows-left ()
"Swap current window with the window to the left."
(interactive)
(ambrevar/swap-windows (window-in-direction 'left)))
(defun ambrevar/swap-windows-below ()
"Swap current window with the window below."
(interactive)
(ambrevar/swap-windows (window-in-direction 'below)))
(defun ambrevar/swap-windows-above ()
"Swap current window with the window above."
(interactive)
(ambrevar/swap-windows (window-in-direction 'above)))
(defun ambrevar/swap-windows-right ()
"Swap current window with the window to the right."
(interactive)
(ambrevar/swap-windows (window-in-direction 'right)))

(defun ambrevar/switch-to-last-buffer ()
"Switch to last open buffer in current window."
(interactive)
(switch-to-buffer (other-buffer (current-buffer) 1)))

(defun ambrevar/toggle-single-window ()
  "Un-maximize current window.
If multiple windows are active, save window configuration and
delete other windows. If only one window is active and a window
configuration was previously save, restore that configuration."
  (interactive)
  (if (= (count-windows) 1)
      (when single-window--last-configuration
        (set-window-configuration single-window--last-configuration))
    (setq single-window--last-configuration (current-window-configuration))
    (delete-other-windows)))

(defun ambrevar/toggle-window-split ()
  "Switch between vertical and horizontal split.
It only works for frames with exactly two windows."
  (interactive)
  (if (= (count-windows) 2)
      (let* ((this-win-buffer (window-buffer))
             (next-win-buffer (window-buffer (next-window)))
             (this-win-edges (window-edges (selected-window)))
             (next-win-edges (window-edges (next-window)))
             (this-win-2nd (not (and (<= (car this-win-edges)
                                         (car next-win-edges))
                                     (<= (cadr this-win-edges)
                                         (cadr next-win-edges)))))
             (splitter
              (if (= (car this-win-edges)
                     (car (window-edges (next-window))))
                  'split-window-horizontally
                'split-window-vertically)))
        (delete-other-windows)
        (let ((first-win (selected-window)))
          (funcall splitter)
          (if this-win-2nd (other-window 1))
          (set-window-buffer (selected-window) this-win-buffer)
          (set-window-buffer (next-window) next-win-buffer)
          (select-window first-win)
          (if this-win-2nd (other-window 1))))))

使用FreeDesktop.org的垃圾箱

Whenever Emacs “delete” a file (from dired, Helm Find-Files or Elisp 原语),让Emacs把它移到垃圾箱里:

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

Lisp的括号编辑

Lisp的一个常见问题是需要平衡括号。

这太明显了. 既然这种人物对及计算机没有任何难度,那很明显Emacs为你可以提供帮助

首先,让我们高亮显示括号。我取消延迟让Emacs立即高亮匹配的括号:

;;; Show matching parenthesis
(show-paren-mode 1)
;;; By default, there’s a small delay before showing a matching parenthesis. Set
;;; it to 0 to deactivate.
(setq show-paren-delay 0)
(setq show-paren-when-point-inside-paren t)

(with-eval-after-load 'paren
  (set-face-background 'show-paren-match "#555555")
  (set-face-foreground 'show-paren-match "#def")
  (set-face-attribute 'show-paren-match nil :weight 'extra-bold))

接下来,我们安装 rainbow-delimiter 这个第三方包,它会根据括号深度显示不同的颜色。就跟C或Algol家族语言中的缩进差不多。

再见Paredit,你好Lispy

Consider using Lispy which brings Lisp syntactic editing to a whole new level: 考虑使用Lispy,它将Lisp语法编辑带到一个全新的层次:: 除了括号平衡之外(这使得前面部分变得多余的),它还提供了高级的表达式导航、代码转换和样式美化等功能。

看一下演示中的那些具体例子。

仔细想想,Lispy对Lisp的编辑器支持有了明显的改进: 它真正利用了Lisp语言的语法是抽象愈发术的这一事实.不使用这一财产真是耻辱啊。

图像处理和缩略图库

有一个不太知名的命令 image-dired: 当在一个图片目录下运行时,它会显示一个带预览的缩略图图库。 按下 空格 就能在另一个window中显示下一张图片,而 C-RET 会使用 image-dired-external-viewer 打开图片. 你还可以旋转文件,在dired中对它们添加标签或添加评论。

第三方包 image+ 为Emacs增加了额外的图片功能,比如stiky转换和文件修改。

奇怪的nconc行为