stumpwm-devel
[Top][All Lists]
Advanced

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

Re: execute incomplete commands when theres only one completion


From: Nathan Shostek
Subject: Re: execute incomplete commands when theres only one completion
Date: Wed, 26 Feb 2020 14:47:56 +0100
User-agent: mu4e 1.2.0; emacs 26.3

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

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