Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ finalizer looks for sockets anywhere in the task's quarantine, without following
links, and stops the server on each one that answers. It fails the build naming
them even when the tests passed. It keeps the directory, and says so, while a
server still answers after `kill-server`. A socket file nothing answers on is
not a leak. `examples/api/run.sh` does the same for the program it starts.
not a leak. `Server.start` does the same for the directory it makes, when the
server it returns is closed.

The finalizer cannot see a server whose socket lies elsewhere. A
[`TmuxExtension`](../libtmux-junit5/) fixture, and the Scala `OwnedTmux` beside
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ release that first shipped the module.

## Unreleased

### Changed

- **The API examples start their own tmux server.** The Java, Kotlin, Scala
Direct, Scala Cats and Clojure programs under `examples/api/` call
`Server.start` (`start!` in Clojure), create `work-one` and `work-two`
themselves where they read them, and take no arguments.
`examples/api/run.sh` is removed. (#142)

### Documented

- **Clojure has native API references and runnable examples.** The module
Expand Down
26 changes: 25 additions & 1 deletion docs/guide/clojure-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,10 @@ reference.

`(close! server)`

Releases the supplied Server's local resources without killing tmux.
Releases the supplied Server's local resources without killing tmux, except for
a
server from [`start!`][api:libtmux.core/start!], whose close stops the tmux
server it owns.
Local closure remains available after
[`libtmux.async/cancel!`][api:libtmux.async/cancel!].
[`open!`][api:libtmux.core/open!] creates another client.
Expand Down Expand Up @@ -423,6 +426,25 @@ A single argv element is executed literally, without shell interpolation.
Use [`apply-layout!`][api:libtmux.core/apply-layout!] to arrange the resulting
window.

### [`libtmux.core/start!`][libtmux.core]

`(start!)`

`(start! options)`

Starts a private tmux server that the returned Server owns.
The server has a directory of its own, an empty config unless `:config-file`
names one,
and none of the caller's TMUX, TMUX_PANE or LIBTMUX_SOCKET*. Optional `:binary`
is a
command name or path, `:root` a directory to make it under, `:timeout` a
positive
java.time.Duration for startup and shutdown. Unknown keys are rejected.
Returns a Server for with-open; closing it stops tmux, removes the directory,
and
throws if the server still answers or a program left another one in it.
Unlike [`open!`][api:libtmux.core/open!], which owns only a client.

### [`libtmux.core/switch-client!`][libtmux.core]

`(switch-client! client session)`
Expand Down Expand Up @@ -907,6 +929,8 @@ deliveries have drained.
#libtmuxcoresnapshot
[api:libtmux.core/split-pane!]:
#libtmuxcoresplit-pane
[api:libtmux.core/start!]:
#libtmuxcorestart
[api:libtmux.core/switch-client!]:
#libtmuxcoreswitch-client
[api:libtmux.core/unlink-window!]:
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/clojure.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The native API references list every documented public var in
They derive arities and docstrings from source; module checks reject stale
output. [Captured values](clojure-reference.md) documents the maps separately.

For complete files with their own entry points and a private-server launcher,
For complete files with their own entry points, each starting its own private server,
use the [Clojure API programs](../../examples/api/README.md#clojure-programs).

Every Clojure fence in reader-facing documentation is discovered and run against
Expand Down
2 changes: 2 additions & 0 deletions docs/reference/operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ Generated from `@Operation`-annotated methods by the operation-catalog Doclet; d
| `io.github.libtmux.Server` | `requireCompatibleTmux()` | READ | Requires a compatible daemon, or a compatible executable when this endpoint is unused. |
| `io.github.libtmux.Server` | `identity()` | CAPTURED | Which server this is. |
| `io.github.libtmux.Server` | `open(io.github.libtmux.ServerConfig)` | LIFECYCLE | A server over a transport it owns and closes. |
| `io.github.libtmux.Server` | `start(io.github.libtmux.ServerLaunch)` | LIFECYCLE | Starts a private tmux server that this handle owns, and returns a client of it. |
| `io.github.libtmux.Server` | `start()` | LIFECYCLE | As {@link #start(ServerLaunch)}, with tmux from {@code PATH} and the default root. |
| `io.github.libtmux.Server` | `using(io.github.libtmux.ServerConfig, io.github.libtmux.transport.TmuxTransport)` | LIFECYCLE | A server over a transport the caller owns. |
| `io.github.libtmux.Server` | `within(java.time.Duration)` | LIFECYCLE | This server, with every command bounded by a deadline of the caller's choosing. |
| `io.github.libtmux.Server` | `builder()` | LIFECYCLE | A builder holding the documented defaults. |
Expand Down
5 changes: 3 additions & 2 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ pinned socket. Other examples retain the core 3.3a minimum.

The [common API programs](api/README.md) provide separate complete Java,
Kotlin, Scala Direct, and Scala Cats examples for construction, listings,
creation, queries, and pane input and capture. Their consumer build files and
private-server launcher are included alongside the source-owned manifest.
creation, queries, and pane input and capture. Each starts its own private
server with `Server.start`; their consumer build files are included alongside
the source-owned manifest.

```console
$ ./gradlew :examples:test
Expand Down
63 changes: 25 additions & 38 deletions examples/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,16 @@ $ ./gradlew :examples:test --tests '*ApiProgramsTest'

## Server ownership

[`run.sh`](run.sh) creates a private tmux server under
`/tmp/libtmux-java-dev/`. It starts `work-one` with an `editor` window containing
two panes and a `logs` window containing one pane. It also starts `work-two`
with one `editor` pane. Every fixture pane runs `/bin/cat`.

The program starts without `TMUX`, `TMUX_PANE` or `LIBTMUX_SOCKET*`, and with a
`TMUX_TMPDIR` of its own, so a client that names no socket cannot reach another
server. The run fails, after stopping it, when the program leaves a server
running there.

Each program receives the tmux executable, socket path, and configuration file
as three arguments. It opens an ordinary client connected to that server.
Closing the client releases its transport; the launcher owns server shutdown.
The launcher stops the server on success or failure and waits for its liveness
probe to fail before removing the directory. A shutdown failure retains the
directory and returns a nonzero status.
Each program starts its own private tmux server with `Server.start`, in a
directory of its own under `/tmp/libtmux-java-dev/`, and ends it when its scope
closes. The server reads an empty config and gets none of the caller's `TMUX`,
`TMUX_PANE` or `LIBTMUX_SOCKET*`. Closing the handle stops the server, waits
until it no longer answers, removes the directory, and throws if the server
still answers or the program left another one under the directory.

The programs that read existing state create it first: `work-one` with an
`editor` window containing two panes and a `logs` window containing one pane,
and `work-two` with one `editor` pane. Every fixture pane runs `/bin/cat`.

Lists returned by a server read capture its state at that read. A session's
windows and a window's panes are captured properties. `NewWindow` reads the
Expand All @@ -42,9 +36,9 @@ their effects through `IOApp`; constructing an `IO` alone does not run it.

Use JDK 25, Git, and tmux 3.3a through 3.7c on Linux. The source checkout
supplies the Gradle wrapper and library dependencies. Save the chosen variant's
`settings.gradle.kts` and `build.gradle.kts`, the shared `gradle.properties` and
`run.sh`, and one complete program under `src/main/java`, `src/main/kotlin`,
or `src/main/scala`. The manifest records those destination paths and each
`settings.gradle.kts` and `build.gradle.kts`, the shared `gradle.properties`,
and one complete program under `src/main/java`, `src/main/kotlin`, or
`src/main/scala`. The manifest records those destination paths and each
program's main class.

For example, prepare the Java capture program in an empty directory:
Expand All @@ -53,18 +47,17 @@ For example, prepare the Java capture program in an empty directory:
$ git clone https://github.com/libtmux/libtmux-java libtmux-source && \
cp libtmux-source/examples/api/java/*.gradle.kts . && \
cp libtmux-source/examples/api/gradle.properties . && \
cp libtmux-source/examples/api/run.sh . && \
mkdir -p src/main/java && \
cp libtmux-source/examples/src/main/java/io/github/libtmux/examples/api/java/Capture.java \
src/main/java/
```

Build the consumer and run it against the launcher's private server:
Build the consumer and run it; the program starts and stops its own server:

```console
$ ./libtmux-source/gradlew --project-dir . --console=plain --quiet \
installDist -PexampleMain=io.github.libtmux.examples.api.java.Capture && \
sh run.sh build/install/api-example/bin/api-example
build/install/api-example/bin/api-example
```

The program waits for each printed marker before capturing and checking it:
Expand All @@ -74,18 +67,17 @@ api-keys
api-line
```

Set `TMUX_BIN` to an absolute executable path to choose a particular tmux
release. Set `API_RUN_ROOT` to move the runner's directories from
`/tmp/libtmux-java-dev`; the default socket path under it must fit in 100
bytes. Each invocation starts a fresh fixture, so none of the programs requires
Put the tmux release you want first on `PATH`; the program runs the `tmux` it
finds there. Each run starts a fresh server, so none of the programs requires
an earlier example to run first.

## Clojure programs

The Clojure catalog uses Tools.deps and a `mainNamespace` entry point for each
program. Each file imports its public namespaces, closes its client, and
prints the manifest's expected output. The same private-server launcher owns
setup and teardown. No program depends on a repository test helper.
prints the manifest's expected output. Each starts its own private server with
`tm/start!` and creates the fixture sessions it reads. No program depends on a
repository test helper.

| Task | Complete program |
| --- | --- |
Expand All @@ -98,7 +90,7 @@ setup and teardown. No program depends on a repository test helper.
| Compare native and Java filters | [query.clj][query] |
| Send input and verify capture | [capture.clj][capture] |

The capability programs use the same fixture and consumer:
The capability programs use the same consumer:

| Task | Complete program |
| --- | --- |
Expand All @@ -125,8 +117,7 @@ consumer folder:

```console
$ mkdir -p build/clojure-api-consumer/src/main && \
cp examples/api/clojure/deps.edn examples/api/run.sh \
build/clojure-api-consumer/ && \
cp examples/api/clojure/deps.edn build/clojure-api-consumer/ && \
cp -R examples/src/main/clojure build/clojure-api-consumer/src/main/
```

Expand All @@ -138,13 +129,11 @@ $ ./gradlew :libtmux:publishToMavenLocal \
-Dmaven.repo.local="$PWD/build/clojure-api-consumer/m2"
```

Run a full program through the private-server launcher:
Run a full program; it starts and stops its own private server:

```console
$ (cd build/clojure-api-consumer && \
sh run.sh clojure \
-M \
-m libtmux.examples.api.capture)
clojure -M -m libtmux.examples.api.capture)
```

It prints `api-keys` and `api-line` on separate lines after verifying actual
Expand All @@ -164,9 +153,7 @@ From the repository root, select the consumer's optional dependency alias:

```console
$ (cd build/clojure-api-consumer && \
sh run.sh clojure \
-M:core-async \
-m libtmux.examples.api.observe-with-async)
clojure -M:core-async -m libtmux.examples.api.observe-with-async)
```

It closes the events consumer, waits for cleanup, and verifies that the
Expand Down
5 changes: 0 additions & 5 deletions examples/api/clojure/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -211,11 +211,6 @@
"sourceFile": "examples/api/clojure/deps.edn",
"path": "deps.edn",
"language": "clojure"
},
{
"sourceFile": "examples/api/run.sh",
"path": "run.sh",
"language": "bash"
}
]
}
Expand Down
20 changes: 0 additions & 20 deletions examples/api/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -460,11 +460,6 @@
"sourceFile": "examples/api/gradle.properties",
"path": "gradle.properties",
"language": "properties"
},
{
"sourceFile": "examples/api/run.sh",
"path": "run.sh",
"language": "bash"
}
]
},
Expand All @@ -484,11 +479,6 @@
"sourceFile": "examples/api/gradle.properties",
"path": "gradle.properties",
"language": "properties"
},
{
"sourceFile": "examples/api/run.sh",
"path": "run.sh",
"language": "bash"
}
]
},
Expand All @@ -508,11 +498,6 @@
"sourceFile": "examples/api/gradle.properties",
"path": "gradle.properties",
"language": "properties"
},
{
"sourceFile": "examples/api/run.sh",
"path": "run.sh",
"language": "bash"
}
]
},
Expand All @@ -532,11 +517,6 @@
"sourceFile": "examples/api/gradle.properties",
"path": "gradle.properties",
"language": "properties"
},
{
"sourceFile": "examples/api/run.sh",
"path": "run.sh",
"language": "bash"
}
]
}
Expand Down
99 changes: 0 additions & 99 deletions examples/api/run.sh

This file was deleted.

Loading
Loading