kaarvok/kaarvok.el
Tom Willemse 4c157a4d5d Parse only directory names too
Files that aren't parsed for their contents should still have their
destination name parsed in case either the file or parent directory have
variables in them.
2013-09-23 00:47:18 +02:00

139 lines
5 KiB
EmacsLisp

;;; kaarvok --- Generate directory structures from templates
;; Copyright (C) 2013 Tom Willemse
;; Author: Tom Willemse <tom@ryuslash.org>
;; Keywords: convenience
;; Package-Version: 0.1.0
;; This file is part of kaarvok.
;; kaarvok 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.
;; kaarvok 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 kaarvok. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This project helps you recreate directory structures from template
;; directory structures. The files, file names and directory names of
;; these templates contain variable placeholders, which will be
;; replaced by whatever value you wish to use when copied.
;;; Code:
(defvar kaarvok-templates-directory "~/.emacs.d/templates"
"Where templates are stored.")
(defvar kaarvok-template-var-delimiter "$!"
"Strings used to delimit variable names.")
(defun kaarvok-template-var-regexp ()
"Create the regexp which identifies a variable in a template."
(concat kaarvok-template-var-delimiter
"\\([^" kaarvok-template-var-delimiter " \t\n]+\\)"
kaarvok-template-var-delimiter))
(defvar kaarvok-value-alist nil
"A placeholder where replacement values will be kept.
This is let-bound when `kaarvok-create-project-from-template' is
called and should not be edited directly.")
(defun kaarvok-replace-all (from to str)
"Simply replace all occurrences of FROM with TO in STR."
(while (string-match from str)
(set 'str (replace-match to t t str)))
str)
(defun kaarvok-get-replacement (key)
"Find, or ask for and save, the replacement value for KEY."
(let ((replacement (assoc key kaarvok-value-alist)))
(when (eq replacement nil)
(set 'replacement
`(,key . ,(read-from-minibuffer (concat key ": "))))
(add-to-list 'kaarvok-value-alist replacement))
replacement))
(defun kaarvok-parse-file-name (filename)
"Parse FILENAME and replace all variables.
Use values provided by the user."
(while (string-match (kaarvok-template-var-regexp) filename)
(let* ((tpl-var (match-string 1 filename))
(replacement-value (kaarvok-get-replacement tpl-var)))
(set 'filename (replace-match (cdr replacement-value) t t
filename))))
(let ((noext (file-name-sans-extension filename))
(ext (file-name-extension filename t)))
(set 'noext (kaarvok-replace-all "\\." "/" noext))
(concat noext ext)))
(defun kaarvok-parse-file (file)
"Parse FILE and replace all variables."
(insert-file-contents file)
(while (re-search-forward (kaarvok-template-var-regexp) nil t)
(let* ((tpl-var (match-string 1))
(replacement-value (kaarvok-get-replacement tpl-var)))
(replace-match (cdr replacement-value) t t))))
(defun kaarvok-parse-and-copy-file (src dst)
"Copy SRC to DST.
If necessary, parse the file and filename first."
(let* ((parsed-dst (kaarvok-parse-file-name dst))
(parsed-dst-dir (file-name-directory parsed-dst)))
(when (not (file-exists-p parsed-dst-dir))
(make-directory parsed-dst-dir t))
(with-temp-file parsed-dst
(kaarvok-parse-file src))))
(defun kaarvok-copy-directory (directory to)
"Copy template DIRECTORY to TO.
Parse both paths and files in the process."
(let ((files (directory-files directory t "[^.]\\{1,2\\}$" t)))
(while files
(let* ((src-filename (car files))
(dst-filename
(concat to "/" (file-name-nondirectory src-filename))))
(if (file-directory-p src-filename)
(kaarvok-copy-directory src-filename dst-filename)
(if (string-equal (file-name-extension src-filename) "etpl")
(kaarvok-parse-and-copy-file
src-filename (file-name-sans-extension dst-filename))
(let ((to (kaarvok-parse-file-name to)))
(if (not (file-exists-p to))
(make-directory to t)
(unless (file-directory-p to)
(error
(concat "Cannot create project at %s, file "
"already exists and is not a directory.") to))))
(copy-file src-filename
(kaarvok-parse-file-name dst-filename)))))
(set 'files (cdr files)))))
;;;###autoload
(defun kaarvok-create-project-from-template (template destination)
"Take TEMPLATE, copy it to DESTINATION.
Replace any occurrences of variables with user-povided values."
(interactive "MTemplate: \nGDestination: ")
(let ((kaarvok-value-alist))
(kaarvok-copy-directory
(concat kaarvok-templates-directory "/" template) destination)))
(provide 'kaarvok)
;;; kaarvok.el ends here