summaryrefslogtreecommitdiff
path: root/src/Buffer.zig
diff options
context:
space:
mode:
authorGravatar Uko Kokņevičs2022-01-01 23:57:41 +0200
committerGravatar Uko Kokņevičs2022-01-01 23:57:41 +0200
commitf03a4b178eacd13226eaaba5f8d10892ccf78711 (patch)
tree09ebb7ac3e1750d3c03d3e7dad570a873f132907 /src/Buffer.zig
parentUpdate make mode (diff)
downloades-f03a4b178eacd13226eaaba5f8d10892ccf78711.tar.gz
es-f03a4b178eacd13226eaaba5f8d10892ccf78711.tar.xz
es-f03a4b178eacd13226eaaba5f8d10892ccf78711.zip
changes
Diffstat (limited to 'src/Buffer.zig')
-rw-r--r--src/Buffer.zig116
1 files changed, 70 insertions, 46 deletions
diff --git a/src/Buffer.zig b/src/Buffer.zig
index f105c55..0c95e6e 100644
--- a/src/Buffer.zig
+++ b/src/Buffer.zig
@@ -6,6 +6,7 @@ const ArrayList = std.ArrayList;
6const Buffer = @This(); 6const Buffer = @This();
7const Config = @import("Config.zig"); 7const Config = @import("Config.zig");
8const File = std.fs.File; 8const File = std.fs.File;
9const files = @import("files.zig");
9const Editor = @import("Editor.zig"); 10const Editor = @import("Editor.zig");
10const Highlight = @import("highlight.zig").Highlight; 11const Highlight = @import("highlight.zig").Highlight;
11const Row = @import("Row.zig"); 12const Row = @import("Row.zig");
@@ -13,8 +14,8 @@ const Syntax = @import("Syntax.zig");
13 14
14allocator: Allocator, 15allocator: Allocator,
15 16
16// TODO: Short name & file name split 17file_path: ?[]u8,
17name: []u8, 18short_name: []u8,
18 19
19rows: ArrayList(Row), 20rows: ArrayList(Row),
20 21
@@ -26,7 +27,6 @@ rowoff: usize,
26coloff: usize, 27coloff: usize,
27 28
28dirty: bool, 29dirty: bool,
29has_file: bool,
30 30
31config: Config, 31config: Config,
32syntax: ?Syntax, 32syntax: ?Syntax,
@@ -41,7 +41,8 @@ pub fn init(allocator: Allocator, name: []const u8) !Buffer {
41 return Buffer{ 41 return Buffer{
42 .allocator = allocator, 42 .allocator = allocator,
43 43
44 .name = name_owned, 44 .file_path = null,
45 .short_name = name_owned,
45 46
46 .rows = ArrayList(Row).init(allocator), 47 .rows = ArrayList(Row).init(allocator),
47 48
@@ -53,7 +54,6 @@ pub fn init(allocator: Allocator, name: []const u8) !Buffer {
53 .coloff = 0, 54 .coloff = 0,
54 55
55 .dirty = false, 56 .dirty = false,
56 .has_file = false,
57 57
58 .config = config, 58 .config = config,
59 .syntax = null, 59 .syntax = null,
@@ -61,7 +61,11 @@ pub fn init(allocator: Allocator, name: []const u8) !Buffer {
61} 61}
62 62
63pub fn deinit(self: Buffer) void { 63pub fn deinit(self: Buffer) void {
64 self.allocator.free(self.name); 64 if (self.file_path) |file_path| {
65 self.allocator.free(file_path);
66 }
67
68 self.allocator.free(self.short_name);
65 69
66 for (self.rows.items) |row| { 70 for (self.rows.items) |row| {
67 row.deinit(); 71 row.deinit();
@@ -218,10 +222,14 @@ pub fn drawRows(self: Buffer, writer: anytype, screenrows: usize, screencols: us
218pub fn drawStatusBar(self: Buffer, writer: anytype, screencols: usize) !void { 222pub fn drawStatusBar(self: Buffer, writer: anytype, screencols: usize) !void {
219 try writer.writeAll("\x1b[m\x1b[7m"); 223 try writer.writeAll("\x1b[m\x1b[7m");
220 224
221 var name = if (self.name.len > 20) 225 var name = if (self.short_name.len > 20)
222 try std.fmt.allocPrint(self.allocator, "{s}...", .{self.name[0..17]}) 226 try std.fmt.allocPrint(
227 self.allocator,
228 "...{s}",
229 .{self.short_name[self.short_name.len - 17 ..]},
230 )
223 else 231 else
224 try self.allocator.dupe(u8, self.name); 232 try self.allocator.dupe(u8, self.short_name);
225 defer self.allocator.free(name); 233 defer self.allocator.free(name);
226 234
227 const modified = if (self.dirty) 235 const modified = if (self.dirty)
@@ -271,7 +279,7 @@ pub fn goToLine(self: *Buffer, editor: *Editor) !void {
271 return; 279 return;
272 }; 280 };
273 281
274 self.cy = std.math.min(line - 1, self.rows.items.len); 282 self.cy = std.math.clamp(line, 1, self.rows.items.len + 1) - 1;
275 if (self.cy == self.rows.items.len) { 283 if (self.cy == self.rows.items.len) {
276 self.cx = 0; 284 self.cx = 0;
277 } else { 285 } else {
@@ -422,54 +430,73 @@ pub fn recenterTopBottom(self: *Buffer, screenrows: usize) void {
422} 430}
423 431
424pub fn save(self: *Buffer, editor: *Editor) !void { 432pub fn save(self: *Buffer, editor: *Editor) !void {
425 if (!self.has_file) { 433 if (self.file_path == null) {
426 const fname = try editor.prompt("Save as"); 434 // TODO: Editor.prompt should take an Allocator
427 if (fname == null) { 435 const fname = (try editor.prompt("Save as")) orelse { return; };
428 return; 436 defer self.allocator.free(fname);
429 } 437
438 const file_path = try files.resolvePath(self.allocator, fname);
439 errdefer self.allocator.free(file_path);
430 440
431 self.allocator.free(self.name); 441 const file_name = std.fs.path.basename(file_path);
432 self.name = fname.?; 442 const short_name = try editor.getUniqueBufferName(self.allocator, file_name);
433 self.has_file = true; 443 errdefer self.allocator.free(short_name);
434 444
435 try self.selectSyntaxHighlighting(); 445 self.allocator.free(self.short_name);
446 self.short_name = short_name;
447 self.file_path = file_path;
448
449 // TODO: Do I want to do this?
450 // try self.selectSyntaxHighlighting();
436 } 451 }
437 452
438 // TODO: Add a config value for this 453 // TODO: Add a config value for this
439 try self.cleanWhiteSpace(); 454 try self.cleanWhiteSpace();
440 455
441 const tmpname = try std.fmt.allocPrint(self.allocator, "{s}~{}", .{ self.name, std.time.milliTimestamp() }); 456 const file_path = self.file_path.?;
442 defer self.allocator.free(tmpname); 457
458 const tmp_path = try std.fmt.allocPrint(
459 self.allocator,
460 "{s}~{}",
461 .{ file_path, std.time.milliTimestamp() },
462 );
463 defer self.allocator.free(tmp_path);
443 464
444 const tmpfile = std.fs.cwd().createFile(tmpname, .{ .truncate = true }) catch |err| { 465 const tmp_file = std.fs.createFileAbsolute(tmp_path, .{ .truncate = true }) catch |err| {
445 try editor.setStatusMessage("Cannot open tempfile '{s}' for writing: {}", .{ tmpname, err }); 466 try editor.setStatusMessage(
467 "Cannot open tempfile '{s}' for writing: {}",
468 .{ tmp_path, err },
469 );
446 return; 470 return;
447 }; 471 };
448 defer tmpfile.close(); 472 defer tmp_file.close();
449 473
450 const stat = statFile(self.name) catch |err| { 474 const mode = statFileMode(file_path) catch |err| {
451 try editor.setStatusMessage("Couldn't stat file '{s}': {}", .{ self.name, err }); 475 try editor.setStatusMessage("Couldn't stat file '{s}': {}", .{ file_path, err });
452 return; 476 return;
453 }; 477 };
454 478
455 tmpfile.chmod(stat.mode) catch |err| { 479 tmp_file.chmod(mode) catch |err| {
456 try editor.setStatusMessage("Couldn't chmod tempfile '{s}': {}", .{ tmpname, err }); 480 try editor.setStatusMessage("Couldn't chmod tempfile '{s}': {}", .{ tmp_path, err });
457 return; 481 return;
458 }; 482 };
459 483
460 // TODO: chown 484 // TODO: chown
461 485
462 self.writeToFile(tmpfile) catch |err| { 486 self.writeToFile(tmp_file) catch |err| {
463 try editor.setStatusMessage("Couldn't write to tempfile '{s}': {}", .{ tmpname, err }); 487 try editor.setStatusMessage("Couldn't write to tempfile '{s}': {}", .{ tmp_path, err });
464 return; 488 return;
465 }; 489 };
466 490
467 std.fs.cwd().rename(tmpname, self.name) catch |err| { 491 std.fs.renameAbsolute(tmp_path, file_path) catch |err| {
468 try editor.setStatusMessage("Couldn't move '{s}' to '{s}': {}", .{ tmpname, self.name, err }); 492 try editor.setStatusMessage(
493 "Couldn't move '{s}' to '{s}': {}",
494 .{ tmp_path, file_path, err },
495 );
469 return; 496 return;
470 }; 497 };
471 498
472 try editor.setStatusMessage("Saved to '{s}'", .{self.name}); 499 try editor.setStatusMessage("Saved to '{s}'", .{file_path});
473 self.dirty = false; 500 self.dirty = false;
474} 501}
475 502
@@ -494,12 +521,17 @@ pub fn scroll(self: *Buffer, screenrows: usize, screencols: usize) void {
494 521
495pub fn selectSyntaxHighlighting(self: *Buffer) !void { 522pub fn selectSyntaxHighlighting(self: *Buffer) !void {
496 self.syntax = null; 523 self.syntax = null;
524 const name = if (self.file_path) |file_path|
525 std.fs.path.basename(file_path)
526 else
527 self.short_name;
497 528
498 const ext = if (std.mem.lastIndexOfScalar(u8, self.name, '.')) |idx| self.name[idx..] else null; 529 const ext = if (std.mem.lastIndexOfScalar(u8, name, '.')) |idx| name[idx..] else null;
499 for (Syntax.database) |syntax| { 530 for (Syntax.database) |syntax| {
500 for (syntax.filematch) |filematch| { 531 for (syntax.filematch) |filematch| {
501 const is_ext = filematch[0] == '.'; 532 const is_ext = filematch[0] == '.';
502 if ((is_ext and ext != null and std.mem.eql(u8, ext.?, filematch)) or (!is_ext and std.mem.eql(u8, self.name, filematch))) { 533 if ((is_ext and ext != null and std.mem.eql(u8, ext.?, filematch))
534 or (!is_ext and std.mem.eql(u8, name, filematch))) {
503 self.syntax = syntax; 535 self.syntax = syntax;
504 536
505 for (self.rows.items) |*row| { 537 for (self.rows.items) |*row| {
@@ -529,22 +561,14 @@ fn printWithLeftPadding(
529 try writer.writeAll(unpadded); 561 try writer.writeAll(unpadded);
530} 562}
531 563
532fn statFile(name: []const u8) !File.Stat { 564fn statFileMode(name: []const u8) !File.Mode {
533 const file = std.fs.cwd().openFile(name, .{}) catch |err| switch (err) { 565 const file = std.fs.cwd().openFile(name, .{}) catch |err| switch (err) {
534 error.FileNotFound => return File.Stat{ 566 error.FileNotFound => return 0o644,
535 .inode = 0,
536 .size = 0,
537 .mode = 0o644,
538 .kind = .File,
539 .atime = 0,
540 .mtime = 0,
541 .ctime = 0,
542 },
543 else => return err, 567 else => return err,
544 }; 568 };
545 defer file.close(); 569 defer file.close();
546 570
547 return file.stat(); 571 return (try file.stat()).mode;
548} 572}
549 573
550fn writeToFile(self: Buffer, file: File) !void { 574fn writeToFile(self: Buffer, file: File) !void {