;;; FG42 --- The mighty editor for the emacsians -*- lexical-binding: t; -*- ;; ;; Copyright (c) 2010-2023 Sameer Rahmani ;; ;; Author: Sameer Rahmani ;; URL: https://devheroes.codes/FG42/FG42 ;; Version: 3.0.0 ;; ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;; ;;; Commentary: ;; `System' is just a state monad which holds the state of the editor. ;; Each system has to have a `start' function to start the setup process. ;; ;;; Code: (require 'seq) (require 'fg42/utils) (fpkg/require 's) (defmacro ->commands (&rest body) "Return a list of shell commands in the BODY. Each element has to be list like `(cd \"..\")' the command is static but the arguments get evaluated." `(quote ,(mapcar (lambda (x) (when (not (listp x)) (error "Expect a list. Got %s" x)) (let ((command (car x)) (args (cdr x))) (cond ((string= command "and") (s-join " && " (eval `(->commands ,@args)))) ((string= command "or") (s-join " || " (eval `(->commands ,@args)))) (t (format "%s %s" command (mapconcat #'eval args " ")))))) body))) (defun shell<- (commands &rest opts) "Run the give list of COMMANDS via ssh on HOST with the given OPTS. OPTS: - `:shell' The shell to run the commands with. (default /bin/bash) - `:buffer' The buffer name to use. (default '*ssh[SHELL]*') - `:erase?' Whether or not erase the buffer. (default t)" (let* ((sh (or (plist-get opts :shell) "/bin/bash")) (buffer (or (plist-get opts :buffer) (format "*shell[%s]*" sh))) (erase? (or (plist-get opts :erase-buffer?) t)) (cmds (format "'%s'" (mapconcat #'identity commands ";"))) (output-buffer (get-buffer-create buffer))) (when erase? (with-current-buffer output-buffer (setq buffer-read-only nil) (erase-buffer))) (let ((p (start-process-shell-command "shell<-" (buffer-name output-buffer) (format "%s -c %s" sh cmds)))) (set-process-sentinel p (lambda (_ event) (when (string= event "finished\n") (message "Commands finished.") ;;(kill-process process) )))))) (defun ssh<- (host commands &rest opts) "Run the give list of COMMANDS via ssh on HOST with the given OPTS. OPTS: - `:shell' The shell to run the commands with. (default /bin/bash) - `:buffer' The buffer name to use. (default '*ssh[HOST]*') - `:erase?' Whether or not erase the buffer. (default t)" (let* ((sh (or (plist-get opts :shell) "/bin/bash")) (buffer (or (plist-get opts :buffer) (format "*ssh[%s]*" host))) (erase? (or (plist-get opts :erase-buffer?) t)) (cmds (format "'%s'" (mapconcat #'identity commands ";"))) (output-buffer (get-buffer-create buffer))) (when erase? (with-current-buffer output-buffer (setq buffer-read-only nil) (erase-buffer))) (let ((p (start-process-shell-command (format "ssh-to-%s" host) (buffer-name output-buffer) (format "ssh -t %s %s -c %s" host sh cmds)))) (set-process-sentinel p (lambda (_ event) (when (string= event "finished\n") (message "Commands finished. Closing SSH connection.") ;;(kill-process process) )))))) (provide 'fg42/shell) ;;; ssh.el ends here