mirror of https://github.com/xrehpicx/pee.git
feat: supports panes and tmux layouts
This commit is contained in:
parent
3e2f137700
commit
17604df9da
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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,11 +102,88 @@ func CreateTmuxSession(config *projectconfig.Configuration) error {
|
|||
if err := selectWindowCmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debug("Ran command", "command", selectWindowCmd.String())
|
||||
|
||||
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
|
||||
|
|
|
@ -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'"},
|
||||
},
|
||||
{
|
||||
Name: "dev server",
|
||||
Commands: []string{"echo 'command to start dev server'", "echo 'command to just initialize ur dependencies'"},
|
||||
ShellCommand: []string{"echo 'run dev server'"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "git",
|
||||
Commands: []string{"echo 'command to open ur git client (use lazygit its amazing)'"},
|
||||
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'"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
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)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue