Download
(defvar dc-auto-insert-directory "~/.el/defaultcontent"
"*Directory from which template files are taken. On
Windows, use forward slashes like: c:/foo/bar/baz ")
(defvar dc-auto-insert-alist
'(("\\.tex$" . "tex-insert.tex")
("\\.c$" . "c-insert.c")
("\\.h$" . "h-insert.h")
("[I]?[Mm]akefile" . "makefile-insert")
("\\.scm$" (dc-fill-initial-content-of-file))
("\\.el$" (dc-fill-initial-content-of-file))
("\\.bib$" . "bib-insert.bib") )
"An Alist specifying text to insert by default into a new file.
Elements look like (REGEXP . FILENAME) or (REGEXP LISP-CODE ...);
if the new file's name matches REGEXP, then the file FILENAME is
inserted into the buffer or LISP-CODE is evaluated with the same goal.
Only the first matching element is effective." )
(defvar dc-auto-insert-mode-alist
'((sh-mode . "sh-insert.sh") )
"An Alist specifying text to insert by default into a new file.
Elements look like (MODE . FILENAME) or (MODE LISP-CODE ...);
if the new file's major mode MODE, then the file FILENAME is
inserted into the buffer or LISP-CODE is evaluated with the same goal.
Only the first matching element is effective. This list is tried
after `dc-auto-insert-alist'." )
(defun dc-fill-initial-content-of-file ()
"Create the initial content of a RCS-kept file appropriately ended."
(goto-char 0)
(insert comment-start)(insert comment-start)(insert comment-start)
(insert " \$Id\$\n\n")(insert comment-end)
(goto-char (point-max))
(insert comment-start)(insert comment-start)(insert comment-start)
(insert " end of ")(insert (file-name-nondirectory buffer-file-name))
(insert comment-end)(insert "\n")
(goto-char 0)(goto-line 2)
(message "Nature dislikes emptyness!") )
(defvar dc-initial-dot-position nil
"This variable defines, if not nil, the initial position of the dot.
It can be set by the @DOT@ pseudo-variable in a template." )
(defvar dc-fast-variable-handling t
"A boolean telling if variables are slowly recognized with a regexp
or quickly handled with a delimiting character." )
(defvar dc-variable-delimiter "\@[^@]*\@"
"Regexp to recognize variables to expand." )
(defvar dc-variable-border "@"
"Delimiting character for variables to expand." )
(defvar dc-expandos-alist
'(( "@BASEFILENAME@" (file-name-nondirectory buffer-file-name) )
( "@BASEFILENAMELESSEXTENSION@"
(dc--filename-remove-extension
(file-name-nondirectory buffer-file-name) ) )
( "@FILENAME@" buffer-file-name )
( "@DATE@" (current-time-string) )
( "@HOST@" (or (getenv "HOST") (getenv "COMPUTERNAME")))
( "@AUTHOR@" (capitalize (or (getenv "USER") (getenv "USERNAME"))))
( "@COMMENT-START@" (if comment-start comment-start "") )
( "@COMMENT-END@" (if comment-end comment-end "") )
( "@DOT@" (setq dc-initial-dot-position (match-beginning 0))
"" )
( "@\\(INSERT\\|INSERTFILE\\)(\\(.+\\))@"
(let ((filename
(buffer-substring-no-properties
(match-beginning 2)
(match-end 2))))
(if (file-readable-p filename)
(with-temp-buffer
(insert-file-contents filename)
(buffer-substring-no-properties (point-min) (point-max)))
(concat "The file '" filename "' is not readable"))))
( "@ENV(\\(.+\\))@" (let ((varname
(buffer-substring-no-properties
(match-beginning 1)
(match-end 1))))
(or (getenv varname) varname)))
( "@@" "@")
( "@LISP(\\(.*\\))@" (let (sexp value (here (point)))
(goto-char (match-beginning 0))
(setq sexp (dc--read-closest-sexp))
(if sexp (setq value (eval sexp)))
(goto-char here)
(if value value "") ) )
)
"An Alist specifying the variables to recognize and how to replace them.
Elements look like (REGEXP LISP-CODE ...). When a variable is recognized,
using dc-variable-delimiter, it is compared to the REGEXPs (if dc-fast-
-variable-handling is false) and once one is found, the associated forms
are evaluated and the result replaces the occurrence of the variable." )
(require 'thingatpt)
(defun dc--read-closest-sexp ()
"utility to read sexp at pt"
(thing-at-point 'sexp))
(defun dc--filename-remove-extension (name &optional extension)
"Return NAME less its EXTENSION. If the extension is given as second
argument then it is an error for the extension not to be present."
(let* ((extension (if extension (regexp-quote extension) "\\.[^.]*"))
(regexp (concat "\\(.*\\)" extension "$")) )
(if (string-match regexp name)
(substring name (match-beginning 1) (match-end 1))
(error "No extension" name) ) ) )
(defvar dc-show-unexpanded-variables t
"This variable shows, if true, the variables that cannot be expanded
by dc-auto-insert-file. Nevertheless, it slows down expansion but gives
you a chance to see bad variables." )
(defun dc-expand-internal-variables (start)
"Replace @ things @ by their expansion in a freshly filled file."
(interactive (list 0))
(goto-char start)
(let ((number-of-expanded-variables 0))
(if dc-fast-variable-handling
(while (search-forward dc-variable-border nil t)
(backward-char 1)
(let ((l dc-expandos-alist))
(while (consp l)
(let ((regexp (car (car l)))
(forms (cdr (car l))) )
(setq l (cdr l))
(if (looking-at regexp)
(let* ((the-first-match (match-data))
(new (eval (cons 'progn forms))) )
(store-match-data the-first-match)
(replace-match new t t)
(setq number-of-expanded-variables
(+ 1 number-of-expanded-variables) )
(setq l nil) )
(if (null l)
(forward-char 1) ) ) ) )))
(while (re-search-forward dc-variable-delimiter nil t)
(let ((the-first-match (match-data))
(beg (match-beginning 0))
(end (match-end 0)) )
(goto-char beg)
(let ((l dc-expandos-alist))
(while (consp l)
(let ((regexp (car (car l)))
(forms (cdr (car l))) )
(setq l (cdr l))
(if (looking-at regexp)
(let (new)
(goto-char end)
(setq new (eval (cons 'progn forms)))
(setq number-of-expanded-variables
(+ 1 number-of-expanded-variables) )
(store-match-data the-first-match)
(replace-match "" t t)
(insert new)
(setq l nil) )
(if (and (null l) dc-show-unexpanded-variables)
(progn
(goto-char (+ beg 1))
(message "Cannot expand \"%s\"."
(buffer-substring beg end) ) )
(sleep-for 1) ) ) ) ) ) ) ) )
(message "Note: %s variable(s) expanded."
number-of-expanded-variables ) ) )
(defvar dc-file-appears-to-be-new nil
"Global variable set to T if find-file-not-found-hooks was run.
Used by dc-insert-auto-insert-file to detect if a file is new." )
(defun dc-mark-file-as-new ()
(setq dc-file-appears-to-be-new t)
() )
(add-hook 'find-file-not-found-hooks 'dc-mark-file-as-new)
(defun dc-insert-auto-insert-file ()
"Matches the visited file name against the elements of `dc-auto-insert-alist'
to determine the initial content of the visited file."
(interactive)
(let ((mfan dc-file-appears-to-be-new))
(if (and (or current-prefix-arg mfan) (= 0 (buffer-size)))
(let ((alist dc-auto-insert-alist)
(name (file-name-sans-versions buffer-file-name))
(data nil) )
(while (and (not data) alist)
(if (string-match (car (car alist)) name)
(setq data (cdr (car alist)))
(setq alist (cdr alist))))
(if (not data)
(let ((pair (assoc major-mode dc-auto-insert-mode-alist)))
(if pair
(setq data (cdr pair)))))
(when (and data (listp data) (stringp (caar data)))
(setq data (completing-read
"Select template to insert: "
(mapcar (lambda (elem) (cons elem elem)) (car data)))))
(cond ((not data)
(message "No initial content specified!") )
((stringp data)
(let ((file (concat (file-name-as-directory dc-auto-insert-directory) data)))
(if (file-readable-p file)
(progn
(insert-file-contents file)
(setq dc-initial-dot-position 0)
(dc-expand-internal-variables 0)
(goto-char dc-initial-dot-position) )
(progn
(message "Auto-insert: Template file %s not found" file)
(sleep-for 1) ) ) ) )
(t (eval (cons 'progn data))) )
(setq dc-file-appears-to-be-new nil) ) ) ) )
(add-hook 'find-file-hooks 'dc-insert-auto-insert-file t)
(provide 'defaultcontent)