Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Prompt with default answer

118 views
Skip to first unread message

drlat

unread,
Jun 15, 2012, 3:04:35 AM6/15/12
to

(defun prompt-read (prompt)
(format *query-io* "~a? " prompt)
(force-output *query-io*)
(read-line *query-io*))

I'm needing a Common Lisp function or macro similar to the function
above from Practical Common Lisp, but with an addition argument that
provides a default answer to the prompt, and can be called like this:

(prompt-read-default "What is your mood today" "I'm happy.")

and will yield:

What is your mood today? I'm happy.

allowing the default string "I'm happy." to be edited before hitting
<Enter>. It seems to me that the read-line function will have to be
modified or replaced to make this work, but so far none of my
experiments have been successful.

I've looked for a solution in CLHS, Practical Common Lisp, Successful
Lisp, ANSI Common Lisp, Basic Lisp Techniques, Common Lisp: the Language
(2nd ed.), COMMON LISP: a Gentle Introduction to Symbolic Computation,
and searched with Google. If the solution is there, then I've missed it.
I just need a simple working example. I would rather it not be a GUI
solution. Any help will be deeply appreciated.

--
drlat

Pascal J. Bourguignon

unread,
Jun 15, 2012, 4:45:19 AM6/15/12
to
The keyword in your question is "EDITED".

So you need an editor.

Happily, the CL standard provides a standard API to call an editor:
CL:ED. So you can write:

(defvar *edit-function* 'cl:ed)

(defun edit (file)
(funcall *edit-function* file))

(defun edit-text (text)
(let ((path "/tmp/edit.text"))
(setf (com.informatimago.common-lisp.cesarum.file:text-file-contents path :if-exists :supersede) text)
(edit path)
(com.informatimago.common-lisp.cesarum.file:text-file-contents path)))

(edit-text "Joones")
;; then edit with the invoked editor the text (eg. remove an 'o'),
;; and when you complete the edition, the function returns:
"Jones
"

Unfortunately, implementations may provide no editor, and may signal an
error such as "This implementation doesn't provide a resident editor."
when you call (cl:ed).

Happily, there are several other editors you can use, such as:


(com.informatimago.common-lisp.ed.ed:ed) ; a ed(1) clone.
;; Unfortunately, it regexps are not integrated yet with it, but you
;; could quickly integrate cl-ppcre to make it useable.


portable-hemlock available thru quicklisp:
(ql:quicklisp :hemlock)
(cl-user:hemlock)
;; Unfortunately, it doesn't run on all implementations, it requires
;; X11, and would need a lot of love.

ccl on MacOSX has an nicely integrated and working hemlock.

clisp has a hook so that you can cl:ed can invoke emacsclient (or any
other editor).
#+clisp (setf custom:*editor* "emacsclient")


Finally, you may invoke your favorite editor with asdf:run-shell-command
(or some other run-program function):

(defvar *editor* "emacsclient")

(defun edit (file)
(asdf:run-shell-command "~S ~S" *editor* file))

(defun edit-text (text)
(let ((path "/tmp/edit.text"))
(setf (com.informatimago.common-lisp.cesarum.file:text-file-contents path :if-exists :supersede) text)
(edit path)
(com.informatimago.common-lisp.cesarum.file:text-file-contents path)))

(edit-text "Joones")
;; Edit.
"John"



Now, if you more precisely want a line editor for the input buffer,
while it's possible with unix terminal drivers to fill the input buffer
with default input, and they implement some editing features (more or
less sophisticated), this is not easily accessible with CL (a POSIX API
for CL is not standardized, much less implemented, and anyways, this
requires unix or even linux specific features). You could still do it
with FFI (and perhaps using a library such as iolib), without using CL
I/O at all.

Alternatively, you could do something in conforming CL, or using
implementation specific extensions. In the later case, there is for
example the SCREEN and KEYBOARD packages in clisp that provide you with
a simple lispy interface over ncurse and raw tty, which would let you
implement a line editor.

Conformingly, you could do something that would not be necessarily as
pretty, but that could be as effective, using LISTEN and
READ-CHAR-NO-HANG (but line discipline will probably remain cooked and
buffered, so you have to take that into account; probably, an ed(1) like
editing feature would be the best).

Or you may dealing with a specific terminal and the escape sequences it
takes, or be compatible with a wider range of terminals using eg. the
terminfo system. (ql:quickload :terminfo)


Finally, instead of trying to include an editor in your application, you
may want to consider doing the reverse, ie. to include your application
into an editor, such as emacs. This is a nice architectural choice, to
use emacs to implement the user interface of a mostly textual
application, be it an application implemented in emacs lisp or not:
emacs is often used as a front-end to random applications, such as
theorem provers, programming language REPLs, web browsers, debuggers,
plotters, TeX processors, etc.


In the case of a CL program, if you run it from a CL implementation
connected to emacs with slime/swank, you can communicate with the slime
REPL thru slime/swank "RPC".

http://paste.lisp.org/display/22414
http://paste.lisp.org/display/127219

So you could write something like

(defun prompt-read (prompt default)
(format *query-io* "~a? " prompt)
(force-output *query-io*)
(eval-in-emacs `(with-current-buffer (slime-repl-buffer)
(do-something-with-slime-internals-to-insert-in-input-buffer ,default)))
(read-line *query-io*))

But to implement
do-something-with-slime-internals-to-insert-in-input-buffer
you need to be a slime hacker.

--
__Pascal Bourguignon__ http://www.informatimago.com/
A bad day in () is better than a good day in {}.

drlat

unread,
Jun 15, 2012, 9:48:24 AM6/15/12
to
Hi Pascal,
Many thanks! This took a good deal of your time. I owe you! Thanks to
you, I was able to get an editor working several ways. Then to get the
prompt function to work, I used with-input-from-string as follows:

(defun prompt-read-default (prompt default)
(format *query-io* "~a: " prompt)
(force-output *query-io*)
(with-input-from-string (str (edit-text default))
(read-line str)))

I have one more question for the sake of my education: what editor is
being used with (read-line *query-io*), and why isn't it possible to use
that editor for my prompt function?

--
drlat


Pascal J. Bourguignon

unread,
Jun 15, 2012, 1:10:03 PM6/15/12
to
drlat <l...@dayspringpublisher.com> writes:

> Many thanks! This took a good deal of your time. I owe you! Thanks to
> you, I was able to get an editor working several ways. Then to get the
> prompt function to work, I used with-input-from-string as follows:
>
> (defun prompt-read-default (prompt default)
> (format *query-io* "~a: " prompt)
> (force-output *query-io*)
> (with-input-from-string (str (edit-text default))
> (read-line str)))
>

I forgot two other options:

Using the readline (GPL) or lineedit libraries. But they only work on
just a few implementations.

http://common-lisp.net/project/linedit/
http://www.cliki.net/admin/search?words=readline

> I have one more question for the sake of my education: what editor is
> being used with (read-line *query-io*), and why isn't it possible to use
> that editor for my prompt function?

You have basically the cooked mode of the unix terminal driver.
Type:

stty -a

at a shell prompt to see what editing command there are.

But some implementation provide further features. For example, clisp
includes readline built-in, so you have the full readline editing
features when you use clisp in a terminal.

When you're running in emacs comint (eg. M-x shell), or slime, then it's
emacs that's providing the editing, and you need to evaluate emacs
expressions to fill the buffer (in a way that it's considered by the
mode as input).

But in all case, the problem is that it's wrapped over by the CL I/O
functions, (read-line, read-char, etc), and that there's no API to fill
the input buffer with your default input. You could use implementation
dependent functions to retrieve the file descriptor underlying
*query-io*, and then use CFFI to call the unix functions to fill the
input buffer (or the readline function, etc), and rely on those terminal
input editing features.

drlat

unread,
Jun 16, 2012, 10:31:10 AM6/16/12
to
Very interesting. Thank you! You've taught me a lot, and I appreciate it.

--
drlat


0 new messages