diff options
| author | 2022-01-01 23:57:41 +0200 | |
|---|---|---|
| committer | 2022-01-01 23:57:41 +0200 | |
| commit | f03a4b178eacd13226eaaba5f8d10892ccf78711 (patch) | |
| tree | 09ebb7ac3e1750d3c03d3e7dad570a873f132907 /src/Buffer.zig | |
| parent | Update make mode (diff) | |
| download | es-f03a4b178eacd13226eaaba5f8d10892ccf78711.tar.gz es-f03a4b178eacd13226eaaba5f8d10892ccf78711.tar.xz es-f03a4b178eacd13226eaaba5f8d10892ccf78711.zip | |
changes
Diffstat (limited to 'src/Buffer.zig')
| -rw-r--r-- | src/Buffer.zig | 116 |
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; | |||
| 6 | const Buffer = @This(); | 6 | const Buffer = @This(); |
| 7 | const Config = @import("Config.zig"); | 7 | const Config = @import("Config.zig"); |
| 8 | const File = std.fs.File; | 8 | const File = std.fs.File; |
| 9 | const files = @import("files.zig"); | ||
| 9 | const Editor = @import("Editor.zig"); | 10 | const Editor = @import("Editor.zig"); |
| 10 | const Highlight = @import("highlight.zig").Highlight; | 11 | const Highlight = @import("highlight.zig").Highlight; |
| 11 | const Row = @import("Row.zig"); | 12 | const Row = @import("Row.zig"); |
| @@ -13,8 +14,8 @@ const Syntax = @import("Syntax.zig"); | |||
| 13 | 14 | ||
| 14 | allocator: Allocator, | 15 | allocator: Allocator, |
| 15 | 16 | ||
| 16 | // TODO: Short name & file name split | 17 | file_path: ?[]u8, |
| 17 | name: []u8, | 18 | short_name: []u8, |
| 18 | 19 | ||
| 19 | rows: ArrayList(Row), | 20 | rows: ArrayList(Row), |
| 20 | 21 | ||
| @@ -26,7 +27,6 @@ rowoff: usize, | |||
| 26 | coloff: usize, | 27 | coloff: usize, |
| 27 | 28 | ||
| 28 | dirty: bool, | 29 | dirty: bool, |
| 29 | has_file: bool, | ||
| 30 | 30 | ||
| 31 | config: Config, | 31 | config: Config, |
| 32 | syntax: ?Syntax, | 32 | syntax: ?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 | ||
| 63 | pub fn deinit(self: Buffer) void { | 63 | pub 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 | |||
| 218 | pub fn drawStatusBar(self: Buffer, writer: anytype, screencols: usize) !void { | 222 | pub 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 | ||
| 424 | pub fn save(self: *Buffer, editor: *Editor) !void { | 432 | pub 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 | ||
| 495 | pub fn selectSyntaxHighlighting(self: *Buffer) !void { | 522 | pub 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 | ||
| 532 | fn statFile(name: []const u8) !File.Stat { | 564 | fn 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 | ||
| 550 | fn writeToFile(self: Buffer, file: File) !void { | 574 | fn writeToFile(self: Buffer, file: File) !void { |