summaryrefslogtreecommitdiff
path: root/src/File.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/File.zig')
-rw-r--r--src/File.zig78
1 files changed, 40 insertions, 38 deletions
diff --git a/src/File.zig b/src/File.zig
index eb1c6a7c..239aa4ba 100644
--- a/src/File.zig
+++ b/src/File.zig
@@ -17,16 +17,17 @@ const Pane = panes.Pane;
const Output = panes.Output;
const Mini = panes.Mini;
const Terminal = panes.Terminal;
+const Text = panes.Text;
const SYNTAX_CONTEXT_AFTER_ROWS: usize = 2;
/// Content and primary selection at one file edit boundary. Keeping only the
-/// primary avoids putting Pane.max_selections ranges in every history entry.
+/// primary avoids putting Text.max_selections ranges in every history entry.
pub const Snapshot = struct {
content: []u8,
cur_row: i32,
cur_col: i32,
- vsel: Pane.CharSel,
+ vsel: Text.CharSel,
};
pub const History = struct {
@@ -652,27 +653,28 @@ pub fn cursorLines(arena: std.mem.Allocator, pane: *Pane, f: *State) ![]const []
/// Use the file's line index only when `text` is its complete live content.
/// Edit-buffer fragments and other temporary text retain modal's scan path.
-fn contentIndex(pane: *Pane, text: []const u8) ?[]const usize {
+fn contentIndex(t: *Text, text: []const u8) ?[]const usize {
+ const pane = t.pane();
const f = if (pane.file) |*file| file else return null;
if (text.ptr != f.content.ptr or text.len != f.content.len) return null;
return lineIndex(pane.gpa, f) catch null;
}
-pub fn textOffset(pane: *Pane, text: []const u8, cursor: modal.Cursor) usize {
- const index = contentIndex(pane, text) orelse return modal.offsetAt(text, cursor);
+pub fn textOffset(t: *Text, text: []const u8, cursor: modal.Cursor) usize {
+ const index = contentIndex(t, text) orelse return modal.offsetAt(text, cursor);
const row = @min(cursor.row, index.len - 1);
const start = index[row];
const end = if (row + 1 < index.len) index[row + 1] - 1 else text.len;
return start + modal.graphemeStart(text[start..end], @min(cursor.col, end - start));
}
-pub fn textLineStart(pane: *Pane, text: []const u8, row: usize) usize {
- const index = contentIndex(pane, text) orelse return modal.lineStartOffset(text, row);
+pub fn textLineStart(t: *Text, text: []const u8, row: usize) usize {
+ const index = contentIndex(t, text) orelse return modal.lineStartOffset(text, row);
return if (row >= index.len) text.len else index[row];
}
-pub fn textLine(pane: *Pane, text: []const u8, row: usize) []const u8 {
- const index = contentIndex(pane, text) orelse return modal.lineSlice(text, row);
+pub fn textLine(t: *Text, text: []const u8, row: usize) []const u8 {
+ const index = contentIndex(t, text) orelse return modal.lineSlice(text, row);
if (row >= index.len) return "";
const start = index[row];
const end = if (row + 1 < index.len) index[row + 1] - 1 else text.len;
@@ -686,7 +688,7 @@ test "indexed text rows preserve fragment and allocation failure semantics" {
const pane = try p.setTestFile("first\n\tλ界\n\nlast\n");
const text = pane.file.?.content;
allocator.fail_index = allocator.alloc_index;
- for (0..lineCount(text) + 2) |row| try std.testing.expectEqualStrings(modal.lineSlice(text, row), textLine(pane, text, row));
+ for (0..lineCount(text) + 2) |row| try std.testing.expectEqualStrings(modal.lineSlice(text, row), textLine(&pane.body, text, row));
try std.testing.expectEqual(@as(usize, 0), pane.file.?.line_starts.len);
allocator.fail_index = std.math.maxInt(usize);
_ = try lineIndex(p.gpa, &pane.file.?);
@@ -695,19 +697,19 @@ test "indexed text rows preserve fragment and allocation failure semantics" {
for ([_][]const u8{ text, text[2..], text[0..8], "other\nrows\n\n", "" }) |fragment| {
for (0..lineCount(fragment) + 2) |row| try std.testing.expectEqualStrings(
modal.lineSlice(fragment, row),
- textLine(pane, fragment, row),
+ textLine(&pane.body, fragment, row),
);
}
try std.testing.expectEqual(allocations, allocator.allocations);
}
-pub fn textLineCount(pane: *Pane, text: []const u8) usize {
- const index = contentIndex(pane, text) orelse return modal.cursorLineCount(text);
+pub fn textLineCount(t: *Text, text: []const u8) usize {
+ const index = contentIndex(t, text) orelse return modal.cursorLineCount(text);
return index.len;
}
-pub fn textPosition(pane: *Pane, text: []const u8, offset: usize) modal.Cursor {
- const index = contentIndex(pane, text) orelse return modal.positionAt(text, offset);
+pub fn textPosition(t: *Text, text: []const u8, offset: usize) modal.Cursor {
+ const index = contentIndex(t, text) orelse return modal.positionAt(text, offset);
const bounded = @min(offset, text.len);
const row = std.sort.upperBound(usize, index, bounded, struct {
fn cmp(key: usize, item: usize) std.math.Order {
@@ -734,8 +736,8 @@ pub fn open(p: *Pardes, id: usize, path: []const u8, line: usize) !*Pane {
const total = lineCount(content);
const scroll: usize = if (line > 0 and line <= total) line - 1 else 0;
pane.file = .{ .path = path_copy, .content = content, .scroll = scroll, .history = history };
- pane.cur_pinned = true;
- pane.cur_row = @intCast(scroll);
+ pane.body.cur_pinned = true;
+ pane.body.cur_row = @intCast(scroll);
p.installPane(id, pane);
if (filesystem.localPath(path) != null) p.emit(.{ .watch = .{ .pane = @intCast(id), .on = true } });
return pane;
@@ -794,8 +796,8 @@ pub fn restore(p: *Pardes, id: usize, src: dump.Pane) !*Pane {
.history = history,
};
pane.file.?.tree_context = saved.tree_context and supportsContext(pane);
- pane.cur_pinned = true;
- pane.cur_row = @intCast(src.scroll);
+ pane.body.cur_pinned = true;
+ pane.body.cur_row = @intCast(src.scroll);
pane.cols = @max(1, src.cols);
pane.rows = @max(1, src.rows);
if (output == null and filesystem.localPath(path) != null) p.emit(.{ .watch = .{ .pane = @intCast(id), .on = true, .mode = .baseline_disk } });
@@ -858,12 +860,12 @@ pub fn restoreSnap(pane: *Pane, f: *State, snap: Snapshot) void {
const n = nlines(pane.gpa, f);
const row: usize = @min(@as(usize, @intCast(@max(0, snap.cur_row))), n - 1);
const llen = modal.lineSlice(f.content, row).len;
- pane.cur_row = @intCast(row);
- pane.cur_col = @intCast(@min(@as(usize, @intCast(@max(0, snap.cur_col))), llen));
- pane.vsel = snap.vsel;
- pane.msel.active = false;
- pane.cur_pinned = true;
- pane.sticky_col = -1;
+ pane.body.cur_row = @intCast(row);
+ pane.body.cur_col = @intCast(@min(@as(usize, @intCast(@max(0, snap.cur_col))), llen));
+ pane.body.vsel = snap.vsel;
+ pane.body.msel.active = false;
+ pane.body.cur_pinned = true;
+ pane.body.sticky_col = -1;
pane.ensureCursorVisible();
}
@@ -883,9 +885,9 @@ pub fn pushUndo(p: *Pardes, pane: *Pane) void {
if (history.undo_len > 0 and std.mem.eql(u8, history.undo[history.undo_len - 1].content, f.content)) return;
const snap: Snapshot = .{
.content = p.gpa.dupe(u8, f.content) catch return,
- .cur_row = pane.cur_row,
- .cur_col = pane.cur_col,
- .vsel = pane.vsel,
+ .cur_row = pane.body.cur_row,
+ .cur_col = pane.body.cur_col,
+ .vsel = pane.body.vsel,
};
pushHistory(p.gpa, &history.undo, &history.undo_len, snap);
for (history.redo[0..history.redo_len]) |item| p.gpa.free(item.content);
@@ -898,9 +900,9 @@ pub fn undo(p: *Pardes, pane: *Pane) void {
if (history.undo_len == 0) return;
const current: Snapshot = .{
.content = p.gpa.dupe(u8, f.content) catch return,
- .cur_row = pane.cur_row,
- .cur_col = pane.cur_col,
- .vsel = pane.vsel,
+ .cur_row = pane.body.cur_row,
+ .cur_col = pane.body.cur_col,
+ .vsel = pane.body.vsel,
};
pushHistory(p.gpa, &history.redo, &history.redo_len, current);
history.undo_len -= 1;
@@ -915,9 +917,9 @@ pub fn redo(p: *Pardes, pane: *Pane) void {
if (history.redo_len == 0) return;
const current: Snapshot = .{
.content = p.gpa.dupe(u8, f.content) catch return,
- .cur_row = pane.cur_row,
- .cur_col = pane.cur_col,
- .vsel = pane.vsel,
+ .cur_row = pane.body.cur_row,
+ .cur_col = pane.body.cur_col,
+ .vsel = pane.body.vsel,
};
pushHistory(p.gpa, &history.undo, &history.undo_len, current);
history.redo_len -= 1;
@@ -939,9 +941,9 @@ pub fn changed(p: *Pardes, id: u8, bytes: []const u8) void {
// restoreSnap only consumes cursor/selection from this synthetic snapshot.
restoreSnap(pane, f, .{
.content = undefined,
- .cur_row = pane.cur_row,
- .cur_col = pane.cur_col,
- .vsel = pane.vsel,
+ .cur_row = pane.body.cur_row,
+ .cur_col = pane.body.cur_col,
+ .vsel = pane.body.vsel,
});
}
@@ -1126,7 +1128,7 @@ pub fn drawGutter(p: *Pardes, pane: *Pane, r: pardes.Rect, tx: u16, tw: u16, bod
const prefix_width = gutterWidth(pane);
const goff = pane.scroll();
const gcur = Terminal.gridCursor(pane);
- const gcrow = if (pane.cur_pinned) pane.cur_row else @as(i32, gcur.y) + goff;
+ const gcrow = if (pane.body.cur_pinned) pane.body.cur_row else @as(i32, gcur.y) + goff;
const cur_line: i32 = if (active and !pane.tag_edit) gcrow else std.math.minInt(i32);
// the body's first row, the way renderPane derives it (Tagbottom)
const body_y = p.bodyTop(r);