Merge commit '01a301d1b606b84d986b735049e7155d2f4cd6aa'
[bpt/guile.git] / guile-readline / ice-9 / readline.scm
1 ;;;; readline.scm --- support functions for command-line editing
2 ;;;;
3 ;;;; Copyright (C) 1997, 1999, 2000, 2001, 2002, 2006, 2009, 2010, 2011,
4 ;;;; 2013, 2014 Free Software Foundation, Inc.
5 ;;;;
6 ;;;; This program is free software; you can redistribute it and/or modify
7 ;;;; it under the terms of the GNU General Public License as published by
8 ;;;; the Free Software Foundation; either version 3, or (at your option)
9 ;;;; any later version.
10 ;;;;
11 ;;;; This program is distributed in the hope that it will be useful,
12 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ;;;; GNU General Public License for more details.
15 ;;;;
16 ;;;; You should have received a copy of the GNU General Public License
17 ;;;; along with this software; see the file COPYING. If not, write to
18 ;;;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 ;;;; Boston, MA 02110-1301 USA
20 ;;;;
21 ;;;; Contributed by Daniel Risacher <risacher@worldnet.att.net>.
22 ;;;; Extensions based upon code by
23 ;;;; Andrew Archibald <aarchiba@undergrad.math.uwaterloo.ca>.
24
25 \f
26
27 (define-module (ice-9 readline)
28 #:use-module (ice-9 session)
29 #:use-module (ice-9 regex)
30 #:use-module (ice-9 buffered-input)
31 #:no-backtrace
32 #:export (filename-completion-function
33 add-history
34 read-history
35 write-history
36 clear-history))
37
38 \f
39
40 ;;; Dynamically link the glue code for accessing the readline library,
41 ;;; but only when it isn't already present.
42
43 (if (not (provided? 'readline))
44 (load-extension "guile-readline" "scm_init_readline"))
45
46 (if (not (provided? 'readline))
47 (scm-error 'misc-error
48 #f
49 "readline is not provided in this Guile installation"
50 '()
51 '()))
52
53 \f
54
55 ;;; Run-time options
56
57 (export
58 readline-options
59 readline-enable
60 readline-disable)
61 (export-syntax
62 readline-set!)
63
64 (define-option-interface
65 (readline-options-interface
66 (readline-options readline-enable readline-disable)
67 (readline-set!)))
68
69 \f
70
71 ;;; MDJ 980513 <djurfeldt@nada.kth.se>:
72 ;;; There should probably be low-level support instead of this code.
73
74 ;;; Dirk:FIXME:: If the-readline-port, input-port or output-port are closed,
75 ;;; guile will enter an endless loop or crash.
76
77 (define-once new-input-prompt "")
78 (define-once continuation-prompt "")
79 (define-once input-port (current-input-port))
80 (define-once output-port (current-output-port))
81 (define-once read-hook #f)
82
83 (define (make-readline-port)
84 (let ((history-buffer #f))
85 (make-line-buffered-input-port (lambda (continuation?)
86 ;; When starting a new read, add
87 ;; the previously read expression
88 ;; to the history.
89 (if (and (not continuation?)
90 history-buffer)
91 (begin
92 (add-history history-buffer)
93 (set! history-buffer #f)))
94 ;; Set up prompts and read a line.
95 (let* ((prompt (if continuation?
96 continuation-prompt
97 new-input-prompt))
98 (str (%readline (if (string? prompt)
99 prompt
100 (prompt))
101 input-port
102 output-port
103 read-hook)))
104 (or (eof-object? str)
105 (string=? str "")
106 (set! history-buffer
107 (if history-buffer
108 (string-append history-buffer
109 "\n"
110 str)
111 str)))
112 str)))))
113
114 ;;; We only create one readline port. There's no point in having
115 ;;; more, since they would all share the tty and history ---
116 ;;; everything except the prompt. And don't forget the
117 ;;; compile/load/run phase distinctions. Also, the readline library
118 ;;; isn't reentrant.
119 (define-once the-readline-port #f)
120
121 (define-once history-variable "GUILE_HISTORY")
122 (define-once history-file
123 (string-append (or (getenv "HOME") ".") "/.guile_history"))
124
125 (define-public readline-port
126 (let ((do (lambda (r/w)
127 (if (memq 'history-file (readline-options-interface))
128 (r/w (or (getenv history-variable)
129 history-file))))))
130 (lambda ()
131 (if (not the-readline-port)
132 (begin
133 (do read-history)
134 (set! the-readline-port (make-readline-port))
135 (add-hook! exit-hook (lambda ()
136 (do write-history)
137 (clear-history)))))
138 the-readline-port)))
139
140 ;;; The user might try to use readline in his programs. It then
141 ;;; becomes very uncomfortable that the current-input-port is the
142 ;;; readline port...
143 ;;;
144 ;;; Here, we detect this situation and replace it with the
145 ;;; underlying port.
146 ;;;
147 ;;; %readline is the low-level readline procedure.
148
149 (define-public (readline . args)
150 (let ((prompt new-input-prompt)
151 (inp input-port))
152 (cond ((not (null? args))
153 (set! prompt (car args))
154 (set! args (cdr args))
155 (cond ((not (null? args))
156 (set! inp (car args))
157 (set! args (cdr args))))))
158 (apply %readline
159 prompt
160 (if (eq? inp the-readline-port)
161 input-port
162 inp)
163 args)))
164
165 (define-public (set-readline-prompt! p . rest)
166 (set! new-input-prompt p)
167 (if (not (null? rest))
168 (set! continuation-prompt (car rest))))
169
170 (define-public (set-readline-input-port! p)
171 (cond ((or (not (file-port? p)) (not (input-port? p)))
172 (scm-error 'wrong-type-arg "set-readline-input-port!"
173 "Not a file input port: ~S" (list p) #f))
174 ((port-closed? p)
175 (scm-error 'misc-error "set-readline-input-port!"
176 "Port not open: ~S" (list p) #f))
177 (else
178 (set! input-port p))))
179
180 (define-public (set-readline-output-port! p)
181 (cond ((or (not (file-port? p)) (not (output-port? p)))
182 (scm-error 'wrong-type-arg "set-readline-input-port!"
183 "Not a file output port: ~S" (list p) #f))
184 ((port-closed? p)
185 (scm-error 'misc-error "set-readline-output-port!"
186 "Port not open: ~S" (list p) #f))
187 (else
188 (set! output-port p))))
189
190 (define-public (set-readline-read-hook! h)
191 (set! read-hook h))
192
193 (define-public apropos-completion-function
194 (let ((completions '()))
195 (lambda (text cont?)
196 (if (not cont?)
197 (set! completions
198 (map symbol->string
199 (apropos-internal
200 (string-append "^" (regexp-quote text))))))
201 (if (null? completions)
202 #f
203 (let ((retval (car completions)))
204 (begin (set! completions (cdr completions))
205 retval))))))
206
207 (if (provided? 'regex)
208 (set! *readline-completion-function* apropos-completion-function))
209
210 (define-public (with-readline-completion-function completer thunk)
211 "With @var{completer} as readline completion function, call @var{thunk}."
212 (let ((old-completer *readline-completion-function*))
213 (dynamic-wind
214 (lambda ()
215 (set! *readline-completion-function* completer))
216 thunk
217 (lambda ()
218 (set! *readline-completion-function* old-completer)))))
219
220 (define-once readline-repl-reader
221 (let ((boot-9-repl-reader repl-reader))
222 (lambda* (repl-prompt #:optional (reader (fluid-ref current-reader)))
223 (let ((port (current-input-port)))
224 (if (eq? port (readline-port))
225 (let ((outer-new-input-prompt new-input-prompt)
226 (outer-continuation-prompt continuation-prompt)
227 (outer-read-hook read-hook))
228 (dynamic-wind
229 (lambda ()
230 (set-buffered-input-continuation?! port #f)
231 (set-readline-prompt! repl-prompt "... ")
232 (set-readline-read-hook! (lambda ()
233 (run-hook before-read-hook))))
234 (lambda () ((or reader read) port))
235 (lambda ()
236 (set-readline-prompt! outer-new-input-prompt
237 outer-continuation-prompt)
238 (set-readline-read-hook! outer-read-hook))))
239 (boot-9-repl-reader repl-prompt reader))))))
240
241 (define-public (activate-readline)
242 (if (isatty? (current-input-port))
243 (begin
244 (set-current-input-port (readline-port))
245 (set! repl-reader readline-repl-reader)
246 (set! (using-readline?) #t))))
247
248 (define-public (make-completion-function strings)
249 "Construct and return a completion function for a list of strings.
250 The returned function is suitable for passing to
251 @code{with-readline-completion-function. The argument @var{strings}
252 should be a list of strings, where each string is one of the possible
253 completions."
254 (letrec ((strs '())
255 (regexp #f)
256 (completer (lambda (text continue?)
257 (if continue?
258 (if (null? strs)
259 #f
260 (let ((str (car strs)))
261 (set! strs (cdr strs))
262 (if (string-match regexp str)
263 str
264 (completer text #t))))
265 (begin
266 (set! strs strings)
267 (set! regexp
268 (string-append "^" (regexp-quote text)))
269 (completer text #t))))))
270 completer))