Hello, I usually run the same command with async-shell-command, however I have to navigate back to the last command history to trigger my last run command, is there any way async-shell-command (and shell-command) can autofill with last run command so that I just hit enter without extra steps?

  • abbreviatedman@alien.topB
    link
    fedilink
    English
    arrow-up
    1
    ·
    11 months ago

    Check the documentation of async-shell-command (M-x describe-function async-shell-command RET), and you’ll see that it takes one necessary argument, the command to run.

    If you run it interactively (through its keybinding or M-x), it prompts you for that argument. Type in the command for Emacs, and Emacs will pass that as the argument to async-shell-command.

    But if you want to, you can call it from Lisp and pass in the argument yourself! Try executing it in the scratch buffer or by running eval-expression (I believe M-: by default).

    (async-shell-command "git status")
    

    That will run git status and give you the result

    Now if you want to run that bit of code more often, wrap it in a function and assign it a keybinding!

    (defun crj-check-git-status ()
      (interactive)
      (async-shell-command "git status"))
    
    (general-define-key (kbd "C-M-&") #'crj-check-git-status)
    

    Would anyone else handle this differently? I’m still learning myself!

    • nqminhuit@alien.topOPB
      link
      fedilink
      English
      arrow-up
      1
      ·
      11 months ago

      I think you misunderstood, I need autofilled with the last run command (which is dynamic) not the the hard-coded one.

  • samuelroy_@alien.topB
    link
    fedilink
    English
    arrow-up
    1
    ·
    11 months ago

    You could make your own wrappers around async-shell-command and shell-command to autofill with your last command + have your whole history. Try the code below to see if it does the job like you’d like.

    (defvar async-shell-command-history nil
      "List of commands executed with `async-shell-command`.")
    
    (defvar shell-command-history nil
      "List of commands executed with `shell-command`.")
    
    (defadvice async-shell-command (before save-command-in-history activate)
      "Save the command executed with `async-shell-command` in its history."
      (let ((command (ad-get-arg 0)))
        (unless (string-blank-p command)
          (setq async-shell-command-history (cons command async-shell-command-history)))))
    
    (defadvice shell-command (before save-command-in-history activate)
      "Save the command executed with `shell-command` in its history."
      (let ((command (ad-get-arg 0)))
        (unless (string-blank-p command)
          (setq shell-command-history (cons command shell-command-history)))))
    
    (defun my-async-shell-command ()
      "Run `async-shell-command` with a choice from its command history."
      (interactive)
      (let* ((command (completing-read "Async shell command: "
                                       async-shell-command-history
                                       nil nil
                                       (car async-shell-command-history)))
             (final-command (if (string-blank-p command) 
                                (or (car async-shell-command-history) "")
                              command)))
        (async-shell-command final-command)))
    
    (defun my-shell-command ()
      "Run `shell-command` with a choice from its command history."
      (interactive)
      (let* ((command (completing-read "Shell command: "
                                       shell-command-history
                                       nil nil
                                       (car shell-command-history)))
             (final-command (if (string-blank-p command) 
                                (or (car shell-command-history) "")
                              command)))
        (shell-command final-command)))
    
    • nqminhuit@alien.topOPB
      link
      fedilink
      English
      arrow-up
      1
      ·
      11 months ago

      Thank you very much for your effort, but I have found a neat way to achieve this, I updated answer on the post.

  • abbreviatedman@alien.topB
    link
    fedilink
    English
    arrow-up
    1
    ·
    11 months ago

    I tweaked the answer somewhat, as it bothered me that we were getting the most recent command inserted and having it show up again when you decide to go back to an earlier command. In other words, with the OP’s advice, we get the most recent command in the minibuffer history twice, once auto-filled and once in the regular history.

    I’m sure there are cleaner ways to do it than using a custom global variable no other functions know about, which could I think lead to some weird side effects at some point… but for now, I like the behavior:

    (defvar crj-most-recent-shell-command (car shell-command-history))
    
    (defun crj--pop-shell-command-history (fn &rest _args)
      (setq crj-most-recent-shell-command (car shell-command-history))
      (let ((shell-command-history (cdr shell-command-history)))
        (apply fn _args)))
    
    (defun crj--auto-fill-shell-commands (args)
      (list (car args) crj-most-recent-shell-command))
    
    (advice-add 'read-shell-command :around #'crj--pop-shell-command-history)
    (advice-add 'read-shell-command :filter-args #'crj--auto-fill-shell-commands)
    

    Let me know if anyone has improvements/objections!