分享一个Emacs酷炫操作:交换两个s-expression子表达式

在编辑 Lisp 代码的时候,有时需要对两个子表达式进行交换。

注:尽管这样的场景不多见,一旦出现还挺烦人的(因为剪切会导致一个空白,完了之后还要手动调整)。

我在网上查了下,发现 Emacs 已经内置了这个功能 transpose-sexps

然而,这个功能的描述挺费解的,使用起来也不方便。

今天经过多次实验后,终于明白了它的用法,重新封装了两个更加直观的接口:

;;----------------------------------------------------------------------------
;; Drag S-Expression
;;----------------------------------------------------------------------------
(defun drag-prev-sexp-forward ()
  (interactive)
  (call-interactively 'transpose-sexps))

(defun drag-prev-sexp-backward ()
  (interactive)
  (let ((current-prefix-arg '(-1)))
    (call-interactively 'transpose-sexps)))

drag-prev-sexp-forward 的意思是:把当前光标之前的 sexp 和它后面的 sexp 交换

drag-prev-sexp-backward 的意思是:把当前光标之前 sexp 和它再前面 的 sexp 交换

注意:这两个命令要求光标必须出现在两个 sexp 表达式的中间。但在实际用的时候,我们只要光标出现在待 drag 的表达式的末尾即可。

下面是演示,很酷炫把:

drag-sexp

学会这个操作,在给人做演示或演讲的时候会加分哦~

:stuck_out_tongue: 赶快绑上你最喜欢的快捷键吧 :stuck_out_tongue:

7 个赞

以前写的一个扩展:

1 个赞

说到这个 emacs 有内置的 secondary selection. 可以用鼠标按着 alt 选,不过没有内置的交换两个 selection 的功能。

1 个赞

关于transpose有这么多的用法,而且默认已经有快捷键了。感觉没有必要再封装。而且默认的快捷键也非常容易理解,符合C, M, C-M越来越大的规则。

4 个赞

经常编辑LISP代码建议尝试下lispy。刚开始会有些别扭,但是习惯以后操作sexp确实比用组合键快捷得多。例如移动光标下的sexp就是W/S键,跟打游戏的键位差不多。

1 个赞

请问有没有交换 C/C++ 函数中参数位置的函数?

PS: 找到了一个解决方案,可以用于 C++ 交换参数位置

Lispy(如果不喜欢vim-like按键的可以看下paredit)确实不错,但是他们都默认占用了太多的按键。导致很多时候,你按下某些键的时候,与你想的根本不一样。

swap region

交换方便不方便要看选择,如果选起来很方便的话。。。

当然临近的两个还是 transpose 好用。

transpose-确实有很多变体,但transpose-sexp的用法和其他变体不完全一致。

这个封装只是让它更符合直觉(此外,prefix args 也是一个记忆负担)。

当然,以上的判断完全是主观的。

2 个赞

确实不完全一样,但非常容易理解。基本上就是交换光标左右的单位。sexp要求光标必须在两个exp中间而已。

我倒是觉得-lines才是比较怪的那个,竟然必须与上一行换,即便是在行尾。

2 个赞

(dotimes (n 10)
  (insert (format "%02d -- %02d\n" n n)))
(defun create-marker-at-point (&optional point)
  (if point
      (save-excursion
        (goto-char point)
        (point-marker))
    (point-marker)))

(defun transpose-region-selection ()
  (interactive)
  (when (and (secondary-selection-exist-p) (use-region-p))
    (let* ((selection (car (secondary-selection-exist-p)))
           (s-beg (create-marker-at-point (overlay-start selection)))
           (s-end (create-marker-at-point (overlay-end selection)))
           (r-beg (create-marker-at-point (region-beginning)))
           (r-end (create-marker-at-point (region-end))))
      (transpose-regions r-beg r-end
                         s-beg s-end)

      ;; move secondary-selection to original region (swap)
      (delete-overlay selection)
      (move-overlay selection (marker-position s-beg) (marker-position r-end))

      ;; move region to original secondary-selection (swap)
      (push-mark (marker-position r-beg) t t)
      (goto-char (marker-position s-end))
      (activate-mark)
      ;; Make region mark activate
      (setq deactivate-mark nil)

      (set-marker s-beg nil)
      (set-marker s-end nil)
      (set-marker r-beg nil)
      (set-marker r-end nil)
      )))
1 个赞