1;;; autoinsert.el --- automatic mode-dependent insertion of text into new files -*- lexical-binding: t -*- 2 3;; Copyright (C) 1985-1987, 1994-1995, 1998, 2000-2021 Free Software 4;; Foundation, Inc. 5 6;; Author: Charlie Martin <crm@cs.duke.edu> 7;; Adapted-By: Daniel Pfeiffer <occitan@esperanto.org> 8;; Keywords: convenience 9;; Maintainer: emacs-devel@gnu.org 10 11;; This file is part of GNU Emacs. 12 13;; GNU Emacs is free software: you can redistribute it and/or modify 14;; it under the terms of the GNU General Public License as published by 15;; the Free Software Foundation, either version 3 of the License, or 16;; (at your option) any later version. 17 18;; GNU Emacs is distributed in the hope that it will be useful, 19;; but WITHOUT ANY WARRANTY; without even the implied warranty of 20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21;; GNU General Public License for more details. 22 23;; You should have received a copy of the GNU General Public License 24;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. 25 26;;; Commentary: 27 28;; The following defines an association list for text to be 29;; automatically inserted when a new file is created, and a function 30;; which automatically inserts these files; the idea is to insert 31;; default text much as the mode is automatically set using 32;; auto-mode-alist. 33;; 34;; To use: 35;; (auto-insert-mode t) 36;; setq auto-insert-directory to an appropriate slash-terminated value 37;; 38;; You can also customize the variable `auto-insert-mode' to load the 39;; package. Alternatively, add the following to your init file: 40;; (auto-insert-mode 1) 41;; 42;; Author: Charlie Martin 43;; Department of Computer Science and 44;; National Biomedical Simulation Resource 45;; Box 3709 46;; Duke University Medical Center 47;; Durham, NC 27710 48;; (crm@cs.duke.edu,mcnc!duke!crm) 49 50;;; Code: 51 52(require 'seq) 53 54(defgroup auto-insert nil 55 "Automatic mode-dependent insertion of text into new files." 56 :prefix "auto-insert-" 57 :group 'files 58 :group 'convenience 59 :link '(custom-manual "(autotype) Autoinserting")) 60 61 62(defcustom auto-insert 'not-modified 63 "Controls automatic insertion into newly found empty files. 64Possible values: 65 nil do nothing 66 t insert if possible 67 other insert if possible, but mark as unmodified. 68Insertion is possible when something appropriate is found in 69`auto-insert-alist'. When the insertion is marked as unmodified, you can 70save it with \\[write-file] RET. 71This variable is used when the function `auto-insert' is called, e.g. 72when you do (add-hook \\='find-file-hook \\='auto-insert). 73With \\[auto-insert], this is always treated as if it were t." 74 :type '(choice (const :tag "Insert if possible" t) 75 (const :tag "Do nothing" nil) 76 (other :tag "insert if possible, mark as unmodified." 77 not-modified))) 78 79(defcustom auto-insert-query 'function 80 "Non-nil means ask user before auto-inserting. 81When this is `function', only ask when called non-interactively." 82 :type '(choice (const :tag "Don't ask" nil) 83 (const :tag "Ask if called non-interactively" function) 84 (other :tag "Ask" t))) 85 86(defcustom auto-insert-prompt "Perform %s auto-insertion? " 87 "Prompt to use when querying whether to auto-insert. 88If this contains a %s, that will be replaced by the matching rule." 89 :type 'string) 90 91 92(defcustom auto-insert-alist 93 '((("\\.\\([Hh]\\|hh\\|hpp\\|hxx\\|h\\+\\+\\)\\'" . "C / C++ header") 94 (replace-regexp-in-string 95 "[^A-Z0-9]" "_" 96 (replace-regexp-in-string 97 "\\+" "P" 98 (upcase (file-name-nondirectory buffer-file-name)))) 99 "#ifndef " str \n 100 "#define " str "\n\n" 101 _ "\n\n#endif") 102 103 (("\\.\\([Cc]\\|cc\\|cpp\\|cxx\\|c\\+\\+\\)\\'" . "C / C++ program") 104 nil 105 "#include \"" 106 (let ((stem (file-name-sans-extension buffer-file-name)) 107 ret) 108 (dolist (ext '("H" "h" "hh" "hpp" "hxx" "h++") ret) 109 (when (file-exists-p (concat stem "." ext)) 110 (setq ret (file-name-nondirectory (concat stem "." ext)))))) 111 & ?\" | -10) 112 113 (("[Mm]akefile\\'" . "Makefile") . "makefile.inc") 114 115 (html-mode . (lambda () (sgml-tag "html"))) 116 117 (plain-tex-mode . "tex-insert.tex") 118 (bibtex-mode . "tex-insert.tex") 119 (latex-mode 120 ;; should try to offer completing read for these 121 "options, RET: " 122 "\\documentclass[" str & ?\] | -1 123 ?{ (read-string "class: ") "}\n" 124 ("package, %s: " 125 "\\usepackage[" (read-string "options, RET: ") & ?\] | -1 ?{ str "}\n") 126 _ "\n\\begin{document}\n" _ 127 "\n\\end{document}") 128 129 (("/bin/.*[^/]\\'" . "Shell-Script mode magic number") . 130 (lambda () 131 (if (eq major-mode (default-value 'major-mode)) 132 (sh-mode)))) 133 134 (ada-mode . ada-header) 135 136 (("\\.[1-9]\\'" . "Man page skeleton") 137 "Short description: " 138 ".\\\" Copyright (C), " (format-time-string "%Y") " " 139 (getenv "ORGANIZATION") | (progn user-full-name) 140 " 141.\\\" You may distribute this file under the terms of the GNU Free 142.\\\" Documentation License. 143.TH " (file-name-base (buffer-file-name)) 144 " " (file-name-extension (buffer-file-name)) 145 " " (format-time-string "%Y-%m-%d ") 146 "\n.SH NAME\n" 147 (file-name-base (buffer-file-name)) 148 " \\- " str 149 "\n.SH SYNOPSIS 150.B " (file-name-base (buffer-file-name)) 151 "\n" 152 _ 153 " 154.SH DESCRIPTION 155.SH OPTIONS 156.SH FILES 157.SH \"SEE ALSO\" 158.SH BUGS 159.SH AUTHOR 160" (user-full-name) 161 '(if (search-backward "&" (line-beginning-position) t) 162 (replace-match (capitalize (user-login-name)) t t)) 163 '(end-of-line 1) " <" (progn user-mail-address) ">\n") 164 165 (".dir-locals.el" 166 nil 167 ";;; Directory Local Variables\n" 168 ";;; For more information see (info \"(emacs) Directory Variables\")\n\n" 169 "((" 170 '(setq v1 (let (modes) 171 (mapatoms (lambda (mode) 172 (let ((name (symbol-name mode))) 173 (when (string-match "-mode$" name) 174 (push name modes))))) 175 (sort modes 'string<))) 176 (completing-read "Local variables for mode: " v1 nil t) 177 " . ((" 178 (let ((all-variables 179 (apropos-internal ".*" 180 (lambda (symbol) 181 (and (boundp symbol) 182 (get symbol 'variable-documentation)))))) 183 (completing-read "Variable to set: " all-variables)) 184 " . " 185 (completing-read "Value to set it to: " nil) 186 "))))\n") 187 188 (("\\.el\\'" . "Emacs Lisp header") 189 "Short description: " 190 ";;; " (file-name-nondirectory (buffer-file-name)) " --- " str 191 (make-string (max 2 (- 80 (current-column) 27)) ?\s) 192 "-*- lexical-binding: t; -*-" '(setq lexical-binding t) 193 " 194 195;; Copyright (C) " (format-time-string "%Y") " " 196 (getenv "ORGANIZATION") | (progn user-full-name) " 197 198;; Author: " (user-full-name) 199'(if (search-backward "&" (line-beginning-position) t) 200 (replace-match (capitalize (user-login-name)) t t)) 201'(end-of-line 1) " <" (progn user-mail-address) "> 202;; Keywords: " 203 '(require 'finder) 204 ;;'(setq v1 (apply 'vector (mapcar 'car finder-known-keywords))) 205 '(setq v1 (mapcar (lambda (x) (list (symbol-name (car x)))) 206 finder-known-keywords) 207 v2 (mapconcat (lambda (x) (format "%12s: %s" (car x) (cdr x))) 208 finder-known-keywords 209 "\n")) 210 ((let ((minibuffer-help-form v2)) 211 (completing-read "Keyword, C-h: " v1 nil t)) 212 str ", ") & -2 " 213 214\;; This program is free software; you can redistribute it and/or modify 215\;; it under the terms of the GNU General Public License as published by 216\;; the Free Software Foundation, either version 3 of the License, or 217\;; (at your option) any later version. 218 219\;; This program is distributed in the hope that it will be useful, 220\;; but WITHOUT ANY WARRANTY; without even the implied warranty of 221\;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 222\;; GNU General Public License for more details. 223 224\;; You should have received a copy of the GNU General Public License 225\;; along with this program. If not, see <https://www.gnu.org/licenses/>. 226 227\;;; Commentary: 228 229\;; " _ " 230 231\;;; Code: 232 233 234 235\(provide '" 236 (file-name-base (buffer-file-name)) 237 ") 238\;;; " (file-name-nondirectory (buffer-file-name)) " ends here\n") 239 (("\\.texi\\(nfo\\)?\\'" . "Texinfo file skeleton") 240 "Title: " 241 "\\input texinfo @c -*-texinfo-*- 242@c %**start of header 243@setfilename " 244 (file-name-base (buffer-file-name)) ".info\n" 245 "@settitle " str " 246@c %**end of header 247@copying\n" 248 (setq short-description (read-string "Short description: ")) 249 ".\n\n" 250 "Copyright @copyright{} " (format-time-string "%Y") " " 251 (getenv "ORGANIZATION") | (progn user-full-name) " 252 253@quotation 254Permission is granted to copy, distribute and/or modify this document 255under the terms of the GNU Free Documentation License, Version 1.3 256or any later version published by the Free Software Foundation; 257with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. 258A copy of the license is included in the section entitled ``GNU 259Free Documentation License''. 260 261A copy of the license is also available from the Free Software 262Foundation Web site at @url{https://www.gnu.org/licenses/fdl.html}. 263 264@end quotation 265 266The document was typeset with 267@uref{https://www.gnu.org/software/texinfo/, GNU Texinfo}. 268 269@end copying 270 271@titlepage 272@title " str " 273@subtitle " short-description " 274@author " (getenv "ORGANIZATION") | (progn user-full-name) 275 " <" (progn user-mail-address) "> 276@page 277@vskip 0pt plus 1filll 278@insertcopying 279@end titlepage 280 281@c Output the table of the contents at the beginning. 282@contents 283 284@ifnottex 285@node Top 286@top " str " 287 288@insertcopying 289@end ifnottex 290 291@c Generate the nodes for this menu with `C-c C-u C-m'. 292@menu 293@end menu 294 295@c Update all node entries with `C-c C-u C-n'. 296@c Insert new nodes with `C-c C-c n'. 297@node Chapter One 298@chapter Chapter One 299 300" _ " 301 302@node Copying This Manual 303@appendix Copying This Manual 304 305@menu 306* GNU Free Documentation License:: License for copying this manual. 307@end menu 308 309@c Get fdl.texi from https://www.gnu.org/licenses/fdl.html 310@include fdl.texi 311 312@node Index 313@unnumbered Index 314 315@printindex cp 316 317@bye 318 319@c " (file-name-nondirectory (buffer-file-name)) " ends here\n")) 320 "A list specifying text to insert by default into a new file. 321Elements look like (CONDITION . ACTION) or ((CONDITION . DESCRIPTION) . ACTION). 322CONDITION may be a regexp that must match the new file's name, or it may be 323a symbol that must match the major mode for this element to apply. 324Only the first matching element is effective. 325Optional DESCRIPTION is a string for filling `auto-insert-prompt'. 326ACTION may be a skeleton to insert (see `skeleton-insert'), an absolute 327file-name or one relative to `auto-insert-directory' or a function to call. 328ACTION may also be a vector containing several successive single actions as 329described above, e.g. [\"header.insert\" date-and-author-update]." 330 :type '(alist :key-type 331 (choice (regexp :tag "Regexp matching file name") 332 (symbol :tag "Major mode") 333 (cons :tag "Condition and description" 334 (choice :tag "Condition" 335 (regexp :tag "Regexp matching file name") 336 (symbol :tag "Major mode")) 337 (string :tag "Description"))) 338 ;; There's no custom equivalent of "repeat" for vectors. 339 :value-type (choice file function 340 (sexp :tag "Skeleton or vector"))) 341 :version "27.1") 342 343 344;; Establish a default value for auto-insert-directory 345(defcustom auto-insert-directory "~/insert/" 346 "Directory from which auto-inserted files are taken. 347The value must be an absolute directory name; 348thus, on a GNU or Unix system, it must end in a slash." 349 :type 'directory) 350 351 352;;;###autoload 353(defun auto-insert () 354 "Insert default contents into new files if variable `auto-insert' is non-nil. 355Matches the visited file name against the elements of `auto-insert-alist'." 356 (interactive) 357 (and (not buffer-read-only) 358 (or (eq this-command 'auto-insert) 359 (and auto-insert 360 (bobp) (eobp))) 361 (let* ((case-fold-search nil) 362 (desc nil) 363 ;; Find first matching alist entry. 364 (action 365 (seq-some 366 (pcase-lambda (`(,cond . ,action)) 367 (if (atom cond) 368 (setq desc cond) 369 (setq desc (cdr cond) 370 cond (car cond))) 371 (when (if (symbolp cond) 372 (derived-mode-p cond) 373 (and buffer-file-name 374 (string-match cond buffer-file-name))) 375 action)) 376 auto-insert-alist))) 377 (goto-char 1) 378 ;; Now, if we found something, do it 379 (and action 380 (or (not (stringp action)) 381 (file-readable-p (expand-file-name 382 action auto-insert-directory))) 383 (or (not auto-insert-query) 384 (if (eq auto-insert-query 'function) 385 (eq this-command 'auto-insert)) 386 (y-or-n-p (format auto-insert-prompt desc))) 387 (mapc 388 (lambda (action) 389 (if (stringp action) 390 (if (file-readable-p 391 (setq action (expand-file-name 392 action auto-insert-directory))) 393 (insert-file-contents action)) 394 (save-window-excursion 395 ;; make buffer visible before skeleton or function 396 ;; which might ask the user for something 397 (switch-to-buffer (current-buffer)) 398 (if (and (consp action) 399 (not (eq (car action) 'lambda))) 400 (skeleton-insert action) 401 (funcall action))))) 402 (if (vectorp action) 403 action 404 (vector action)))) 405 (and (buffer-modified-p) 406 (not (eq this-command 'auto-insert)) 407 (set-buffer-modified-p (eq auto-insert t))))) 408 ;; Return nil so that it could be used in 409 ;; `find-file-not-found-functions', though that's probably inadvisable. 410 nil) 411 412 413;;;###autoload 414(defun define-auto-insert (condition action &optional after) 415 "Associate CONDITION with (additional) ACTION in `auto-insert-alist'. 416Optional AFTER means to insert action after all existing actions for CONDITION, 417or if CONDITION had no actions, after all other CONDITIONs." 418 (let ((elt (assoc condition auto-insert-alist))) 419 (if elt 420 (setcdr elt 421 (if (vectorp (cdr elt)) 422 (vconcat (if after (cdr elt)) 423 (if (vectorp action) action (vector action)) 424 (if after () (cdr elt))) 425 (if after 426 (vector (cdr elt) action) 427 (vector action (cdr elt))))) 428 (if after 429 (nconc auto-insert-alist (list (cons condition action))) 430 (push (cons condition action) auto-insert-alist))))) 431 432;;;###autoload 433(define-minor-mode auto-insert-mode 434 "Toggle Auto-insert mode, a global minor mode. 435 436When Auto-insert mode is enabled, when new files are created you can 437insert a template for the file depending on the mode of the buffer." 438 :global t :group 'auto-insert 439 (if auto-insert-mode 440 (add-hook 'find-file-hook 'auto-insert) 441 (remove-hook 'find-file-hook 'auto-insert))) 442 443(provide 'autoinsert) 444 445;;; autoinsert.el ends here 446