Refactoring in Emacs

I am working on breaking the code into smaller files and refactoring it a bit. As the section I want to extract, consider the following code below:

(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))) 
  • I want to take the section above and replace it with something like (require `file-name)
  • Then replace the text and put it in a new file in the current directory named file-name.el
  • Then add a line at the beginning of the file (provides `file-name)

It would be great if I hit with a key and then typed in a name and this will happen. If there is an easy way to do this, I would like to hear possible solutions.

Edit: I begin generosity because I think this applies to many types of code than Lisp, and I would like to have something more general that I can extend.

I reviewed yasnippet, but I don't think it is powerful enough to complete the task. In principle, an ideal workflow will mark the lines to be extracted, replacing them with the appropriate require or include directive and sending the text to its own file. Ideally one command and something that is known about the type of file being edited or at least in the main mode, so the behavior can be customized, again yasnippet can perform different tasks when editing in different main modes, but I would have no idea how to do this work or evaluate the possibility of its work.

Let me know if you need more information.

+6
source share
4 answers

Lightly tested:

 (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)))) 
+4
source

A common solution to this type of problem is keyboard macros (not to be confused with (Emacs) LISP macros). Basically, Emacs allows you to record a sequence of keystrokes and "play them back" later. This can be a very handy tool in situations where writing custom LISP code seems unnecessary.

For example, you can create the following keyboard macro (enter the key combination on the left side, the right side shows explanations for each key press):

 Cx ( ; start recording a keyboard macro Cx h ; mark whole buffer Cw ; kill region (require 'file-name) ; insert a require statement into the buffer Cx Cs ; save buffer Cx Cf ; find file file-name.el <RET> ; specify the name of the file M-< ; move to the beginning of the buffer Cu Cy ; insert the previously killed text, leaving point where it is (provide 'file-name) <RET> <RET> ; insert a provide statement into the buffer Cx ) ; stop recording the keyboard macro 

Now you can replay this macro in some other buffer by typing Cx e or save it for later use. You can also bind a macro to a shortcut in the same way as to a function.

However, there is one drawback to this approach: you want to be able to specify a file name, and not just use the string "file name" every time. This is a bit complicated - by default, the macro on the keyboard does not provide general features for user request (except for the minimum Cx q , as described here ).

Emacs Wiki , however, this requires several workarounds, instead of asking the user in the minibuffer, sometimes it may be enough to run the macro, killing the current line and keeping its text in the register.

 Cx ( Ce C-<SPC> Ca ; mark current line Cx rs T ; copy line to register T Ck Ck ; kill current line ... ; actual macro Cx ) 

Now, when you want to use your macro, you must first write the desired file name to an otherwise empty line, and then make Cx e in that line. Whenever a file name value is required in a macro, you can get it from the T register:

 Cx ri T ; insert file-name into buffer 

For example, for the provide statement in the above macro, you can write: (provide 'Cx ri T) . Please note that this technique (insertion) also works in the minibuffer, and, of course, you can save several lines in different registers.

It may seem complicated, but in practice it is quite simple.

+10
source

For such a thing, I use the following snippet (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))`) 
  • Create file C - x C - f file-name.el RET
  • then insert the fragment using C - C & C - s
  • and add any piece of code you want.

I also have the following hook:

 (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)))) 

to create .elc whenever I save .el .

+1
source
 (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-np "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-np (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))))) 
0
source

Source: https://habr.com/ru/post/904293/


All Articles