Skip to content

Commit f28f0d0

Browse files
committed
Extract tempo templates into adoc-mode-tempo.el
The tempo subsystem (template definitions, handlers, helpers) is self-contained, so move it to its own file to reduce the size of the main file. Tempo tests move to test/adoc-mode-tempo-test.el.
1 parent 48abf50 commit f28f0d0

4 files changed

Lines changed: 443 additions & 354 deletions

File tree

adoc-mode-tempo.el

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
;;; adoc-mode-tempo.el --- Tempo templates for adoc-mode -*- lexical-binding: t; -*-
2+
;;
3+
;; Copyright 2009-2016 Florian Kaufmann <sensorflo@gmail.com>
4+
;; Copyright 2022-2026 Bozhidar Batsov <bozhidar@batsov.dev> and adoc-mode contributors
5+
;;
6+
;; This file is not part of GNU Emacs.
7+
;;
8+
;; This program is free software; you can redistribute it and/or modify
9+
;; it under the terms of the GNU General Public License as published by
10+
;; the Free Software Foundation; either version 2, or (at your option)
11+
;; any later version.
12+
;;
13+
;; This program is distributed in the hope that it will be useful,
14+
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
;; GNU General Public License for more details.
17+
;;
18+
;; You should have received a copy of the GNU General Public License
19+
;; along with this program; see the file COPYING. If not, write to
20+
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21+
;; Floor, Boston, MA 02110-1301, USA.
22+
23+
;;; Commentary:
24+
25+
;; Tempo template definitions and handlers for adoc-mode.
26+
27+
;;; Code:
28+
29+
(require 'tempo)
30+
31+
;; Defined in adoc-mode.el; declarations without initial values
32+
;; silence the byte-compiler without interfering with defcustom/defconst.
33+
(defvar adoc-title-style)
34+
(defvar adoc-help-emphasis)
35+
(defvar adoc-help-bold)
36+
(defvar adoc-help-monospace)
37+
(defvar adoc-help-single-quote)
38+
(defvar adoc-help-double-quote)
39+
(defvar adoc-help-attributed)
40+
(defvar adoc-help-line-break)
41+
(defvar adoc-help-page-break)
42+
(defvar adoc-help-ruler-line)
43+
(defvar adoc-help-entity-reference)
44+
(defvar adoc-help-literal-paragraph)
45+
(defvar adoc-help-delimited-block-comment)
46+
(defvar adoc-help-delimited-block-passthrouh)
47+
(defvar adoc-help-delimited-block-listing)
48+
(defvar adoc-help-delimited-block-literal)
49+
(defvar adoc-help-delimited-block-quote)
50+
(defvar adoc-help-delimited-block-example)
51+
(defvar adoc-help-delimited-block-sidebar)
52+
(defvar adoc-help-delimited-block-open-block)
53+
(defvar adoc-help-list-item-continuation)
54+
(defvar adoc-help-url)
55+
(defvar adoc-help-anchor)
56+
(defvar adoc-help-xref)
57+
(defvar adoc-help-pass)
58+
(defvar adoc-help-asciimath)
59+
(defvar adoc-help-latexmath)
60+
(defvar adoc-help-pass-+++)
61+
(defvar adoc-help-pass-$$)
62+
63+
(declare-function adoc-make-two-line-title-underline "adoc-mode")
64+
(declare-function adoc-insert-indented "adoc-mode")
65+
66+
(defcustom adoc-tempo-frwk 'tempo-vanilla
67+
"Tempo framework to be used by adoc's templates. "
68+
:type '(choice (const :tag "tempo" tempo-vanilla)
69+
(const :tag "tempo-snippets" tempo-snippets))
70+
:group 'adoc)
71+
72+
;;;; tempos
73+
;; TODO: tell user to make use of tempo-interactive
74+
;; TODO: tell user to how to use tempo-snippets?? that there are clear methods
75+
;; TODO: tell user to how to use tempo-snippets?? suggested customizations working best with adoc
76+
;; TODO: after changing adoc-tempo-frwk, all adoc-tempo-define need to be
77+
;; evaluated again. This doesn't feel right
78+
;; TODO: titles,block titles,blockid,... should start on a new line
79+
;; PROBLEM: snippets don't allow empty 'field', e.g. empty caption
80+
;; Workaround: mark whole 'edit-field' and delete it
81+
(if (eq adoc-tempo-frwk 'tempo-snippets)
82+
(require 'tempo-snippets)
83+
(require 'tempo))
84+
85+
(defun adoc-tempo-insert-template-fix (orig-fn template on-region)
86+
"Work around `tempo-insert-template' failing when ON-REGION is
87+
non-nil but no mark is set. When `tempo-insert-region' is nil,
88+
`tempo-define-template' passes the raw prefix arg as ON-REGION,
89+
which can be truthy even without an active region."
90+
(funcall orig-fn template (and on-region (mark t) on-region)))
91+
92+
(advice-add 'tempo-insert-template :around #'adoc-tempo-insert-template-fix)
93+
94+
(defun adoc-tempo-define (&rest args)
95+
(if (eq adoc-tempo-frwk 'tempo-snippets)
96+
(apply 'tempo-define-snippet args) ;; optional package, not always available
97+
(apply #'tempo-define-template args)))
98+
99+
(defun adoc-template-str-title (&optional level title-text)
100+
"Returns the string tempo-template-adoc-title-x would insert"
101+
(with-temp-buffer
102+
(insert (or title-text "foo"))
103+
(set-mark (point-min))
104+
(funcall (intern-soft (concat "tempo-template-adoc-title-" (number-to-string (1+ (or level 0))))))
105+
(replace-regexp-in-string "\n" "\\\\n"
106+
(buffer-substring-no-properties (point-min) (point-max)))))
107+
108+
;; Text formatting - constrained quotes
109+
(adoc-tempo-define "adoc-emphasis" '("_" (r "text" text) "_") nil (bound-and-true-p adoc-help-emphasis))
110+
(adoc-tempo-define "adoc-bold" '("*" (r "text" text) "*") nil (bound-and-true-p adoc-help-bold))
111+
(adoc-tempo-define "adoc-typewriter-face" '("+" (r "text" text) "+") nil (bound-and-true-p adoc-help-monospace))
112+
(adoc-tempo-define "adoc-monospace-literal" '("`" (r "text" text) "`"))
113+
(adoc-tempo-define "adoc-single-quote" '("`" (r "text" text) "'") nil (bound-and-true-p adoc-help-single-quote))
114+
(adoc-tempo-define "adoc-double-quote" '("``" (r "text" text) "''") nil (bound-and-true-p adoc-help-double-quote))
115+
(adoc-tempo-define "adoc-attributed" '("[" p "]#" (r "text" text) "#") nil (bound-and-true-p adoc-help-double-quote))
116+
117+
;; Text formatting - unconstrained quotes
118+
(adoc-tempo-define "adoc-emphasis-uc" '("__" (r "text" text) "__") nil (bound-and-true-p adoc-help-emphasis))
119+
(adoc-tempo-define "adoc-bold-uc" '("**" (r "text" text) "**") nil (bound-and-true-p adoc-help-bold))
120+
(adoc-tempo-define "adoc-monospace-uc" '("++" (r "text" text) "++") nil (bound-and-true-p adoc-help-monospace))
121+
(adoc-tempo-define "adoc-attributed-uc" '("[" p "]##" (r "text" text) "##") nil (bound-and-true-p adoc-help-attributed))
122+
(adoc-tempo-define "adoc-superscript" '("^" (r "text" text) "^"))
123+
(adoc-tempo-define "adoc-subscript" '("~" (r "text" text) "~"))
124+
125+
;; Text formatting - misc
126+
(adoc-tempo-define "adoc-line-break" '((if (eq (char-before) ?\s) "" " ") "+" %) nil (bound-and-true-p adoc-help-line-break))
127+
(adoc-tempo-define "adoc-page-break" '(bol "<<<" %) nil (bound-and-true-p adoc-help-page-break))
128+
(adoc-tempo-define "adoc-ruler-line" '(bol "---" %) nil (bound-and-true-p adoc-help-ruler-line))
129+
130+
;; Text formatting - replacements
131+
(adoc-tempo-define "adoc-copyright" '("(C)"))
132+
(adoc-tempo-define "adoc-trademark" '("(T)"))
133+
(adoc-tempo-define "adoc-registered-trademark" '("(R)"))
134+
(adoc-tempo-define "adoc-dash" '("---"))
135+
(adoc-tempo-define "adoc-ellipsis" '("..."))
136+
(adoc-tempo-define "adoc-right-arrow" '("->"))
137+
(adoc-tempo-define "adoc-left-arrow" '("<-"))
138+
(adoc-tempo-define "adoc-right-double-arrow" '("=>"))
139+
(adoc-tempo-define "adoc-left-double-arrow" '("<="))
140+
(adoc-tempo-define "adoc-entity-reference" '("&" r ";") nil (bound-and-true-p adoc-help-entity-reference))
141+
142+
;; Titles
143+
;; todo
144+
;; - merge with adoc-make-title
145+
;; - dwim:
146+
;; - when point is on a text line, convert that line to a title
147+
;; - when it is already a title .... correct underlines?
148+
;; - ensure n blank lines before and m blank lines after title, or unchanged if n/m nil
149+
(dotimes (level 5) ; level starting at 0
150+
(let ((one-line-del (make-string (1+ level) ?\=)))
151+
152+
(adoc-tempo-define
153+
(concat "adoc-title-" (number-to-string (1+ level)))
154+
;; see adoc-tempo-handler for what the (tr ...) does.
155+
(list
156+
`(cond
157+
((eq adoc-title-style 'adoc-title-style-one-line)
158+
'(tr bol ,one-line-del " " (r "text" text)))
159+
((eq adoc-title-style 'adoc-title-style-one-line-enclosed)
160+
'(tr bol ,one-line-del " " (r "text" text) " " ,one-line-del))
161+
;; BUG in tempo: when first thing is a tempo element which introduces a marker, that
162+
;; marker is skipped
163+
((eq adoc-title-style 'adoc-title-style-two-line)
164+
'(tr bol (r "text" text) "\n"
165+
(adoc-make-two-line-title-underline ,level (if (adoc-tempo-on-region) (- tempo-region-stop tempo-region-start)))))
166+
(t
167+
(error "Unknown title style"))))
168+
nil
169+
(concat
170+
"Inserts a level " (number-to-string (1+ level)) " (starting at 1) title.
171+
Is influenced by customization variables such as `adoc-title-style'."))))
172+
173+
(adoc-tempo-define "adoc-block-title" '(bol "." (r "text" text) %))
174+
175+
;; Paragraphs
176+
(adoc-tempo-define "adoc-literal-paragraph" '(bol " " (r "text" text) %) nil (bound-and-true-p adoc-help-literal-paragraph))
177+
(adoc-tempo-define "adoc-paragraph-tip" '(bol "TIP: " (r "text" text) %))
178+
(adoc-tempo-define "adoc-paragraph-note" '(bol "NOTE: " (r "text" text) %))
179+
(adoc-tempo-define "adoc-paragraph-important" '(bol "IMPORTANT: " (r "text" text) %))
180+
(adoc-tempo-define "adoc-paragraph-warning" '(bol "WARNING: " (r "text" text) %))
181+
(adoc-tempo-define "adoc-paragraph-caution" '(bol "CAUTION: " (r "text" text) %))
182+
183+
;; delimited blocks
184+
(adoc-tempo-define "adoc-delimited-block-comment"
185+
'(bol (make-string 50 ?/) n (r-or-n "text" text) bol (make-string 50 ?/) %)
186+
nil (bound-and-true-p adoc-help-delimited-block-comment))
187+
(adoc-tempo-define "adoc-delimited-block-passthrough"
188+
'(bol (make-string 50 ?+) n (r-or-n "text" text) bol (make-string 50 ?+) %)
189+
nil (bound-and-true-p adoc-help-delimited-block-passthrouh))
190+
(adoc-tempo-define "adoc-delimited-block-listing"
191+
'(bol (make-string 50 ?-) n (r-or-n "text" text) bol (make-string 50 ?-) %)
192+
nil (bound-and-true-p adoc-help-delimited-block-listing))
193+
(adoc-tempo-define "adoc-delimited-block-literal"
194+
'(bol (make-string 50 ?.) n (r-or-n "text" text) bol (make-string 50 ?.) %)
195+
nil (bound-and-true-p adoc-help-delimited-block-literal))
196+
(adoc-tempo-define "adoc-delimited-block-quote"
197+
'(bol (make-string 50 ?_) n (r-or-n "text" text) bol (make-string 50 ?_) %)
198+
nil (bound-and-true-p adoc-help-delimited-block-quote))
199+
(adoc-tempo-define "adoc-delimited-block-example"
200+
'(bol (make-string 50 ?=) n (r-or-n "text" text) bol (make-string 50 ?=) %)
201+
nil (bound-and-true-p adoc-help-delimited-block-example))
202+
(adoc-tempo-define "adoc-delimited-block-sidebar"
203+
'(bol (make-string 50 ?*) n (r-or-n "text" text) bol (make-string 50 ?*) %)
204+
nil (bound-and-true-p adoc-help-delimited-block-sidebar))
205+
(adoc-tempo-define "adoc-delimited-block-open-block"
206+
'(bol "--" n (r-or-n "text" text) bol "--" %)
207+
nil (bound-and-true-p adoc-help-delimited-block-open-block))
208+
209+
;; Lists
210+
;; TODO: customize indentation
211+
(adoc-tempo-define "adoc-bulleted-list-item-1" '(bol (adoc-insert-indented "- " 1) (r "text" text)))
212+
(adoc-tempo-define "adoc-bulleted-list-item-2" '(bol (adoc-insert-indented "** " 2) (r "text" text)))
213+
(adoc-tempo-define "adoc-bulleted-list-item-3" '(bol (adoc-insert-indented "*** " 3) (r "text" text)))
214+
(adoc-tempo-define "adoc-bulleted-list-item-4" '(bol (adoc-insert-indented "**** " 4) (r "text" text)))
215+
(adoc-tempo-define "adoc-bulleted-list-item-5" '(bol (adoc-insert-indented "***** " 5) (r "text" text)))
216+
(adoc-tempo-define "adoc-numbered-list-item" '(bol (p "number" number) ". " (r "text" text)))
217+
(adoc-tempo-define "adoc-numbered-list-item-roman" '(bol (p "number" number) ") " (r "text" text)))
218+
(adoc-tempo-define "adoc-implicit-numbered-list-item-1" '(bol (adoc-insert-indented ". " 1) (r "text" text)))
219+
(adoc-tempo-define "adoc-implicit-numbered-list-item-2" '(bol (adoc-insert-indented ".. " 2) (r "text" text)))
220+
(adoc-tempo-define "adoc-implicit-numbered-list-item-3" '(bol (adoc-insert-indented "... " 3) (r "text" text)))
221+
(adoc-tempo-define "adoc-implicit-numbered-list-item-4" '(bol (adoc-insert-indented ".... " 4) (r "text" text)))
222+
(adoc-tempo-define "adoc-implicit-numbered-list-item-5" '(bol (adoc-insert-indented "..... " 5) (r "text" text)))
223+
(adoc-tempo-define "adoc-labeled-list-item" '(bol (p "label" label) ":: " (r "text" text)))
224+
(adoc-tempo-define "adoc-list-item-continuation" '(bol "+" %) nil (bound-and-true-p adoc-help-list-item-continuation))
225+
226+
;; tables
227+
(adoc-tempo-define "adoc-example-table"
228+
'(bol "|====================\n"
229+
"| cell 11 | cell 12\n"
230+
"| cell 21 | cell 22\n"
231+
"|====================\n" % ))
232+
233+
;; Macros (inline & block)
234+
(adoc-tempo-define "adoc-url" '("http://foo.com") nil (bound-and-true-p adoc-help-url))
235+
(adoc-tempo-define "adoc-url-caption" '("http://foo.com[" (r "caption" caption) "]") nil (bound-and-true-p adoc-help-url))
236+
(adoc-tempo-define "adoc-email" '("bob@foo.com") nil (bound-and-true-p adoc-help-url))
237+
(adoc-tempo-define "adoc-email-caption" '("mailto:" (p "address" address) "[" (r "caption" caption) "]") nil (bound-and-true-p adoc-help-url))
238+
(adoc-tempo-define "adoc-anchor" '("[[" (r "id" id) "]]") nil (bound-and-true-p adoc-help-anchor))
239+
(adoc-tempo-define "adoc-anchor-default-syntax" '("anchor:" (r "id" id) "[" (p "xreflabel" xreflabel) "]") nil (bound-and-true-p adoc-help-anchor))
240+
(adoc-tempo-define "adoc-xref" '("<<" (p "id" id) "," (r "caption" caption) ">>") nil (bound-and-true-p adoc-help-xref))
241+
(adoc-tempo-define "adoc-xref-default-syntax" '("xref:" (p "id" id) "[" (r "caption" caption) "]") nil (bound-and-true-p adoc-help-xref))
242+
(adoc-tempo-define "adoc-image" '("image:" (r "target-path" target-path) "[" (p "caption" caption) "]"))
243+
244+
;; Passthrough
245+
(adoc-tempo-define "adoc-pass" '("pass:[" (r "text" text) "]") nil (bound-and-true-p adoc-help-pass))
246+
(adoc-tempo-define "adoc-asciimath" '("asciimath:[" (r "text" text) "]") nil (bound-and-true-p adoc-help-asciimath))
247+
(adoc-tempo-define "adoc-latexmath" '("latexmath:[" (r "text" text) "]") nil (bound-and-true-p adoc-help-latexmath))
248+
(adoc-tempo-define "adoc-pass-+++" '("+++" (r "text" text) "+++") nil (bound-and-true-p adoc-help-pass-+++))
249+
(adoc-tempo-define "adoc-pass-$$" '("$$" (r "text" text) "$$") nil (bound-and-true-p adoc-help-pass-$$))
250+
; backticks handled in tempo-template-adoc-monospace-literal
251+
252+
;;;; tempo handlers
253+
254+
(defun adoc-tempo-handler (element)
255+
"Tempo user element handler, see `tempo-user-elements'."
256+
(let ((on-region (adoc-tempo-on-region)))
257+
(cond
258+
259+
;; tr / tempo-recurse : tempo-insert the remaining args of the list
260+
((and (listp element)
261+
(memq (car element) '(tr tempo-recurse)))
262+
(mapc (lambda (elt) (tempo-insert elt on-region)) (cdr element))
263+
"")
264+
265+
;; bol: ensure point is at the beginning of a line by inserting a newline if needed
266+
((eq element 'bol)
267+
(if (bolp) "" "\n"))
268+
269+
;; r-or-n
270+
((eq element 'r-or-n)
271+
(if on-region 'r '(tr p n)))
272+
;; (r-or-n ...)
273+
((and (consp element)
274+
(eq (car element) 'r-or-n))
275+
(if on-region (cons 'r (cdr element)) '(tr p n))))))
276+
277+
(add-to-list 'tempo-user-elements 'adoc-tempo-handler)
278+
279+
(defun adoc-tempo-on-region ()
280+
"Guesses the on-region argument `tempo-insert' is given.
281+
282+
Is a workaround the problem that tempo's user handlers don't get
283+
passed the on-region argument."
284+
(let* (;; try to determine the arg with which the tempo-template-xxx was
285+
;; called that eventually brought us here. If we came here not by an
286+
;; interactive call to tempo-template-xxx we can't have a clue - assume
287+
;; nil.
288+
(arg (if (string-match "^tempo-template-" (symbol-name this-command))
289+
current-prefix-arg
290+
nil))
291+
;; copy from tempo-define-template
292+
(on-region (if tempo-insert-region
293+
(not arg)
294+
arg)))
295+
(when (region-active-p)
296+
(setq on-region t))
297+
on-region))
298+
299+
(provide 'adoc-mode-tempo)
300+
301+
;; Local Variables:
302+
;; indent-tabs-mode: nil
303+
;; coding: utf-8
304+
;; End:
305+
;;; adoc-mode-tempo.el ends here

0 commit comments

Comments
 (0)