diff --git a/build.zig b/build.zig index 1c46370..d698061 100644 --- a/build.zig +++ b/build.zig @@ -17,11 +17,11 @@ pub fn build(b: *std.Build) void { const test_step = b.step("test", "Run unit tests"); const lint_step = b.step("lint", "Run lints"); - var version: std.ArrayList(u8) = .empty; - defer version.deinit(b.allocator); - gen_version(b, version.writer(b.allocator)) catch { - version.clearAndFree(b.allocator); - version.appendSlice(b.allocator, "unknown") catch {}; + var version = std.ArrayList(u8).init(b.allocator); + defer version.deinit(); + gen_version(b, version.writer()) catch { + version.clearAndFree(); + version.appendSlice("unknown") catch {}; }; return (if (release) &build_release else &build_development)( @@ -225,11 +225,11 @@ pub fn build_exe( else => std.debug.panic("makeDir(\".cache/cdb\") failed: {any}", .{e}), }; - var version_info: std.ArrayList(u8) = .empty; - defer version_info.deinit(b.allocator); - gen_version_info(b, target, version_info.writer(b.allocator), optimize) catch { - version_info.clearAndFree(b.allocator); - version_info.appendSlice(b.allocator, "unknown") catch {}; + var version_info = std.ArrayList(u8).init(b.allocator); + defer version_info.deinit(); + gen_version_info(b, target, version_info.writer(), optimize) catch { + version_info.clearAndFree(); + version_info.appendSlice("unknown") catch {}; }; const wf = b.addWriteFiles(); @@ -687,17 +687,17 @@ fn gen_version_info( const branch_ = try b.runAllowFail(&[_][]const u8{ "git", "rev-parse", "--abbrev-ref", "HEAD" }, &code, .Ignore); const branch = std.mem.trimRight(u8, branch_, "\r\n "); const tracking_branch_ = blk: { - var buf: std.ArrayList(u8) = .empty; - defer buf.deinit(b.allocator); - try buf.appendSlice(b.allocator, branch); - try buf.appendSlice(b.allocator, "@{upstream}"); + var buf = std.ArrayList(u8).init(b.allocator); + defer buf.deinit(); + try buf.appendSlice(branch); + try buf.appendSlice("@{upstream}"); break :blk (b.runAllowFail(&[_][]const u8{ "git", "rev-parse", "--abbrev-ref", buf.items }, &code, .Ignore) catch ""); }; const tracking_remote_name = if (std.mem.indexOfScalar(u8, tracking_branch_, '/')) |pos| tracking_branch_[0..pos] else ""; const tracking_remote_ = if (tracking_remote_name.len > 0) blk: { - var remote_config_path: std.ArrayList(u8) = .empty; - defer remote_config_path.deinit(b.allocator); - try remote_config_path.writer(b.allocator).print("remote.{s}.url", .{tracking_remote_name}); + var remote_config_path = std.ArrayList(u8).init(b.allocator); + defer remote_config_path.deinit(); + try remote_config_path.writer().print("remote.{s}.url", .{tracking_remote_name}); break :blk b.runAllowFail(&[_][]const u8{ "git", "config", remote_config_path.items }, &code, .Ignore) catch "(remote not found)"; } else ""; const remote_ = b.runAllowFail(&[_][]const u8{ "git", "config", "remote.origin.url" }, &code, .Ignore) catch "(origin not found)"; diff --git a/build.zig.version b/build.zig.version index e815b86..2e95286 100644 --- a/build.zig.version +++ b/build.zig.version @@ -1 +1 @@ -0.15.1 +0.15.0-dev.1034+bd97b6618 diff --git a/build.zig.zon b/build.zig.zon index 66453d6..19890f5 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -5,21 +5,18 @@ .fingerprint = 0x52c0d670590aa80f, .dependencies = .{ - .syntax = .{ - .url = "git+https://github.com/neurocyte/flow-syntax?ref=master#2c28091fe5ccfe66b304da0532b46f98fa1c28d3", - .hash = "flow_syntax-0.1.0-X8jOockOAQBBkI-6qpOw17CjdlrQeRhiFJFszv9U_THm", - }, + .syntax = .{ .path = "src/syntax" }, .flags = .{ - .url = "git+https://github.com/neurocyte/flags?ref=main#984b27948da3e4e40a253f76c85b51ec1a9ada11", - .hash = "flags-0.10.0-a_9h3gB4AADAYn0XaZuUqH1jKRmy6dqvAIbomtTIF6V1", + .url = "https://github.com/n0s4/flags/archive/372501d1576b5723829bcba98e41361132c7b618.tar.gz", + .hash = "flags-0.8.0-AAAAAJV0AACuGBBnpUnHqZzAhoGTp4ibFROBQQQZGRqx", }, .dizzy = .{ .url = "https://github.com/neurocyte/dizzy/archive/c9219d23daccd9aa226cfde754fea278cb516459.tar.gz", .hash = "dizzy-1.0.0-q40X4YCRAAAGYO9QOZiYYSOwiiFlqZlecMuQcxPiBcXM", }, .thespian = .{ - .url = "git+https://github.com/neurocyte/thespian?ref=master#8b2535db199d2215bea438abb323f3e3f6c58d99", - .hash = "thespian-0.0.1-owFOjjQbBgABzTPy7sWTQMJ7Ts5ielIYO3_6hfhxbQa_", + .url = "git+https://github.com/neurocyte/thespian?ref=master#0a386496cda74ef827d5770f6f071a7d2d54b91a", + .hash = "thespian-0.0.1-owFOjlgaBgCAOkRjH9_mDN7dnL8n8K3XA2hqqchjXZIk", }, .themes = .{ .url = "https://github.com/neurocyte/flow-themes/releases/download/master-952f9f630ea9544088fd30293666ee0650b7a690/flow-themes.tar.gz", @@ -30,12 +27,12 @@ .hash = "fuzzig-0.1.1-Ji0xivxIAQBD0g8O_NV_0foqoPf3elsg9Sc3pNfdVH4D", }, .vaxis = .{ - .url = "git+https://github.com/neurocyte/libvaxis?ref=zig-0.15#851427cce25664626f5005322903c7398ecce781", - .hash = "vaxis-0.5.1-BWNV_B8YCQCzszczPof3gSnh1UJYBkGUouqR1m_NKbL3", + .url = "https://github.com/neurocyte/libvaxis/archive/2a4137dadbe560b13b712fd3aa8a1c313fdd8c6e.tar.gz", + .hash = "vaxis-0.1.0-BWNV_KMOCQAe8oPD6cCn62Rg7oIVgF8FEWLpcAi7xDZQ", }, .zeit = .{ - .url = "git+https://github.com/rockorager/zeit?ref=zig-0.15#ed2ca60db118414bda2b12df2039e33bad3b0b88", - .hash = "zeit-0.6.0-5I6bk0J9AgCVa0nnyL0lNY9Xa9F68hHq-ZarhuXNV-Jb", + .url = "https://github.com/rockorager/zeit/archive/991f38266f86535e68431675e8feb84efa1f011b.tar.gz", + .hash = "zeit-0.6.0-5I6bk0t7AgCPM_cY1DoqJB2pnmG7MMtpdO5IxNpryJDy", }, .win32 = .{ .url = "https://github.com/marlersoft/zigwin32/archive/e8739b32a33ce48a3286aba31918b26a9dfc6ef0.tar.gz", diff --git a/src/LSP.zig b/src/LSP.zig index 185e95d..58012a3 100644 --- a/src/LSP.zig +++ b/src/LSP.zig @@ -3,6 +3,7 @@ const tp = @import("thespian"); const cbor = @import("cbor"); const root = @import("root"); const tracy = @import("tracy"); +const log = @import("log"); allocator: std.mem.Allocator, pid: tp.pid, @@ -171,7 +172,6 @@ const Process = struct { project: [:0]const u8, sp_tag: [:0]const u8, log_file: ?std.fs.File = null, - log_file_path: ?[]const u8 = null, next_id: i32 = 0, requests: std.StringHashMap(tp.pid), state: enum { init, running } = .init, @@ -188,8 +188,10 @@ const Process = struct { } else if (try cbor.match(cmd.buf, .{ tp.extract(&tag), tp.more })) { // } else { + const logger = log.logger("LSP"); + defer logger.deinit(); var buf: [1024]u8 = undefined; - send_msg(tp.self_pid().clone(), tag, .err, "invalid command: {d} {s}", .{ cmd.buf.len, cmd.to_json(&buf) catch "{command too large}" }); + logger.print_err("create", "invalid command: {d} {s}", .{ cmd.buf.len, cmd.to_json(&buf) catch "{command too large}" }); return error.InvalidLspCommand; } const self = try allocator.create(Process); @@ -225,7 +227,6 @@ const Process = struct { self.close() catch {}; self.write_log("### terminated LSP process ###\n", .{}); if (self.log_file) |file| file.close(); - if (self.log_file_path) |file_path| self.allocator.free(file_path); } fn close(self: *Process) error{CloseFailed}!void { @@ -244,20 +245,6 @@ const Process = struct { } } - fn msg(self: *const Process, comptime fmt: anytype, args: anytype) void { - send_msg(self.parent, self.tag, .msg, fmt, args); - } - - fn err_msg(self: *const Process, comptime fmt: anytype, args: anytype) void { - send_msg(self.parent, self.tag, .err, fmt, args); - } - - fn send_msg(proc: tp.pid, tag: []const u8, type_: enum { msg, err }, comptime fmt: anytype, args: anytype) void { - var buf: [@import("log").max_log_message]u8 = undefined; - const output = std.fmt.bufPrint(&buf, fmt, args) catch "MESSAGE TOO LARGE"; - proc.send(.{ "lsp", type_, tag, output }) catch {}; - } - fn start(self: *Process) tp.result { const frame = tracy.initZone(@src(), .{ .name = module_name ++ " start" }); defer frame.deinit(); @@ -268,9 +255,8 @@ const Process = struct { var log_file_path = std.ArrayList(u8).init(self.allocator); defer log_file_path.deinit(); const state_dir = root.get_state_dir() catch |e| return tp.exit_error(e, @errorReturnTrace()); - log_file_path.writer().print("{s}{c}lsp-{s}.log", .{ state_dir, std.fs.path.sep, self.tag }) catch |e| return tp.exit_error(e, @errorReturnTrace()); + log_file_path.writer().print("{s}/lsp-{s}.log", .{ state_dir, self.tag }) catch |e| return tp.exit_error(e, @errorReturnTrace()); self.log_file = std.fs.createFileAbsolute(log_file_path.items, .{ .truncate = true }) catch |e| return tp.exit_error(e, @errorReturnTrace()); - self.log_file_path = log_file_path.toOwnedSlice() catch null; } fn receive(self: *Process, from: tp.pid_ref, m: tp.message) tp.result { @@ -454,14 +440,18 @@ const Process = struct { } fn handle_not_found(self: *Process) error{ExitNormal}!void { - self.err_msg("'{s}' executable not found", .{self.tag}); + const logger = log.logger("LSP"); + defer logger.deinit(); + logger.print_err("init", "'{s}' executable not found", .{self.tag}); self.write_log("### '{s}' executable not found ###\n", .{self.tag}); self.parent.send(.{ sp_tag, self.tag, "not found" }) catch {}; return error.ExitNormal; } fn handle_terminated(self: *Process, err: []const u8, code: u32) error{ExitNormal}!void { - self.msg("terminated: {s} {d}", .{ err, code }); + const logger = log.logger("LSP"); + defer logger.deinit(); + logger.print("terminated: {s} {d}", .{ err, code }); self.write_log("### subprocess terminated {s} {d} ###\n", .{ err, code }); self.parent.send(.{ sp_tag, self.tag, "done" }) catch {}; return error.ExitNormal; @@ -473,9 +463,9 @@ const Process = struct { const id = self.next_id; self.next_id += 1; - var request = std.ArrayList(u8).init(self.allocator); - defer request.deinit(); - const msg_writer = request.writer(); + var msg = std.ArrayList(u8).init(self.allocator); + defer msg.deinit(); + const msg_writer = msg.writer(); try cbor.writeMapHeader(msg_writer, 4); try cbor.writeValue(msg_writer, "jsonrpc"); try cbor.writeValue(msg_writer, "2.0"); @@ -486,7 +476,7 @@ const Process = struct { try cbor.writeValue(msg_writer, "params"); _ = try msg_writer.write(params_cb); - const json = try cbor.toJsonAlloc(self.allocator, request.items); + const json = try cbor.toJsonAlloc(self.allocator, msg.items); defer self.allocator.free(json); var output = std.ArrayList(u8).init(self.allocator); defer output.deinit(); @@ -509,9 +499,9 @@ const Process = struct { fn send_response(self: *Process, cbor_id: []const u8, result_cb: []const u8) (error{Closed} || SendError || cbor.Error || cbor.JsonEncodeError)!void { const sp = if (self.sp) |*sp| sp else return error.Closed; - var response = std.ArrayList(u8).init(self.allocator); - defer response.deinit(); - const msg_writer = response.writer(); + var msg = std.ArrayList(u8).init(self.allocator); + defer msg.deinit(); + const msg_writer = msg.writer(); try cbor.writeMapHeader(msg_writer, 3); try cbor.writeValue(msg_writer, "jsonrpc"); try cbor.writeValue(msg_writer, "2.0"); @@ -520,7 +510,7 @@ const Process = struct { try cbor.writeValue(msg_writer, "result"); _ = try msg_writer.write(result_cb); - const json = try cbor.toJsonAlloc(self.allocator, response.items); + const json = try cbor.toJsonAlloc(self.allocator, msg.items); defer self.allocator.free(json); var output = std.ArrayList(u8).init(self.allocator); defer output.deinit(); @@ -538,9 +528,9 @@ const Process = struct { fn send_error_response(self: *Process, cbor_id: []const u8, error_code: ErrorCode, message: []const u8) (error{Closed} || SendError || cbor.Error || cbor.JsonEncodeError)!void { const sp = if (self.sp) |*sp| sp else return error.Closed; - var response = std.ArrayList(u8).init(self.allocator); - defer response.deinit(); - const msg_writer = response.writer(); + var msg = std.ArrayList(u8).init(self.allocator); + defer msg.deinit(); + const msg_writer = msg.writer(); try cbor.writeMapHeader(msg_writer, 3); try cbor.writeValue(msg_writer, "jsonrpc"); try cbor.writeValue(msg_writer, "2.0"); @@ -553,7 +543,7 @@ const Process = struct { try cbor.writeValue(msg_writer, "message"); try cbor.writeValue(msg_writer, message); - const json = try cbor.toJsonAlloc(self.allocator, response.items); + const json = try cbor.toJsonAlloc(self.allocator, msg.items); defer self.allocator.free(json); var output = std.ArrayList(u8).init(self.allocator); defer output.deinit(); @@ -573,9 +563,9 @@ const Process = struct { const have_params = !(cbor.match(params_cb, cbor.null_) catch false); - var notification = std.ArrayList(u8).init(self.allocator); - defer notification.deinit(); - const msg_writer = notification.writer(); + var msg = std.ArrayList(u8).init(self.allocator); + defer msg.deinit(); + const msg_writer = msg.writer(); try cbor.writeMapHeader(msg_writer, 3); try cbor.writeValue(msg_writer, "jsonrpc"); try cbor.writeValue(msg_writer, "2.0"); @@ -588,7 +578,7 @@ const Process = struct { try cbor.writeMapHeader(msg_writer, 0); } - const json = try cbor.toJsonAlloc(self.allocator, notification.items); + const json = try cbor.toJsonAlloc(self.allocator, msg.items); defer self.allocator.free(json); var output = std.ArrayList(u8).init(self.allocator); defer output.deinit(); @@ -627,9 +617,9 @@ const Process = struct { const json = if (params) |p| try cbor.toJsonPrettyAlloc(self.allocator, p) else null; defer if (json) |p| self.allocator.free(p); self.write_log("### RECV req: {s}\nmethod: {s}\n{s}\n###\n", .{ json_id, method, json orelse "no params" }); - var request = std.ArrayList(u8).init(self.allocator); - defer request.deinit(); - const writer = request.writer(); + var msg = std.ArrayList(u8).init(self.allocator); + defer msg.deinit(); + const writer = msg.writer(); try cbor.writeArrayHeader(writer, 7); try cbor.writeValue(writer, sp_tag); try cbor.writeValue(writer, self.project); @@ -638,7 +628,7 @@ const Process = struct { try cbor.writeValue(writer, method); try writer.writeAll(cbor_id); if (params) |p| _ = try writer.write(p) else try cbor.writeValue(writer, null); - self.parent.send_raw(.{ .buf = request.items }) catch return error.SendFailed; + self.parent.send_raw(.{ .buf = msg.items }) catch return error.SendFailed; } fn receive_lsp_response(self: *Process, cbor_id: []const u8, result: ?[]const u8, err: ?[]const u8) Error!void { @@ -650,9 +640,9 @@ const Process = struct { defer if (json_err) |p| self.allocator.free(p); self.write_log("### RECV rsp: {s} {s}\n{s}\n###\n", .{ json_id, if (json_err) |_| "error" else "response", json_err orelse json orelse "no result" }); const from = self.requests.get(cbor_id) orelse return; - var response = std.ArrayList(u8).init(self.allocator); - defer response.deinit(); - const writer = response.writer(); + var msg = std.ArrayList(u8).init(self.allocator); + defer msg.deinit(); + const writer = msg.writer(); try cbor.writeArrayHeader(writer, 4); try cbor.writeValue(writer, sp_tag); try cbor.writeValue(writer, self.tag); @@ -663,16 +653,16 @@ const Process = struct { try cbor.writeValue(writer, "result"); _ = try writer.write(result_); } - from.send_raw(.{ .buf = response.items }) catch return error.SendFailed; + from.send_raw(.{ .buf = msg.items }) catch return error.SendFailed; } fn receive_lsp_notification(self: *Process, method: []const u8, params: ?[]const u8) Error!void { const json = if (params) |p| try cbor.toJsonPrettyAlloc(self.allocator, p) else null; defer if (json) |p| self.allocator.free(p); self.write_log("### RECV notify:\nmethod: {s}\n{s}\n###\n", .{ method, json orelse "no params" }); - var notification = std.ArrayList(u8).init(self.allocator); - defer notification.deinit(); - const writer = notification.writer(); + var msg = std.ArrayList(u8).init(self.allocator); + defer msg.deinit(); + const writer = msg.writer(); try cbor.writeArrayHeader(writer, 6); try cbor.writeValue(writer, sp_tag); try cbor.writeValue(writer, self.project); @@ -680,7 +670,7 @@ const Process = struct { try cbor.writeValue(writer, "notify"); try cbor.writeValue(writer, method); if (params) |p| _ = try writer.write(p) else try cbor.writeValue(writer, null); - self.parent.send_raw(.{ .buf = notification.items }) catch return error.SendFailed; + self.parent.send_raw(.{ .buf = msg.items }) catch return error.SendFailed; } fn write_log(self: *Process, comptime format: []const u8, args: anytype) void { diff --git a/src/Project.zig b/src/Project.zig index ad71f8c..23eaae1 100644 --- a/src/Project.zig +++ b/src/Project.zig @@ -8,7 +8,6 @@ const Buffer = @import("Buffer"); const fuzzig = @import("fuzzig"); const tracy = @import("tracy"); const git = @import("git"); -const file_type_config = @import("file_type_config"); const builtin = @import("builtin"); const LSP = @import("LSP.zig"); @@ -53,9 +52,6 @@ pub const LspOrClientError = (LspError || ClientError); const File = struct { path: []const u8, - type: []const u8, - icon: []const u8, - color: u24, mtime: i128, pos: FilePos = .{}, visited: bool = false, @@ -345,7 +341,7 @@ pub fn request_n_most_recent_file(self: *Self, from: tp.pid_ref, n: usize) Clien pub fn request_recent_files(self: *Self, from: tp.pid_ref, max: usize) ClientError!void { defer from.send(.{ "PRJ", "recent_done", self.longest_file_path, "" }) catch {}; for (self.files.items, 0..) |file, i| { - from.send(.{ "PRJ", "recent", self.longest_file_path, file.path, file.type, file.icon, file.color }) catch return error.ClientFailed; + from.send(.{ "PRJ", "recent", self.longest_file_path, file.path }) catch return error.ClientFailed; if (i >= max) return; } } @@ -360,7 +356,7 @@ fn simple_query_recent_files(self: *Self, from: tp.pid_ref, max: usize, query: [ defer self.allocator.free(matches); var n: usize = 0; while (n < query.len) : (n += 1) matches[n] = idx + n; - from.send(.{ "PRJ", "recent", self.longest_file_path, file.path, file.type, file.icon, file.color, matches }) catch return error.ClientFailed; + from.send(.{ "PRJ", "recent", self.longest_file_path, file.path, matches }) catch return error.ClientFailed; i += 1; if (i >= max) return i; } @@ -383,9 +379,6 @@ pub fn query_recent_files(self: *Self, from: tp.pid_ref, max: usize, query: []co const Match = struct { path: []const u8, - type: []const u8, - icon: []const u8, - color: u24, score: i32, matches: []const usize, }; @@ -396,9 +389,6 @@ pub fn query_recent_files(self: *Self, from: tp.pid_ref, max: usize, query: []co if (match.score) |score| { (try matches.addOne()).* = .{ .path = file.path, - .type = file.type, - .icon = file.icon, - .color = file.color, .score = score, .matches = try self.allocator.dupe(usize, match.matches), }; @@ -414,24 +404,13 @@ pub fn query_recent_files(self: *Self, from: tp.pid_ref, max: usize, query: []co std.mem.sort(Match, matches.items, {}, less_fn); for (matches.items[0..@min(max, matches.items.len)]) |match| - from.send(.{ "PRJ", "recent", self.longest_file_path, match.path, match.type, match.icon, match.color, match.matches }) catch return error.ClientFailed; + from.send(.{ "PRJ", "recent", self.longest_file_path, match.path, match.matches }) catch return error.ClientFailed; return @min(max, matches.items.len); } -pub fn walk_tree_entry( - self: *Self, - file_path: []const u8, - mtime: i128, -) OutOfMemoryError!void { - const file_type: []const u8, const file_icon: []const u8, const file_color: u24 = guess_file_type(file_path); +pub fn walk_tree_entry(self: *Self, file_path: []const u8, mtime: i128) OutOfMemoryError!void { self.longest_file_path = @max(self.longest_file_path, file_path.len); - (try self.pending.addOne(self.allocator)).* = .{ - .path = try self.allocator.dupe(u8, file_path), - .type = file_type, - .icon = file_icon, - .color = file_color, - .mtime = mtime, - }; + (try self.pending.addOne(self.allocator)).* = .{ .path = try self.allocator.dupe(u8, file_path), .mtime = mtime }; } pub fn walk_tree_done(self: *Self, parent: tp.pid_ref) OutOfMemoryError!void { @@ -441,35 +420,6 @@ pub fn walk_tree_done(self: *Self, parent: tp.pid_ref) OutOfMemoryError!void { return self.loaded(parent); } -fn default_ft() struct { []const u8, []const u8, u24 } { - return .{ - file_type_config.default.name, - file_type_config.default.icon, - file_type_config.default.color, - }; -} - -pub fn guess_path_file_type(path: []const u8, file_name: []const u8) struct { []const u8, []const u8, u24 } { - var buf: [4096]u8 = undefined; - const file_path = std.fmt.bufPrint(&buf, "{s}{}{s}", .{ path, std.fs.path.sep, file_name }) catch return default_ft(); - return guess_file_type(file_path); -} - -pub fn guess_file_type(file_path: []const u8) struct { []const u8, []const u8, u24 } { - var buf: [1024]u8 = undefined; - const content: []const u8 = blk: { - const file = std.fs.cwd().openFile(file_path, .{ .mode = .read_only }) catch break :blk &.{}; - defer file.close(); - const size = file.read(&buf) catch break :blk &.{}; - break :blk buf[0..size]; - }; - return if (file_type_config.guess_file_type(file_path, content)) |ft| .{ - ft.name, - ft.icon orelse file_type_config.default.icon, - ft.color orelse file_type_config.default.color, - } else default_ft(); -} - fn merge_pending_files(self: *Self) OutOfMemoryError!void { defer self.sort_files_by_mtime(); const existing = try self.files.toOwnedSlice(self.allocator); @@ -519,13 +469,9 @@ fn update_mru_internal(self: *Self, file_path: []const u8, mtime: i128, row: usi } return; } - const file_type: []const u8, const file_icon: []const u8, const file_color: u24 = guess_file_type(file_path); if (row != 0) { (try self.files.addOne(self.allocator)).* = .{ .path = try self.allocator.dupe(u8, file_path), - .type = file_type, - .icon = file_icon, - .color = file_color, .mtime = mtime, .pos = .{ .row = row, .col = col }, .visited = true, @@ -533,9 +479,6 @@ fn update_mru_internal(self: *Self, file_path: []const u8, mtime: i128, row: usi } else { (try self.files.addOne(self.allocator)).* = .{ .path = try self.allocator.dupe(u8, file_path), - .type = file_type, - .icon = file_icon, - .color = file_color, .mtime = mtime, }; } @@ -1530,16 +1473,7 @@ fn read_position(position: []const u8) !Position { return .{ .line = line.?, .character = character.? }; } -pub fn show_message(self: *Self, params_cb: []const u8) !void { - return self.show_or_log_message(.show, params_cb); -} - -pub fn log_message(self: *Self, params_cb: []const u8) !void { - return self.show_or_log_message(.log, params_cb); -} - -fn show_or_log_message(self: *Self, operation: enum { show, log }, params_cb: []const u8) !void { - if (!tp.env.get().is("lsp_verbose")) return; +pub fn show_message(self: *Self, _: tp.pid_ref, params_cb: []const u8) !void { var type_: i32 = 0; var message: ?[]const u8 = null; var iter = params_cb; @@ -1559,14 +1493,7 @@ fn show_or_log_message(self: *Self, operation: enum { show, log }, params_cb: [] if (type_ <= 2) self.logger_lsp.err_msg("lsp", msg) else - self.logger_lsp.print("{s}: {s}", .{ @tagName(operation), msg }); -} - -pub fn show_notification(self: *Self, method: []const u8, params_cb: []const u8) !void { - if (!tp.env.get().is("lsp_verbose")) return; - const params = try cbor.toJsonAlloc(self.allocator, params_cb); - defer self.allocator.free(params); - self.logger_lsp.print("LSP notification: {s} -> {s}", .{ method, params }); + self.logger_lsp.print("{s}", .{msg}); } pub fn register_capability(self: *Self, from: tp.pid_ref, cbor_id: []const u8, params_cb: []const u8) ClientError!void { @@ -2014,14 +1941,7 @@ pub fn process_git(self: *Self, parent: tp.pid_ref, m: tp.message) (OutOfMemoryE } else if (try m.match(.{ tp.any, tp.any, "workspace_files", tp.extract(&path) })) { self.longest_file_path = @max(self.longest_file_path, path.len); const stat = std.fs.cwd().statFile(path) catch return; - const file_type: []const u8, const file_icon: []const u8, const file_color: u24 = guess_file_type(path); - (try self.pending.addOne(self.allocator)).* = .{ - .path = try self.allocator.dupe(u8, path), - .type = file_type, - .icon = file_icon, - .color = file_color, - .mtime = stat.mtime, - }; + (try self.pending.addOne(self.allocator)).* = .{ .path = try self.allocator.dupe(u8, path), .mtime = stat.mtime }; } else if (try m.match(.{ tp.any, tp.any, "workspace_files", tp.null_ })) { self.state.workspace_files = .done; try self.loaded(parent); diff --git a/src/buffer/Manager.zig b/src/buffer/Manager.zig index 86422b8..9097d58 100644 --- a/src/buffer/Manager.zig +++ b/src/buffer/Manager.zig @@ -80,7 +80,7 @@ pub fn extract_state(self: *Self, iter: *[]const u8) !void { } } -pub fn get_buffer_for_file(self: *const Self, file_path: []const u8) ?*Buffer { +pub fn get_buffer_for_file(self: *Self, file_path: []const u8) ?*Buffer { return self.buffers.get(file_path); } diff --git a/src/command.zig b/src/command.zig index 16ad9f6..8577a9a 100644 --- a/src/command.zig +++ b/src/command.zig @@ -33,7 +33,6 @@ const Vtable = struct { pub const Metadata = struct { description: []const u8 = &[_]u8{}, arguments: []const ArgumentType = &[_]ArgumentType{}, - icon: ?[]const u8 = null, }; pub const ArgumentType = enum { @@ -189,11 +188,6 @@ pub fn get_arguments(id: ID) ?[]const ArgumentType { return (commands.items[id] orelse return null).meta.arguments; } -pub fn get_icon(id: ID) ?[]const u8 { - if (id >= commands.items.len) return null; - return (commands.items[id] orelse return null).meta.icon; -} - const suppressed_errors = std.StaticStringMap(void).initComptime(.{ .{ "enable_fast_scroll", void }, .{ "disable_fast_scroll", void }, diff --git a/src/config.zig b/src/config.zig index bb12d9a..f2a65e6 100644 --- a/src/config.zig +++ b/src/config.zig @@ -36,13 +36,6 @@ show_fileicons: bool = true, start_debugger_on_crash: bool = false, -widget_style: WidgetStyle = .compact, -palette_style: WidgetStyle = .bars_top_bottom, -panel_style: WidgetStyle = .compact, -home_style: WidgetStyle = .bars_top_bottom, - -lsp_output: enum { quiet, verbose } = .quiet, - include_files: []const u8 = "", pub const DigitStyle = enum { @@ -63,25 +56,3 @@ pub const IndentMode = enum { spaces, tabs, }; - -pub const WidgetType = enum { - none, - palette, - panel, - home, -}; - -pub const WidgetStyle = enum { - bars_top_bottom, - bars_left_right, - thick_boxed, - extra_thick_boxed, - dotted_boxed, - rounded_boxed, - double_boxed, - single_double_top_bottom_boxed, - single_double_left_right_boxed, - boxed, - spacious, - compact, -}; diff --git a/src/file_type_config.zig b/src/file_type_config.zig index 944ea16..008bb9d 100644 --- a/src/file_type_config.zig +++ b/src/file_type_config.zig @@ -20,8 +20,6 @@ pub const default = struct { pub const color = 0x000000; }; -pub const folder_icon = ""; - fn from_file_type(file_type: syntax.FileType) @This() { return .{ .name = file_type.name, @@ -86,7 +84,7 @@ pub fn get(file_type_name: []const u8) !?@This() { break :file_type if (syntax.FileType.get_by_name_static(file_type_name)) |ft| from_file_type(ft) else null; } }; - try cache.put(cache_allocator, try cache_allocator.dupe(u8, file_type_name), file_type); + try cache.put(cache_allocator, file_type_name, file_type); break :self file_type; }; } diff --git a/src/keybind/builtin/flow.json b/src/keybind/builtin/flow.json index 30ddc9c..339972b 100644 --- a/src/keybind/builtin/flow.json +++ b/src/keybind/builtin/flow.json @@ -1,7 +1,6 @@ { "project": { "press": [ - ["ctrl+alt+shift+r", "restart"], ["ctrl+e", "find_file"], ["ctrl+shift+n", "create_new_file"], ["ctrl+r", "open_recent_project"], @@ -29,8 +28,7 @@ ["f10", "theme_next"], ["f11", "toggle_panel"], ["f12", "toggle_inputview"], - ["alt+!", "run_task"], - ["ctrl+1", "add_task"], + ["alt+!", "select_task"], ["ctrl+tab", "next_tab"], ["ctrl+shift+tab", "previous_tab"], ["ctrl+shift+e", "switch_buffers"], @@ -40,7 +38,6 @@ ["f5", ["create_scratch_buffer", "*test*"], ["shell_execute_insert", "zig", "build", "test"]], ["f7", ["create_scratch_buffer", "*build*"], ["shell_execute_insert", "zig", "build"]], ["ctrl+f6", "open_version_info"], - ["alt+shift+t", "set_session_tab_width"], ["alt+d", ["shell_execute_insert", "date", "--iso-8601"]], ["ctrl+alt+shift+d", ["shell_execute_insert", "date", "--iso-8601=seconds"]] ] @@ -165,7 +162,6 @@ ["shift+f11", "toggle_highlight_columns"], ["ctrl+f11", "toggle_inspector_view"], ["f12", "goto_definition"], - ["ctrl+.", "completion"], ["f34", "toggle_whitespace_mode"], ["escape", "cancel"], ["enter", "smart_insert_line"], @@ -240,8 +236,6 @@ ["page_down", "select_page_down"], ["ctrl+page_up", "select_scroll_page_up"], ["ctrl+page_down", "select_scroll_page_down"], - ["ctrl+b", "move_to_char", "select_to_char_left"], - ["ctrl+t", "move_to_char", "select_to_char_right"], ["ctrl+space", "enter_mode", "normal"], ["ctrl+x", ["cut"], ["enter_mode", "normal"], ["cancel"]], ["ctrl+c", ["copy"], ["enter_mode", "normal"], ["cancel"]], @@ -255,7 +249,6 @@ "inherit": "project", "on_match_failure": "ignore", "press": [ - ["alt+f9", "home_next_widget_style"], ["ctrl+e", "find_file"], ["f", "find_file"], ["e", "find_file"], @@ -286,12 +279,9 @@ }, "overlay/palette": { "press": [ - ["alt+f9", "overlay_next_widget_style"], - ["alt+!", "add_task"], ["ctrl+j", "toggle_panel"], ["ctrl+q", "quit"], ["ctrl+w", "close_file"], - ["ctrl+shift+f", "find_in_files"], ["ctrl+p", "palette_menu_up"], ["ctrl+n", "palette_menu_down"], ["ctrl+e", "palette_menu_down"], @@ -338,7 +328,7 @@ ["right_control", "palette_menu_activate_quick"] ] }, - "mini/numeric": { + "mini/goto": { "press": [ ["ctrl+q", "quit"], ["ctrl+v", "system_paste"], @@ -348,7 +338,7 @@ ["ctrl+l", "scroll_view_center_cycle"], ["ctrl+space", "mini_mode_cancel"], ["escape", "mini_mode_cancel"], - ["enter", "mini_mode_select"], + ["enter", "exit_mini_mode"], ["backspace", "mini_mode_delete_backwards"] ] }, @@ -357,8 +347,6 @@ ["ctrl+g", "mini_mode_cancel"], ["ctrl+c", "mini_mode_cancel"], ["ctrl+l", "scroll_view_center_cycle"], - ["tab", "mini_mode_insert_bytes", "\t"], - ["enter", "mini_mode_insert_bytes", "\n"], ["escape", "mini_mode_cancel"], ["backspace", "mini_mode_cancel"] ] diff --git a/src/log.zig b/src/log.zig index 808f9d8..4c4e038 100644 --- a/src/log.zig +++ b/src/log.zig @@ -11,8 +11,6 @@ subscriber: ?tp.pid, heap: [32 + 1024]u8, fba: std.heap.FixedBufferAllocator, msg_store: MsgStore, -no_stdout: bool = false, -no_stderr: bool = false, const MsgStore = std.DoublyLinkedList; const MsgStoreEntry = struct { @@ -87,23 +85,12 @@ fn store_reset(self: *Self) void { fn receive(self: *Self, from: tp.pid_ref, m: tp.message) tp.result { errdefer self.deinit(); - var output: []const u8 = undefined; - if (try m.match(.{ "log", "error", tp.string, "std.log", "->", tp.extract(&output) })) { + if (try m.match(.{ "log", tp.more })) { if (self.subscriber) |subscriber| { subscriber.send_raw(m) catch {}; } else { self.store(m); } - if (!self.no_stderr) - std.io.getStdErr().writer().print("{s}\n", .{output}) catch {}; - } else if (try m.match(.{ "log", tp.string, tp.extract(&output) })) { - if (self.subscriber) |subscriber| { - subscriber.send_raw(m) catch {}; - } else { - self.store(m); - } - if (!self.no_stdout) - std.io.getStdOut().writer().print("{s}\n", .{output}) catch {}; } else if (try m.match(.{"subscribe"})) { // log("subscribed"); if (self.subscriber) |*s| s.deinit(); @@ -114,14 +101,6 @@ fn receive(self: *Self, from: tp.pid_ref, m: tp.message) tp.result { if (self.subscriber) |*s| s.deinit(); self.subscriber = null; self.store_reset(); - } else if (try m.match(.{ "stdout", "enable" })) { - self.no_stdout = false; - } else if (try m.match(.{ "stdout", "disable" })) { - self.no_stdout = true; - } else if (try m.match(.{ "stderr", "enable" })) { - self.no_stderr = false; - } else if (try m.match(.{ "stderr", "disable" })) { - self.no_stderr = true; } else if (try m.match(.{"shutdown"})) { return tp.exit_normal(); } @@ -229,14 +208,6 @@ pub fn unsubscribe() tp.result { return tp.env.get().proc("log").send(.{"unsubscribe"}); } -pub fn stdout(state: enum { enable, disable }) void { - tp.env.get().proc("log").send(.{ "stdout", state }) catch {}; -} - -pub fn stderr(state: enum { enable, disable }) void { - tp.env.get().proc("log").send(.{ "stderr", state }) catch {}; -} - var std_log_pid: ?tp.pid_ref = null; pub fn set_std_log_pid(pid: ?tp.pid_ref) void { diff --git a/src/main.zig b/src/main.zig index e914235..f337409 100644 --- a/src/main.zig +++ b/src/main.zig @@ -6,7 +6,6 @@ const color = @import("color"); const flags = @import("flags"); const builtin = @import("builtin"); const bin_path = @import("bin_path"); -const sep = std.fs.path.sep; const list_languages = @import("list_languages.zig"); pub const file_link = @import("file_link.zig"); @@ -331,12 +330,7 @@ pub fn main() anyerror!void { try cbor.writeValue(writer, cmd_); try cbor.writeArrayHeader(writer, count - 1); - while (cmd_args.next()) |arg| { - if (std.fmt.parseInt(isize, arg, 10) catch null) |i| - try cbor.writeValue(writer, i) - else - try cbor.writeValue(writer, arg); - } + while (cmd_args.next()) |arg| try cbor.writeValue(writer, arg); try tui_proc.send_raw(.{ .buf = msg.items }); } @@ -404,7 +398,7 @@ fn trace_to_file(m: thespian.message.c_buffer_type) callconv(.C) void { const a = std.heap.c_allocator; var path = std.ArrayList(u8).init(a); defer path.deinit(); - path.writer().print("{s}{c}trace.log", .{ get_state_dir() catch return, sep }) catch return; + path.writer().print("{s}/trace.log", .{get_state_dir() catch return}) catch return; const file = std.fs.createFileAbsolute(path.items, .{ .truncate = true }) catch return; State.state = .{ .file = file, @@ -508,12 +502,12 @@ pub fn parse_text_config_file(T: type, allocator: std.mem.Allocator, conf: *T, b lineno += 1; if (line.len == 0 or line[0] == '#') continue; - const spc = std.mem.indexOfScalar(u8, line, ' ') orelse { + const sep = std.mem.indexOfScalar(u8, line, ' ') orelse { std.log.err("{s}:{}: {s} missing value", .{ file_name, lineno, line }); continue; }; - const name = line[0..spc]; - const value_str = line[spc + 1 ..]; + const name = line[0..sep]; + const value_str = line[sep + 1 ..]; const cb = cbor.fromJsonAlloc(allocator, value_str) catch { std.log.err("{s}:{}: {s} has bad value: {s}", .{ file_name, lineno, name, value_str }); continue; @@ -787,11 +781,6 @@ pub const ConfigDirError = error{ AppConfigDirUnavailable, }; -fn make_dir_error(path: []const u8, err: anytype) @TypeOf(err) { - std.log.err("failed to create directory: '{s}'", .{path}); - return err; -} - fn get_app_config_dir(appname: []const u8) ConfigDirError![]const u8 { const a = std.heap.c_allocator; const local = struct { @@ -802,22 +791,22 @@ fn get_app_config_dir(appname: []const u8) ConfigDirError![]const u8 { dir else if (std.process.getEnvVarOwned(a, "XDG_CONFIG_HOME") catch null) |xdg| ret: { defer a.free(xdg); - break :ret try std.fmt.bufPrint(&local.config_dir_buffer, "{s}{c}{s}", .{ xdg, sep, appname }); + break :ret try std.fmt.bufPrint(&local.config_dir_buffer, "{s}/{s}", .{ xdg, appname }); } else if (std.process.getEnvVarOwned(a, "HOME") catch null) |home| ret: { defer a.free(home); - const dir = try std.fmt.bufPrint(&local.config_dir_buffer, "{s}{c}.config", .{ home, sep }); + const dir = try std.fmt.bufPrint(&local.config_dir_buffer, "{s}/.config", .{home}); std.fs.makeDirAbsolute(dir) catch |e| switch (e) { error.PathAlreadyExists => {}, - else => return make_dir_error(dir, error.MakeHomeConfigDirFailed), + else => return error.MakeHomeConfigDirFailed, }; - break :ret try std.fmt.bufPrint(&local.config_dir_buffer, "{s}{c}.config{c}{s}", .{ home, sep, sep, appname }); + break :ret try std.fmt.bufPrint(&local.config_dir_buffer, "{s}/.config/{s}", .{ home, appname }); } else if (builtin.os.tag == .windows) ret: { if (std.process.getEnvVarOwned(a, "APPDATA") catch null) |appdata| { defer a.free(appdata); - const dir = try std.fmt.bufPrint(&local.config_dir_buffer, "{s}{c}{s}", .{ appdata, sep, appname }); + const dir = try std.fmt.bufPrint(&local.config_dir_buffer, "{s}/{s}", .{ appdata, appname }); std.fs.makeDirAbsolute(dir) catch |e| switch (e) { error.PathAlreadyExists => {}, - else => return make_dir_error(dir, error.MakeAppConfigDirFailed), + else => return error.MakeAppConfigDirFailed, }; break :ret dir; } else return error.AppConfigDirUnavailable; @@ -826,14 +815,14 @@ fn get_app_config_dir(appname: []const u8) ConfigDirError![]const u8 { local.config_dir = config_dir; std.fs.makeDirAbsolute(config_dir) catch |e| switch (e) { error.PathAlreadyExists => {}, - else => return make_dir_error(config_dir, error.MakeConfigDirFailed), + else => return error.MakeConfigDirFailed, }; var keybind_dir_buffer: [std.posix.PATH_MAX]u8 = undefined; - std.fs.makeDirAbsolute(try std.fmt.bufPrint(&keybind_dir_buffer, "{s}{c}{s}", .{ config_dir, sep, keybind_dir })) catch {}; + std.fs.makeDirAbsolute(try std.fmt.bufPrint(&keybind_dir_buffer, "{s}/{s}", .{ config_dir, keybind_dir })) catch {}; var theme_dir_buffer: [std.posix.PATH_MAX]u8 = undefined; - std.fs.makeDirAbsolute(try std.fmt.bufPrint(&theme_dir_buffer, "{s}{c}{s}", .{ config_dir, sep, theme_dir })) catch {}; + std.fs.makeDirAbsolute(try std.fmt.bufPrint(&theme_dir_buffer, "{s}/{s}", .{ config_dir, theme_dir })) catch {}; return config_dir; } @@ -852,22 +841,22 @@ fn get_app_cache_dir(appname: []const u8) ![]const u8 { dir else if (std.process.getEnvVarOwned(a, "XDG_CACHE_HOME") catch null) |xdg| ret: { defer a.free(xdg); - break :ret try std.fmt.bufPrint(&local.cache_dir_buffer, "{s}{c}{s}", .{ xdg, sep, appname }); + break :ret try std.fmt.bufPrint(&local.cache_dir_buffer, "{s}/{s}", .{ xdg, appname }); } else if (std.process.getEnvVarOwned(a, "HOME") catch null) |home| ret: { defer a.free(home); - const dir = try std.fmt.bufPrint(&local.cache_dir_buffer, "{s}{c}.cache", .{ home, sep }); + const dir = try std.fmt.bufPrint(&local.cache_dir_buffer, "{s}/.cache", .{home}); std.fs.makeDirAbsolute(dir) catch |e| switch (e) { error.PathAlreadyExists => {}, - else => return make_dir_error(dir, e), + else => return e, }; - break :ret try std.fmt.bufPrint(&local.cache_dir_buffer, "{s}{c}.cache{c}{s}", .{ home, sep, sep, appname }); + break :ret try std.fmt.bufPrint(&local.cache_dir_buffer, "{s}/.cache/{s}", .{ home, appname }); } else if (builtin.os.tag == .windows) ret: { if (std.process.getEnvVarOwned(a, "APPDATA") catch null) |appdata| { defer a.free(appdata); - const dir = try std.fmt.bufPrint(&local.cache_dir_buffer, "{s}{c}{s}", .{ appdata, sep, appname }); + const dir = try std.fmt.bufPrint(&local.cache_dir_buffer, "{s}/{s}", .{ appdata, appname }); std.fs.makeDirAbsolute(dir) catch |e| switch (e) { error.PathAlreadyExists => {}, - else => return make_dir_error(dir, e), + else => return e, }; break :ret dir; } else return error.AppCacheDirUnavailable; @@ -876,7 +865,7 @@ fn get_app_cache_dir(appname: []const u8) ![]const u8 { local.cache_dir = cache_dir; std.fs.makeDirAbsolute(cache_dir) catch |e| switch (e) { error.PathAlreadyExists => {}, - else => return make_dir_error(cache_dir, e), + else => return e, }; return cache_dir; } @@ -895,27 +884,27 @@ fn get_app_state_dir(appname: []const u8) ![]const u8 { dir else if (std.process.getEnvVarOwned(a, "XDG_STATE_HOME") catch null) |xdg| ret: { defer a.free(xdg); - break :ret try std.fmt.bufPrint(&local.state_dir_buffer, "{s}{c}{s}", .{ xdg, sep, appname }); + break :ret try std.fmt.bufPrint(&local.state_dir_buffer, "{s}/{s}", .{ xdg, appname }); } else if (std.process.getEnvVarOwned(a, "HOME") catch null) |home| ret: { defer a.free(home); - var dir = try std.fmt.bufPrint(&local.state_dir_buffer, "{s}{c}.local", .{ home, sep }); + var dir = try std.fmt.bufPrint(&local.state_dir_buffer, "{s}/.local", .{home}); std.fs.makeDirAbsolute(dir) catch |e| switch (e) { error.PathAlreadyExists => {}, - else => return make_dir_error(dir, e), + else => return e, }; - dir = try std.fmt.bufPrint(&local.state_dir_buffer, "{s}{c}.local{c}state", .{ home, sep, sep }); + dir = try std.fmt.bufPrint(&local.state_dir_buffer, "{s}/.local/state", .{home}); std.fs.makeDirAbsolute(dir) catch |e| switch (e) { error.PathAlreadyExists => {}, - else => return make_dir_error(dir, e), + else => return e, }; - break :ret try std.fmt.bufPrint(&local.state_dir_buffer, "{s}{c}.local{c}state{c}{s}", .{ home, sep, sep, sep, appname }); + break :ret try std.fmt.bufPrint(&local.state_dir_buffer, "{s}/.local/state/{s}", .{ home, appname }); } else if (builtin.os.tag == .windows) ret: { if (std.process.getEnvVarOwned(a, "APPDATA") catch null) |appdata| { defer a.free(appdata); - const dir = try std.fmt.bufPrint(&local.state_dir_buffer, "{s}{c}{s}", .{ appdata, sep, appname }); + const dir = try std.fmt.bufPrint(&local.state_dir_buffer, "{s}/{s}", .{ appdata, appname }); std.fs.makeDirAbsolute(dir) catch |e| switch (e) { error.PathAlreadyExists => {}, - else => return make_dir_error(dir, e), + else => return e, }; break :ret dir; } else return error.AppCacheDirUnavailable; @@ -924,7 +913,7 @@ fn get_app_state_dir(appname: []const u8) ![]const u8 { local.state_dir = state_dir; std.fs.makeDirAbsolute(state_dir) catch |e| switch (e) { error.PathAlreadyExists => {}, - else => return make_dir_error(state_dir, e), + else => return e, }; return state_dir; } @@ -937,7 +926,7 @@ fn get_app_config_dir_file_name(appname: []const u8, comptime config_file_name: const local = struct { var config_file_buffer: [std.posix.PATH_MAX]u8 = undefined; }; - return std.fmt.bufPrint(&local.config_file_buffer, "{s}{c}{s}", .{ try get_app_config_dir(appname), sep, config_file_name }); + return std.fmt.bufPrint(&local.config_file_buffer, "{s}/{s}", .{ try get_app_config_dir(appname), config_file_name }); } pub fn get_config_file_name(T: type) ![]const u8 { @@ -953,7 +942,7 @@ pub fn get_restore_file_name() ![]const u8 { const restore_file = if (local.restore_file) |file| file else - try std.fmt.bufPrint(&local.restore_file_buffer, "{s}{c}{s}", .{ try get_app_state_dir(application_name), sep, restore_file_name }); + try std.fmt.bufPrint(&local.restore_file_buffer, "{s}/{s}", .{ try get_app_state_dir(application_name), restore_file_name }); local.restore_file = restore_file; return restore_file; } @@ -969,7 +958,7 @@ fn get_keybind_namespaces_directory() ![]const u8 { defer a.free(dir); return try std.fmt.bufPrint(&local.dir_buffer, "{s}", .{dir}); } - return try std.fmt.bufPrint(&local.dir_buffer, "{s}{c}{s}", .{ try get_app_config_dir(application_name), sep, keybind_dir }); + return try std.fmt.bufPrint(&local.dir_buffer, "{s}/{s}", .{ try get_app_config_dir(application_name), keybind_dir }); } pub fn get_keybind_namespace_file_name(namespace_name: []const u8) ![]const u8 { @@ -977,7 +966,7 @@ pub fn get_keybind_namespace_file_name(namespace_name: []const u8) ![]const u8 { const local = struct { var file_buffer: [std.posix.PATH_MAX]u8 = undefined; }; - return try std.fmt.bufPrint(&local.file_buffer, "{s}{c}{s}.json", .{ dir, sep, namespace_name }); + return try std.fmt.bufPrint(&local.file_buffer, "{s}/{s}.json", .{ dir, namespace_name }); } const theme_dir = "themes"; @@ -991,7 +980,7 @@ fn get_theme_directory() ![]const u8 { defer a.free(dir); return try std.fmt.bufPrint(&local.dir_buffer, "{s}", .{dir}); } - return try std.fmt.bufPrint(&local.dir_buffer, "{s}{c}{s}", .{ try get_app_config_dir(application_name), sep, theme_dir }); + return try std.fmt.bufPrint(&local.dir_buffer, "{s}/{s}", .{ try get_app_config_dir(application_name), theme_dir }); } pub fn get_theme_file_name(theme_name: []const u8) ![]const u8 { @@ -999,7 +988,7 @@ pub fn get_theme_file_name(theme_name: []const u8) ![]const u8 { const local = struct { var file_buffer: [std.posix.PATH_MAX]u8 = undefined; }; - return try std.fmt.bufPrint(&local.file_buffer, "{s}{c}{s}.json", .{ dir, sep, theme_name }); + return try std.fmt.bufPrint(&local.file_buffer, "{s}/{s}.json", .{ dir, theme_name }); } fn restart() noreturn { diff --git a/src/project_manager.zig b/src/project_manager.zig index 44e2f31..c29a78a 100644 --- a/src/project_manager.zig +++ b/src/project_manager.zig @@ -27,7 +27,7 @@ const OutOfMemoryError = error{OutOfMemory}; const FileSystemError = error{FileSystem}; const SetCwdError = if (builtin.os.tag == .windows) error{UnrecognizedVolume} else error{}; const CallError = tp.CallError; -const ProjectManagerError = (SpawnError || error{ ProjectManagerFailed, InvalidProjectDirectory }); +const ProjectManagerError = (SpawnError || error{ProjectManagerFailed}); pub fn get() SpawnError!Self { const pid = tp.env.get().proc(module_name); @@ -63,7 +63,6 @@ pub fn open(rel_project_directory: []const u8) (ProjectManagerError || FileSyste const project_directory = std.fs.cwd().realpath(rel_project_directory, &path_buf) catch "(none)"; const current_project = tp.env.get().str("project"); if (std.mem.eql(u8, current_project, project_directory)) return; - if (!root.is_directory(project_directory)) return error.InvalidProjectDirectory; var dir = try std.fs.openDirAbsolute(project_directory, .{}); try dir.setAsCwd(); dir.close(); @@ -98,9 +97,9 @@ pub fn request_recent_files(max: usize) (ProjectManagerError || ProjectError)!vo return send(.{ "request_recent_files", project, max }); } -pub fn request_recent_projects() (ProjectManagerError || ProjectError)!void { +pub fn request_recent_projects(allocator: std.mem.Allocator) (ProjectError || CallError)!tp.message { const project = tp.env.get().str("project"); - return send(.{ "request_recent_projects", project }); + return (try get()).pid.call(allocator, request_timeout, .{ "request_recent_projects", project }); } pub fn query_recent_files(max: usize, query: []const u8) (ProjectManagerError || ProjectError)!void { @@ -333,8 +332,6 @@ const Process = struct { var n: usize = 0; var task: []const u8 = undefined; var context: usize = undefined; - var tag: []const u8 = undefined; - var message: []const u8 = undefined; var eol_mode: Buffer.EolModeTag = @intFromEnum(Buffer.EolMode.lf); @@ -407,11 +404,6 @@ const Process = struct { self.hover(from, project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed; } else if (try cbor.match(m.buf, .{ "get_mru_position", tp.extract(&project_directory), tp.extract(&path) })) { self.get_mru_position(from, project_directory, path) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed; - } else if (try cbor.match(m.buf, .{ "lsp", "msg", tp.extract(&tag), tp.extract(&message) })) { - if (tp.env.get().is("lsp_verbose")) - self.logger.print("{s}: {s}", .{ tag, message }); - } else if (try cbor.match(m.buf, .{ "lsp", "err", tp.extract(&tag), tp.extract(&message) })) { - self.logger.print("{s} error: {s}", .{ tag, message }); } else if (try cbor.match(m.buf, .{"shutdown"})) { self.persist_projects(); from.send(.{ "project_manager", "shutdown" }) catch return error.ClientFailed; @@ -469,9 +461,6 @@ const Process = struct { self.sort_projects_by_last_used(&recent_projects); var message = std.ArrayList(u8).init(self.allocator); const writer = message.writer(); - try cbor.writeArrayHeader(writer, 3); - try cbor.writeValue(writer, "PRJ"); - try cbor.writeValue(writer, "recent_projects"); try cbor.writeArrayHeader(writer, recent_projects.items.len); for (recent_projects.items) |project| { try cbor.writeArrayHeader(writer, 2); @@ -479,7 +468,6 @@ const Process = struct { try cbor.writeValue(writer, if (self.projects.get(project.name)) |_| true else false); } from.send_raw(.{ .buf = message.items }) catch return error.ClientFailed; - self.logger.print("{d} projects found", .{recent_projects.items.len}); } fn query_recent_files(self: *Process, from: tp.pid_ref, project_directory: []const u8, max: usize, query: []const u8) (ProjectError || Project.ClientError)!void { @@ -615,11 +603,14 @@ const Process = struct { return if (std.mem.eql(u8, method, "textDocument/publishDiagnostics")) project.publish_diagnostics(self.parent.ref(), params_cb) else if (std.mem.eql(u8, method, "window/showMessage")) - project.show_message(params_cb) + project.show_message(self.parent.ref(), params_cb) else if (std.mem.eql(u8, method, "window/logMessage")) - project.log_message(params_cb) - else - project.show_notification(method, params_cb); + project.show_message(self.parent.ref(), params_cb) + else { + const params = try cbor.toJsonAlloc(self.allocator, params_cb); + defer self.allocator.free(params); + self.logger.print("LSP notification: {s} -> {s}", .{ method, params }); + }; } fn dispatch_request(self: *Process, from: tp.pid_ref, project_directory: []const u8, language_server: []const u8, method: []const u8, cbor_id: []const u8, params_cb: []const u8) (ProjectError || Project.ClientError || cbor.Error || cbor.JsonEncodeError || UnsupportedError)!void { @@ -802,19 +793,12 @@ fn request_path_files_async(a_: std.mem.Allocator, parent_: tp.pid_ref, project_ var iter = self.dir.iterateAssumeFirstIteration(); errdefer |e| self.parent.send(.{ "PRJ", "path_error", self.project_name, self.path, e }) catch {}; while (try iter.next()) |entry| { - const event_type = switch (entry.kind) { - .directory => "DIR", - .sym_link => "LINK", - .file => "FILE", + switch (entry.kind) { + .directory => try self.parent.send(.{ "PRJ", "path_entry", self.project_name, self.path, "DIR", entry.name }), + .sym_link => try self.parent.send(.{ "PRJ", "path_entry", self.project_name, self.path, "LINK", entry.name }), + .file => try self.parent.send(.{ "PRJ", "path_entry", self.project_name, self.path, "FILE", entry.name }), else => continue, - }; - const default = file_type_config.default; - const file_type, const icon, const color = switch (entry.kind) { - .directory => .{ "directory", file_type_config.folder_icon, default.color }, - .sym_link, .file => Project.guess_path_file_type(self.path, entry.name), - else => .{ default.name, default.icon, default.color }, - }; - try self.parent.send(.{ "PRJ", "path_entry", self.project_name, self.path, event_type, entry.name, file_type, icon, color }); + } count += 1; if (count >= self.max) break; } diff --git a/src/renderer/vaxis/renderer.zig b/src/renderer/vaxis/renderer.zig index f9343de..44b121d 100644 --- a/src/renderer/vaxis/renderer.zig +++ b/src/renderer/vaxis/renderer.zig @@ -323,9 +323,6 @@ pub fn process_renderer_event(self: *Self, msg: []const u8) Error!void { })), }; }, - .mouse_leave => { - if (self.dispatch_event) |f| f(self.handler_ctx, try self.fmtmsg(.{"mouse_leave"})); - }, .focus_in => { if (self.dispatch_event) |f| f(self.handler_ctx, try self.fmtmsg(.{"focus_in"})); }, diff --git a/src/syntax/.gitignore b/src/syntax/.gitignore new file mode 100644 index 0000000..5211f11 --- /dev/null +++ b/src/syntax/.gitignore @@ -0,0 +1 @@ +/.zig-cache/ diff --git a/src/syntax/LICENSE b/src/syntax/LICENSE new file mode 100644 index 0000000..0c64a22 --- /dev/null +++ b/src/syntax/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 CJ van den Berg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/syntax/README.md b/src/syntax/README.md new file mode 100644 index 0000000..f1cc61a --- /dev/null +++ b/src/syntax/README.md @@ -0,0 +1,2 @@ +# flow-syntax +Syntax highlighting module used by [flow](https://github.com/neurocyte/flow), [zat](https://github.com/neurocyte/zat) and [zine](https://github.com/kristoff-it/zine) diff --git a/src/syntax/build.zig b/src/syntax/build.zig new file mode 100644 index 0000000..4251ae1 --- /dev/null +++ b/src/syntax/build.zig @@ -0,0 +1,154 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const use_tree_sitter = b.option(bool, "use_tree_sitter", "Enable tree-sitter (default: yes)") orelse true; + const options = b.addOptions(); + options.addOption(bool, "use_tree_sitter", use_tree_sitter); + const options_mod = options.createModule(); + + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const tree_sitter_dep = b.dependency("tree_sitter", .{ + .target = target, + .optimize = optimize, + }); + + const tree_sitter_host_dep = b.dependency("tree_sitter", .{}); + + const cbor_dep = b.dependency("cbor", .{ + .target = target, + .optimize = optimize, + }); + + const ts_bin_query_gen = b.addExecutable(.{ + .name = "ts_bin_query_gen", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/ts_bin_query_gen.zig"), + .target = b.graph.host, + .optimize = .Debug, + }), + }); + ts_bin_query_gen.linkLibC(); + ts_bin_query_gen.root_module.addImport("cbor", cbor_dep.module("cbor")); + ts_bin_query_gen.root_module.addImport("treez", tree_sitter_host_dep.module("treez")); + ts_bin_query_gen.root_module.addImport("build_options", options_mod); + + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "queries/cmake/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-agda/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-astro/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-bash/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-c-sharp/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-c/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-cpp/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-css/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-diff/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-dockerfile/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-elixir/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-git-rebase/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-gitcommit/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-gleam/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-go/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-fish/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-haskell/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-hare/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-html/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-hurl/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-java/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-javascript/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-jsdoc/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-json/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-julia/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-kdl/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-lua/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-mail/queries/mail/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-make/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-markdown/tree-sitter-markdown/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-markdown/tree-sitter-markdown-inline/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-nasm/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-nim/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-ninja/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-nix/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-nu/queries/nu/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-ocaml/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-odin/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-openscad/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-org/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-php/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-powershell/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-proto/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-python/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-purescript/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-regex/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-rpmspec/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-ruby/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-rust/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-ssh-config/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-scala/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-scheme/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-superhtml/tree-sitter-superhtml/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-sql/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-swift/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-toml/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-typescript/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-typst/queries/typst/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-uxntal/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-vim/queries/vim/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-xml/queries/dtd/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-xml/queries/xml/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-yaml/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-zig/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-ziggy/tree-sitter-ziggy/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-ziggy/tree-sitter-ziggy-schema/queries/highlights.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "nvim-treesitter/queries/verilog/highlights.scm"); + + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "queries/cmake/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-astro/queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-cpp/queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-elixir/queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-gitcommit/queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-hare/queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-html/queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-hurl/queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-javascript/queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-kdl/queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-lua/queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-markdown/tree-sitter-markdown-inline/queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-markdown/tree-sitter-markdown/queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-nasm/queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-nix/queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-nu/queries/nu/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-odin/queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-openscad/queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-php/queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-purescript/queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-purescript/vim_queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-rust/queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-superhtml/tree-sitter-superhtml/queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-swift/queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-typst/queries/typst/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-uxntal/queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-vim/queries/vim/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-zig/queries/injections.scm"); + ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "nvim-treesitter/queries/verilog/injections.scm"); + + const syntax_mod = b.addModule("syntax", .{ + .root_source_file = b.path("src/syntax.zig"), + .imports = &.{ + .{ .name = "build_options", .module = options_mod }, + .{ .name = "cbor", .module = cbor_dep.module("cbor") }, + .{ .name = "treez", .module = tree_sitter_dep.module("treez") }, + }, + }); + + if (use_tree_sitter) { + const ts_bin_query_gen_step = b.addRunArtifact(ts_bin_query_gen); + const output = ts_bin_query_gen_step.addOutputFileArg("bin_queries.cbor"); + syntax_mod.addAnonymousImport("syntax_bin_queries", .{ .root_source_file = output }); + } +} + +fn ts_queryfile(b: *std.Build, dep: *std.Build.Dependency, bin_gen: *std.Build.Step.Compile, comptime sub_path: []const u8) void { + const module = b.createModule(.{ .root_source_file = dep.path(sub_path) }); + bin_gen.root_module.addImport(sub_path, module); +} diff --git a/src/syntax/build.zig.zon b/src/syntax/build.zig.zon new file mode 100644 index 0000000..5d8194d --- /dev/null +++ b/src/syntax/build.zig.zon @@ -0,0 +1,22 @@ +.{ + .name = .flow_syntax, + .version = "0.1.0", + .fingerprint = 0x3ba2584ea1cec85f, + .minimum_zig_version = "0.15.0-dev.1048+f43f89a70", + + .dependencies = .{ + .tree_sitter = .{ + .url = "https://github.com/neurocyte/tree-sitter/releases/download/master-f1f032d24f621e2ee4deab1c424d3bf9fb809f6e/source.tar.gz", + .hash = "tree_sitter-0.22.4-150-g7e3f5726-z0LhyN88UicDHlr22vQnOZ3DW9NWN1gOhDwLuCRXvrh2", + }, + .cbor = .{ + .url = "git+https://github.com/neurocyte/cbor#6eccce0b984296e7d05c20d83933cb31530e4fac", + .hash = "cbor-1.0.0-RcQE_N3yAADXjbyvhsmTQ6lf22l1nYgePq5FT8NaC4ic", + }, + }, + .paths = .{ + "src", + "build.zig", + "build.zig.zon", + }, +} diff --git a/src/syntax/src/QueryCache.zig b/src/syntax/src/QueryCache.zig new file mode 100644 index 0000000..7b011d5 --- /dev/null +++ b/src/syntax/src/QueryCache.zig @@ -0,0 +1,195 @@ +const std = @import("std"); +const build_options = @import("build_options"); + +const treez = if (build_options.use_tree_sitter) + @import("treez") +else + @import("treez_dummy.zig"); + +const Self = @This(); + +pub const tss = @import("ts_serializer.zig"); +pub const FileType = @import("file_type.zig"); +const Query = treez.Query; + +allocator: std.mem.Allocator, +mutex: ?std.Thread.Mutex, +highlights: std.StringHashMapUnmanaged(*CacheEntry) = .{}, +injections: std.StringHashMapUnmanaged(*CacheEntry) = .{}, +errors: std.StringHashMapUnmanaged(*CacheEntry) = .{}, +ref_count: usize = 1, + +const CacheEntry = struct { + mutex: ?std.Thread.Mutex, + query: ?*Query, + query_arena: ?*std.heap.ArenaAllocator, + query_type: QueryType, + file_type_name: []const u8, + lang_fn: FileType.LangFn, + + fn destroy(self: *@This(), allocator: std.mem.Allocator) void { + if (self.query_arena) |a| { + a.deinit(); + allocator.destroy(a); + } else if (self.query) |q| + q.destroy(); + self.query_arena = null; + self.query = null; + } +}; + +pub const QueryType = enum { + highlights, + errors, + injections, +}; + +const QueryParseError = error{ + InvalidSyntax, + InvalidNodeType, + InvalidField, + InvalidCapture, + InvalidStructure, + InvalidLanguage, +}; + +const CacheError = error{ + NotFound, + OutOfMemory, +}; + +pub const Error = CacheError || QueryParseError || QuerySerializeError; + +pub fn create(allocator: std.mem.Allocator, opts: struct { lock: bool = false }) !*Self { + const self = try allocator.create(Self); + errdefer allocator.destroy(self); + self.* = .{ + .allocator = allocator, + .mutex = if (opts.lock) .{} else null, + }; + return self; +} + +pub fn deinit(self: *Self) void { + self.release_ref_unlocked_and_maybe_destroy(); +} + +fn add_ref_locked(self: *Self) void { + std.debug.assert(self.ref_count > 0); + self.ref_count += 1; +} + +fn release_ref_unlocked_and_maybe_destroy(self: *Self) void { + { + if (self.mutex) |*mtx| mtx.lock(); + defer if (self.mutex) |*mtx| mtx.unlock(); + self.ref_count -= 1; + if (self.ref_count > 0) return; + } + + release_cache_entry_hash_map(self.allocator, &self.highlights); + release_cache_entry_hash_map(self.allocator, &self.errors); + release_cache_entry_hash_map(self.allocator, &self.injections); + self.allocator.destroy(self); +} + +fn release_cache_entry_hash_map(allocator: std.mem.Allocator, hash_map: *std.StringHashMapUnmanaged(*CacheEntry)) void { + var iter = hash_map.iterator(); + while (iter.next()) |p| { + allocator.free(p.key_ptr.*); + p.value_ptr.*.destroy(allocator); + allocator.destroy(p.value_ptr.*); + } + hash_map.deinit(allocator); +} + +fn get_cache_entry(self: *Self, file_type: FileType, comptime query_type: QueryType) CacheError!*CacheEntry { + if (self.mutex) |*mtx| mtx.lock(); + defer if (self.mutex) |*mtx| mtx.unlock(); + + const hash = switch (query_type) { + .highlights => &self.highlights, + .errors => &self.errors, + .injections => &self.injections, + }; + + return if (hash.get(file_type.name)) |entry| entry else blk: { + const entry_ = try hash.getOrPut(self.allocator, try self.allocator.dupe(u8, file_type.name)); + + const q = try self.allocator.create(CacheEntry); + q.* = .{ + .query = null, + .query_arena = null, + .mutex = if (self.mutex) |_| .{} else null, + .lang_fn = file_type.lang_fn, + .file_type_name = file_type.name, + .query_type = query_type, + }; + entry_.value_ptr.* = q; + + break :blk q; + }; +} + +fn get_cached_query(self: *Self, entry: *CacheEntry) Error!?*Query { + if (entry.mutex) |*mtx| mtx.lock(); + defer if (entry.mutex) |*mtx| mtx.unlock(); + + return if (entry.query) |query| query else blk: { + const lang = entry.lang_fn() orelse std.debug.panic("tree-sitter parser function failed for language: {s}", .{entry.file_type_name}); + const queries = FileType.queries.get(entry.file_type_name) orelse return null; + const query_bin = switch (entry.query_type) { + .highlights => queries.highlights_bin, + .errors => queries.errors_bin, + .injections => queries.injections_bin orelse return null, + }; + const query, const arena = try deserialize_query(query_bin, lang, self.allocator); + entry.query = query; + entry.query_arena = arena; + break :blk entry.query.?; + }; +} + +fn pre_load_internal(self: *Self, file_type: *const FileType, comptime query_type: QueryType) Error!void { + _ = try self.get_cached_query(try self.get_cache_entry(file_type, query_type)); +} + +pub fn pre_load(self: *Self, lang_name: []const u8) Error!void { + const file_type = FileType.get_by_name(lang_name) orelse return; + _ = try self.pre_load_internal(file_type, .highlights); + _ = try self.pre_load_internal(file_type, .errors); + _ = try self.pre_load_internal(file_type, .injections); +} + +fn ReturnType(comptime query_type: QueryType) type { + return switch (query_type) { + .highlights => *Query, + .errors => *Query, + .injections => ?*Query, + }; +} + +pub fn get(self: *Self, file_type: FileType, comptime query_type: QueryType) Error!ReturnType(query_type) { + const query = try self.get_cached_query(try self.get_cache_entry(file_type, query_type)); + self.add_ref_locked(); + return switch (@typeInfo(ReturnType(query_type))) { + .optional => |_| query, + else => query.?, + }; +} + +pub fn release(self: *Self, query: *Query, comptime query_type: QueryType) void { + _ = query; + _ = query_type; + self.release_ref_unlocked_and_maybe_destroy(); +} + +pub const QuerySerializeError = (tss.SerializeError || tss.DeserializeError); + +fn deserialize_query(query_bin: []const u8, language: ?*const treez.Language, allocator: std.mem.Allocator) QuerySerializeError!struct { *Query, *std.heap.ArenaAllocator } { + var ts_query_out, const arena = try tss.fromCbor(query_bin, allocator); + ts_query_out.language = @intFromPtr(language); + + const query_out: *Query = @alignCast(@ptrCast(ts_query_out)); + return .{ query_out, arena }; +} diff --git a/src/syntax/src/file_type.zig b/src/syntax/src/file_type.zig new file mode 100644 index 0000000..9faa6fa --- /dev/null +++ b/src/syntax/src/file_type.zig @@ -0,0 +1,207 @@ +const std = @import("std"); +const cbor = @import("cbor"); +const build_options = @import("build_options"); + +const treez = if (build_options.use_tree_sitter) + @import("treez") +else + @import("treez_dummy.zig"); + +pub const FileType = @This(); + +color: u24, +icon: []const u8, +name: []const u8, +description: []const u8, +lang_fn: LangFn, +extensions: []const []const u8, +first_line_matches: ?FirstLineMatch = null, +comment: []const u8, +formatter: ?[]const []const u8, +language_server: ?[]const []const u8, + +pub fn get_by_name_static(name: []const u8) ?FileType { + return FileType.static_file_types.get(name); +} + +pub fn get_all() []const FileType { + return FileType.static_file_types.values(); +} + +pub fn guess_static(file_path: ?[]const u8, content: []const u8) ?FileType { + if (guess_first_line_static(content)) |ft| return ft; + for (static_file_types.values()) |file_type| + if (file_path) |fp| if (match_file_type(file_type.extensions, fp)) + return file_type; + return null; +} + +fn guess_first_line_static(content: []const u8) ?FileType { + const first_line = if (std.mem.indexOf(u8, content, "\n")) |pos| content[0..pos] else content; + for (static_file_types.values()) |file_type| + if (file_type.first_line_matches) |match| + if (match_first_line(match.prefix, match.content, first_line)) + return file_type; + return null; +} + +pub fn match_first_line(match_prefix: ?[]const u8, match_content: ?[]const u8, first_line: []const u8) bool { + if (match_prefix == null and match_content == null) return false; + if (match_prefix) |prefix| + if (prefix.len > first_line.len or !std.mem.eql(u8, first_line[0..prefix.len], prefix)) + return false; + if (match_content) |content| + if (std.mem.indexOf(u8, first_line, content)) |_| {} else return false; + return true; +} + +pub fn match_file_type(extensions: []const []const u8, file_path: []const u8) bool { + const basename = std.fs.path.basename(file_path); + const extension = std.fs.path.extension(file_path); + return for (extensions) |ext| { + if (ext.len == basename.len and std.mem.eql(u8, ext, basename)) + return true; + if (extension.len > 0 and ext.len == extension.len - 1 and std.mem.eql(u8, ext, extension[1..])) + return true; + } else false; +} + +pub fn Parser(comptime lang: []const u8) LangFn { + return get_parser(lang); +} + +fn get_parser(comptime lang: []const u8) LangFn { + if (build_options.use_tree_sitter) { + const language_name = ft_func_name(lang); + return @extern(?LangFn, .{ .name = "tree_sitter_" ++ language_name }) orelse @compileError(std.fmt.comptimePrint("Cannot find extern tree_sitter_{s}", .{language_name})); + } else { + return treez.Language.LangFn; + } +} + +fn ft_func_name(comptime lang: []const u8) []const u8 { + var transform: [lang.len]u8 = undefined; + for (lang, 0..) |c, i| + transform[i] = if (c == '-') '_' else c; + const func_name = transform; + return &func_name; +} + +pub const LangFn = *const fn () callconv(.c) ?*const treez.Language; + +pub const FirstLineMatch = struct { + prefix: ?[]const u8 = null, + content: ?[]const u8 = null, +}; + +const static_file_type_list = load_file_types(@import("file_types.zig")); +const static_file_types = std.StaticStringMap(FileType).initComptime(static_file_type_list); + +fn vec(comptime args: anytype) []const []const u8 { + var cmd: []const []const u8 = &[_][]const u8{}; + inline for (args) |arg| { + cmd = cmd ++ [_][]const u8{arg}; + } + return cmd; +} + +const ListEntry = struct { []const u8, FileType }; + +fn load_file_types(comptime Namespace: type) []const ListEntry { + comptime switch (@typeInfo(Namespace)) { + .@"struct" => |info| { + var count = 0; + for (info.decls) |_| { + // @compileLog(decl.name, @TypeOf(@field(Namespace, decl.name))); + count += 1; + } + var construct_types: [count]ListEntry = undefined; + var i = 0; + for (info.decls) |decl| { + const lang = decl.name; + const args = @field(Namespace, lang); + construct_types[i] = .{ lang, .{ + .color = if (@hasField(@TypeOf(args), "color")) args.color else 0xffffff, + .icon = if (@hasField(@TypeOf(args), "icon")) args.icon else "󱀫", + .name = lang, + .description = args.description, + .lang_fn = if (@hasField(@TypeOf(args), "parser")) args.parser else get_parser(lang), + .extensions = vec(args.extensions), + .comment = args.comment, + .first_line_matches = if (@hasField(@TypeOf(args), "first_line_matches")) args.first_line_matches else null, + .formatter = if (@hasField(@TypeOf(args), "formatter")) vec(args.formatter) else null, + .language_server = if (@hasField(@TypeOf(args), "language_server")) vec(args.language_server) else null, + } }; + i += 1; + } + const types = construct_types; + return &types; + }, + else => @compileError("expected tuple or struct type"), + }; +} + +pub const FileTypeQueries = struct { + highlights_bin: []const u8, + errors_bin: []const u8, + injections_bin: ?[]const u8, +}; + +pub const queries = std.StaticStringMap(FileTypeQueries).initComptime(load_queries()); + +fn load_queries() []const struct { []const u8, FileTypeQueries } { + if (!build_options.use_tree_sitter) return &.{}; + @setEvalBranchQuota(32000); + const queries_cb = @embedFile("syntax_bin_queries"); + var iter: []const u8 = queries_cb; + var len = cbor.decodeMapHeader(&iter) catch |e| { + @compileLog("cbor.decodeMapHeader", e); + @compileError("invalid syntax_bin_queries"); + }; + var construct_types: [len]struct { []const u8, FileTypeQueries } = undefined; + var i = 0; + while (len > 0) : (len -= 1) { + var lang: []const u8 = undefined; + if (!try cbor.matchString(&iter, &lang)) + @compileError("invalid language name field"); + construct_types[i] = .{ lang, .{ + .highlights_bin = blk: { + var iter_: []const u8 = iter; + break :blk get_query_value_bin(&iter_, "highlights") orelse @compileError("missing highlights for " ++ lang); + }, + .errors_bin = blk: { + var iter_: []const u8 = iter; + break :blk get_query_value_bin(&iter_, "errors") orelse @compileError("missing errors query for " ++ lang); + }, + .injections_bin = blk: { + var iter_: []const u8 = iter; + break :blk get_query_value_bin(&iter_, "injections"); + }, + } }; + try cbor.skipValue(&iter); + i += 1; + } + const types = construct_types; + return &types; +} + +fn get_query_value_bin(iter: *[]const u8, comptime query: []const u8) ?[]const u8 { + var len = cbor.decodeMapHeader(iter) catch |e| { + @compileLog("cbor.decodeMapHeader", e); + @compileError("invalid query map in syntax_bin_queries"); + }; + while (len > 0) : (len -= 1) { + var query_name: []const u8 = undefined; + if (!try cbor.matchString(iter, &query_name)) + @compileError("invalid query name field"); + if (std.mem.eql(u8, query_name, query)) { + var query_value: []const u8 = undefined; + if (try cbor.matchValue(iter, cbor.extract(&query_value))) + return query_value; + @compileError("invalid query value field"); + } else { + try cbor.skipValue(iter); + } + } + return null; +} diff --git a/src/syntax/src/file_types.zig b/src/syntax/src/file_types.zig new file mode 100644 index 0000000..efb8ca2 --- /dev/null +++ b/src/syntax/src/file_types.zig @@ -0,0 +1,609 @@ +const file_type = @import("file_type.zig"); +const FirstLineMatch = file_type.FirstLineMatch; + +pub const agda = .{ + .description = "Agda", + .extensions = .{"agda"}, + .comment = "--", +}; + +pub const astro = .{ + .description = "Astro", + .icon = "", + .extensions = .{"astro"}, + .comment = "//", + .language_server = .{ "astro-ls", "--stdio" }, +}; + +pub const bash = .{ + .description = "Bash", + .color = 0x3e474a, + .icon = "󱆃", + .extensions = .{ "sh", "bash", ".profile" }, + .comment = "#", + .first_line_matches = FirstLineMatch{ .prefix = "#!", .content = "sh" }, + .formatter = .{ "shfmt", "--indent", "4" }, + .language_server = .{ "bash-language-server", "start" }, +}; + +pub const c = .{ + .description = "C", + .icon = "", + .extensions = .{"c"}, + .comment = "//", + .formatter = .{"clang-format"}, + .language_server = .{"clangd"}, +}; + +pub const @"c-sharp" = .{ + .description = "C#", + .color = 0x68217a, + .icon = "󰌛", + .extensions = .{"cs"}, + .comment = "//", + .language_server = .{ "omnisharp", "-lsp" }, + .formatter = .{ "csharpier", "format" }, +}; + +pub const conf = .{ + .description = "Config", + .color = 0x000000, + .icon = "", + .extensions = .{ "conf", "log", "config", ".gitconfig", "gui_config" }, + .highlights = fish.highlights, + .comment = "#", + .parser = fish.parser, +}; + +pub const cmake = .{ + .description = "CMake", + .color = 0x004078, + .icon = "", + .extensions = .{ "CMakeLists.txt", "cmake", "cmake.in" }, + .comment = "#", + .highlights = "queries/cmake/highlights.scm", + .injections = "queries/cmake/injections.scm", + .formatter = .{"cmake-format"}, + .language_server = .{"cmake-language-server"}, +}; + +pub const cpp = .{ + .description = "C++", + .color = 0x9c033a, + .icon = "", + .extensions = .{ "cc", "cpp", "cxx", "hpp", "hxx", "h", "ipp", "ixx" }, + .comment = "//", + .highlights_list = .{ + "tree-sitter-c/queries/highlights.scm", + "tree-sitter-cpp/queries/highlights.scm", + }, + .injections = "tree-sitter-cpp/queries/injections.scm", + .formatter = .{"clang-format"}, + .language_server = .{"clangd"}, +}; + +pub const css = .{ + .description = "CSS", + .color = 0x3d8fc6, + .icon = "󰌜", + .extensions = .{"css"}, + .comment = "//", + .language_server = .{ "vscode-css-language-server", "--stdio" }, +}; + +pub const diff = .{ + .description = "Diff", + .extensions = .{ "diff", "patch", "rej" }, + .comment = "#", +}; + +pub const dockerfile = .{ + .description = "Docker", + .color = 0x019bc6, + .icon = "", + .extensions = .{ "Dockerfile", "dockerfile", "docker", "Containerfile", "container" }, + .comment = "#", +}; + +pub const dtd = .{ + .description = "DTD", + .icon = "󰗀", + .extensions = .{"dtd"}, + .comment = "