feat: supports panes and tmux layouts

This commit is contained in:
raj 2023-10-22 14:01:39 +05:30
parent 3e2f137700
commit 17604df9da
5 changed files with 236 additions and 88 deletions

View File

@ -11,15 +11,32 @@ import (
"gopkg.in/yaml.v2"
)
type Pane struct {
ShellCommand []string `yaml:"shell_command"`
}
type Window struct {
WindowName string `yaml:"window_name"`
Layout string `yaml:"layout"`
ShellCommandBefore []string `yaml:"shell_command_before"`
Panes []Pane `yaml:"panes"`
}
type Configuration struct {
Name string `yaml:"name"`
SessionName string `yaml:"session_name"`
WorkingDir string `yaml:"working_dir"` // New field for working directory
Tabs []struct {
Name string `yaml:"name"`
Commands []string `yaml:"commands"`
} `yaml:"tabs"`
LastOpened time.Time `yaml:"last_opened"`
SessionName string `yaml:"name"`
EditorCommand string `yaml:"editor"`
WorkingDir string `yaml:"root"`
Windows []Window `yaml:"windows"`
LastOpened time.Time
Attach bool `yaml:"attach"`
StartupWindow string `yaml:"startup_window"`
StartupPane int `yaml:"startup_pane"`
OnProjectStart string `yaml:"on_project_start"`
OnProjectFirstStart string `yaml:"on_project_first_start"`
OnProjectRestart string `yaml:"on_project_restart"`
OnProjectExit string `yaml:"on_project_exit"`
OnProjectStop string `yaml:"on_project_stop"`
SocketName string `yaml:"socket_name"`
}
var configDir string
@ -99,6 +116,17 @@ func UpdateLastOpened(projectName string) error {
return nil
}
func GetEditorCommand(projectName string) (string, error) {
configFile := ProjectConfigFilePath(projectName)
config, err := Load(configFile)
if err != nil {
return "", err
}
return config.EditorCommand, nil
}
func ListProjects() (map[string]*Configuration, error) {
projectConfigs := make(map[string]*Configuration)
@ -155,39 +183,18 @@ func ProjectExists(projectName string) bool {
return true
}
func CreateProject(projectName, sessionName, workingDir string, tabs []struct {
Name string
Commands []string
},
) (string, error) {
configFile := ProjectConfigFilePath(projectName)
func CreateProject(sessionName, workingDir string, windows []Window) (string, error) {
configFile := ProjectConfigFilePath(sessionName)
if _, err := os.Stat(configFile); err == nil {
return "", fmt.Errorf("Project with the name '%s' already exists", projectName)
}
var tabsWithYAMLTags []struct {
Name string `yaml:"name"`
Commands []string `yaml:"commands"`
}
for _, tab := range tabs {
tabWithYAMLTags := struct {
Name string `yaml:"name"`
Commands []string `yaml:"commands"`
}{
Name: tab.Name,
Commands: tab.Commands,
}
tabsWithYAMLTags = append(tabsWithYAMLTags, tabWithYAMLTags)
return "", fmt.Errorf("Project with the name '%s' already exists", sessionName)
}
newConfig := &Configuration{
Name: projectName,
SessionName: sessionName,
WorkingDir: workingDir,
Tabs: tabsWithYAMLTags,
LastOpened: time.Now(),
Windows: windows,
Attach: true,
}
err := WriteConfigToFile(configFile, newConfig)
@ -204,23 +211,10 @@ func WriteConfigToFile(filename string, config *Configuration) error {
return err
}
indentedYAML := indentYAML(string(data), "") // Convert data to string
err = os.WriteFile(filename, []byte(indentedYAML), 0644)
err = os.WriteFile(filename, data, 0644)
if err != nil {
return err
}
return nil
}
func indentYAML(yamlString, prefix string) string {
lines := strings.Split(yamlString, "\n")
indentedLines := make([]string, len(lines))
for i, line := range lines {
indentedLines[i] = prefix + line
}
return strings.Join(indentedLines, "\n")
}

View File

@ -10,65 +10,91 @@ import (
"github.com/charmbracelet/log"
)
// CreateTmuxSession creates a tmux session based on the given Configuration.
// CreateTmuxSession creates a Tmux session based on the given Configuration.
func CreateTmuxSession(config *projectconfig.Configuration) error {
sessionName := config.SessionName
// Check if the session exists
checkSessionCmd := exec.Command("tmux", "has-session", "-t", sessionName)
log.Debug("Ran command", "command", checkSessionCmd.String())
if err := checkSessionCmd.Run(); err == nil {
// If it exists, switch to the session
switchSessionCmd := exec.Command("tmux", "switch-client", "-t", sessionName)
if err := switchSessionCmd.Run(); err != nil {
return err
}
log.Debug("Ran command", "command", switchSessionCmd.String())
} else {
// If it doesn't exist, create the session
createSessionCmd := exec.Command("tmux", "new-session", "-d", "-s", sessionName)
if err := createSessionCmd.Run(); err != nil {
return err
}
log.Info("Ran command", "command", createSessionCmd.String())
log.Debug("Ran command", "command", createSessionCmd.String())
// Change the working directory
changeDirCmd := exec.Command("tmux", "send-keys", "-t", sessionName, "cd "+config.WorkingDir, "Enter")
if err := changeDirCmd.Run(); err != nil {
return err
}
log.Info("Ran command", "command", changeDirCmd.String())
log.Debug("Ran command", "command", changeDirCmd.String())
// Send commands to the session for the first tab
sendCommandsCmd := exec.Command("tmux", "send-keys", "-t", sessionName, strings.Join(config.Tabs[0].Commands, " && "), "Enter")
// Create the first window outside the loop
// createWindow(config, sessionName, 0)
window := config.Windows[0]
windowName := fmt.Sprintf("%s:%d", sessionName, 1)
sendCommandsCmd := exec.Command("tmux", "send-keys", "-t", windowName, strings.Join(window.Panes[0].ShellCommand, " && "), "Enter")
if err := sendCommandsCmd.Run(); err != nil {
return err
}
log.Info("Ran command", "command", sendCommandsCmd.String())
// Rename the tab to the specified name
renameTabCmd := exec.Command("tmux", "rename-window", "-t", sessionName+":1", config.Tabs[0].Name)
if err := renameTabCmd.Run(); err != nil {
log.Debug("Ran command", "command", sendCommandsCmd.String())
// Rename the window to the specified name
renameWindowCmd := exec.Command("tmux", "rename-window", "-t", windowName, window.WindowName)
if err := renameWindowCmd.Run(); err != nil {
return err
}
log.Debug("Ran command", "command", renameWindowCmd.String())
// Create and run commands for additional tabs
for i, tab := range config.Tabs[1:] {
windowName := fmt.Sprintf("%s:%d", sessionName, i+2)
createWindowCmd := exec.Command("tmux", "new-window", "-t", windowName, "-n", tab.Name)
if err := createWindowCmd.Run(); err != nil {
// Create and run commands for additional panes in the window
for j, pane := range window.Panes[1:] {
paneName := fmt.Sprintf("%s:%d.%d", sessionName, 1, j+2)
// Split the window horizontally
splitPaneCmd := exec.Command("tmux", "split-window", "-t", windowName, "-h", "-p", "50")
if err := splitPaneCmd.Run(); err != nil {
return err
}
log.Info("Ran command", "command", createWindowCmd.String())
log.Debug("Ran command", "command", splitPaneCmd.String())
changeDirCmd = exec.Command("tmux", "send-keys", "-t", windowName, "cd "+config.WorkingDir, "Enter")
if err := changeDirCmd.Run(); err != nil {
// Select the new pane
selectPaneCmd := exec.Command("tmux", "select-pane", "-t", paneName)
if err := selectPaneCmd.Run(); err != nil {
return err
}
log.Info("Ran command", "command", changeDirCmd.String())
log.Debug("Ran command", "command", selectPaneCmd.String())
sendCommandsCmd = exec.Command("tmux", "send-keys", "-t", windowName, strings.Join(tab.Commands, " && "), "Enter")
// Send commands to the pane
sendCommandsCmd := exec.Command("tmux", "send-keys", "-t", paneName, strings.Join(pane.ShellCommand, " && "), "Enter")
if err := sendCommandsCmd.Run(); err != nil {
return err
}
log.Info("Ran command", "command", sendCommandsCmd.String())
log.Debug("Ran command", "command", sendCommandsCmd.String())
}
if window.Layout != "" {
layoutCmd := exec.Command("tmux", "select-layout", "-t", windowName, window.Layout)
if err := layoutCmd.Run(); err != nil {
return err
}
log.Debug("Ran command", "command", layoutCmd.String())
}
// Create and run commands for each window inside the loop
for i := 1; i < len(config.Windows); i++ {
createWindow(config, sessionName, i)
}
// Select the initial window and switch to the session
@ -76,12 +102,89 @@ func CreateTmuxSession(config *projectconfig.Configuration) error {
if err := selectWindowCmd.Run(); err != nil {
return err
}
log.Debug("Ran command", "command", selectWindowCmd.String())
switchSessionCmd := exec.Command("tmux", "switch-client", "-t", sessionName)
if err := switchSessionCmd.Run(); err != nil {
return err
if config.Attach {
switchSessionCmd := exec.Command("tmux", "switch-client", "-t", sessionName)
if err := switchSessionCmd.Run(); err != nil {
return err
}
log.Debug("Ran command", "command", switchSessionCmd.String())
}
}
return nil
}
func createWindow(config *projectconfig.Configuration, sessionName string, index int) error {
if index >= len(config.Windows) {
return nil
}
window := config.Windows[index]
windowName := fmt.Sprintf("%s:%d", sessionName, index+1)
// Create a new window
createWindowCmd := exec.Command("tmux", "new-window", "-t", sessionName, "-n", windowName)
if err := createWindowCmd.Run(); err != nil {
return err
}
log.Debug("Ran command", "command", createWindowCmd.String())
// Change the working directory for the window
changeDirCmd := exec.Command("tmux", "send-keys", "-t", windowName, "cd "+config.WorkingDir, "Enter")
if err := changeDirCmd.Run(); err != nil {
return err
}
log.Debug("Ran command", "command", changeDirCmd.String())
// Send commands to the window
sendCommandsCmd := exec.Command("tmux", "send-keys", "-t", windowName, strings.Join(window.Panes[0].ShellCommand, " && "), "Enter")
if err := sendCommandsCmd.Run(); err != nil {
return err
}
log.Debug("Ran command", "command", sendCommandsCmd.String())
// Rename the window to the specified name
renameWindowCmd := exec.Command("tmux", "rename-window", "-t", windowName, window.WindowName)
if err := renameWindowCmd.Run(); err != nil {
return err
}
log.Debug("Ran command", "command", renameWindowCmd.String())
// Create and run commands for additional panes in the window
for j, pane := range window.Panes[1:] {
paneName := fmt.Sprintf("%s:%d.%d", sessionName, index+1, j+2)
// Split the window horizontally
splitPaneCmd := exec.Command("tmux", "split-window", "-t", windowName, "-h", "-p", "50")
if err := splitPaneCmd.Run(); err != nil {
return err
}
log.Debug("Ran command", "command", splitPaneCmd.String())
// Select the new pane
selectPaneCmd := exec.Command("tmux", "select-pane", "-t", paneName)
if err := selectPaneCmd.Run(); err != nil {
return err
}
log.Debug("Ran command", "command", selectPaneCmd.String())
// Send commands to the pane
sendCommandsCmd := exec.Command("tmux", "send-keys", "-t", paneName, strings.Join(pane.ShellCommand, " && "), "Enter")
if err := sendCommandsCmd.Run(); err != nil {
return err
}
log.Debug("Ran command", "command", sendCommandsCmd.String())
}
if window.Layout != "" {
layoutCmd := exec.Command("tmux", "select-layout", "-t", windowName, window.Layout)
if err := layoutCmd.Run(); err != nil {
return err
}
log.Debug("Ran command", "command", layoutCmd.String())
}
return nil
}

View File

@ -8,6 +8,7 @@ import (
"github.com/xrehpicx/pee/controller"
"github.com/xrehpicx/pee/ui/filepicker"
"github.com/xrehpicx/pee/ui/table"
"github.com/xrehpicx/pee/utils"
btable "github.com/charmbracelet/bubbles/table"
"github.com/charmbracelet/log"
@ -43,7 +44,12 @@ var ListProjects = &cobra.Command{
selectedRow, action := table.Table(columns, rows)
if action == "edit" {
// print a vim command to open the config file
fmt.Println("vim", projectconfig.ProjectConfigFilePath(selectedRow[0]))
editorCommand, err := projectconfig.GetEditorCommand(selectedRow[0])
if err != nil {
editorCommand = "vim"
}
utils.EditFile(projectconfig.ProjectConfigFilePath(selectedRow[0]), editorCommand)
log.Debug("Opened config file", "file", projectconfig.ProjectConfigFilePath(selectedRow[0]))
}
if action == "open" {
ExecuteProjectEnv(selectedRow[0])
@ -71,7 +77,7 @@ func ExecuteProjectEnv(projectName string) {
return
}
projectconfig.UpdateLastOpened(projectName)
log.Info("Created tmux session", "name", config.SessionName)
log.Debug("Created tmux session", "name", config.SessionName)
}
var InitCmd = &cobra.Command{
@ -94,36 +100,57 @@ var InitCmd = &cobra.Command{
log.Error(err)
return
}
log.Info("Selected", "work_dir", selected)
log.Debug("Selected", "work_dir", selected)
sessionName := projectName
tabs := []struct {
Name string
Commands []string
}{
// Define the session configuration
workingDir := selected
windows := []projectconfig.Window{
{
Name: "editor",
Commands: []string{"echo 'command to open ur editor'"},
WindowName: "editor",
Layout: "8070,202x58,0,0[202x46,0,0,89,202x11,0,47,92]",
Panes: []projectconfig.Pane{
{
ShellCommand: []string{"echo 'command to open your editor'"},
},
{
ShellCommand: []string{"echo 'run dev server'"},
},
},
},
{
Name: "dev server",
Commands: []string{"echo 'command to start dev server'", "echo 'command to just initialize ur dependencies'"},
WindowName: "ssh windows",
ShellCommandBefore: []string{
"ls -lr",
},
Panes: []projectconfig.Pane{
{
ShellCommand: []string{"echo 'command to open your ssh windows'"},
},
{
ShellCommand: []string{"echo 'command to open your ssh windows'"},
},
},
},
{
Name: "git",
Commands: []string{"echo 'command to open ur git client (use lazygit its amazing)'"},
WindowName: "git",
Panes: []projectconfig.Pane{
{
ShellCommand: []string{"echo 'command to open your git client'"},
},
},
},
}
logger := log.NewWithOptions(os.Stderr, log.Options{
ReportCaller: false,
ReportTimestamp: false,
})
ppath, err := projectconfig.CreateProject(projectName, sessionName, selected, tabs)
ppath, err := projectconfig.CreateProject(projectName, workingDir, windows)
if err != nil {
logger.Error(err)
} else {
// logger.Info("Created Project", "path", ppath)
fmt.Println("Created Project", "setup your config by editing: ", ppath)
fmt.Println("Created Project, set up your config by editing:", ppath)
}
},
}

BIN
ui/.DS_Store vendored Normal file

Binary file not shown.

24
utils/editor.go Normal file
View File

@ -0,0 +1,24 @@
package utils
import (
"os"
"os/exec"
)
func EditFile(filePath string, editorCommand string) error {
if editorCommand == "" {
editorCommand = "vim"
}
cmd := exec.Command(editorCommand, filePath)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
return err
}
return nil
}