diff --git a/src/font/shape.zig b/src/font/shape.zig index cc67fc7a0..5e1e30eec 100644 --- a/src/font/shape.zig +++ b/src/font/shape.zig @@ -2,6 +2,9 @@ const builtin = @import("builtin"); const options = @import("main.zig").options; const run = @import("shaper/run.zig"); const feature = @import("shaper/feature.zig"); +const configpkg = @import("../config.zig"); +const terminal = @import("../terminal/main.zig"); +const SharedGrid = @import("main.zig").SharedGrid; pub const noop = @import("shaper/noop.zig"); pub const harfbuzz = @import("shaper/harfbuzz.zig"); pub const coretext = @import("shaper/coretext.zig"); @@ -61,6 +64,38 @@ pub const Options = struct { features: []const []const u8 = &.{}, }; +/// Options for runIterator. +pub const RunOptions = struct { + /// The font state for the terminal screen. This is mutable because + /// cached values may be updated during shaping. + grid: *SharedGrid, + + /// The terminal screen to shape. + screen: *const terminal.Screen, + + /// The row within the screen to shape. This row must exist within + /// screen; it is not validated. + row: terminal.Pin, + + /// The selection boundaries. This is used to break shaping on + /// selection boundaries. This can be disabled by setting this to + /// null. + selection: ?terminal.Selection = null, + + /// The cursor position within this row. This is used to break shaping + /// on cursor boundaries. This can be disabled by setting this to + /// null. + cursor_x: ?usize = null, + + /// Apply the font break configuration to the run. + pub fn applyBreakConfig( + self: *RunOptions, + config: configpkg.FontShapingBreak, + ) void { + if (!config.cursor) self.cursor_x = null; + } +}; + test { _ = Cache; _ = Shaper; diff --git a/src/font/shaper/coretext.zig b/src/font/shaper/coretext.zig index 654af02d9..1fd9719bb 100644 --- a/src/font/shaper/coretext.zig +++ b/src/font/shaper/coretext.zig @@ -289,21 +289,11 @@ pub const Shaper = struct { pub fn runIterator( self: *Shaper, - grid: *SharedGrid, - screen: *const terminal.Screen, - row: terminal.Pin, - selection: ?terminal.Selection, - cursor_x: ?usize, - break_config: config.FontShapingBreak, + opts: font.shape.RunOptions, ) font.shape.RunIterator { return .{ .hooks = .{ .shaper = self }, - .grid = grid, - .screen = screen, - .row = row, - .selection = selection, - .cursor_x = cursor_x, - .break_config = break_config, + .opts = opts, }; } @@ -597,14 +587,11 @@ test "run iterator" { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |_| count += 1; try testing.expectEqual(@as(usize, 1), count); @@ -617,14 +604,11 @@ test "run iterator" { try screen.testWriteString("ABCD EFG"); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |_| count += 1; try testing.expectEqual(@as(usize, 1), count); @@ -638,14 +622,11 @@ test "run iterator" { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |_| count += 1; try testing.expectEqual(@as(usize, 3), count); @@ -660,14 +641,11 @@ test "run iterator" { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |_| count += 1; try testing.expectEqual(@as(usize, 2), count); @@ -708,14 +686,11 @@ test "run iterator: empty cells with background set" { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); { const run = (try it.next(alloc)).?; const cells = try shaper.shape(run); @@ -745,14 +720,11 @@ test "shape" { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -781,14 +753,11 @@ test "shape nerd fonts" { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -810,14 +779,11 @@ test "shape inconsolata ligs" { try screen.testWriteString(">="); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -836,14 +802,11 @@ test "shape inconsolata ligs" { try screen.testWriteString("==="); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -870,14 +833,11 @@ test "shape monaspace ligs" { try screen.testWriteString("==="); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -905,14 +865,11 @@ test "shape left-replaced lig in last run" { try screen.testWriteString("!=="); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -940,14 +897,11 @@ test "shape left-replaced lig in early run" { try screen.testWriteString("!==X"); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); const run = (try it.next(alloc)).?; @@ -972,14 +926,11 @@ test "shape U+3C9 with JB Mono" { try screen.testWriteString("\u{03C9} foo"); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var run_count: usize = 0; var cell_count: usize = 0; @@ -1006,14 +957,11 @@ test "shape emoji width" { try screen.testWriteString("👍"); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1057,14 +1005,11 @@ test "shape emoji width long" { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 1 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 1 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1094,14 +1039,11 @@ test "shape variation selector VS15" { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1130,14 +1072,11 @@ test "shape variation selector VS16" { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1163,14 +1102,11 @@ test "shape with empty cells in between" { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1202,14 +1138,11 @@ test "shape Chinese characters" { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1243,14 +1176,11 @@ test "shape box glyphs" { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1280,18 +1210,16 @@ test "shape selection boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - terminal.Selection.init( + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + .selection = terminal.Selection.init( screen.pages.pin(.{ .active = .{ .x = 0, .y = 0 } }).?, screen.pages.pin(.{ .active = .{ .x = screen.pages.cols - 1, .y = 0 } }).?, false, ), - null, - .{}, - ); + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1304,18 +1232,16 @@ test "shape selection boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - terminal.Selection.init( + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + .selection = terminal.Selection.init( screen.pages.pin(.{ .active = .{ .x = 2, .y = 0 } }).?, screen.pages.pin(.{ .active = .{ .x = screen.pages.cols - 1, .y = 0 } }).?, false, ), - null, - .{}, - ); + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1328,18 +1254,16 @@ test "shape selection boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - terminal.Selection.init( + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + .selection = terminal.Selection.init( screen.pages.pin(.{ .active = .{ .x = 0, .y = 0 } }).?, screen.pages.pin(.{ .active = .{ .x = 3, .y = 0 } }).?, false, ), - null, - .{}, - ); + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1352,18 +1276,16 @@ test "shape selection boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - terminal.Selection.init( + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + .selection = terminal.Selection.init( screen.pages.pin(.{ .active = .{ .x = 1, .y = 0 } }).?, screen.pages.pin(.{ .active = .{ .x = 3, .y = 0 } }).?, false, ), - null, - .{}, - ); + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1376,18 +1298,16 @@ test "shape selection boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - terminal.Selection.init( + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + .selection = terminal.Selection.init( screen.pages.pin(.{ .active = .{ .x = 1, .y = 0 } }).?, screen.pages.pin(.{ .active = .{ .x = 1, .y = 0 } }).?, false, ), - null, - .{}, - ); + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1413,14 +1333,11 @@ test "shape cursor boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1434,14 +1351,12 @@ test "shape cursor boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - 0, - .{ .cursor = true }, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + .cursor_x = 0, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1453,14 +1368,11 @@ test "shape cursor boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - 0, - .{ .cursor = false }, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1475,14 +1387,12 @@ test "shape cursor boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - 1, - .{ .cursor = true }, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + .cursor_x = 1, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1494,14 +1404,11 @@ test "shape cursor boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - 1, - .{ .cursor = false }, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1515,14 +1422,12 @@ test "shape cursor boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - 9, - .{ .cursor = true }, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + .cursor_x = 9, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1534,14 +1439,11 @@ test "shape cursor boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - 9, - .{ .cursor = false }, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1568,14 +1470,11 @@ test "shape cursor boundary and colored emoji" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1588,14 +1487,12 @@ test "shape cursor boundary and colored emoji" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - 0, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + .cursor_x = 0, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1606,14 +1503,11 @@ test "shape cursor boundary and colored emoji" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - 0, - .{ .cursor = false }, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1624,14 +1518,12 @@ test "shape cursor boundary and colored emoji" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - 1, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + .cursor_x = 1, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1642,14 +1534,11 @@ test "shape cursor boundary and colored emoji" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - 1, - .{ .cursor = false }, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1673,14 +1562,11 @@ test "shape cell attribute change" { try screen.testWriteString(">="); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1698,14 +1584,11 @@ test "shape cell attribute change" { try screen.testWriteString("="); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1724,14 +1607,11 @@ test "shape cell attribute change" { try screen.testWriteString("="); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1750,14 +1630,11 @@ test "shape cell attribute change" { try screen.testWriteString("="); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1775,14 +1652,11 @@ test "shape cell attribute change" { try screen.testWriteString("="); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1816,14 +1690,11 @@ test "shape high plane sprite font codepoint" { try screen.testWriteString("\u{1FB70}"); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); // We should get one run const run = (try it.next(alloc)).?; // The run state should have the UTF-16 encoding of the character. diff --git a/src/font/shaper/harfbuzz.zig b/src/font/shaper/harfbuzz.zig index 56654e88f..4209f795c 100644 --- a/src/font/shaper/harfbuzz.zig +++ b/src/font/shaper/harfbuzz.zig @@ -90,21 +90,11 @@ pub const Shaper = struct { /// and assume the y value matches. pub fn runIterator( self: *Shaper, - grid: *SharedGrid, - screen: *const terminal.Screen, - row: terminal.Pin, - selection: ?terminal.Selection, - cursor_x: ?usize, - break_config: config.FontShapingBreak, + opts: font.shape.RunOptions, ) font.shape.RunIterator { return .{ .hooks = .{ .shaper = self }, - .grid = grid, - .screen = screen, - .row = row, - .selection = selection, - .cursor_x = cursor_x, - .break_config = break_config, + .opts = opts, }; } @@ -228,14 +218,11 @@ test "run iterator" { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |_| count += 1; try testing.expectEqual(@as(usize, 1), count); @@ -248,14 +235,11 @@ test "run iterator" { try screen.testWriteString("ABCD EFG"); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |_| count += 1; try testing.expectEqual(@as(usize, 1), count); @@ -269,14 +253,11 @@ test "run iterator" { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |_| { count += 1; @@ -322,14 +303,11 @@ test "run iterator: empty cells with background set" { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); { const run = (try it.next(alloc)).?; try testing.expectEqual(@as(u32, 3), shaper.hb_buf.getLength()); @@ -360,14 +338,11 @@ test "shape" { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -390,14 +365,11 @@ test "shape inconsolata ligs" { try screen.testWriteString(">="); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -416,14 +388,11 @@ test "shape inconsolata ligs" { try screen.testWriteString("==="); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -450,14 +419,11 @@ test "shape monaspace ligs" { try screen.testWriteString("==="); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -487,14 +453,11 @@ test "shape arabic forced LTR" { try screen.testWriteString(@embedFile("testdata/arabic.txt")); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -525,14 +488,11 @@ test "shape emoji width" { try screen.testWriteString("👍"); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -578,14 +538,11 @@ test "shape emoji width long" { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 1 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 1 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -617,14 +574,11 @@ test "shape variation selector VS15" { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -655,14 +609,11 @@ test "shape variation selector VS16" { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -690,14 +641,11 @@ test "shape with empty cells in between" { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -729,14 +677,11 @@ test "shape Chinese characters" { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -770,14 +715,11 @@ test "shape box glyphs" { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -808,18 +750,16 @@ test "shape selection boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - terminal.Selection.init( + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + .selection = terminal.Selection.init( screen.pages.pin(.{ .active = .{ .x = 0, .y = 0 } }).?, screen.pages.pin(.{ .active = .{ .x = screen.pages.cols - 1, .y = 0 } }).?, false, ), - null, - .{}, - ); + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -832,18 +772,16 @@ test "shape selection boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - terminal.Selection.init( + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + .selection = terminal.Selection.init( screen.pages.pin(.{ .active = .{ .x = 2, .y = 0 } }).?, screen.pages.pin(.{ .active = .{ .x = screen.pages.cols - 1, .y = 0 } }).?, false, ), - null, - .{}, - ); + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -856,18 +794,16 @@ test "shape selection boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - terminal.Selection.init( + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + .selection = terminal.Selection.init( screen.pages.pin(.{ .active = .{ .x = 0, .y = 0 } }).?, screen.pages.pin(.{ .active = .{ .x = 3, .y = 0 } }).?, false, ), - null, - .{}, - ); + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -880,18 +816,16 @@ test "shape selection boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - terminal.Selection.init( + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + .selection = terminal.Selection.init( screen.pages.pin(.{ .active = .{ .x = 1, .y = 0 } }).?, screen.pages.pin(.{ .active = .{ .x = 3, .y = 0 } }).?, false, ), - null, - .{}, - ); + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -904,18 +838,16 @@ test "shape selection boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - terminal.Selection.init( + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + .selection = terminal.Selection.init( screen.pages.pin(.{ .active = .{ .x = 1, .y = 0 } }).?, screen.pages.pin(.{ .active = .{ .x = 1, .y = 0 } }).?, false, ), - null, - .{}, - ); + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -941,14 +873,11 @@ test "shape cursor boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -962,14 +891,12 @@ test "shape cursor boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - 0, - .{ .cursor = true }, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + .cursor_x = 0, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -981,14 +908,11 @@ test "shape cursor boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - 0, - .{ .cursor = false }, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1003,14 +927,12 @@ test "shape cursor boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - 1, - .{ .cursor = true }, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + .cursor_x = 1, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1022,14 +944,11 @@ test "shape cursor boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - 1, - .{ .cursor = false }, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1043,14 +962,12 @@ test "shape cursor boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - 9, - .{ .cursor = true }, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + .cursor_x = 9, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1062,14 +979,11 @@ test "shape cursor boundary" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - 9, - .{ .cursor = false }, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1096,14 +1010,11 @@ test "shape cursor boundary and colored emoji" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1116,14 +1027,12 @@ test "shape cursor boundary and colored emoji" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - 0, - .{ .cursor = true }, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + .cursor_x = 0, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1134,14 +1043,11 @@ test "shape cursor boundary and colored emoji" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - 0, - .{ .cursor = false }, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1152,14 +1058,12 @@ test "shape cursor boundary and colored emoji" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - 1, - .{ .cursor = true }, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + .cursor_x = 1, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1170,14 +1074,11 @@ test "shape cursor boundary and colored emoji" { { // Get our run iterator var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - 1, - .{ .cursor = false }, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1201,14 +1102,11 @@ test "shape cell attribute change" { try screen.testWriteString(">="); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1226,14 +1124,11 @@ test "shape cell attribute change" { try screen.testWriteString("="); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1252,14 +1147,11 @@ test "shape cell attribute change" { try screen.testWriteString("="); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1278,14 +1170,11 @@ test "shape cell attribute change" { try screen.testWriteString("="); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; @@ -1303,14 +1192,11 @@ test "shape cell attribute change" { try screen.testWriteString("="); var shaper = &testdata.shaper; - var it = shaper.runIterator( - testdata.grid, - &screen, - screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, - null, - null, - .{}, - ); + var it = shaper.runIterator(.{ + .grid = testdata.grid, + .screen = &screen, + .row = screen.pages.pin(.{ .screen = .{ .y = 0 } }).?, + }); var count: usize = 0; while (try it.next(alloc)) |run| { count += 1; diff --git a/src/font/shaper/noop.zig b/src/font/shaper/noop.zig index 1041954e6..8723071d7 100644 --- a/src/font/shaper/noop.zig +++ b/src/font/shaper/noop.zig @@ -3,7 +3,6 @@ const assert = std.debug.assert; const Allocator = std.mem.Allocator; const trace = @import("tracy").trace; const font = @import("../main.zig"); -const config = @import("../../config.zig"); const Face = font.Face; const Collection = font.Collection; const DeferredFace = font.DeferredFace; @@ -71,21 +70,11 @@ pub const Shaper = struct { pub fn runIterator( self: *Shaper, - grid: *SharedGrid, - screen: *const terminal.Screen, - row: terminal.Pin, - selection: ?terminal.Selection, - cursor_x: ?usize, - break_config: config.FontShapingBreak, + opts: font.shape.RunOptions, ) font.shape.RunIterator { return .{ .hooks = .{ .shaper = self }, - .grid = grid, - .screen = screen, - .row = row, - .selection = selection, - .cursor_x = cursor_x, - .break_config = break_config, + .opts = opts, }; } diff --git a/src/font/shaper/run.zig b/src/font/shaper/run.zig index a6ff79e39..92e629e19 100644 --- a/src/font/shaper/run.zig +++ b/src/font/shaper/run.zig @@ -6,8 +6,6 @@ const shape = @import("../shape.zig"); const terminal = @import("../../terminal/main.zig"); const autoHash = std.hash.autoHash; const Hasher = std.hash.Wyhash; -const configpkg = @import("../../config.zig"); -const Config = configpkg.Config; /// A single text run. A text run is only valid for one Shaper instance and /// until the next run is created. A text run never goes across multiple @@ -37,16 +35,11 @@ pub const TextRun = struct { /// RunIterator is an iterator that yields text runs. pub const RunIterator = struct { hooks: font.Shaper.RunIteratorHook, - grid: *font.SharedGrid, - screen: *const terminal.Screen, - row: terminal.Pin, - selection: ?terminal.Selection = null, - cursor_x: ?usize = null, - break_config: configpkg.FontShapingBreak, + opts: shape.RunOptions, i: usize = 0, pub fn next(self: *RunIterator, alloc: Allocator) !?TextRun { - const cells = self.row.cells(.all); + const cells = self.opts.row.cells(.all); // Trim the right side of a row that might be empty const max: usize = max: { @@ -61,7 +54,7 @@ pub const RunIterator = struct { // Invisible cells don't have any glyphs rendered, // so we explicitly skip them in the shaping process. while (self.i < max and - self.row.style(&cells[self.i]).flags.invisible) + self.opts.row.style(&cells[self.i]).flags.invisible) { self.i += 1; } @@ -79,7 +72,7 @@ pub const RunIterator = struct { var hasher = Hasher.init(0); // Let's get our style that we'll expect for the run. - const style = self.row.style(&cells[self.i]); + const style = self.opts.row.style(&cells[self.i]); // Go through cell by cell and accumulate while we build our run. var j: usize = self.i; @@ -89,9 +82,9 @@ pub const RunIterator = struct { // If we have a selection and we're at a boundary point, then // we break the run here. - if (self.selection) |unordered_sel| { + if (self.opts.selection) |unordered_sel| { if (j > self.i) { - const sel = unordered_sel.ordered(self.screen, .forward); + const sel = unordered_sel.ordered(self.opts.screen, .forward); const start_x = sel.start().x; const end_x = sel.end().x; @@ -145,7 +138,7 @@ pub const RunIterator = struct { // The style is different. We allow differing background // styles but any other change results in a new run. const c1 = comparableStyle(style); - const c2 = comparableStyle(self.row.style(&cells[j])); + const c2 = comparableStyle(self.opts.row.style(&cells[j])); if (!c1.eql(c2)) break; } @@ -165,7 +158,7 @@ pub const RunIterator = struct { const presentation: ?font.Presentation = if (cell.hasGrapheme()) p: { // We only check the FIRST codepoint because I believe the // presentation format must be directly adjacent to the codepoint. - const cps = self.row.grapheme(cell) orelse break :p null; + const cps = self.opts.row.grapheme(cell) orelse break :p null; assert(cps.len > 0); if (cps[0] == 0xFE0E) break :p .text; if (cps[0] == 0xFE0F) break :p .emoji; @@ -178,38 +171,36 @@ pub const RunIterator = struct { break :emoji null; }; - if (self.break_config.cursor) { - // If our cursor is on this line then we break the run around the - // cursor. This means that any row with a cursor has at least - // three breaks: before, exactly the cursor, and after. - // - // We do not break a cell that is exactly the grapheme. If there - // are cells following that contain joiners, we allow those to - // break. This creates an effect where hovering over an emoji - // such as a skin-tone emoji is fine, but hovering over the - // joiners will show the joiners allowing you to modify the - // emoji. - if (!cell.hasGrapheme()) { - if (self.cursor_x) |cursor_x| { - // Exactly: self.i is the cursor and we iterated once. This - // means that we started exactly at the cursor and did at - // exactly one iteration. Why exactly one? Because we may - // start at our cursor but do many if our cursor is exactly - // on an emoji. - if (self.i == cursor_x and j == self.i + 1) break; + // If our cursor is on this line then we break the run around the + // cursor. This means that any row with a cursor has at least + // three breaks: before, exactly the cursor, and after. + // + // We do not break a cell that is exactly the grapheme. If there + // are cells following that contain joiners, we allow those to + // break. This creates an effect where hovering over an emoji + // such as a skin-tone emoji is fine, but hovering over the + // joiners will show the joiners allowing you to modify the + // emoji. + if (!cell.hasGrapheme()) { + if (self.opts.cursor_x) |cursor_x| { + // Exactly: self.i is the cursor and we iterated once. This + // means that we started exactly at the cursor and did at + // exactly one iteration. Why exactly one? Because we may + // start at our cursor but do many if our cursor is exactly + // on an emoji. + if (self.i == cursor_x and j == self.i + 1) break; - // Before: up to and not including the cursor. This means - // that we started before the cursor (self.i < cursor_x) - // and j is now at the cursor meaning we haven't yet processed - // the cursor. - if (self.i < cursor_x and j == cursor_x) { - assert(j > 0); - break; - } - - // After: after the cursor. We don't need to do anything - // special, we just let the run complete. + // Before: up to and not including the cursor. This means + // that we started before the cursor (self.i < cursor_x) + // and j is now at the cursor meaning we haven't yet processed + // the cursor. + if (self.i < cursor_x and j == cursor_x) { + assert(j > 0); + break; } + + // After: after the cursor. We don't need to do anything + // special, we just let the run complete. } } @@ -232,7 +223,7 @@ pub const RunIterator = struct { // Otherwise we need a fallback character. Prefer the // official replacement character. - if (try self.grid.getIndex( + if (try self.opts.grid.getIndex( alloc, 0xFFFD, // replacement char font_style, @@ -240,7 +231,7 @@ pub const RunIterator = struct { )) |idx| break :font_info .{ .idx = idx, .fallback = 0xFFFD }; // Fallback to space - if (try self.grid.getIndex( + if (try self.opts.grid.getIndex( alloc, ' ', font_style, @@ -278,7 +269,7 @@ pub const RunIterator = struct { @intCast(cluster), ); if (cell.hasGrapheme()) { - const cps = self.row.grapheme(cell).?; + const cps = self.opts.row.grapheme(cell).?; for (cps) |cp| { // Do not send presentation modifiers if (cp == 0xFE0E or cp == 0xFE0F) continue; @@ -303,7 +294,7 @@ pub const RunIterator = struct { .hash = hasher.final(), .offset = @intCast(self.i), .cells = @intCast(j - self.i), - .grid = self.grid, + .grid = self.opts.grid, .font_index = current_font, }; } @@ -331,7 +322,7 @@ pub const RunIterator = struct { cell.codepoint() == 0 or cell.codepoint() == terminal.kitty.graphics.unicode.placeholder) { - return try self.grid.getIndex( + return try self.opts.grid.getIndex( alloc, ' ', style, @@ -341,7 +332,7 @@ pub const RunIterator = struct { // Get the font index for the primary codepoint. const primary_cp: u32 = cell.codepoint(); - const primary = try self.grid.getIndex( + const primary = try self.opts.grid.getIndex( alloc, primary_cp, style, @@ -354,7 +345,7 @@ pub const RunIterator = struct { // If this is a grapheme, we need to find a font that supports // all of the codepoints in the grapheme. - const cps = self.row.grapheme(cell) orelse return primary; + const cps = self.opts.row.grapheme(cell) orelse return primary; var candidates = try std.ArrayList(font.Collection.Index).initCapacity(alloc, cps.len + 1); defer candidates.deinit(); candidates.appendAssumeCapacity(primary); @@ -370,7 +361,7 @@ pub const RunIterator = struct { // to support the base presentation, since it is common for emoji // fonts to support the base emoji with emoji presentation but not // certain ZWJ-combined characters like the male and female signs. - const idx = try self.grid.getIndex( + const idx = try self.opts.grid.getIndex( alloc, cp, style, @@ -381,11 +372,11 @@ pub const RunIterator = struct { // We need to find a candidate that has ALL of our codepoints for (candidates.items) |idx| { - if (!self.grid.hasCodepoint(idx, primary_cp, presentation)) continue; + if (!self.opts.grid.hasCodepoint(idx, primary_cp, presentation)) continue; for (cps) |cp| { // Ignore Emoji ZWJs if (cp == 0xFE0E or cp == 0xFE0F or cp == 0x200D) continue; - if (!self.grid.hasCodepoint(idx, cp, null)) break; + if (!self.opts.grid.hasCodepoint(idx, cp, null)) break; } else { // If the while completed, then we have a candidate that // supports all of our codepoints. diff --git a/src/font/shaper/web_canvas.zig b/src/font/shaper/web_canvas.zig index 95e220b84..4ed4b7db6 100644 --- a/src/font/shaper/web_canvas.zig +++ b/src/font/shaper/web_canvas.zig @@ -4,7 +4,6 @@ const Allocator = std.mem.Allocator; const ziglyph = @import("ziglyph"); const font = @import("../main.zig"); const terminal = @import("../../terminal/main.zig"); -const config = @import("../../config.zig"); const log = std.log.scoped(.font_shaper); @@ -62,19 +61,11 @@ pub const Shaper = struct { /// for a Shaper struct since they share state. pub fn runIterator( self: *Shaper, - group: *font.GroupCache, - row: terminal.Screen.Row, - selection: ?terminal.Selection, - cursor_x: ?usize, - break_config: config.FontShapingBreak, + opts: font.shape.RunOptions, ) font.shape.RunIterator { return .{ .hooks = .{ .shaper = self }, - .group = group, - .row = row, - .selection = selection, - .cursor_x = cursor_x, - .break_config = break_config, + .opts = opts, }; } diff --git a/src/renderer/generic.zig b/src/renderer/generic.zig index bf189fc4c..fba577231 100644 --- a/src/renderer/generic.zig +++ b/src/renderer/generic.zig @@ -513,6 +513,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type { font_thicken_strength: u8, font_features: std.ArrayListUnmanaged([:0]const u8), font_styles: font.CodepointResolver.StyleStatus, + font_shaping_break: configpkg.FontShapingBreak, cursor_color: ?terminal.color.RGB, cursor_invert: bool, cursor_opacity: f64, @@ -578,6 +579,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type { .font_thicken_strength = config.@"font-thicken-strength", .font_features = font_features.list, .font_styles = font_styles, + .font_shaping_break = config.@"font-shaping-break", .cursor_color = if (!cursor_invert and config.@"cursor-color" != null) config.@"cursor-color".?.toTerminalRGB() @@ -2467,13 +2469,15 @@ pub fn Renderer(comptime GraphicsAPI: type) type { } // Iterator of runs for shaping. - var run_iter = self.font_shaper.runIterator( - self.font_grid, - screen, - row, - row_selection, - if (shape_cursor) screen.cursor.x else null, - ); + var run_iter_opts: font.shape.RunOptions = .{ + .grid = self.font_grid, + .screen = screen, + .row = row, + .selection = row_selection, + .cursor_x = if (shape_cursor) screen.cursor.x else null, + }; + run_iter_opts.applyBreakConfig(self.config.font_shaping_break); + var run_iter = self.font_shaper.runIterator(run_iter_opts); var shaper_run: ?font.shape.TextRun = try run_iter.next(self.alloc); var shaper_cells: ?[]const font.shape.Cell = null; var shaper_cells_i: usize = 0;