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: Thu, 27 Feb 2020 22:52:36 +0100
User-agent: mu4e 1.2.0; emacs 26.3

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

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