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