stumpwm-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

execute incomplete commands when theres only one completion


From: Nathan Shostek
Subject: execute incomplete commands when theres only one completion
Date: Tue, 25 Feb 2020 19:57:08 +0100
User-agent: mu4e 1.2.0; emacs 26.3

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 via email to

[Prev in Thread] Current Thread [Next in Thread]