window: fix word wrapping

This commit is contained in:
Tim Culverhouse 2024-04-19 19:31:37 -08:00
parent cde945f2ef
commit bb277b8e37

View file

@ -306,37 +306,57 @@ pub fn print(self: Window, segments: []Segment, opts: PrintOptions) !PrintResult
}, },
.word => { .word => {
var col: usize = 0; var col: usize = 0;
const overflow: bool = blk: for (segments) |segment| { var overflow: bool = false;
var line_iter = std.mem.tokenizeAny(u8, segment.text, "\r\n"); var soft_wrapped: bool = false;
while (line_iter.next()) |line| { for (segments) |segment| {
col = 0; var start: usize = 0;
defer row += 1; var i: usize = 0;
var word_iter = std.mem.tokenizeScalar(u8, line, ' '); while (i < segment.text.len) : (i += 1) {
while (word_iter.next()) |word| { // for (segment.text, 0..) |b, i| {
const b = segment.text[i];
const end = switch (b) {
' ',
'\r',
'\n',
=> i,
else => if (i != segment.text.len - 1) continue else i + 1,
};
const word = segment.text[start..end];
// find the start of the next word
start = while (i + 1 < segment.text.len) : (i += 1) {
if (segment.text[i + 1] == ' ') continue;
break i + 1;
} else i;
const width = self.gwidth(word); const width = self.gwidth(word);
if (width == 0) continue; const non_wsp_width: usize = for (word, 0..) |wb, wi| {
// only wrap when the word can fit by itself on a if (wb == '\r' or wb == '\n') {
// line
if (width + col + 1 > self.width and width < self.width) {
row += 1; row += 1;
col = 0; col = 0;
break width -| wi -| 1;
} }
if (row >= self.height) if (wb != ' ') break width - wi;
break :blk true; } else 0;
if (col > 0) {
if (opts.commit) self.writeCell(col, row, .{ if (width + col > self.width and non_wsp_width < self.width) {
.char = .{ // wrap
.grapheme = " ", row += 1;
.width = 1, col = 0;
}, soft_wrapped = true;
.style = segment.style,
.link = segment.link,
});
col += 1;
} }
var iter = GraphemeIterator.init(word); if (row >= self.height) {
overflow = true;
break;
}
// if we are soft wrapped, (col == 0 and row > 0), then trim
// leading spaces
const printed_word = if (soft_wrapped)
std.mem.trimLeft(u8, word, " ")
else
word;
defer soft_wrapped = false;
var iter = GraphemeIterator.init(printed_word);
while (iter.next()) |grapheme| { while (iter.next()) |grapheme| {
const s = grapheme.slice(word); const s = grapheme.slice(printed_word);
const w = self.gwidth(s); const w = self.gwidth(s);
if (opts.commit) self.writeCell(col, row, .{ if (opts.commit) self.writeCell(col, row, .{
.char = .{ .char = .{
@ -352,12 +372,33 @@ pub fn print(self: Window, segments: []Segment, opts: PrintOptions) !PrintResult
col = 0; col = 0;
} }
} }
switch (b) {
' ' => {
if (col > 0) {
if (opts.commit) self.writeCell(col, row, .{
.char = .{
.grapheme = " ",
.width = 1,
},
.style = segment.style,
.link = segment.link,
});
col += 1;
}
},
'\r',
'\n',
=> {
col = 0;
row += 1;
},
else => {},
}
} }
} }
} else false;
return .{ return .{
// remove last row counter // remove last row counter
.row = row - 1, .row = row,
.col = col, .col = col,
.overflow = overflow, .overflow = overflow,
}; };
@ -625,4 +666,63 @@ test "print: word" {
try std.testing.expectEqual(2, result.row); try std.testing.expectEqual(2, result.row);
try std.testing.expectEqual(false, result.overflow); try std.testing.expectEqual(false, result.overflow);
} }
{
var segments = [_]Segment{
.{ .text = "h" },
.{ .text = "e" },
};
const result = try win.print(&segments, opts);
try std.testing.expectEqual(2, result.col);
try std.testing.expectEqual(0, result.row);
try std.testing.expectEqual(false, result.overflow);
}
{
var segments = [_]Segment{
.{ .text = "h" },
.{ .text = "e" },
.{ .text = "l" },
.{ .text = "l" },
.{ .text = "o" },
};
const result = try win.print(&segments, opts);
try std.testing.expectEqual(1, result.col);
try std.testing.expectEqual(1, result.row);
try std.testing.expectEqual(false, result.overflow);
}
{
var segments = [_]Segment{
.{ .text = "he\n" },
};
const result = try win.print(&segments, opts);
try std.testing.expectEqual(0, result.col);
try std.testing.expectEqual(1, result.row);
try std.testing.expectEqual(false, result.overflow);
}
{
var segments = [_]Segment{
.{ .text = "he\n\n" },
};
const result = try win.print(&segments, opts);
try std.testing.expectEqual(0, result.col);
try std.testing.expectEqual(2, result.row);
try std.testing.expectEqual(false, result.overflow);
}
{
var segments = [_]Segment{
.{ .text = "not now" },
};
const result = try win.print(&segments, opts);
try std.testing.expectEqual(3, result.col);
try std.testing.expectEqual(1, result.row);
try std.testing.expectEqual(false, result.overflow);
}
{
var segments = [_]Segment{
.{ .text = "note now" },
};
const result = try win.print(&segments, opts);
try std.testing.expectEqual(3, result.col);
try std.testing.expectEqual(1, result.row);
try std.testing.expectEqual(false, result.overflow);
}
} }