readline.scm 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  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. (define-module (ice-9 readline)
  25. #:use-module (ice-9 session)
  26. #:use-module (ice-9 regex)
  27. #:use-module (ice-9 buffered-input)
  28. #:no-backtrace
  29. #:export (filename-completion-function
  30. add-history
  31. read-history
  32. write-history
  33. clear-history))
  34. ;;; Dynamically link the glue code for accessing the readline library,
  35. ;;; but only when it isn't already present.
  36. (if (not (provided? 'readline))
  37. (load-extension "guile-readline" "scm_init_readline"))
  38. (if (not (provided? 'readline))
  39. (scm-error 'misc-error
  40. #f
  41. "readline is not provided in this Guile installation"
  42. '()
  43. '()))
  44. ;;; Run-time options
  45. (export
  46. readline-options
  47. readline-enable
  48. readline-disable)
  49. (export-syntax
  50. readline-set!)
  51. (define-option-interface
  52. (readline-options-interface
  53. (readline-options readline-enable readline-disable)
  54. (readline-set!)))
  55. ;;; MDJ 980513 <djurfeldt@nada.kth.se>:
  56. ;;; There should probably be low-level support instead of this code.
  57. ;;; Dirk:FIXME:: If the-readline-port, input-port or output-port are closed,
  58. ;;; guile will enter an endless loop or crash.
  59. (define-once new-input-prompt "")
  60. (define-once continuation-prompt "")
  61. (define-once input-port (current-input-port))
  62. (define-once output-port (current-output-port))
  63. (define-once read-hook #f)
  64. (define (make-readline-port)
  65. (let ((history-buffer #f))
  66. (make-line-buffered-input-port (lambda (continuation?)
  67. ;; When starting a new read, add
  68. ;; the previously read expression
  69. ;; to the history.
  70. (if (and (not continuation?)
  71. history-buffer)
  72. (begin
  73. (add-history history-buffer)
  74. (set! history-buffer #f)))
  75. ;; Set up prompts and read a line.
  76. (let* ((prompt (if continuation?
  77. continuation-prompt
  78. new-input-prompt))
  79. (str (%readline (if (string? prompt)
  80. prompt
  81. (prompt))
  82. input-port
  83. output-port
  84. read-hook)))
  85. (or (eof-object? str)
  86. (string=? str "")
  87. (set! history-buffer
  88. (if history-buffer
  89. (string-append history-buffer
  90. "\n"
  91. str)
  92. str)))
  93. str)))))
  94. ;;; We only create one readline port. There's no point in having
  95. ;;; more, since they would all share the tty and history ---
  96. ;;; everything except the prompt. And don't forget the
  97. ;;; compile/load/run phase distinctions. Also, the readline library
  98. ;;; isn't reentrant.
  99. (define-once the-readline-port #f)
  100. (define-once history-variable "GUILE_HISTORY")
  101. (define-once history-file
  102. (string-append (or (getenv "HOME") ".") "/.guile_history"))
  103. (define-public readline-port
  104. (let ((do (lambda (r/w)
  105. (if (memq 'history-file (readline-options-interface))
  106. (r/w (or (getenv history-variable)
  107. history-file))))))
  108. (lambda ()
  109. (if (not the-readline-port)
  110. (begin
  111. (do read-history)
  112. (set! the-readline-port (make-readline-port))
  113. (add-hook! exit-hook (lambda ()
  114. (do write-history)
  115. (clear-history)))))
  116. the-readline-port)))
  117. ;;; The user might try to use readline in his programs. It then
  118. ;;; becomes very uncomfortable that the current-input-port is the
  119. ;;; readline port...
  120. ;;;
  121. ;;; Here, we detect this situation and replace it with the
  122. ;;; underlying port.
  123. ;;;
  124. ;;; %readline is the low-level readline procedure.
  125. (define-public (readline . args)
  126. (let ((prompt new-input-prompt)
  127. (inp input-port))
  128. (cond ((not (null? args))
  129. (set! prompt (car args))
  130. (set! args (cdr args))
  131. (cond ((not (null? args))
  132. (set! inp (car args))
  133. (set! args (cdr args))))))
  134. (apply %readline
  135. prompt
  136. (if (eq? inp the-readline-port)
  137. input-port
  138. inp)
  139. args)))
  140. (define-public (set-readline-prompt! p . rest)
  141. (set! new-input-prompt p)
  142. (if (not (null? rest))
  143. (set! continuation-prompt (car rest))))
  144. (define-public (set-readline-input-port! p)
  145. (cond ((or (not (file-port? p)) (not (input-port? p)))
  146. (scm-error 'wrong-type-arg "set-readline-input-port!"
  147. "Not a file input port: ~S" (list p) #f))
  148. ((port-closed? p)
  149. (scm-error 'misc-error "set-readline-input-port!"
  150. "Port not open: ~S" (list p) #f))
  151. (else
  152. (set! input-port p))))
  153. (define-public (set-readline-output-port! p)
  154. (cond ((or (not (file-port? p)) (not (output-port? p)))
  155. (scm-error 'wrong-type-arg "set-readline-input-port!"
  156. "Not a file output port: ~S" (list p) #f))
  157. ((port-closed? p)
  158. (scm-error 'misc-error "set-readline-output-port!"
  159. "Port not open: ~S" (list p) #f))
  160. (else
  161. (set! output-port p))))
  162. (define-public (set-readline-read-hook! h)
  163. (set! read-hook h))
  164. (define-public apropos-completion-function
  165. (let ((completions '()))
  166. (lambda (text cont?)
  167. (if (not cont?)
  168. (set! completions
  169. (map symbol->string
  170. (apropos-internal
  171. (string-append "^" (regexp-quote text))))))
  172. (if (null? completions)
  173. #f
  174. (let ((retval (car completions)))
  175. (begin (set! completions (cdr completions))
  176. retval))))))
  177. (if (provided? 'regex)
  178. (set! *readline-completion-function* apropos-completion-function))
  179. (define-public (with-readline-completion-function completer thunk)
  180. "With @var{completer} as readline completion function, call @var{thunk}."
  181. (let ((old-completer *readline-completion-function*))
  182. (dynamic-wind
  183. (lambda ()
  184. (set! *readline-completion-function* completer))
  185. thunk
  186. (lambda ()
  187. (set! *readline-completion-function* old-completer)))))
  188. (define-once readline-repl-reader
  189. (let ((boot-9-repl-reader repl-reader))
  190. (lambda* (repl-prompt #:optional (reader (fluid-ref current-reader)))
  191. (let ((port (current-input-port)))
  192. (if (eq? port (readline-port))
  193. (let ((outer-new-input-prompt new-input-prompt)
  194. (outer-continuation-prompt continuation-prompt)
  195. (outer-read-hook read-hook))
  196. (dynamic-wind
  197. (lambda ()
  198. (set-buffered-input-continuation?! port #f)
  199. (set-readline-prompt! repl-prompt "... ")
  200. (set-readline-read-hook! (lambda ()
  201. (run-hook before-read-hook))))
  202. (lambda () ((or reader read) port))
  203. (lambda ()
  204. (set-readline-prompt! outer-new-input-prompt
  205. outer-continuation-prompt)
  206. (set-readline-read-hook! outer-read-hook))))
  207. (boot-9-repl-reader repl-prompt reader))))))
  208. (define-public (activate-readline)
  209. (if (isatty? (current-input-port))
  210. (begin
  211. (set-current-input-port (readline-port))
  212. (set! repl-reader readline-repl-reader)
  213. (set! (using-readline?) #t))))
  214. (define-public (make-completion-function strings)
  215. "Construct and return a completion function for a list of strings.
  216. The returned function is suitable for passing to
  217. @code{with-readline-completion-function. The argument @var{strings}
  218. should be a list of strings, where each string is one of the possible
  219. completions."
  220. (letrec ((strs '())
  221. (regexp #f)
  222. (completer (lambda (text continue?)
  223. (if continue?
  224. (if (null? strs)
  225. #f
  226. (let ((str (car strs)))
  227. (set! strs (cdr strs))
  228. (if (string-match regexp str)
  229. str
  230. (completer text #t))))
  231. (begin
  232. (set! strs strings)
  233. (set! regexp
  234. (string-append "^" (regexp-quote text)))
  235. (completer text #t))))))
  236. completer))