* srecode: New directory for SRecode template files.
[bpt/emacs.git] / lisp / cedet / srecode / insert.el
CommitLineData
4d902e6f
CY
1;;; srecode/insert --- Insert srecode templates to an output stream.
2
2f10955c 3;; Copyright (C) 2005, 2007, 2008, 2009 Free Software Foundation, Inc.
4d902e6f
CY
4
5;; Author: Eric M. Ludlam <zappo@gnu.org>
6
7;; This file is part of GNU Emacs.
8
9;; GNU Emacs is free software: you can redistribute it and/or modify
10;; it under the terms of the GNU General Public License as published by
11;; the Free Software Foundation, either version 3 of the License, or
12;; (at your option) any later version.
13
14;; GNU Emacs is distributed in the hope that it will be useful,
15;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17;; GNU General Public License for more details.
18
19;; You should have received a copy of the GNU General Public License
20;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
21
22;;; Commentary:
23;;
24;; Define and implements specific inserter objects.
25;;
26;; Manage the insertion process for a template.
27;;
28
29(require 'srecode/compile)
30(require 'srecode/find)
31(require 'srecode/dictionary)
32
33(defvar srecode-template-inserter-point)
34(declare-function srecode-overlaid-activate "srecode/fields")
35(declare-function srecode-template-inserted-region "srecode/fields")
36
37;;; Code:
38
39(defcustom srecode-insert-ask-variable-method 'ask
40 "Determine how to ask for a dictionary value when inserting a template.
41Only the ASK style inserter will query the user for a value.
42Dictionary value references that ask begin with the ? character.
43Possible values are:
44 'ask - Prompt in the minibuffer as the value is inserted.
45 'field - Use the dictionary macro name as the inserted value,
46 and place a field there. Matched fields change together.
47
48NOTE: The field feature does not yet work with XEmacs."
49 :group 'srecode
50 :type '(choice (const :tag "Ask" ask)
51 (cons :tag "Field" field)))
52
53(defvar srecode-insert-with-fields-in-progress nil
54 "Non-nil means that we are actively inserting a template with fields.")
55
56;;; INSERTION COMMANDS
57;;
58;; User level commands for inserting stuff.
59(defvar srecode-insertion-start-context nil
60 "The context that was at point at the beginning of the template insertion.")
61
62(defun srecode-insert-again ()
63 "Insert the previously inserted template (by name) again."
64 (interactive)
65 (let ((prev (car srecode-read-template-name-history)))
66 (if prev
67 (srecode-insert prev)
68 (call-interactively 'srecode-insert))))
69
70;;;###autoload
71(defun srecode-insert (template-name &rest dict-entries)
2f10955c 72 "Insert the template TEMPLATE-NAME into the current buffer at point.
4d902e6f
CY
73DICT-ENTRIES are additional dictionary values to add."
74 (interactive (list (srecode-read-template-name "Template Name: ")))
75 (if (not (srecode-table))
76 (error "No template table found for mode %s" major-mode))
77 (let ((newdict (srecode-create-dictionary))
78 (temp (srecode-template-get-table (srecode-table) template-name))
79 (srecode-insertion-start-context (srecode-calculate-context))
80 )
81 (if (not temp)
82 (error "No Template named %s" template-name))
83 (while dict-entries
84 (srecode-dictionary-set-value newdict
85 (car dict-entries)
86 (car (cdr dict-entries)))
87 (setq dict-entries (cdr (cdr dict-entries))))
88 ;;(srecode-resolve-arguments temp newdict)
89 (srecode-insert-fcn temp newdict)
90 ;; Don't put code here. We need to return the end-mark
91 ;; for this insertion step.
92 ))
93
94(defun srecode-insert-fcn (template dictionary &optional stream skipresolver)
95 "Insert TEMPLATE using DICTIONARY into STREAM.
96Optional SKIPRESOLVER means to avoid refreshing the tag list,
97or resolving any template arguments. It is assumed the caller
98has set everything up already."
99 ;; Perform the insertion.
100 (let ((standard-output (or stream (current-buffer)))
101 (end-mark nil))
102 (unless skipresolver
103 ;; Make sure the semantic tags are up to date.
104 (semantic-fetch-tags)
105 ;; Resolve the arguments
106 (srecode-resolve-arguments template dictionary))
107 ;; Insert
108 (if (bufferp standard-output)
109 ;; If there is a buffer, turn off various hooks. This will cause
110 ;; the mod hooks to be buffered up during the insert, but
111 ;; prevent tools like font-lock from fontifying mid-template.
112 ;; Especialy important during insertion of complex comments that
113 ;; cause the new font-lock to comment-color stuff after the inserted
114 ;; comment.
115 ;;
116 ;; I'm not sure about the motion hooks. It seems like a good
117 ;; idea though.
118 ;;
119 ;; Borrowed these concepts out of font-lock.
120 ;;
121 ;; I tried `combine-after-change-calls', but it did not have
122 ;; the effect I wanted.
123 (let ((start (point)))
124 (let ((inhibit-point-motion-hooks t)
125 (inhibit-modification-hooks t)
126 )
127 (srecode--insert-into-buffer template dictionary)
128 )
129 ;; Now call those after change functions.
130 (run-hook-with-args 'after-change-functions
131 start (point) 0)
132 )
133 (srecode-insert-method template dictionary))
134 ;; Handle specialization of the POINT inserter.
135 (when (and (bufferp standard-output)
136 (slot-boundp 'srecode-template-inserter-point 'point)
137 )
138 (set-buffer standard-output)
139 (setq end-mark (point-marker))
140 (goto-char (oref srecode-template-inserter-point point)))
141 (oset-default 'srecode-template-inserter-point point eieio-unbound)
142
143 ;; Return the end-mark.
144 (or end-mark (point)))
145 )
146
147(defun srecode--insert-into-buffer (template dictionary)
148 "Insert a TEMPLATE with DICTIONARY into a buffer.
149Do not call this function yourself. Instead use:
150 `srecode-insert' - Inserts by name.
151 `srecode-insert-fcn' - Insert with objects.
152This function handles the case from one of the above functions when
153the template is inserted into a buffer. It looks
154at `srecode-insert-ask-variable-method' to decide if unbound dictionary
155entries ask questions or insert editable fields.
156
157Buffer based features related to change hooks is handled one level up."
158 ;; This line prevents the field archive from being let bound
159 ;; while the field insert tool is loaded via autoloads during
160 ;; the insert.
161 (when (eq srecode-insert-ask-variable-method 'field)
b82525f2 162 (require 'srecode/fields))
4d902e6f
CY
163
164 (let ((srecode-field-archive nil) ; Prevent field leaks during insert
165 (start (point)) ; Beginning of the region.
166 )
167 ;; This sub-let scopes the 'in-progress' piece so we know
168 ;; when to setup the end-template.
169 (let ((srecode-insert-with-fields-in-progress
170 (if (eq srecode-insert-ask-variable-method 'field) t nil))
171 )
172 (srecode-insert-method template dictionary)
173 )
174 ;; If we are not in-progress, and we insert fields, then
175 ;; create the end-template with fields editable area.
176 (when (and (not srecode-insert-with-fields-in-progress)
177 (eq srecode-insert-ask-variable-method 'field) ; Only if user asked
178 srecode-field-archive ; Only if there were fields created
179 )
180 (let ((reg
181 ;; Create the field-driven editable area.
182 (srecode-template-inserted-region
183 "TEMPLATE" :start start :end (point))))
184 (srecode-overlaid-activate reg))
185 )
186 ;; We return with 'point being the end of the template insertion
187 ;; area. Return value is not important.
188 ))
189
190;;; TEMPLATE ARGUMENTS
191;;
192;; Some templates have arguments. Each argument is assocaited with
193;; a function that can resolve the inputs needed.
194(defun srecode-resolve-arguments (temp dict)
195 "Resolve all the arguments needed by the template TEMP.
196Apply anything learned to the dictionary DICT."
197 (srecode-resolve-argument-list (oref temp args) dict temp))
198
199(defun srecode-resolve-argument-list (args dict &optional temp)
200 "Resolve arguments in the argument list ARGS.
201ARGS is a list of symbols, such as :blank, or :file.
202Apply values to DICT.
203Optional argument TEMP is the template that is getting it's arguments resolved."
204 (let ((fcn nil))
205 (while args
206 (setq fcn (intern-soft (concat "srecode-semantic-handle-"
207 (symbol-name (car args)))))
208 (if (not fcn)
209 (error "Error resolving template argument %S" (car args)))
210 (if temp
211 (condition-case nil
212 ;; Allow some to accept a 2nd argument optionally.
213 ;; They throw an error if not available, so try again.
214 (funcall fcn dict temp)
215 (wrong-number-of-arguments (funcall fcn dict)))
216 (funcall fcn dict))
217 (setq args (cdr args)))
218 ))
219
220;;; INSERTION STACK & METHOD
221;;
222;; Code managing the top-level insert method and the current
223;; insertion stack.
224;;
225(defmethod srecode-push ((st srecode-template))
226 "Push the srecoder template ST onto the active stack."
227 (oset st active (cons st (oref st active))))
228
229(defmethod srecode-pop :STATIC ((st srecode-template))
230 "Pop the srecoder template ST onto the active stack.
231ST can be a class, or an object."
232 (oset st active (cdr (oref st active))))
233
234(defmethod srecode-peek :STATIC ((st srecode-template))
235 "Fetch the topmost active template record. ST can be a class."
236 (car (oref st active)))
237
238(defmethod srecode-insert-method ((st srecode-template) dictionary)
239 "Insert the srecoder template ST."
240 ;; Merge any template entries into the input dictionary.
241 (when (slot-boundp st 'dictionary)
242 (srecode-dictionary-merge dictionary (oref st dictionary)))
243 ;; Do an insertion.
244 (unwind-protect
245 (let ((c (oref st code)))
246 (srecode-push st)
247 (srecode-insert-code-stream c dictionary))
248 ;; Poping the stack is protected
249 (srecode-pop st)))
250
251(defun srecode-insert-code-stream (code dictionary)
252 "Insert the CODE from a template into `standard-output'.
253Use DICTIONARY to resolve any macros."
254 (while code
255 (cond ((stringp (car code))
256 (princ (car code)))
257 (t
258 (srecode-insert-method (car code) dictionary)))
259 (setq code (cdr code))))
260
261;;; INSERTERS
262;;
263;; Specific srecode inserters.
264;; The base class is from srecode-compile.
265;;
266;; Each inserter handles various macro codes from the temlate.
267;; The `code' slot specifies a character used to identify which
268;; inserter is to be created.
269;;
270(defclass srecode-template-inserter-newline (srecode-template-inserter)
271 ((key :initform "\n"
272 :allocation :class
273 :documentation
274 "The character code used to identify inserters of this style.")
275 (hard :initform nil
276 :initarg :hard
277 :documentation
278 "Is this a hard newline (always inserted) or optional?
279Optional newlines don't insert themselves if they are on a blank line
280by themselves.")
281 )
282 "Insert a newline, and possibly do indenting.
283Specify the :indent argument to enable automatic indentation when newlines
284occur in your template.")
285
286(defmethod srecode-insert-method ((sti srecode-template-inserter-newline)
287 dictionary)
288 "Insert the STI inserter."
289 ;; To be safe, indent the previous line since the template will
290 ;; change what is there to indent
291 (let ((i (srecode-dictionary-lookup-name dictionary "INDENT"))
292 (inbuff (bufferp standard-output))
293 (doit t)
294 (pm (point-marker)))
295 (when (and inbuff (not (oref sti hard)))
296 ;; If this is not a hard newline, we need do the calculation
297 ;; and set "doit" to nil.
298 (beginning-of-line)
299 (save-restriction
300 (narrow-to-region (point) pm)
301 (when (looking-at "\\s-*$")
302 (setq doit nil)))
303 (goto-char pm)
304 )
305 ;; Do indentation reguardless of the newline.
306 (when (and (eq i t) inbuff)
307 (indent-according-to-mode)
308 (goto-char pm))
309
310 (when doit
311 (princ "\n")
312 ;; Indent after the newline, particularly for numeric indents.
313 (cond ((and (eq i t) (bufferp standard-output))
314 ;; WARNING - indent according to mode requires that standard-output
315 ;; is a buffer!
316 ;; @todo - how to indent in a string???
317 (setq pm (point-marker))
318 (indent-according-to-mode)
319 (goto-char pm))
320 ((numberp i)
321 (princ (make-string i " ")))
322 ((stringp i)
323 (princ i))))))
324
325(defmethod srecode-dump ((ins srecode-template-inserter-newline) indent)
326 "Dump the state of the SRecode template inserter INS."
327 (call-next-method)
328 (when (oref ins hard)
329 (princ " : hard")
330 ))
331
332(defclass srecode-template-inserter-blank (srecode-template-inserter)
333 ((key :initform "\r"
334 :allocation :class
335 :documentation
336 "The character represeinting this inserter style.
337Can't be blank, or it might be used by regular variable insertion.")
338 (where :initform 'begin
339 :initarg :where
340 :documentation
341 "This should be 'begin or 'end, indicating where to insrt a CR.
342When set to 'begin, it will insert a CR if we are not at 'bol'.
343When set to 'end it will insert a CR if we are not at 'eol'")
344 ;; @TODO - Add slot and control for the number of blank
345 ;; lines before and after point.
346 )
347 "Insert a newline before and after a template, and possibly do indenting.
348Specify the :blank argument to enable this inserter.")
349
350(defmethod srecode-insert-method ((sti srecode-template-inserter-blank)
351 dictionary)
352 "Make sure there is no text before or after point."
353 (let ((i (srecode-dictionary-lookup-name dictionary "INDENT"))
354 (inbuff (bufferp standard-output))
355 (pm (point-marker)))
356 (when (and inbuff
357 ;; Don't do this if we are not the active template.
358 (= (length (oref srecode-template active)) 1))
359
360 (when (and (eq i t) inbuff (not (eq (oref sti where) 'begin)))
361 (indent-according-to-mode)
362 (goto-char pm))
363
364 (cond ((and (eq (oref sti where) 'begin) (not (bolp)))
365 (princ "\n"))
366 ((eq (oref sti where) 'end)
367 ;; If there is whitespace after pnt, then clear it out.
368 (when (looking-at "\\s-*$")
369 (delete-region (point) (point-at-eol)))
370 (when (not (eolp))
371 (princ "\n")))
372 )
373 (setq pm (point-marker))
374 (when (and (eq i t) inbuff (not (eq (oref sti where) 'end)))
375 (indent-according-to-mode)
376 (goto-char pm))
377 )))
378
379(defclass srecode-template-inserter-comment (srecode-template-inserter)
380 ((key :initform ?!
381 :allocation :class
382 :documentation
383 "The character code used to identify inserters of this style.")
384 )
385 "Allow comments within template coding. This inserts nothing.")
386
387(defmethod srecode-inserter-prin-example :STATIC ((ins srecode-template-inserter-comment)
388 escape-start escape-end)
389 "Insert an example using inserter INS.
390Arguments ESCAPE-START and ESCAPE-END are the current escape sequences in use."
391 (princ " ")
392 (princ escape-start)
393 (princ "! Miscellaneous text commenting in your template. ")
394 (princ escape-end)
395 (terpri)
396 )
397
398(defmethod srecode-insert-method ((sti srecode-template-inserter-comment)
399 dictionary)
400 "Don't insert anything for comment macros in STI."
401 nil)
402
403
404(defclass srecode-template-inserter-variable (srecode-template-inserter)
405 ((key :initform nil
406 :allocation :class
407 :documentation
408 "The character code used to identify inserters of this style."))
409 "Insert the value of a dictionary entry
410If there is no entry, insert nothing.")
411
412(defvar srecode-inserter-variable-current-dictionary nil
413 "The active dictionary when calling a variable filter.")
414
415(defmethod srecode-insert-variable-secondname-handler
416 ((sti srecode-template-inserter-variable) dictionary value secondname)
417 "For VALUE handle SECONDNAME behaviors for this variable inserter.
418Return the result as a string.
419By default, treat as a function name.
420If SECONDNAME is nil, return VALUE."
421 (if secondname
422 (let ((fcnpart (read secondname)))
423 (if (fboundp fcnpart)
424 (let ((srecode-inserter-variable-current-dictionary dictionary))
425 (funcall fcnpart value))
426 ;; Else, warn.
2f10955c 427 (error "Variable insertion second arg %s is not a function"
4d902e6f
CY
428 secondname)))
429 value))
430
431(defmethod srecode-insert-method ((sti srecode-template-inserter-variable)
432 dictionary)
433 "Insert the STI inserter."
434 ;; Convert the name into a name/fcn pair
435 (let* ((name (oref sti :object-name))
436 (fcnpart (oref sti :secondname))
437 (val (srecode-dictionary-lookup-name
438 dictionary name))
439 (do-princ t)
440 )
441 ;; Alert if a macro wasn't found.
442 (when (not val)
443 (message "Warning: macro %S was not found in the dictionary." name)
444 (setq val ""))
445 ;; If there was a functional part, call that function.
446 (cond ;; Strings
447 ((stringp val)
448 (setq val (srecode-insert-variable-secondname-handler
449 sti dictionary val fcnpart)))
450 ;; Compound data value
451 ((srecode-dictionary-compound-value-child-p val)
452 ;; Force FCN to be a symbol
453 (when fcnpart (setq fcnpart (read fcnpart)))
454 ;; Convert compound value to a string with the fcn.
455 (setq val (srecode-compound-toString val fcnpart dictionary))
456 ;; If the value returned is nil, then it may be a special
457 ;; field inserter that requires us to set do-princ to nil.
458 (when (not val)
459 (setq do-princ nil)
460 )
461 )
462 ;; Dictionaries... not allowed in this style
463 ((srecode-dictionary-child-p val)
2f10955c 464 (error "Macro %s cannot insert a dictionary - use section macros instead"
4d902e6f
CY
465 name))
466 ;; Other stuff... convert
467 (t
2f10955c 468 (error "Macro %s cannot insert arbitrary data" name)
4d902e6f
CY
469 ;;(if (and val (not (stringp val)))
470 ;; (setq val (format "%S" val))))
471 ))
472 ;; Output the dumb thing unless the type of thing specifically
473 ;; did the inserting forus.
474 (when do-princ
475 (princ val))))
476
477(defclass srecode-template-inserter-ask (srecode-template-inserter-variable)
478 ((key :initform ??
479 :allocation :class
480 :documentation
481 "The character code used to identify inserters of this style.")
482 (prompt :initarg :prompt
483 :initform nil
484 :documentation
485 "The prompt used to query for this dictionary value.")
486 (defaultfcn :initarg :defaultfcn
487 :initform nil
488 :documentation
489 "The function which can calculate a default value.")
490 (read-fcn :initarg :read-fcn
491 :initform 'read-string
492 :documentation
493 "The function used to read in the text for this prompt.")
494 )
495 "Insert the value of a dictionary entry
496If there is no entry, prompt the user for the value to use.
497The prompt text used is derived from the previous PROMPT command in the
498template file.")
499
500(defmethod srecode-inserter-apply-state ((ins srecode-template-inserter-ask) STATE)
501 "For the template inserter INS, apply information from STATE.
502Loop over the prompts to see if we have a match."
503 (let ((prompts (oref STATE prompts))
504 )
505 (while prompts
506 (when (string= (semantic-tag-name (car prompts))
507 (oref ins :object-name))
508 (oset ins :prompt
509 (semantic-tag-get-attribute (car prompts) :text))
510 (oset ins :defaultfcn
511 (semantic-tag-get-attribute (car prompts) :default))
512 (oset ins :read-fcn
513 (or (semantic-tag-get-attribute (car prompts) :read)
514 'read-string))
515 )
516 (setq prompts (cdr prompts)))
517 ))
518
519(defmethod srecode-insert-method ((sti srecode-template-inserter-ask)
520 dictionary)
521 "Insert the STI inserter."
522 (let ((val (srecode-dictionary-lookup-name
523 dictionary (oref sti :object-name))))
524 (if val
525 ;; Does some extra work. Oh well.
526 (call-next-method)
527
528 ;; How is our -ask value determined?
529 (if srecode-insert-with-fields-in-progress
530 ;; Setup editable fields.
531 (setq val (srecode-insert-method-field sti dictionary))
532 ;; Ask the question...
533 (setq val (srecode-insert-method-ask sti dictionary)))
534
535 ;; After asking, save in the dictionary so that
536 ;; the user can use the same name again later.
537 (srecode-dictionary-set-value
538 (srecode-root-dictionary dictionary)
539 (oref sti :object-name) val)
540
541 ;; Now that this value is safely stowed in the dictionary,
542 ;; we can do what regular inserters do.
543 (call-next-method))))
544
545(defmethod srecode-insert-ask-default ((sti srecode-template-inserter-ask)
546 dictionary)
547 "Derive the default value for an askable inserter STI.
548DICTIONARY is used to derive some values."
549 (let ((defaultfcn (oref sti :defaultfcn)))
550 (cond ((stringp defaultfcn)
551 defaultfcn)
552 ((functionp defaultfcn)
553 (funcall defaultfcn))
554 ((and (listp defaultfcn)
555 (eq (car defaultfcn) 'macro))
556 (srecode-dictionary-lookup-name
557 dictionary (cdr defaultfcn)))
558 ((null defaultfcn)
559 "")
560 (t
561 (error "Unknown default for prompt: %S"
562 defaultfcn)))))
563
564(defmethod srecode-insert-method-ask ((sti srecode-template-inserter-ask)
565 dictionary)
566 "Do the \"asking\" for the template inserter STI.
567Use DICTIONARY to resolve values."
568 (let* ((prompt (oref sti prompt))
569 (default (srecode-insert-ask-default sti dictionary))
570 (reader (oref sti :read-fcn))
571 (val nil)
572 )
573 (cond ((eq reader 'y-or-n-p)
574 (if (y-or-n-p (or prompt
575 (format "%s? "
576 (oref sti :object-name))))
577 (setq val default)
578 (setq val "")))
579 ((eq reader 'read-char)
580 (setq val (format
581 "%c"
582 (read-char (or prompt
583 (format "Char for %s: "
584 (oref sti :object-name))))))
585 )
586 (t
587 (save-excursion
588 (setq val (funcall reader
589 (or prompt
590 (format "Specify %s: "
591 (oref sti :object-name)))
592 default
593 )))))
594 ;; Return our derived value.
595 val)
596 )
597
598(defmethod srecode-insert-method-field ((sti srecode-template-inserter-ask)
599 dictionary)
600 "Create an editable field for the template inserter STI.
601Use DICTIONARY to resolve values."
602 (let* ((default (srecode-insert-ask-default sti dictionary))
603 (compound-value
604 (srecode-field-value (oref sti :object-name)
605 :firstinserter sti
606 :defaultvalue default))
607 )
608 ;; Return this special compound value as the thing to insert.
609 ;; This special compound value will repeat our asked question
610 ;; across multiple locations.
611 compound-value))
612
613(defmethod srecode-dump ((ins srecode-template-inserter-ask) indent)
614 "Dump the state of the SRecode template inserter INS."
615 (call-next-method)
616 (princ " : \"")
617 (princ (oref ins prompt))
618 (princ "\"")
619 )
620
621(defclass srecode-template-inserter-width (srecode-template-inserter-variable)
622 ((key :initform ?|
623 :allocation :class
624 :documentation
625 "The character code used to identify inserters of this style.")
626 )
627 "Inserts the value of a dictionary variable with a specific width.
628The second argument specifies the width, and a pad, seperated by a colon.
629thus a specification of `10:left' will insert the value of A
630to 10 characters, with spaces added to the left. Use `right' for adding
631spaces to the right.")
632
633(defmethod srecode-insert-variable-secondname-handler
634 ((sti srecode-template-inserter-width) dictionary value width)
635 "For VALUE handle WIDTH behaviors for this variable inserter.
636Return the result as a string.
637By default, treat as a function name."
638 (if width
639 ;; Trim or pad to new length
640 (let* ((split (split-string width ":"))
641 (width (string-to-number (nth 0 split)))
642 (second (nth 1 split))
643 (pad (cond ((or (null second) (string= "right" second))
644 'right)
645 ((string= "left" second)
646 'left)
647 (t
648 (error "Unknown pad type %s" second)))))
649 (if (>= (length value) width)
650 ;; Simple case - too long.
651 (substring value 0 width)
652 ;; We need to pad on one side or the other.
653 (let ((padchars (make-string (- width (length value)) ? )))
654 (if (eq pad 'left)
655 (concat padchars value)
656 (concat value padchars)))))
2f10955c 657 (error "Width not specified for variable/width inserter")))
4d902e6f
CY
658
659(defmethod srecode-inserter-prin-example :STATIC ((ins srecode-template-inserter-width)
660 escape-start escape-end)
661 "Insert an example using inserter INS.
662Arguments ESCAPE-START and ESCAPE-END are the current escape sequences in use."
663 (princ " ")
664 (princ escape-start)
665 (princ "|A:10:right")
666 (princ escape-end)
667 (terpri)
668 )
669
670(defvar srecode-template-inserter-point-override nil
2f10955c 671 "When non-nil, the point inserter will do this function instead.")
4d902e6f
CY
672
673(defclass srecode-template-inserter-point (srecode-template-inserter)
674 ((key :initform ?^
675 :allocation :class
676 :documentation
677 "The character code used to identify inserters of this style.")
678 (point :type (or null marker)
679 :allocation :class
680 :documentation
681 "Record the value of (point) in this class slot.
682It is the responsibility of the inserter algorithm to clear this
683after a successful insertion."))
684 "Record the value of (point) when inserted.
685The cursor is placed at the ^ macro after insertion.
686Some inserter macros, such as `srecode-template-inserter-include-wrap'
687will place text at the ^ macro from the included macro.")
688
689(defmethod srecode-inserter-prin-example :STATIC ((ins srecode-template-inserter-point)
690 escape-start escape-end)
691 "Insert an example using inserter INS.
692Arguments ESCAPE-START and ESCAPE-END are the current escape sequences in use."
693 (princ " ")
694 (princ escape-start)
695 (princ "^")
696 (princ escape-end)
697 (terpri)
698 )
699
700(defmethod srecode-insert-method ((sti srecode-template-inserter-point)
701 dictionary)
702 "Insert the STI inserter.
703Save point in the class allocated 'point' slot.
704If `srecode-template-inserter-point-override' then this generalized
705marker will do something else. See `srecode-template-inserter-include-wrap'
706as an example."
707 (if srecode-template-inserter-point-override
708 ;; Disable the old override while we do this.
709 (let ((over srecode-template-inserter-point-override)
710 (srecode-template-inserter-point-override nil))
711 (funcall over dictionary)
712 )
713 (oset sti point (point-marker))
714 ))
715
716(defclass srecode-template-inserter-subtemplate (srecode-template-inserter)
717 ()
718 "Wrap a section of a template under the control of a macro."
719 :abstract t)
720
721(defmethod srecode-inserter-prin-example :STATIC ((ins srecode-template-inserter-subtemplate)
722 escape-start escape-end)
723 "Insert an example using inserter INS.
724Arguments ESCAPE-START and ESCAPE-END are the current escape sequences in use."
725 (call-next-method)
726 (princ " Template Text to control")
727 (terpri)
728 (princ " ")
729 (princ escape-start)
730 (princ "/VARNAME")
731 (princ escape-end)
732 (terpri)
733 )
734
735(defmethod srecode-insert-subtemplate ((sti srecode-template-inserter-subtemplate)
736 dict slot)
737 "Insert a subtemplate for the inserter STI with dictionary DICT."
738 ;; make sure that only dictionaries are used.
739 (when (not (srecode-dictionary-child-p dict))
740 (error "Only section dictionaries allowed for %s"
741 (object-name-string sti)))
742 ;; Output the code from the sub-template.
743 (srecode-insert-method (slot-value sti slot) dict)
744 )
745
746(defmethod srecode-insert-method-helper ((sti srecode-template-inserter-subtemplate)
747 dictionary slot)
748 "Do the work for inserting the STI inserter.
749Loops over the embedded CODE which was saved here during compilation.
750The template to insert is stored in SLOT."
751 (let ((dicts (srecode-dictionary-lookup-name
752 dictionary (oref sti :object-name))))
753 ;; If there is no section dictionary, then don't output anything
754 ;; from this section.
755 (while dicts
756 (srecode-insert-subtemplate sti (car dicts) slot)
757 (setq dicts (cdr dicts)))))
758
759(defmethod srecode-insert-method ((sti srecode-template-inserter-subtemplate)
760 dictionary)
761 "Insert the STI inserter.
762Calls back to `srecode-insert-method-helper' for this class."
763 (srecode-insert-method-helper sti dictionary 'template))
764
765
766(defclass srecode-template-inserter-section-start (srecode-template-inserter-subtemplate)
767 ((key :initform ?#
768 :allocation :class
769 :documentation
770 "The character code used to identify inserters of this style.")
771 (template :initarg :template
772 :documentation
773 "A Template used to frame the codes from this inserter.")
774 )
775 "Apply values from a sub-dictionary to a template section.
776The dictionary saved at the named dictionary entry will be
777applied to the text between the section start and the
778`srecode-template-inserter-section-end' macro.")
779
780(defmethod srecode-parse-input ((ins srecode-template-inserter-section-start)
781 tag input STATE)
782 "For the section inserter INS, parse INPUT.
783Shorten input until the END token is found.
784Return the remains of INPUT."
785 (let* ((out (srecode-compile-split-code tag input STATE
786 (oref ins :object-name))))
787 (oset ins template (srecode-template
788 (object-name-string ins)
789 :context nil
790 :args nil
791 :code (cdr out)))
792 (car out)))
793
794(defmethod srecode-dump ((ins srecode-template-inserter-section-start) indent)
795 "Dump the state of the SRecode template inserter INS."
796 (call-next-method)
797 (princ "\n")
798 (srecode-dump-code-list (oref (oref ins template) code)
799 (concat indent " "))
800 )
801
802(defclass srecode-template-inserter-section-end (srecode-template-inserter)
803 ((key :initform ?/
804 :allocation :class
805 :documentation
806 "The character code used to identify inserters of this style.")
807 )
808 "All template segments between the secion-start and section-end
809are treated specially.")
810
811(defmethod srecode-insert-method ((sti srecode-template-inserter-section-end)
812 dictionary)
813 "Insert the STI inserter."
814 )
815
816(defmethod srecode-match-end ((ins srecode-template-inserter-section-end) name)
817
818 "For the template inserter INS, do I end a section called NAME?"
819 (string= name (oref ins :object-name)))
820
821(defclass srecode-template-inserter-include (srecode-template-inserter-subtemplate)
822 ((key :initform ?>
823 :allocation :class
824 :documentation
825 "The character code used to identify inserters of this style.")
826 (includedtemplate
827 :initarg :includedtemplate
828 :documentation
829 "The template included for this inserter."))
830 "Include a different template into this one.
831The included template will have additional dictionary entries from the subdictionary
832stored specified by this macro.")
833
834(defmethod srecode-inserter-prin-example :STATIC ((ins srecode-template-inserter-include)
835 escape-start escape-end)
836 "Insert an example using inserter INS.
837Arguments ESCAPE-START and ESCAPE-END are the current escape sequences in use."
838 (princ " ")
839 (princ escape-start)
840 (princ ">DICTNAME:contextname:templatename")
841 (princ escape-end)
842 (terpri)
843 )
844
845(defmethod srecode-insert-include-lookup ((sti srecode-template-inserter-include)
846 dictionary)
847 "For the template inserter STI, lookup the template to include.
848Finds the template with this macro function part and stores it in
849this template instance."
850 (let* ((templatenamepart (oref sti :secondname))
851 )
852 ;; If there was no template name, throw an error
853 (if (not templatenamepart)
2f10955c 854 (error "Include macro %s needs a template name" (oref sti :object-name)))
4d902e6f
CY
855 ;; Find the template by name, and save it.
856 (if (or (not (slot-boundp sti 'includedtemplate))
857 (not (oref sti includedtemplate)))
858 (let ((tmpl (srecode-template-get-table (srecode-table)
859 templatenamepart))
860 (active (oref srecode-template active))
861 ctxt)
862 (when (not tmpl)
863 ;; If it isn't just available, scan back through
864 ;; the active template stack, searching for a matching
865 ;; context.
866 (while (and (not tmpl) active)
867 (setq ctxt (oref (car active) context))
868 (setq tmpl (srecode-template-get-table (srecode-table)
869 templatenamepart
870 ctxt))
871 (when (not tmpl)
872 (when (slot-boundp (car active) 'table)
873 (let ((app (oref (oref (car active) table) application)))
874 (when app
875 (setq tmpl (srecode-template-get-table
876 (srecode-table)
877 templatenamepart
878 ctxt app)))
879 )))
880 (setq active (cdr active)))
881 (when (not tmpl)
882 ;; If it wasn't in this context, look to see if it
883 ;; defines it's own context
884 (setq tmpl (srecode-template-get-table (srecode-table)
885 templatenamepart)))
886 )
887 (oset sti :includedtemplate tmpl)))
888
889 (if (not (oref sti includedtemplate))
890 ;; @todo - Call into a debugger to help find the template in question.
891 (error "No template \"%s\" found for include macro `%s'"
892 templatenamepart (oref sti :object-name)))
893 ))
894
895(defmethod srecode-insert-method ((sti srecode-template-inserter-include)
896 dictionary)
897 "Insert the STI inserter.
898Finds the template with this macro function part, and inserts it
899with the dictionaries found in the dictinary."
900 (srecode-insert-include-lookup sti dictionary)
901 ;; Insert the template.
902 ;; Our baseclass has a simple way to do this.
903 (if (srecode-dictionary-lookup-name dictionary (oref sti :object-name))
904 ;; If we have a value, then call the next method
905 (srecode-insert-method-helper sti dictionary 'includedtemplate)
906 ;; If we don't have a special dictitonary, then just insert with the
907 ;; current dictionary.
908 (srecode-insert-subtemplate sti dictionary 'includedtemplate))
909 )
910
911;;
912;; This template combines the include template and the sectional template.
913;; It will first insert the included template, then insert the embedded
914;; template wherever the $^$ in the included template was.
915;;
916;; Since it uses dual inheretance, it will magically get the end-matching
917;; behavior of #, with the including feature of >.
918;;
919(defclass srecode-template-inserter-include-wrap (srecode-template-inserter-include srecode-template-inserter-section-start)
920 ((key :initform ?<
921 :allocation :class
922 :documentation
923 "The character code used to identify inserters of this style.")
924 )
925 "Include a different template into this one, and add text at the ^ macro.
926The included template will have additional dictionary entries from the subdictionary
927stored specified by this macro. If the included macro includes a ^ macro,
928then the text between this macro and the end macro will be inserted at
929the ^ macro.")
930
931(defmethod srecode-inserter-prin-example :STATIC ((ins srecode-template-inserter-include-wrap)
932 escape-start escape-end)
933 "Insert an example using inserter INS.
934Arguments ESCAPE-START and ESCAPE-END are the current escape sequences in use."
935 (princ " ")
936 (princ escape-start)
937 (princ "<DICTNAME:contextname:templatename")
938 (princ escape-end)
939 (terpri)
940 (princ " Template Text to insert at ^ macro")
941 (terpri)
942 (princ " ")
943 (princ escape-start)
944 (princ "/DICTNAME")
945 (princ escape-end)
946 (terpri)
947 )
948
949(defmethod srecode-insert-method ((sti srecode-template-inserter-include-wrap)
950 dictionary)
951 "Insert the template STI.
952This will first insert the include part via inheritance, then
953insert the section it wraps into the location in the included
954template where a ^ inserter occurs."
955 ;; Step 1: Look up the included inserter
956 (srecode-insert-include-lookup sti dictionary)
957 ;; Step 2: Temporarilly override the point inserter.
958 (let* ((vaguely-unique-name sti)
959 (srecode-template-inserter-point-override
960 (lambda (dict2)
961 (if (srecode-dictionary-lookup-name
962 dict2 (oref vaguely-unique-name :object-name))
963 ;; Insert our sectional part with looping.
964 (srecode-insert-method-helper
965 vaguely-unique-name dict2 'template)
966 ;; Insert our sectional part just once.
967 (srecode-insert-subtemplate vaguely-unique-name
968 dict2 'template))
969 )))
970 ;; Do a regular insertion for an include, but with our override in
971 ;; place.
972 (call-next-method)
973 ))
974
975(provide 'srecode/insert)
976
977;; Local variables:
978;; generated-autoload-file: "loaddefs.el"
979;; generated-autoload-feature: srecode/loaddefs
980;; generated-autoload-load-name: "srecode/insert"
981;; End:
982
3999968a 983;; arch-tag: a5aa3401-924a-4617-8513-2f0f01256872
4d902e6f 984;;; srecode/insert.el ends here