@tishlang/tish-format 1.0.12 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.toml +51 -0
- package/LICENSE +13 -0
- package/bin/tish-format +0 -0
- package/crates/js_to_tish/Cargo.toml +11 -0
- package/crates/js_to_tish/README.md +18 -0
- package/crates/js_to_tish/src/error.rs +55 -0
- package/crates/js_to_tish/src/lib.rs +11 -0
- package/crates/js_to_tish/src/span_util.rs +35 -0
- package/crates/js_to_tish/src/transform/expr.rs +611 -0
- package/crates/js_to_tish/src/transform/stmt.rs +503 -0
- package/crates/js_to_tish/src/transform.rs +60 -0
- package/crates/tish/Cargo.toml +62 -0
- package/crates/tish/build.rs +21 -0
- package/crates/tish/src/cargo_native_registry.rs +32 -0
- package/crates/tish/src/cli_help.rs +576 -0
- package/crates/tish/src/main.rs +853 -0
- package/crates/tish/src/repl_completion.rs +199 -0
- package/crates/tish/tests/cargo_example_compile.rs +67 -0
- package/crates/tish/tests/error_source_location.rs +36 -0
- package/crates/tish/tests/fixtures/cargo_example_project/Cargo.toml +3 -0
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/Cargo.toml +11 -0
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/src/lib.rs +12 -0
- package/crates/tish/tests/fixtures/cargo_example_project/package.json +10 -0
- package/crates/tish/tests/fixtures/cargo_example_project/src/main.tish +3 -0
- package/crates/tish/tests/fixtures/runtime_error_location.tish +5 -0
- package/crates/tish/tests/fixtures/trycatch_runtime_errors.tish +15 -0
- package/crates/tish/tests/fixtures/tty_capability.tish +9 -0
- package/crates/tish/tests/integration_test.rs +1406 -0
- package/crates/tish/tests/run_optimize_stdout_parity.rs +50 -0
- package/crates/tish/tests/shortcircuit.rs +65 -0
- package/crates/tish/tests/trycatch_runtime_errors.rs +45 -0
- package/crates/tish/tests/tty_capability.rs +43 -0
- package/crates/tish_ast/Cargo.toml +9 -0
- package/crates/tish_ast/src/ast.rs +649 -0
- package/crates/tish_ast/src/lib.rs +5 -0
- package/crates/tish_build_utils/Cargo.toml +11 -0
- package/crates/tish_build_utils/src/lib.rs +577 -0
- package/crates/tish_builtins/Cargo.toml +22 -0
- package/crates/tish_builtins/src/array.rs +803 -0
- package/crates/tish_builtins/src/collections.rs +481 -0
- package/crates/tish_builtins/src/construct.rs +199 -0
- package/crates/tish_builtins/src/date.rs +538 -0
- package/crates/tish_builtins/src/globals.rs +293 -0
- package/crates/tish_builtins/src/helpers.rs +35 -0
- package/crates/tish_builtins/src/iterator.rs +129 -0
- package/crates/tish_builtins/src/lib.rs +21 -0
- package/crates/tish_builtins/src/math.rs +89 -0
- package/crates/tish_builtins/src/number.rs +96 -0
- package/crates/tish_builtins/src/object.rs +36 -0
- package/crates/tish_builtins/src/string.rs +646 -0
- package/crates/tish_builtins/src/symbol.rs +83 -0
- package/crates/tish_builtins/src/typedarrays.rs +298 -0
- package/crates/tish_bytecode/Cargo.toml +17 -0
- package/crates/tish_bytecode/src/chunk.rs +164 -0
- package/crates/tish_bytecode/src/compiler.rs +2604 -0
- package/crates/tish_bytecode/src/encoding.rs +102 -0
- package/crates/tish_bytecode/src/lib.rs +20 -0
- package/crates/tish_bytecode/src/opcode.rs +185 -0
- package/crates/tish_bytecode/src/peephole.rs +189 -0
- package/crates/tish_bytecode/src/serialize.rs +193 -0
- package/crates/tish_bytecode/tests/break_continue_bytecode.rs +44 -0
- package/crates/tish_bytecode/tests/constant_folding.rs +84 -0
- package/crates/tish_bytecode/tests/sort_optimization.rs +31 -0
- package/crates/tish_compile/Cargo.toml +27 -0
- package/crates/tish_compile/src/check.rs +774 -0
- package/crates/tish_compile/src/codegen.rs +7317 -0
- package/crates/tish_compile/src/infer.rs +1681 -0
- package/crates/tish_compile/src/lib.rs +206 -0
- package/crates/tish_compile/src/resolve.rs +1951 -0
- package/crates/tish_compile/src/types.rs +605 -0
- package/crates/tish_compile_js/Cargo.toml +18 -0
- package/crates/tish_compile_js/examples/jsx_vdom_smoke.tish +8 -0
- package/crates/tish_compile_js/src/codegen.rs +938 -0
- package/crates/tish_compile_js/src/error.rs +20 -0
- package/crates/tish_compile_js/src/lib.rs +26 -0
- package/crates/tish_compile_js/src/tests_jsx.rs +414 -0
- package/crates/tish_compiler_wasm/Cargo.toml +21 -0
- package/crates/tish_compiler_wasm/src/lib.rs +57 -0
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +473 -0
- package/crates/tish_core/Cargo.toml +32 -0
- package/crates/tish_core/src/console_style.rs +170 -0
- package/crates/tish_core/src/json.rs +430 -0
- package/crates/tish_core/src/lib.rs +20 -0
- package/crates/tish_core/src/macros.rs +36 -0
- package/crates/tish_core/src/shape.rs +85 -0
- package/crates/tish_core/src/uri.rs +118 -0
- package/crates/tish_core/src/value.rs +1350 -0
- package/crates/tish_core/src/vmref.rs +183 -0
- package/crates/tish_cranelift/Cargo.toml +19 -0
- package/crates/tish_cranelift/src/lib.rs +43 -0
- package/crates/tish_cranelift/src/link.rs +130 -0
- package/crates/tish_cranelift/src/lower.rs +85 -0
- package/crates/tish_cranelift_runtime/Cargo.toml +26 -0
- package/crates/tish_cranelift_runtime/src/lib.rs +45 -0
- package/crates/tish_eval/Cargo.toml +51 -0
- package/crates/tish_eval/src/eval.rs +4265 -0
- package/crates/tish_eval/src/http.rs +191 -0
- package/crates/tish_eval/src/lib.rs +99 -0
- package/crates/tish_eval/src/natives.rs +551 -0
- package/crates/tish_eval/src/promise.rs +179 -0
- package/crates/tish_eval/src/regex.rs +299 -0
- package/crates/tish_eval/src/timers.rs +120 -0
- package/crates/tish_eval/src/value.rs +336 -0
- package/crates/tish_eval/src/value_convert.rs +117 -0
- package/crates/tish_ffi/Cargo.toml +26 -0
- package/crates/tish_ffi/src/lib.rs +518 -0
- package/crates/tish_ffi/tests/fixtures/testmod/Cargo.toml +18 -0
- package/crates/tish_ffi/tests/fixtures/testmod/src/lib.rs +46 -0
- package/crates/tish_ffi/tests/loader.rs +65 -0
- package/crates/tish_fmt/Cargo.toml +16 -0
- package/crates/tish_fmt/src/bin/tish-fmt.rs +41 -0
- package/crates/tish_fmt/src/lib.rs +2157 -0
- package/crates/tish_jsx_web/Cargo.toml +9 -0
- package/crates/tish_jsx_web/README.md +5 -0
- package/crates/tish_jsx_web/src/lib.rs +2 -0
- package/crates/tish_lexer/Cargo.toml +9 -0
- package/crates/tish_lexer/src/lib.rs +1104 -0
- package/crates/tish_lexer/src/token.rs +170 -0
- package/crates/tish_lint/Cargo.toml +18 -0
- package/crates/tish_lint/src/bin/tish-lint.rs +195 -0
- package/crates/tish_lint/src/lib.rs +281 -0
- package/crates/tish_llvm/Cargo.toml +13 -0
- package/crates/tish_llvm/src/lib.rs +115 -0
- package/crates/tish_lsp/Cargo.toml +25 -0
- package/crates/tish_lsp/README.md +26 -0
- package/crates/tish_lsp/src/builtin_goto.rs +362 -0
- package/crates/tish_lsp/src/import_goto.rs +564 -0
- package/crates/tish_lsp/src/main.rs +1459 -0
- package/crates/tish_native/Cargo.toml +16 -0
- package/crates/tish_native/src/build.rs +481 -0
- package/crates/tish_native/src/config.rs +48 -0
- package/crates/tish_native/src/lib.rs +416 -0
- package/crates/tish_opt/Cargo.toml +13 -0
- package/crates/tish_opt/src/lib.rs +1046 -0
- package/crates/tish_parser/Cargo.toml +11 -0
- package/crates/tish_parser/src/lib.rs +386 -0
- package/crates/tish_parser/src/parser.rs +2726 -0
- package/crates/tish_pg/Cargo.toml +34 -0
- package/crates/tish_pg/README.md +38 -0
- package/crates/tish_pg/src/error.rs +52 -0
- package/crates/tish_pg/src/lib.rs +955 -0
- package/crates/tish_resolve/Cargo.toml +13 -0
- package/crates/tish_resolve/src/lib.rs +3601 -0
- package/crates/tish_resolve/src/pos.rs +141 -0
- package/crates/tish_runtime/Cargo.toml +100 -0
- package/crates/tish_runtime/src/http.rs +1347 -0
- package/crates/tish_runtime/src/http_fetch.rs +492 -0
- package/crates/tish_runtime/src/http_hyper.rs +441 -0
- package/crates/tish_runtime/src/http_prefork.rs +189 -0
- package/crates/tish_runtime/src/lib.rs +1447 -0
- package/crates/tish_runtime/src/native_promise.rs +15 -0
- package/crates/tish_runtime/src/promise.rs +558 -0
- package/crates/tish_runtime/src/promise_io.rs +38 -0
- package/crates/tish_runtime/src/timers.rs +172 -0
- package/crates/tish_runtime/src/tty.rs +226 -0
- package/crates/tish_runtime/src/ws.rs +778 -0
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +102 -0
- package/crates/tish_ui/Cargo.toml +17 -0
- package/crates/tish_ui/src/jsx.rs +692 -0
- package/crates/tish_ui/src/lib.rs +20 -0
- package/crates/tish_ui/src/runtime/hooks.rs +573 -0
- package/crates/tish_ui/src/runtime/mod.rs +183 -0
- package/crates/tish_vm/Cargo.toml +60 -0
- package/crates/tish_vm/src/jit.rs +1050 -0
- package/crates/tish_vm/src/lib.rs +41 -0
- package/crates/tish_vm/src/vm.rs +3536 -0
- package/crates/tish_vm/tests/concurrent_shared_state.rs +140 -0
- package/crates/tish_vm/tests/fixtures/or_string_cmd.tish +2 -0
- package/crates/tish_vm/tests/lexical_scope_declare.rs +34 -0
- package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +150 -0
- package/crates/tish_wasm/Cargo.toml +15 -0
- package/crates/tish_wasm/src/lib.rs +428 -0
- package/crates/tish_wasm_runtime/Cargo.toml +37 -0
- package/crates/tish_wasm_runtime/src/gpu.rs +429 -0
- package/crates/tish_wasm_runtime/src/lib.rs +42 -0
- package/crates/tishlang_cargo_bindgen/Cargo.toml +26 -0
- package/crates/tishlang_cargo_bindgen/src/classify.rs +261 -0
- package/crates/tishlang_cargo_bindgen/src/discover.rs +125 -0
- package/crates/tishlang_cargo_bindgen/src/infer.rs +382 -0
- package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
- package/crates/tishlang_cargo_bindgen/src/main.rs +167 -0
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +117 -0
- package/justfile +276 -0
- package/package.json +2 -2
- package/platform/darwin-arm64/tish-fmt +0 -0
- package/platform/darwin-x64/tish-fmt +0 -0
- package/platform/linux-arm64/tish-fmt +0 -0
- package/platform/linux-x64/tish-fmt +0 -0
- package/platform/win32-x64/tish-fmt.exe +0 -0
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
//! Long help text, terminal styling, and ASCII banner for the `tish` CLI.
|
|
2
|
+
|
|
3
|
+
use std::io::{self, IsTerminal, Write};
|
|
4
|
+
use std::thread;
|
|
5
|
+
use std::time::Duration;
|
|
6
|
+
|
|
7
|
+
use clap::builder::styling::{Color, Effects, RgbColor, Style, Styles};
|
|
8
|
+
use clap::{CommandFactory, Parser, Subcommand};
|
|
9
|
+
|
|
10
|
+
/// FIGlet-style block letters (UTF-8). On a TTY, a short expand + palette-color animation runs.
|
|
11
|
+
const TISH_BANNER_LINES: &[&str] = &[
|
|
12
|
+
"",
|
|
13
|
+
"████████╗██╗███████╗██╗ ██╗",
|
|
14
|
+
"╚══██╔══╝██║██╔════╝██║ ██║",
|
|
15
|
+
" ██║ ██║███████╗███████║",
|
|
16
|
+
" ██║ ██║╚════██║██╔══██║",
|
|
17
|
+
" ██║ ██║███████║██║ ██║",
|
|
18
|
+
" ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝",
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
/// Frames used for the left-to-right expand reveal.
|
|
22
|
+
const BANNER_REVEAL_FRAMES: usize = 14;
|
|
23
|
+
/// Extra frames of rainbow cycling after the logo is fully visible.
|
|
24
|
+
const BANNER_CYCLE_FRAMES: usize = 4;
|
|
25
|
+
const BANNER_FRAME_MS: u64 = 20;
|
|
26
|
+
|
|
27
|
+
/// Orange → Yellow → Green → Teal → Blue → Purple → Pink (matching the brand palette).
|
|
28
|
+
const PALETTE: &[(u8, u8, u8)] = &[
|
|
29
|
+
(255, 159, 64), // Orange
|
|
30
|
+
(255, 213, 64), // Yellow
|
|
31
|
+
(52, 199, 89), // Green
|
|
32
|
+
(48, 209, 188), // Teal
|
|
33
|
+
(10, 132, 255), // Blue
|
|
34
|
+
(175, 82, 222), // Purple
|
|
35
|
+
(255, 55, 148), // Pink
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
fn ease_out_cubic(t: f32) -> f32 {
|
|
39
|
+
let u = 1.0 - t.clamp(0.0, 1.0);
|
|
40
|
+
1.0 - u * u * u
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// Linearly interpolate between two palette colors.
|
|
44
|
+
fn lerp_color(a: (u8, u8, u8), b: (u8, u8, u8), t: f32) -> (u8, u8, u8) {
|
|
45
|
+
let t = t.clamp(0.0, 1.0);
|
|
46
|
+
(
|
|
47
|
+
(a.0 as f32 + (b.0 as f32 - a.0 as f32) * t).round() as u8,
|
|
48
|
+
(a.1 as f32 + (b.1 as f32 - a.1 as f32) * t).round() as u8,
|
|
49
|
+
(a.2 as f32 + (b.2 as f32 - a.2 as f32) * t).round() as u8,
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// Smooth palette sample for a given (row, col) cell and scrolling color frame.
|
|
54
|
+
/// Uses column as the primary gradient axis so every row has a continuous sweep.
|
|
55
|
+
/// A small per-row offset adds a gentle diagonal tilt rather than flat stripes.
|
|
56
|
+
fn palette_color(row: usize, col: usize, color_frame: usize) -> (u8, u8, u8) {
|
|
57
|
+
let n = PALETTE.len();
|
|
58
|
+
// one full palette cycle every ~5 columns; row adds a slight diagonal
|
|
59
|
+
let scroll = color_frame as f32 * 0.22;
|
|
60
|
+
let pos = ((col as f32 / 5.0) + (row as f32 * 0.25) + scroll).rem_euclid(n as f32);
|
|
61
|
+
let lo = pos.floor() as usize % n;
|
|
62
|
+
let hi = (lo + 1) % n;
|
|
63
|
+
lerp_color(PALETTE[lo], PALETTE[hi], pos.fract())
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// Render one frame. `reveal_t` is 0..=1 (how much of each line is visible).
|
|
67
|
+
/// `color_frame` is the ever-incrementing counter that drives the rainbow scroll.
|
|
68
|
+
fn write_tish_banner_frame(out: &mut impl Write, reveal_t: f32, color_frame: usize) {
|
|
69
|
+
for (row, line) in TISH_BANNER_LINES.iter().enumerate() {
|
|
70
|
+
let chars: Vec<char> = line.chars().collect();
|
|
71
|
+
let len = chars.len();
|
|
72
|
+
let visible = ((len as f32) * reveal_t).round() as usize;
|
|
73
|
+
let visible = visible.min(len);
|
|
74
|
+
|
|
75
|
+
for (col, &ch) in chars.iter().enumerate() {
|
|
76
|
+
if col >= visible || ch == ' ' {
|
|
77
|
+
let _ = write!(out, " ");
|
|
78
|
+
} else {
|
|
79
|
+
let (r, g, b) = palette_color(row, col, color_frame);
|
|
80
|
+
let _ = write!(out, "\x1b[1;38;2;{r};{g};{b}m{ch}\x1b[0m");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
let _ = writeln!(out);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
fn print_tish_banner_plain(out: &mut impl Write) {
|
|
88
|
+
for line in TISH_BANNER_LINES {
|
|
89
|
+
let _ = writeln!(out, "{line}");
|
|
90
|
+
}
|
|
91
|
+
let _ = writeln!(out);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
fn print_tish_banner_animated(out: &mut impl Write) {
|
|
95
|
+
let n = TISH_BANNER_LINES.len();
|
|
96
|
+
let total = BANNER_REVEAL_FRAMES + BANNER_CYCLE_FRAMES;
|
|
97
|
+
|
|
98
|
+
for f in 0..total {
|
|
99
|
+
if f > 0 {
|
|
100
|
+
let _ = write!(out, "\x1b[{n}A");
|
|
101
|
+
}
|
|
102
|
+
// Phase 1: ease-out expand. Phase 2: fully visible, rainbow keeps scrolling.
|
|
103
|
+
let reveal_t = if f < BANNER_REVEAL_FRAMES {
|
|
104
|
+
ease_out_cubic((f + 1) as f32 / BANNER_REVEAL_FRAMES as f32)
|
|
105
|
+
} else {
|
|
106
|
+
1.0
|
|
107
|
+
};
|
|
108
|
+
write_tish_banner_frame(out, reveal_t, f);
|
|
109
|
+
let _ = out.flush();
|
|
110
|
+
thread::sleep(Duration::from_millis(BANNER_FRAME_MS));
|
|
111
|
+
}
|
|
112
|
+
let _ = writeln!(out);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/// Print the `TISH` tile banner to stdout (animated palette on a TTY; plain text otherwise).
|
|
116
|
+
pub fn print_tish_banner() {
|
|
117
|
+
let mut out = io::stdout().lock();
|
|
118
|
+
if io::stdout().is_terminal() {
|
|
119
|
+
print_tish_banner_animated(&mut out);
|
|
120
|
+
} else {
|
|
121
|
+
print_tish_banner_plain(&mut out);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/// Build the `Command` with all colored after_help text attached.
|
|
126
|
+
/// Use this instead of `Cli::command()` everywhere so the help text is consistent.
|
|
127
|
+
pub fn build_command() -> clap::Command {
|
|
128
|
+
Cli::command()
|
|
129
|
+
.after_help(cli_after_help())
|
|
130
|
+
.mut_subcommand("run", |sub| sub.after_help(run_after_help()))
|
|
131
|
+
.mut_subcommand("repl", |sub| sub.after_help(repl_after_help()))
|
|
132
|
+
.mut_subcommand("build", |sub| sub.after_long_help(build_after_help()))
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/// Write help text to `w` (plain bytes, used for line-counting only).
|
|
136
|
+
fn count_help_lines(cmd: &mut clap::Command, sub_name: Option<&str>) -> usize {
|
|
137
|
+
let mut buf = Vec::<u8>::new();
|
|
138
|
+
if let Some(name) = sub_name {
|
|
139
|
+
if cmd.find_subcommand(name).is_some() {
|
|
140
|
+
let _ = cmd
|
|
141
|
+
.find_subcommand_mut(name)
|
|
142
|
+
.unwrap()
|
|
143
|
+
.write_long_help(&mut buf);
|
|
144
|
+
} else {
|
|
145
|
+
let _ = cmd.write_long_help(&mut buf);
|
|
146
|
+
}
|
|
147
|
+
} else {
|
|
148
|
+
let _ = cmd.write_long_help(&mut buf);
|
|
149
|
+
}
|
|
150
|
+
buf.iter().filter(|&&b| b == b'\n').count()
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/// Print help text directly to stdout via clap's own stdout path (guaranteed colors).
|
|
154
|
+
fn print_help_to_stdout(cmd: &mut clap::Command, sub_name: Option<&str>) {
|
|
155
|
+
if let Some(name) = sub_name {
|
|
156
|
+
if cmd.find_subcommand(name).is_some() {
|
|
157
|
+
let _ = cmd.find_subcommand_mut(name).unwrap().print_long_help();
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
let _ = cmd.print_long_help();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/// Detect which subcommand (if any) is being asked about from raw argv.
|
|
165
|
+
fn sub_name_from_argv(argv: &[String]) -> Option<String> {
|
|
166
|
+
match argv.get(1).map(String::as_str) {
|
|
167
|
+
Some("help") => argv.get(2).map(String::to_string), // tish help run
|
|
168
|
+
Some(s) if !s.starts_with('-') => Some(s.to_string()), // tish run --help
|
|
169
|
+
_ => None,
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
|
174
|
+
/// ANSI: bold purple (175, 82, 222)
|
|
175
|
+
const H_PURPLE: &str = "\x1b[1;38;2;175;82;222m";
|
|
176
|
+
/// ANSI: medium grey — clearly less-than-white on dark backgrounds
|
|
177
|
+
const H_GREY: &str = "\x1b[38;2;150;150;150m";
|
|
178
|
+
/// ANSI: pink (255, 55, 148) — used for the website URL
|
|
179
|
+
const H_PINK: &str = "\x1b[38;2;255;55;148m";
|
|
180
|
+
const H_RESET: &str = "\x1b[0m";
|
|
181
|
+
|
|
182
|
+
/// Branded header used for subcommand help pages.
|
|
183
|
+
/// Prints `[purple]Tish[reset] [grey](version x)[reset]`
|
|
184
|
+
/// `[pink]https://tishlang.com[reset]`
|
|
185
|
+
/// followed by a blank line.
|
|
186
|
+
fn print_small_header() {
|
|
187
|
+
if io::stdout().is_terminal() {
|
|
188
|
+
println!("{H_PURPLE}Tish{H_RESET} {H_GREY}(version {VERSION}){H_RESET}");
|
|
189
|
+
println!("{H_PINK}https://tishlang.com{H_RESET}\n");
|
|
190
|
+
} else {
|
|
191
|
+
println!("Tish (version {VERSION})");
|
|
192
|
+
println!("https://tishlang.com\n");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/// Number of lines the main-help manual prefix takes (printed before clap output).
|
|
197
|
+
/// Layout: title, description, url, blank = 4 lines.
|
|
198
|
+
const MAIN_PREFIX_LINES: usize = 4;
|
|
199
|
+
|
|
200
|
+
/// Print help, prefixed with the right header and (for top-level only) the
|
|
201
|
+
/// animated banner. Help is written via clap's own stdout path for full colors.
|
|
202
|
+
pub fn print_banner_with_help(argv: &[String]) {
|
|
203
|
+
let sub_name = sub_name_from_argv(argv);
|
|
204
|
+
let sub = sub_name.as_deref();
|
|
205
|
+
|
|
206
|
+
// ── Subcommand help: compact static header, no animation ─────────────
|
|
207
|
+
if sub.is_some() {
|
|
208
|
+
print_small_header();
|
|
209
|
+
let mut cmd = build_command();
|
|
210
|
+
cmd.build();
|
|
211
|
+
print_help_to_stdout(&mut cmd, sub);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ── Top-level help ────────────────────────────────────────────────────
|
|
216
|
+
|
|
217
|
+
if !io::stdout().is_terminal() {
|
|
218
|
+
let mut out = io::stdout().lock();
|
|
219
|
+
print_tish_banner_plain(&mut out);
|
|
220
|
+
drop(out);
|
|
221
|
+
let mut cmd = build_command().color(clap::ColorChoice::Never);
|
|
222
|
+
cmd.build();
|
|
223
|
+
print_help_to_stdout(&mut cmd, sub);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Line-count pass (ANSI codes never add \n, so Never == Always count).
|
|
228
|
+
let h: usize = {
|
|
229
|
+
let mut cmd = build_command().color(clap::ColorChoice::Never);
|
|
230
|
+
cmd.build();
|
|
231
|
+
count_help_lines(&mut cmd, sub)
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
let n = TISH_BANNER_LINES.len();
|
|
235
|
+
|
|
236
|
+
// 1. First banner frame + manual prefix + full help – all visible immediately.
|
|
237
|
+
{
|
|
238
|
+
let mut out = io::stdout().lock();
|
|
239
|
+
write_tish_banner_frame(&mut out, 1.0, 0);
|
|
240
|
+
let _ = writeln!(out); // blank separator (row n+1)
|
|
241
|
+
// ── Manual prefix (MAIN_PREFIX_LINES = 4 lines) ──────────────────
|
|
242
|
+
let _ = writeln!(
|
|
243
|
+
out,
|
|
244
|
+
"{H_PURPLE}Tish{H_RESET} {H_GREY}(version {VERSION}){H_RESET}"
|
|
245
|
+
);
|
|
246
|
+
let _ = writeln!(out, "Minimal TS/JS-ish language");
|
|
247
|
+
let _ = writeln!(out, "{H_PINK}https://tishlang.com{H_RESET}");
|
|
248
|
+
let _ = writeln!(out); // blank before Usage
|
|
249
|
+
let _ = out.flush();
|
|
250
|
+
}
|
|
251
|
+
{
|
|
252
|
+
let mut cmd = build_command();
|
|
253
|
+
cmd.build();
|
|
254
|
+
print_help_to_stdout(&mut cmd, sub);
|
|
255
|
+
let _ = io::stdout().flush();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// 2. Jump cursor back to banner top and cycle colors.
|
|
259
|
+
// Total rows above cursor: n + 1 (sep) + MAIN_PREFIX_LINES + h (clap)
|
|
260
|
+
{
|
|
261
|
+
let mut out = io::stdout().lock();
|
|
262
|
+
let _ = write!(out, "\x1b[{}A", n + 1 + MAIN_PREFIX_LINES + h);
|
|
263
|
+
let _ = out.flush();
|
|
264
|
+
|
|
265
|
+
let frames = BANNER_CYCLE_FRAMES;
|
|
266
|
+
for f in 0..frames {
|
|
267
|
+
write_tish_banner_frame(&mut out, 1.0, f);
|
|
268
|
+
if f < frames - 1 {
|
|
269
|
+
let _ = write!(out, "\x1b[{}A", n);
|
|
270
|
+
}
|
|
271
|
+
let _ = out.flush();
|
|
272
|
+
thread::sleep(Duration::from_millis(BANNER_FRAME_MS));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// After last frame cursor is at row n; skip sep + prefix + clap rows.
|
|
276
|
+
let _ = write!(out, "\x1b[{}B", 1 + MAIN_PREFIX_LINES + h);
|
|
277
|
+
let _ = writeln!(out);
|
|
278
|
+
let _ = out.flush();
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/// Whether argv will cause clap to print help (top-level or subcommand).
|
|
283
|
+
pub fn argv_requests_help(argv: &[String]) -> bool {
|
|
284
|
+
argv.iter().any(|a| a == "--help" || a == "-h")
|
|
285
|
+
|| matches!(argv.get(1).map(String::as_str), Some("help"))
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/// Build a bold true-color `Style` from the brand palette.
|
|
289
|
+
fn rgb_bold(r: u8, g: u8, b: u8) -> Style {
|
|
290
|
+
Style::new().fg_color(Some(Color::Rgb(RgbColor(r, g, b)))) | Effects::BOLD
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/// Help colors using the brand palette.
|
|
294
|
+
/// Orange → section headers / usage. Teal → literals (commands, flags). Yellow → placeholders.
|
|
295
|
+
pub fn cargo_help_styles() -> Styles {
|
|
296
|
+
Styles::styled()
|
|
297
|
+
.header(rgb_bold(255, 159, 64)) // Orange – "Commands:", "Options:", "Usage:"
|
|
298
|
+
.usage(rgb_bold(255, 159, 64)) // Orange
|
|
299
|
+
.literal(rgb_bold(48, 209, 188)) // Teal – run, repl, --help, -V …
|
|
300
|
+
.placeholder(rgb_bold(255, 213, 64)) // Yellow – <FILE>, <NAME>, …
|
|
301
|
+
.error(rgb_bold(255, 55, 148)) // Pink – error messages
|
|
302
|
+
.valid(rgb_bold(52, 199, 89)) // Green – valid values
|
|
303
|
+
.invalid(rgb_bold(255, 55, 148)) // Pink – invalid values
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/// Returns the colored `after_help` text for the top-level `tish --help`.
|
|
307
|
+
/// Colors are emitted only when stdout is a TTY.
|
|
308
|
+
pub fn cli_after_help() -> String {
|
|
309
|
+
let (oh, t, r) = if io::stdout().is_terminal() {
|
|
310
|
+
(
|
|
311
|
+
"\x1b[1;38;2;255;159;64m",
|
|
312
|
+
"\x1b[1;38;2;48;209;188m",
|
|
313
|
+
"\x1b[0m",
|
|
314
|
+
)
|
|
315
|
+
} else {
|
|
316
|
+
("", "", "")
|
|
317
|
+
};
|
|
318
|
+
format!(
|
|
319
|
+
"\
|
|
320
|
+
{oh}Environment variables:{r}
|
|
321
|
+
{t}TISH_NO_OPTIMIZE=1{r}
|
|
322
|
+
Disable AST and bytecode optimizations for run/build
|
|
323
|
+
{t}TISH_IGNORE_INDENT=1{r}
|
|
324
|
+
Ignore indentation syntax: parse blocks by braces only (debug nested-block transpilation)
|
|
325
|
+
|
|
326
|
+
See {t}tish run --help{r} and {t}tish build --help{r} for backend and feature options."
|
|
327
|
+
)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
fn capabilities_section(oh: &str, t: &str, r: &str) -> String {
|
|
331
|
+
format!(
|
|
332
|
+
"\
|
|
333
|
+
{oh}Backends{r} (--backend):
|
|
334
|
+
{t}vm{r}
|
|
335
|
+
Bytecode VM (default)
|
|
336
|
+
{t}interp{r}
|
|
337
|
+
Tree-walking interpreter
|
|
338
|
+
|
|
339
|
+
{oh}Capabilities{r} (--feature, repeatable; comma-separated values are split):
|
|
340
|
+
{t}http{r}
|
|
341
|
+
Network: fetch, fetchAll, serve, Promise (and `await`); enabling http also enables timers
|
|
342
|
+
{t}timers{r}
|
|
343
|
+
setTimeout, setInterval, clearTimeout, clearInterval (global + `import from \"timers\"` / tish:timers)
|
|
344
|
+
{t}fs{r}
|
|
345
|
+
Filesystem: readFile, writeFile, fileExists, isDir, readDir, mkdir
|
|
346
|
+
{t}process{r}
|
|
347
|
+
process.exit, cwd, exec, argv, env
|
|
348
|
+
{t}regex{r}
|
|
349
|
+
RegExp
|
|
350
|
+
{t}ws{r}
|
|
351
|
+
WebSocket client / server
|
|
352
|
+
{t}tty{r}
|
|
353
|
+
Interactive terminal: raw mode, key/resize events, size, alt screen (`import from \"tish:tty\"`)
|
|
354
|
+
{t}full{r}
|
|
355
|
+
All of the above (http, timers, fs, process, regex, ws, tty)
|
|
356
|
+
|
|
357
|
+
Omit --feature to allow every capability compiled into this `tish` binary; pass flags to restrict what scripts may use. The CLI is normally built with all of them (Cargo default on `tishlang`)."
|
|
358
|
+
)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/// Returns the colored `after_help` for `tish run --help`.
|
|
362
|
+
pub fn run_after_help() -> String {
|
|
363
|
+
let (oh, t, r) = if io::stdout().is_terminal() {
|
|
364
|
+
(
|
|
365
|
+
"\x1b[1;38;2;255;159;64m",
|
|
366
|
+
"\x1b[1;38;2;48;209;188m",
|
|
367
|
+
"\x1b[0m",
|
|
368
|
+
)
|
|
369
|
+
} else {
|
|
370
|
+
("", "", "")
|
|
371
|
+
};
|
|
372
|
+
capabilities_section(oh, t, r)
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/// Returns the colored `after_help` for `tish repl --help`.
|
|
376
|
+
pub fn repl_after_help() -> String {
|
|
377
|
+
let (oh, t, r) = if io::stdout().is_terminal() {
|
|
378
|
+
(
|
|
379
|
+
"\x1b[1;38;2;255;159;64m",
|
|
380
|
+
"\x1b[1;38;2;48;209;188m",
|
|
381
|
+
"\x1b[0m",
|
|
382
|
+
)
|
|
383
|
+
} else {
|
|
384
|
+
("", "", "")
|
|
385
|
+
};
|
|
386
|
+
capabilities_section(oh, t, r)
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/// Returns the colored `after_long_help` for `tish build --help`.
|
|
390
|
+
pub fn build_after_help() -> String {
|
|
391
|
+
let (oh, t, r) = if io::stdout().is_terminal() {
|
|
392
|
+
(
|
|
393
|
+
"\x1b[1;38;2;255;159;64m",
|
|
394
|
+
"\x1b[1;38;2;48;209;188m",
|
|
395
|
+
"\x1b[0m",
|
|
396
|
+
)
|
|
397
|
+
} else {
|
|
398
|
+
("", "", "")
|
|
399
|
+
};
|
|
400
|
+
format!(
|
|
401
|
+
"\
|
|
402
|
+
{oh}Build targets{r} (--target, default: native):
|
|
403
|
+
{t}native{r}
|
|
404
|
+
Native executable (see --native-backend)
|
|
405
|
+
{t}js{r}
|
|
406
|
+
JavaScript bundle
|
|
407
|
+
{t}wasm{r}
|
|
408
|
+
WebAssembly (.tish project; .js source supported on some paths)
|
|
409
|
+
{t}wasi{r}
|
|
410
|
+
WASI WebAssembly
|
|
411
|
+
{t}bytecode{r}
|
|
412
|
+
Raw serialized bytecode chunk (no VM binary/JS/HTML); for hosts that already ship the runtime
|
|
413
|
+
|
|
414
|
+
{oh}Native backends{r} (--native-backend, only with --target native, default: rust):
|
|
415
|
+
{t}rust{r}
|
|
416
|
+
Emit Rust + link tishlang_runtime via cargo
|
|
417
|
+
{t}cranelift{r}
|
|
418
|
+
Embedded bytecode + Cranelift/VM runtime binary
|
|
419
|
+
{t}llvm{r}
|
|
420
|
+
Embedded bytecode + LLVM/clang link path
|
|
421
|
+
|
|
422
|
+
{oh}Capabilities{r} (--feature, repeatable; comma-separated values are split):
|
|
423
|
+
{t}http{r}
|
|
424
|
+
Network: fetch, fetchAll, serve, Promise (and `await`); enabling http also enables timers
|
|
425
|
+
{t}timers{r}
|
|
426
|
+
setTimeout, setInterval, clearTimeout, clearInterval (global + `import from \"timers\"` / tish:timers)
|
|
427
|
+
{t}fs{r}
|
|
428
|
+
Filesystem: readFile, writeFile, fileExists, isDir, readDir, mkdir
|
|
429
|
+
{t}process{r}
|
|
430
|
+
process.exit, cwd, exec, argv, env
|
|
431
|
+
{t}regex{r}
|
|
432
|
+
RegExp
|
|
433
|
+
{t}ws{r}
|
|
434
|
+
WebSocket client / server
|
|
435
|
+
{t}tty{r}
|
|
436
|
+
Interactive terminal: raw mode, key/resize events, size, alt screen (`import from \"tish:tty\"`)
|
|
437
|
+
{t}full{r}
|
|
438
|
+
All of the above (http, timers, fs, process, regex, ws, tty)
|
|
439
|
+
|
|
440
|
+
For `--target native`, these choose what is linked into the **output** executable (omit = same set as this `tish` binary was built with). Minimal native outputs still use a full `tish` CLI unless you built it with `cargo build -p tishlang --no-default-features`."
|
|
441
|
+
)
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
#[derive(Parser)]
|
|
445
|
+
#[command(name = "tish")]
|
|
446
|
+
#[command(version = env!("CARGO_PKG_VERSION"))]
|
|
447
|
+
#[command(styles = cargo_help_styles())]
|
|
448
|
+
pub(crate) struct Cli {
|
|
449
|
+
#[command(subcommand)]
|
|
450
|
+
pub command: Option<Commands>,
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
#[derive(Parser)]
|
|
454
|
+
pub(crate) struct RunArgs {
|
|
455
|
+
/// Path to a `.tish` file, or `-` to read the program from stdin (like `node -`).
|
|
456
|
+
#[arg(
|
|
457
|
+
required = true,
|
|
458
|
+
allow_hyphen_values = true,
|
|
459
|
+
value_name = "FILE",
|
|
460
|
+
help_heading = "Arguments"
|
|
461
|
+
)]
|
|
462
|
+
pub file: String,
|
|
463
|
+
/// `vm` or `interp` (see `tish --help` for capabilities / `--feature`).
|
|
464
|
+
#[arg(
|
|
465
|
+
long,
|
|
466
|
+
default_value = "vm",
|
|
467
|
+
value_name = "NAME",
|
|
468
|
+
help_heading = "Options"
|
|
469
|
+
)]
|
|
470
|
+
pub backend: String,
|
|
471
|
+
/// Restrict which platform APIs the script may use (omit = all capabilities compiled into this `tish`).
|
|
472
|
+
#[arg(
|
|
473
|
+
long = "feature",
|
|
474
|
+
value_name = "NAME",
|
|
475
|
+
action = clap::ArgAction::Append,
|
|
476
|
+
help_heading = "Options"
|
|
477
|
+
)]
|
|
478
|
+
pub features: Vec<String>,
|
|
479
|
+
/// Disable AST and bytecode optimizations (for debugging).
|
|
480
|
+
#[arg(long, help_heading = "Options")]
|
|
481
|
+
pub no_optimize: bool,
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
#[derive(Parser)]
|
|
485
|
+
pub(crate) struct ReplArgs {
|
|
486
|
+
/// `vm` or `interp` (see `tish --help`).
|
|
487
|
+
#[arg(
|
|
488
|
+
long,
|
|
489
|
+
default_value = "vm",
|
|
490
|
+
value_name = "NAME",
|
|
491
|
+
help_heading = "Options"
|
|
492
|
+
)]
|
|
493
|
+
pub backend: String,
|
|
494
|
+
/// Restrict which platform APIs the REPL may use (omit = all capabilities compiled into this `tish`).
|
|
495
|
+
#[arg(
|
|
496
|
+
long = "feature",
|
|
497
|
+
value_name = "NAME",
|
|
498
|
+
action = clap::ArgAction::Append,
|
|
499
|
+
help_heading = "Options"
|
|
500
|
+
)]
|
|
501
|
+
pub features: Vec<String>,
|
|
502
|
+
#[arg(long, help_heading = "Options")]
|
|
503
|
+
pub no_optimize: bool,
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
#[derive(Parser)]
|
|
507
|
+
pub(crate) struct BuildArgs {
|
|
508
|
+
#[arg(
|
|
509
|
+
short,
|
|
510
|
+
long,
|
|
511
|
+
default_value = "tish_out",
|
|
512
|
+
value_name = "PATH",
|
|
513
|
+
help_heading = "Options"
|
|
514
|
+
)]
|
|
515
|
+
pub output: String,
|
|
516
|
+
/// `native`, `js`, `wasm`, `wasi`, or `bytecode` (see long help below).
|
|
517
|
+
#[arg(
|
|
518
|
+
long,
|
|
519
|
+
default_value = "native",
|
|
520
|
+
value_name = "TARGET",
|
|
521
|
+
help_heading = "Options"
|
|
522
|
+
)]
|
|
523
|
+
pub target: String,
|
|
524
|
+
/// `rust`, `cranelift`, or `llvm` (only for `--target native`).
|
|
525
|
+
#[arg(
|
|
526
|
+
long,
|
|
527
|
+
default_value = "rust",
|
|
528
|
+
value_name = "BACKEND",
|
|
529
|
+
help_heading = "Options"
|
|
530
|
+
)]
|
|
531
|
+
pub native_backend: String,
|
|
532
|
+
/// For `--target native`: which capabilities to link into the produced binary (omit = same as this `tish`; see long help).
|
|
533
|
+
#[arg(
|
|
534
|
+
long = "feature",
|
|
535
|
+
value_name = "NAME",
|
|
536
|
+
action = clap::ArgAction::Append,
|
|
537
|
+
help_heading = "Options"
|
|
538
|
+
)]
|
|
539
|
+
pub features: Vec<String>,
|
|
540
|
+
/// Cross-compile to an Apple iOS triple (e.g. `aarch64-apple-ios-sim`). Implies `--crate-type staticlib`.
|
|
541
|
+
#[arg(long, value_name = "TRIPLE", help_heading = "Options")]
|
|
542
|
+
pub ios_triple: Option<String>,
|
|
543
|
+
/// Output artifact for `--target native` (default: `bin`; use `staticlib` for embedded iOS).
|
|
544
|
+
#[arg(long, value_name = "TYPE", default_value = "bin", help_heading = "Options")]
|
|
545
|
+
pub crate_type: String,
|
|
546
|
+
#[arg(long, help_heading = "Options")]
|
|
547
|
+
pub no_optimize: bool,
|
|
548
|
+
/// For `--target js` project builds: emit `OUTPUT.js.map` and `//# sourceMappingURL=…` so JS/TS tools can jump to original `.tish` (implies `--no-optimize` for that build).
|
|
549
|
+
#[arg(long, help_heading = "Options")]
|
|
550
|
+
pub source_map: bool,
|
|
551
|
+
/// Run the gradual type checker: `warn` prints `line:col` type diagnostics; `error` also fails the build on them. (Equivalent to setting `TISH_CHECK`.)
|
|
552
|
+
#[arg(long, value_name = "MODE", help_heading = "Options")]
|
|
553
|
+
pub check: Option<String>,
|
|
554
|
+
/// Entry `.tish` file (or `.js` where supported).
|
|
555
|
+
#[arg(required = true, value_name = "FILE", help_heading = "Arguments")]
|
|
556
|
+
pub file: String,
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
#[derive(Subcommand)]
|
|
560
|
+
pub(crate) enum Commands {
|
|
561
|
+
/// Run a Tish file (interpret)
|
|
562
|
+
Run(RunArgs),
|
|
563
|
+
/// Interactive REPL
|
|
564
|
+
Repl(ReplArgs),
|
|
565
|
+
/// Build native binary, wasm, wasi, or JavaScript output
|
|
566
|
+
Build(BuildArgs),
|
|
567
|
+
/// Parse and dump AST
|
|
568
|
+
#[command(name = "dump-ast")]
|
|
569
|
+
DumpAst {
|
|
570
|
+
#[arg(required = true, value_name = "FILE", help_heading = "Arguments")]
|
|
571
|
+
file: String,
|
|
572
|
+
/// Ignore indentation syntax: parse blocks by braces only (same as TISH_IGNORE_INDENT=1).
|
|
573
|
+
#[arg(long, help_heading = "Options")]
|
|
574
|
+
ignore_indent: bool,
|
|
575
|
+
},
|
|
576
|
+
}
|