git-project-manager/cmd/project_open.go

191 lines
4.1 KiB
Go
Raw Normal View History

2023-12-12 21:30:33 +00:00
package cmd
import (
"errors"
"os"
"os/exec"
"path/filepath"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var projectOpenCmd = &cobra.Command{
Use: "open [fuzzy alias search]",
Short: "Open project in your IDE",
Aliases: []string{"goto", "cd"},
Args: cobra.OnlyValidArgs,
2023-12-12 21:30:33 +00:00
ValidArgsFunction: validAliasesFunc,
Long: projOpenCmdLong,
Run: projectOpenCmdRun,
}
var knownEditors = []string{
"vim",
"emacs",
"code",
"codium",
"/Applications/GoLand.app/Contents/MacOS/goland",
}
var entrypointFiles = []string{
"README.md",
"README",
"main.go",
"main.py",
"app.py",
"index.php",
"server.php",
"index.js",
"app.js",
"server.js",
"app.ts",
"main.c",
"main.cpp",
}
func projectOpenCmdRun(cmd *cobra.Command, args []string) {
// Find an editor
editor := findEditor()
if editor == "" {
plog.Fatal("No usable editor found")
}
gitlabs := viper.GetStringSlice("project.gitlabs")
fzfOpts := &fzfProjectOpts{
Ctx: cmd.Context(),
Search: searchStringFromArgs(args),
Gitlabs: gitlabs,
}
project := fzfCwdOrSearchProjectAliases(fzfOpts)
2023-12-12 21:30:33 +00:00
if project == nil {
plog.Fatal("No project to open, nothing to do")
2023-12-12 21:30:33 +00:00
}
// Check the project
2024-01-15 21:02:15 +00:00
path := projectCache.GetProjectPath(project)
2023-12-12 21:30:33 +00:00
if _, err := os.Stat(path); err != nil {
plog.Fatal("Unable to open project", plog.Args("error", err))
}
// Open the project with the editor
file := getEntrypointFile(path)
openEditor(editor, path+"/"+file)
}
func openEditor(editor string, path string) {
// Compile arguments
args := make([]string, 0, 1)
if conf.Editor.OpenFlags != "" {
args = append(args, conf.Editor.OpenFlags)
}
args = append(args, path)
// Launch editor
cmd := exec.CommandContext(rootCmd.Context(), editor, args...)
cmd.Dir = filepath.Dir(path)
2023-12-13 18:28:07 +00:00
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
2023-12-12 21:30:33 +00:00
if err := cmd.Run(); err != nil {
plog.Error("Failed to open project", plog.Args(
"error", err.Error(),
"command", cmd.String(),
))
}
cmd.Wait()
}
func getEntrypointFile(projectPath string) string {
if err := os.Chdir(projectPath); err != nil {
return ""
} else if conf.Editor.OpenDirectory {
2023-12-29 14:22:19 +00:00
return "."
2023-12-12 21:30:33 +00:00
}
// Search list of well-known main files
2023-12-12 21:30:33 +00:00
for _, f := range entrypointFiles {
if _, err := os.Stat(f); err == nil {
return f
}
}
2023-12-12 21:30:33 +00:00
return ""
}
func findEditor() string {
var editor string
var err error
// First try configured editor
if conf.Editor.Binary != "" {
editor, err = getEditor(conf.Editor.Binary)
if err != nil || editor == "" {
plog.Error("Configured editor is not usable, finding others", plog.Args(
"error", err,
))
}
}
// Then try to find a known editor
if editor == "" || err != nil {
conf.Editor.OpenFlags = ""
for _, e := range knownEditors {
path, err := getEditor(e)
if err == nil && path != "" {
editor = path
break
}
}
}
plog.Debug("Editor found for open", plog.Args(
"editor", editor,
"command", editor+" "+conf.Editor.OpenFlags,
))
return editor
}
func getEditor(editor string) (string, error) {
path, err := getEditorPath(editor)
if path != "" && err == nil {
if !isEditorExecutable(path) {
err = errors.New("Editor is not executable")
}
}
return path, err
}
func getEditorPath(editor string) (string, error) {
// Check path first
if editor[0] != '/' {
editor, _ = exec.LookPath(editor)
}
return resolvePath(editor)
}
func isEditorExecutable(editor string) bool {
var canExec bool
stat, err := os.Stat(editor)
if err == nil && (stat.Mode()&0444 != 0 && stat.Mode()&0111 != 0) {
canExec = true
}
return canExec
}
func init() {
projectCmd.AddCommand(projectOpenCmd)
projectOpenCmd.PersistentFlags().String("displayName", "", "Set display name of editor (meant for config file)")
projectOpenCmd.PersistentFlags().String("binary", "", "Path to editor binary")
projectOpenCmd.PersistentFlags().String("openFlags", "", "Optional flags when opening project (e.g. --reuse-window)")
viper.BindPFlag("editor.displayName", projectOpenCmd.Flag("displayName"))
viper.BindPFlag("editor.binary", projectOpenCmd.Flag("binary"))
viper.BindPFlag("editor.openFlags", projectOpenCmd.Flag("openFlags"))
}