[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: execute incomplete commands when theres only one completion
From: |
David Bjergaard |
Subject: |
Re: execute incomplete commands when theres only one completion |
Date: |
Tue, 3 Mar 2020 19:01:36 -0500 |
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))))
>
>
- Re: execute incomplete commands when theres only one completion,
David Bjergaard <=