Skip to content

Commit

Permalink
Add scaffolding library
Browse files Browse the repository at this point in the history
  • Loading branch information
marwan-at-work committed May 28, 2021
1 parent 40604a5 commit 8617afb
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 6 deletions.
4 changes: 2 additions & 2 deletions cmd/goiterm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func main() {
{
Name: "install",
Usage: "goiterm install <go binary name>",
Description: "Installs your plugin to the iTerm app",
Description: "Installs your plugin to iTerm2",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "name",
Expand All @@ -27,7 +27,7 @@ func main() {
Action: func(c *cli.Context) error {
bin := c.Args().First()
if bin == "" {
return cli.NewExitError("must pass go binary as first argument", 1)
return cli.Exit("must pass go binary as first argument", 1)
}
appName := c.String("name")
if appName == "" {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ require (
github.com/golang/protobuf v1.4.3
github.com/gorilla/websocket v1.4.2
github.com/urfave/cli/v2 v2.3.0
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
google.golang.org/protobuf v1.25.0
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
Expand Down Expand Up @@ -51,6 +50,8 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand All @@ -74,7 +75,6 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
Expand Down
137 changes: 137 additions & 0 deletions scaffold/scaffold.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Package scaffold lets you declare a preset
// window with tabs, pains, env vars, and other
// configurations to get your multi-terminal program
// up fast.
package scaffold

import (
"fmt"

"golang.org/x/sync/errgroup"
"marwan.io/iterm2"
)

// WindowSpec specifies a window configuration.
type WindowSpec struct {
Title string
Tabs []TabSpec
}

// Run takes a window spec and creates a new iTerm2 session and uses it
// to create a new window with the given specs.
func Run(appName string, w WindowSpec) error {
app, err := iterm2.NewApp(appName)
if err != nil {
return fmt.Errorf("iterm2.NewApp: %w", err)
}
defer app.Close()
window, err := app.CreateWindow()
if err != nil {
return fmt.Errorf("app.CreateWindow: %w", err)
}
err = window.SetTitle(w.Title)
if err != nil {
return fmt.Errorf("window.SetTitle: %w", err)
}
var eg errgroup.Group
for i, ts := range w.Tabs {
var tab iterm2.Tab
if i == 0 {
tabs, err := window.ListTabs()
if err != nil {
return fmt.Errorf("window.ListTabs: %w", err)
}
tab = tabs[0]
} else {
tab, err = window.CreateTab()
if err != nil {
return fmt.Errorf("window.CreateTab: %w", err)
}
}
ts := ts
eg.Go(func() error { return createTab(tab, ts) })
}
err = eg.Wait()
if err != nil {
return fmt.Errorf("createTab: %w", err)
}
return nil
}

func createTab(tab iterm2.Tab, ts TabSpec) error {
err := tab.SetTitle(ts.Title)
if err != nil {
return fmt.Errorf("tab.SetTitle: %w", err)
}
sessions, err := tab.ListSessions()
if err != nil {
return fmt.Errorf("tab.ListSessions: %w", err)
}
sesh := sessions[0]
err = sesh.SendText(fmt.Sprintf("cd %v\n", ts.Dir))
if err != nil {
return fmt.Errorf("error changing directory: %w", err)
}
for _, e := range ts.Env.GetEnv() {
err = sesh.SendText(fmt.Sprintf("export %s\n", e))
if err != nil {
return fmt.Errorf("error exporting env: %w", err)
}
}
if ts.OnCreate != nil {
err = sesh.SendText(ts.OnCreate(sesh))
if err != nil {
return fmt.Errorf("ts.OnCreate: %w", err)
}
}
if ts.pane != nil {
pane, err := sesh.SplitPane(iterm2.SplitPaneOptions{
Vertical: true,
})
if err != nil {
return fmt.Errorf("sesh.SplitPane: %w", err)
}
if ts.pane.OnCreate != nil {
err = ts.pane.OnCreate(pane)
if err != nil {
return fmt.Errorf("pane.OnCreate: %w", err)
}
}
}
return nil
}

// EnvGetter defines an interface that can be used
// to retrieve environment variables to be set on
// an iTerm2 session. The reason for this interface
// is that so you can specify dynamic tabs such as tabs
// being read from a file instead of having to statically
// create them. If you want to statically pass a set of env vars to
// a TabSpec, just use scaffold.Env which is a slice of strings that
// implements this interface
type EnvGetter interface {
GetEnv() []string
}

// Env is a slice of environment variables
// to be exported in an iTerm2 session
type Env []string

// GetEnv implements EnvGetter
func (e Env) GetEnv() []string {
return []string(e)
}

// TabSpec specifies a main tab in your iTerm2 window
type TabSpec struct {
Title string
Dir string
Env EnvGetter
OnCreate func(s iterm2.Session) string
pane *PaneSpec
}

// PaneSpec specifies a vertical right pane within a tab
type PaneSpec struct {
OnCreate func(s iterm2.Session) error
}
4 changes: 2 additions & 2 deletions window.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func (w *window) ListTabs() ([]Tab, error) {
}

func (w *window) SetTitle(s string) error {
w.c.Call(&api.ClientOriginatedMessage{
_, err := w.c.Call(&api.ClientOriginatedMessage{
Submessage: &api.ClientOriginatedMessage_InvokeFunctionRequest{
InvokeFunctionRequest: &api.InvokeFunctionRequest{
Invocation: str(fmt.Sprintf(`iterm2.set_title(title: "%s")`, s)),
Expand All @@ -81,5 +81,5 @@ func (w *window) SetTitle(s string) error {
},
},
})
return nil
return err
}

0 comments on commit 8617afb

Please sign in to comment.