magus v0.4.2 is out. See what's new
¶ View generated markdown
5 min read

term

Terminal interaction: capability probes, an interactive picker, and styled output. Renders to stderr; pick raises rather than hanging when there is no terminal.

Naming convention: import the module under its bare name (import "term"), reach members with a backslash, and call methods in camelCase: term\someMethod.

Note

The examples below are reference-only. term performs real IO (filesystem, process, network, or environment access) that the in-browser playground's sandbox cannot provide, so it is not registered there and its examples have no Run button. Pure-compute modules such as strings and json run their examples live in the page.

Methods

isInteractive

Report whether this run can prompt at all: both standard input and standard error are terminals. Branch on it before calling pick - in CI, behind a pipe, or under a daemon this is false, and pick would raise. It is the one call that makes an interactive step safe to add to a target that also runs unattended.

Signature: term\isInteractive() -> bool - source

Returns: bool

wantsColor

Report whether styled output should be emitted: standard error is a terminal and the environment does not ask for plain text (NO_COLOR, TERM=dumb). colorize already consults this, so a caller needs it only to make a wider rendering choice - a box-drawing table versus a plain one.

Signature: term\wantsColor() -> bool - source

Returns: bool

size

Return the terminal's {width, height} in character cells. Both are 0 when there is no terminal to measure - piped output, no controlling terminal - so check width rather than expecting a raise. Use it to wrap or truncate output to the reader's actual window instead of assuming 80 columns.

Signature: term\size() -> TermSize - source

Returns: map[string]any

Example:

import "std";
import "term";

// width is 0 when there is no terminal to measure, so check it rather than
// catching an error.
final size = term\size();
final width = if (size.width > 0) size.width else 80;
std\print("wrapping to {width} columns");

colorize

Wrap s in the given style and close it again. Returns s UNCHANGED when the output is not a terminal or the environment asked for plain text, so a magusfile never has to guard the call and escape codes cannot leak into a CI log. A style of none is also pass-through, which lets a conditionally-computed style be passed without branching.

Signature: term\colorize(s, style) -> string - source

Parameter Type Optional Description
s string
style string

Returns: string

pick

Prompt the reader to choose one of items and return its index. Type to filter (matching every whitespace-separated token), arrow keys or Ctrl-N/Ctrl-P to move, Enter to choose. RAISES when there is no terminal to prompt on - guard with is_interactive - and raises when the reader aborts with ESC, Ctrl-C or Ctrl-D, so a cancel ends the run rather than quietly returning a choice nobody made. Renders to stderr.

Signature: term\pick(items, [prompt], [initial_filter], [initial], [max_rows]) -> int - source

Parameter Type Optional Description
items []string
prompt string yes
initial_filter string yes
initial int yes
max_rows int yes

Returns: int

Example:

import "std";
import "term";

// pick RAISES when there is no terminal, so an interactive step that also has to
// run unattended guards on isInteractive and declares its own default. It raises
// again when the reader aborts with ESC or Ctrl-C, which is why the call is
// caught rather than merely guarded: a cancel is not a choice.
final projects = ["console", "docs", "libs/gopherbuzz"];

fun choose() > str {
    if (!term\isInteractive()) {
        return projects[0];
    }
    try {
        return projects[term\pick(projects, prompt: "project")];
    } catch (e) {
        return projects[0];
    }
}

std\print(term\colorize(choose(), style: term\TermStyle.brightGreen));

notify

Raise a notification into the band magus pins at the bottom of the terminal, where it shows for a few seconds and then disappears on its own. Unlike log.info it does not join the scrolling transcript: it is for something worth GLANCING at during a long run, not for the record. Returns immediately - the message expires on its own clock - and never raises: it is DROPPED when there is no terminal to show it on, or when the band has no room, so a piped or CI run is never given a repainted view it cannot use and no caller has to guard a notification. Log the same fact if it also needs recording. ttl_ms defaults to 5000; a negative ttl_ms pins the notification until newer ones push it out.

Signature: term\notify(message, [level], [ttl_ms]) - source

Parameter Type Optional Description
message string
level string yes
ttl_ms int yes

Example:

import "term";

// A notification is something to GLANCE at during a long run, not a record of
// it. It is dropped when there is no terminal to show it on, so anything that
// must survive the run goes through log as well.
term\notify("docs regenerated");

// Severity picks the color; the default ttl is 5 seconds.
term\notify("cache stampede on go-build", level: term\LogLevel.warn);

// A longer ttl for something worth reading twice.
term\notify("deploy skipped: no credentials", level: term\LogLevel.error, ttl_ms: 15000);

// A negative ttl keeps it until newer notifications push it out of the band.
term\notify("daemon unreachable", level: term\LogLevel.error, ttl_ms: -1);

clearScreen

Erase the screen and move the cursor home, the repaint a full-screen refresh loop issues before redrawing. Scrollback is preserved, so a reader who scrolls up after the loop ends still sees what came before. A no-op when there is no terminal, so a watch loop needs no guard.

Signature: term\clearScreen() - source

generatedreference/buzz/termmodulestdlibmagusfile
Last updated (4f8cc295)
Earlier changes on this page (3)

Full history ↗ · Blame source ↗

Glossary

Project

A directory magus recognizes as a unit of work (it has a magusfile); the unit of caching, scheduling, and dependency tracking. See workspace.

Magusfile

The magusfile.buzz that declares a project's targets (as export funs) and binds its spells. See targets.

Target

A named operation (build, test, ...) you invoke with magus run <target>; it may compose a spell's tool-native operations and depend on other targets. See targets.

Op

A single tool-native command a target composes (long form: operation); the middle of the work hierarchy (Spell to Op to Target). See operations.

Module

A magus stdlib namespace a magusfile imports for host capabilities: filesystem, exec, vcs, and more. See the module reference.

Buzz

The language magusfiles are written in (the .buzz engine). See engines.

Cache

The content-addressed store magus consults before running a target, so unchanged work is skipped. See cache.

Sandbox

The restricted filesystem and environment a target runs in, so builds stay reproducible and side-effect-free. See sandbox.

Daemon

The background magus host that owns shared state such as services and the warm knowledge graph. See daemon.

CI

An ordinary magusfile-defined target you compose yourself with magus\needs - magus does not hardcode its stages. Magus.RunCI treats it specially only in that it strips the rw charm, it is the anchor magus affected ci keys off, and a selected scope with no project declaring it is a load error rather than a silent no-op. See targets.

Conventions

Admonitions

Call-outs are rendered from GitHub-style alert blockquotes and carry a colored accent per type:

Note

Context worth knowing, but not a warning.

Warning

Something that can bite you if ignored.

The types are NOTE, TIP, IMPORTANT, WARNING, and CAUTION.