Skip to content

Commit

Permalink
fix: clearer execution context
Browse files Browse the repository at this point in the history
  • Loading branch information
cfoust committed Jul 25, 2024
1 parent d0af0af commit 090b5a8
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 55 deletions.
13 changes: 12 additions & 1 deletion docs/src/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

`cy` was designed to be as configurable as possible. Users of `cy` provide a configuration written in [Janet](https://janet-lang.org/) that uses `cy`'s [API](/api.md) to define key bindings, set parameters, and set up `cy` in any way they like.

Janet is a fun, embeddable [Lisp-like](https://en.wikipedia.org/wiki/Lisp_(programming_language) language that is easy to learn. If you are new to Janet, I recommend starting out with its [documentation](https://janet-lang.org/docs/syntax.html) and Ian Henry's fantastic [_Janet for Mortals_](https://janet.guide/).
Janet is a fun, embeddable [Lisp-like](https://en.wikipedia.org/wiki/Lisp_(programming_language)) language that is easy to learn. If you are new to Janet, I recommend starting out with its [documentation](https://janet-lang.org/docs/syntax.html) and Ian Henry's fantastic [_Janet for Mortals_](https://janet.guide/).

Janet looks like this:

Expand Down Expand Up @@ -49,3 +49,14 @@ An example configuration that uses functionality from this API is shown below. Y
# Bind a key sequence to this function
(key/bind :root ["ctrl+a" "g"] toast-pane-path)
```

### Execution context

The Janet code executed in `cy` can be executed in two different contexts:

- **The context of the server:** This is the context in which your configuration file is run, because when the `cy` server starts up, there are no clients.
- **The context of a client:** When a client invokes an [action](/keybindings.md#actions) or types a keybinding sequence, the executed Janet code can see what client ran the action and respond appropriately.

Because of this, _some API functionality can only be used in a keybinding or action._ This is because some kinds of state, such as the state that `(viewport/*)` family of functions uses to work, only makes sense in the context of a user.

This is a confusing aspect of `cy` that I plan to improve over time, such as by letting you execute custom code in the context of a client whenever one connects.
17 changes: 8 additions & 9 deletions pkg/cy/api/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package api

import (
"context"
"fmt"
"math/rand"

"github.com/cfoust/cy/pkg/anim"
Expand Down Expand Up @@ -32,17 +31,17 @@ type FuzzyParams struct {

func (i *InputModule) Find(
ctx context.Context,
user interface{},
context interface{},
choices *janet.Value,
named *janet.Named[FuzzyParams],
) (interface{}, error) {
defer choices.Free()

params := named.Values()

client, ok := user.(Client)
if !ok {
return nil, fmt.Errorf("missing client context")
client, err := getClient(context)
if err != nil {
return nil, err
}

options, err := fuzzy.UnmarshalOptions(choices)
Expand Down Expand Up @@ -154,15 +153,15 @@ type TextParams struct {

func (i *InputModule) Text(
ctx context.Context,
user interface{},
context interface{},
prompt string,
named *janet.Named[TextParams],
) (interface{}, error) {
params := named.Values()

client, ok := user.(Client)
if !ok {
return nil, fmt.Errorf("missing client context")
client, err := getClient(context)
if err != nil {
return nil, err
}

outerLayers := client.OuterLayers()
Expand Down
10 changes: 5 additions & 5 deletions pkg/cy/api/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,11 @@ func (k *KeyModule) Get(target *janet.Value) ([]Binding, error) {
return binds, nil
}

func (k *KeyModule) Current(user interface{}) []Binding {
client, ok := user.(Client)
if !ok {
return nil
func (k *KeyModule) Current(context interface{}) ([]Binding, error) {
client, err := getClient(context)
if err != nil {
return nil, err
}

return client.Binds()
return client.Binds(), nil
}
13 changes: 13 additions & 0 deletions pkg/cy/api/module.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package api

import (
"fmt"

"github.com/cfoust/cy/pkg/frames"
"github.com/cfoust/cy/pkg/mux/screen"
"github.com/cfoust/cy/pkg/mux/screen/toasts"
Expand Down Expand Up @@ -28,3 +30,14 @@ type Server interface {
ExecuteJanet(path string) error
Log(level zerolog.Level, message string)
}

func getClient(context interface{}) (Client, error) {
client, ok := context.(Client)
if !ok {
return nil, fmt.Errorf(
"this functionality can only be accessed in a keybinding or action, see https://cfoust.github.io/cy/configuration.html#execution-context for more information",
)
}

return client, nil
}
6 changes: 3 additions & 3 deletions pkg/cy/api/msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ func (m *MsgModule) Toast(context interface{}, level *janet.Value, message strin
return err
}

client, ok := context.(Client)
if !ok {
return fmt.Errorf("unable to detect client")
client, err := getClient(context)
if err != nil {
return err
}

client.Toast(toasts.Toast{
Expand Down
20 changes: 9 additions & 11 deletions pkg/cy/api/pane.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package api

import (
"fmt"

"github.com/cfoust/cy/pkg/janet"
"github.com/cfoust/cy/pkg/mux/screen/tree"
)
Expand All @@ -14,9 +12,9 @@ type PaneModule struct {
func (p *PaneModule) Attach(context interface{}, id *janet.Value) error {
defer id.Free()

client, ok := context.(Client)
if !ok {
return fmt.Errorf("missing client context")
client, err := getClient(context)
if err != nil {
return err
}

pane, err := resolvePane(p.Tree, id)
Expand All @@ -28,18 +26,18 @@ func (p *PaneModule) Attach(context interface{}, id *janet.Value) error {
}

func (p *PaneModule) HistoryForward(context interface{}) error {
client, ok := context.(Client)
if !ok {
return fmt.Errorf("missing client context")
client, err := getClient(context)
if err != nil {
return err
}

return client.HistoryForward()
}

func (p *PaneModule) HistoryBackward(context interface{}) error {
client, ok := context.(Client)
if !ok {
return fmt.Errorf("missing client context")
client, err := getClient(context)
if err != nil {
return err
}

return client.HistoryBackward()
Expand Down
16 changes: 7 additions & 9 deletions pkg/cy/api/params.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package api

import (
"fmt"

"github.com/cfoust/cy/pkg/janet"
"github.com/cfoust/cy/pkg/mux/screen/tree"
"github.com/cfoust/cy/pkg/params"
Expand Down Expand Up @@ -43,9 +41,9 @@ func (p *ParamModule) Get(
}

if params.Target == nil || isClientTarget(params.Target) {
client, ok := context.(Client)
if !ok {
return nil, fmt.Errorf("missing client context")
client, err := getClient(context)
if err != nil {
return nil, err
}

value, _ := client.Get(string(keyword))
Expand Down Expand Up @@ -78,11 +76,11 @@ func (p *ParamModule) Set(

var params *params.Parameters
if isClientTarget(target) {
if client, ok := context.(Client); ok {
params = client.Params()
} else {
return fmt.Errorf("missing client context")
client, err := getClient(context)
if err != nil {
return err
}
params = client.Params()
} else {
node, err := resolveNode(p.Tree, target)
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions pkg/cy/api/replay.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ type ReplayModule struct {
}

func (m *ReplayModule) send(context interface{}, msg taro.Msg) error {
client, ok := context.(Client)
if !ok {
return fmt.Errorf("no client could be inferred")
client, err := getClient(context)
if err != nil {
return err
}

node := client.Node()
Expand Down
33 changes: 19 additions & 14 deletions pkg/cy/api/viewport.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package api

import (
"fmt"

"github.com/cfoust/cy/pkg/anim"
"github.com/cfoust/cy/pkg/frames"
"github.com/cfoust/cy/pkg/geom"
Expand All @@ -9,34 +11,37 @@ import (
type ViewportModule struct {
}

func (c *ViewportModule) Size(context interface{}) *geom.Vec2 {
client, ok := context.(Client)
if !ok {
return nil
func (c *ViewportModule) Size(context interface{}) (*geom.Vec2, error) {
client, err := getClient(context)
if err != nil {
return nil, err
}

size := client.Margins().Size()
return &size
return &size, nil
}

func (c *ViewportModule) SetSize(context interface{}, size geom.Size) {
client, ok := context.(Client)
if !ok {
return
func (c *ViewportModule) SetSize(context interface{}, size geom.Size) error {
client, err := getClient(context)
if err != nil {
return err
}
client.Margins().SetSize(size)
return nil
}

func (c *ViewportModule) SetFrame(context interface{}, name string) {
client, ok := context.(Client)
if !ok {
return
func (c *ViewportModule) SetFrame(context interface{}, name string) error {
client, err := getClient(context)
if err != nil {
return err
}

frame, ok := frames.Frames[name]
if !ok {
return
return fmt.Errorf("could not find frame '%s'", name)
}
client.Frame().Set(frame)
return nil
}

func (c *ViewportModule) GetFrames(context interface{}) []string {
Expand Down

0 comments on commit 090b5a8

Please sign in to comment.