feat: WIP start work on goto_definition LSP command

This commit is contained in:
CJ van den Berg 2024-03-31 22:38:25 +02:00
parent eb94bf5258
commit 2e8fd58ba5
5 changed files with 121 additions and 82 deletions

View file

@ -52,7 +52,7 @@ const Process = struct {
.recv_buf = std.ArrayList(u8).init(a), .recv_buf = std.ArrayList(u8).init(a),
.parent = tp.self_pid().clone(), .parent = tp.self_pid().clone(),
.tag = try a.dupeZ(u8, tag), .tag = try a.dupeZ(u8, tag),
.logger = log.logger(@typeName(Self)), .logger = log.logger(module_name),
}; };
return tp.spawn_link(self.a, self, Process.start, tag) catch |e| tp.exit_error(e); return tp.spawn_link(self.a, self, Process.start, tag) catch |e| tp.exit_error(e);
} }
@ -72,7 +72,7 @@ const Process = struct {
fn start(self: *Process) tp.result { fn start(self: *Process) tp.result {
_ = tp.set_trap(true); _ = tp.set_trap(true);
self.sp = tp.subprocess.init(self.a, self.cmd, sp_tag, self.stdin_behavior) catch |e| return tp.exit_error(e); self.sp = tp.subprocess.init(self.a, self.cmd, sp_tag, .Pipe) catch |e| return tp.exit_error(e);
tp.receive(&self.receiver); tp.receive(&self.receiver);
} }
@ -101,30 +101,20 @@ const Process = struct {
fn handle_output(self: *Process, bytes: []u8) !void { fn handle_output(self: *Process, bytes: []u8) !void {
try self.recv_buf.appendSlice(bytes); try self.recv_buf.appendSlice(bytes);
@import("log").logger(module_name).print("{s}", .{bytes}) catch {}; self.logger.print("{s}", .{bytes});
const message = try self.frame_message() orelse return; const message = try self.frame_message() orelse return;
_ = message; _ = message;
} }
fn handle_terminated(self: *Process) !void { fn handle_terminated(self: *Process) !void {
const recv_buf = try self.recv_buf.toOwnedSlice(); self.logger.print("done", .{});
var it = std.mem.splitScalar(u8, recv_buf, '\n');
while (it.next()) |json| {
if (json.len == 0) continue;
var msg_buf: [tp.max_message_size]u8 = undefined;
const msg: tp.message = .{ .buf = try cbor.fromJson(json, &msg_buf) };
try self.dispatch(msg);
// var buf: [tp.max_message_size]u8 = undefined;
// @import("log").logger(module_name).print("json: {s}", .{try msg.to_json(&buf)}) catch {};
}
@import("log").logger(module_name).print("done", .{}) catch {};
try self.parent.send(.{ self.tag, "done" }); try self.parent.send(.{ self.tag, "done" });
} }
fn frame_message(self: *Self) !?Message { fn frame_message(self: *Process) !?Message {
const end = std.mem.indexOf(u8, self.recv_buf, "\r\n\r\n") orelse return null; const end = std.mem.indexOf(u8, self.recv_buf.items, "\r\n\r\n") orelse return null;
const headers = try Headers.parse(self.recv_buf[0..end]); const headers = try Headers.parse(self.recv_buf.items[0..end]);
const body = self.recv_buf[end + 2 ..]; const body = self.recv_buf.items[end + 2 ..];
if (body.len < headers.content_length) return null; if (body.len < headers.content_length) return null;
return .{ .body = body }; return .{ .body = body };
} }
@ -153,14 +143,14 @@ const Headers = struct {
else else
sep + 1; sep + 1;
const value = buf[vstart..end]; const value = buf[vstart..end];
ret.parse_one(name, value); try ret.parse_one(name, value);
buf = if (end < buf.len - 2) buf[end + 2 ..] else return ret; buf = if (end < buf.len - 2) buf[end + 2 ..] else return ret;
} }
} }
fn parse_one(self: *Headers, name: []const u8, value: []const u8) void { fn parse_one(self: *Headers, name: []const u8, value: []const u8) !void {
if (std.mem.eql(u8, "Content-Length", name)) { if (std.mem.eql(u8, "Content-Length", name)) {
self.content_length = std.fmt.parseInt(@TypeOf(self.content_length), value, 10); self.content_length = try std.fmt.parseInt(@TypeOf(self.content_length), value, 10);
} else if (std.mem.eql(u8, "Content-Type", name)) { } else if (std.mem.eql(u8, "Content-Type", name)) {
self.content_type = value; self.content_type = value;
} }

84
src/Project.zig Normal file
View file

@ -0,0 +1,84 @@
const std = @import("std");
const tp = @import("thespian");
const Lsp = @import("Lsp.zig");
a: std.mem.Allocator,
name: []const u8,
files: std.ArrayList(File),
open_time: i64,
lsp: ?Lsp = null,
const Self = @This();
const File = struct {
path: []const u8,
mtime: i128,
};
pub fn init(a: std.mem.Allocator, name: []const u8) error{OutOfMemory}!Self {
return .{
.a = a,
.name = try a.dupe(u8, name),
.files = std.ArrayList(File).init(a),
.open_time = std.time.milliTimestamp(),
};
}
pub fn deinit(self: *Self) void {
for (self.files.items) |file| self.a.free(file.path);
self.files.deinit();
if (self.lsp) |*lsp| lsp.deinit();
self.a.free(self.name);
}
fn get_lsp(self: *Self) !Lsp {
if (self.lsp) |lsp| return lsp;
self.lsp = try Lsp.open(self.a, tp.message.fmt(.{"zls"}), "LSP");
return self.lsp.?;
}
pub fn add_file(self: *Self, path: []const u8, mtime: i128) error{OutOfMemory}!void {
(try self.files.addOne()).* = .{ .path = try self.a.dupe(u8, path), .mtime = mtime };
}
pub fn sort_files_by_mtime(self: *Self) void {
const less_fn = struct {
fn less_fn(_: void, lhs: File, rhs: File) bool {
return lhs.mtime > rhs.mtime;
}
}.less_fn;
std.mem.sort(File, self.files.items, {}, less_fn);
}
pub fn request_recent_files(self: *Self, from: tp.pid_ref, max: usize) error{ OutOfMemory, Exit }!void {
defer from.send(.{ "PRJ", "recent_done", "" }) catch {};
for (self.files.items, 0..) |file, i| {
try from.send(.{ "PRJ", "recent", file.path });
if (i >= max) return;
}
}
pub fn query_recent_files(self: *Self, from: tp.pid_ref, max: usize, query: []const u8) error{ OutOfMemory, Exit }!usize {
var i: usize = 0;
defer from.send(.{ "PRJ", "recent_done", query }) catch {};
for (self.files.items) |file| {
if (file.path.len < query.len) continue;
if (std.mem.indexOf(u8, file.path, query)) |_| {
try from.send(.{ "PRJ", "recent", file.path });
i += 1;
if (i >= max) return i;
}
}
return i;
}
pub fn goto_definition(self: *Self, from: tp.pid_ref, file_path: []const u8, file_type: []const u8, row: usize, col: usize) tp.result {
const lsp = self.get_lsp() catch |e| return tp.exit_error(e);
_ = from;
_ = file_path;
_ = file_type;
_ = row;
_ = col;
_ = lsp;
}

View file

@ -4,6 +4,8 @@ const cbor = @import("cbor");
const log = @import("log"); const log = @import("log");
const tracy = @import("tracy"); const tracy = @import("tracy");
const Project = @import("Project.zig");
pid: tp.pid_ref, pid: tp.pid_ref,
const Self = @This(); const Self = @This();
@ -55,6 +57,13 @@ pub fn query_recent_files(max: usize, query: []const u8) tp.result {
return (try get()).pid.send(.{ "query_recent_files", project, max, query }); return (try get()).pid.send(.{ "query_recent_files", project, max, query });
} }
pub fn goto_definition(file_path: []const u8, file_type: []const u8, row: usize, col: usize) tp.result {
const project = tp.env.get().str("project");
if (project.len == 0)
return tp.exit("No project");
return (try get()).pid.send(.{ "goto_definition", project, file_path, file_type, row, col });
}
const Process = struct { const Process = struct {
a: std.mem.Allocator, a: std.mem.Allocator,
parent: tp.pid, parent: tp.pid,
@ -101,9 +110,12 @@ const Process = struct {
var project_directory: []const u8 = undefined; var project_directory: []const u8 = undefined;
var path: []const u8 = undefined; var path: []const u8 = undefined;
var query: []const u8 = undefined; var query: []const u8 = undefined;
var file_type: []const u8 = undefined;
var high: i64 = 0; var high: i64 = 0;
var low: i64 = 0; var low: i64 = 0;
var max: usize = 0; var max: usize = 0;
var row: usize = 0;
var col: usize = 0;
if (try m.match(.{ "walk_tree_entry", tp.extract(&project_directory), tp.extract(&path), tp.extract(&high), tp.extract(&low) })) { if (try m.match(.{ "walk_tree_entry", tp.extract(&project_directory), tp.extract(&path), tp.extract(&high), tp.extract(&low) })) {
const mtime = (@as(i128, @intCast(high)) << 64) | @as(i128, @intCast(low)); const mtime = (@as(i128, @intCast(high)) << 64) | @as(i128, @intCast(low));
@ -126,6 +138,8 @@ const Process = struct {
self.request_recent_files(from, project_directory, max) catch |e| return from.send_raw(tp.exit_message(e)); self.request_recent_files(from, project_directory, max) catch |e| return from.send_raw(tp.exit_message(e));
} else if (try m.match(.{ "query_recent_files", tp.extract(&project_directory), tp.extract(&max), tp.extract(&query) })) { } else if (try m.match(.{ "query_recent_files", tp.extract(&project_directory), tp.extract(&max), tp.extract(&query) })) {
self.query_recent_files(from, project_directory, max, query) catch |e| return from.send_raw(tp.exit_message(e)); self.query_recent_files(from, project_directory, max, query) catch |e| return from.send_raw(tp.exit_message(e));
} else if (try m.match(.{ "goto_definition", tp.extract(&project_directory), tp.extract(&path), tp.extract(&file_type), tp.extract(&row), tp.extract(&col) })) {
self.goto_definition(from, project_directory, path, file_type, row, col) catch |e| return from.send_raw(tp.exit_message(e));
} else if (try m.match(.{"shutdown"})) { } else if (try m.match(.{"shutdown"})) {
if (self.walker) |pid| pid.send(.{"stop"}) catch {}; if (self.walker) |pid| pid.send(.{"stop"}) catch {};
try from.send(.{ "project_manager", "shutdown" }); try from.send(.{ "project_manager", "shutdown" });
@ -161,67 +175,10 @@ const Process = struct {
_ = matched; _ = matched;
// self.logger.print("queried: {s} for {s} match {d} in {d} ms", .{ project_directory, query, matched, std.time.milliTimestamp() - start_time }); // self.logger.print("queried: {s} for {s} match {d} in {d} ms", .{ project_directory, query, matched, std.time.milliTimestamp() - start_time });
} }
};
const Project = struct { fn goto_definition(self: *Process, from: tp.pid_ref, project_directory: []const u8, file_path: []const u8, file_type: []const u8, row: usize, col: usize) tp.result {
a: std.mem.Allocator, const project = if (self.projects.get(project_directory)) |p| p else return tp.exit("No project");
name: []const u8, return project.goto_definition(from, file_path, file_type, row, col);
files: std.ArrayList(File),
open_time: i64,
const File = struct {
path: []const u8,
mtime: i128,
};
fn init(a: std.mem.Allocator, name: []const u8) error{OutOfMemory}!Project {
return .{
.a = a,
.name = try a.dupe(u8, name),
.files = std.ArrayList(File).init(a),
.open_time = std.time.milliTimestamp(),
};
}
fn deinit(self: *Project) void {
for (self.files.items) |file| self.a.free(file.path);
self.files.deinit();
self.a.free(self.name);
}
fn add_file(self: *Project, path: []const u8, mtime: i128) error{OutOfMemory}!void {
(try self.files.addOne()).* = .{ .path = try self.a.dupe(u8, path), .mtime = mtime };
}
fn sort_files_by_mtime(self: *Project) void {
const less_fn = struct {
fn less_fn(_: void, lhs: File, rhs: File) bool {
return lhs.mtime > rhs.mtime;
}
}.less_fn;
std.mem.sort(File, self.files.items, {}, less_fn);
}
fn request_recent_files(self: *Project, from: tp.pid_ref, max: usize) error{ OutOfMemory, Exit }!void {
defer from.send(.{ "PRJ", "recent_done", "" }) catch {};
for (self.files.items, 0..) |file, i| {
try from.send(.{ "PRJ", "recent", file.path });
if (i >= max) return;
}
}
fn query_recent_files(self: *Project, from: tp.pid_ref, max: usize, query: []const u8) error{ OutOfMemory, Exit }!usize {
var i: usize = 0;
defer from.send(.{ "PRJ", "recent_done", query }) catch {};
for (self.files.items) |file| {
if (file.path.len < query.len) continue;
if (std.mem.indexOf(u8, file.path, query)) |_| {
try from.send(.{ "PRJ", "recent", file.path });
i += 1;
if (i >= max) return i;
}
}
return i;
} }
}; };

View file

@ -9,6 +9,7 @@ const ripgrep = @import("ripgrep");
const tracy = @import("tracy"); const tracy = @import("tracy");
const text_manip = @import("text_manip"); const text_manip = @import("text_manip");
const syntax = @import("syntax"); const syntax = @import("syntax");
const project_manager = @import("project_manager");
const scrollbar_v = @import("scrollbar_v.zig"); const scrollbar_v = @import("scrollbar_v.zig");
const editor_gutter = @import("editor_gutter.zig"); const editor_gutter = @import("editor_gutter.zig");
@ -3146,6 +3147,13 @@ pub const Editor = struct {
try self.send_editor_jump_destination(); try self.send_editor_jump_destination();
} }
pub fn goto_definition(self: *Self, _: command.Context) tp.result {
const file_path = self.file_path orelse return;
const primary = self.get_primary();
const file_type = (self.syntax orelse return).file_type.name;
return project_manager.goto_definition(file_path, file_type, primary.cursor.row, primary.cursor.col);
}
pub fn select(self: *Self, ctx: command.Context) tp.result { pub fn select(self: *Self, ctx: command.Context) tp.result {
var sel: Selection = .{}; var sel: Selection = .{};
if (!try ctx.args.match(.{ tp.extract(&sel.begin.row), tp.extract(&sel.begin.col), tp.extract(&sel.end.row), tp.extract(&sel.end.col) })) if (!try ctx.args.match(.{ tp.extract(&sel.begin.row), tp.extract(&sel.begin.col), tp.extract(&sel.end.row), tp.extract(&sel.end.col) }))

View file

@ -186,7 +186,7 @@ fn mapPress(self: *Self, keypress: u32, egc: u32, modifiers: u32) tp.result {
key.F09 => self.cmd("theme_prev", .{}), key.F09 => self.cmd("theme_prev", .{}),
key.F10 => self.cmd("theme_next", .{}), key.F10 => self.cmd("theme_next", .{}),
key.F11 => self.cmd("toggle_logview", .{}), key.F11 => self.cmd("toggle_logview", .{}),
key.F12 => self.cmd("toggle_inputview", .{}), key.F12 => self.cmd("goto_definition", .{}),
key.F34 => self.cmd("toggle_whitespace", .{}), // C-F10 key.F34 => self.cmd("toggle_whitespace", .{}), // C-F10
key.ESC => self.cmd("cancel", .{}), key.ESC => self.cmd("cancel", .{}),
key.ENTER => self.cmd("smart_insert_line", .{}), key.ENTER => self.cmd("smart_insert_line", .{}),