From de4def3548a6729b0dfd2120495a61beef8c8c2c Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Sun, 5 Jul 2026 20:21:50 -0300 Subject: 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). --- paint.zig | 340 -------------------------------------------------------------- 1 file changed, 340 deletions(-) delete mode 100644 paint.zig (limited to 'paint.zig') 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; -} -- cgit v1.3