基于标记对象的键盘宏展开插件
Emacs
2022-11-24 6222字

写代码时, 一般我们有两种多标签使用场景:

  1. 快速重命名: 一般都是重命名光标下的 相同符号 , 这方面 symbol-overlay 做的最简单易用
  2. 数据清洗: 快速把不同名字的符号进行格式化清洗, 从一种格式转换成另外一种格式, 这方面 Meow 的键盘宏做的最好用

但是我平常写代码的时间远远多于看代码的时间, 非常不习惯任何模式转换插件, 所以我下午研究了一下 Meow 的键盘宏原理, 重写了一个新的插件 markmacro

markmacro 的原理和 Meow 键盘宏的理念一样:

  1. 用 overlay 快速标记一系列对象, 并自动启动键盘宏记录
  2. 对最后一个对象进行任意操作
  3. 最后操作一下, 把一个对象上的所有键盘宏扩展到所有标记的对象

通过上面三个步骤, 我们灵活结合 Emacs 其他插件的能力, 快速清洗数据成我们期望的格式, 提升我们日常的编程效率。

为了直观的让大家理解这个插件的作用, 我编写了一些测试用例:

Case 1

fruit = apple watermelon peaches

=>

fruit = ["apple", "watermelon", "peaches"]

Case 2

it-is-ver-long-variable

=>

it_is_ver_long_variable

Case 3

flex-auto
flex-col

flex-col-reverse
flex-grow
flex-initial

flex-no-wrap
flex-none
flex-nowrap
flex-row

=>

prefix-flex-auto.txtt
prefix-flex-col.txtt

prefix-flex-col-reverse.txtt
prefix-flex-grow.txtt
prefix-flex-initial.txtt

prefix-flex-no-wrap.txtt
prefix-flex-none.txtt
prefix-flex-nowrap.txtt
prefix-flex-row.txt

Case 4

("s-/" . markmacro-mark-WORDS)
("s-?" . markmacro-mark-LINES)
("s-<" . markmacro-apply-ALL)
("s->" . markmacro-apply-FIRST)

=>

("s-/" . markmacro-words)
("s-?" . markmacro-lines)
("s-<" . markmacro-all)
("s->" . markmacro-first)

Case 5

("s-/" . markmacro-mark-words)
("s-?" . markmacro-mark-lines)
("s-<" . markmacro-apply-all)
("s->" . markmacro-apply-all-except-first)
("s-M" . markmacro-rect-set)
("s-D" . markmacro-rect-delete)
("s-F" . markmacro-rect-replace)
("s-I" . markmacro-rect-insert)
("s-C" . markmacro-rect-mark-columns)
("s-S" . markmacro-rect-mark-symbols)

=>

(global-set-key (kbd "s-/") 'markmacro-mark-words)
(global-set-key (kbd "s-?") 'markmacro-mark-lines)
(global-set-key (kbd "s-<") 'markmacro-apply-all)
(global-set-key (kbd "s->") 'markmacro-apply-all-except-first)
(global-set-key (kbd "s-M") 'markmacro-rect-set)
(global-set-key (kbd "s-D") 'markmacro-rect-delete)
(global-set-key (kbd "s-F") 'markmacro-rect-replace)
(global-set-key (kbd "s-I") 'markmacro-rect-insert)
(global-set-key (kbd "s-C") 'markmacro-rect-mark-columns)
(global-set-key (kbd "s-S") 'markmacro-rect-mark-symbols)

Case 6

(defun test-function-1 (arg)
  )

(defun test-function-2 (arg)
  )

(defun test-function-3 (arg)
  )

=>

(defun test-function-1 (new-arg arg)
  )

(defun test-function-2 (new-arg arg)
  )

(defun test-function-3 (new-arg arg)
  )

Case 7

(defun markmacro-mark-words ()
  (interactive)
  (let ((bound (if (region-active-p)
                   (cons (region-beginning) (region-end))
                 (bounds-of-thing-at-point 'symbol)))
        (mark-bounds '()))
    (when bound
      (when (region-active-p)
        (deactivate-mark))

      (let ((mark-bound-start (car bound))
            (mark-bound-end (cdr bound))
            (last-point 0)
            current-bound)
        (save-excursion
          (goto-char mark-bound-start)
          (while (and (<= (point) mark-bound-end)
                      (> (point) last-point))
            (setq current-bound (bounds-of-thing-at-point 'word))
            (when current-bound
              (add-to-list 'mark-bounds current-bound t))
            (setq last-point (point))
            (forward-word))))

      (dolist (bound mark-bounds)
        (let* ((overlay (make-overlay (car bound) (cdr bound))))
          (overlay-put overlay 'face 'markmacro-mark-face)
          (add-to-list 'markmacro-overlays overlay t)))

      (markmacro-select-last-overlay))))

=>

(defun markmacro-mark-words ()
  (interactive)
  (let ((bound (if (region-active-p)
                   (cons (region-beginning) (region-end))
                 (bounds-of-thing-at-point 'symbol)))
        (mark-bounds '()))
    (when bound
      (when (region-active-p)
        (deactivate-mark))

      (let ((mark-bound-start (car bound))
            (mark-bound-end (cdr bound))
            (last-point 0)
            new-current-bound)
        (save-excursion
          (goto-char mark-bound-start)
          (while (and (<= (point) mark-bound-end)
                      (> (point) last-point))
            (setq new-current-bound (bounds-of-thing-at-point 'word))
            (when new-current-bound
              (add-to-list 'mark-bounds new-current-bound t))
            (setq last-point (point))
            (forward-word))))

      (dolist (bound mark-bounds)
        (let* ((overlay (make-overlay (car bound) (cdr bound))))
          (overlay-put overlay 'face 'markmacro-mark-face)
          (add-to-list 'markmacro-overlays overlay t)))

      (markmacro-select-last-overlay))))

Case 8

window.scrollTo(0, 0); document.getElementsByTagName('html')[0].style.visibility = 'hidden'; document.getElementsByClassName('lf_area')[0].style.visibility = 'visible'; document.getElementsByTagName('header')[0].style.display = 'none'; document.getElementsByClassName('contentPadding')[0].style.padding = '10px';

=> 

window.scrollTo(0, 0);
document.getElementsByTagName('html')[0].style.visibility = 'hidden';
document.getElementsByClassName('lf_area')[0].style.visibility = 'visible';
document.getElementsByTagName('header')[0].style.display = 'none';
document.getElementsByClassName('contentPadding')[0].style.padding = '10px';

最后

用心体验一下, 就会发现这个插件带来的生产力非常高。 ;)