Подтвердить что ты не робот

Поиск и замена внутри прямоугольника в emacs

Можно ли выполнить операцию replace-string внутри прямоугольной области в emacs? Если да, то как?

4b9b3361

Ответ 1

Если вы включите режим выбора CUA:

M-x cua-selection-mode RET

или постоянно в файле инициализации:

(cua-selection-mode 1)

Затем вы можете использовать свои усовершенствованные средства редактирования прямоугольника.

(или если вы пользователь cua-mode, то вам не нужно это делать.)

  • C-RET, чтобы отметить угол.
  • Переместить точку в противоположный угол.
  • M-r выполнить замену регулярного выражения в отмеченном прямоугольнике.
  • C-RET, чтобы отменить/удалить редактирование прямоугольника.

Для документации найдите заголовок "Поддержка прямоугольника CUA" в комментарии в M-x find-library RET cua-base RET

Если вы не хотите использовать объекты прямоугольника cua по какой-либо причине (возможно, если вам действительно нужно replace-string), пользовательская функция, использующая apply-on-rectangle, будет довольно просто скомпоновать.

Изменить: На самом деле, немного сложнее, чем я ожидал, но большая часть кода - это интерактивная спецификация и поддержка поведения аргументов префиксного разделителя (как на основе replace-string),.

Изменить 2. Я решил, что это стоит делать более полно:

Ниже приведены C-x r M-% и C-x r C-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)

Ответ 2

В недавних Emacs (например, Emacs 25) вы можете сделать это из коробки, используя M-% (query-replace) или C-M-% (query-replace-regexp).

Используйте M-x rectangle-mark-mode для создания прямоугольной области. Используйте C-x C-x, если необходимо, чтобы поставить точку перед меткой. Затем просто замените запрос.

Тем не менее, они не сделали то же самое изменение в Emacs 25 для replace-string. Вы можете сделать это, если хотите, просто добавив аргумент 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 оттуда. Он делает то, что вы хотите.

FWIW, я только что отправил Emacs ошибка # 27897, чтобы добавить эту функцию в replace-string и несколько других команд в том же библиотека, replace.el.

Ответ 3

Если вы находитесь в режиме CUA, вы можете использовать cua-replace-in-rectangle, привязанный к 'M-r'.

Ответ 4

Для злых пользователей вы можете использовать мой пакет evil-visual-replace, чтобы получить query-replace и replace-regexp внутри злых визуальных блоков.