Introduce `font-shaping-break` config option
parent
2592286988
commit
beb961fb80
|
|
@ -20,6 +20,7 @@ pub const ConfirmCloseSurface = Config.ConfirmCloseSurface;
|
|||
pub const CopyOnSelect = Config.CopyOnSelect;
|
||||
pub const CustomShaderAnimation = Config.CustomShaderAnimation;
|
||||
pub const FontSyntheticStyle = Config.FontSyntheticStyle;
|
||||
pub const FontShapingBreak = Config.FontShapingBreak;
|
||||
pub const FontStyle = Config.FontStyle;
|
||||
pub const FreetypeLoadFlags = Config.FreetypeLoadFlags;
|
||||
pub const Keybinds = Config.Keybinds;
|
||||
|
|
|
|||
|
|
@ -270,6 +270,32 @@ pub const compatibility = std.StaticStringMap(
|
|||
/// This is currently only supported on macOS.
|
||||
@"font-thicken-strength": u8 = 255,
|
||||
|
||||
/// Locations to break font shaping into multiple runs.
|
||||
///
|
||||
/// A "run" is a contiguous segment of text that is shaped together. "Shaping"
|
||||
/// is the process of converting text (codepoints) into glyphs (renderable
|
||||
/// characters). This is how ligatures are formed, among other things.
|
||||
/// For example, if a coding font turns "!=" into a single glyph, then it
|
||||
/// must see "!" and "=" next to each other in a single run. When a run
|
||||
/// is broken, the text is shaped separately. To continue our example, if
|
||||
/// "!" is at the end of one run and "=" is at the start of the next run,
|
||||
/// then the ligature will not be formed.
|
||||
///
|
||||
/// Ghostty breaks runs at certain points to improve readability or usability.
|
||||
/// For example, Ghostty by default will break runs under the cursor so that
|
||||
/// text editing can see the individual characters rather than a ligature.
|
||||
/// This configuration lets you configure this behavior.
|
||||
///
|
||||
/// Combine values with a comma to set multiple options. Prefix an
|
||||
/// option with "no-" to disable it. Enabling and disabling options
|
||||
/// can be done at the same time.
|
||||
///
|
||||
/// Available options:
|
||||
///
|
||||
/// * `cursor` - Break runs under the cursor.
|
||||
///
|
||||
@"font-shaping-break": FontShapingBreak = .{},
|
||||
|
||||
/// What color space to use when performing alpha blending.
|
||||
///
|
||||
/// This affects the appearance of text and of any images with transparency.
|
||||
|
|
@ -6214,6 +6240,11 @@ pub const FontSyntheticStyle = packed struct {
|
|||
@"bold-italic": bool = true,
|
||||
};
|
||||
|
||||
/// See "font-shaping-break" for documentation
|
||||
pub const FontShapingBreak = packed struct {
|
||||
cursor: bool = true,
|
||||
};
|
||||
|
||||
/// See "link" for documentation.
|
||||
pub const RepeatableLink = struct {
|
||||
const Self = @This();
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ const trace = @import("tracy").trace;
|
|||
const font = @import("../main.zig");
|
||||
const os = @import("../../os/main.zig");
|
||||
const terminal = @import("../../terminal/main.zig");
|
||||
const config = @import("../../config.zig");
|
||||
const Feature = font.shape.Feature;
|
||||
const FeatureList = font.shape.FeatureList;
|
||||
const default_features = font.shape.default_features;
|
||||
|
|
@ -293,6 +294,7 @@ pub const Shaper = struct {
|
|||
row: terminal.Pin,
|
||||
selection: ?terminal.Selection,
|
||||
cursor_x: ?usize,
|
||||
break_config: config.FontShapingBreak,
|
||||
) font.shape.RunIterator {
|
||||
return .{
|
||||
.hooks = .{ .shaper = self },
|
||||
|
|
@ -301,6 +303,7 @@ pub const Shaper = struct {
|
|||
.row = row,
|
||||
.selection = selection,
|
||||
.cursor_x = cursor_x,
|
||||
.break_config = break_config,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -600,6 +603,7 @@ test "run iterator" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |_| count += 1;
|
||||
|
|
@ -619,6 +623,7 @@ test "run iterator" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |_| count += 1;
|
||||
|
|
@ -639,6 +644,7 @@ test "run iterator" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |_| count += 1;
|
||||
|
|
@ -660,6 +666,7 @@ test "run iterator" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |_| count += 1;
|
||||
|
|
@ -707,6 +714,7 @@ test "run iterator: empty cells with background set" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
{
|
||||
const run = (try it.next(alloc)).?;
|
||||
|
|
@ -743,6 +751,7 @@ test "shape" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -778,6 +787,7 @@ test "shape nerd fonts" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -806,6 +816,7 @@ test "shape inconsolata ligs" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -831,6 +842,7 @@ test "shape inconsolata ligs" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -864,6 +876,7 @@ test "shape monaspace ligs" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -898,6 +911,7 @@ test "shape left-replaced lig in last run" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -932,6 +946,7 @@ test "shape left-replaced lig in early run" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
|
||||
const run = (try it.next(alloc)).?;
|
||||
|
|
@ -963,6 +978,7 @@ test "shape U+3C9 with JB Mono" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
|
||||
var run_count: usize = 0;
|
||||
|
|
@ -996,6 +1012,7 @@ test "shape emoji width" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1046,6 +1063,7 @@ test "shape emoji width long" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 1 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1082,6 +1100,7 @@ test "shape variation selector VS15" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1117,6 +1136,7 @@ test "shape variation selector VS16" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1149,6 +1169,7 @@ test "shape with empty cells in between" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1187,6 +1208,7 @@ test "shape Chinese characters" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1227,6 +1249,7 @@ test "shape box glyphs" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1267,6 +1290,7 @@ test "shape selection boundary" {
|
|||
false,
|
||||
),
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1290,6 +1314,7 @@ test "shape selection boundary" {
|
|||
false,
|
||||
),
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1313,6 +1338,7 @@ test "shape selection boundary" {
|
|||
false,
|
||||
),
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1336,6 +1362,7 @@ test "shape selection boundary" {
|
|||
false,
|
||||
),
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1359,6 +1386,7 @@ test "shape selection boundary" {
|
|||
false,
|
||||
),
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1391,6 +1419,7 @@ test "shape cursor boundary" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1400,6 +1429,7 @@ test "shape cursor boundary" {
|
|||
try testing.expectEqual(@as(usize, 1), count);
|
||||
}
|
||||
|
||||
{
|
||||
// Cursor at index 0 is two runs
|
||||
{
|
||||
// Get our run iterator
|
||||
|
|
@ -1410,6 +1440,7 @@ test "shape cursor boundary" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
0,
|
||||
.{ .cursor = true },
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1418,7 +1449,28 @@ test "shape cursor boundary" {
|
|||
}
|
||||
try testing.expectEqual(@as(usize, 2), count);
|
||||
}
|
||||
// And without cursor splitting remains one
|
||||
{
|
||||
// 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 count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
count += 1;
|
||||
_ = try shaper.shape(run);
|
||||
}
|
||||
try testing.expectEqual(@as(usize, 1), count);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Cursor at index 1 is three runs
|
||||
{
|
||||
// Get our run iterator
|
||||
|
|
@ -1429,6 +1481,7 @@ test "shape cursor boundary" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
1,
|
||||
.{ .cursor = true },
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1437,7 +1490,27 @@ test "shape cursor boundary" {
|
|||
}
|
||||
try testing.expectEqual(@as(usize, 3), count);
|
||||
}
|
||||
|
||||
// And without cursor splitting remains one
|
||||
{
|
||||
// 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 count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
count += 1;
|
||||
_ = try shaper.shape(run);
|
||||
}
|
||||
try testing.expectEqual(@as(usize, 1), count);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Cursor at last col is two runs
|
||||
{
|
||||
// Get our run iterator
|
||||
|
|
@ -1448,6 +1521,7 @@ test "shape cursor boundary" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
9,
|
||||
.{ .cursor = true },
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1456,6 +1530,26 @@ test "shape cursor boundary" {
|
|||
}
|
||||
try testing.expectEqual(@as(usize, 2), count);
|
||||
}
|
||||
// And without cursor splitting remains one
|
||||
{
|
||||
// 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 count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
count += 1;
|
||||
_ = try shaper.shape(run);
|
||||
}
|
||||
try testing.expectEqual(@as(usize, 1), count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "shape cursor boundary and colored emoji" {
|
||||
|
|
@ -1480,6 +1574,7 @@ test "shape cursor boundary and colored emoji" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1499,6 +1594,25 @@ test "shape cursor boundary and colored emoji" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
0,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
count += 1;
|
||||
_ = try shaper.shape(run);
|
||||
}
|
||||
try testing.expectEqual(@as(usize, 1), count);
|
||||
}
|
||||
{
|
||||
// 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 count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1516,6 +1630,25 @@ test "shape cursor boundary and colored emoji" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
1,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
count += 1;
|
||||
_ = try shaper.shape(run);
|
||||
}
|
||||
try testing.expectEqual(@as(usize, 1), count);
|
||||
}
|
||||
{
|
||||
// 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 count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1546,6 +1679,7 @@ test "shape cell attribute change" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1570,6 +1704,7 @@ test "shape cell attribute change" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1595,6 +1730,7 @@ test "shape cell attribute change" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1620,6 +1756,7 @@ test "shape cell attribute change" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1644,6 +1781,7 @@ test "shape cell attribute change" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1684,6 +1822,7 @@ test "shape high plane sprite font codepoint" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
// We should get one run
|
||||
const run = (try it.next(alloc)).?;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ const Allocator = std.mem.Allocator;
|
|||
const harfbuzz = @import("harfbuzz");
|
||||
const font = @import("../main.zig");
|
||||
const terminal = @import("../../terminal/main.zig");
|
||||
const config = @import("../../config.zig");
|
||||
const Feature = font.shape.Feature;
|
||||
const FeatureList = font.shape.FeatureList;
|
||||
const default_features = font.shape.default_features;
|
||||
|
|
@ -94,6 +95,7 @@ pub const Shaper = struct {
|
|||
row: terminal.Pin,
|
||||
selection: ?terminal.Selection,
|
||||
cursor_x: ?usize,
|
||||
break_config: config.FontShapingBreak,
|
||||
) font.shape.RunIterator {
|
||||
return .{
|
||||
.hooks = .{ .shaper = self },
|
||||
|
|
@ -102,6 +104,7 @@ pub const Shaper = struct {
|
|||
.row = row,
|
||||
.selection = selection,
|
||||
.cursor_x = cursor_x,
|
||||
.break_config = break_config,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -231,6 +234,7 @@ test "run iterator" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |_| count += 1;
|
||||
|
|
@ -250,6 +254,7 @@ test "run iterator" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |_| count += 1;
|
||||
|
|
@ -270,6 +275,7 @@ test "run iterator" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |_| {
|
||||
|
|
@ -322,6 +328,7 @@ test "run iterator: empty cells with background set" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
{
|
||||
const run = (try it.next(alloc)).?;
|
||||
|
|
@ -359,6 +366,7 @@ test "shape" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -388,6 +396,7 @@ test "shape inconsolata ligs" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -413,6 +422,7 @@ test "shape inconsolata ligs" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -446,6 +456,7 @@ test "shape monaspace ligs" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -482,6 +493,7 @@ test "shape arabic forced LTR" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -519,6 +531,7 @@ test "shape emoji width" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -571,6 +584,7 @@ test "shape emoji width long" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 1 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -609,6 +623,7 @@ test "shape variation selector VS15" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -646,6 +661,7 @@ test "shape variation selector VS16" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -680,6 +696,7 @@ test "shape with empty cells in between" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -718,6 +735,7 @@ test "shape Chinese characters" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -758,6 +776,7 @@ test "shape box glyphs" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -799,6 +818,7 @@ test "shape selection boundary" {
|
|||
false,
|
||||
),
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -822,6 +842,7 @@ test "shape selection boundary" {
|
|||
false,
|
||||
),
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -845,6 +866,7 @@ test "shape selection boundary" {
|
|||
false,
|
||||
),
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -868,6 +890,7 @@ test "shape selection boundary" {
|
|||
false,
|
||||
),
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -891,6 +914,7 @@ test "shape selection boundary" {
|
|||
false,
|
||||
),
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -923,6 +947,7 @@ test "shape cursor boundary" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -932,6 +957,7 @@ test "shape cursor boundary" {
|
|||
try testing.expectEqual(@as(usize, 1), count);
|
||||
}
|
||||
|
||||
{
|
||||
// Cursor at index 0 is two runs
|
||||
{
|
||||
// Get our run iterator
|
||||
|
|
@ -942,6 +968,7 @@ test "shape cursor boundary" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
0,
|
||||
.{ .cursor = true },
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -950,7 +977,28 @@ test "shape cursor boundary" {
|
|||
}
|
||||
try testing.expectEqual(@as(usize, 2), count);
|
||||
}
|
||||
// And without cursor splitting remains one
|
||||
{
|
||||
// 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 count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
count += 1;
|
||||
_ = try shaper.shape(run);
|
||||
}
|
||||
try testing.expectEqual(@as(usize, 1), count);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Cursor at index 1 is three runs
|
||||
{
|
||||
// Get our run iterator
|
||||
|
|
@ -961,6 +1009,7 @@ test "shape cursor boundary" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
1,
|
||||
.{ .cursor = true },
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -969,7 +1018,27 @@ test "shape cursor boundary" {
|
|||
}
|
||||
try testing.expectEqual(@as(usize, 3), count);
|
||||
}
|
||||
|
||||
// And without cursor splitting remains one
|
||||
{
|
||||
// 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 count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
count += 1;
|
||||
_ = try shaper.shape(run);
|
||||
}
|
||||
try testing.expectEqual(@as(usize, 1), count);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Cursor at last col is two runs
|
||||
{
|
||||
// Get our run iterator
|
||||
|
|
@ -980,6 +1049,7 @@ test "shape cursor boundary" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
9,
|
||||
.{ .cursor = true },
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -988,6 +1058,26 @@ test "shape cursor boundary" {
|
|||
}
|
||||
try testing.expectEqual(@as(usize, 2), count);
|
||||
}
|
||||
// And without cursor splitting remains one
|
||||
{
|
||||
// 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 count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
count += 1;
|
||||
_ = try shaper.shape(run);
|
||||
}
|
||||
try testing.expectEqual(@as(usize, 1), count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "shape cursor boundary and colored emoji" {
|
||||
|
|
@ -1012,6 +1102,7 @@ test "shape cursor boundary and colored emoji" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1031,6 +1122,25 @@ test "shape cursor boundary and colored emoji" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
0,
|
||||
.{ .cursor = true },
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
count += 1;
|
||||
_ = try shaper.shape(run);
|
||||
}
|
||||
try testing.expectEqual(@as(usize, 1), count);
|
||||
}
|
||||
{
|
||||
// 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 count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1048,6 +1158,25 @@ test "shape cursor boundary and colored emoji" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
1,
|
||||
.{ .cursor = true },
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
count += 1;
|
||||
_ = try shaper.shape(run);
|
||||
}
|
||||
try testing.expectEqual(@as(usize, 1), count);
|
||||
}
|
||||
{
|
||||
// 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 count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1078,6 +1207,7 @@ test "shape cell attribute change" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1102,6 +1232,7 @@ test "shape cell attribute change" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1127,6 +1258,7 @@ test "shape cell attribute change" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1152,6 +1284,7 @@ test "shape cell attribute change" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
@ -1176,6 +1309,7 @@ test "shape cell attribute change" {
|
|||
screen.pages.pin(.{ .screen = .{ .y = 0 } }).?,
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
);
|
||||
var count: usize = 0;
|
||||
while (try it.next(alloc)) |run| {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ 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;
|
||||
|
|
@ -75,6 +76,7 @@ pub const Shaper = struct {
|
|||
row: terminal.Pin,
|
||||
selection: ?terminal.Selection,
|
||||
cursor_x: ?usize,
|
||||
break_config: config.FontShapingBreak,
|
||||
) font.shape.RunIterator {
|
||||
return .{
|
||||
.hooks = .{ .shaper = self },
|
||||
|
|
@ -83,6 +85,7 @@ pub const Shaper = struct {
|
|||
.row = row,
|
||||
.selection = selection,
|
||||
.cursor_x = cursor_x,
|
||||
.break_config = break_config,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ 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
|
||||
|
|
@ -40,6 +42,7 @@ pub const RunIterator = struct {
|
|||
row: terminal.Pin,
|
||||
selection: ?terminal.Selection = null,
|
||||
cursor_x: ?usize = null,
|
||||
break_config: configpkg.FontShapingBreak,
|
||||
i: usize = 0,
|
||||
|
||||
pub fn next(self: *RunIterator, alloc: Allocator) !?TextRun {
|
||||
|
|
@ -175,6 +178,7 @@ 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.
|
||||
|
|
@ -207,6 +211,7 @@ pub const RunIterator = struct {
|
|||
// special, we just let the run complete.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We need to find a font that supports this character. If
|
||||
// there are additional zero-width codepoints (to form a single
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ 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);
|
||||
|
||||
|
|
@ -65,6 +66,7 @@ pub const Shaper = struct {
|
|||
row: terminal.Screen.Row,
|
||||
selection: ?terminal.Selection,
|
||||
cursor_x: ?usize,
|
||||
break_config: config.FontShapingBreak,
|
||||
) font.shape.RunIterator {
|
||||
return .{
|
||||
.hooks = .{ .shaper = self },
|
||||
|
|
@ -72,6 +74,7 @@ pub const Shaper = struct {
|
|||
.row = row,
|
||||
.selection = selection,
|
||||
.cursor_x = cursor_x,
|
||||
.break_config = break_config,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue