From d2d205e3817555f95287fa703bb3557189131be3 Mon Sep 17 00:00:00 2001 From: Yury Sulsky Date: Sat, 14 Oct 2017 18:51:17 +0000 Subject: [PATCH 1/7] Make evil mode optional, more keybindings. --- shell-file.el | 49 +++++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/shell-file.el b/shell-file.el index 9bedaa6..720a7de 100644 --- a/shell-file.el +++ b/shell-file.el @@ -68,7 +68,7 @@ exit ############################################################### "insert a new shell block on top of the shell-file" (interactive) (unless block-text - (when (evil-visual-state-p) + (when (and (featurep 'evil) (evil-visual-state-p)) (setq block-text (buffer-substring-no-properties (region-beginning) (region-end))) @@ -220,27 +220,36 @@ exit ############################################################### "add shell-file keybindings that should be available globally" (dolist (binding - '(("f" shell-file-find) - ("i" shell-file-insert-block) - ("x" shell-file-insert-file) - ("g" shell-file-insert-cd) - ("r" shell-file-run))) - (let* ((key (car binding)) - (key (concat key-prefix key)) - (def (cadr binding))) - (define-key map key def)))) + '((shell-file-find "f" "\C-f") + (shell-file-insert-block "i" "\C-i") + (shell-file-insert-file "x" "\C-x") + (shell-file-insert-cd "g" "\C-g") + (shell-file-run "r" "\C-r"))) + (let* ((def (car binding)) + (keys (cdr binding))) + (dolist (key keys) + (define-key map (concat key-prefix key) def))))) + +(defun define-shell-file-mode-key (key-prefix key def) + (let ((key (concat key-prefix key))) + (if (featurep 'evil) + (dolist (state (list 'normal 'motion)) + (evil-define-key state shell-file-mode-map key def)) + (define-key shell-file-mode-map key def)))) (defun shell-file-define-minor-mode-keys (key-prefix) "add shell-file keybindings that should be available in shell-file-mode" (dolist (binding - '(("b" shell-file-bubble-block) - ("d" shell-file-delete-block) - ("j" shell-file-next-block) - ("k" shell-file-prev-block) - ("s" shell-file-split-block))) - (let* ((key (car binding)) - (key (concat key-prefix key)) - (def (cadr binding))) - (dolist (state (list 'normal 'motion)) - (evil-define-key state shell-file-mode-map key def))))) + '((shell-file-bubble-block "b" "\C-b") + (shell-file-delete-block "d" "\C-d") + (shell-file-next-block "j" "n" "\C-j" "\C-n") + (shell-file-prev-block "k" "p" "\C-k" "\C-p") + (shell-file-split-block "s" "\C-s"))) + (let* ((def (car binding)) + (keys (cdr binding))) + (dolist (key keys) + (define-shell-file-mode-key key-prefix key def))))) + +(provide 'shell-file) +;;; shell-file ends here From a9e83144d8a7854a95d69871a8494459090b7e85 Mon Sep 17 00:00:00 2001 From: Yury Sulsky Date: Tue, 21 Nov 2017 12:37:26 -0500 Subject: [PATCH 2/7] make the cd command customizable --- shell-file.el | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/shell-file.el b/shell-file.el index 720a7de..f6e23ef 100644 --- a/shell-file.el +++ b/shell-file.el @@ -83,11 +83,15 @@ exit ############################################################### (when block-text (insert block-text))) ) +(defun shell-file-cd-command (dir) + "the cd command inserted into shell-file.sh" + (concat "cd " dir)) + (defun shell-file-insert-cd () "insert a new shell block on top of the shell-file" (interactive) (let* ((dir default-directory)) - (shell-file-insert-block (concat "cd " dir)))) + (shell-file-insert-block (shell-file-cd-command dir)))) (defun shell-file-insert-file () "insert a new shell block on top of the shell-file" @@ -97,7 +101,7 @@ exit ############################################################### (file-name-nondirectory (if (eq major-mode 'dired-mode) (dired-filename-at-point) (buffer-file-name))))) - (shell-file-insert-block (concat "cd " dir "\n./" file)))) + (shell-file-insert-block (concat (shell-file-cd-command dir) "\n./" file)))) (defun shell-file-delete-block (num-times) "delete a shell block off the top of the shell-file" From e1ec6e94fbcd502ecb7923f62d5c8176b8c4d04c Mon Sep 17 00:00:00 2001 From: Yury Sulsky Date: Thu, 16 Aug 2018 18:41:29 +0100 Subject: [PATCH 3/7] Fix a bug where the previously killed text is inserted when bubbling blocks. --- shell-file.el | 1 + 1 file changed, 1 insertion(+) diff --git a/shell-file.el b/shell-file.el index f6e23ef..11bbb05 100644 --- a/shell-file.el +++ b/shell-file.el @@ -139,6 +139,7 @@ exit ############################################################### (next-line) (let ((beg (point))) (search-forward exit-line) + (setq last-command nil) ; Don't append to the previous kill. (kill-region beg (point))) (goto-line 1) (search-forward init-line) From ae368fa4003deb29e8fa82293ebecbbda41a3593 Mon Sep 17 00:00:00 2001 From: Yury Sulsky Date: Mon, 5 Aug 2024 19:54:38 -0700 Subject: [PATCH 4/7] Add remote execution support --- shell-file.el | 90 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 26 deletions(-) diff --git a/shell-file.el b/shell-file.el index 11bbb05..fb070a6 100644 --- a/shell-file.el +++ b/shell-file.el @@ -6,6 +6,10 @@ (defcustom shell-file-dir (format "/tmp/shell-file.%s" (user-login-name)) "Where shell-file commands and output will be logged") +(defcustom shell-file-remote-host nil + "Remote host to execute shell-file commands on. +Set this to a string like \"user@host\" to enable remote execution.") + (setq shell-file-init-line-re "^### START #########################.*\n") (setq shell-file-exit-line-re "^exit ##############################.*\n") @@ -191,35 +195,68 @@ exit ############################################################### (throw 'body (switch-to-buffer name))) (setq slot (+ slot 1))))))) + +(defun shell-file-log (message) + "Log a message to the *Messages* buffer." + (message "[Shell-File] %s" message)) + + (defun shell-file-run () - "run shell-file top block in the background" + "Run shell-file top block in the background, locally or remotely." (interactive) (when (shell-file-current-buffer-is-block-buffer) (shell-file-bubble-block t)) - (let* - (;; compute log files - (dir shell-file-dir) - (_ (mkdir dir t)) - (uid (format-time-string "%Y-%m-%d.%H:%M:%S")) - (cmd (format "%s/%s.command" dir uid)) - (out (format "%s/%s.output" dir uid)) - ;; write out block - (_ - (save-window-excursion - (save-excursion - (shell-file-find) - (goto-char (point-min)) - (search-forward-regexp (shell-file-exit-line)) - (write-region (point-min) (match-end 0) cmd)))) - ;; execute block - (_ (chmod cmd - (file-modes-symbolic-to-number "u+x" (file-modes cmd)))) - (cmd (format "%s 2>&1 | tee %s" cmd out)) - (buf (save-window-excursion (shell-file-output))) - (_ (async-shell-command cmd buf)) - (_ (display-buffer buf nil 'visible)) - ) - nil)) + (let* ((remote-prefix (when shell-file-remote-host + (format "/ssh:%s:" shell-file-remote-host))) + (default-directory (if remote-prefix + (concat remote-prefix "/") + default-directory)) + (dir (if remote-prefix + (concat remote-prefix shell-file-dir) + shell-file-dir)) + (_ (make-directory dir t)) + (uid (format-time-string "%Y-%m-%d.%H:%M:%S")) + (cmd-file (expand-file-name (format "%s.command" uid) dir)) + (out-file (expand-file-name (format "%s.output" uid) dir))) + + ;; Write out block + (save-window-excursion + (save-excursion + (shell-file-find) + (goto-char (point-min)) + (search-forward-regexp (shell-file-exit-line)) + (setq block-end (match-beginning 0)) + (write-region (point-min) block-end cmd-file))) + + ;; Set execute permissions + (shell-file-log (format "Setting execute permissions for %s" cmd-file)) + (set-file-modes cmd-file #o755) + + ;; Prepare command + (setq full-cmd (format "bash %s 2>&1 | tee %s" + (file-local-name cmd-file) + (file-local-name out-file))) + + ;; Log the command + (shell-file-log (format "Executing command: %s" full-cmd)) + (shell-file-log (format "Remote host: %s" (or shell-file-remote-host "local"))) + (shell-file-log (format "Working directory: %s" default-directory)) + + ;; Execute command + (let ((buf (save-window-excursion (shell-file-output)))) + (async-shell-command full-cmd buf) + (display-buffer buf nil 'visible)) + + ;; Log completion + (shell-file-log "Command execution initiated."))) + + +(defun shell-file-set-remote-host (host) + "Set the remote host for shell-file execution." + (interactive "sEnter remote host (user@host): ") + (setq shell-file-remote-host (if (string-empty-p host) nil host)) + (message "Remote host set to %s" (or shell-file-remote-host "nil (local execution)"))) + (defun shell-file-define-global-keys (map key-prefix) "add shell-file keybindings that should be available globally" @@ -229,7 +266,8 @@ exit ############################################################### (shell-file-insert-block "i" "\C-i") (shell-file-insert-file "x" "\C-x") (shell-file-insert-cd "g" "\C-g") - (shell-file-run "r" "\C-r"))) + (shell-file-run "r" "\C-r") + (shell-file-set-remote-host "h" "\C-h"))) (let* ((def (car binding)) (keys (cdr binding))) (dolist (key keys) From d3f00b209da56370f5d7210e352de90286e9531a Mon Sep 17 00:00:00 2001 From: Yury Sulsky Date: Fri, 4 Oct 2024 15:49:12 -0700 Subject: [PATCH 5/7] Save outputs to a single file, add a keybinding to visit them --- shell-file.el | 71 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/shell-file.el b/shell-file.el index fb070a6..72b2197 100644 --- a/shell-file.el +++ b/shell-file.el @@ -200,7 +200,6 @@ exit ############################################################### "Log a message to the *Messages* buffer." (message "[Shell-File] %s" message)) - (defun shell-file-run () "Run shell-file top block in the background, locally or remotely." (interactive) @@ -216,47 +215,60 @@ exit ############################################################### shell-file-dir)) (_ (make-directory dir t)) (uid (format-time-string "%Y-%m-%d.%H:%M:%S")) - (cmd-file (expand-file-name (format "%s.command" uid) dir)) (out-file (expand-file-name (format "%s.output" uid) dir))) - ;; Write out block - (save-window-excursion - (save-excursion - (shell-file-find) - (goto-char (point-min)) - (search-forward-regexp (shell-file-exit-line)) - (setq block-end (match-beginning 0)) - (write-region (point-min) block-end cmd-file))) - - ;; Set execute permissions - (shell-file-log (format "Setting execute permissions for %s" cmd-file)) - (set-file-modes cmd-file #o755) - - ;; Prepare command - (setq full-cmd (format "bash %s 2>&1 | tee %s" - (file-local-name cmd-file) - (file-local-name out-file))) - - ;; Log the command - (shell-file-log (format "Executing command: %s" full-cmd)) + (shell-file-log "Preparing to run command...") (shell-file-log (format "Remote host: %s" (or shell-file-remote-host "local"))) (shell-file-log (format "Working directory: %s" default-directory)) - ;; Execute command - (let ((buf (save-window-excursion (shell-file-output)))) - (async-shell-command full-cmd buf) - (display-buffer buf nil 'visible)) + (shell-file-write-command-file out-file) + (shell-file-execute-command out-file remote-prefix))) - ;; Log completion +(defun shell-file-write-command-file (cmd-file) + "Write the shell-file command to the specified file, including the prelude and first block." + (shell-file-log (format "Writing command to %s" cmd-file)) + (save-window-excursion + (save-excursion + (shell-file-find) + (goto-char (point-min)) + (let ((block-end (progn + (search-forward-regexp (shell-file-init-line)) + (search-forward-regexp (shell-file-exit-line)) + (search-forward-regexp "^") + (match-beginning 0)))) + (let ((content (buffer-substring-no-properties (point-min) block-end))) + (write-region content nil cmd-file)))))) + +(defun shell-file-execute-command (out-file remote-prefix) + "Execute the shell-file command, locally or remotely." + (let* ((out-file-local (file-local-name out-file)) + (full-cmd (format "bash %s 2>&1 | tee -a %s" + (shell-quote-argument out-file-local) + (shell-quote-argument out-file-local)))) + (shell-file-log (format "Executing command: %s" full-cmd)) + (let ((buf (shell-file-output))) + (if remote-prefix + (let ((remote-cmd (format "ssh %s %s" + (shell-quote-argument (file-remote-p remote-prefix 'host)) + (shell-quote-argument full-cmd)))) + (shell-file-log (format "Remote command: %s" remote-cmd)) + (async-shell-command remote-cmd buf)) + (async-shell-command full-cmd buf)) + (display-buffer buf nil 'visible)) (shell-file-log "Command execution initiated."))) - (defun shell-file-set-remote-host (host) "Set the remote host for shell-file execution." (interactive "sEnter remote host (user@host): ") (setq shell-file-remote-host (if (string-empty-p host) nil host)) (message "Remote host set to %s" (or shell-file-remote-host "nil (local execution)"))) +(defun shell-file-visit-outputs () + (interactive) + (dired shell-file-dir) + (dired-sort-other "-lt") ;; Sort by time, newest first. + (goto-char (point-min)) + (forward-line)) (defun shell-file-define-global-keys (map key-prefix) "add shell-file keybindings that should be available globally" @@ -267,7 +279,8 @@ exit ############################################################### (shell-file-insert-file "x" "\C-x") (shell-file-insert-cd "g" "\C-g") (shell-file-run "r" "\C-r") - (shell-file-set-remote-host "h" "\C-h"))) + (shell-file-set-remote-host "h" "\C-h") + (shell-file-visit-outputs "o" "\C-o"))) (let* ((def (car binding)) (keys (cdr binding))) (dolist (key keys) From a1a0cc431f05cc4b87db97734adad033921af17a Mon Sep 17 00:00:00 2001 From: Yury Sulsky Date: Tue, 19 Nov 2024 16:30:55 -0800 Subject: [PATCH 6/7] Print out the command being run. --- shell-file.el | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/shell-file.el b/shell-file.el index 72b2197..e757495 100644 --- a/shell-file.el +++ b/shell-file.el @@ -242,7 +242,10 @@ exit ############################################################### (defun shell-file-execute-command (out-file remote-prefix) "Execute the shell-file command, locally or remotely." (let* ((out-file-local (file-local-name out-file)) - (full-cmd (format "bash %s 2>&1 | tee -a %s" + (full-cmd (format "sed -n %s %s; echo; bash %s 2>&1 | tee -a %s" + (shell-quote-argument + (concat "/" (replace-regexp-in-string "\n$" "" shell-file-init-line-re) "/,$p")) + (shell-quote-argument out-file-local) (shell-quote-argument out-file-local) (shell-quote-argument out-file-local)))) (shell-file-log (format "Executing command: %s" full-cmd)) From 1ba7d1ca3739ecfcb2377c41390b2327d6dfd47d Mon Sep 17 00:00:00 2001 From: Yury Sulsky Date: Wed, 27 Nov 2024 23:58:13 -0800 Subject: [PATCH 7/7] Keep the shell file buffer interactive. --- shell-file.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell-file.el b/shell-file.el index e757495..9919e1e 100644 --- a/shell-file.el +++ b/shell-file.el @@ -242,7 +242,7 @@ exit ############################################################### (defun shell-file-execute-command (out-file remote-prefix) "Execute the shell-file command, locally or remotely." (let* ((out-file-local (file-local-name out-file)) - (full-cmd (format "sed -n %s %s; echo; bash %s 2>&1 | tee -a %s" + (full-cmd (format "(sed -n %s %s; echo; script -qc 'bash -i %s' /dev/null 0<&0) 2>&1 | tee -a %s" (shell-quote-argument (concat "/" (replace-regexp-in-string "\n$" "" shell-file-init-line-re) "/,$p")) (shell-quote-argument out-file-local)