terminal: selectionString uses arraylist to build results

This commit is contained in:
Mitchell Hashimoto
2023-11-26 09:33:52 -08:00
parent 7fc95690bc
commit 0487dbfb25

View File

@ -2140,62 +2140,14 @@ pub fn selectionString(
// Get the slices for the string // Get the slices for the string
const slices = self.selectionSlices(sel); const slices = self.selectionSlices(sel);
// We can now know how much space we'll need to store the string. We loop // Use an ArrayList so that we can grow the array as we go. We
// over and UTF8-encode and calculate the exact size required. We will be // build an initial capacity of just our rows in our selection times
// off here by at most "newlines" values in the worst case that every // columns. It can be more or less based on graphemes, newlines, etc.
// single line is soft-wrapped. var strbuilder = try std.ArrayList(u8).initCapacity(alloc, slices.rows * self.cols);
const chars = chars: { defer strbuilder.deinit();
var count: usize = 0;
// We need to keep track of our x/y so that we can get graphemes.
var y: usize = slices.sel.start.y;
var x: usize = 0;
var row: Row = undefined;
const arr = [_][]StorageCell{ slices.top, slices.bot };
for (arr) |slice| {
for (slice, 0..) |cell, i| {
// detect row headers
if (@mod(i, self.cols + 1) == 0) {
// We use each row header as an opportunity to "count"
// a new row, and therefore count a possible newline.
count += 1;
// Increase our row count and get our next row
y += 1;
x = 0;
row = self.getRow(.{ .screen = y - 1 });
continue;
}
var buf: [4]u8 = undefined;
const char = if (cell.cell.char > 0) cell.cell.char else ' ';
count += try std.unicode.utf8Encode(@intCast(char), &buf);
// We need to also count any grapheme chars
var it = row.codepointIterator(x);
while (it.next()) |cp| {
count += try std.unicode.utf8Encode(cp, &buf);
}
x += 1;
}
}
break :chars count;
};
const buf = try alloc.alloc(u8, chars + 1);
errdefer alloc.free(buf);
// Special case the empty case
if (chars == 0) {
buf[0] = 0;
return buf[0..0 :0];
}
// Connect the text from the two slices // Connect the text from the two slices
const arr = [_][]StorageCell{ slices.top, slices.bot }; const arr = [_][]StorageCell{ slices.top, slices.bot };
var buf_i: usize = 0;
var row_count: usize = 0; var row_count: usize = 0;
for (arr) |slice| { for (arr) |slice| {
const row_start: usize = row_count; const row_start: usize = row_count;
@ -2215,6 +2167,13 @@ pub fn selectionString(
// the first row. // the first row.
var skip: usize = if (row_count == 0) slices.top_offset else 0; var skip: usize = if (row_count == 0) slices.top_offset else 0;
// If we have runtime safety we need to initialize the row
// so that the proper union tag is set. In release modes we
// don't need to do this because we zero the memory.
if (std.debug.runtime_safety) {
_ = self.getRow(.{ .screen = slices.sel.start.y + row_i });
}
const row: Row = .{ .screen = self, .storage = slice[start_idx..end_idx] }; const row: Row = .{ .screen = self, .storage = slice[start_idx..end_idx] };
var it = row.cellIterator(); var it = row.cellIterator();
var x: usize = 0; var x: usize = 0;
@ -2230,50 +2189,60 @@ pub fn selectionString(
if (cell.attrs.wide_spacer_head or if (cell.attrs.wide_spacer_head or
cell.attrs.wide_spacer_tail) continue; cell.attrs.wide_spacer_tail) continue;
var buf: [4]u8 = undefined;
const char = if (cell.char > 0) cell.char else ' '; const char = if (cell.char > 0) cell.char else ' ';
buf_i += try std.unicode.utf8Encode(@intCast(char), buf[buf_i..]); {
const encode_len = try std.unicode.utf8Encode(@intCast(char), &buf);
try strbuilder.appendSlice(buf[0..encode_len]);
}
var cp_it = row.codepointIterator(x); var cp_it = row.codepointIterator(x);
while (cp_it.next()) |cp| { while (cp_it.next()) |cp| {
buf_i += try std.unicode.utf8Encode(cp, buf[buf_i..]); const encode_len = try std.unicode.utf8Encode(cp, &buf);
try strbuilder.appendSlice(buf[0..encode_len]);
} }
} }
// If this row is not soft-wrapped, add a newline // If this row is not soft-wrapped, add a newline
if (!row.header().flags.wrap) { if (!row.header().flags.wrap) try strbuilder.append('\n');
buf[buf_i] = '\n';
buf_i += 1;
}
} }
} }
// Remove our trailing newline, its never correct. // Remove our trailing newline, its never correct.
if (buf_i > 0 and buf[buf_i - 1] == '\n') buf_i -= 1; if (strbuilder.items.len > 0 and
strbuilder.items[strbuilder.items.len - 1] == '\n')
{
strbuilder.items.len -= 1;
}
// Remove any trailing spaces on lines. We could do optimize this by // Remove any trailing spaces on lines. We could do optimize this by
// doing this in the loop above but this isn't very hot path code and // doing this in the loop above but this isn't very hot path code and
// this is simple. // this is simple.
if (trim) { if (trim) {
var it = std.mem.tokenize(u8, buf[0..buf_i], "\n"); var it = std.mem.tokenize(u8, strbuilder.items, "\n");
buf_i = 0;
// Reset our items. We retain our capacity. Because we're only
// removing bytes, we know that the trimmed string must be no longer
// than the original string so we copy directly back into our
// allocated memory.
strbuilder.clearRetainingCapacity();
while (it.next()) |line| { while (it.next()) |line| {
const trimmed = std.mem.trimRight(u8, line, " \t"); const trimmed = std.mem.trimRight(u8, line, " \t");
std.mem.copy(u8, buf[buf_i..], trimmed); const i = strbuilder.items.len;
buf_i += trimmed.len; strbuilder.items.len += trimmed.len;
buf[buf_i] = '\n'; std.mem.copyForwards(u8, strbuilder.items[i..], trimmed);
buf_i += 1; strbuilder.appendAssumeCapacity('\n');
} }
// Remove our trailing newline again // Remove our trailing newline again
if (buf_i > 0) buf_i -= 1; if (strbuilder.items.len > 0) strbuilder.items.len -= 1;
} }
// Add null termination // Get our final string
buf[buf_i] = 0; const string = try strbuilder.toOwnedSliceSentinel(0);
errdefer alloc.free(string);
// Realloc so our free length is exactly correct return string;
const result = try alloc.realloc(buf, buf_i + 1);
return result[0..buf_i :0];
} }
/// Returns the slices that make up the selection, in order. There are at most /// Returns the slices that make up the selection, in order. There are at most