EMACS-DOCUMENT

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

一些Emacs技巧

下面是我配置中的一些对他人可能有用的Emacs Lisp内容。 我不想把它们发表在MELPA上,因为我不想维护它们。它们可能有bug,未经过彻底的测试等问题。 如果它们适合你的需要,你可以随意挑选,但是如果它们不起作用,也不要抱怨!

编辑HTML时浏览器自动重新加载

Switching to the browser and refreshing it got old rather quickly, so I scripted it. 当我使用HTML/CSS/JS进行web编程时,我喜欢使用浏览器来快速测试我的更改。 切换到浏览器再刷新这种方法已经过时了,而且太慢,因此我编写了个脚本来完成它。

reload-browser.el 提供了一个函数来重新加载多个浏览器窗口。 第一次调用它时,它会把光标变成一个方框,要求您单击要重新加载的窗口。之后,再次调用该函数将重新加载所选择的窗口。 如果你传递一个数字前缀参数,您还可以选择多个窗口(例如,Firefox和Chrome)。 无需更复杂的设置,我就能通过快捷键来保存当前buffer并重新加载所有浏览器窗口了。

在后台,它只是简单地调用 xdotool 来完成繁重的工作。脚本基本上很直观,但Chromium需要光标定位后才能重新加载,而Firefox则不需要,脚本中必须处理这个问题。

编辑共享LaTeX时每行一个句子

我在写东西的时候通常会开启 auto-fill-mode,它让一行只包含80个字符,从而使编辑结果在任何文本编辑器中都便于阅读。 不过,在使用LaTeX写文章时,我希望每行都是一句完整的话,这样更方便检查差异和处理冲突。 当然,我希望我的编辑器自动帮我以这种法那个时进行填充。

ospl.el 重新定义了 auto-fill-function 来进行断句,并提供了 ospl-paragraph 函数将当前段落填充为每行一句。 这里的技巧是检测句子的结束位置再插入新行。Emacs可以通过查找冒号来识别句子,但是请注意缩写词也以冒号结尾(“e.g.”、“i.e.”)。 处理这种情况的最佳方法是在句子后面使用双空格,在其他冒号使用情况后面使用单空格(请参阅变量 sentence-end-double-space)。

不幸的是,并不是每个人都认识到双空格是更好的形式,所以 ospl 假设您使用单空格,并避免在常见的缩写词之后中断,这使得它变得更复杂一些。

编辑汇编语言时预览定义

最近我一直在涉猎6502汇编,浏览代码时,我经常发现需要查看光标所在标签的定义。 因此,我编写了一个用于汇编的xref后端(xrefasm.el),这样就可以使用标准的快捷键跳转到定义并返回了。 不多对于大多数标签,我并不需要特意跳转到定义处,只需要看到标签上下几行,就可以记起它的作用。

因此我编写了 xref-posframe.el, 另一个使用 posframe 来预览定义的包. Posframe 允许你创建不带window decoration的一次性Emacs frame, 这要比在当前buffer内容上的overlay要更方便也更利于配置,而且速度也足够快。借助 xref-posframe 我可以一键预览定义。

再按一键,我甚至可以跳转到定义。

而且它可以在支持xref的任何地方工作,而不仅仅是在汇编语言时。

突出显示TODO关键字

This can be done by adding keywords to font lock: 这个很简单。我让评论中的 TODO:FIXME: 关键字现实时使用自定义字体,这样我可以立即看到它们,知道有东西要修复。这可以通过为font lock添加关键字来实现:

(defun add-watchwords ()
  "Add TODO: words to font-lock keywords."
  (font-lock-add-keywords
   nil '(("\(\<TODO\|\<FIXME\|\<HACK\|@.+\):"
          1 font-lock-warning-face t))))
(add-hook 'prog-mode-hook #'add-watchwords)

todo-highlight.png

这种方法有一个缺点:它会高亮现实编程buffer中所有出现这些单词的地方,即使在注释之外也是如此。 有一些复杂的包可以以更健壮的方式进行高亮,确保只在注释中突出显示这些词。 但我认为不值得因为这些情况而增加复杂性。对我来说,99.99%的情况下,上面的方法是有效的。当出现假阳性时,我就忽略它。

最近我使用了更多描述性的关键字,比如 @correct:@optimization:,所以我在后面添加了一个通配符来处理 "@" 和 ":" 之间的所有单词。即便如此,误报也不是问题。

在org文档中插入截图

我在Org上做了很多笔记。当我做一些设计可视化的东西时,我比较喜欢包含一些屏幕截图,因为它们通常更有助于表达。为了减少截屏,剪切,插入链接到Org文件的不爽之处,我写了一些Elisp来帮我:

(defun insert-screenshot (file-name)
  "Save screenshot to FILE-NAME and insert an Org link at point.

This calls the `import' from ImageMagick to take the screenshot,
and `optipng' to reduce the file size if the program is present."
  (interactive "FSave to file: ")
  ;; Get absolute path
  (let ((file (expand-file-name file-name)))
    ;; Create the directory if necessary
    (make-directory (file-name-directory file) 'parents)
    ;; Still, make sure to signal if the screenshot was in fact not created
    (unless (= 0 (call-process "import" nil nil nil file))
      (user-error "`import' failed to create screenshot %s" file))
    (if (executable-find "optipng")
        (start-process "optipng" nil "optipng" file))
    (insert
     ;; A link relative to the buffer where it is inserted is more portable
     (format "[[file:%s]]"
             (file-relative-name file
                                 (file-name-directory buffer-file-name))))
    (when (eq major-mode 'org-mode)
      (org-redisplay-inline-images))))

这段代码请求输入一个文件名,然后调用 import 来选择我想要截屏的区域并保存它,然后在光标处插入一个org格式的图像链接。 org-mode会显示内联图像,这样我就可以立即看到结果。如果安装了 optipng,它甚至会压缩屏幕截图以节省空间。

插入、删除或更改分隔符

在Spacemacs和Vim中,有一种极好的方法可以快速地用引号、大括号和其他分隔符包裹一部分文本分。当我用会普通Emacs时,手动添加引号就很无聊了。

delimiter.el 解决了这个问题. 它非常简单: delimiter-surround 会提示你输入分隔符,并用它包裹选中的文本. 如果没有选中区域,它将包裹当前的语句。因此我只需要快速按两次键,就能引用一个单词。 某些分隔符是成对的,比如一个左大括号 "{" 使用与其匹配的右大括号 "}" 作为结束分隔符。

还有一个 delimiter-change 能将现有的分隔符对更改为其他内容,还有一个 delimiter-delete 这个作用看名字就知道干啥的了。

注意,这个函数不太聪明:如果您有嵌套的分隔符,它将只删除最接近光标的分隔符,所以在以下情况下(插入符用 | 标记点):

(he|re (is) a list)

执行 M-x delimiter-delete ( RET 会产生下面记i恶果:

he|re (is a list)

但我们想要的应该是删除最外层括号:

he|re (is) a list

如果你想要聪明的行为,你去别的地方找找看。

无延迟auto-revert

就在昨天,我还想研究某些简单C文件GCC的汇编输出。我打开了两个窗口:一个是C代码,另一个是汇编输出。我将后者置于 auto-revert-mode 中,这样我就能在重新编译后快速看到结果。

结果, auto-revert-mode 在我的机器上出现了一些奇怪的延迟,有时候需要几秒钟才能刷新缓冲区让我看到结果。 还不如直接在终端上运行 watch -n 0.1 cat a.s 呢。

我编写了一个小小的minor模式,它在从 inotify 获取到更改事件后就会刷新buffer,并且持续这种瞬时操作。

稍后,我检查了 auto-revert-mode 的源代码,发现它与我的minor模式大致相同,首先使用 filenotify watcher,如果不支持filenotify或调用filenotify失败,则使用轮询。 然而,它仍然需要花几秒钟时间来刷新,而我的minor mode工作正常。

如果您遇到了与 auto-revert-mode 相同的问题,您可以尝试一下 inotify-revert.el