在emacs中矩形内查找和替换

33

在Emacs中,是否可以在矩形区域内执行replace-string操作?如果可以,如何操作?

4个回答

30
如果您启用CUA选择模式:

M-x cua-selection-mode RET

或者在您的初始化文件中永久启用:
(cua-selection-mode 1)

你可以使用其高级矩形编辑功能。

(如果你是 cua-mode 用户,则不需要这样做.)

  • C-RET 选择一个角落。
  • 将光标移动到对面的角落上。
  • M-r 在已选择的矩形内进行 regexp 替换。
  • C-RET 取消选择/退出矩形编辑。

有关文档,请在 M-x find-library RET cua-base RET 中查找 "CUA rectangle support" 标题。

如果出于某些原因您不想使用cua矩形操作(可能您确实需要特别的replace-string),那么使用apply-on-rectangle编写自定义函数将会非常简单。

编辑: 实际上比我预期的要稍微复杂一些,但大部分代码都是基于replace-string的交互规范和对“delimited”前缀参数行为的支持。

编辑2: 我决定以一种更全面的方式完成这个任务:

以下提供了 C-xrM-%C-xrC-M-%,它们会按照您的预期进行操作。

(require 'rect)

(defun my-search-replace-in-rectangle
  (start end search-pattern replacement search-function literal)
  "Replace all instances of SEARCH-PATTERN (as found by SEARCH-FUNCTION)
with REPLACEMENT, in each line of the rectangle established by the START
and END buffer positions.

SEARCH-FUNCTION should take the same BOUND and NOERROR arguments as
`search-forward' and `re-search-forward'.

The LITERAL argument is passed to `replace-match' during replacement.

If `case-replace' is nil, do not alter case of replacement text."
  (apply-on-rectangle
   (lambda (start-col end-col search-function search-pattern replacement)
     (move-to-column start-col)
     (let ((bound (min (+ (point) (- end-col start-col))
                       (line-end-position)))
           (fixedcase (not case-replace)))
       (while (funcall search-function search-pattern bound t)
         (replace-match replacement fixedcase literal))))
   start end search-function search-pattern replacement))

(defun my-replace-regexp-rectangle-read-args (regexp-flag)
  "Interactively read arguments for `my-replace-regexp-rectangle'
or `my-replace-string-rectangle' (depending upon REGEXP-FLAG)."
  (let ((args (query-replace-read-args
               (concat "Replace"
                       (if current-prefix-arg " word" "")
                       (if regexp-flag " regexp" " string"))
               regexp-flag)))
    (list (region-beginning) (region-end)
          (nth 0 args) (nth 1 args) (nth 2 args))))

(defun my-replace-regexp-rectangle
  (start end regexp to-string &optional delimited)
  "Perform a regexp search and replace on each line of a rectangle
established by START and END (interactively, the marked region),
similar to `replace-regexp'.

Optional arg DELIMITED (prefix arg if interactive), if non-nil, means
replace only matches surrounded by word boundaries.

If `case-replace' is nil, do not alter case of replacement text."
  (interactive (my-replace-regexp-rectangle-read-args t))
  (when delimited
    (setq regexp (concat "\\b" regexp "\\b")))
  (my-search-replace-in-rectangle
   start end regexp to-string 're-search-forward nil))

(defun my-replace-string-rectangle
  (start end from-string to-string &optional delimited)
  "Perform a string search and replace on each line of a rectangle
established by START and END (interactively, the marked region),
similar to `replace-string'.

Optional arg DELIMITED (prefix arg if interactive), if non-nil, means
replace only matches surrounded by word boundaries.

If `case-replace' is nil, do not alter case of replacement text."
  (interactive (my-replace-regexp-rectangle-read-args nil))
  (let ((search-function 'search-forward))
    (when delimited
      (setq search-function 're-search-forward
            from-string (concat "\\b" (regexp-quote from-string) "\\b")))
    (my-search-replace-in-rectangle
     start end from-string to-string search-function t)))

(global-set-key (kbd "C-x r M-%") 'my-replace-string-rectangle)
(global-set-key (kbd "C-x r C-M-%") 'my-replace-regexp-rectangle)

1
谢谢!不幸的是,在emacs GUI中,我只能用C-RET标记一个角落,而在终端模式下则不行。 - user545424
这可能是由于您的终端未发送该序列所致。您可能会发现xterm更成功(另请参阅https://dev59.com/m2855IYBdhLWcg3wcz5h)。我还使用更完整的代码更新了我的答案,以适用于非CUA方法。 - phils
功能请求:搜索矩形。我有一个巨大的列,需要检查它是否有除了“DPP”之外的任何内容。我使用一些复杂的正则表达式 - 如果该列非常大 - 我会得到正则表达式堆栈溢出。 - Adobe
只是给其他可能和我一样困惑的人一个提示 - 当选定一个矩形时,M-r 绑定到 cua-replace-in-rectangle。 - Michael Paulukonis
谢谢您提供的Lisp,它可以直接使用! - hell_ical_vortex

6
在最近的Emacs版本(例如Emacs 25)中,您可以直接使用M-%query-replace)或C-M-%query-replace-regexp)来进行替换操作。
使用M-x rectangle-mark-mode创建矩形区域。如果需要,在将点放在标记之前时使用C-x C-x。然后进行查询替换即可。
然而,对于replace-string,Emacs 25没有做出相同的更改。如果您愿意,可以通过向其添加参数region-noncontiguous-p来实现与query-replace相同的功能。代码很简单:
(defun replace-string (from-string to-string &optional delimited start end backward
                       region-noncontiguous-p)
  "..."
  (declare (interactive-only
            "use `search-forward' and `replace-match' instead."))
  (interactive
   (let ((common
          (query-replace-read-args
           (concat "Replace"
                   (if current-prefix-arg
                       (if (eq current-prefix-arg '-) " backward" " word")
                     "")
                   " string"
                   (if (use-region-p) " in region" ""))
           nil)))
     (list (nth 0 common) (nth 1 common) (nth 2 common)
           (if (use-region-p) (region-beginning))
           (if (use-region-p) (region-end))
           (nth 3 common)
           (if (use-region-p) (region-noncontiguous-p)))))
  (perform-replace
    from-string to-string nil nil delimited nil nil start end backward region-noncontiguous-p))

您可以选择下载库replace+.el,使用其中的replace-string版本,它可以实现您想要的功能。如果您需要更多帮助,请参考链接。
另外,我刚刚在Emacs bug#27897上提交了一个问题,希望能够将此功能添加到replace-string和其他相关命令中,这些命令都包含在同一库中:replace.el

还有一个与此相关的anzu bug(可能会替换您的query-replace版本):https://github.com/syohex/emacs-anzu/issues/69 - Micah Elliott

2

对于恶意用户,你可以使用我的软件包evil-visual-replace在恶意视觉块内获取 query-replacereplace-regexp 功能。


0

如果您处于CUA模式下,可以使用绑定到'M-r'的cua-replace-in-rectangle。


但是这只会替换第一个匹配到的内容(默认 Vim replace-regexp 行为)。 - Adobe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接