-
Notifications
You must be signed in to change notification settings - Fork 0
/
template.el
2374 lines (2115 loc) · 94.6 KB
/
template.el
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;;; template.el --- use templates, decorate comments, auto-update buffers
;; Copyright (C) 1995-2002 Free Software Foundation, Inc.
;;
;; Author: Christoph Wedler <[email protected]>
;; Version: 3.1x
;; Keywords: template, comment decoration, auto-updating, data, tools
;; X-URL: http://emacs-template.sourceforge.net/
;; 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 2, 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, write to the Free Software
;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
;;; Commentary:
;; When you create a new file with Emacs, package Template supplies an initial
;; buffer content via a template: a file with normal text and expansion
;; forms. There is a menu to easily create such templates. You can also use new
;; commands to decorate comments and update the buffer contents.
;; The main difference between Template and other similar packages is that you
;; can define very flexible templates without having to learn Lisp or changing
;; your Emacs init file. This package does not help Lisp programmers to define
;; complex macros.
;; For details, check <http://emacs-template.sourceforge.net/> or, if you
;; prefer the manual style, the documentation of the following commands and
;; variables:
;;
;; * for templates: \\[template-new-file], `template-auto-insert',
;; `template-derivation-alist', `template-default-expansion-alist' and
;; `template-definition-start',
;; * for comment decoration: \\[template-single-comment] and
;; \\[template-block-comment], `template-comment-specification-alist'
;; * for updating: \\[template-update-buffer], `template-auto-update',
;; `template-update-buffer-alist' and `template-header-regexp-alist'.
;; Bug fixes, bug reports, improvements, and suggestions for the newest version
;; are strongly appreciated.
;;; Installation:
;; This file requires Emacs-20.2, XEmacs-20.2 or higher.
;; Put this file into your load-path and the following into your ~/.emacs:
;; (require 'template)
;; (template-initialize)
;; You might want to add another item to the "File" menu by (in XEmacs):
;; (add-menu-button '("File")
;; ["Insert and Expand Template..."
;; template-expand-template
;; :active (not buffer-read-only)]
;; "Insert File...")
;; To customize, use `M-x customize-group RET template RET' or the customize
;; entry in menu Options.
;;; Code:
(provide 'template)
(require 'custom)
(eval-when-compile
(require 'cl)
(defvar current-menubar)
(defvar init-file-loaded) ; would be useful in Emacs, too...
(defvar menu-bar-files-menu)
(defvar menu-bar-edit-menu)
(defvar file-name-buffer-file-type-alist))
(eval-and-compile
(if (string-match "XEmacs" emacs-version)
(defalias 'template-directory-files 'directory-files)
(defun template-directory-files (dirname &optional full match nosort
files-only)
(let ((files (directory-files dirname full match nosort))
result)
(if (null files-only)
files
(while files
(if (if (file-directory-p (car files))
(null (eq files-only t))
(eq files-only t))
(push (car files) result))
(setq files (cdr files)))
(nreverse result)))))
(defalias 'template-characterp
(if (fboundp 'characterp) 'characterp 'integerp))
(if (not (fboundp 'int-to-char))
(defalias 'template-int-to-char 'identity)
(defun template-int-to-char (int-or-char)
"Convert integer INT-OR-CHAR into equivalent char or return INT-OR-CHAR."
(or (and (integerp int-or-char) (int-to-char int-or-char))
int-or-char)))
(if (fboundp 'point-at-bol)
(defalias 'template-point-at-bol 'point-at-bol)
(defun template-point-at-bol (&optional arg)
(save-excursion
(beginning-of-line arg)
(point))))
(if (fboundp 'point-at-eol)
(defalias 'template-point-at-eol 'point-at-eol)
(defun template-point-at-eol (&optional arg)
(save-excursion
(end-of-line arg)
(point)))))
;;;;##########################################################################
;;;; User Options, Variables
;;;;##########################################################################
(defconst template-version "3.1a"
"Current version of package template.
Check <http://emacs-template.sourceforge.net/> for the newest.")
;;;===========================================================================
;;; Customization and initialization
;;;===========================================================================
(defgroup template nil
"Use templates, decorate comments, auto-update buffers."
:group 'data
:link '(emacs-commentary-link "template.el")
:link '(url-link "http://emacs-template.sourceforge.net/")
:prefix "template-")
(defgroup template-comments nil
"Comment decorations in package template."
:group 'template
:prefix "template-")
(defgroup template-updating nil
"Updating with package template."
:group 'template
:prefix "template-")
(defgroup template-derivation nil
"Deriving templates for new files."
:group 'template
:prefix "template-")
(defgroup template-expansion nil
"Expanding the expansion forms in templates."
:group 'template
:prefix "template-")
(defgroup template-miscellaneous nil
"Miscellaneous configurations of package template."
:group 'template
:prefix "template-")
;; I could imagine that a future version of package custom could make this
;; `PACKAGE-initialize' stuff easier
(defcustom template-use-package nil
"Pseudo variable. Used to initialize template in custom buffer.
Put `(template-initialize)' into your ~/.emacs to initialize package
template in future sessions. See variable `template-initialize'."
:group 'template
:type '(boolean :format "%{%t%}: %[(template-initialize)%], %v\n"
:on "in use" :off "not yet initialized"
:help-echo "Initialize package Template."
:action template-initialize))
(defcustom template-initialize t
"Whether/what to initialize with `template-initialize'.
If t, do full initialization. Otherwise, the value should be a list
with elements. To enable, include
* `auto' to enable `template-auto-update' and `template-auto-insert',
* `ffap' to make sure that `auto' works with `find-file-at-point',
* `cc-mode' to enable correct C/C++/Java/Antlr comment filling, i.e.,
to add `template-c-init-fill-function' to `c-mode-common-hook',
* `de-html-helper' to disable `html-helper's template and time-stamps,
* `keys' to setup the default key bindings,
* `menus' to setup the menus."
:group 'template-miscellaneous
:type '(choice (const :tag "All" t)
(set :value (auto cc-mode keys menus)
(const :tag "Auto Updating/Inserting" auto)
(const :tag "Correct Auto Inserting with Ffap" ffap)
(const :tag "Correct C Comment Filling" cc-mode)
(const :tag "Deactivate html-helper" de-html-helper)
(const :tag "Setup Key Bindings" keys)
(const :tag "Setup Menus" menus))))
;;;===========================================================================
;;; Menu
;;;===========================================================================
(defvar template-comment-menu
'("Comment"
["Decorate Comment Line" template-single-comment
:active (and (not buffer-read-only)
(memq (template-comment-at-point) '(none delimited single)))]
["Decorate Comment Block" template-block-comment
:active (and (not buffer-read-only)
(memq (template-comment-at-point) '(single block)))]
"---"
["Indent for Comment" indent-for-comment
:active (and comment-start (not buffer-read-only))]
["Continue Comment" indent-new-comment-line
:active (and comment-start (not buffer-read-only))]
["Comment Region" comment-region
:active (and comment-start (not buffer-read-only) (mark))]
["Comment Region 2" (comment-region 2)
:active (and comment-start (not buffer-read-only) (mark))]
["Comment Region 3" (comment-region 3)
:active (and comment-start (not buffer-read-only) (mark))]
"---"
["Update Buffer" template-update-buffer
:active (and template-update-buffer-alist (not buffer-read-only))])
"Menu for comment functions.")
(defvar template-creation-menu
'("Template Creation"
:filter template-menu-filter
["Open Template" template-open-template
:active (null (template-buffer-template-p))]
"--"
["Define User Input" template-define-prompt t]
["Define Text Register" template-define-register t]
["Define Temp Message" template-define-message t]
"---"
["Insert Expansion Form" template-insert-form t])
"Menu for template creation.")
;;;===========================================================================
;;; Commenting
;;;===========================================================================
(defcustom template-max-column -1
"*Width of the separator line, to use with an empty `comment-end'.
If the value is zero or negative, it is added to `fill-column'. See
also `template-max-column-with-end'."
:group 'template-comments
:type 'integer)
(defcustom template-max-column-with-end 0
"*Width of the separator line including a non-empty `comment-end'.
If the value is zero or negative, it is added to `fill-column'. See
also `template-max-column'."
:group 'template-comments
:type 'integer)
(defcustom template-alt-comment-syntax-alist
'((t "/* " " */"))
"Alternative comment syntax for languages with \"mixed\" comments.
Used by function `template-comment-syntax'. Elements look like
(MODES-OR-REGEXP COMMENT-START COMMENT-END)
If the current `major-mode' has a empty `comment-end' and a commenting
command does not work at `point' with the usual `comment-start', we
search for the first matching alternative comment syntax in this alist.
Each element must \"pass\" MODES-OR-REGEXP. If this is a list, it must
include the current major-mode, if this is a regexp, it must match the
`buffer-file-name' without version, otherwise it must be non-nil.
Then, COMMENT-START and COMMENT-END is used as the alternative comment
syntax if `comment-start-skip' matches COMMENT-START."
:group 'template-comments
:type '(repeat (group (choice (repeat :tag "In major modes" :value nil
function)
(regexp :tag "Buffer matching" :value "")
(sexp :tag "Always" :value t))
(string :tag "Alt comment start" :value "/* ")
(string :tag "Alt comment end" :value " */"))))
(defcustom template-comment-indent t
"Non-nil means, indent single-line/block comments.
Commands \\[template-single-comment] and \\[template-block-comment]
indent the comment lines if this value is non-nil and the current major
mode is not a member of `template-indent-mode-disable-list' or if this
value is nil and the current major mode is a member of
`template-indent-mode-enable-list'."
:group 'template-comments
:type 'boolean)
(defcustom template-indent-mode-disable-list '(sh-mode makefile-mode)
"Major modes not having indented single-line/block comments.
Used if `template-comment-indent' is non-nil. Major modes in which
pressing TAB twice is different from pressing TAB once are good
candidates for this list."
:group 'template-comments
:type '(repeat (function :tag "Major mode")))
(defcustom template-indent-mode-enable-list nil
"Major modes having indented single-line/block comments.
Used if `template-comment-indent' is nil."
:group 'template-comments
:type '(repeat (function :tag "Major mode")))
(defcustom template-comment-specification-alist
'(("-" "" "" 0)
("-" "" "" 0)
("=" "\n\n" "\n" 1)
("#" "\n\n\f\n" "\n\n" 2))
"List of specifications for comment functions.
Each specification at LEVEL, starting at 1, is a list
(SEPARATOR BEFORE-BLOCK AFTER-BLOCK DELETE-LINES)
SEPARATOR is the string which is inserted repeatedly by commands
\\[template-single-comment] and \\[template-block-comment] up to
`template-max-column'.
After that, \\[template-block-comment] deletes DELETE-LINES after the
comment block and inserts string AFTER-BLOCK at the end of the block and
BEFORE-BLOCK at the front of the block.
The specification LEVEL to use is determined by:
(1) If the prefix argument is non-nil and its numeric value is > 0,
this value is the LEVEL.
(2) If the prefix argument is nil, and there is an old comment style,
use old comment style.
(3) If `template-comment-specification-special' is a function or the
current major mode has a property with this name and its value is a
function, this function returns the specification.
(4) If `comment-end' is empty and `comment-start' is a string of length
1: LEVEL is number of repetitions of `comment-start' at the
beginning of the line. Otherwise, if the correctly indented line
starts at the beginning of the line, LEVEL=3, else LEVEL=2."
:group 'template-comments
:type '(repeat (group (string :tag "Separator" :value "-")
(string :tag "Before block" :value "")
(string :tag "After block" :value "")
(integer :tag "Delete lines" :value 0))))
(defcustom template-comment-specification-special nil
"Function used for special commenting styles or nil.
See `template-comment-specification-alist' for details."
:group 'template-comments
:type '(choice (const nil) function))
;;;===========================================================================
;;; Auto updating
;;;===========================================================================
(defcustom template-auto-update 'query
"*Whether to update parts of the file when saving the buffer.
When non-nil and `template-auto-update-disable-regexp' does not match
the file name, automatically updates parts of the buffer, see
`template-update-buffer-alist'. With value t or if the entry in the
alist has no prompt, do not ask for confirmation.
You should have called `template-initialize' to enable this feature."
:group 'template-updating
:type '(radio (const :tag "No" nil)
(const :tag "Without confirmation" t)
(sexp :tag "With confirmation" :format "%t" :value query)))
(defcustom template-auto-update-disable-regexp nil
"*Regexp matching files not to automatically update.
Value nil matches no file. See `template-auto-update'."
:group 'template-updating
:type '(choice (const :tag "none" nil) regexp))
(defcustom template-update-buffer-alist
'((t "Update header in %s? "
(template-update-header t)
(file-name-sans-versions (file-name-nondirectory buffer-file-name)))
((html-mode) "Update date inside <address> in %s? "
(-2000
"\\([0-9]+[ \t]+[A-Za-z][A-Za-z][A-Za-z][ \t]+[0-9]+\\)[ \t\n]*</address>"
1)
(format-time-string "%d %b %Y")))
"Alist used how to update parts of the buffer.
Used by function `template-update-buffer'. Elements look like
(MODES-OR-REGEXP PROMPT TEST NEW REPLACEMENT-FUN)
Each element must \"pass\" MODES-OR-REGEXP. If this is a list, it must
include the current major-mode, if this is a regexp, it must match the
`buffer-file-name' without version, otherwise it must be non-nil.
Then, TEST is `eval'd and must return the region = (BEG . END) to be
replaced or nil if nothing should be updated according to the current
element. If TEST is a list and the `car' of TEST is not a function,
`template-update-buffer-region' is used as the default function, i.e.,
REPLACEMENT-FUN looks like (LIMIT REGEXP GROUP). Then, check first/last
LIMIT characters in buffer and return region according to GROUP's regexp
group in REGEXP.
Then, NEW is `eval'd. If it is a string, it is considered as
replacement for the region, otherwise REPLACE-FUN must be non-nil.
Then, ask user for confirmation with PROMPT where %s is substituted by
the buffer name if PROMPT is a string and `template-auto-update' is not
t.
Finally, REPLACEMENT-FUN is called the `eval'd NEW and the beginning and
the end of the region returned by TEST. If REPLACEMENT-FUN is nil, just
replace the region by the `eval'd NEW."
:group 'template-updating
:type '(repeat (group (choice (repeat :tag "In major modes" :value nil
function)
(regexp :tag "Buffer matching" :value "")
(sexp :tag "Always" :value t))
(string :tag "Prompt" :value "Update in %s? ")
(choice (list :tag "Default test"
(choice (const :tag "No limit" nil)
(integer :tag "Limit" -1000))
regexp
(integer :tag "Regexp group" :value 0))
(sexp :tag "Eval sexp"))
(sexp :tag "Eval New string")
(option (function :tag "Replacement function")))))
(defcustom template-header-lines 3
"*Last line number which is checked by \\[template-update-header]."
:group 'template-updating
:type 'integer)
(put 'template-header-lines 'template-secure-value #'integerp)
(defcustom template-header-regexp-alist
'(("@(#)\\([^ \t\n]+\\)" . 1)
("^%s[ \t]*\\([^ \t\n]+\\)[ \t]+--" . 1))
"Alist of regexps matching the file name in the header.
The `car' of each element is the REGEXP with %s, if present, substituted
by the comment start. The `cdr' is the regexp group to be replaced.
Used by \\[template-update-header].
The comment start is evaluated from `comment-start', the first character
in the buffer or \"#\". It is assumed that a non-alpha single character
comment start may be repeated. For example, the substituted regexp in
`emacs-lisp-mode' is \"\;+\", in `c++-mode' \"//\"."
:group 'template-updating
:type '(repeat (cons :format "%v"
regexp
(integer :tag "Regexp group" :value 0))))
;;;===========================================================================
;;; Templates: finding templates
;;;===========================================================================
(defcustom template-auto-insert 'query
"*Whether to automatically use template files for new files.
Used if the user gave a non-existent file as argument to a command in
`template-find-file-commands'. When non-nil and a matching template
file can be found, use a template like in `template-new-file'. File
name refinement is never performed, see `template-derivation-alist'.
With value t, do not ask for confirmation.
You should have called `template-initialize' to enable this feature."
:group 'template-derivation
:type '(radio (const :tag "No" nil)
(const :tag "Without confirmation" t)
(sexp :tag "With confirmation" :format "%t" :value query)))
(defcustom template-find-file-commands
'(find-file find-file-other-frame find-file-other-screen
find-file-other-window find-file-at-point ffap nil)
"*Commands which use templates as last resort, see `template-auto-insert'.
See also `template-file-select-commands'.
Include nil if you want to use templates for non-existing files as
command line arguments when starting Emacs."
:group 'template-derivation
:type '(repeat (function :tag "Command")))
(defcustom template-file-select-commands
'(exit-minibuffer minibuffer-complete-and-exit
list-mode-item-mouse-selected
list-mode-item-keyboard-selected)
"*Commands which select the file name via minibuffer/completions.
Checked with commands in `template-find-file-commands'."
:group 'template-derivation
:type '(repeat (function :tag "Command")))
(defface template-message-face
'((t (:bold t)))
"Face for temporary message at point. This only works with XEmacs."
:group 'template-miscellaneous)
(defcustom template-extension ".tpl"
"*Extension used for template files."
:group 'template-derivation
:type 'string)
(defcustom template-subdirectories '("./" "Templates/")
"*List of subdirectories for template files.
See `template-derivation-alist' for details."
:group 'template-derivation
:type '(repeat directory))
(defcustom template-stop-derivation
(cond ((fboundp 'file-remote-p) 'file-remote-p)
((fboundp 'efs-ftp-path) 'efs-ftp-path)
((fboundp 'ange-ftp-ftp-path) 'ange-ftp-ftp-path))
"If non-nil, function used to determine whether to stop derivation.
If non-nil, function is called with argument DIR. If it returns t,
`template-derivation' stops to search for more project specific
templates, i\.e\., just searches in `template-default-directories'."
:group 'template-derivation
:type '(choice ((const :tag "Never" nil)
function)))
(defcustom template-default-directories
(cons (if (and (not (file-directory-p "~/.templates/"))
(file-directory-p "~/lib/templates"))
(expand-file-name "~/lib/templates/")
(expand-file-name "~/.templates/"))
(and (fboundp 'locate-data-directory)
(let ((dir (locate-data-directory "template")))
(and dir (list dir)))))
"*List of default directories for template files.
See `template-derivation-alist' for details."
:group 'template-derivation
:type '(repeat directory))
(defcustom template-derivation-alist
'(;;(("00readme" "" ".txt" "\\`00") . ("00readme" "" ".txt"))
((t "" t))
((t nil null) . (nil nil t 1))
(("TEMPLATE" "" t)))
"Alist for template file name derivation and file name refinement.
Template derivation searches for the most specific readable template
file. By default, files with the same RAW part as the name of the new
file are considered to be more specific than files with just the same
EXT part. Also files in the same directory are considered to be more
specific than files in their parent directory or any default template
directory. This behavior can be changed by this alist.
Each FORM in this alist has the form (TEMPLATE . REFINEMENT). If
TEMPLATE matches, we have found a valid template file and the
corresponding REFINEMENT is used for the file name refinement.
Before the derivation, the given file name is split into the directory
part DIR, the file name without directory FILE, and the raw part RAW of
FILE, the numbering NUM and the extension EXT. The result is stored in
`template-file'.
TEMPLATE can have the form (FUNCTION ARG...). If TEMPLATE matches,
FUNCTION, called with arguments ARGs, should return the split template
file name, see `template-split-filename'.
TEMPLATE can also have the form (T-RAW T-NUM T-EXT F-REGEXP) where all
elements are optional, i.e., have value nil as default. For TEMPLATE to
match, all conditions T-RAW, T-NUM and T-EXT must be met and F-REGEXP,
if non-nil, should match FILE, the non-directory part of the given file
name. If a condition is a string, the corresponding part of the
template file must be equal to it. If t, the part must be equal to
RAW/NUM/EXT of the given file name. If nil, any value will do it. Any
other value acts like t when the part of the given file name is
non-empty, as nil otherwise.
REFINEMENT can have the form (FUNCTION ARG...). FUNCTION, called with
the list of the split template filename and ARGs as arguments, should
set `template-file' if the file name should be refined.
REFINEMENT can also have the form (F-RAW F-NUM F-EXT AUTO-NUM) where all
elements are optional, i.e., have value nil as default. If F-RAW, F-NUM
and F-EXT are non-nil, they change RAW/NUM/EXT of `template-file'. A
string will be used as the new part. If t, the corresponding part of
the template name will be used.
We will use auto numbering in the following two cases: if NUM is
non-empty and the file exists already, or if NUM is empty and AUTO-NUM
is non-nil. Auto numbering looks at the file names in DIR to generate
the next unique number which is at least as high as NUM in the first
case and AUTO-NUM in the second.
Let us use parts of the default value as examples:
Use a template with the same RAW part of the given file name and the
same EXT part if provided, e.g., for \"exercise2\" use template
\"exercise.tex.tpl\". Refine file name to use the extension of the
template file, also use auto numbering, e.g., if files \"exercise2.tex\"
and \"exercise3.tex\" exist, refine name to \"exercise4.tex\":
((t nil null) \. (nil nil t 1))
For a file with extension EXT, use TEMPLATE.EXT:
((\"TEMPLATE\" \"\" t))
We could define: If the given file name starts with \"00\", use template
\"00readme.txt.tpl\". Refine file name to \"00readme.txt\":
((\"00readme\" \"\" \".txt\" \"\\`00\") \. (\"00readme\" \"\" \".txt\"))
Since more than one template file could meet this conditions, the
template derivation searches for first readable file with extension
`template-extension' which is found by the following algorithm:
forall FORMs in `template-derivation-alist' do
for directory BASE from DIR
while not stopped according to `template-stop-derivation' do
forall subdirectories DIRs in `template-subdirectories'
relative to BASE do
forall TEMPLATEs in DIR do
if check_form (FORM, FULL, TEMPLATE) return TEMPLATE
forall directories DIRs in `template-default-directories' do
forall TEMPLATEs in DIR do
if check_form (FORM, FULL, TEMPLATE) return TEMPLATE
if not used via `template-auto-insert'
forall TEMPLATEs in `template-default-directories'
where name_nondir (TEMPLATE) = \"DEFAULT.tpl\" do
if readable (TEMPLATE) return TEMPLATE
return TEMPLATE in first (`template-default-directories')
where name_nondir (TEMPLATE) = \"DEFAULT.tpl\""
:group 'template-derivation
:type '(repeat (cons :format "%v"
(sexp :tag "Derivation" :value ("TEMPLATE" nil t))
(sexp :tag "Refinement" :value nil))))
;;;===========================================================================
;;; Templates: expanding templates
;;;===========================================================================
(defcustom template-confirm-insecure t
"*Non-nil means, ask whether to use insecure template expansions.
Only set this to nil if you ALWAYS check template files before using
it!"
:group 'template-expansion
:type 'boolean)
(put 'template-confirm-insecure 'risky-local-variable t)
(defcustom template-message-timeout 600
"*Maximum duration the temporary message will be displayed at point.
Any user event will also make the temporary message disappear. The
temporary message uses face in `template-message-face'."
:group 'template-miscellaneous
:type 'integer)
(put 'template-message-timeout 'template-secure-value #'integerp)
(defcustom template-date-format "%d %b %Y"
"*Date/time format used with the expansion form (>>>DATE<<<).
See `template-default-expansion-alist' and `format-time-string'. See
also `template-time-format'."
:group 'template-expansion
:type 'string)
(put 'template-date-format 'template-secure-value #'stringp)
(defcustom template-time-format "%T"
"*Date/time format used with the expansion form (>>>TIME<<<).
See `template-default-expansion-alist' and `format-time-string'. See
also `template-date-format'."
:group 'template-expansion
:type 'string)
(put 'template-time-format 'template-secure-value #'stringp)
(defcustom template-string-default "%0.0S"
"*Format string used for non-string variable extensions.
If SYMBOL in (\"KEY\" \. SYMBOL) is not a string, use string with
substitution SYMBOL/%S. Default value \"%0.0S\" causes to print
nothing. See `template-definition-start'."
:group 'template-expansion
:type 'string)
(put 'template-string-default 'template-secure-value #'stringp)
(defcustom template-expansion-format "(>>>%s<<<)"
"Format string for expansion forms.
Is a expansion form with substitution KEY/%s. The value should
correspond with `template-expansion-regexp'. Used by
`template-insert-form'."
:group 'template-expansion
:type 'string)
(put 'template-expansion-format 'template-secure-value #'stringp)
(defcustom template-expansion-regexp "(>>>\\([A-Za-z0-9_]+\\)<<<)"
"Regexp matching strings which are replaced by their expansions.
The first regexp group contains the KEY used by the per-template
expansion, see `template-definition-start' and the global expansions in
`template-expansion-alist' and `template-default-expansion-alist'. The
value should correspond with `template-expansion-alist'.
If there is no defined expansion for the key, ask the user for a
replacement, see `template-read'. If the key is matched by
`template-register-regexp', store buffer position in register, see
`template-register', .
If you want to use a text literally which is matched by this regexp, use
the zero expansion form (>>>ZERO_FORM<<<)."
:group 'template-expansion
:type 'regexp)
(put 'template-expansion-regexp 'template-secure-value #'stringp)
(defcustom template-literal-environment '("LITERAL" . "/LITERAL")
"Environment for literal text in template.
Looks like (OPEN . CLOSE). Text between expansion forms with keys OPEN
and CLOSE is not expanded. If you change OPEN, you should change key
\"LITERAL\" in `template-default-expansion-alist' accordingly."
:group 'template-expansion
:type '(cons (string :tag "Open tag") (string :tag "Close tag")))
(defcustom template-register-regexp "\\`[0-9]\\'"
"*Regexp matching keys for storing point positions in registers.
These keys use `template-register' as the default expansion instead of
`template-read'. See `template-expansion-regexp'. If a register is used
twice, it is marked by a \"*\" in the echo area after the expansion."
:group 'template-expansion
:type 'regexp)
(put 'template-register-regexp 'template-secure-value #'stringp)
(defcustom template-expansion-alist nil
"User defined expansions forms.
Predefined expansion forms for `template-expansion-regexp'. Each entry
has the form (KEY . SEXP). These expansion forms shadow those in
`template-default-expansion-alist' and are shadowed by those in the
per-template definition section. See `template-definition-start'."
:group 'template-expansion
:type '(repeat (cons :format "%v"
(string :tag "Key" :value "")
(repeat :tag "Evaluate all" sexp))))
(put 'template-expansion-alist 'risky-local-variable t)
(defvar template-default-expansion-alist
'(("POINT" (setq template-point (point-marker))) ; point
("MARK" (setq template-mark (point-marker))) ; mark
("DIR" (insert (car template-file))) ; directory
("FILE" (insert (cadr template-file))) ; file name without directory
("FILE_SANS" (insert (nth 2 template-file)
(nth 3 template-file)))
("FILE_RAW" (insert (nth 2 template-file))) ; raw file name without number
("FILE_NUM" (insert (nth 3 template-file))) ; number
("FILE_UPCASE" (insert (upcase (nth 2 template-file))
(nth 3 template-file)))
("FILE_EXT" (or (string= (nth 4 template-file) "") ; extension
(insert (substring (nth 4 template-file) 1))))
("DATE" (template-insert-time template-date-format))
("TIME" (template-insert-time template-time-format))
("VC_DATE" (set-time-zone-rule "UTC")
(template-insert-time "%Y/%m/%d %T" "0000/00/00 00:00:00")
;; using saved `current-time-zone' doesn't work, but nil does
(set-time-zone-rule nil))
("YEAR" (template-insert-time "%Y" "0000"))
("ISO_DATE" (template-insert-time "%Y-%m-%d" "0000-00-00"))
("COMMENT" (template-read "Initial comment: ")) ; comment
("AUTHOR" (insert (or user-mail-address ; author
(and (fboundp 'user-mail-address)
(user-mail-address))
(concat (user-login-name) "@" (system-name)))))
("USER_NAME" (insert (or (and (boundp 'user-full-name) ; user name
user-full-name)
(user-full-name))))
("LOGIN_NAME" (insert (user-login-name))) ; login name
("HOST_ADDR" (insert (or (and (boundp 'mail-host-address) ; host address
(stringp mail-host-address)
mail-host-address)
(system-name))))
("LITERAL" (if (search-forward (format template-expansion-format
(cdr template-literal-environment))
nil 'limit)
(delete-region (match-beginning 0) (match-end 0))))
("ZERO_FORM")) ; zero form
"Predefined default expansions forms.
Predefined expansion forms for `template-expansion-regexp'. Each entry
has the form (KEY . SEXP). These expansion forms are shadowed by those
in `template-expansion-alist' and by those in the per-template
definition section. See `template-definition-start'.
The default predefined expansion forms are --default is inserting--:
(>>>POINT<<<) set point
(>>>MARK<<<) set mark, jump to it with \\[exchange-point-and-mark]
(>>>DIR<<<) directory: /home/clstaff/wedler/lib/
(>>>FILE<<<) file w/o directory: text1.txt
(>>>FILE_SANS<<<) file name w/o extension: text1
(>>>FILE_RAW<<<) raw file name: text
(>>>FILE_NUM<<<) number in name: 1
(>>>FILE_EXT<<<) extension: txt
(>>>FILE_UPCASE<<<) upcase file name w/o extension: TEXT1
(>>>DATE<<<) date using `template-date-format': 11 Jan 1999
(>>>TIME<<<) time using `template-time-format': 11:58:49
(>>>YEAR<<<) the year: 1999
(>>>ISO_DATE<<<) ISO 8601 date: 1999-01-11
(>>>VC_DATE<<<) UTC date/time for vc: 1999/01/11 10:58:49
(>>>COMMENT<<<) ask user for initial comment
(>>>AUTHOR<<<) author, i.e., `user-mail-address'
(>>>USER_NAME<<<) user name: Christoph Wedler
(>>>LOGIN_NAME<<<) login name: wedler
(>>>HOST_ADDR<<<) Host address: fmi.uni-passau.de
(>>>LITERAL<<<) literal text up to (>>>/LITERAL<<<)
(>>>ZERO_FORM<<<) zero form, i.e., insert nothing. Useful to insert
a text part matched by `template-expansion-regexp' literally.
There are aliases with one-letter keys, see `template-key-alias-alist'.
It is useful to follow the following conventions: upper case keys for
predefined extensions, lower case and digits for per-template and the
following default expansions:
(>>>0<<<) to (>>>9<<<) set registers 0 to 9, jump to it with
\\[jump-to-register] 0 etc., see `template-register-regexp'
(>>>x<<<) where x is any unused letter sequence: ask user.")
(put 'template-default-expansion-alist 'risky-local-variable t)
(defvar template-key-alias-alist
'(("P" . "POINT")
("M" . "MARK")
("D" . "DIR")
("F" . "FILE")
("R" . "FILE_RAW")
("N" . "FILE_NUM")
("B" . "FILE_UPCASE")
("E" . "FILE_EXT")
("T" . "DATE")
("V" . "VC_DATE")
("Y" . "YEAR")
("I" . "ISO_DATE")
("C" . "COMMENT")
("A" . "AUTHOR")
("U" . "USER_NAME")
("L" . "LOGIN_NAME")
("H" . "HOST_ADDR")
("Z" . "ZERO_FORM"))
"Alist to support the old one-letter predefined expansion forms.
Used for `template-expansion-alist' and
`template-default-expansion-alist'.")
(defcustom template-definition-start
">>>TEMPLATE-DEFINITION-SECTION<<<"
"Header for the per-template definition section.
The region following the the first match of this regexp defines the
per-template definition section. The region will be deleted before the
actual expansion, see `template-new-file'. If you use the \"Local
Variables:\" section, define it before this region.
The definition section defines expansion forms for strings KEYs matched
by `template-expansion-regexp' which might shadow those in
`template-expansion-alist' and `template-default-expansion-alist':
(\"KEY\"): zero form, same as (>>>ZERO_FORM<<<) in default value of
`template-default-expansion-alist', useful for inserting text matched by
`template-expansion-regexp' literally.
(\"KEY\". CHAR): CHAR is the register where the current buffer
position is stored, see `template-register-regexp'.
(\"KEY\" \"PROMPT\" \"PREFIX\" \"SUFFIX\" \"DEFAULT\" AGAIN-P) where
the last four arguments are optional: ask user with PROMPT for a STRING.
If STRING is not \"\", insert PREFIX STRING SUFFIX, otherwise DEFAULT.
For AGAIN-P, see `template-read'. To define, use
\\[template-define-prompt].
(\"KEY\" \"PROMPT\" (\"ANSWER\" \. \"TEXT\")...): ask user with PROMPT
for an input with completion over all ANSWERs and insert corresponding
TEXT. Expansion forms in TEXT will be expanded.
(\"KEY\" \"PROMPT\" (t \. \"TEXT-y\") (nil \. \"TEXT-n\")): ask user
with PROMPT a \"y or n\" question with `y-or-n-p' and insert TEXT-y or
TEXT-n, correspondingly. Expansion forms in TEXT-X will be expanded.
The y-case and the n-case are optional and can be exchanged.
(\"KEY\" \. SYMBOL): insert value of SYMBOL; if value is no string at
the time of the replacement, use `template-string-default' as format
string for SYMBOL.
(\"KEY\" COMMAND \. PREFIX): COMMAND is a symbol or a vector and is
called with `command-execute' after setting `prefix-arg' to PREFIX, not
evaluated. If COMMANDs symbol property `template-secure-command' is
nil, the form is insecure. If that symbol property is a function, it is
called with PREFIX to check whether COMMAND could be called directly
with PREFIX as remaining arguments.
(\"KEY\" SEXPR...): evaluate SEXPR during the expansion, see
`template-expansion-alist' for examples. This form is insecure.
There are other per-template definitions:
\"MESSAGE\": additional line displayed at point until first user event
or after `template-message-timeout' seconds. The lines are displayed
with face in `template-message-face'. To define, use
\\[template-define-message].
(CHAR \. \"CONTENTS\"): Set register CHAR to have contents CONTENTS.
CONTENTS can then be inserted into a buffer with \\[insert-register] CHAR.
(CHAR \"CONTENTS\" COMMENT) where COMMENT is optional: Set register
CHAR to have contents CONTENTS. CONTENTS can then be inserted into a
buffer with \\[insert-register] CHAR. Also display an additional line
at point to show the contents with COMMENT. To define, use
\\[template-define-register].
nil: the following forms depend on how often this form has appeared
yet.
(VARIABLE . VALUE): set SYMBOL's local value to VALUE, not evaluated.
This form is only secure if VARIABLE has a symbol property
`template-secure-value' which returns non-nil when applied to VALUE, not
evaluated. This form is useful for variables which determine the
expansion, like `template-time-format' and `template-date-format'. For
local variables in your new file, use the normal way via the \"Local
Variables:\" section. The nil form should not have appeared yet.
COMMAND: COMMAND is a symbol or a vector and is called with
`command-execute' before the expansion if the nil form has appeared
once, and after the expansion if the nil form has appeared twice yet.
If COMMANDs symbol property `template-secure-command' is nil, the form
is insecure. You should use the safe command `normal-mode' in the
pre-expansion forms if the expansion forms depend on the correct major
mode.
SEXPR: evaluate SEXPR before the expansion if the nil form has
appeared once, and after the expansion if the nil form has appeared
twice yet. This form is insecure.
If any insecure forms have been used, the user of the template will be
asked whether to use the template, see `template-confirm-insecure'."
:group 'template-expansion
:type 'string)
;;;;##########################################################################
;;;; Commenting
;;;;##########################################################################
;;;===========================================================================
;;; Main functions
;;;===========================================================================
;;;###autoload
(defun template-single-comment (&optional arg)
"Decorate the current comment-only line with dashes and alike.
That is, jump to the end of the current comment-only line and insert the
dashes and the final comment end-string up-to the fill position. Prefix
argument ARG and `template-comment-specification' determines the comment
style to use. The length of the resulting line is determined by
`template-max-column' and `template-max-column-with-end'."
(interactive "*P")
(let* ((syntax (if (and comment-start comment-start-skip)
(template-comment-syntax (point-marker))
(back-to-indentation)
nil))
(sep (template-comment-separator-regexp syntax))
(end (template-point-at-eol))
old)
(save-excursion
(cond ((re-search-forward sep end t)
;; with sep in current line
(setq old (buffer-substring (match-beginning 1) (match-end 1)))
(delete-region (match-beginning 0) (match-end 0)))
((cdr syntax) ; with start-end comment
(if (looking-at (concat "[ \t]*\\(.+\\)?"
(regexp-quote (cadr syntax))
"[ \t]*\\(.+\\)?$"))
(if (or (match-beginning 1) (match-beginning 2))
(error "This line contains non-separator chars and %S"
(cadr syntax))
;; Delete comment-end. Don't delete its first char if it is
;; the same as the second of comment-start.
(delete-region (if (and (= (length (car syntax)) 2)
(= (length (cadr syntax)) 2)
(eq (aref (car syntax) 1)
(aref (cadr syntax) 0)))
(1+ (match-beginning 0))
(match-beginning 0))
(match-end 0))))
(goto-char (cddr syntax))
(if (re-search-forward sep end t)
;; sep in line between comment-start and point-at-eol
(setq old (buffer-substring (match-beginning 1)
(match-end 1)))))))
(template-insert-separator
(car (template-comment-specification arg old syntax))
nil syntax)))
(put 'template-single-comment 'template-secure-command t)
;;;###autoload
(defun template-block-comment (&optional arg)
"Decorate the current block of comment-only lines with dashes and alike.
That is, surround the the contiguous comment-only lines around point
with extra lines containing dashes and alike and to put the correct
number of newlines around the block.
Barf if the comment syntax at point has a non-empty `comment-end' or if
point is not in a comment-only line.
A block comment consists of all neighboring lines which start with
spaces and `comment-start'. If `comment-start' is a string of length 1,
the number of repetitions of `comment-start' must be the same or larger