Skip to content

Commit

Permalink
feat: unbreakable API references
Browse files Browse the repository at this point in the history
  • Loading branch information
cfoust committed Jun 26, 2024
1 parent bca68be commit 35786db
Show file tree
Hide file tree
Showing 9 changed files with 67 additions and 31 deletions.
4 changes: 2 additions & 2 deletions docs/book.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ description = "A next-generation terminal multiplexer that records everything yo
authors = ["Caleb Foust"]

[output.html]
default-theme = "light"
preferred-dark-theme = "ayu"
default-theme = "navy"
preferred-dark-theme = "navy"
site-url = "/cy/"
git-repository-url = "https://github.com/cfoust/cy"
additional-css = ["./theme/asciinema-player.css"]
Expand Down
36 changes: 36 additions & 0 deletions docs/gendoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

GENDOC_REGEX = re.compile("{{gendoc (.+)}}")
KEYS_REGEX = re.compile(r"{{keys (.+)}}")
API_REGEX = re.compile(r"{{api ([a-z0-9/]+)}}")


class Symbol(NamedTuple):
Expand Down Expand Up @@ -186,10 +187,17 @@ def render_keys(bindings: List[Binding], args: List[str]) -> str:
binding['Function'] = symbol_lookup[func]
bindings.append(Binding(**binding))

errors: int = 0
def report_error(chapter, start, end, msg):
global errors
errors += 1
print(f"{chapter['name']}:{start}{end}: {msg}", file=sys.stderr)

def transform_chapter(chapter) -> None:
replace = []

content = chapter['content']

for ref in GENDOC_REGEX.finditer(content):
command = ref.group(1)
if len(command) == 0:
Expand Down Expand Up @@ -227,6 +235,30 @@ def transform_chapter(chapter) -> None:
)
)

for ref in API_REGEX.finditer(content):
name = ref.group(1)
if len(name) == 0:
continue

if not name in symbol_lookup:
report_error(
chapter,
ref.start(0),
ref.end(0),
f"missing symbol: {name}",
)
continue

symbol = symbol_lookup[name]

replace.append(
(
ref.start(0),
ref.end(0),
render_symbol_link(symbol),
)
)

for start, end, text in reversed(replace):
content = content[:start] + text + content[end:]

Expand All @@ -244,4 +276,8 @@ def transform_chapter(chapter) -> None:

transform_chapter(section['Chapter'])

if errors > 0:
print(f"{errors} error(s) while preprocessing")
exit(1)

print(json.dumps(book))
4 changes: 2 additions & 2 deletions docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Janet code executed with `cy` can also access everything from [Janet's standard
Several API functions related to binding keys return a `Binding`. A `Binding` table represents a single key sequence and its associated function. Each table has the following properties:

- `:node`: the [NodeID](api.md#nodeid) where the binding is defined.
- `:sequence`: a list of strings representing the key sequence that will execute this action. If the original call to [`(key/bind)`](api.md#keybind) used a [regex](keybindings.md#regexes), it will be returned as a string with a `re:` prefix.
- `:sequence`: a list of strings representing the key sequence that will execute this action. If the original call to {{api key/bind}} used a [regex](keybindings.md#regexes), it will be returned as a string with a `re:` prefix.
- `:function`: the Janet function that will be called when this sequence is executed.

For example:
Expand All @@ -29,6 +29,6 @@ For example:
Many API functions have a parameter of type `NodeID`, which can be one of two values:

- `:root` which is a short way of referring to `cy`'s top-level group.
- An integer that refers to a node in `cy`'s [node tree](groups-and-panes.md#the-node-tree). You cannot infer these yourself, but they are returned from API functions like [`(pane/current)`](api.md#panecurrent) and [`(group/children)`](api.md#groupchildren).
- An integer that refers to a node in `cy`'s [node tree](groups-and-panes.md#the-node-tree). You cannot infer these yourself, but they are returned from API functions like {{api pane/current}} and {{api group/children}}.

{{gendoc api}}
6 changes: 3 additions & 3 deletions docs/src/default-keys.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ These bindings apply everywhere and can always be invoked.

### Prefixed

All of the bindings in this table are prefixed by <kbd>ctrl+a</kbd> by default. You can change the prefix for cy's bindings using [`(key/remap)`](api.md#keyremap).
All of the bindings in this table are prefixed by <kbd>ctrl+a</kbd> by default. You can change the prefix for cy's bindings using {{api key/remap}}.

#### General

Expand All @@ -34,7 +34,7 @@ These bindings are not prefixed by <kbd>ctrl+a</kbd>.

## Fuzzy finding

[`(input/find)`](api.md#inputfind) has several key bindings that are not yet configurable, but are worth documenting.
{{api input/find}} has several key bindings that are not yet configurable, but are worth documenting.

| Sequence | Description |
| ------------------------------------ | ----------------------------------- |
Expand All @@ -45,7 +45,7 @@ These bindings are not prefixed by <kbd>ctrl+a</kbd>.

## Replay mode

The actions found in the tables below are only valid in a pane that is in replay mode. Replay mode uses two isolated binding scopes that can be accessed by providing `:time` (for time mode) or `:copy` (for copy mode) to a [`(key/bind)`](api.md#keybind) call:
The actions found in the tables below are only valid in a pane that is in replay mode. Replay mode uses two isolated binding scopes that can be accessed by providing `:time` (for time mode) or `:copy` (for copy mode) to a {{api key/bind}} call:

```janet
(key/bind :time ["ctrl+b"] (fn [&] (do-something)))
Expand Down
16 changes: 8 additions & 8 deletions docs/src/fuzzy-finding.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

{{story cast input/find/full-bottom}}

Simple, fast, and configurable fuzzy finding is one of `cy`'s most important features. `cy` provides a purpose-built fuzzy finder (similar to [fzf](https://github.com/junegunn/fzf)) in the form of [`(input/find)`](./api.md#inputfind), which is a function available in [the API](./api.md#inputfind).
Simple, fast, and configurable fuzzy finding is one of `cy`'s most important features. `cy` provides a purpose-built fuzzy finder (similar to [fzf](https://github.com/junegunn/fzf)) in the form of {{api input/find}}, which is a function available in [the API](./api.md#inputfind).

## Choosing a string from a list

In its simplest form, [`(input/find)`](api.md#inputfind) takes a single parameter: a [Janet array](https://janet-lang.org/docs/data_structures/arrays.html) of strings that it will present to the user and from which they can choose a single option:
In its simplest form, {{api input/find}} takes a single parameter: a [Janet array](https://janet-lang.org/docs/data_structures/arrays.html) of strings that it will present to the user and from which they can choose a single option:

```janet
(input/find @["one" "two" "three"])
Expand All @@ -16,7 +16,7 @@ By default, the background will be animated with one of `cy`'s [animations](./an

## Choosing an arbitrary value from a list

[`(input/find)`](api.md#inputfind) also allows you to ask the user to choose from a list of items, each of which has an underlying Janet value that is returned instead of the string value that the user filters.
{{api input/find}} also allows you to ask the user to choose from a list of items, each of which has an underlying Janet value that is returned instead of the string value that the user filters.

You do this by providing a Janet array of tuples, each of which has two elements:

Expand All @@ -30,13 +30,13 @@ You do this by providing a Janet array of tuples, each of which has two elements
["three" 3]])
```

If the user chooses `"one"`, [`(input/find)`](api.md#inputfind) will return `1`.
If the user chooses `"one"`, {{api input/find}} will return `1`.

## Filtering tabular data

{{story cast input/find/table/full-bottom}}

It is sometimes handy to be able to have the user choose from a row in a table rather than a single line of text. [`(input/find)`](api.md#inputfind) allows you to provide tabular data in addition to titles for each column in the form of tuples.
It is sometimes handy to be able to have the user choose from a row in a table rather than a single line of text. {{api input/find}} allows you to provide tabular data in addition to titles for each column in the form of tuples.

```janet
(input/find
Expand All @@ -51,13 +51,13 @@ It is sometimes handy to be able to have the user choose from a row in a table r

## Choosing with previews

Where [`(input/find)`](api.md#inputfind) really shines, however, is in its ability to show a preview window for each option, which is conceptually similar to `fzf`'s `--preview` command line flag. [`(input/find)`](api.md#inputfind) can preview three different types of content:
Where {{api input/find}} really shines, however, is in its ability to show a preview window for each option, which is conceptually similar to `fzf`'s `--preview` command line flag. {{api input/find}} can preview three different types of content:

- **Panes:** Show the current state of a pane in `cy`'s [node tree](./groups-and-panes.md#the-node-tree). This is the live view of a pane, regardless of how many other clients are interacting with it or what is happening on the screen.
- **`.borg` files:** Show a moment in time in a `.borg` file.
- **Text** Render some text.

Options with previews are passed to [`(input/find)`](api.md#inputfind) as Janet tuples with three elements:
Options with previews are passed to {{api input/find}} as Janet tuples with three elements:

1. The text (or columns) that the user will filter against
1. A Janet [table](https://janet-lang.org/docs/data_structures/tables.html) describing how this option should be previewed
Expand All @@ -76,4 +76,4 @@ Here are some examples:
])
```

[`(input/find)`](api.md#inputfind) is used extensively in `cy`'s [default startup script](https://github.com/cfoust/cy/blob/main/pkg/cy/cy-boot.janet). You can find several idiomatic examples of its usage there.
{{api input/find}} is used extensively in `cy`'s [default startup script](https://github.com/cfoust/cy/blob/main/pkg/cy/cy-boot.janet). You can find several idiomatic examples of its usage there.
4 changes: 2 additions & 2 deletions docs/src/groups-and-panes.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Every pane `cy` belongs to a **group**. A group has a name and children, which c
Groups also have two unique features:

- **Key bindings:** You may define key bindings that will only activate when you type that sequence while attached to any descendant of that group.
- **Parameters:** A key-value store that can be interacted with using `(cy/get)` and `(cy/set)`. Parameters are used both [to configure aspects of `cy`](./parameters.md) and also to create any functionality you desire by storing state in `cy`'s tree.
- **Parameters:** A key-value store that can be interacted with using {{api cy/get}} and {{api cy/set}}. Parameters are used both [to configure aspects of `cy`](./parameters.md) and also to create any functionality you desire by storing state in `cy`'s tree.

## The node tree

Expand All @@ -38,7 +38,7 @@ Instead, each node is permanently assigned a unique identifier (which is just an
`cy`'s flexibility comes from the way key bindings and parameters interact:

- **Key bindings** are inherited down the tree, but can be overridden by descendant groups.
- **Parameters** work the same way: [`(cy/get)`](api.md#cyget) will get the value of a parameter from the closest parent group that defines it.
- **Parameters** work the same way: {{api cy/get}} will get the value of a parameter from the closest parent group that defines it.

Imagine that you are attached to `/my-project/group-2/pane-3` in the example above:

Expand Down
22 changes: 11 additions & 11 deletions docs/src/keybindings.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Keybindings

In `cy`, keybindings consist of a sequence of one or more keys that execute Janet code when you type them. You define new key sequences with the [`(key/bind)`](api.md#keybind) function.
In `cy`, keybindings consist of a sequence of one or more keys that execute Janet code when you type them. You define new key sequences with the {{api key/bind}} function.

For example:

Expand All @@ -10,23 +10,23 @@ For example:

This tells `cy` that whenever you type `ctrl+l` it should show a toast with the text "you hit ctrl+l".

The [`(key/bind)`](api.md#keybind) function takes three parameters:
The {{api key/bind}} function takes three parameters:

1. **A scope**: The circumstances in which this binding should apply, such as a [group](./groups-and-panes.md) or mode (e.g. `:time`). In this case we use the `:root` [keyword](https://janet-lang.org/docs/strings.html), which is a handy way of saying this binding should apply everywhere.
1. **A key sequence**: A Janet [tuple](https://janet-lang.org/docs/data_structures/tuples.html) that indicates the keys that must be typed for the callback to execute.
1. **A function**: The callback that should be executed when this key sequence matches.

Scopes will be covered in a later chapter: here we will cover key sequences and functions at length.

You can avoid calling [`(key/bind)`](api.md#keybind) over and over by using the [`(key/bind-many)`](./api.md#keybind-many) macro. Here is an example:
You can avoid calling {{api key/bind}} over and over by using the {{api key/bind-many}} macro. Here is an example:

```janet
(key/bind-many :root
["ctrl+b" "1"] do-something
["ctrl+b" "2"] do-something-else)
```

You can also clear previously bound key bindings with [`(key/unbind)`](./api.md#keyunbind) or rebind them with [`(key/remap)`](./api.md#keyremap).
You can also clear previously bound key bindings with {{api key/unbind}} or rebind them with {{api key/remap}}.

## Key sequences

Expand Down Expand Up @@ -79,17 +79,17 @@ Accessing individual match groups is not supported; functions always receive the

## Functions

Any Janet function can be passed as a callback to [`(key/bind)`](api.md#keybind). The arity of that function should match the output of the provided sequence; for key sequences that do not include any regex patterns, this means that the function should not take any arguments.
Any Janet function can be passed as a callback to {{api key/bind}}. The arity of that function should match the output of the provided sequence; for key sequences that do not include any regex patterns, this means that the function should not take any arguments.

Like `tmux`, many users at once can connect to the same `cy` server. The function provided to [`(key/bind)`](api.md#keybind) **is executed in the context of the user that invoked it**. Certain functions in `cy`'s API, such as [`(pane/current)`](api.md#panecurrent), return information about the state of the current user, rather than the server as a whole. This means that if two users type the same sequence, they will get different results.
Like `tmux`, many users at once can connect to the same `cy` server. The function provided to {{api key/bind}} **is executed in the context of the user that invoked it**. Certain functions in `cy`'s API, such as {{api pane/current}}, return information about the state of the current user, rather than the server as a whole. This means that if two users type the same sequence, they will get different results.

### Actions

In some cases it is inconvenient to have to provide functions directly to [`(key/bind)`](api.md#keybind). For example, if you are writing a plugin, you might want to be able to provide new actions that a user can take without forcing them to use your key bindings. The user also may not want to assign all of your plugin's functionality to arcane bindings they won't remember.
In some cases it is inconvenient to have to provide functions directly to {{api key/bind}}. For example, if you are writing a plugin, you might want to be able to provide new actions that a user can take without forcing them to use your key bindings. The user also may not want to assign all of your plugin's functionality to arcane bindings they won't remember.

To assist with this, `cy` has a system for **actions**, which are similar in nature to commands [in VSCode](https://code.visualstudio.com/api/extension-guides/command) or [in Sublime Text](https://docs.sublimetext.io/reference/commands.html). An action consists of a short description and a function. When the user opens the command palette (which is bound by default to `"ctrl+a" "ctrl+p"`), they can search for and execute an action based on that description.

You define new actions using the [`(key/action)`](api.md#keyaction) macro. Here is an example from `cy`'s source code:
You define new actions using the {{api key/action}} macro. Here is an example from `cy`'s source code:

```janet
(key/action
Expand All @@ -106,7 +106,7 @@ You define new actions using the [`(key/action)`](api.md#keyaction) macro. Here
(key/bind :root ["ctrl+b" "b"] cy/kill-current-pane)
```

[`(key/action)`](api.md#keyaction) actually just invokes Janet's `(defn)` macro under the hood. This means that actions are just ordinary Janet functions that happen to be registered with `cy`. [`(key/action)`](api.md#keyaction) exists so that you can clearly identify to the user the functionality your plugin provides.
{{api key/action}} actually just invokes Janet's `(defn)` macro under the hood. This means that actions are just ordinary Janet functions that happen to be registered with `cy`. {{api key/action}} exists so that you can clearly identify to the user the functionality your plugin provides.

You can also just use actions to avoid memorizing a key binding you rarely use:

Expand All @@ -119,12 +119,12 @@ You can also just use actions to avoid memorizing a key binding you rarely use:

## Changing and deleting existing keybindings

`cy` provides two API functions for manipulating existing keybindings, [`(key/remap)`](api.md#keyremap) and [`(key/unbind)`](api.md#keyunbind).
`cy` provides two API functions for manipulating existing keybindings, {{api key/remap}} and {{api key/unbind}}.

It is sometimes convenient to change the activation sequence for many bindings at once. For example, you may want to change the prefix used for most of cy's bindings, <kbd>ctrl+a</kbd>, into <kbd>ctrl+v</kbd>:

```janet
(key/remap :root ["ctrl+a"] ["ctrl+v"])
```

Similarly, you may also delete keybindings with [`(key/unbind)`](api.md#keyunbind).
Similarly, you may also delete keybindings with {{api key/unbind}}.
4 changes: 2 additions & 2 deletions docs/src/parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

`cy` contains a primitive key-value store it refers to as parameters. In addition to being available for use from Janet for arbitrary purposes, parameters are also the primary means of configuring `cy`'s behavior.

Parameters are set with [`(cy/set)`](api.md#cyset) and retrieved with [`(cy/get)`](api.md#cyget):
Parameters are set with {{api cy/set}} and retrieved with {{api cy/get}}:

```janet
(cy/set :some-parameter true)
Expand All @@ -19,4 +19,4 @@ Some parameters are used by `cy` to change how it performs certain operations.
| ---------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| `:data-dir` | [inferred on startup](replay-mode.md#recording-terminal-sessions-to-disk) | the directory in which `.borg` files are saved; if empty, recording to file is disabled |
| `:animate` | `true` | whether animations are enabled (disabled over SSH connections by default) |
| `:default-shell` | inferred from `$SHELL` on startup | the default command used for [`(cmd/new)`](api.md#cmdnew) |
| `:default-shell` | inferred from `$SHELL` on startup | the default command used for {{api cmd/new}} |
2 changes: 1 addition & 1 deletion docs/src/replay-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,4 @@ The directory will be created if it does not exist.

You can access previous sessions through the `cy/open-log` action, which by default can be invoked by searching for `open an existing log file` in the command palette (`ctrl+a` `ctrl+p`).

You are also free to use the API call [`(replay/open)`](api.md#replayopen) to open `.borg` files anywhere on your filesystem.
You are also free to use the API call {{api replay/open}} to open `.borg` files anywhere on your filesystem.

0 comments on commit 35786db

Please sign in to comment.