event_jr
event_jr

Reputation: 17707

emacs query-replace-regexp inverted

Is there an existing package that targets subexps for replacement during query-replace-regexp?

For example given the following

var foo1 = blah( properties, property_id);

var foo2 = blah(properties, property_id );

var foo3 = blah(  properties, property_id      );

I want to remove the padding around braces.

Normally, the way is to subgroup the bits you want to keep and assemble a replacement.

search:

\(var .* = blah\s-*(\)\s-*\(.*?\)\s-*\()\)

replace:

\1\2\3

However, it seems much easier to some up with a regexp that groups the bits I want to delete than the otherway around. Like this one:

var .* = blah\s-*(\(\s-*\).*?\(\s-*\))

I'll get two subgroups out of this. How can I target them for replacement?

EDIT: I'm asking for an interactive way to "invert" the given regexp. So the interface would be similar to query-replace-regexp

  1. enter regexp
  2. enter replacement for group 1
  3. enter replacement for group 2

Upvotes: 3

Views: 393

Answers (2)

event_jr
event_jr

Reputation: 17707

I've made it hooking into query-replace-regexp on github

Here is a paste in case of link rot:

;; -*- lexical-binding: t -*-

(provide inverted-replace)

(require 're-builder)
(require 'parallel-replace)

(defun inverted-replace-generate-replacement (from to)
  "invert result of current match (match-string 0)"
  (let ((string (match-string 0))
        (count (reb-count-subexps from))
        (replacements (parallel-replace-read-list to)))
    (save-match-data
      (string-match from string)
      (dotimes (i count)
        (setq string (replace-match (nth i replacements) nil nil string (- count i)))))
    string))

(defun inverted-replace-regexp (from to)
  (interactive (destructuring-bind (from to _)
                   (query-replace-read-args "inverted-replace-regexp: " t)
                 (list from to)))
  (query-replace-regexp from
                        (quote (replace-eval-replacement
                                replace-quote
                                (inverted-replace-generate-replacement from to)))
                        nil (and (and transient-mark-mode mark-active)
                               (region-beginning))
                        (and (and transient-mark-mode mark-active) (region-end))))

Upvotes: 1

Ivan Andrus
Ivan Andrus

Reputation: 5301

I think some variation of this should work:

(defun remove-padding ()
  (interactive)
  (while (search-forward-regexp
          "var .* = [a-zA-Z_]+\\s-*(\\(\\s-*\\).*?\\(\\s-*\\))"
          nil t)
    ;; Replace the 2 subexpressions with nothing
    (replace-match "" nil t nil 2)
    (replace-match "" nil t nil 1)))

However, you might also consider using a tool like indent depending on what your use cases are.

EDIT: Below is a very minimal interactive version. The function query-replace-regexp is very complex and I have made no attempt to reproduce all of it's functionality.

(require 're-builder)
(defun query-replace-subexpressions (regexp replacements)
  "REPLACEMENTS need to be in reverse order if passed from lisp!"
  ;; Read the correct number of subexpressions
  (interactive
   (let* ((re (read-from-minibuffer "Query replace subexps: "))
          (num-subexps (reb-count-subexps re))
          (replacement-list nil)
          (replacements (dotimes (rep num-subexps)
                          (setq replacement-list
                                (cons
                                 (read-from-minibuffer
                                  (format "Replace subexpression %s with: " rep))
                                 replacement-list)))))
     (list re replacement-list)))
  ;; Search
  (let ((len (length replacements)))
    (while (search-forward-regexp regexp nil t)
      (replace-highlight (match-beginning 0) (match-end 0)
                         (point-min) (point-max) regexp
                         t case-fold-search)
      ;; Query
      (when (save-match-data (y-or-n-p "Replace this occurrence? "))
        ;; Make all the replacements
        (dotimes (i len)
          (replace-match (nth i replacements) nil nil nil (- len i)))))
    (replace-dehighlight)))


;; Test it out below
(query-replace-subexpressions
 "var .* = [a-zA-Z_]+\\s-*(\\(\\s-*\\).*?\\(\\s-*\\))"
 '("" ""))

var foo1 = blah(properties, property_id    );

var foo2 = blah (properties, property_id );

var foo3 = blah( properties, property_id      );

Upvotes: 1

Related Questions