Emacs,Ruby:将do end块转换为花括号,反之亦然

6

我经常需要将像这样的代码转换成以下形式:

before do 
  :something
end

to

before { :something }

有没有一种方法可以在emacs中自动化这个任务?我使用ruby-mode和rinary,但它们在这里并不太有用。
4个回答

15

Emacs24.3及以上版本中的ruby-mode拥有ruby-toggle-block命令。

默认绑定键为C-c {


5
我相信这可以更短更好,但目前我有以下内容:
(defun ruby-get-containing-block ()
  (let ((pos (point))
        (block nil))
    (save-match-data
      (save-excursion
        (catch 'break
          ;; If in the middle of or at end of do, go back until at start
          (while (and (not (looking-at "do"))
                      (string-equal (word-at-point) "do"))
            (backward-char 1))
          ;; Keep searching for the containing block (i.e. the block that begins
          ;; before our point, and ends after it)
          (while (not block)
            (if (looking-at "do\\|{")
                (let ((start (point)))
                  (ruby-forward-sexp)
                  (if (> (point) pos)
                      (setq block (cons start (point)))
                    (goto-char start))))
            (if (not (search-backward-regexp "do\\|{" (point-min) t))
                (throw 'break nil))))))
        block))

(defun ruby-goto-containing-block-start ()
  (interactive)
  (let ((block (ruby-get-containing-block)))
    (if block
        (goto-char (car block)))))

(defun ruby-flip-containing-block-type ()
  (interactive)
  (save-excursion
    (let ((block (ruby-get-containing-block)))
      (goto-char (car block))
      (save-match-data
        (let ((strings (if (looking-at "do")
                           (cons
                            (if (= 3 (count-lines (car block) (cdr block)))
                                "do\\( *|[^|]+|\\)? *\n *\\(.*?\\) *\n *end"
                              "do\\( *|[^|]+|\\)? *\\(\\(.*\n?\\)+\\) *end")
                            "{\\1 \\2 }")
                         (cons
                          "{\\( *|[^|]+|\\)? *\\(\\(.*\n?\\)+\\) *}"
                          (if (= 1 (count-lines (car block) (cdr block)))
                              "do\\1\n\\2\nend"
                            "do\\1\\2end")))))
          (when (re-search-forward (car strings) (cdr block) t)
            (replace-match (cdr strings) t)
            (delete-trailing-whitespace (match-beginning 0) (match-end 0))
            (indent-region (match-beginning 0) (match-end 0))))))))

有两个函数需要绑定到键:ruby-goto-containing-block-startruby-flip-containing-block-type
这两个命令在块内的任何位置都可以使用,希望它们可以跳过应该跳过的块 - 尽管如果您正在转换为短块格式,则不应该成为问题。 ruby-flip-containing-block-type将三行do..end块折叠为单行{},反之亦然。如果块的长度不是恰好3行或1行,则应保持不变。
我现在正在我的ruby设置上使用它,所以我会欣赏改进。

1
你可以使用跨越换行符的正则表达式。
/do(C-q C-j\?)*(.*)(C-q C-j\?)*end/
然后替换为
{\2 } 

这样的东西可能会起作用。然后,您可以自定义它,直到它完全符合您的需求,并将其绑定到宏上,以便您随时可以使用它并给朋友留下深刻印象!

我在vi(我的首选编辑器)中测试了上述正则表达式,它们都起作用。因此,类似的东西对您也应该有效。

有关更多信息,请确保查看{{link1:emacs wiki}}!


不错。唯一需要注意的是,在Emacs中,您需要在正则表达式中输入C-q C-J而不是\n来匹配换行符 :) - Gabriel
哈!谢谢你(我是一个vim用户,所以我不确定那些东西...我会查一下语法并修改我的答案)。 - Ziggy
这个是否可以递归运行?我不熟悉emacs的正则表达式支持。 - jtbandes
可以在使用replace-regexp或query-replace-regexp时起作用,但我想要的是导航到块的开头,按下一个键,然后将该块转换为do..end。顺便说一句,我之所以问这个问题,是因为我羡慕我的同事们,他们沉迷于Vi和RubyMine并享受这个功能。这大约是我第76次承诺要学习Lisp了:( - iosadchii

1

这里是一个函数。我是一个elisp初学者。它只能从do到{,让我知道它是否适用于你。


对我没用,会把整个文件搞乱。但至少我有了一个实现的思路,谢谢。 - iosadchii
1
@iosadchii 请随意在这里添加您的解决方案,并接受它。我可能会从中“获得灵感”。 - vhallac

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