[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))))