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 {}.