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 <s...@posteo.net> 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)))) >> >>