Compare commits

...

3 commits

6 changed files with 101 additions and 33 deletions

View file

@ -461,6 +461,7 @@ pub fn build_exe(
.{ .name = "syntax", .module = syntax_mod },
.{ .name = "dizzy", .module = dizzy_dep.module("dizzy") },
.{ .name = "fuzzig", .module = fuzzig_dep.module("fuzzig") },
.{ .name = "git", .module = git_mod },
},
});

View file

@ -7,6 +7,7 @@ const dizzy = @import("dizzy");
const Buffer = @import("Buffer");
const fuzzig = @import("fuzzig");
const tracy = @import("tracy");
const git = @import("git");
const builtin = @import("builtin");
const LSP = @import("LSP.zig");
@ -21,6 +22,11 @@ language_servers: std.StringHashMap(LSP),
file_language_server: std.StringHashMap(LSP),
tasks: std.ArrayList(Task),
persistent: bool = false,
logger_lsp: log.Logger,
logger_git: log.Logger,
workspace: ?[]const u8 = null,
branch: ?[]const u8 = null,
const Self = @This();
@ -59,10 +65,14 @@ pub fn init(allocator: std.mem.Allocator, name: []const u8) OutOfMemoryError!Sel
.language_servers = std.StringHashMap(LSP).init(allocator),
.file_language_server = std.StringHashMap(LSP).init(allocator),
.tasks = std.ArrayList(Task).init(allocator),
.logger_lsp = log.logger("lsp"),
.logger_git = log.logger("git"),
};
}
pub fn deinit(self: *Self) void {
if (self.workspace) |p| self.allocator.free(p);
if (self.branch) |p| self.allocator.free(p);
var i_ = self.file_language_server.iterator();
while (i_.next()) |p| {
self.allocator.free(p.key_ptr.*);
@ -76,6 +86,8 @@ pub fn deinit(self: *Self) void {
self.files.deinit();
for (self.tasks.items) |task| self.allocator.free(task.command);
self.tasks.deinit();
self.logger_lsp.deinit();
self.logger_git.deinit();
self.allocator.free(self.name);
}
@ -631,6 +643,7 @@ fn send_goto_request(self: *Self, from: tp.pid_ref, file_path: []const u8, row:
const handler: struct {
from: tp.pid,
name: []const u8,
project: *Self,
pub fn deinit(self_: *@This()) void {
std.heap.c_allocator.free(self_.name);
@ -644,7 +657,7 @@ fn send_goto_request(self: *Self, from: tp.pid_ref, file_path: []const u8, row:
if (try cbor.match(response.buf, .{ tp.any, tp.any, tp.any, .{tp.extract_cbor(&link)} })) {
try navigate_to_location_link(self_.from.ref(), link);
} else if (try cbor.match(response.buf, .{ tp.any, tp.any, tp.any, tp.extract_cbor(&locations) })) {
try send_reference_list(self_.from.ref(), locations, self_.name);
try self_.project.send_reference_list(self_.from.ref(), locations, self_.name);
}
} else if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.null_ })) {
return;
@ -655,6 +668,7 @@ fn send_goto_request(self: *Self, from: tp.pid_ref, file_path: []const u8, row:
} = .{
.from = from.clone(),
.name = try std.heap.c_allocator.dupe(u8, self.name),
.project = self,
};
lsp.send_request(self.allocator, method, .{
@ -725,11 +739,12 @@ pub fn references(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usi
const lsp = try self.get_language_server(file_path);
const uri = try self.make_URI(file_path);
defer self.allocator.free(uri);
log.logger("lsp").print("finding references...", .{});
self.logger_lsp.print("finding references...", .{});
const handler: struct {
from: tp.pid,
name: []const u8,
project: *Self,
pub fn deinit(self_: *@This()) void {
std.heap.c_allocator.free(self_.name);
@ -741,12 +756,13 @@ pub fn references(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usi
if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.null_ })) {
return;
} else if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.extract_cbor(&locations) })) {
try send_reference_list(self_.from.ref(), locations, self_.name);
try self_.project.send_reference_list(self_.from.ref(), locations, self_.name);
}
}
} = .{
.from = from.clone(),
.name = try std.heap.c_allocator.dupe(u8, self.name),
.project = self,
};
lsp.send_request(self.allocator, "textDocument/references", .{
@ -756,7 +772,7 @@ pub fn references(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usi
}, handler) catch return error.LspFailed;
}
fn send_reference_list(to: tp.pid_ref, locations: []const u8, name: []const u8) (ClientError || InvalidMessageError || GetLineOfFileError || cbor.Error)!void {
fn send_reference_list(self: *Self, to: tp.pid_ref, locations: []const u8, name: []const u8) (ClientError || InvalidMessageError || GetLineOfFileError || cbor.Error)!void {
defer to.send(.{ "REF", "done" }) catch {};
var iter = locations;
var len = try cbor.decodeArrayHeader(&iter);
@ -767,7 +783,7 @@ fn send_reference_list(to: tp.pid_ref, locations: []const u8, name: []const u8)
try send_reference(to, location, name);
} else return error.InvalidMessageField;
}
log.logger("lsp").print("found {d} references", .{count});
self.logger_lsp.print("found {d} references", .{count});
}
fn send_reference(to: tp.pid_ref, location: []const u8, name: []const u8) (ClientError || InvalidMessageError || GetLineOfFileError || cbor.Error)!void {
@ -1162,7 +1178,7 @@ pub fn hover(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usize, c
const lsp = try self.get_language_server(file_path);
const uri = try self.make_URI(file_path);
defer self.allocator.free(uri);
// log.logger("lsp").print("fetching hover information...", .{});
// self.logger_lsp.print("fetching hover information...", .{});
const handler: struct {
from: tp.pid,
@ -1412,7 +1428,7 @@ fn read_position(position: []const u8) !Position {
return .{ .line = line.?, .character = character.? };
}
pub fn show_message(_: *Self, _: tp.pid_ref, params_cb: []const u8) !void {
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;
@ -1429,12 +1445,10 @@ pub fn show_message(_: *Self, _: tp.pid_ref, params_cb: []const u8) !void {
}
}
const msg = message orelse return;
const logger = log.logger("lsp");
defer logger.deinit();
if (type_ <= 2)
logger.err_msg("lsp", msg)
self.logger_lsp.err_msg("lsp", msg)
else
logger.print("{s}", .{msg});
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 {
@ -1462,6 +1476,7 @@ fn send_lsp_init_request(self: *Self, lsp: LSP, project_path: []const u8, projec
const handler: struct {
language_server: []const u8,
lsp: LSP,
project: *Self,
pub fn deinit(self_: *@This()) void {
self_.lsp.pid.deinit();
@ -1471,7 +1486,7 @@ fn send_lsp_init_request(self: *Self, lsp: LSP, project_path: []const u8, projec
pub fn receive(self_: @This(), _: tp.message) !void {
self_.lsp.send_notification("initialized", .{}) catch return error.LspFailed;
if (self_.lsp.pid.expired()) return error.LspFailed;
log.logger("lsp").print("initialized LSP: {s}", .{fmt_lsp_name_func(self_.language_server)});
self_.project.logger_lsp.print("initialized LSP: {s}", .{fmt_lsp_name_func(self_.language_server)});
}
} = .{
.language_server = try std.heap.c_allocator.dupe(u8, language_server),
@ -1479,6 +1494,7 @@ fn send_lsp_init_request(self: *Self, lsp: LSP, project_path: []const u8, projec
.allocator = lsp.allocator,
.pid = lsp.pid.clone(),
},
.project = self,
};
try lsp.send_request(self.allocator, "initialize", .{
@ -1840,3 +1856,26 @@ pub fn get_line(allocator: std.mem.Allocator, buf: []const u8) ![]const u8 {
}
return allocator.dupe(u8, buf);
}
pub fn query_git(self: *Self) void {
git.workspace_path(@intFromPtr(self)) catch {};
git.current_branch(@intFromPtr(self)) catch {};
}
pub fn process_git(self: *Self, m: tp.message) !void {
var value: []const u8 = undefined;
if (try m.match(.{ tp.any, tp.any, "workspace_path", tp.null_ })) {
// no git workspace
} else if (try m.match(.{ tp.any, tp.any, "workspace_path", tp.extract(&value) })) {
if (self.workspace) |p| self.allocator.free(p);
self.workspace = try self.allocator.dupe(u8, value);
git.workspace_files(@intFromPtr(self)) catch {};
} else if (try m.match(.{ tp.any, tp.any, "current_branch", tp.extract(&value) })) {
if (self.branch) |p| self.allocator.free(p);
self.branch = try self.allocator.dupe(u8, value);
} else if (try m.match(.{ tp.any, tp.any, "workspace_files", tp.extract(&value) })) {
// TODO
} else {
self.logger_git.err("git", tp.unexpected(m));
}
}

View file

@ -7,37 +7,55 @@ pub const Error = error{ OutOfMemory, GitNotFound, GitCallFailed };
const log_execute = false;
pub fn workspace_path() Error!void {
pub fn workspace_path(context_: usize) Error!void {
const fn_name = @src().fn_name;
try git(.{ "rev-parse", "--show-toplevel" }, struct {
fn result(parent: tp.pid_ref, output: []const u8) void {
try git(context_, .{ "rev-parse", "--show-toplevel" }, struct {
fn result(context: usize, parent: tp.pid_ref, output: []const u8) void {
var it = std.mem.splitScalar(u8, output, '\n');
while (it.next()) |branch| if (branch.len > 0)
parent.send(.{ module_name, fn_name, branch }) catch {};
while (it.next()) |value| if (value.len > 0)
parent.send(.{ module_name, context, fn_name, value }) catch {};
}
}.result, exit_null_on_error(fn_name));
}
pub fn current_branch() Error!void {
pub fn current_branch(context_: usize) Error!void {
const fn_name = @src().fn_name;
try git(.{ "rev-parse", "--abbrev-ref", "HEAD" }, struct {
fn result(parent: tp.pid_ref, output: []const u8) void {
try git(context_, .{ "rev-parse", "--abbrev-ref", "HEAD" }, struct {
fn result(context: usize, parent: tp.pid_ref, output: []const u8) void {
var it = std.mem.splitScalar(u8, output, '\n');
while (it.next()) |branch| if (branch.len > 0)
parent.send(.{ module_name, fn_name, branch }) catch {};
while (it.next()) |value| if (value.len > 0)
parent.send(.{ module_name, context, fn_name, value }) catch {};
}
}.result, exit_null_on_error(fn_name));
}
pub fn workspace_files(context_: usize) Error!void {
const fn_name = @src().fn_name;
try git_err(context_, .{ "ls-files", "--cached", "--others", "--exclude-standard" }, struct {
fn result(context: usize, parent: tp.pid_ref, output: []const u8) void {
var it = std.mem.splitScalar(u8, output, '\n');
while (it.next()) |value| if (value.len > 0)
parent.send(.{ module_name, context, fn_name, value }) catch {};
}
}.result, struct {
fn result(_: usize, _: tp.pid_ref, output: []const u8) void {
var it = std.mem.splitScalar(u8, output, '\n');
while (it.next()) |line| std.log.err("{s}: {s}", .{ module_name, line });
}
}.result, exit_null_on_error(fn_name));
}
fn git(
context: usize,
cmd: anytype,
out: OutputHandler,
exit: ExitHandler,
) Error!void {
return git_err(cmd, out, noop, exit);
return git_err(context, cmd, out, noop, exit);
}
fn git_err(
context: usize,
cmd: anytype,
out: OutputHandler,
err: OutputHandler,
@ -54,6 +72,7 @@ fn git_err(
inline for (info.fields) |f|
try cbor.writeValue(writer, @field(cmd, f.name));
return shell.execute(allocator, .{ .buf = buf.items }, .{
.context = context,
.out = to_shell_output_handler(out),
.err = to_shell_output_handler(err),
.exit = exit,
@ -74,18 +93,18 @@ fn exit_null_on_error(comptime tag: []const u8) shell.ExitHandler {
}.exit;
}
const OutputHandler = fn (parent: tp.pid_ref, output: []const u8) void;
const OutputHandler = fn (context: usize, parent: tp.pid_ref, output: []const u8) void;
const ExitHandler = shell.ExitHandler;
fn to_shell_output_handler(handler: anytype) shell.OutputHandler {
return struct {
fn out(_: usize, parent: tp.pid_ref, _: []const u8, output: []const u8) void {
handler(parent, output);
fn out(context: usize, parent: tp.pid_ref, _: []const u8, output: []const u8) void {
handler(context, parent, output);
}
}.out;
}
fn noop(_: tp.pid_ref, _: []const u8) void {}
fn noop(_: usize, _: tp.pid_ref, _: []const u8) void {}
var git_path: ?struct {
path: ?[:0]const u8 = null,

View file

@ -325,6 +325,7 @@ const Process = struct {
var text_len: usize = 0;
var n: usize = 0;
var task: []const u8 = undefined;
var context: usize = undefined;
var root_dst: usize = 0;
var root_src: usize = 0;
@ -341,6 +342,9 @@ const Process = struct {
if (self.walker) |pid| pid.deinit();
self.walker = null;
self.loaded(project_directory) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed;
} else if (try cbor.match(m.buf, .{ "git", tp.extract(&context), tp.more })) {
const project: *Project = @ptrFromInt(context);
project.process_git(m) catch {};
} else if (try cbor.match(m.buf, .{ "update_mru", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) {
self.update_mru(project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed;
} else if (try cbor.match(m.buf, .{ "child", tp.extract(&project_directory), tp.extract(&language_server), "notify", tp.extract(&method), tp.extract_cbor(&params_cb) })) {
@ -423,6 +427,7 @@ const Process = struct {
self.walker = try walk_tree_async(self.allocator, project_directory);
self.restore_project(project) catch |e| self.logger.err("restore_project", e);
project.sort_files_by_mtime();
project.query_git();
} else {
self.logger.print("switched to: {s}", .{project_directory});
}

View file

@ -96,6 +96,7 @@ pub fn log_handler(context: usize, parent: tp.pid_ref, arg0: []const u8, output:
_ = parent;
_ = arg0;
const logger = log.logger(@typeName(Self));
defer logger.deinit();
var it = std.mem.splitScalar(u8, output, '\n');
while (it.next()) |line| if (line.len > 0) logger.print("{s}", .{line});
}
@ -104,6 +105,7 @@ pub fn log_err_handler(context: usize, parent: tp.pid_ref, arg0: []const u8, out
_ = context;
_ = parent;
const logger = log.logger(@typeName(Self));
defer logger.deinit();
var it = std.mem.splitScalar(u8, output, '\n');
while (it.next()) |line| logger.print_err(arg0, "{s}", .{line});
}
@ -112,6 +114,7 @@ pub fn log_exit_handler(context: usize, parent: tp.pid_ref, arg0: []const u8, er
_ = context;
_ = parent;
const logger = log.logger(@typeName(Self));
defer logger.deinit();
if (exit_code > 0) {
logger.print_err(arg0, "'{s}' terminated {s} exitcode: {d}", .{ arg0, err_msg, exit_code });
} else {
@ -123,6 +126,7 @@ pub fn log_exit_err_handler(context: usize, parent: tp.pid_ref, arg0: []const u8
_ = context;
_ = parent;
const logger = log.logger(@typeName(Self));
defer logger.deinit();
if (exit_code > 0) {
logger.print_err(arg0, "'{s}' terminated {s} exitcode: {d}", .{ arg0, err_msg, exit_code });
}

View file

@ -29,7 +29,7 @@ pub fn create(
.plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent),
};
try tui.message_filters().add(MessageFilter.bind(self, receive_git));
git.workspace_path() catch {};
git.workspace_path(0) catch {};
return Widget.to(self);
}
@ -51,11 +51,11 @@ fn process_git(
m: tp.message,
) MessageFilter.Error!bool {
var branch: []const u8 = undefined;
if (try match(m.buf, .{ any, "workspace_path", null_ })) {
self.branch = try self.allocator.dupe(u8, "null");
} else if (try match(m.buf, .{ any, "workspace_path", string })) {
git.current_branch() catch {};
} else if (try match(m.buf, .{ any, "current_branch", extract(&branch) })) {
if (try match(m.buf, .{ any, any, "workspace_path", null_ })) {
// do nothing, we do not have a git workspace
} else if (try match(m.buf, .{ any, any, "workspace_path", string })) {
git.current_branch(0) catch {};
} else if (try match(m.buf, .{ any, any, "current_branch", extract(&branch) })) {
if (self.branch) |p| self.allocator.free(p);
self.branch = try self.allocator.dupe(u8, branch);
} else {