Saving a file
/// Try to save the current file, prompt for a file name if currently not set.
/// Currently saving the file fails if directory doesn't exist, and there is no
/// tilde expansion.
fn saveFile(e: *Editor) !void {
var B = &e.buffer;
if (B.filename == null) {
// will prompt for a filename
return;
}
// code to come...
}
Before saving, we want to determine in advance how many bytes we'll write to disk, so that we can print it in a message.
Since e.buffer.filename
is optional, once we are certain that it can't be
null
, we can access safely its non-null value with the .?
notation.
// determine number of bytes to write, make room for \n characters
var fsize: usize = B.rows.items.len;
for (B.rows.items) |row| {
fsize += row.chars.items.len;
}
const file = std.fs.cwd().createFile(B.filename.?, .{ .truncate = true });
if (file) |f| {
// write lines to file
}
else |err|{
e.alc.free(B.filename.?);
B.filename = null;
return e.ioerr(err);
}
We will try to open the file in writing mode, truncating it and replacing all
bytes. Here the key std function is std.fs.cwd().createFile()
.
In this block we write the lines:
if (file) |f| {
// write lines to file
var buf: [1024]u8 = undefined;
var writer = f.writer(&buf);
defer f.close();
// for each line, write the bytes, then the \n character
for (B.rows.items) |row| {
writer.interface.writeAll(row.chars.items) catch |err| return e.ioerr(err);
writer.interface.writeByte('\n') catch |err| return e.ioerr(err);
}
// write what's left in the buffer
try writer.interface.flush();
try e.statusMessage(message.status.get("bufwrite").?, .{
B.filename.?, B.rows.items.len, fsize
});
B.dirty = false;
return;
Before writing, we need a buffered writer. The size doesn't matter too much I think, but too small would be close to unbuffered.
To actually write the file, we use the Io.Writer
interface, which is accessed
at writer.interface
.
After we wrote all bytes, we have to flush the writer. This is what happens:
1. | we provide a small buffer, that lives on the stack |
2. | this buffer is filled by the writer with characters that have to be written |
3. | when the buffer is full, the writer actually writes the data, then empties the buffer and repeats |
4. | when there's nothing more to write, there can be something left in the buffer, because the writer only writes the buffer when it's full |
5. | so we flush the buffer: the writer empties it and writes what's left |
When we're done we print a message that says the name of the written file, how many lines and bytes have been written to disk.
If for some reason the write fails, the buffer filename is freed and made null.