编辑技巧
使用一些技巧,在编辑文本的过程中,更有效率,比如快速替换,快速选择,多行同时编辑等。
需求
- 快速修改特定的字符串
- 快速改变英文字符串大小写
- 修改当前文件名
- 快速移动当前文件
- 快速打开配置文件
- 代码块缩进跟随
- 代码块移动
- 快速跳转
- 自动重新加载
- 文件内容对比
- 符号自动配对
- 插入时删除选择
- 撤销管理
- 高亮光标所在符号字符
- 优化行首跳转
- 高亮光标位置
解决方案
multiple-cursors
针对多行,多光标编辑,批量修改字符串等,主要应对,在不同的位置不错相同的内容。它可以补充 iedit
的缺点,进行多光标编辑。实际上它本身包括了 iedit
的所有功能,但是对选择标记没有高亮,所以,在正常情况下,使用 iedit
更多一些,特别是批量修改内容时。只有在对不同位置进行编辑时使用 multiple-cursors
。
iedit
同时编辑相同的地方。在做代码重构的过程中经常使用的技巧,原来需要多次编辑的地方,可以一次选择标记高亮显示进行编辑。缺点,无法进行多光标编辑,比如在多行不同位置插入相同的文本内容时,它无法完成。
expand-region
扩展标记选择,对标记的内容进行扩展,收缩。有时候选择少了,可以使用它进行扩展标记,选择多了就收缩选择范围。
selected
对选择的区域进行快速编辑的快捷键绑定,比如标记一个字符串,那么可以通过简单的快捷键进行修改编辑。
undo-tree
撤销管理,显示一个编辑点的树,可以通过上下左右方向键进行快速撤销到某一个编辑点,很直观的显示,让撤销恢复变得可视化,不容易出错。
delsel
在插入数据时,删除标记选择。
elec-pair
自动匹配括号的后半部分。
ediff
快速对比两个文件的差异,支持多个文件对比,比如对不同版本的文件进行对比差异性。
hungry-delete
删除光标前后多余的空格。
autorevert
自动重新加载被外部程序修改的文件。
drag-stuff
快速移动代码块,支持行,字,标记内容等。
flyspell
拼写检查。
rainbow-mode
颜色显示,经常在编辑 css
等文件时,想要显示颜色时使用。
kurecolor
颜色编辑工具,配合 rainbow-mode
使用。
aggressive-indent
编辑代码时,自动缩进,特别是修改代码结构时非常有用,代码编辑缩进跟随当前。
anzu
搜索替换时,自动标记高亮匹配的内容。能快速识别匹配的内容。
beacon
高亮鼠标所在行,一个长长的尾巴,这样能快速的看到光标所在位置。
hl-todo
高亮 TODO
关键字,容易识别那些需要接下来做的事情。
symbol-overlay
标记高亮显示。
avy
快速跳转,可以通过字符,字等快速跳转到某行。
mwim
快速跳转行首尾,包括注释。避免跳转行尾时直接跑到注释后面。必须按两次才到最后一个字符。
goto-chg
快速跳转到最后编辑的位置。
goto-last-point
快速跳转到光标的最后位置。
dumb-jump
代码定义跳转。
origami
代码块隐藏/显示。
focus
聚焦光标位置,写作或者编写代码时用起来舒服,专注当前编辑。
实现代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
|
(use-package flyspell
:ensure t
:diminish
:if (executable-find "aspell")
:hook (((text-mode outline-mode) . flyspell-mode)
(prog-mode . flyspell-prog-mode)
(flyspell-mode . (lambda ()
(dolist (key '("C-;" "C-," "C-."))
(unbind-key key flyspell-mode-map)))))
:init (setq flyspell-issue-message-flag nil
ispell-program-name "aspell"
ispell-extra-args '("--sug-mode=ultra" "--lang=en_US" "--run-together"))
:config
;; Correcting words with flyspell via Ivy
(use-package flyspell-correct-ivy
:after ivy
:bind (:map flyspell-mode-map
([remap flyspell-correct-word-before-point] . flyspell-correct-wrapper))
:init (setq flyspell-correct-interface #'flyspell-correct-ivy)))
;; Automatically reload files was modified by external program
(use-package autorevert
:ensure t
:diminish
:hook (after-init . global-auto-revert-mode))
;; delete hungry
(use-package hungry-delete)
;; ;; Drag stuff (lines, words, region, etc...) around
(use-package drag-stuff
:diminish
:commands drag-stuff-define-keys
:hook (after-init . drag-stuff-global-mode)
:config
(add-to-list 'drag-stuff-except-modes 'org-mode)
(drag-stuff-define-keys))
;; A comprehensive visual interface to diff & patch
(use-package ediff
:ensure nil
:hook(;; show org ediffs unfolded
(ediff-prepare-buffer . outline-show-all)
;; restore window layout when done
(ediff-quit . winner-undo))
:config
(setq ediff-window-setup-function 'ediff-setup-windows-plain)
(setq ediff-split-window-function 'split-window-horizontally)
(setq ediff-merge-split-window-function 'split-window-horizontally))
;; Rectangle
(use-package rect
:ensure nil
:bind (:map text-mode-map
("<C-return>" . rect-hydra/body)
:map prog-mode-map
("<C-return>" . rect-hydra/body))
:init (with-eval-after-load 'org
(bind-key "<C-M-return>" #'rect-hydra/body org-mode-map))
:pretty-hydra
((:title "Rectangle" :color amaranth :body-pre (rectangle-mark-mode) :post (deactivate-mark) :quit-key ("q" "C-g"))
("Move"
(("h" backward-char "←")
("j" next-line "↓")
("k" previous-line "↑")
("l" forward-char "→"))
"Action"
(("w" copy-rectangle-as-kill "copy") ; C-x r M-w
("y" yank-rectangle "yank") ; C-x r y
("t" string-rectangle "string") ; C-x r t
("d" kill-rectangle "kill") ; C-x r d
("c" clear-rectangle "clear") ; C-x r c
("o" open-rectangle "open")) ; C-x r o
"Misc"
(("N" rectangle-number-lines "number lines") ; C-x r N
("e" rectangle-exchange-point-and-mark "exchange") ; C-x C-x
("u" undo "undo")
("r" (if (region-active-p)
(deactivate-mark)
(rectangle-mark-mode 1))
"reset")))))
;; Automatic parenthesis pairing
(use-package elec-pair
:ensure nil
:hook (after-init . electric-pair-mode)
:init (setq electric-pair-inhibit-predicate 'electric-pair-conservative-inhibit))
;; Edit multiple regions in the same way simultaneously
(use-package iedit
:defines desktop-minor-mode-table
:bind (("C-;" . iedit-mode)
("C-x r RET" . iedit-rectangle-mode)
:map isearch-mode-map ("C-;" . iedit-mode-from-isearch)
:map esc-map ("C-;" . iedit-execute-last-modification)
:map help-map ("C-;" . iedit-mode-toggle-on-function))
:config
;; Avoid restoring `iedit-mode'
(with-eval-after-load 'desktop
(add-to-list 'desktop-minor-mode-table
'(iedit-mode nil))))
;; Delete selection if you insert
(use-package delsel
:hook (after-init . delete-selection-mode))
;; edit undo tree
(use-package undo-tree
:init
(global-undo-tree-mode))
(use-package rainbow-mode
:hook ((html-mode css-mode) . rainbow-mode)
:bind (("C-x c m" . rainbow-mode-hydra/body))
:pretty-hydra
((:title (pretty-hydra-title "Colors Management" 'faicon "windows")
:foreign-keys warn :quit-key "q")
("Actions"
(("w" kurecolor-decrease-brightness-by-step "kurecolor-decrease-brightness-by-step")
("W" kurecolor-increase-brightness-by-step "kurecolor-increase-brightness-by-step")
("d" kurecolor-decrease-saturation-by-step "kurecolor-decrease-saturation-by-step")
("D" kurecolor-increase-saturation-by-step "kurecolor-increase-saturation-by-step")
("e" kurecolor-decrease-hue-by-step "kurecolor-decrease-hue-by-step")
("E" kurecolor-increase-hue-by-step "kurecolor-increase-hue-by-step"))))
:config
(use-package kurecolor))
;; Highlight brackets according to their depth
(use-package rainbow-delimiters
:hook (prog-mode . rainbow-delimiters-mode))
;; Increase selected region by semantic units
(use-package expand-region
:bind (("M-+" . er/expand-region)
("M--" . er/contract-region)))
(use-package selected
:bind (:map selected-keymap
("c" . copy-region-as-kill)
("e" . er/expand-region)
("E" . er/contract-region)
("l" . downcase-region)
("u" . upcase-region)
("w" . kill-region)
("W" . count-words-region)
("m" . apply-macro-to-region-lines)
("/" . indent-region)
(";" . comment-or-uncomment-region))
:init
(selected-global-mode))
;; Multiple cursors
(use-package multiple-cursors
:pretty-hydra
((:title (pretty-hydra-title "Multiple-Cursors" 'material "border_all" :height 1.1 :v-adjust -0.225)
:color amaranth :quit-key ("q" "C-g"))
("Actions"
(
("@" mc/edit-lines "edit lines")
("," mc/skip-to-next-like-this "skip to next like this")
("." mc/skip-to-previous-like-this "skip to previous like this")
("/" mc/mark-pop "Mark pop")
(";" mc/mark-all-words-like-this "Only mark all matches word")
("[" mc/mark-next-word-like-this "Only mark next like this world")
("]" mc/mark-previous-word-like-this "Only mark previous like this word")
("+" mc/mark-all-words-like-this-in-defun "Only mark all current function matches word")
("f" mc/mark-all-like-this-in-defun "Mark all current function matches the current region")
)
"---"
(
("m" mc/mark-all-like-this "Mark all matches the current region")
("n" mc/mark-next-like-this "Add cursor to next line")
("M" mc/mark-all-dwim "Smart mark all")
("N" mc/unmark-next-like-this "Unmark next like this")
("p" mc/mark-previous-like-this "Add cursor to previous line")
("P" mc/unmark-previous-like-this "Unmark previous like this")
("w" mc/mark-next-like-this-word "Mark next like this word")
("W" mc/mark-previous-like-this-word "Mark previous like this word")
("<mouse-1>" mc/add-cursor-on-click "Bind to a mouse event to add cursors by clicking")
)))
:bind (("C-M-<mouse-1>" . mc/add-cursor-on-click)
("<mouse-1>" . mc/keyboard-quit)
("C->" . mc/mark-next-lines)
("C-<" . mc/mark-previous-lines)
("<double-mouse-1>" . mouse-set-point)
("C-M-SPC" . set-rectangular-region-anchor)
("C->" . mc/mark-next-like-this)
("C-<" . mc/mark-previous-like-this)
("C-c C->" . mc/mark-all-like-this)
("C-c SPC" . mc/edit-lines)
:map mc/keymap
("C-|" . mc/vertical-align-with-space))
:config
(defun my-append-to-list (list-var elements)
"Append ELEMENTS to the end of LIST-VAR.
The return value is the new value of LIST-VAR."
(unless (consp elements)
(error "ELEMENTS must be a list"))
(let ((list (symbol-value list-var)))
(if list
(setcdr (last list) elements)
(set list-var elements)))
(symbol-value list-var))
(with-eval-after-load 'multiple-cursors-core
(my-append-to-list 'mc--default-cmds-to-run-once '(swiper-mc
mouse-drag-region-rectangle
mc/vertical-align-with-space
multiple-cursors-hydra/body
multiple-cursors-hydra/mc/mark-next-like-this
multiple-cursors-hydra/mc/mark-next-like-this-word
multiple-cursors-hydra/mc/mark-next-word-like-this
multiple-cursors-hydra/mc/mark-previous-like-this
multiple-cursors-hydra/mc/mark-previous-like-this-word
multiple-cursors-hydra/mc/mark-previous-word-like-this
multiple-cursors-hydra/mc/add-cursor-on-click
multiple-cursors-hydra/mc/mark-pop
multiple-cursors-hydra/mc/unmark-next-like-this
multiple-cursors-hydra/mc/unmark-previous-like-this
multiple-cursors-hydra/mc/skip-to-next-like-this
multiple-cursors-hydra/mc/skip-to-previous-like-this
multiple-cursors-hydra/mc/edit-lines
multiple-cursors-hydra/mc/mark-all-like-this
multiple-cursors-hydra/mc/mark-all-words-like-this
multiple-cursors-hydra/mc/mark-all-like-this-in-defun
multiple-cursors-hydra/mc/mark-all-words-like-this-in-defun
multiple-cursors-hydra/mc/mark-all-dwim
multiple-cursors-hydra/nil))
;; (my-append-to-list 'mc--default-cmds-to-run-for-all '(mc/vertical-align-with-space))
(mc/save-lists))
(with-eval-after-load 'hungry-delete
(add-to-list 'mc--default-cmds-to-run-for-all 'hungry-delete-backward)
(mc/save-lists))
:init
(setq mc/list-file (concat dotfairy-etc-dir "mc-lists.el")))
;; Smartly select region, rectangle, multi cursors
(use-package smart-region
:hook (after-init . smart-region-on))
(use-package wgrep
:init
(setq wgrep-auto-save-buffer t
wgrep-change-readonly-file t))
;; Minor mode to aggressively keep your code always indented
(use-package aggressive-indent
:diminish
:hook ((after-init . global-aggressive-indent-mode)
;; FIXME: Disable in big files due to the performance issues
;; https://github.com/Malabarba/aggressive-indent-mode/issues/73
(find-file . (lambda ()
(if (> (buffer-size) (* 3000 80))
(aggressive-indent-mode -1)))))
:config
;; Disable in some modes
(dolist (mode '(asm-mode web-mode html-mode css-mode go-mode scala-mode prolog-inferior-mode))
(push mode aggressive-indent-excluded-modes))
;; Disable in some commands
(add-to-list 'aggressive-indent-protected-commands #'delete-trailing-whitespace t)
;; Be slightly less aggressive in C/C++/C#/Java/Go/Swift
(add-to-list 'aggressive-indent-dont-indent-if
'(and (derived-mode-p 'c-mode 'c++-mode 'csharp-mode
'java-mode 'go-mode 'swift-mode)
(null (string-match "\\([;{}]\\|\\b\\(if\\|for\\|while\\)\\b\\)"
(thing-at-point 'line))))))
;; Show number of matches in mode-line while searching
(use-package anzu
:diminish
:bind (([remap query-replace] . anzu-query-replace)
([remap query-replace-regexp] . anzu-query-replace-regexp)
:map isearch-mode-map
([remap isearch-query-replace] . anzu-isearch-query-replace)
([remap isearch-query-replace-regexp] . anzu-isearch-query-replace-regexp))
:hook (after-init . global-anzu-mode))
;; Redefine M-< and M-> for some modes
(use-package beginend
:diminish (beginend-mode beginend-global-mode)
:hook (after-init . beginend-global-mode)
:config
(mapc (lambda (pair)
(add-hook (car pair) (lambda () (diminish (cdr pair)))))
beginend-modes))
(use-package beacon
:ensure t
:init
(beacon-mode 1)
:config
(setq beacon-color "#666600"))
;; TODO
(use-package hl-todo
:hook (after-init . global-hl-todo-mode)
:config
(dolist (keyword '("BUG" "DEFECT" "ISSUE"))
(cl-pushnew `(,keyword . ,(face-foreground 'error)) hl-todo-keyword-faces))
(dolist (keyword '("WORKAROUND" "HACK" "TRICK"))
(cl-pushnew `(,keyword . ,(face-foreground 'warning)) hl-todo-keyword-faces)))
;; Highlight symbols
(use-package symbol-overlay
:diminish
:functions (turn-off-symbol-overlay turn-on-symbol-overlay)
:custom-face (symbol-overlay-default-face ((t (:inherit (region bold)))))
:bind (("M-i" . symbol-overlay-put)
("M-n" . symbol-overlay-jump-next)
("M-p" . symbol-overlay-jump-prev)
("M-N" . symbol-overlay-switch-forward)
("M-P" . symbol-overlay-switch-backward)
("M-C" . symbol-overlay-remove-all)
([M-f3] . symbol-overlay-remove-all))
:hook ((prog-mode . symbol-overlay-mode)
(iedit-mode . turn-off-symbol-overlay)
(iedit-mode-end . turn-on-symbol-overlay))
:init (setq symbol-overlay-idle-time 0.1)
:config
;; Disable symbol highlighting while selecting
(defun turn-off-symbol-overlay (&rest _)
"Turn off symbol highlighting."
(interactive)
(symbol-overlay-mode -1))
(advice-add #'set-mark :after #'turn-off-symbol-overlay)
(defun turn-on-symbol-overlay (&rest _)
"Turn on symbol highlighting."
(interactive)
(when (derived-mode-p 'prog-mode)
(symbol-overlay-mode 1)))
(advice-add #'deactivate-mark :after #'turn-on-symbol-overlay))
;; Jump to things in Emacs tree-style
(use-package avy
:bind (("C-:" . avy-goto-char)
("C-'" . avy-goto-char-2)
("M-g f" . avy-goto-line)
("M-g w" . avy-goto-word-1)
("M-g e" . avy-goto-word-0))
:hook (after-init . avy-setup-default)
:config (setq avy-all-windows nil
avy-all-windows-alt t
avy-background t
avy-style 'pre))
(use-package mwim
:bind (([remap move-beginning-of-line] . mwim-beginning-of-code-or-line)
([remap move-end-of-line] . mwim-end-of-code-or-line)
:map prog-mode-map
("C-a" . mwim-beginning-of-code-or-line-or-comment)
("C-e" . mwim-end-of-code-or-line)))
;; Goto last change
(use-package goto-chg
:bind ("C-," . goto-last-change))
;; Record and jump to the last point in the buffer
(use-package goto-last-point
:diminish
:bind ("C-." . goto-last-point)
:hook (after-init . goto-last-point-mode))
;; Jump to definition
(use-package dumb-jump
:pretty-hydra
((:title "Dump Jump" :color blue :quit-key "q")
("Jump"
(("j" dumb-jump-go "Go")
("o" dumb-jump-go-other-window "Go other window")
("e" dumb-jump-go-prefer-external "Go external")
("x" dumb-jump-go-prefer-external-other-window "Go external other window"))
"Other"
(("i" dumb-jump-go-prompt "Prompt")
("l" dumb-jump-quick-look "Quick look")
("b" dumb-jump-back "Back"))))
:bind (("M-g o" . dumb-jump-go-other-window)
("M-g j" . dumb-jump-go)
("M-g i" . dumb-jump-go-prompt)
("M-g x" . dumb-jump-go-prefer-external)
("M-g z" . dumb-jump-go-prefer-external-other-window)
("C-M-j" . dumb-jump-hydra/body))
:init
(add-hook 'xref-backend-functions #'dumb-jump-xref-activate)
(setq dumb-jump-prefer-searcher 'rg
dumb-jump-selector 'ivy))
(use-package editorconfig
:diminish
:hook (after-init . editorconfig-mode))
;; Hideshow
(use-package hideshow
:diminish hs-minor-mode
:bind (:map hs-minor-mode-map
("C-`" . hs-toggle-hiding)))
;; Flexible text folding
(use-package origami
:pretty-hydra
((:title "Origami" :color amaranth :quit-key "q")
("Node"
((":" origami-recursively-toggle-node "toggle recursively")
("a" origami-toggle-all-nodes "toggle all")
("t" origami-toggle-node "toggle current")
("o" origami-show-only-node "only show current"))
"Actions"
(("u" origami-undo "undo")
("d" origami-redo "redo")
("r" origami-reset "reset"))))
:bind (:map origami-mode-map
("C-`" . origami-hydra/body))
:hook (prog-mode . origami-mode)
:init (setq origami-show-fold-header t)
:config (face-spec-reset-face 'origami-fold-header-face)
;; lsp-origami provides support for origami.el using language server protocol’s
;; textDocument/foldingRange functionality.
;; https://github.com/emacs-lsp/lsp-origami/
(use-package lsp-origami
:hook ((lsp-after-open . lsp-origami-try-enable))))
(provide 'init-iedit)
;;; init-iedit.el ends here
|