2011-12-22 14 views
6

Estoy trabajando para dividir el código en archivos más pequeños y refactorizarlo un poco. Considere el siguiente código a continuación como la sección quiero extraer:Refactorización en Emacs

(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))) 
  • Quiero aprovechar la sección anterior y reemplazarlo con algo así como (require `file-name)
  • Luego tomar el texto reemplazado y el lugar que en una nueva archivo en el directorio actual llamado file-name.el
  • Y a continuación, añadir una línea en la parte superior del archivo (provides `file-name)

sería muy bueno si pudiera presionar un teclado y luego escribir un nombre y que esto ocurra. Si hay una manera fácil de hacerlo, me encantaría escuchar posibles soluciones.

Editar: estoy empezando una recompensa, porque creo que esto se aplica a más tipos de código de Lisp y me gustaría tener algo un poco más general que pueda ampliar.

He considerado yasnippet pero no creo que sea lo suficientemente potente como para realizar la tarea en cuestión. Básicamente, el flujo de trabajo ideal sería marcar las líneas a ser extraídas, reemplazando eso con una directiva de requerimiento o inclusión apropiada y enviando el texto a su propio archivo. Idealmente, un comando y algo que tenga en cuenta el tipo de archivo que se está editando o al menos el modo principal para que el comportamiento se pueda personalizar, una vez más yasnippet es bueno para realizar tareas diferentes al editar en diferentes modos principales, sin embargo no tendría idea de cómo hacer que funcione o evaluar la posibilidad de hacerlo funcionar.

Avíseme si necesita más información.

Respuesta

4

Ligeramente probados:

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

Me doy cuenta de que esto es bastante viejo, pero solo una sugerencia; Personalmente moví las llamadas '(delete-region ...) (insert)' * después de * el bloque '(with-current-buffer)', así que si p. hay un error al crear el archivo (tal vez directorio de solo lectura o algo así), la función abortará sin haber "dañado" el archivo original. – BRFennPocock

1

Para una cosa tal que utilizo el siguiente fragmento (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))`) 
  • primero crear el archivo C - xC - ffile-name.elRET
  • entonces inserte el fragmento con C - c&C - s
  • y añadir cualquier pieza de código que desea.

He también el gancho siguiente:

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

para producir un .elc cada vez que puedo guardar una .el.

10

Una solución general a este tipo de problema son keyboard macros (que no debe confundirse con (macros Emacs) LISP). Básicamente, Emacs le permite grabar una secuencia de teclas y "reproducirlas" después. Esta puede ser una herramienta muy útil en situaciones en las que escribir código LISP personalizado parece excesivo.

Por ejemplo, podría crear la siguiente macro de teclado (escriba las combinaciones de teclas en el lado izquierdo, el lado derecho muestra las explicaciones para cada golpe de tecla):

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 

Ahora se puede volver a jugar que macro en algún otro buffer escribiendo Cx e, o save it para su uso posterior. También puede vincular una macro a un acceso directo como una función.

Sin embargo, hay una debilidad con este enfoque: desea poder realmente especificar el nombre de archivo, y no solo usar la cadena "file-name" cada vez. Eso es un poco difícil: de forma predeterminada, las macros de teclado no ofrecen ninguna posibilidad general para consultar al usuario (excepto el muy mínimo C-x q, como se documentó en here).

El Emacs Wiki tiene algunas soluciones alternativas para eso, sin embargo, en lugar de preguntar al usuario en el minibúfer, a veces puede ser suficiente para iniciar la macro matando la línea actual y guardando su texto en un registro.

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) 

Ahora, cuando se desea utilizar la macro, primero escriba el nombre del archivo deseado en una línea por lo demás vacío, y luego hacer C-x e en esa línea. Cada vez se necesita el valor del nombre de archivo en la macro se puede recuperar del registro T:

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

Por ejemplo, para la declaración provide en la macro anterior, se podría escribir: (proporcionan 'Cx ri T). Tenga en cuenta que esta técnica (inserción) también funciona en el minibúfer y, por supuesto, puede guardar varias líneas en diferentes registros.

Puede sonar complicado, pero en realidad es bastante fácil en la práctica.

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