summaryrefslogtreecommitdiff
path: root/paint.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-07-05 20:21:50 -0300
committerGabriel Schneider <[email protected]>2026-08-01 15:02:07 -0300
commitde4def3548a6729b0dfd2120495a61beef8c8c2c (patch)
tree12144f0f64dc96bb4741459c1f9db57f44349330 /paint.zig
parent7988d9bc6e31210ff13994f18d5622dd57ba9341 (diff)
downloadpardes-de4def3548a6729b0dfd2120495a61beef8c8c2c.tar.gz
pardes-de4def3548a6729b0dfd2120495a61beef8c8c2c.zip
pardes v2: the rewrite, complete and organized. src/ (core + three shells), test/ (snapshot parity harness + 18 frozen goldens). One sans-IO core, vaxis tty + SDL3 GPU native + wasm web shells, 18/18 parity with the purged prototype, 7.6k lines vs 12.1k. Fix: gui shell pre-sized the core at init so the greet-releasing resize never fired (blank panes until first interaction); live sessions now init at defaults and get the real grid as a resize event (the shell contract, documented on Options).
Diffstat (limited to 'paint.zig')
-rw-r--r--paint.zig340
1 files changed, 0 insertions, 340 deletions
diff --git a/paint.zig b/paint.zig
deleted file mode 100644
index 97efc334..00000000
--- a/paint.zig
+++ /dev/null
@@ -1,340 +0,0 @@
-const std = @import("std");
-const ui = @import("ui");
-const app_config = @import("app_config");
-const ghostty_vt = @import("ghostty-vt");
-const syntax_highlighting = app_config.syntax_highlighting;
-const syntax_minimal_grammars = app_config.syntax_minimal_grammars;
-const syntax_full_grammars = app_config.syntax_full_grammars;
-const ts = if (syntax_highlighting) @import("tree-sitter") else struct {
- pub const Language = opaque {};
- pub const Query = opaque {};
-};
-const ts_queries = if (syntax_highlighting) @import("ts_queries") else struct {};
-
-extern fn tree_sitter_ada() callconv(.c) *const ts.Language;
-extern fn tree_sitter_bash() callconv(.c) *const ts.Language;
-extern fn tree_sitter_c() callconv(.c) *const ts.Language;
-extern fn tree_sitter_c_sharp() callconv(.c) *const ts.Language;
-extern fn tree_sitter_clojure() callconv(.c) *const ts.Language;
-extern fn tree_sitter_cpp() callconv(.c) *const ts.Language;
-extern fn tree_sitter_css() callconv(.c) *const ts.Language;
-extern fn tree_sitter_elixir() callconv(.c) *const ts.Language;
-extern fn tree_sitter_erlang() callconv(.c) *const ts.Language;
-extern fn tree_sitter_fortran() callconv(.c) *const ts.Language;
-extern fn tree_sitter_go() callconv(.c) *const ts.Language;
-extern fn tree_sitter_haskell() callconv(.c) *const ts.Language;
-extern fn tree_sitter_html() callconv(.c) *const ts.Language;
-extern fn tree_sitter_java() callconv(.c) *const ts.Language;
-extern fn tree_sitter_javascript() callconv(.c) *const ts.Language;
-extern fn tree_sitter_json() callconv(.c) *const ts.Language;
-extern fn tree_sitter_kotlin() callconv(.c) *const ts.Language;
-extern fn tree_sitter_ocaml() callconv(.c) *const ts.Language;
-extern fn tree_sitter_pascal() callconv(.c) *const ts.Language;
-extern fn tree_sitter_php() callconv(.c) *const ts.Language;
-extern fn tree_sitter_powershell() callconv(.c) *const ts.Language;
-extern fn tree_sitter_python() callconv(.c) *const ts.Language;
-extern fn tree_sitter_ruby() callconv(.c) *const ts.Language;
-extern fn tree_sitter_rust() callconv(.c) *const ts.Language;
-extern fn tree_sitter_scala() callconv(.c) *const ts.Language;
-extern fn tree_sitter_zig() callconv(.c) *const ts.Language;
-
-pub const accent = if (app_config.app_kind == .replay) [3]u8{ 0xc8, 0x58, 0x58 } else [3]u8{ 0x6e, 0x4f, 0xd0 };
-pub const accent_dim = if (app_config.app_kind == .replay) [3]u8{ 0x54, 0x38, 0x38 } else [3]u8{ 0x38, 0x3c, 0x54 };
-const dark_tag_bg = if (app_config.app_kind == .replay) [3]u8{ 0x3e, 0x2a, 0x2a } else [3]u8{ 0x2c, 0x2a, 0x3e };
-
-pub const Theme = struct {
- name: []const u8,
- bg: ?[3]u8,
- fg: ?[3]u8,
- tag_bg: [3]u8,
- tag_fg: [3]u8,
- kw: [3]u8,
- str: [3]u8,
- num: [3]u8,
- comment: [3]u8,
- lineno: [3]u8,
- scroll_track: [3]u8,
- scroll_thumb: [3]u8,
- palette: ?[16][3]u8,
-};
-
-pub const themes = [_]Theme{
- .{
- .name = "dark",
- .bg = null,
- .fg = null,
- .tag_bg = dark_tag_bg,
- .tag_fg = .{ 0x96, 0x96, 0x96 },
- .kw = .{ 0xcc, 0x70, 0xd8 },
- .str = .{ 0x8a, 0xb8, 0x7a },
- .num = .{ 0xd0, 0xc0, 0x70 },
- .comment = .{ 0xf0, 0xf0, 0xf0 },
- .lineno = .{ 0x5a, 0x5a, 0x5a },
- .scroll_track = .{ 0x2a, 0x2a, 0x2a },
- .scroll_thumb = .{ 0x52, 0x52, 0x52 },
- .palette = null,
- },
- .{
- .name = "acme",
- .bg = .{ 0xff, 0xff, 0xea },
- .fg = .{ 0x00, 0x00, 0x00 },
- .tag_bg = .{ 0xea, 0xff, 0xff },
- .tag_fg = .{ 0x00, 0x00, 0x00 },
- .kw = .{ 0x88, 0x22, 0x99 },
- .str = .{ 0x00, 0x77, 0x33 },
- .num = .{ 0xa0, 0x52, 0x00 },
- .comment = .{ 0x55, 0x55, 0x55 },
- .lineno = .{ 0x99, 0x99, 0x80 },
- .scroll_track = .{ 0x99, 0x99, 0x4c },
- .scroll_thumb = .{ 0xff, 0xff, 0xea },
- .palette = .{
- .{ 0x00, 0x00, 0x00 }, .{ 0xaa, 0x22, 0x22 }, .{ 0x22, 0x80, 0x22 }, .{ 0x88, 0x66, 0x00 },
- .{ 0x22, 0x44, 0xbb }, .{ 0x99, 0x33, 0x99 }, .{ 0x11, 0x88, 0x88 }, .{ 0x55, 0x55, 0x55 },
- .{ 0x88, 0x88, 0x88 }, .{ 0xcc, 0x33, 0x33 }, .{ 0x33, 0xaa, 0x33 }, .{ 0xaa, 0x77, 0x00 },
- .{ 0x33, 0x55, 0xdd }, .{ 0xbb, 0x44, 0xbb }, .{ 0x22, 0xaa, 0xaa }, .{ 0x00, 0x00, 0x00 },
- },
- },
-};
-
-var theme_idx: usize = 0;
-
-pub fn T() *const Theme {
- return &themes[theme_idx];
-}
-
-pub fn nextTheme() void {
- theme_idx = (theme_idx + 1) % themes.len;
-}
-
-pub fn currentThemeName() []const u8 {
- return T().name;
-}
-
-pub fn setThemeByName(name: []const u8) bool {
- for (themes, 0..) |theme, i| {
- if (std.mem.eql(u8, theme.name, name)) {
- theme_idx = i;
- return true;
- }
- }
- return false;
-}
-
-pub fn rgbColor(c: [3]u8) ui.Color {
- return .{ .rgb = c };
-}
-
-pub fn colorOpt(c: ?[3]u8) ui.Color {
- return if (c) |x| .{ .rgb = x } else .default;
-}
-
-pub const Syn = enum(u8) { none, keyword, string, number, comment };
-
-pub const SynStyle = struct {
- fg: [3]u8,
- bold: bool,
-};
-
-const HighlightLanguage = struct {
- lang: *const ts.Language,
- query: *ts.Query,
-};
-
-const HighlightTier = enum { minimal, full };
-
-const HighlightSpec = struct {
- tier: HighlightTier,
- exts: []const []const u8,
- language: *const fn () callconv(.c) *const ts.Language,
- query_src: []const u8,
- compiled_query: ?*ts.Query = null,
-};
-
-var highlight_specs = if (syntax_minimal_grammars) [_]HighlightSpec{
- .{ .tier = .minimal, .exts = &[_][]const u8{ ".c", ".h" }, .language = tree_sitter_c, .query_src = ts_queries.c_highlights },
- .{ .tier = .minimal, .exts = &[_][]const u8{ ".cpp", ".cc", ".cxx", ".hpp", ".hh", ".hxx" }, .language = tree_sitter_cpp, .query_src = ts_queries.cpp_highlights },
- .{ .tier = .minimal, .exts = &[_][]const u8{ ".zig", ".zon" }, .language = tree_sitter_zig, .query_src = ts_queries.zig_highlights },
-} else [_]HighlightSpec{};
-
-var full_highlight_specs = if (syntax_full_grammars) [_]HighlightSpec{
- .{ .tier = .full, .exts = &[_][]const u8{ ".adb", ".ads", ".ada" }, .language = tree_sitter_ada, .query_src = ts_queries.ada_highlights },
- .{ .tier = .full, .exts = &[_][]const u8{ ".sh", ".bash", ".zsh" }, .language = tree_sitter_bash, .query_src = ts_queries.bash_highlights },
- .{ .tier = .full, .exts = &[_][]const u8{ ".cs", ".csx" }, .language = tree_sitter_c_sharp, .query_src = ts_queries.c_sharp_highlights },
- .{ .tier = .full, .exts = &[_][]const u8{ ".clj", ".cljs", ".cljc", ".edn" }, .language = tree_sitter_clojure, .query_src = ts_queries.clojure_highlights },
- .{ .tier = .full, .exts = &[_][]const u8{".css"}, .language = tree_sitter_css, .query_src = ts_queries.css_highlights },
- .{ .tier = .full, .exts = &[_][]const u8{ ".ex", ".exs" }, .language = tree_sitter_elixir, .query_src = ts_queries.elixir_highlights },
- .{ .tier = .full, .exts = &[_][]const u8{ ".erl", ".hrl" }, .language = tree_sitter_erlang, .query_src = ts_queries.erlang_highlights },
- .{ .tier = .full, .exts = &[_][]const u8{ ".f", ".for", ".ftn", ".f90", ".f95", ".f03", ".f08" }, .language = tree_sitter_fortran, .query_src = ts_queries.fortran_highlights },
- .{ .tier = .full, .exts = &[_][]const u8{".go"}, .language = tree_sitter_go, .query_src = ts_queries.go_highlights },
- .{ .tier = .full, .exts = &[_][]const u8{ ".hs", ".lhs" }, .language = tree_sitter_haskell, .query_src = ts_queries.haskell_highlights },
- .{ .tier = .full, .exts = &[_][]const u8{ ".html", ".htm" }, .language = tree_sitter_html, .query_src = ts_queries.html_highlights },
- .{ .tier = .full, .exts = &[_][]const u8{".java"}, .language = tree_sitter_java, .query_src = ts_queries.java_highlights },
- .{ .tier = .full, .exts = &[_][]const u8{ ".js", ".jsx", ".mjs", ".cjs" }, .language = tree_sitter_javascript, .query_src = ts_queries.javascript_highlights },
- .{ .tier = .full, .exts = &[_][]const u8{".json"}, .language = tree_sitter_json, .query_src = ts_queries.json_highlights },
- .{ .tier = .full, .exts = &[_][]const u8{ ".kt", ".kts" }, .language = tree_sitter_kotlin, .query_src = ts_queries.kotlin_highlights },
- .{ .tier = .full, .exts = &[_][]const u8{ ".ml", ".mli" }, .language = tree_sitter_ocaml, .query_src = ts_queries.ocaml_highlights },
- .{ .tier = .full, .exts = &[_][]const u8{ ".pas", ".pp", ".p" }, .language = tree_sitter_pascal, .query_src = ts_queries.pascal_highlights },
- .{ .tier = .full, .exts = &[_][]const u8{ ".php", ".phtml", ".php3", ".php4", ".php5" }, .language = tree_sitter_php, .query_src = ts_queries.php_highlights },
- .{ .tier = .full, .exts = &[_][]const u8{ ".ps1", ".psm1", ".psd1" }, .language = tree_sitter_powershell, .query_src = ts_queries.powershell_highlights },
- .{ .tier = .full, .exts = &[_][]const u8{ ".py", ".pyw" }, .language = tree_sitter_python, .query_src = ts_queries.python_highlights },
- .{ .tier = .full, .exts = &[_][]const u8{ ".rb", ".rake" }, .language = tree_sitter_ruby, .query_src = ts_queries.ruby_highlights },
- .{ .tier = .full, .exts = &[_][]const u8{".rs"}, .language = tree_sitter_rust, .query_src = ts_queries.rust_highlights },
- .{ .tier = .full, .exts = &[_][]const u8{ ".scala", ".sc" }, .language = tree_sitter_scala, .query_src = ts_queries.scala_highlights },
-} else [_]HighlightSpec{};
-
-fn tierEnabled(tier: HighlightTier) bool {
- return switch (tier) {
- .minimal => syntax_minimal_grammars,
- .full => syntax_full_grammars,
- };
-}
-
-fn specMatchesExt(spec: HighlightSpec, ext: []const u8) bool {
- for (spec.exts) |choice| {
- if (std.ascii.eqlIgnoreCase(ext, choice)) return true;
- }
- return false;
-}
-
-fn ensureHighlightLanguage(spec: *HighlightSpec) !HighlightLanguage {
- const lang = spec.language();
- if (spec.compiled_query) |query| return .{ .lang = lang, .query = query };
- var error_offset: u32 = 0;
- const query = try ts.Query.create(lang, spec.query_src, &error_offset);
- spec.compiled_query = query;
- return .{ .lang = lang, .query = query };
-}
-
-fn highlightLanguageForExt(ext: []const u8) !?HighlightLanguage {
- for (&highlight_specs) |*spec| {
- if (!tierEnabled(spec.tier)) continue;
- if (!specMatchesExt(spec.*, ext)) continue;
- return try ensureHighlightLanguage(spec);
- }
- for (&full_highlight_specs) |*spec| {
- if (!tierEnabled(spec.tier)) continue;
- if (!specMatchesExt(spec.*, ext)) continue;
- return try ensureHighlightLanguage(spec);
- }
- return null;
-}
-
-pub fn compileStartupHighlightQueries() void {
- if (!syntax_highlighting) return;
- for (&highlight_specs) |*spec| {
- if (spec.tier != .minimal) continue;
- if (!tierEnabled(spec.tier)) continue;
- _ = ensureHighlightLanguage(spec) catch {};
- }
-}
-
-pub fn synStyle(s: Syn) ?SynStyle {
- return switch (s) {
- .none => null,
- .keyword => .{ .fg = T().kw, .bold = true },
- .string => .{ .fg = T().str, .bold = false },
- .number => .{ .fg = T().num, .bold = false },
- .comment => .{ .fg = T().comment, .bold = true },
- };
-}
-
-fn synFor(name: []const u8) Syn {
- for ([_]struct { []const u8, Syn }{
- .{ "comment", .comment },
- .{ "string", .string },
- .{ "character", .string },
- .{ "number", .number },
- .{ "float", .number },
- .{ "boolean", .number },
- .{ "keyword", .keyword },
- .{ "include", .keyword },
- .{ "conditional", .keyword },
- .{ "repeat", .keyword },
- }) |m| {
- if (std.mem.indexOf(u8, name, m[0]) != null) return m[1];
- }
- return .none;
-}
-
-pub fn highlightFile(gpa: std.mem.Allocator, path: []const u8, content: []const u8) ![]u8 {
- return highlightFileRange(gpa, path, content, 0, content.len);
-}
-
-pub fn highlightFileRange(gpa: std.mem.Allocator, path: []const u8, content: []const u8, start_byte_raw: usize, end_byte_raw: usize) ![]u8 {
- if (!syntax_highlighting) return &.{};
- const ext = std.fs.path.extension(path);
- const selected = (highlightLanguageForExt(ext) catch return &.{}) orelse return &.{};
-
- const start_byte = @min(start_byte_raw, content.len);
- const end_byte = @max(start_byte, @min(end_byte_raw, content.len));
- const source = content[start_byte..end_byte];
- const styles = try gpa.alloc(u8, source.len);
- errdefer gpa.free(styles);
- @memset(styles, 0);
-
- const parser = ts.Parser.create();
- defer parser.destroy();
- parser.setLanguage(selected.lang) catch return styles;
- const tree = parser.parseString(source, null) orelse return styles;
- defer tree.destroy();
-
- const cursor = ts.QueryCursor.create();
- defer cursor.destroy();
- cursor.exec(selected.query, tree.rootNode());
-
- while (cursor.nextMatch()) |match| {
- for (match.captures) |cap| {
- const syn = synFor(selected.query.captureNameForId(cap.index) orelse "");
- if (syn == .none) continue;
- var b: usize = cap.node.startByte();
- const end = @min(@as(usize, cap.node.endByte()), styles.len);
- while (b < end) : (b += 1) styles[b] = @intFromEnum(syn);
- }
- }
- return styles;
-}
-
-fn palColor(idx: u8) ui.Color {
- if (T().palette) |p| if (idx < 16) return rgbColor(p[idx]);
- return .{ .index = idx };
-}
-
-fn ghostColor(color: ghostty_vt.Style.Color, is_bg: bool) ui.Color {
- return switch (color) {
- .none => colorOpt(if (is_bg) T().bg else T().fg),
- .palette => |idx| palColor(idx),
- .rgb => |rgb| .{ .rgb = .{ rgb.r, rgb.g, rgb.b } },
- };
-}
-
-pub fn cellStyle(ci: ghostty_vt.PageList.Cell) ui.Style {
- const style = ci.style();
- var vs: ui.Style = .{
- .fg = ghostColor(style.fg_color, false),
- .bg = ghostColor(style.bg_color, true),
- .bold = style.flags.bold,
- .dim = style.flags.faint,
- .italic = style.flags.italic,
- .blink = style.flags.blink,
- .reverse = style.flags.inverse,
- .invisible = style.flags.invisible,
- .strikethrough = style.flags.strikethrough,
- .ul_style = switch (style.flags.underline) {
- .none => .off,
- .single => .single,
- .double => .double,
- .curly => .curly,
- .dotted => .dotted,
- .dashed => .dashed,
- },
- };
- switch (ci.cell.content_tag) {
- .bg_color_palette => vs.bg = palColor(ci.cell.content.color_palette.data),
- .bg_color_rgb => {
- const rgb = ci.cell.content.color_rgb;
- vs.bg = .{ .rgb = .{ rgb.r, rgb.g, rgb.b } };
- },
- else => {},
- }
- return vs;
-}