How can I swap or replace multiple lines in code at the same time?

Given the following code example:

uint8_t i, in, ni; i = in = 2; ni = 1; while (2 == i > ni) in++; 

How to replace i, in, and ni with in, ni, and i or inni, inin, and nini using the emacs, vi, * nix commands or something else?

+6
c bash vi emacs refactoring
source share
5 answers

If I'm not mistaken, the solutions provided so far (using Perl and Vim) do not work properly if any of the replacements is among the last words to be replaced. In particular, none of the solutions work for the first example: "i" will be replaced by "in", which will then be incorrectly replaced by "ni", and then back to "i" by subsequent rules, while it should remain how in".

Substitutions cannot be considered independent and applied consistently; they must be applied in parallel.

In Emacs, you can do this:

Mx parallel-replace ,

and at the command prompt enter

i in in ni ni i .

Replacements will be made between the cursor and the end of the buffer or in the area, if selected.

(If you have this definition in ~/.emacs.d/init.el

 (require 'cl) (defun parallel-replace (plist &optional start end) (interactive `(,(loop with input = (read-from-minibuffer "Replace: ") with limit = (length input) for (item . index) = (read-from-string input 0) then (read-from-string input index) collect (prin1-to-string item t) until (<= limit index)) ,@(if (use-region-p) `(,(region-beginning) ,(region-end))))) (let* ((alist (loop for (key val . tail) on plist by #'cddr collect (cons key val))) (matcher (regexp-opt (mapcar #'car alist) 'words))) (save-excursion (goto-char (or start (point))) (while (re-search-forward matcher (or end (point-max)) t) (replace-match (cdr (assoc-string (match-string 0) alist))))))) 

Edit (2013-08-20):

Several improvements:

  • For the special case, when only two elements are specified, exchange instead (i.e. replace each other);
  • Request confirmation for each replacement in the same way as query-replace .
 (require 'cl) (defun parallel-query-replace (plist &optional delimited start end) "Replace every occurrence of the (2n)th token of PLIST in buffer with the (2n+1)th token; if only two tokens are provided, replace them with each other (ie, swap them). If optional second argument DELIMITED is nil, match words according to syntax-table; otherwise match symbols. When called interactively, PLIST is input as space separated tokens, and DELIMITED as prefix arg." (interactive `(,(loop with input = (read-from-minibuffer "Replace: ") with limit = (length input) for j = 0 then i for (item . i) = (read-from-string input j) collect (prin1-to-string item t) until (<= limit i)) ,current-prefix-arg ,@(if (use-region-p) `(,(region-beginning) ,(region-end))))) (let* ((alist (cond ((= (length plist) 2) (list plist (reverse plist))) ((loop for (key val . tail) on plist by #'cddr collect (list (prin1-to-string key t) val))))) (matcher (regexp-opt (mapcar #'car alist) (if delimited 'words 'symbols))) (to-spec `(replace-eval-replacement replace-quote (cadr (assoc-string (match-string 0) ',alist case-fold-search))))) (query-replace-regexp matcher to-spec nil start end))) 
+10
source share

Emacs shorter solution:

CM-% (query-replace-regexp)

To match: \ <\ (i \ | in \ | ni \) \> (I assume you only want to combine whole words)

Replace with: \, (case (intern \ 1) (i "in") (in "ni") (ni "i"))

At some point you need require 'cl to do this in order to get the case macro from the CL package. You could achieve the same effect without this package, but it would not be so difficult.

EDIT ADD: Actually, it can be as brief as I understood when I recently answered a similar question about Reddit.

To match: \<\(?:\(i\)\|\(in\)\|ni\)\>

Replace with: \,(if \1 "in" (if \2 "ni" "i"))

+12
source share
 > cat foo uint8_t i, in, ni; i = in = 2; ni = 1; while (2 == i > ni) in++; > perl -p -i -e 's/\bi\b/inni/; s/\bin\b/inin/; s/\bni\b/i/;' foo > cat foo uint8_t inni, inin, i; inni = inin = 2; i = 1; while (2 == inni > i) inin++; > 

You can use any other tool that supports regular expressions besides perl.

+4
source share

In vim , you can have multiple commands separated by | , so you can do the replacement with the %s command:

 :%s/\<i\>/inni/ | %s/\<in\>/inin/ | %s/\<ni\>/nini/ 

Soon, this means:

 %s/search/replacement/ 

Searches for the specified search template and replaces its replacement ;

the characters " \< " and " \> " stand here as word boundaries.

+2
source share

The easiest way is to use the gedit replacement function.

In vi ,

%s/old/new/g will replace all occurrences of the "old" with "new" through the file.

+1
source share

All Articles