2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4 ;; MODULE : tm-dialogue.scm
5 ;; DESCRIPTION : Interactive dialogues between Scheme and C++
6 ;; COPYRIGHT : (C) 1999 Joris van der Hoeven
8 ;; This software falls under the GNU general public license version 3 or later.
9 ;; It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
10 ;; in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
12 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
14 (texmacs-module (kernel texmacs tm-dialogue)
15 (:use (kernel texmacs tm-define)))
17 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
19 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
21 (with-module texmacs-user ;; switch modules for old versions of Guile
22 (define-public dialogue-break #f)
23 (define-public dialogue-return #f)
24 (define-public dialogue-error #f))
26 (define-public (dialogue-report-errors)
28 (with error dialogue-error
29 (set! dialogue-error #f)
30 (apply throw error))))
32 (define-public-macro (dialogue . body)
38 (exec-delayed (lambda () (dialogue ,@body)))
39 (dialogue-return (noop))))
43 (set! dialogue-break cont)
46 (lambda err (set! dialogue-error err)))
47 (set! dialogue-break #f))
48 (if dialogue-return (dialogue-return (noop)))
49 (dialogue-report-errors)))))
51 (define-public ((dialogue-machine local-continue) result)
53 (set! dialogue-return cont)
54 (local-continue result))
55 (set! dialogue-return #f)
56 (dialogue-report-errors))
58 (define-public-macro (dialogue-user local-continue . body)
59 `(with local-break dialogue-break
60 (set! dialogue-break #f)
61 (with r (with-cc ,local-continue
64 (set! dialogue-break local-break)
67 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
69 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
71 (define-public (dialogue-ask prompt)
73 (dialogue-user local-continue
74 (tm-interactive (dialogue-machine local-continue)
76 (list (build-interactive-arg prompt))
78 (texmacs-error "dialogue-ask" "Not in dialogue")))
81 (with lan (get-output-language)
82 (cond ((== lan "french") "oui")
83 ((in? lan '("dutch" "german")) "ja")
84 ((in? lan '("italian" "spanish")) "si")
88 (with lan (get-output-language)
89 (cond ((== lan "french") "non")
90 ((== lan "dutch") "nee")
91 ((== lan "german") "nein")
94 (define-public (dialogue-confirm? prompt default)
96 (yes? (dialogue-ask (list prompt "question" (yes) (no))))
97 (yes? (dialogue-ask (list prompt "question" (no) (yes))))))
99 (define-public (dialogue-url prompt type)
101 (dialogue-user local-continue
103 (choose-file (dialogue-machine local-continue) prompt type)))
104 (texmacs-error "dialogue-ask" "Not in dialogue")))
106 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
107 ;; Delayed execution of commands
108 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
110 (define-public (delayed-sub body)
111 (cond ((or (npair? body) (nlist? (car body)) (not (keyword? (caar body))))
112 `(lambda () ,@body #t))
113 ((== (caar body) :pause)
114 `(let* ((start (texmacs-time))
115 (proc ,(delayed-sub (cdr body))))
117 (with left (- (+ start ,(cadar body)) (texmacs-time))
120 (set! start (texmacs-time))
122 ((== (caar body) :every)
123 `(let* ((time (+ (texmacs-time) ,(cadar body)))
124 (proc ,(delayed-sub (cdr body))))
126 (with left (- time (texmacs-time))
129 (set! time (+ (texmacs-time) ,(cadar body)))
131 ((== (caar body) :idle)
132 `(with proc ,(delayed-sub (cdr body))
134 (with left (- ,(cadar body) (idle-time))
137 ((== (caar body) :refresh)
140 (proc ,(delayed-sub (cdr body))))
142 (if (!= ,sym (change-time)) 0
143 (with left (- ,(cadar body) (idle-time))
146 (set! ,sym (change-time))
148 ((== (caar body) :require)
149 `(with proc ,(delayed-sub (cdr body))
151 (if (not ,(cadar body)) 0
153 ((== (caar body) :while)
154 `(with proc ,(delayed-sub (cdr body))
156 (if (not ,(cadar body)) #t
158 ((== (caar body) :clean)
159 `(with proc ,(delayed-sub (cdr body))
162 (if (!= left #t) left
163 (begin ,(cadar body) #t))))))
164 ((== (caar body) :permanent)
165 `(with proc ,(delayed-sub (cdr body))
168 (if (!= left #t) left
169 (with next ,(cadar body)
172 ((== (caar body) :do)
173 `(with proc ,(delayed-sub (cdr body))
177 (else (delayed-sub (cdr body)))))
179 (define-public-macro (delayed . body)
181 `(dialogue-user local-continue
183 (with proc ,(delayed-sub body)
186 (if r ((dialogue-machine local-continue) (noop)))
188 `(exec-delayed-pause ,(delayed-sub body))))
190 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
191 ;; Messages and feedback on the status bar
192 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
194 (define-public message-serial 0)
196 (define-public (set-message-notify)
197 (set! message-serial (+ message-serial 1)))
199 (define-public (recall-message-after len)
200 (with current message-serial
203 (when (== message-serial current)
206 (define-public (set-temporary-message left right len)
207 (set-message-temp left right #t)
208 (recall-message-after len))
210 (define-public (texmacs-banner)
211 (with tmv (string-append "GNU TeXmacs " (texmacs-version))
213 (set-message "Welcome to GNU TeXmacs" tmv)
216 (set-message "GNU TeXmacs falls under the GNU general public license" tmv)
219 (set-message "GNU TeXmacs comes without any form of legal warranty" tmv)
223 "More information about GNU TeXmacs can be found in the Help->About menu"
227 (set-message "" ""))))))))
229 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
230 ;; Interactive commands
231 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
233 (define interactive-arg-table (make-ahash-table))
235 (define (list-but l1 l2)
236 (cond ((null? l1) l1)
237 ((in? (car l1) l2) (list-but (cdr l1) l2))
238 (else (cons (car l1) (list-but (cdr l1) l2)))))
241 (cond ((tree? x) (tree->stree x))
246 (define-public (procedure-symbol-name fun)
247 (cond ((symbol? fun) fun)
248 ((string? fun) (string->symbol fun))
249 ((and (procedure? fun) (procedure-name fun)) => identity)
252 (define-public (procedure-string-name fun)
253 (and-with name (procedure-symbol-name fun)
254 (symbol->string name)))
256 (define-public (learn-interactive fun assoc-t)
257 "Learn interactive values for @fun"
258 (set! assoc-t (map (lambda (x) (cons (car x) (as-stree (cdr x)))) assoc-t))
259 (set! fun (procedure-symbol-name fun))
261 (let* ((l1 (or (ahash-ref interactive-arg-table fun) '()))
262 (l2 (cons assoc-t (list-but l1 (list assoc-t)))))
263 (ahash-set! interactive-arg-table fun l2))))
265 (define-public (learned-interactive fun)
266 "Return learned list of interactive values for @fun"
267 (set! fun (procedure-symbol-name fun))
268 (or (ahash-ref interactive-arg-table fun) '()))
270 (define (learned-interactive-arg fun nr)
271 (let* ((l (learned-interactive fun))
272 (arg (number->string nr))
273 (extract (lambda (assoc-l) (assoc-ref assoc-l arg))))
276 (define (compute-interactive-arg-text fun which)
277 (with arg (property fun (list :argument which))
278 (cond ((npair? arg) (upcase-first (symbol->string which)))
279 ((and (string? (car arg)) (null? (cdr arg))) (car arg))
280 ((string? (cadr arg)) (cadr arg))
281 (else (upcase-first (symbol->string which))))))
283 (define (compute-interactive-arg-type fun which)
284 (with arg (property fun (list :argument which))
285 (cond ((or (npair? arg) (npair? (cdr arg))) "string")
286 ((string? (car arg)) (car arg))
287 ((symbol? (car arg)) (symbol->string (car arg)))
290 (define (compute-interactive-arg-proposals fun which)
291 (let* ((default (property fun (list :default which)))
292 (proposals (property fun (list :proposals which)))
294 (cond ((procedure? default) (list (default)))
295 ((procedure? proposals) (proposals))
298 (define (compute-interactive-arg fun which)
299 (cons (compute-interactive-arg-text fun which)
300 (cons (compute-interactive-arg-type fun which)
301 (compute-interactive-arg-proposals fun which))))
303 (define (compute-interactive-args-try-hard fun)
304 (with src (procedure-source fun)
305 (if (and (pair? src) (== (car src) 'lambda)
306 (pair? (cdr src)) (list? (cadr src)))
307 (map upcase-first (map symbol->string (cadr src)))
310 (tm-define (compute-interactive-args fun)
311 (with args (property fun :arguments)
313 (compute-interactive-args-try-hard fun)
314 (map (lambda (which) (compute-interactive-arg fun which)) args))))
316 (define (build-interactive-arg s)
317 (cond ((string-ends? s ":") s)
318 ((string-ends? s "?") s)
319 (else (string-append s ":"))))
321 (tm-define (build-interactive-args fun l nr learned?)
324 (build-interactive-args
325 fun (cons (list (car l) "string") (cdr l)) nr learned?))
327 (let* ((name (build-interactive-arg (caar l)))
331 ;;(ql (if (null? pl) '("") pl))
332 (ll (if learned? (learned-interactive-arg fun nr) '()))
333 (rl (append ql (list-but ll ql)))
334 (props (if (<= (length ql) 1) rl ql)))
335 (cons (cons name (cons type props))
336 (build-interactive-args fun (cdr l) (+ nr 1) learned?))))))
338 (tm-define (interactive fun . args)
339 (:synopsis "Call @fun with interactively specified arguments @args")
341 (lazy-define-force fun)
342 (if (null? args) (set! args (compute-interactive-args fun)))
343 (with fun-args (build-interactive-args fun args 0 #t)
345 (dialogue-user local-continue
348 (with r* (apply fun args*)
349 ((dialogue-machine local-continue) r*)
352 (tm-interactive fun fun-args))))
354 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
355 ;; Store learned arguments from one session to another
356 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
358 (define (save-learned)
359 (with l (ahash-table->list interactive-arg-table)
360 (save-object "$TEXMACS_HOME_PATH/system/interactive.scm" l)))
362 (define (ahash-set-2! t x)
365 (with a (or (ahash-ref t form) '())
366 (set! a (assoc-set! a arg l))
367 (ahash-set! t form a)))))
369 (define (rearrange-old x)
371 (let* ((len (apply min (map length l)))
372 (truncl (map (cut sublist <> 0 len) l))
373 (sl (sort truncl (lambda (l1 l2) (< (car l1) (car l2)))))
374 (nl (map (lambda (x) (cons (number->string (car x)) (cdr x))) sl))
375 (build (lambda args (map cons (map car nl) args)))
376 (r (apply map (cons build (map cdr nl)))))
379 (define (decode-old l)
380 (let* ((t (make-ahash-table))
381 (setter (cut ahash-set-2! t <>)))
383 (let* ((r (ahash-table->list t))
384 (m (map rearrange-old r)))
385 (list->ahash-table m))))
387 (define (retrieve-learned)
388 (if (url-exists? "$TEXMACS_HOME_PATH/system/interactive.scm")
389 (let* ((l (load-object "$TEXMACS_HOME_PATH/system/interactive.scm"))
390 (old? (and (pair? l) (pair? (car l)) (list-2? (caar l))))
391 (decode (if old? decode-old list->ahash-table)))
392 (set! interactive-arg-table (decode l)))))
394 (on-entry (retrieve-learned))
395 (on-exit (save-learned))