[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, 05 Mar 2020 16:43:50 +0100 |
User-agent: |
mu4e 1.2.0; emacs 26.3 |
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 <address@hidden> 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))))
>>
>>