Emacs中的重构

7

我正在将代码拆分为更小的文件并对其进行一些重构。请考虑以下代码作为我想要提取的部分:

(require 'package)
(add-to-list 'package-archives
             '("marmalade" . "http://marmalade-repo.org/packages/") t)
(package-initialize)
(when (not package-archive-contents)
  (package-refresh-contents))

(defvar my-packages '(org magit)
  "A list of packages to ensure are installed at launch.")

(dolist (p my-packages)
  (when (not (package-installed-p p))
    (package-install p)))
  • 我想要将上面的部分替换为类似于(require `file-name)的内容
  • 然后将替换的文本放置在当前目录中名为file-name.el的新文件中
  • 接着在文件顶部添加一行(provides `file-name)

如果我能按下一个键并输入名称,就可以完成这个操作,那将会很棒。如果有简单的方法可以做到这一点,我很想听听可能的解决方案。

编辑: 我正在发起悬赏,因为我认为这适用于比Lisp更多类型的代码,并且我希望拥有更通用的东西,以便我可以进行扩展。

我考虑过yasnippet,但我认为它不足以执行手头的任务。基本上,理想的工作流程是标记要提取的行,用适当的require或include指令替换它们并将文本发送到自己的文件中。最理想的情况是只需一个命令,就可以了解正在编辑的文件类型或至少是主要模式,以便可以自定义行为。再次提醒,yasnippet在不同的主要模式下执行不同的任务非常出色,但我不知道如何使其工作或评估使其工作的可能性。

如果您需要更多信息,请告诉我。

4个回答

10
这类问题的一般解决方案是键盘宏(不要与(Emacs)LISP宏混淆)。基本上,Emacs允许您记录一系列按键操作,并在以后“播放”它们。在写自定义LISP代码似乎过于复杂的情况下,这可能是一个非常方便的工具。
例如,您可以创建以下键盘宏(在左侧键组合中输入,右侧显示每个按键操作的说明):
C-x (                            ; start recording a keyboard macro
C-x h                            ; mark whole buffer
C-w                              ; kill region
(require 'file-name)             ; insert a require statement into the buffer
C-x C-s                          ; save buffer
C-x C-f                          ; find file
file-name.el <RET>               ; specify the name of the file
M-<                              ; move to the beginning of the buffer
C-u C-y                          ; insert the previously killed text, leaving point where it is
(provide 'file-name) <RET> <RET> ; insert a provide statement into the buffer
C-x )                            ; stop recording the keyboard macro

现在,您可以通过键入C-x e在其他缓冲区中重新播放该宏,或保存以备将来使用。您还可以像函数一样将宏绑定到快捷方式。
但是,这种方法有一个弱点:您希望能够实际指定文件名,而不仅仅是每次使用字符串“file-name”。这有点困难-默认情况下,键盘宏没有通用的查询用户的功能(除了非常小的C-x q,如此处所述)。 Emacs Wiki有一些解决方法,但是,与其在迷你缓冲区中提示用户,有时将宏启动通过删除当前行并将其文本保存到寄存器中可能足够。
C-x (
C-e C-<SPC> C-a    ; mark current line
C-x r s T          ; copy line to register T
C-k C-k            ; kill current line
...                ; actual macro
C-x )

现在,当你想使用宏时,你需要在一个空行中写入所需的文件名,然后在该行中按下C-x e。每当宏需要文件名的值时,你可以从寄存器T中检索它:

C-x r i T          ; insert file-name into buffer

例如,在上面的宏中,对于provide语句,您可以编写:(provide ' C-x r i T )。请注意,这种插入技术在迷你缓冲区中也适用,并且您当然可以将多行保存到不同的寄存器中。
可能听起来很复杂,但在实践中其实非常容易。

4

稍加测试:

(defun extract-to-package (name start end)
  (interactive (list (read-string "Package name to create: ")
                     (region-beginning) (region-end)))
  (let ((snip (buffer-substring start end)))
    (delete-region start end)
    (insert (format "(require '%s)\n" name))
    (with-current-buffer (find-file-noselect (concat name ".el"))
      (insert snip)
      (insert (format "(provide '%s)\n" name))
      (save-buffer))))

我知道这篇文章有点老了,但是我有一个建议:我个人将(delete-region…)(insert)调用放在(with-current-buffer)块之后,这样如果创建文件时出现错误(例如只读目录或其他原因),函数将会中止而不会“损坏”原始文件。 - BRPocock

1
我使用以下代码片段(with yasnippet)来完成这件事:
;; `(buffer-name)`
;; Copyright (C) `(format-time-string "%Y")` name

;; Author: name <email>

;; This program is free software: you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation, either version 3 of
;; the License, or (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <http://www.gnu.org/licenses/>.

$0

(provide '`(subseq (buffer-name) 0 (- (length (buffer-name)) 3))`)
  • 首先创建文件 C-xC-f文件名.elRET
  • 然后使用 C-c&C-s 插入代码片段
  • 添加任何你想要的代码片段。

我还有以下钩子:

(add-hook 'after-save-hook 'autocompile)

(defun autocompile ()
  "Byte compile an elisp."
  (interactive)
  (require 'bytecomp)
  (let ((filename (buffer-file-name)))
    (if (string-match "\\.el$" filename)
        (byte-compile-file filename))))

每当我保存一个.el文件时,生成一个.elc文件。

0
(defun region-to-file+require (beg end file append)
  "Move region text to FILE, and replace it with `(require 'FEATURE)'.
You are prompted for FILE, the name of an Emacs-Lisp file.  If FILE
does not yet exist then it is created.

With a prefix argument, the region text is appended to existing FILE.

FEATURE is the relative name of FILE, minus the extension `.el'."
  (interactive "@*r\nG\nP")
  (when (string= (expand-file-name (buffer-file-name)) (expand-file-name file))
    (error "Same file as current"))
  (unless (string-match-p ".+[.]el$" file)
    (error "File extension must be `.el' (Emacs-Lisp file)"))
  (unless (or (region-active-p)
              (y-or-n-p "Region is not active.  Use it anyway? "))
    (error "OK, canceled"))
  (unless (> (region-end) (region-beginning)) (error "Region is empty"))
  (unless (or (not append)
              (and (file-exists-p file) (file-readable-p file))
              (y-or-n-p (format "File `%s' does not exist.  Create it? " file)))
    (error "OK, canceled"))
  (write-region beg end file append nil nil (not append))
  (delete-region beg end)
  (let ((feature  (and (string-match "\\(.+\\)[.]el$" file)
                       (match-string 1 file))))
    (when feature
      (insert (format "(require '%s)\n" feature)))))

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