feat: port editor to use configurable file types

This commit is contained in:
CJ van den Berg 2025-07-14 13:25:58 +02:00
parent 818b2cf915
commit bffc56b618
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9
3 changed files with 43 additions and 49 deletions

View file

@ -591,6 +591,7 @@ pub fn build_exe(
exe.root_module.addImport("renderer", renderer_mod); exe.root_module.addImport("renderer", renderer_mod);
exe.root_module.addImport("input", input_mod); exe.root_module.addImport("input", input_mod);
exe.root_module.addImport("syntax", syntax_mod); exe.root_module.addImport("syntax", syntax_mod);
exe.root_module.addImport("file_type_config", file_type_config_mod);
exe.root_module.addImport("color", color_mod); exe.root_module.addImport("color", color_mod);
exe.root_module.addImport("bin_path", bin_path_mod); exe.root_module.addImport("bin_path", bin_path_mod);
exe.root_module.addImport("version", b.createModule(.{ .root_source_file = version_file })); exe.root_module.addImport("version", b.createModule(.{ .root_source_file = version_file }));
@ -635,6 +636,7 @@ pub fn build_exe(
check_exe.root_module.addImport("renderer", renderer_mod); check_exe.root_module.addImport("renderer", renderer_mod);
check_exe.root_module.addImport("input", input_mod); check_exe.root_module.addImport("input", input_mod);
check_exe.root_module.addImport("syntax", syntax_mod); check_exe.root_module.addImport("syntax", syntax_mod);
check_exe.root_module.addImport("file_type_config", file_type_config_mod);
check_exe.root_module.addImport("color", color_mod); check_exe.root_module.addImport("color", color_mod);
check_exe.root_module.addImport("bin_path", bin_path_mod); check_exe.root_module.addImport("bin_path", bin_path_mod);
check_exe.root_module.addImport("version", b.createModule(.{ .root_source_file = version_file })); check_exe.root_module.addImport("version", b.createModule(.{ .root_source_file = version_file }));

View file

@ -8,6 +8,7 @@ const ripgrep = @import("ripgrep");
const tracy = @import("tracy"); const tracy = @import("tracy");
const text_manip = @import("text_manip"); const text_manip = @import("text_manip");
const syntax = @import("syntax"); const syntax = @import("syntax");
const file_type_config = @import("file_type_config");
const project_manager = @import("project_manager"); const project_manager = @import("project_manager");
const root_mod = @import("root"); const root_mod = @import("root");
@ -330,6 +331,7 @@ pub const Editor = struct {
utf8_sanitized: bool = false, utf8_sanitized: bool = false,
} = .{}, } = .{},
file_type: ?file_type_config = null,
syntax: ?*syntax = null, syntax: ?*syntax = null,
syntax_no_render: bool = false, syntax_no_render: bool = false,
syntax_report_timing: bool = false, syntax_report_timing: bool = false,
@ -579,30 +581,30 @@ pub const Editor = struct {
try new_buf.root.store(content.writer(std.heap.c_allocator), new_buf.file_eol_mode); try new_buf.root.store(content.writer(std.heap.c_allocator), new_buf.file_eol_mode);
} }
const syn_file_type = blk: { self.file_type = blk: {
const frame_ = tracy.initZone(@src(), .{ .name = "guess" }); const frame_ = tracy.initZone(@src(), .{ .name = "guess" });
defer frame_.deinit(); defer frame_.deinit();
break :blk if (lang_override.len > 0) break :blk if (lang_override.len > 0)
syntax.FileType.get_by_name(lang_override) try file_type_config.get(lang_override)
else else
syntax.FileType.guess(self.file_path, content.items); file_type_config.guess_file_type(self.file_path, content.items);
}; };
const syn = blk: { const syn = blk: {
const frame_ = tracy.initZone(@src(), .{ .name = "create" }); const frame_ = tracy.initZone(@src(), .{ .name = "create" });
defer frame_.deinit(); defer frame_.deinit();
break :blk if (syn_file_type) |ft| break :blk if (self.file_type) |ft|
syntax.create(ft, self.allocator, tui.query_cache()) catch null ft.create_syntax(self.allocator, tui.query_cache()) catch null
else else
null; null;
}; };
if (syn) |syn_| { if (self.file_type) |ft| {
const frame_ = tracy.initZone(@src(), .{ .name = "did_open" }); const frame_ = tracy.initZone(@src(), .{ .name = "did_open" });
defer frame_.deinit(); defer frame_.deinit();
project_manager.did_open( project_manager.did_open(
file_path, file_path,
syn_.file_type, ft,
self.lsp_version, self.lsp_version,
try content.toOwnedSlice(std.heap.c_allocator), try content.toOwnedSlice(std.heap.c_allocator),
new_buf.is_ephemeral(), new_buf.is_ephemeral(),
@ -614,9 +616,9 @@ pub const Editor = struct {
self.syntax_no_render = tp.env.get().is("no-syntax"); self.syntax_no_render = tp.env.get().is("no-syntax");
self.syntax_report_timing = tp.env.get().is("syntax-report-timing"); self.syntax_report_timing = tp.env.get().is("syntax-report-timing");
const ftn = if (self.syntax) |syn| syn.file_type.name else "text"; const ftn = if (self.file_type) |ft| ft.name else "text";
const fti = if (self.syntax) |syn| syn.file_type.icon else "🖹"; const fti = if (self.file_type) |ft| ft.icon orelse "🖹" else "🖹";
const ftc = if (self.syntax) |syn| syn.file_type.color else 0x000000; const ftc = if (self.file_type) |ft| ft.color orelse 0x000000 else 0x000000;
if (self.buffer) |buffer| { if (self.buffer) |buffer| {
buffer.file_type_name = ftn; buffer.file_type_name = ftn;
buffer.file_type_icon = fti; buffer.file_type_icon = fti;
@ -3608,7 +3610,7 @@ pub const Editor = struct {
pub const toggle_prefix_meta: Meta = .{ .arguments = &.{.string} }; pub const toggle_prefix_meta: Meta = .{ .arguments = &.{.string} };
pub fn toggle_comment(self: *Self, _: Context) Result { pub fn toggle_comment(self: *Self, _: Context) Result {
const comment = if (self.syntax) |syn| syn.file_type.comment else "//"; const comment = if (self.file_type) |file_type| file_type.comment else "#";
return self.toggle_prefix(command.fmt(.{comment})); return self.toggle_prefix(command.fmt(.{comment}));
} }
pub const toggle_comment_meta: Meta = .{ .description = "Toggle comment" }; pub const toggle_comment_meta: Meta = .{ .description = "Toggle comment" };
@ -4647,7 +4649,7 @@ pub const Editor = struct {
var content = std.ArrayListUnmanaged(u8).empty; var content = std.ArrayListUnmanaged(u8).empty;
defer content.deinit(self.allocator); defer content.deinit(self.allocator);
try root.store(content.writer(self.allocator), eol_mode); try root.store(content.writer(self.allocator), eol_mode);
self.syntax = syntax.create_guess_file_type(self.allocator, content.items, self.file_path, tui.query_cache()) catch |e| switch (e) { self.syntax = file_type_config.create_syntax_guess_file_type(self.allocator, content.items, self.file_path, tui.query_cache()) catch |e| switch (e) {
error.NotFound => null, error.NotFound => null,
else => return e, else => return e,
}; };
@ -5537,7 +5539,7 @@ pub const Editor = struct {
pub const select_meta: Meta = .{ .arguments = &.{ .integer, .integer, .integer, .integer } }; pub const select_meta: Meta = .{ .arguments = &.{ .integer, .integer, .integer, .integer } };
fn get_formatter(self: *Self) ?[]const []const u8 { fn get_formatter(self: *Self) ?[]const []const u8 {
if (self.syntax) |syn| if (syn.file_type.formatter) |fmtr| if (fmtr.len > 0) return fmtr; if (self.file_type) |file_type| if (file_type.formatter) |fmtr| if (fmtr.len > 0) return fmtr;
return null; return null;
} }
@ -5743,11 +5745,11 @@ pub const Editor = struct {
saved.cursor = sel.end; saved.cursor = sel.end;
break :ret sel; break :ret sel;
}; };
var result = std.ArrayListUnmanaged(u8).empty; var result = std.ArrayList(u8).init(self.allocator);
defer result.deinit(self.allocator); defer result.deinit();
const writer: struct { const writer: struct {
self_: *Self, self_: *Self,
result: *std.ArrayListUnmanaged(u8), result: *std.ArrayList(u8),
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
const Error = @typeInfo(@typeInfo(@TypeOf(Buffer.unicode.LetterCasing.toUpperStr)).@"fn".return_type.?).error_union.error_set; const Error = @typeInfo(@typeInfo(@TypeOf(Buffer.unicode.LetterCasing.toUpperStr)).@"fn".return_type.?).error_union.error_set;
@ -5758,7 +5760,7 @@ pub const Editor = struct {
else else
try letter_casing.toLowerStr(writer.self_.allocator, bytes); try letter_casing.toLowerStr(writer.self_.allocator, bytes);
defer writer.self_.allocator.free(flipped); defer writer.self_.allocator.free(flipped);
return writer.result.appendSlice(writer.allocator, flipped); return writer.result.appendSlice(flipped);
} }
fn map_error(e: anyerror, _: ?*std.builtin.StackTrace) Error { fn map_error(e: anyerror, _: ?*std.builtin.StackTrace) Error {
return @errorCast(e); return @errorCast(e);
@ -5833,29 +5835,38 @@ pub const Editor = struct {
self.syntax_refresh_full = true; self.syntax_refresh_full = true;
self.syntax_incremental_reparse = false; self.syntax_incremental_reparse = false;
self.syntax = syntax: { const file_type_config_ = try file_type_config.get(file_type);
self.file_type = file_type_config_;
self.syntax = blk: {
break :blk if (self.file_type) |ft|
ft.create_syntax(self.allocator, tui.query_cache()) catch null
else
null;
};
if (self.file_type) |ft| {
var content = std.ArrayListUnmanaged(u8).empty; var content = std.ArrayListUnmanaged(u8).empty;
defer content.deinit(std.heap.c_allocator); defer content.deinit(std.heap.c_allocator);
const root = try self.buf_root(); const root = try self.buf_root();
try root.store(content.writer(std.heap.c_allocator), try self.buf_eol_mode()); try root.store(content.writer(std.heap.c_allocator), try self.buf_eol_mode());
const syn = syntax.create_file_type(self.allocator, file_type, tui.query_cache()) catch null;
if (syn) |syn_| if (self.file_path) |file_path| if (self.file_path) |file_path|
project_manager.did_open( project_manager.did_open(
file_path, file_path,
syn_.file_type, ft,
self.lsp_version, self.lsp_version,
try content.toOwnedSlice(std.heap.c_allocator), try content.toOwnedSlice(std.heap.c_allocator),
if (self.buffer) |p| p.is_ephemeral() else true, if (self.buffer) |p| p.is_ephemeral() else true,
) catch |e| ) catch |e|
self.logger.print("project_manager.did_open failed: {any}", .{e}); self.logger.print("project_manager.did_open failed: {any}", .{e});
break :syntax syn; }
};
self.syntax_no_render = tp.env.get().is("no-syntax"); self.syntax_no_render = tp.env.get().is("no-syntax");
self.syntax_report_timing = tp.env.get().is("syntax-report-timing"); self.syntax_report_timing = tp.env.get().is("syntax-report-timing");
const ftn = if (self.syntax) |syn| syn.file_type.name else "text"; const ftn = if (self.file_type) |ft| ft.name else "text";
const fti = if (self.syntax) |syn| syn.file_type.icon else "🖹"; const fti = if (self.file_type) |ft| ft.icon orelse "🖹" else "🖹";
const ftc = if (self.syntax) |syn| syn.file_type.color else 0x000000; const ftc = if (self.file_type) |ft| ft.color orelse 0x000000 else 0x000000;
const file_exists = if (self.buffer) |b| b.file_exists else false; const file_exists = if (self.buffer) |b| b.file_exists else false;
try self.send_editor_open(self.file_path orelse "", file_exists, ftn, fti, ftc); try self.send_editor_open(self.file_path orelse "", file_exists, ftn, fti, ftc);
self.logger.print("file type {s}", .{file_type}); self.logger.print("file type {s}", .{file_type});

View file

@ -265,22 +265,6 @@ fn open_style_config(self: *Self, Style: type) command.Result {
if (self.get_active_buffer()) |buffer| buffer.mark_not_ephemeral(); if (self.get_active_buffer()) |buffer| buffer.mark_not_ephemeral();
} }
fn get_file_type_config_file_path(allocator: std.mem.Allocator, file_type: []const u8) ![]const u8 {
var stream = std.ArrayList(u8).init(allocator);
const writer = stream.writer();
_ = try writer.writeAll(try root.get_config_dir());
_ = try writer.writeByte(std.fs.path.sep);
_ = try writer.writeAll("file_type");
_ = try writer.writeByte(std.fs.path.sep);
std.fs.makeDirAbsolute(stream.items) catch |e| switch (e) {
error.PathAlreadyExists => {},
else => return e,
};
_ = try writer.writeAll(file_type);
_ = try writer.writeAll(".conf");
return stream.toOwnedSlice();
}
const cmds = struct { const cmds = struct {
pub const Target = Self; pub const Target = Self;
const Ctx = command.Context; const Ctx = command.Context;
@ -524,7 +508,7 @@ const cmds = struct {
@import("mode/overlay/file_type_palette.zig").Variant("open_file_type_config", "Edit file type", true).Type, @import("mode/overlay/file_type_palette.zig").Variant("open_file_type_config", "Edit file type", true).Type,
); );
const file_name = try get_file_type_config_file_path(self.allocator, file_type_name); const file_name = try file_type_config.get_config_file_path(self.allocator, file_type_name);
defer self.allocator.free(file_name); defer self.allocator.free(file_name);
const file: ?std.fs.File = std.fs.openFileAbsolute(file_name, .{ .mode = .read_only }) catch null; const file: ?std.fs.File = std.fs.openFileAbsolute(file_name, .{ .mode = .read_only }) catch null;
@ -533,17 +517,14 @@ const cmds = struct {
return tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_name } }); return tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_name } });
} }
const file_type = syntax.FileType.get_by_name(file_type_name) orelse return error.UnknownFileType; const content = try file_type_config.get_default(self.allocator, file_type_name);
const config = file_type_config.from_file_type(file_type); defer self.allocator.free(content);
var conf = std.ArrayListUnmanaged(u8).empty;
defer conf.deinit(self.allocator);
root.write_config_to_writer(file_type_config, config, conf.writer(self.allocator)) catch {};
tui.reset_drag_context(); tui.reset_drag_context();
try self.create_editor(); try self.create_editor();
try command.executeName("open_scratch_buffer", command.fmt(.{ try command.executeName("open_scratch_buffer", command.fmt(.{
file_name, file_name,
conf.items, content,
"conf", "conf",
})); }));
if (self.get_active_buffer()) |buffer| buffer.mark_not_ephemeral(); if (self.get_active_buffer()) |buffer| buffer.mark_not_ephemeral();