diff --git a/controller/screen.go b/controller/screen.go new file mode 100644 index 0000000..7e6fc21 --- /dev/null +++ b/controller/screen.go @@ -0,0 +1,107 @@ +package controller + +import ( + "fmt" + "os/exec" + "strings" + + "github.com/charmbracelet/log" + projectconfig "github.com/xrehpicx/pee/config" +) + +// RunShellCommand executes a shell command and logs it. +func RunShellCommand(cmd *exec.Cmd) error { + log.Debug("Running command:", "command", cmd.String()) + output, err := cmd.CombinedOutput() + if err != nil { + log.Error("Error running command:", "command", cmd.String(), "error", err) + log.Info("Command output:", "output", string(output)) + return err + } + return nil +} + +// CreateScreenSession creates a new screen session based on the given Configuration. +func CreateScreenSession(config *projectconfig.Configuration) error { + sessionName := config.SessionName + + // Check if the session already exists + checkSessionCmd := exec.Command("screen", "-S", sessionName, "-Q", "windows") + _, err := checkSessionCmd.CombinedOutput() + if err == nil { + // If it exists, attach to the session + attachSessionCmd := exec.Command("screen", "-d", "-r", sessionName) + RunShellCommand(attachSessionCmd) + } else { + // If it doesn't exist, create the session + createSessionCmd := exec.Command("screen", "-S", sessionName, "-d", "-m") + RunShellCommand(createSessionCmd) + + // Create and run commands for windows + for i, window := range config.Windows { + windowName := fmt.Sprintf("%s-%d", sessionName, i+1) + + // Create a new window within the session + createWindowCmd := exec.Command("screen", "-S", sessionName, "-X", "screen", "-t", windowName) + RunShellCommand(createWindowCmd) + + // Change the working directory for the window + changeDirCmd := exec.Command("screen", "-S", sessionName, "-p", fmt.Sprint(i), "-X", "chdir", config.WorkingDir) + RunShellCommand(changeDirCmd) + + // Send commands to the window + sendCommandsCmd := exec.Command("screen", "-S", sessionName, "-p", fmt.Sprint(i), "-X", "stuff", strings.Join(window.Panes[0].ShellCommand, " && ")+"\n") + RunShellCommand(sendCommandsCmd) + + // Rename the window to the specified name + renameWindowCmd := exec.Command("screen", "-S", sessionName, "-p", fmt.Sprint(i), "-X", "title", window.WindowName) + RunShellCommand(renameWindowCmd) + + // warn user of compatibility issues using more than one pane with screen + if len(window.Panes) > 1 { + log.Warn("Screen does not support multiple panes. Only the first pane will be used.") + } + + // Create and run commands for additional panes in the window + for _, pane := range window.Panes[1:] { + // Split the window vertically + splitPaneCmd := exec.Command("screen", "-S", sessionName, "-p", fmt.Sprint(i), "-X", "split") + RunShellCommand(splitPaneCmd) + + // Select the new pane + selectPaneCmd := exec.Command("screen", "-S", sessionName, "-p", fmt.Sprint(i+1)) // Select the next pane + RunShellCommand(selectPaneCmd) + + // Send commands to the new pane + sendCommandsCmd := exec.Command("screen", "-S", sessionName, "-X", "stuff", strings.Join(pane.ShellCommand, " && ")+"\n") + RunShellCommand(sendCommandsCmd) + } + + if window.Layout != "" { + layoutCmd := exec.Command("screen", "-S", sessionName, "-p", fmt.Sprint(i), "-X", "layout", window.Layout) + RunShellCommand(layoutCmd) + } + } + + if config.Attach { + // Attach to the session + attachSessionCmd := exec.Command("screen", "-d", "-r", sessionName) + RunShellCommand(attachSessionCmd) + } + } + + return nil +} + +// KillScreenSession kills a screen session by name. +func KillScreenSession(sessionName string) error { + // Kill the screen session + killSessionCmd := exec.Command("screen", "-S", sessionName, "-X", "quit") + err := killSessionCmd.Run() + if err != nil { + log.Error("Error killing screen session:", "sessionName", sessionName, "error", err) + return err + } + log.Info("Killed screen session:", "sessionName", sessionName) + return nil +} diff --git a/main.go b/main.go index 8d21f16..0909ca0 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,7 @@ func init() { projectconfig.Init() projectmanager.RootCmd.AddCommand(projectmanager.ListProjects) projectmanager.RootCmd.AddCommand(projectmanager.InitCmd) + projectmanager.RootCmd.AddCommand(projectmanager.ScreenCmd) } func main() { diff --git a/project-manager/screen.go b/project-manager/screen.go new file mode 100644 index 0000000..2eb8801 --- /dev/null +++ b/project-manager/screen.go @@ -0,0 +1,43 @@ +package projectmanager + +import ( + "github.com/charmbracelet/log" + "github.com/spf13/cobra" + projectconfig "github.com/xrehpicx/pee/config" + "github.com/xrehpicx/pee/controller" +) + +var KillScreenCmd = &cobra.Command{ + Use: "kill", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + controller.KillScreenSession(args[0]) + }, +} + +var ScreenCmd = &cobra.Command{ + Use: "scr", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + ExecuteProjectEnvUsingScreen(args[0]) + }, +} + +func ExecuteProjectEnvUsingScreen(projectName string) { + config, err := projectconfig.GetProjectConfig(projectName) + if err != nil { + log.Error(err) + return + } + err = controller.CreateScreenSession(config) + if err != nil { + log.Error(err) + return + } + projectconfig.UpdateLastOpened(projectName) + log.Debug("Created tmux session", "name", config.SessionName) +} + +func init() { + ScreenCmd.AddCommand(KillScreenCmd) +}