execute incomplete commands when theres only one completion

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

execute incomplete commands when theres only one completion

Nathan Shostek
Hello,

When Emacs is accepting input via M-x one can forego writing the whole command
due to the way emacs completes it. While I'm not skilled enough to implement
emacs style completion, I do think it would be nice to be able to complete
incomplete commands when there is only one possible completion. I'm putting this
here instead of in a github pull request because, in all truthfulness, github
forking and pull-requesting confuses me, and someone told me I could also
submit my contributions throught this mailing list.

I've modified the functions eval-command and call-interactively to have an
additional optional argument called auto-complete, which when true retries
completion before throwing an error. I'm not sure if this is the right way
to get the behavior I want, but it works. If theres a better way to do this
I'll gladly try to implement it.

Apologies for the length of this email; I figured it was better to include every
function than just the small bits i changed, but if its not let me know and I'll
do it differently in the future.

Cheers,
Nathan

Heres the code:

(defun call-interactively (command &optional (input "") auto-complete)
  "Parse the command's arguments from input given the command's
argument specifications then execute it. Returns a string or nil if
user aborted."
  (declare (type (or string symbol) command)
           (type (or string argument-line) input))
  ;; Catch parse errors
  (catch 'error
    (let* ((arg-line (if (stringp input)
                         (make-argument-line :string input
                                             :start 0)
                         input))
           (cmd-data (or (get-command-structure command)
                         (and auto-complete
                              (let ((comp (input-find-completions command (all-commands))))
                                (when (and comp (= 1 (length comp)))
                                  (get-command-structure (car comp)))))
                         (throw 'error (format nil "Command '~a' not found." command))))
           (arg-specs (command-args cmd-data))
           (args (loop for spec in arg-specs
                       collect (let* ((type (if (listp spec)
                                                (first spec)
                                                spec))
                                      (prompt (when (listp spec)
                                                (second spec)))
                                      (fn (gethash type *command-type-hash*)))
                                 (unless fn
                                   (throw 'error (format nil "Bad argument type: ~s" type)))
                                 ;; If the prompt is NIL then it's
                                 ;; considered an optional argument and
                                 ;; we shouldn't prompt for it if the
                                 ;; arg line is empty.
                                 (if (and (null prompt)
                                          (argument-line-end-p arg-line))
                                     (loop-finish)
                                     (funcall fn arg-line prompt))))))
      ;; Did the whole string get parsed?
      (unless (or (argument-line-end-p arg-line)
                  (position-if 'alphanumericp (argument-line-string arg-line) :start (argument-line-start arg-line)))
        (throw 'error (format nil "Trailing garbage: ~{~A~^ ~}" (subseq (argument-line-string arg-line)
                                                                        (argument-line-start arg-line)))))
      ;; Success
      (prog1
          (apply (command-name cmd-data) args)
        (setf *last-command* command)))))

(defun eval-command (cmd &optional interactivep auto-complete)
  "exec cmd and echo the result."
  (labels ((parse-and-run-command (input)
             (let* ((arg-line (make-argument-line :string input
                                                  :start 0))
                    (cmd (argument-pop arg-line)))
               (let ((*interactivep* interactivep))
                 (call-interactively cmd arg-line auto-complete)))))
    (multiple-value-bind (result error-p)
        ;; this fancy footwork lets us grab the backtrace from where the
        ;; error actually happened.
        (restart-case
            (handler-bind
                ((error (lambda (c)
                          (invoke-restart 'eval-command-error
                                          (format nil "^B^1*Error In Command '^b~a^B': ^n~A~a"
                                                  cmd c (if *show-command-backtrace*
                                                            (backtrace-string) ""))))))
              (parse-and-run-command cmd))
          (eval-command-error (err-text)
            :interactive (lambda () nil)
            (values err-text t)))
      ;; interactive commands update the modeline
      (update-all-mode-lines)
      (cond ((stringp result)
             (if error-p
                 (message-no-timeout "~a" result)
                 (message "~a" result)))
            ((eq result :abort)
             (unless *suppress-abort-messages*
               (message "Abort.")))))))

(defcommand colon (&optional initial-input) (:rest)
  "Read a command from the user. @var{initial-text} is optional. When
supplied, the text will appear in the prompt.

String arguments with spaces may be passed to the command by
delimiting them with double quotes. A backslash can be used to escape
double quotes or backslashes inside the string. This does not apply to
commands taking :REST or :SHELL type arguments."
  (let ((cmd (completing-read (current-screen) ": " (all-commands) :initial-input (or initial-input ""))))
    (unless cmd
      (throw 'error :abort))
    (when (plusp (length cmd))
      (eval-command cmd t t))))

Reply | Threaded
Open this post in threaded view
|

Re: execute incomplete commands when theres only one completion

Nathan Shostek
After digging further, I think the best place to make changes isnt inthese
functions, but rather in get-completion-preview-list. Does this sound correct?

Thanks,
Nathan s

Responding to:

> Hello,
>
> When Emacs is accepting input via M-x one can forego writing the whole command
> due to the way emacs completes it. While I'm not skilled enough to implement
> emacs style completion, I do think it would be nice to be able to complete
> incomplete commands when there is only one possible completion. I'm putting this
> here instead of in a github pull request because, in all truthfulness, github
> forking and pull-requesting confuses me, and someone told me I could also
> submit my contributions throught this mailing list.
>
> I've modified the functions eval-command and call-interactively to have an
> additional optional argument called auto-complete, which when true retries
> completion before throwing an error. I'm not sure if this is the right way
> to get the behavior I want, but it works. If theres a better way to do this
> I'll gladly try to implement it.
>
> Apologies for the length of this email; I figured it was better to include every
> function than just the small bits i changed, but if its not let me know and I'll
> do it differently in the future.
>
> Cheers,
> Nathan
>
> Heres the code:
>
> (defun call-interactively (command &optional (input "") auto-complete)
>   "Parse the command's arguments from input given the command's
> argument specifications then execute it. Returns a string or nil if
> user aborted."
>   (declare (type (or string symbol) command)
>            (type (or string argument-line) input))
>   ;; Catch parse errors
>   (catch 'error
>     (let* ((arg-line (if (stringp input)
>                          (make-argument-line :string input
>                                              :start 0)
>                          input))
>            (cmd-data (or (get-command-structure command)
> (and auto-complete
>      (let ((comp (input-find-completions command (all-commands))))
> (when (and comp (= 1 (length comp)))
>  (get-command-structure (car comp)))))
> (throw 'error (format nil "Command '~a' not found." command))))
>            (arg-specs (command-args cmd-data))
>            (args (loop for spec in arg-specs
>       collect (let* ((type (if (listp spec)
> (first spec)
> spec))
>      (prompt (when (listp spec)
> (second spec)))
>      (fn (gethash type *command-type-hash*)))
> (unless fn
>   (throw 'error (format nil "Bad argument type: ~s" type)))
> ;; If the prompt is NIL then it's
> ;; considered an optional argument and
> ;; we shouldn't prompt for it if the
> ;; arg line is empty.
> (if (and (null prompt)
>  (argument-line-end-p arg-line))
>     (loop-finish)
>     (funcall fn arg-line prompt))))))
>       ;; Did the whole string get parsed?
>       (unless (or (argument-line-end-p arg-line)
>                   (position-if 'alphanumericp (argument-line-string arg-line) :start (argument-line-start arg-line)))
>         (throw 'error (format nil "Trailing garbage: ~{~A~^ ~}" (subseq (argument-line-string arg-line)
>                                                                         (argument-line-start arg-line)))))
>       ;; Success
>       (prog1
>           (apply (command-name cmd-data) args)
>         (setf *last-command* command)))))
>
> (defun eval-command (cmd &optional interactivep auto-complete)
>   "exec cmd and echo the result."
>   (labels ((parse-and-run-command (input)
>              (let* ((arg-line (make-argument-line :string input
>                                                   :start 0))
>                     (cmd (argument-pop arg-line)))
>                (let ((*interactivep* interactivep))
>                  (call-interactively cmd arg-line auto-complete)))))
>     (multiple-value-bind (result error-p)
>         ;; this fancy footwork lets us grab the backtrace from where the
>         ;; error actually happened.
>         (restart-case
>             (handler-bind
>                 ((error (lambda (c)
>  (invoke-restart 'eval-command-error
>  (format nil "^B^1*Error In Command '^b~a^B': ^n~A~a"
>  cmd c (if *show-command-backtrace*
>    (backtrace-string) ""))))))
>               (parse-and-run-command cmd))
>           (eval-command-error (err-text)
>             :interactive (lambda () nil)
>             (values err-text t)))
>       ;; interactive commands update the modeline
>       (update-all-mode-lines)
>       (cond ((stringp result)
>              (if error-p
>                  (message-no-timeout "~a" result)
>                  (message "~a" result)))
>             ((eq result :abort)
>              (unless *suppress-abort-messages*
>                (message "Abort.")))))))
>
> (defcommand colon (&optional initial-input) (:rest)
>   "Read a command from the user. @var{initial-text} is optional. When
> supplied, the text will appear in the prompt.
>
> String arguments with spaces may be passed to the command by
> delimiting them with double quotes. A backslash can be used to escape
> double quotes or backslashes inside the string. This does not apply to
> commands taking :REST or :SHELL type arguments."
>   (let ((cmd (completing-read (current-screen) ": " (all-commands) :initial-input (or initial-input ""))))
>     (unless cmd
>       (throw 'error :abort))
>     (when (plusp (length cmd))
>       (eval-command cmd t t))))


Reply | Threaded
Open this post in threaded view
|

Re: execute incomplete commands when theres only one completion

Nathan Shostek
Sorry for spamming this mailing list, but I've worked out how to achieve
the emacs-esq completions I would like to have when inputing commands
via colon. Knowing myself, I'm probably doing something wrong, but it
seems to work fine.

Heres the code: https://gist.github.com/szos/d7dbd8f1c9f4b7d71666bafe8621629c

Cheers,
Nathan

Nathan Shostek writes:

> After digging further, I think the best place to make changes isnt inthese
> functions, but rather in get-completion-preview-list. Does this sound correct?
>
> Thanks,
> Nathan s
>
> Responding to:
>
>> Hello,
>>
>> When Emacs is accepting input via M-x one can forego writing the whole command
>> due to the way emacs completes it. While I'm not skilled enough to implement
>> emacs style completion, I do think it would be nice to be able to complete
>> incomplete commands when there is only one possible completion. I'm putting this
>> here instead of in a github pull request because, in all truthfulness, github
>> forking and pull-requesting confuses me, and someone told me I could also
>> submit my contributions throught this mailing list.
>>
>> I've modified the functions eval-command and call-interactively to have an
>> additional optional argument called auto-complete, which when true retries
>> completion before throwing an error. I'm not sure if this is the right way
>> to get the behavior I want, but it works. If theres a better way to do this
>> I'll gladly try to implement it.
>>
>> Apologies for the length of this email; I figured it was better to include every
>> function than just the small bits i changed, but if its not let me know and I'll
>> do it differently in the future.
>>
>> Cheers,
>> Nathan
>>
>> Heres the code:
>>
>> (defun call-interactively (command &optional (input "") auto-complete)
>>   "Parse the command's arguments from input given the command's
>> argument specifications then execute it. Returns a string or nil if
>> user aborted."
>>   (declare (type (or string symbol) command)
>>            (type (or string argument-line) input))
>>   ;; Catch parse errors
>>   (catch 'error
>>     (let* ((arg-line (if (stringp input)
>>                          (make-argument-line :string input
>>                                              :start 0)
>>                          input))
>>            (cmd-data (or (get-command-structure command)
>> (and auto-complete
>>      (let ((comp (input-find-completions command (all-commands))))
>> (when (and comp (= 1 (length comp)))
>>  (get-command-structure (car comp)))))
>> (throw 'error (format nil "Command '~a' not found." command))))
>>            (arg-specs (command-args cmd-data))
>>            (args (loop for spec in arg-specs
>>       collect (let* ((type (if (listp spec)
>> (first spec)
>> spec))
>>      (prompt (when (listp spec)
>> (second spec)))
>>      (fn (gethash type *command-type-hash*)))
>> (unless fn
>>   (throw 'error (format nil "Bad argument type: ~s" type)))
>> ;; If the prompt is NIL then it's
>> ;; considered an optional argument and
>> ;; we shouldn't prompt for it if the
>> ;; arg line is empty.
>> (if (and (null prompt)
>>  (argument-line-end-p arg-line))
>>     (loop-finish)
>>     (funcall fn arg-line prompt))))))
>>       ;; Did the whole string get parsed?
>>       (unless (or (argument-line-end-p arg-line)
>>                   (position-if 'alphanumericp (argument-line-string arg-line) :start (argument-line-start arg-line)))
>>         (throw 'error (format nil "Trailing garbage: ~{~A~^ ~}" (subseq (argument-line-string arg-line)
>>                                                                         (argument-line-start arg-line)))))
>>       ;; Success
>>       (prog1
>>           (apply (command-name cmd-data) args)
>>         (setf *last-command* command)))))
>>
>> (defun eval-command (cmd &optional interactivep auto-complete)
>>   "exec cmd and echo the result."
>>   (labels ((parse-and-run-command (input)
>>              (let* ((arg-line (make-argument-line :string input
>>                                                   :start 0))
>>                     (cmd (argument-pop arg-line)))
>>                (let ((*interactivep* interactivep))
>>                  (call-interactively cmd arg-line auto-complete)))))
>>     (multiple-value-bind (result error-p)
>>         ;; this fancy footwork lets us grab the backtrace from where the
>>         ;; error actually happened.
>>         (restart-case
>>             (handler-bind
>>                 ((error (lambda (c)
>>  (invoke-restart 'eval-command-error
>>  (format nil "^B^1*Error In Command '^b~a^B': ^n~A~a"
>>  cmd c (if *show-command-backtrace*
>>    (backtrace-string) ""))))))
>>               (parse-and-run-command cmd))
>>           (eval-command-error (err-text)
>>             :interactive (lambda () nil)
>>             (values err-text t)))
>>       ;; interactive commands update the modeline
>>       (update-all-mode-lines)
>>       (cond ((stringp result)
>>              (if error-p
>>                  (message-no-timeout "~a" result)
>>                  (message "~a" result)))
>>             ((eq result :abort)
>>              (unless *suppress-abort-messages*
>>                (message "Abort.")))))))
>>
>> (defcommand colon (&optional initial-input) (:rest)
>>   "Read a command from the user. @var{initial-text} is optional. When
>> supplied, the text will appear in the prompt.
>>
>> String arguments with spaces may be passed to the command by
>> delimiting them with double quotes. A backslash can be used to escape
>> double quotes or backslashes inside the string. This does not apply to
>> commands taking :REST or :SHELL type arguments."
>>   (let ((cmd (completing-read (current-screen) ": " (all-commands) :initial-input (or initial-input ""))))
>>     (unless cmd
>>       (throw 'error :abort))
>>     (when (plusp (length cmd))
>>       (eval-command cmd t t))))


Reply | Threaded
Open this post in threaded view
|

Re: execute incomplete commands when theres only one completion

David Bjergaard
If the community would benefit feel free to open a pull request and we’ll get the code reviewed and integrated in stumpwm proper.

    David

> On Feb 27, 2020, at 4:53 PM, Nathan Shostek <[hidden email]> wrote:
>
> Sorry for spamming this mailing list, but I've worked out how to achieve
> the emacs-esq completions I would like to have when inputing commands
> via colon. Knowing myself, I'm probably doing something wrong, but it
> seems to work fine.
>
> Heres the code: https://gist.github.com/szos/d7dbd8f1c9f4b7d71666bafe8621629c
>
> Cheers,
> Nathan
>
> Nathan Shostek writes:
>
>> After digging further, I think the best place to make changes isnt inthese
>> functions, but rather in get-completion-preview-list. Does this sound correct?
>>
>> Thanks,
>> Nathan s
>>
>> Responding to:
>>
>>> Hello,
>>>
>>> When Emacs is accepting input via M-x one can forego writing the whole command
>>> due to the way emacs completes it. While I'm not skilled enough to implement
>>> emacs style completion, I do think it would be nice to be able to complete
>>> incomplete commands when there is only one possible completion. I'm putting this
>>> here instead of in a github pull request because, in all truthfulness, github
>>> forking and pull-requesting confuses me, and someone told me I could also
>>> submit my contributions throught this mailing list.
>>>
>>> I've modified the functions eval-command and call-interactively to have an
>>> additional optional argument called auto-complete, which when true retries
>>> completion before throwing an error. I'm not sure if this is the right way
>>> to get the behavior I want, but it works. If theres a better way to do this
>>> I'll gladly try to implement it.
>>>
>>> Apologies for the length of this email; I figured it was better to include every
>>> function than just the small bits i changed, but if its not let me know and I'll
>>> do it differently in the future.
>>>
>>> Cheers,
>>> Nathan
>>>
>>> Heres the code:
>>>
>>> (defun call-interactively (command &optional (input "") auto-complete)
>>>  "Parse the command's arguments from input given the command's
>>> argument specifications then execute it. Returns a string or nil if
>>> user aborted."
>>>  (declare (type (or string symbol) command)
>>>           (type (or string argument-line) input))
>>>  ;; Catch parse errors
>>>  (catch 'error
>>>    (let* ((arg-line (if (stringp input)
>>>                         (make-argument-line :string input
>>>                                             :start 0)
>>>                         input))
>>>           (cmd-data (or (get-command-structure command)
>>>             (and auto-complete
>>>                  (let ((comp (input-find-completions command (all-commands))))
>>>                (when (and comp (= 1 (length comp)))
>>>                  (get-command-structure (car comp)))))
>>>             (throw 'error (format nil "Command '~a' not found." command))))
>>>           (arg-specs (command-args cmd-data))
>>>           (args (loop for spec in arg-specs
>>>               collect (let* ((type (if (listp spec)
>>>                        (first spec)
>>>                        spec))
>>>                      (prompt (when (listp spec)
>>>                        (second spec)))
>>>                      (fn (gethash type *command-type-hash*)))
>>>                 (unless fn
>>>                   (throw 'error (format nil "Bad argument type: ~s" type)))
>>>                 ;; If the prompt is NIL then it's
>>>                 ;; considered an optional argument and
>>>                 ;; we shouldn't prompt for it if the
>>>                 ;; arg line is empty.
>>>                 (if (and (null prompt)
>>>                      (argument-line-end-p arg-line))
>>>                     (loop-finish)
>>>                     (funcall fn arg-line prompt))))))
>>>      ;; Did the whole string get parsed?
>>>      (unless (or (argument-line-end-p arg-line)
>>>                  (position-if 'alphanumericp (argument-line-string arg-line) :start (argument-line-start arg-line)))
>>>        (throw 'error (format nil "Trailing garbage: ~{~A~^ ~}" (subseq (argument-line-string arg-line)
>>>                                                                        (argument-line-start arg-line)))))
>>>      ;; Success
>>>      (prog1
>>>          (apply (command-name cmd-data) args)
>>>        (setf *last-command* command)))))
>>>
>>> (defun eval-command (cmd &optional interactivep auto-complete)
>>>  "exec cmd and echo the result."
>>>  (labels ((parse-and-run-command (input)
>>>             (let* ((arg-line (make-argument-line :string input
>>>                                                  :start 0))
>>>                    (cmd (argument-pop arg-line)))
>>>               (let ((*interactivep* interactivep))
>>>                 (call-interactively cmd arg-line auto-complete)))))
>>>    (multiple-value-bind (result error-p)
>>>        ;; this fancy footwork lets us grab the backtrace from where the
>>>        ;; error actually happened.
>>>        (restart-case
>>>            (handler-bind
>>>                ((error (lambda (c)
>>>              (invoke-restart 'eval-command-error
>>>                      (format nil "^B^1*Error In Command '^b~a^B': ^n~A~a"
>>>                          cmd c (if *show-command-backtrace*
>>>                                (backtrace-string) ""))))))
>>>              (parse-and-run-command cmd))
>>>          (eval-command-error (err-text)
>>>            :interactive (lambda () nil)
>>>            (values err-text t)))
>>>      ;; interactive commands update the modeline
>>>      (update-all-mode-lines)
>>>      (cond ((stringp result)
>>>             (if error-p
>>>                 (message-no-timeout "~a" result)
>>>                 (message "~a" result)))
>>>            ((eq result :abort)
>>>             (unless *suppress-abort-messages*
>>>               (message "Abort.")))))))
>>>
>>> (defcommand colon (&optional initial-input) (:rest)
>>>  "Read a command from the user. @var{initial-text} is optional. When
>>> supplied, the text will appear in the prompt.
>>>
>>> String arguments with spaces may be passed to the command by
>>> delimiting them with double quotes. A backslash can be used to escape
>>> double quotes or backslashes inside the string. This does not apply to
>>> commands taking :REST or :SHELL type arguments."
>>>  (let ((cmd (completing-read (current-screen) ": " (all-commands) :initial-input (or initial-input ""))))
>>>    (unless cmd
>>>      (throw 'error :abort))
>>>    (when (plusp (length cmd))
>>>      (eval-command cmd t t))))
>
>

Reply | Threaded
Open this post in threaded view
|

Re: execute incomplete commands when theres only one completion

Nathan Shostek
I'm unsure how useful this would be to the community. It seems (to me)
to be pretty hacky, and there are a couple edge cases where it doesnt
behave properly with regards to inserting a hyphen or a space

David Bjergaard writes:

> If the community would benefit feel free to open a pull request and we’ll get the code reviewed and integrated in stumpwm proper.
>
>     David
>
>> On Feb 27, 2020, at 4:53 PM, Nathan Shostek <[hidden email]> wrote:
>>
>> Sorry for spamming this mailing list, but I've worked out how to achieve
>> the emacs-esq completions I would like to have when inputing commands
>> via colon. Knowing myself, I'm probably doing something wrong, but it
>> seems to work fine.
>>
>> Heres the code: https://gist.github.com/szos/d7dbd8f1c9f4b7d71666bafe8621629c
>>
>> Cheers,
>> Nathan
>>
>> Nathan Shostek writes:
>>
>>> After digging further, I think the best place to make changes isnt inthese
>>> functions, but rather in get-completion-preview-list. Does this sound correct?
>>>
>>> Thanks,
>>> Nathan s
>>>
>>> Responding to:
>>>
>>>> Hello,
>>>>
>>>> When Emacs is accepting input via M-x one can forego writing the whole command
>>>> due to the way emacs completes it. While I'm not skilled enough to implement
>>>> emacs style completion, I do think it would be nice to be able to complete
>>>> incomplete commands when there is only one possible completion. I'm putting this
>>>> here instead of in a github pull request because, in all truthfulness, github
>>>> forking and pull-requesting confuses me, and someone told me I could also
>>>> submit my contributions throught this mailing list.
>>>>
>>>> I've modified the functions eval-command and call-interactively to have an
>>>> additional optional argument called auto-complete, which when true retries
>>>> completion before throwing an error. I'm not sure if this is the right way
>>>> to get the behavior I want, but it works. If theres a better way to do this
>>>> I'll gladly try to implement it.
>>>>
>>>> Apologies for the length of this email; I figured it was better to include every
>>>> function than just the small bits i changed, but if its not let me know and I'll
>>>> do it differently in the future.
>>>>
>>>> Cheers,
>>>> Nathan
>>>>
>>>> Heres the code:
>>>>
>>>> (defun call-interactively (command &optional (input "") auto-complete)
>>>>  "Parse the command's arguments from input given the command's
>>>> argument specifications then execute it. Returns a string or nil if
>>>> user aborted."
>>>>  (declare (type (or string symbol) command)
>>>>           (type (or string argument-line) input))
>>>>  ;; Catch parse errors
>>>>  (catch 'error
>>>>    (let* ((arg-line (if (stringp input)
>>>>                         (make-argument-line :string input
>>>>                                             :start 0)
>>>>                         input))
>>>>           (cmd-data (or (get-command-structure command)
>>>>             (and auto-complete
>>>>                  (let ((comp (input-find-completions command (all-commands))))
>>>>                (when (and comp (= 1 (length comp)))
>>>>                  (get-command-structure (car comp)))))
>>>>             (throw 'error (format nil "Command '~a' not found." command))))
>>>>           (arg-specs (command-args cmd-data))
>>>>           (args (loop for spec in arg-specs
>>>>               collect (let* ((type (if (listp spec)
>>>>                        (first spec)
>>>>                        spec))
>>>>                      (prompt (when (listp spec)
>>>>                        (second spec)))
>>>>                      (fn (gethash type *command-type-hash*)))
>>>>                 (unless fn
>>>>                   (throw 'error (format nil "Bad argument type: ~s" type)))
>>>>                 ;; If the prompt is NIL then it's
>>>>                 ;; considered an optional argument and
>>>>                 ;; we shouldn't prompt for it if the
>>>>                 ;; arg line is empty.
>>>>                 (if (and (null prompt)
>>>>                      (argument-line-end-p arg-line))
>>>>                     (loop-finish)
>>>>                     (funcall fn arg-line prompt))))))
>>>>      ;; Did the whole string get parsed?
>>>>      (unless (or (argument-line-end-p arg-line)
>>>>                  (position-if 'alphanumericp (argument-line-string arg-line) :start (argument-line-start arg-line)))
>>>>        (throw 'error (format nil "Trailing garbage: ~{~A~^ ~}" (subseq (argument-line-string arg-line)
>>>>                                                                        (argument-line-start arg-line)))))
>>>>      ;; Success
>>>>      (prog1
>>>>          (apply (command-name cmd-data) args)
>>>>        (setf *last-command* command)))))
>>>>
>>>> (defun eval-command (cmd &optional interactivep auto-complete)
>>>>  "exec cmd and echo the result."
>>>>  (labels ((parse-and-run-command (input)
>>>>             (let* ((arg-line (make-argument-line :string input
>>>>                                                  :start 0))
>>>>                    (cmd (argument-pop arg-line)))
>>>>               (let ((*interactivep* interactivep))
>>>>                 (call-interactively cmd arg-line auto-complete)))))
>>>>    (multiple-value-bind (result error-p)
>>>>        ;; this fancy footwork lets us grab the backtrace from where the
>>>>        ;; error actually happened.
>>>>        (restart-case
>>>>            (handler-bind
>>>>                ((error (lambda (c)
>>>>              (invoke-restart 'eval-command-error
>>>>                      (format nil "^B^1*Error In Command '^b~a^B': ^n~A~a"
>>>>                          cmd c (if *show-command-backtrace*
>>>>                                (backtrace-string) ""))))))
>>>>              (parse-and-run-command cmd))
>>>>          (eval-command-error (err-text)
>>>>            :interactive (lambda () nil)
>>>>            (values err-text t)))
>>>>      ;; interactive commands update the modeline
>>>>      (update-all-mode-lines)
>>>>      (cond ((stringp result)
>>>>             (if error-p
>>>>                 (message-no-timeout "~a" result)
>>>>                 (message "~a" result)))
>>>>            ((eq result :abort)
>>>>             (unless *suppress-abort-messages*
>>>>               (message "Abort.")))))))
>>>>
>>>> (defcommand colon (&optional initial-input) (:rest)
>>>>  "Read a command from the user. @var{initial-text} is optional. When
>>>> supplied, the text will appear in the prompt.
>>>>
>>>> String arguments with spaces may be passed to the command by
>>>> delimiting them with double quotes. A backslash can be used to escape
>>>> double quotes or backslashes inside the string. This does not apply to
>>>> commands taking :REST or :SHELL type arguments."
>>>>  (let ((cmd (completing-read (current-screen) ": " (all-commands) :initial-input (or initial-input ""))))
>>>>    (unless cmd
>>>>      (throw 'error :abort))
>>>>    (when (plusp (length cmd))
>>>>      (eval-command cmd t t))))
>>
>>