aboutsummaryrefslogtreecommitdiffstats
path: root/kaarvok.el
blob: 974e844c3f2eb87b1cb005cea9f657c6278e6024 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
;;; 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)
     (expand-file-name destination))))

(provide 'kaarvok)
;;; kaarvok.el ends here