如何在Emacs或Vim中分发字符串

The*_*ude 6 vim emacs

在Emacs或Vim中,连接字符串的方法是什么,如下例所示:

Transform from:
    (alpha, beta, gamma) blah (123, 456, 789)
To:
    (alpha=123, beta=456, gamma=789)

它需要扩展到:

  • 其中很多行
  • 括号中的许多元素

我最近发现自己经常需要这种转变.

我在Emacs中使用Evil,这就是为什么Vim的答案可能也会有所帮助.

更新:

解决方案并不像我希望的那样普遍.例如,当我有一个字符串列表并希望将它们分发到一个大型XML文档中时,我希望该解决方案也能正常工作.例如:

<item foo="" bar="barval1"/>
<item foo="" bar="barval2"/>
<item foo="" bar="barval3"/>
<item foo="" bar="barval4"/>

fooval1
fooval2
fooval3
fooval4
Run Code Online (Sandbox Code Playgroud)

我制定了一个解决方案,并将其作为答案添加.

The*_*ude 0

我的方法是创建一个命令来设置匹配列表,然后使用replace-regexp 作为第二个命令来分发匹配列表,利用replace-regexp 的现有 \, 设施。

评估 Elisp,例如在 .emacs 文件中:

(defvar match-list nil
  "A list of matches, as set through the set-match-list and consumed by the cycle-match-list function. ")
(defvar match-list-iter nil
  "Iterator through the global match-list variable. ")
(defun reset-match-list-iter ()
  "Set match-list-iter to the beginning of match-list and return it. "
  (interactive)
  (setq match-list-iter match-list))
(defun make-match-list (match-regexp use-regexp beg end)
  "Set the match-list variable as described in the documentation for set-match-list. "
  ;; Starts at the beginning of region, searches forward and builds match-list.
  ;; For efficiency, matches are appended to the front of match-list and then reversed
  ;; at the end.
  ;;
  ;; Note that the behavior of re-search-backward is such that the same match-list
  ;; is not created by starting at the end of the region and searching backward.
  (let ((match-list nil))
    (save-excursion
      (goto-char beg)
      (while
          (let ((old-pos (point)) (new-pos (re-search-forward match-regexp end t)))
            (when (equal old-pos new-pos)
              (error "re-search-forward makes no progress.  old-pos=%s new-pos=%s end=%s match-regexp=%s"
                     old-pos new-pos end match-regexp))
            new-pos)
        (setq match-list
              (cons (replace-regexp-in-string match-regexp
                                              use-regexp
                                              (match-string 0)
                                              t)
                    match-list)))
      (setq match-list (nreverse match-list)))))
(defun set-match-list (match-regexp use-regexp beg end)
  "Set the match-list global variable to a list of regexp matches.  MATCH-REGEXP
is used to find matches in the region from BEG to END, and USE-REGEXP is the
regexp to place in the match-list variable.

For example, if the region contains the text: {alpha,beta,gamma}
and MATCH-REGEXP is: \\([a-z]+\\),
and USE-REGEXP is: \\1
then match-list will become the list of strings: (\"alpha\" \"beta\")"
  (interactive "sMatch regexp: \nsPlace in match-list: \nr")
  (setq match-list (make-match-list match-regexp use-regexp beg end))
  (reset-match-list-iter))
(defun cycle-match-list (&optional after-end-string)
  "Return the next element of match-list.

If AFTER-END-STRING is nil, cycle back to the beginning of match-list.
Else return AFTER-END-STRING once the end of match-list is reached."
  (let ((ret-elm (car match-list-iter)))
    (unless ret-elm
      (if after-end-string
          (setq ret-elm after-end-string)
        (reset-match-list-iter)
        (setq ret-elm (car match-list-iter))))
    (setq match-list-iter (cdr match-list-iter))
    ret-elm))
(defadvice replace-regexp (before my-advice-replace-regexp activate)
  "Advise replace-regexp to support match-list functionality. "
  (reset-match-list-iter))
Run Code Online (Sandbox Code Playgroud)

然后解决原来的问题:

M-x set-match-list
Match regexp: \([0-9]+\)[,)]
Place in match-list: \1

M-x replace-regexp
Replace regexp: \([a-z]+\)\([,)]\)
Replace regexp with: \1=\,(cycle-match-list)\2
Run Code Online (Sandbox Code Playgroud)

并解决 XML 示例:

[Select fooval strings.]
M-x set-match-list
Match regexp: .+
Place in match-list: \&

[Select XML tags.]
M-x replace-regexp
Replace regexp: foo=""
Replace regexp with: foo="\,(cycle-match-list)"
Run Code Online (Sandbox Code Playgroud)