···88const IndexPos = @import("indexpos.zig").IndexPos;
99const Range = @import("Range.zig");
1010const Selection = @import("Selection.zig");
1111+const Language = @import("Language.zig");
1212+const Zig = @import("languages/Zig.zig");
11131214const Allocator = std.mem.Allocator;
1315···5052/// An array tracking all of the selections in the editor. **Modifying this will cause undefined
5153/// behavior**. Use the methods on the editor to manipulate the selections instead.
5254selections: std.ArrayListUnmanaged(Selection),
5555+/// Adds language support features when present.
5656+language: ?Language = null,
53575458pub const Command = union(enum) {
5559 /// Adds a new cursor at the given position.
···115119 const file = try std.fs.cwd().createFile(filename, .{ .read = true, .truncate = false });
116120 defer file.close();
117121118118- // 3. Get a reader to read the file.
122122+ self.text.clearAndFree(allocator);
119123120120- var buffer: [1024]u8 = undefined;
121121- var file_reader = file.reader(&buffer);
122122- var reader = &file_reader.interface;
124124+ if (try file.getEndPos() > 0) {
125125+ // 3. Get a reader to read the file.
123126124124- // 4. Read the file and store in state.
127127+ var buffer: [1024]u8 = undefined;
128128+ var file_reader = file.reader(&buffer);
129129+ var reader = &file_reader.interface;
125130126126- self.text.clearAndFree(allocator);
131131+ // 4. Read the file and store in state.
127132128128- var writer: std.Io.Writer.Allocating = .fromArrayList(allocator, &self.text);
129129- defer writer.deinit();
133133+ var writer: std.Io.Writer.Allocating = .fromArrayList(allocator, &self.text);
134134+ defer writer.deinit();
130135131131- // Stream from file to the text array list.
132132- _ = try reader.stream(&writer.writer, .unlimited);
133133- try writer.writer.flush();
134134- self.text = writer.toArrayList();
136136+ // Stream from file to the text array list.
137137+ _ = try reader.stream(&writer.writer, .unlimited);
138138+ try writer.writer.flush();
139139+ self.text = writer.toArrayList();
140140+ }
135141136142 // 5. Only after the file has been successfully read do we update file name and other state.
137143···145151 // 7. Tokenize the new text.
146152147153 try self.tokenize(allocator);
154154+155155+ // 8. Initialize a language, if we have an implementation for it.
156156+157157+ if (std.mem.eql(u8, std.fs.path.extension(self.filename.items), ".zig")) {
158158+ self.language = Zig.init();
159159+ } else {
160160+ self.language = null;
161161+ }
148162}
149163150164/// Saves the text to the current location based on the `filename` field.
151151-pub fn saveFile(self: *Editor) !void {
152152- const file = try std.fs.cwd().createFile(self.filename.items, .{});
165165+pub fn saveFile(self: *Editor, gpa: std.mem.Allocator) !void {
166166+ if (self.language) |*l| format_text: {
167167+ const formatted = l.formatter.format(gpa, self.text.items) catch break :format_text;
168168+ defer gpa.free(formatted);
169169+170170+ self.text.clearRetainingCapacity();
171171+ try self.text.ensureTotalCapacity(gpa, formatted.len);
172172+ self.text.appendSliceAssumeCapacity(formatted);
173173+174174+ try self.updateLines(gpa);
175175+ try self.tokenize(gpa);
176176+ }
177177+178178+ std.log.debug("saving file: {s}", .{self.filename.items});
179179+180180+ const file = try std.fs.cwd().createFile(self.filename.items, .{ .read = true, .truncate = true });
153181 defer file.close();
154182155183 var buffer: [1024]u8 = undefined;
+15
src/libfn/Formatter.zig
···11+const std = @import("std");
22+33+const Formatter = @This();
44+55+vtable: *const VTable,
66+77+pub const VTable = struct {
88+ format: *const fn (f: *Formatter, gpa: std.mem.Allocator, input: []const u8) anyerror![]const u8 = format,
99+};
1010+1111+/// Runs a formatter on the provided input, and returns the formatted output. Caller owns the
1212+/// returned memory.
1313+pub fn format(f: *Formatter, gpa: std.mem.Allocator, input: []const u8) anyerror![]const u8 {
1414+ return try f.vtable.format(f, gpa, input);
1515+}