Emacs builtin mode 功能介绍

类似 imenu ,但是要更自定义一些,而不是根据语法来的。这样就可以通过注释来实现了

不是很清楚这有什么用处,大佬你可以尝试实现一下。

imenu 本身也是通过正则匹配的,我当时帮别人修 ahk-mode 的 bug 时搞过类似的:

image

;imenu 开头的注释就可以被 imenu 捕捉到

感觉用 outline 可以凑合用啊。

修改 outline-regrex 类似为 .* ;;;;; 这样的。

我都是优先使用内置包,不够用了就用第三方或者自己扩展

3 个赞

outline的部份可以用这个:

目录buffer我昨天正好写了一个。感觉没有想象中的那么有用,尤其是在小屏幕。

https://github.com/casouri/lunarymacs/blob/master/site-lisp/sidebar.el

sidebar

6 个赞

Changelog:

  • 2020-07-02 增加了shell-command-on-region的一个使用场景 (以ydcv为例)

shell-command-on-region的妙用

在注释看到不认识的单词,一般做法是复制、打开终端、然后调用外部程序 ydcv来翻译。

最近发现了shell-command-on-region之后,可以省掉前2个步骤了!

例如papaya单词不认识,那么选中它,然后M-| (shell-command-on-region)运行一下,输入ydcv然后执行。如果输出内容比较少,则 直接会在echo area处显示(当resize-mini-windows不为nil时由 max-mini-window-height决定)。如果想强制它输出在buffer下(方便复制),可以在 上面再封装一层、例用advice机制,或者提前创建一个名叫*Shell Command Output*buffer

以下是一个advice的例子(不推荐使用advice,有较大的侵入性,这里只是做个演示)

(define-advice shell-command-on-region (:around (func &rest args))
  (let ((max-mini-window-height 0))
    (apply func args)))

甚至可以直接封装一个叫做ydcv的命令来完成这个工作!

(defun ydcv (beg end)
  (interactive "r")
  (let ((max-mini-window-height 0))
    (shell-command-on-region beg end "ydcv")))

当然还可以有ydcv-at-point, ydcv-dwim等一系列函数!

9 个赞

一人血书求更(

在写了在写了(

woman和info蛮好用的(

楼主加油,又发掘出好些忽略的built-in好工具

我也加几个内置的 mode, 直接上代码, 不需要解释.

(global-prettify-symbols-mode t)
(global-display-fill-column-indicator-mode t)
(savehist-mode t)
(recentf-mode t)
(save-place-mode t)
(electric-pair-mode t)
(add-hook 'prog-mode-hook 'display-line-numbers-mode)

不过还是要提示下, 你打开越多的 mode, emacs 速度越慢 …

1 个赞

Changelog:

  • 2020-08-22 增加了winner-undotransient-map搭配的例子
  • 2020-08-22 增加了strokes-mode的介绍
  • 2020-08-25 增加了webjump的介绍
  • 2020-08-30 增加了transient-map的小技巧

winner-mode

winner-mode是一个全局的minor mode,它的主要功能是记录窗体的变动。例如当前有 2 个窗口,然后你关了一个,这时可以通过winner-undo来恢复。还可以再winner-redo 来撤销刚才的undo.

它默认按键绑定为:

C-c Left winner-undo

C-c Right winner-redo

如果不想它绑定在C-c前缀按键上,可以通过

(setq winner-dont-bind-my-keys nil)

来禁止。

建议配置:

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

同时,它也可以应用在ediff上,恢复由ediff导致的窗体变动。

(use-package ediff
  :ensure nil
  :hook (ediff-quit . winner-undo)

winner-undo 搭配 transient-map

如果平常有注意观察text-scale-adjust (C-x C-=) 的行为,会发现只需要 按全一次C-x C-=,之后可以只按+-或者0来缩放字体。而如果触发了 其他按钮则会退出这个状态,它背后主要依赖transient-map机制。

我们可以仿照着写一个transient-winner-undo的版本,在需要连续执行winner-undo的 时候只需要按一个u就好了。

(defun transient-winner-undo ()
  "Transient version of `winner-undo'."
  (interactive)
  (let ((echo-keystrokes nil))
    (winner-undo)
    (message "Winner: [u]ndo [r]edo")
    (set-transient-map
     (let ((map (make-sparse-keymap)))
       (define-key map [?u] #'winner-undo)
       (define-key map [?r] #'winner-redo)
       map)
     t)))

着实方便许多!

strokes

如果你想用鼠标来控制Emacs的行为,有点像现在浏览器上的鼠标手势。不过它只能识别 鼠标移动轨迹所描绘的形状,不能判断它的方向。

  1. 执行strokes-mode打开minor-mode
  2. 执行strokes-global-set-stroke在弹出的buffer内使用Shift+鼠标左键(也可以用中键) 绘出想作为快捷操作的大致形状,假设是一个 C 的形状,然后鼠标右键结束绘制。稍后 会提示输入与stroke对应的命令,假设是strokes-help
  3. 移动鼠标,使得它的轨迹是个 C 的形状
  4. Shift+鼠标中键以执行与这个stroke对应的命令,也就是strokes-help

想要更详细的信息?请M-x strokes-help.

webjump

你想在Emacs里快速调用搜索引擎搜索吗?原来这个功能早已经内置了!

由于webjump-sites早已有默认值了,如果想急着体验一下可以立即M-x webjump。其原 理也是相当简单,通过用户选择它想要用的搜索引擎+查询内容构造出实际url,然后通过 browse-url调用托管给浏览器。

我的配置是这样的:

(use-package webjump
  :ensure nil
  :bind ("C-c /" . webjump)
  :custom
  (webjump-sites '(
                   ;; Emacs.
                   ("Emacs Home Page" .
                    "www.gnu.org/software/emacs/emacs.html")
                   ("Savannah Emacs page" .
                    "savannah.gnu.org/projects/emacs")

                   ;; Internet search engines.
                   ("DuckDuckGo" .
                    [simple-query "duckduckgo.com"
                                  "duckduckgo.com/?q=" ""])
                   ("Google" .
                    [simple-query "www.google.com"
                                  "www.google.com/search?q=" ""])
                   ("Google Groups" .
                    [simple-query "groups.google.com"
                                  "groups.google.com/groups?q=" ""])
                   ("Wikipedia" .
                    [simple-query "wikipedia.org" "wikipedia.org/wiki/" ""]))))

如果只是想要简单的查询,那么可以作为 engine-mode的内置替换方案了。

当然,还可以配置多级查询选项,可参考webjump-to-iwin的实现。

transient-map 小技巧

因为transient-map的优先级比其他keymap都要高,所以可以将它当作菜单来使用。

如果嫌set-transient-map用起来不方便,可以使用hydra代替。

编辑时拷贝 (配合 avy)

如果在编辑文字时发现要拷贝一个url,但是当前窗口内有多个url,类似的场景如下:

/// url1: https://www.google.com
/// url2: https://www.baidu.com
/// url3: https://duckduckgo.com
int foo(int x) {
    const char* url = "";
}

想要为url赋值为注释内的 3 个 url 之一。

  1. 首先将光标移动到想复制的url
  2. 再将这个url复制到kill-ring当中
  3. 再回到原来的位置
  4. 再粘贴
(defhydra hydra-copy (:color blue)
  "Copy"
  ("w" copy-word-at-point "word")
  ("u" copy-url-at-point "url")
  ("q" nil "cancel"))

(defun copy-url-at-point ()
  "Copy url at point."
  (interactive)
  (save-excursion
    (avy-goto-word-or-subword-1)
    (kill-new (thing-at-point 'url))))

这里使用hydra来偷懒一下。

效果图: hydra

20 个赞

好文章好文章,感谢

hydra-copy 这个注意不错。

我试了一下,个人感觉先跳位置、再选类型比较顺手。

我常年养成的习惯是,先盯住要选的位置,然后快捷键呼出 avy,输入高亮字符。此时如果 minibuffer 先弹出个窗口,眼睛会不自觉地往下瞄一下,这样先前盯住的焦点就跑了,需要眼睛再次搜索。如果先跳好位置就跑不掉了,可以从容思考拷贝什么类型。

方法定义如下:

(defun avy-copy-thing-at-point ()
  "Copy thing at point."
  (interactive)
  (save-excursion
    (avy-goto-word-or-subword-1)
    (let ((thing
           (cl-case (read-char
                     (format
                      "Copy thing at point (%s: word %s: symbol %s: list %s: url): "
                      (propertize "w" 'face 'error)
                      (propertize "s" 'face 'error)
                      (propertize "l" 'face 'error)
                      (propertize "u" 'face 'error)))
             (?w  'word)
             (?s  'symbol)
             (?l  'list)
             (?u  'url))))
      (kill-new (thing-at-point thing))
      (message "%s copied" thing))))

如果想要美观一点就换 hydra,我这里先用 read-char 顶着。

1 个赞

下面这个提示用的熟了之后可以直接无视,因为绑定的前缀按键跟单词的前缀一样。

刚写了一个包avy-thing-edit,给懒猫大神的thing-edit 加入了avy-jump 功能,对于远处的目标可以很方便地copy/cut/replace

2 个赞

Changelog:

  • 2020-09-04 newcomment 模块注释与反注释
  • 2020-09-04 打字打累了?看一下type-break
  • 2020-09-04 通过timeclock来管理时间
  • 2020-09-18 增加elide-head的介绍
  • 2020-09-19 增加midnight-mode的介绍

newcomment

如果你想要一个足够简单的注释与反注释功能,那么自带的newcomment就可以做到。

(use-package newcomment
  :ensure nil
  :bind ([remap comment-dwim] . #'comment-or-uncomment)
  :config
  (defun comment-or-uncomment ()
    (interactive)
    (if (region-active-p)
        (comment-or-uncomment-region (region-beginning) (region-end))
      (if (save-excursion
            (beginning-of-line)
            (looking-at "\\s-*$"))
          (call-interactively 'comment-dwim)
        (comment-or-uncomment-region (line-beginning-position) (line-end-position)))))
  :custom
  (comment-auto-fill-only-comments t))

上方的函数它可以完成:

  • 当用户选中区间时,在对应区间上注释或者反注释
  • 如果当前行是空的,那么会插入一个注释并且将它对齐 (偷懒,直接调用了comment-dwim)
  • 其他情况则对当前行注释或者反注释

这个行为也与evil-nerd-commenter保持一致。

这里有必要比较一下其他comment函数:

  1. comment-dwim
    • 当用户选中区间时,会在对应区间注释或者反注释
    • 如果当前行是空的,那么会插入一个注释并且将它对齐
    • 如果使用C-u前缀,会则调用comment-kill来删除这个注释
    • 其他情况下则调用comment-indent在尾部插入注释并对齐
  2. comment-line
    • 当用户选中区间时,会在对应区间再加上下一行进行注释或者反注释
    • 如果当前行是空的,那么只会跳到下一行不会插入注释
    • 其他情况下则会将当前行注释或者反注释并跳到下一行
  3. comment-box 看例子就行
(defun add (a b)
  (+ a b))

;;;;;;;;;;;;;;;;;;;;;;
;; (defun add (a b) ;;
;;   (+ a b))       ;;
;;;;;;;;;;;;;;;;;;;;;;

type-break

历史老物,1994 年的时候就已经出现了。

打字打累了,想休息一下?看代码看累了,想放松一下?

那么它可能会适合你。如果在一段时间内的敲击键盘次数大于阈值,那么它会假设平均速度35 wpm,每个单词长度5来推算出要休息多少分钟。

而到达休息状态时,它可能会显示出一个汉诺塔移动的动画。可以M-x type-break立即体验!

timeclock

这是一个计算时间到底去哪里了的包,不过都有org-mode了,真的还会有人来用这个吗?

org timeclock
org-clock-in timeclock-in
org-clock-out timeclock-out

功能与org-mode几乎一致,不过它可以随时timeclock-out不用管记录时间的文件打开与否,而在org-modeclock-out则要保证运行clock的那个文件还处于打开状态。

elide-head

依旧是怀旧向的内置包,可以将源代码文件的头部中大量的license说明折叠起来,效果 跟hideshow包类似。可以通过配置elide-head-headers-to-hide来自定义想要的折叠区间。

midnight 深夜模式

在晚上零点的时候定期执行一些任务,默认是clean-buffer-list,可以设置midnight-hook来自定义行为。

M-x midnight-mode 来开启深夜模式。嗯,又到了深夜网抑云音乐时间了。

9 个赞

发现一个可以给 evil-mode 实现相对行号的方法:

;;; relative line number
(defun my-enable-relative-line-number ()
  (interactive)
  (display-line-numbers-mode -1)
  (setq display-line-numbers-type 'relative)
  (display-line-numbers-mode 1))

(defun my-disable-relative-line-number ()
  (interactive)
  (display-line-numbers-mode -1)
  (setq display-line-numbers-type t)
  (display-line-numbers-mode 1))

(add-hook 'evil-normal-state-entry-hook 'my-enable-relative-line-number)
(add-hook 'evil-insert-state-entry-hook 'my-disable-relative-line-number)
(add-hook 'evil-insert-state-exit-hook 'my-enable-relative-line-number)
3 个赞

Deferred-Eval 如果一个计算结果在未来不必需要,那么您应该想避免对其进行耗时的计算,这种情况下 对一个表达式的延迟求值就显得很有用了。 thunk.el 就是这样一个支持延迟求值的库。

;;; 只有当 derived-number 变量被调用时才会对其后跟着的表达式进行求值运算
(setq lexical-binding t)
(require 'thunk)
(defun f (number)
  (thunk-let ((derived-number
	       (progn (message "Calculating 1 plus 2 times %d" number)
		      (1+ (* 2 number)))))
    (if (> number 10)
	derived-number
      number)))
(f 5) ; ⇒ 5

(f 12); ⊣ Calculating 1 plus 2 times 12 ⇒ 25
(setq lexical-binding t)
(require 'thunk)
(thunk-let* ((x (prog2 (message "Calculating x...")
		    (+ 1 1)
		  (message "Finished calculating x")))
	     (y (prog2 (message "Calculating y...")
		    (+ x 1)
		  (message "Finished calculating y")))
	     (z (prog2 (message "Calculating z...")
		    (+ y 1)
		  (message "Finished calculating z")))
	     (a (prog2 (message "Calculating a...")
		    (+ z 1)
		  (message "Finished calculating a"))))
  (* z x))

;⊣ Calculating z...
;⊣ Calculating y...
;⊣ Calculating x...
;⊣ Finished calculating x
;⊣ Finished calculating y
;⊣ Finished calculating z
;⇒ 8

PS: 发完才发现是内置 mode 的介绍,囧…

1 个赞