tiny.ui.text.segment
Defined in text.
API (2)
Actions
Public operations.
Types and contracts
Public types and contracts.
Source
Source: lib/ui/src/text/root.zig:7
zig
pub const segment = @import("segment.zig");Source: lib/ui/src/text/segment.zig
zig
const std = @import("std");const unicode = @import("unicode");pub const Boundary = struct { byte_offset: u32, action: unicode.LineBreakAction,};const Case = struct { text: []const u8, actions: []const unicode.LineBreakAction,};pub fn boundaries( text: []const u8, output: []Boundary,) error{ OutputTooSmall, TextTooLong }![]Boundary { if (text.len > std.math.maxInt(u32)) return error.TextTooLong; std.debug.assert(std.unicode.utf8ValidateSlice(text)); var graphemes = unicode.GraphemeIterator.init(text); var line: unicode.LineBreakState = .{}; var count: usize = 0; while (graphemes.next()) |cluster| { std.debug.assert(cluster.valid_utf8); var cursor: usize = 0; var action: unicode.LineBreakAction = .prohibited; while (cursor < cluster.text.len) { const len = std.unicode.utf8ByteSequenceLength(cluster.text[cursor]) catch unreachable; const codepoint = std.unicode.utf8Decode(cluster.text[cursor..][0..len]) catch unreachable; const next = line.consume(codepoint); if (cursor == 0) action = next; cursor += len; } if (count >= output.len) return error.OutputTooSmall; output[count] = .{ .byte_offset = @intCast(cluster.start), .action = action }; count += 1; } if (count >= output.len) return error.OutputTooSmall; output[count] = .{ .byte_offset = @intCast(text.len), .action = line.finish() }; return output[0 .. count + 1];}test "line boundaries follow selected Unicode 17 conformance cases" { const cases = [_]Case{ .{ .text = "❗ ❗", .actions = &.{ .prohibited, .prohibited, .opportunity, .mandatory } }, .{ .text = "❗\u{0308}❗", .actions = &.{ .prohibited, .prohibited, .mandatory } }, .{ .text = "❗\u{200b}❗", .actions = &.{ .prohibited, .prohibited, .opportunity, .mandatory }, }, .{ .text = "🇦🇦🇦🇦", .actions = &.{ .prohibited, .opportunity, .mandatory } }, .{ .text = "a b", .actions = &.{ .prohibited, .prohibited, .opportunity, .mandatory } }, }; for (cases) |case| { var output: [16]Boundary = undefined; const found = try boundaries(case.text, &output); try std.testing.expectEqual(case.actions.len, found.len); for (found, case.actions) |actual, expected| { try std.testing.expectEqual(expected, actual.action); } }}test "line boundaries never split a combining grapheme" { var output: [4]Boundary = undefined; const found = try boundaries("a\u{0308}b", &output); try std.testing.expectEqual(@as(usize, 3), found.len); try std.testing.expectEqual(@as(u32, 0), found[0].byte_offset); try std.testing.expectEqual(@as(u32, 3), found[1].byte_offset); try std.testing.expectEqual(@as(u32, 4), found[2].byte_offset);}test "empty text has one mandatory end boundary" { var output: [1]Boundary = undefined; const found = try boundaries("", &output); try std.testing.expectEqual(@as(usize, 1), found.len); try std.testing.expectEqual(@as(u32, 0), found[0].byte_offset); try std.testing.expectEqual(unicode.LineBreakAction.mandatory, found[0].action);}Audit
| Definitions | 3 |
|---|---|
| Public names | 3 |
| Members | 2 |
| Version | 26.7.0 |
| Revision | daab053ee433 |