diff --git a/cmd/docs/gen-api.janet b/cmd/docs/gen-api.janet new file mode 100644 index 00000000..f58207c9 --- /dev/null +++ b/cmd/docs/gen-api.janet @@ -0,0 +1,36 @@ +(defn- all-entries + [&opt env] + (default env root-env) + (sort (pairs env))) + +(var envs @[]) + +(var env (cy/env)) +(while (not= nil env) + (array/push envs (seq [x :in (all-entries env) + :let [[k entry] x] + :when (symbol? k) + :when (get entry :doc) + :when (not (get entry :private))] + x)) + (set env (getproto env))) + +# Remove cy/doc and cy/env +(set envs (array/slice envs 2 -2)) + +(var symbols @[]) +(each env envs + (array/concat symbols env)) + +# Format and send all the symbols upwards +(each [name {:macro macro :doc doc :source-map [file line]}] symbols + (default macro false) + (cy/doc + (string name) + doc + (if (> (length file) 0) + (string + "https://github.com/cfoust/cy/blob/main/pkg/cy/cy-boot.janet#L" + line) + "") + macro)) diff --git a/cmd/docs/main.go b/cmd/docs/main.go new file mode 100644 index 00000000..535188f0 --- /dev/null +++ b/cmd/docs/main.go @@ -0,0 +1,59 @@ +package main + +import ( + "context" + "fmt" + + "github.com/cfoust/cy/pkg/cy" + "github.com/cfoust/cy/pkg/janet" + + "github.com/alecthomas/kong" +) + +import _ "embed" + +//go:embed gen-api.janet +var GEN_API string + +var CLI struct { + API struct { + } `cmd:"" help:"Generate Markdown for the full API."` +} + +func main() { + ctx := kong.Parse(&CLI, + kong.Name("cy-docs"), + kong.Description("generate documentation for cy programmatically"), + kong.UsageOnError(), + kong.ConfigureHelp(kong.HelpOptions{ + Compact: true, + Summary: true, + })) + + switch ctx.Command() { + case "api": + ctx := context.Background() + cy, err := cy.Start(ctx, cy.Options{}) + if err != nil { + panic(err) + } + + err = cy.Callback("cy/env", func() *janet.Value { + return cy.Env().Value + }) + if err != nil { + panic(err) + } + + cy.Callback("cy/doc", func(name string, docstring string, link string, macro bool) { + fmt.Printf("%s %s %t\n", name, docstring, macro) + }) + + err = cy.Execute(ctx, GEN_API) + if err != nil { + panic(err) + } + default: + panic(ctx.Command()) + } +} diff --git a/docs/src/groups-and-panes.md b/docs/src/groups-and-panes.md index e4fd5127..03936da9 100644 --- a/docs/src/groups-and-panes.md +++ b/docs/src/groups-and-panes.md @@ -6,10 +6,6 @@ A **pane** refers to a terminal window with a process running inside it, typically a shell or text editor. Every pane has a name. Panes in `cy` work exactly the same way that they do in `tmux`: you can have arbitrarily many panes open and switch between them on demand. -For the time being, `cy` only allows each user to view and interact with one pane at once, which is centered inside of the **viewport** and automatically resized as that user changes the bounds of their terminal. - -By default, `cy` only allows a pane to expand to a width of 80 columns or the width of your terminal window, whichever is smaller, but this is configurable. This is practical because `cy` makes it easy to switch between panes. - ## Groups Every pane `cy` belongs to a **group**. A group has a name and children, which consist of either panes or other groups. diff --git a/docs/src/installation.md b/docs/src/installation.md index 22bbbd0d..d1c75993 100644 --- a/docs/src/installation.md +++ b/docs/src/installation.md @@ -4,7 +4,7 @@ You can install `cy` using pre-compiled binaries or from source. ### Binaries -Download the latest version of `cy` on [the releases page](https://github.com/cfoust/cy/releases) and extract the archive that corresponds to your operating system into your `$PATH`. +Download the latest version of `cy` on [the releases page](https://github.com/cfoust/cy/releases) and extract the archive that corresponds to your operating system and architecture into your `$PATH`. ### From source diff --git a/pkg/cy/cy-boot.janet b/pkg/cy/cy-boot.janet index 309005db..b0692938 100644 --- a/pkg/cy/cy-boot.janet +++ b/pkg/cy/cy-boot.janet @@ -9,7 +9,7 @@ "register an action" [name docstring & body] ~(upscope - (def ,name (fn ,name [&] ,;body)) + (defn ,name ,docstring [&] ,;body) (,array/push actions [,docstring ,name]))) (key/def diff --git a/pkg/cy/janet.go b/pkg/cy/janet.go index 0782cd21..75f12576 100644 --- a/pkg/cy/janet.go +++ b/pkg/cy/janet.go @@ -21,7 +21,7 @@ var CY_BOOT_FILE []byte // execute runs some Janet code on behalf of the Client. This is only used in testing. func (c *Client) execute(code string) error { - return c.cy.janet.ExecuteCall(c.Ctx(), c, janet.Call{ + return c.cy.ExecuteCall(c.Ctx(), c, janet.Call{ Code: []byte(code), Options: janet.DEFAULT_CALL_OPTIONS, }) @@ -52,7 +52,7 @@ func resolveLevel(level *janet.Value) (toasts.ToastLevel, error) { return toasts.ToastLevelError, fmt.Errorf("you must provide one of :info, :warn, or :error") } -func (c *Cy) initJanet(ctx context.Context, dataDir string) (*janet.VM, error) { +func (c *Cy) initJanet(ctx context.Context) (*janet.VM, error) { vm, err := janet.New(ctx) if err != nil { return nil, err diff --git a/pkg/cy/module.go b/pkg/cy/module.go index ca05e855..1a92ed0c 100644 --- a/pkg/cy/module.go +++ b/pkg/cy/module.go @@ -44,8 +44,7 @@ type historyEvent struct { type Cy struct { util.Lifetime deadlock.RWMutex - - janet *janet.VM + *janet.VM nextClientID atomic.Int32 @@ -79,7 +78,7 @@ type Cy struct { } func (c *Cy) loadUserConfig(ctx context.Context) { - err := c.janet.ExecuteFile(ctx, c.configPath) + err := c.ExecuteFile(ctx, c.configPath) if err != nil { c.log.Error().Err(err).Msg("failed to execute config") message := fmt.Sprintf("an error occurred while loading %s: %s", c.configPath, err.Error()) @@ -223,16 +222,18 @@ func Start(ctx context.Context, options Options) (*Cy, error) { go cy.pollInteractions(cy.Ctx(), cy.lastWrite, cy.writes) go cy.pollInteractions(cy.Ctx(), cy.lastVisit, cy.visits) - replayable, _ := cmd.New( - cy.Ctx(), - stream.CmdOptions{ - Command: "/bin/bash", - }, - options.DataDir, - replayBinds, - ) - - t.Root().NewPane(cy.Ctx(), replayable) + if len(options.Shell) > 0 { + replayable, _ := cmd.New( + cy.Ctx(), + stream.CmdOptions{ + Command: "/bin/bash", + }, + options.DataDir, + replayBinds, + ) + + t.Root().NewPane(cy.Ctx(), replayable) + } logs := stream.NewReader() terminal := screen.NewTerminal(cy.Ctx(), logs, geom.DEFAULT_SIZE) @@ -242,12 +243,12 @@ func Start(ctx context.Context, options Options) (*Cy, error) { consoleWriter := zerolog.ConsoleWriter{Out: logs.Writer(), TimeFormat: time.RFC3339} cy.log = log.Output(zerolog.MultiLevelWriter(consoleWriter, os.Stdout)) - vm, err := cy.initJanet(ctx, options.DataDir) + vm, err := cy.initJanet(ctx) if err != nil { return nil, err } - cy.janet = vm + cy.VM = vm if len(options.Config) != 0 { cy.configPath = options.Config diff --git a/pkg/janet/interop.go b/pkg/janet/interop.go index 96891b31..3278b6f5 100644 --- a/pkg/janet/interop.go +++ b/pkg/janet/interop.go @@ -105,12 +105,15 @@ func isErrorType(type_ reflect.Type) bool { } func handleReturn(v *VM, value reflect.Value) (C.Janet, error) { - isPointer := value.Kind() == reflect.Pointer - if isPointer { + if value.Kind() == reflect.Pointer { if value.IsNil() { return C.janet_wrap_nil(), nil } + if v, ok := value.Interface().(*Value); ok { + return v.janet, nil + } + value = value.Elem() } diff --git a/pkg/janet/module.go b/pkg/janet/module.go index 438c592f..4290bd54 100644 --- a/pkg/janet/module.go +++ b/pkg/janet/module.go @@ -41,6 +41,10 @@ func deInitJanet() { C.janet_deinit() } +func (v *VM) Env() *Table { + return v.env +} + // Wait for code calls and process them. func (v *VM) poll(ctx context.Context, ready chan bool) { // All Janet state is thread-local, so we explicitly want to execute diff --git a/pkg/janet/translate.go b/pkg/janet/translate.go index 220470cb..09caaf69 100644 --- a/pkg/janet/translate.go +++ b/pkg/janet/translate.go @@ -65,7 +65,7 @@ func isValidType(type_ reflect.Type) bool { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Float64, reflect.Bool, reflect.String: return true case reflect.Pointer: - if _, ok := reflect.New(type_.Elem()).Interface().(*Function); ok { + if _, ok := reflect.New(type_.Elem()).Interface().(*Value); ok { return true } @@ -340,7 +340,7 @@ func (v *VM) unmarshal(source C.Janet, dest interface{}) error { ptr := reflect.New(type_.Elem()) err := v.unmarshal(source, ptr.Interface()) if err != nil { - return err + return err } value.Set(ptr) } else {