feat: first (mostly) working version of goto_definition command

Tested only with zls and clangd so far.
This commit is contained in:
CJ van den Berg 2024-04-05 22:52:47 +02:00
parent 7e17f713e7
commit fc15c8bda6
6 changed files with 186 additions and 56 deletions

View file

@ -144,6 +144,7 @@ pub fn build(b: *std.Build) void {
.{ .name = "cbor", .module = cbor_mod }, .{ .name = "cbor", .module = cbor_mod },
.{ .name = "thespian", .module = thespian_mod }, .{ .name = "thespian", .module = thespian_mod },
.{ .name = "tracy", .module = tracy_mod }, .{ .name = "tracy", .module = tracy_mod },
.{ .name = "syntax", .module = syntax_dep.module("syntax") },
}, },
}); });

View file

@ -241,6 +241,11 @@ cycle style of editing.
- Ctrl-F10 => - Ctrl-F10 =>
Toggle visible whitespace mode Toggle visible whitespace mode
### Language Server Commands
- F12 =>
Goto definition
### Debugging Commands ### Debugging Commands
- F5, Ctrl-Shift-i => - F5, Ctrl-Shift-i =>
@ -255,9 +260,6 @@ cycle style of editing.
- F11, Ctrl-J, Alt-l => - F11, Ctrl-J, Alt-l =>
Toggle log view Toggle log view
- F12, Alt-i =>
Toggle input view
- Ctrl-Shift-/ => - Ctrl-Shift-/ =>
Dump current widget tree to log view Dump current widget tree to log view

View file

@ -5,7 +5,7 @@ const root = @import("root");
const tracy = @import("tracy"); const tracy = @import("tracy");
a: std.mem.Allocator, a: std.mem.Allocator,
pid: ?tp.pid, pid: tp.pid,
const Self = @This(); const Self = @This();
const module_name = @typeName(Self); const module_name = @typeName(Self);
@ -13,34 +13,29 @@ const sp_tag = "child";
const debug_lsp = true; const debug_lsp = true;
pub const Error = error{ OutOfMemory, Exit }; pub const Error = error{ OutOfMemory, Exit };
pub fn open(a: std.mem.Allocator, cmd: tp.message, tag: [:0]const u8) Error!Self { pub fn open(a: std.mem.Allocator, cmd: tp.message) Error!Self {
return .{ .a = a, .pid = try Process.create(a, cmd, tag) }; return .{ .a = a, .pid = try Process.create(a, cmd) };
} }
pub fn deinit(self: *Self) void { pub fn deinit(self: *Self) void {
if (self.pid) |pid| { self.pid.send(.{"close"}) catch {};
pid.send(.{"close"}) catch {}; self.pid.deinit();
self.pid = null;
pid.deinit();
}
} }
pub fn send_request(self: Self, a: std.mem.Allocator, method: []const u8, m: anytype) error{Exit}!tp.message { pub fn send_request(self: Self, a: std.mem.Allocator, method: []const u8, m: anytype) error{Exit}!tp.message {
// const frame = tracy.initZone(@src(), .{ .name = module_name ++ ".send_request" }); // const frame = tracy.initZone(@src(), .{ .name = module_name ++ ".send_request" });
// defer frame.deinit(); // defer frame.deinit();
const pid = if (self.pid) |pid| pid else return tp.exit_error(error.Closed);
var cb = std.ArrayList(u8).init(self.a); var cb = std.ArrayList(u8).init(self.a);
defer cb.deinit(); defer cb.deinit();
cbor.writeValue(cb.writer(), m) catch |e| return tp.exit_error(e); cbor.writeValue(cb.writer(), m) catch |e| return tp.exit_error(e);
return pid.call(a, .{ "REQ", method, cb.items }) catch |e| return tp.exit_error(e); return self.pid.call(a, .{ "REQ", method, cb.items }) catch |e| return tp.exit_error(e);
} }
pub fn send_notification(self: Self, method: []const u8, m: anytype) tp.result { pub fn send_notification(self: Self, method: []const u8, m: anytype) tp.result {
const pid = if (self.pid) |pid| pid else return tp.exit_error(error.Closed);
var cb = std.ArrayList(u8).init(self.a); var cb = std.ArrayList(u8).init(self.a);
defer cb.deinit(); defer cb.deinit();
cbor.writeValue(cb.writer(), m) catch |e| return tp.exit_error(e); cbor.writeValue(cb.writer(), m) catch |e| return tp.exit_error(e);
return pid.send(.{ "NTFY", method, cb.items }); return self.pid.send(.{ "NTFY", method, cb.items });
} }
pub fn close(self: *Self) void { pub fn close(self: *Self) void {
@ -62,7 +57,15 @@ const Process = struct {
const Receiver = tp.Receiver(*Process); const Receiver = tp.Receiver(*Process);
pub fn create(a: std.mem.Allocator, cmd: tp.message, tag: [:0]const u8) Error!tp.pid { pub fn create(a: std.mem.Allocator, cmd: tp.message) Error!tp.pid {
var tag: []const u8 = undefined;
if (try cmd.match(.{tp.extract(&tag)})) {
//
} else if (try cmd.match(.{ tp.extract(&tag), tp.more })) {
//
} else {
return tp.exit("no LSP command");
}
const self = try a.create(Process); const self = try a.create(Process);
var sp_tag_ = std.ArrayList(u8).init(a); var sp_tag_ = std.ArrayList(u8).init(a);
defer sp_tag_.deinit(); defer sp_tag_.deinit();
@ -78,7 +81,7 @@ const Process = struct {
.requests = std.AutoHashMap(i32, tp.pid).init(a), .requests = std.AutoHashMap(i32, tp.pid).init(a),
.sp_tag = try sp_tag_.toOwnedSliceSentinel(0), .sp_tag = try sp_tag_.toOwnedSliceSentinel(0),
}; };
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, self.tag) catch |e| tp.exit_error(e);
} }
fn deinit(self: *Process) void { fn deinit(self: *Process) void {
@ -225,13 +228,16 @@ const Process = struct {
try cbor.writeValue(msg_writer, "params"); try cbor.writeValue(msg_writer, "params");
_ = try msg_writer.write(params_cb); _ = try msg_writer.write(params_cb);
const json = try cbor.toJsonPrettyAlloc(self.a, msg.items); const json = try cbor.toJsonAlloc(self.a, msg.items);
defer self.a.free(json); defer self.a.free(json);
var output = std.ArrayList(u8).init(self.a); var output = std.ArrayList(u8).init(self.a);
defer output.deinit(); defer output.deinit();
const writer = output.writer(); const writer = output.writer();
try writer.print("Content-Length: {d}\r\nContent-Type: application/vscode-jsonrpc; charset=utf-8\r\n\r\n", .{json.len}); const terminator = "\r\n";
const content_length = json.len + terminator.len;
try writer.print("Content-Length: {d}\r\nContent-Type: application/vscode-jsonrpc; charset=utf-8\r\n\r\n", .{content_length});
_ = try writer.write(json); _ = try writer.write(json);
_ = try writer.write(terminator);
try sp.send(output.items); try sp.send(output.items);
self.write_log("### SEND request:\n{s}\n###\n", .{output.items}); self.write_log("### SEND request:\n{s}\n###\n", .{output.items});
@ -241,6 +247,8 @@ const Process = struct {
fn send_notification(self: *Process, method: []const u8, params_cb: []const u8) !void { fn send_notification(self: *Process, method: []const u8, params_cb: []const u8) !void {
const sp = if (self.sp) |*sp| sp else return error.Closed; const sp = if (self.sp) |*sp| sp else return error.Closed;
const have_params = !(cbor.match(params_cb, cbor.null_) catch false);
var msg = std.ArrayList(u8).init(self.a); var msg = std.ArrayList(u8).init(self.a);
defer msg.deinit(); defer msg.deinit();
const msg_writer = msg.writer(); const msg_writer = msg.writer();
@ -250,15 +258,22 @@ const Process = struct {
try cbor.writeValue(msg_writer, "method"); try cbor.writeValue(msg_writer, "method");
try cbor.writeValue(msg_writer, method); try cbor.writeValue(msg_writer, method);
try cbor.writeValue(msg_writer, "params"); try cbor.writeValue(msg_writer, "params");
if (have_params) {
_ = try msg_writer.write(params_cb); _ = try msg_writer.write(params_cb);
} else {
try cbor.writeMapHeader(msg_writer, 0);
}
const json = try cbor.toJsonPrettyAlloc(self.a, msg.items); const json = try cbor.toJsonAlloc(self.a, msg.items);
defer self.a.free(json); defer self.a.free(json);
var output = std.ArrayList(u8).init(self.a); var output = std.ArrayList(u8).init(self.a);
defer output.deinit(); defer output.deinit();
const writer = output.writer(); const writer = output.writer();
try writer.print("Content-Length: {d}\r\n\r\n", .{json.len}); const terminator = "\r\n";
const content_length = json.len + terminator.len;
try writer.print("Content-Length: {d}\r\nContent-Type: application/vscode-jsonrpc; charset=utf-8\r\n\r\n", .{content_length});
_ = try writer.write(json); _ = try writer.write(json);
_ = try writer.write(terminator);
try sp.send(output.items); try sp.send(output.items);
self.write_log("### SEND notification:\n{s}\n###\n", .{output.items}); self.write_log("### SEND notification:\n{s}\n###\n", .{output.items});

View file

@ -9,8 +9,8 @@ a: std.mem.Allocator,
name: []const u8, name: []const u8,
files: std.ArrayList(File), files: std.ArrayList(File),
open_time: i64, open_time: i64,
lsp: ?LSP = null, language_servers: std.StringHashMap(LSP),
lsp_name: [:0]const u8, file_language_server: std.StringHashMap(LSP),
const Self = @This(); const Self = @This();
@ -25,27 +25,44 @@ pub fn init(a: std.mem.Allocator, name: []const u8) error{OutOfMemory}!Self {
.name = try a.dupe(u8, name), .name = try a.dupe(u8, name),
.files = std.ArrayList(File).init(a), .files = std.ArrayList(File).init(a),
.open_time = std.time.milliTimestamp(), .open_time = std.time.milliTimestamp(),
.lsp_name = "zls", .language_servers = std.StringHashMap(LSP).init(a),
.file_language_server = std.StringHashMap(LSP).init(a),
}; };
} }
pub fn deinit(self: *Self) void { pub fn deinit(self: *Self) void {
var i_ = self.file_language_server.iterator();
while (i_.next()) |p| {
self.a.free(p.key_ptr.*);
}
var i = self.language_servers.iterator();
while (i.next()) |p| {
self.a.free(p.key_ptr.*);
p.value_ptr.*.deinit();
}
for (self.files.items) |file| self.a.free(file.path); for (self.files.items) |file| self.a.free(file.path);
self.files.deinit(); self.files.deinit();
if (self.lsp) |*lsp| lsp.deinit();
self.a.free(self.name); self.a.free(self.name);
} }
fn get_lsp(self: *Self) !LSP { fn get_lsp(self: *Self, language_server: []const u8) !LSP {
if (self.lsp) |lsp| return lsp; if (self.language_servers.get(language_server)) |lsp| return lsp;
self.lsp = try LSP.open(self.a, tp.message.fmt(.{self.lsp_name}), self.lsp_name); const lsp = try LSP.open(self.a, .{ .buf = language_server });
try self.language_servers.put(try self.a.dupe(u8, language_server), lsp);
const uri = try self.make_URI(null); const uri = try self.make_URI(null);
defer self.a.free(uri); defer self.a.free(uri);
const basename_begin = std.mem.lastIndexOfScalar(u8, self.name, std.fs.path.sep); const basename_begin = std.mem.lastIndexOfScalar(u8, self.name, std.fs.path.sep);
const basename = if (basename_begin) |begin| self.name[begin + 1 ..] else self.name; const basename = if (basename_begin) |begin| self.name[begin + 1 ..] else self.name;
const response = try self.send_lsp_init_request(self.name, basename, uri); const response = try self.send_lsp_init_request(lsp, self.name, basename, uri);
defer self.a.free(response.buf); defer self.a.free(response.buf);
return self.lsp.?; try lsp.send_notification("initialized", .{});
return lsp;
}
fn get_file_lsp(self: *Self, file_path: []const u8) !LSP {
const lsp = self.file_language_server.get(file_path) orelse return tp.exit("no language server");
if (lsp.pid.expired()) return tp.exit("no language server");
return lsp;
} }
fn make_URI(self: *Self, file_path: ?[]const u8) ![]const u8 { fn make_URI(self: *Self, file_path: ?[]const u8) ![]const u8 {
@ -92,9 +109,12 @@ pub fn query_recent_files(self: *Self, from: tp.pid_ref, max: usize, query: []co
return i; return i;
} }
pub fn did_open(self: *Self, from: tp.pid_ref, file_path: []const u8, file_type: []const u8, version: usize, text: []const u8) tp.result { pub fn did_open(self: *Self, file_path: []const u8, file_type: []const u8, language_server: []const u8, version: usize, text: []const u8) tp.result {
_ = from; // autofix const lsp = self.get_lsp(language_server) catch |e| return tp.exit_error(e);
const lsp = self.get_lsp() catch |e| return tp.exit_error(e); if (!self.file_language_server.contains(file_path)) {
const key = self.a.dupe(u8, file_path) catch |e| return tp.exit_error(e);
self.file_language_server.put(key, lsp) catch |e| return tp.exit_error(e);
}
const uri = self.make_URI(file_path) catch |e| return tp.exit_error(e); const uri = self.make_URI(file_path) catch |e| return tp.exit_error(e);
defer self.a.free(uri); defer self.a.free(uri);
try lsp.send_notification("textDocument/didOpen", .{ try lsp.send_notification("textDocument/didOpen", .{
@ -102,8 +122,8 @@ pub fn did_open(self: *Self, from: tp.pid_ref, file_path: []const u8, file_type:
}); });
} }
pub fn goto_definition(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usize, col: usize) tp.result { pub fn goto_definition(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usize, col: usize) !void {
const lsp = self.get_lsp() catch |e| return tp.exit_error(e); const lsp = try self.get_file_lsp(file_path);
const uri = self.make_URI(file_path) catch |e| return tp.exit_error(e); const uri = self.make_URI(file_path) catch |e| return tp.exit_error(e);
defer self.a.free(uri); defer self.a.free(uri);
const response = try lsp.send_request(self.a, "textDocument/definition", .{ const response = try lsp.send_request(self.a, "textDocument/definition", .{
@ -111,11 +131,100 @@ pub fn goto_definition(self: *Self, from: tp.pid_ref, file_path: []const u8, row
.position = .{ .line = row, .character = col }, .position = .{ .line = row, .character = col },
}); });
defer self.a.free(response.buf); defer self.a.free(response.buf);
try from.send_raw(response); var link: []const u8 = undefined;
if (try response.match(.{ "child", tp.string, "result", tp.array })) {
if (try response.match(.{ tp.any, tp.any, tp.any, .{ tp.extract_cbor(&link), tp.more } })) {
try self.navigate_to_location_link(from, link);
} else if (try response.match(.{ tp.any, tp.any, tp.any, .{tp.extract_cbor(&link)} })) {
try self.navigate_to_location_link(from, link);
}
} else if (try response.match(.{ "child", tp.string, "result", tp.null_ })) {
return;
} else if (try response.match(.{ "child", tp.string, "result", tp.extract_cbor(&link) })) {
try self.navigate_to_location_link(from, link);
}
} }
fn send_lsp_init_request(self: *Self, project_path: []const u8, project_basename: []const u8, project_uri: []const u8) error{Exit}!tp.message { fn navigate_to_location_link(self: *Self, from: tp.pid_ref, location_link: []const u8) !void {
return self.lsp.?.send_request(self.a, "initialize", .{ var iter = location_link;
var targetUri: ?[]const u8 = null;
var targetRange: ?Range = null;
var targetSelectionRange: ?Range = null;
var len = try cbor.decodeMapHeader(&iter);
while (len > 0) : (len -= 1) {
var field_name: []const u8 = undefined;
if (!(try cbor.matchString(&iter, &field_name))) return error.InvalidMessage;
if (std.mem.eql(u8, field_name, "targetUri") or std.mem.eql(u8, field_name, "uri")) {
var value: []const u8 = undefined;
if (!(try cbor.matchValue(&iter, cbor.extract(&value)))) return error.InvalidMessageField;
targetUri = value;
} else if (std.mem.eql(u8, field_name, "targetRange") or std.mem.eql(u8, field_name, "range")) {
var range: []const u8 = undefined;
if (!(try cbor.matchValue(&iter, cbor.extract_cbor(&range)))) return error.InvalidMessageField;
targetRange = try read_range(range);
} else if (std.mem.eql(u8, field_name, "targetSelectionRange")) {
var range: []const u8 = undefined;
if (!(try cbor.matchValue(&iter, cbor.extract_cbor(&range)))) return error.InvalidMessageField;
targetSelectionRange = try read_range(range);
} else {
try cbor.skipValue(&iter);
}
}
if (targetUri == null or targetRange == null) return error.InvalidMessageField;
if (!std.mem.eql(u8, targetUri.?[0..7], "file://")) return error.InvalidTargetURI;
const file_path = try std.Uri.unescapeString(self.a, targetUri.?[7..]);
defer self.a.free(file_path);
try from.send(.{ "cmd", "navigate", .{ .file = file_path, .line = targetRange.?.start.line + 1, .column = targetRange.?.start.character + 1 } });
}
const Range = struct { start: Position, end: Position };
fn read_range(range: []const u8) !Range {
var iter = range;
var start: ?Position = null;
var end: ?Position = null;
var len = try cbor.decodeMapHeader(&iter);
while (len > 0) : (len -= 1) {
var field_name: []const u8 = undefined;
if (!(try cbor.matchString(&iter, &field_name))) return error.InvalidMessage;
if (std.mem.eql(u8, field_name, "start")) {
var position: []const u8 = undefined;
if (!(try cbor.matchValue(&iter, cbor.extract_cbor(&position)))) return error.InvalidMessageField;
start = try read_position(position);
} else if (std.mem.eql(u8, field_name, "end")) {
var position: []const u8 = undefined;
if (!(try cbor.matchValue(&iter, cbor.extract_cbor(&position)))) return error.InvalidMessageField;
end = try read_position(position);
} else {
try cbor.skipValue(&iter);
}
}
if (start == null or end == null) return error.InvalidMessageField;
return .{ .start = start.?, .end = end.? };
}
const Position = struct { line: usize, character: usize };
fn read_position(position: []const u8) !Position {
var iter = position;
var line: ?usize = 0;
var character: ?usize = 0;
var len = try cbor.decodeMapHeader(&iter);
while (len > 0) : (len -= 1) {
var field_name: []const u8 = undefined;
if (!(try cbor.matchString(&iter, &field_name))) return error.InvalidMessage;
if (std.mem.eql(u8, field_name, "line")) {
if (!(try cbor.matchValue(&iter, cbor.extract(&line)))) return error.InvalidMessageField;
} else if (std.mem.eql(u8, field_name, "character")) {
if (!(try cbor.matchValue(&iter, cbor.extract(&character)))) return error.InvalidMessageField;
} else {
try cbor.skipValue(&iter);
}
}
if (line == null or character == null) return error.InvalidMessageField;
return .{ .line = line.?, .character = character.? };
}
fn send_lsp_init_request(self: *Self, lsp: LSP, project_path: []const u8, project_basename: []const u8, project_uri: []const u8) error{Exit}!tp.message {
return lsp.send_request(self.a, "initialize", .{
.processId = std.os.linux.getpid(), .processId = std.os.linux.getpid(),
.rootPath = project_path, .rootPath = project_path,
.rootUri = project_uri, .rootUri = project_uri,
@ -145,7 +254,7 @@ fn send_lsp_init_request(self: *Self, project_path: []const u8, project_basename
.normalizesLineEndings = true, .normalizesLineEndings = true,
.changeAnnotationSupport = .{ .groupsOnLabel = true }, .changeAnnotationSupport = .{ .groupsOnLabel = true },
}, },
.configuration = true, // .configuration = true,
.didChangeWatchedFiles = .{ .didChangeWatchedFiles = .{
.dynamicRegistration = true, .dynamicRegistration = true,
.relativePatternSupport = true, .relativePatternSupport = true,
@ -160,7 +269,7 @@ fn send_lsp_init_request(self: *Self, project_path: []const u8, project_basename
}, },
.codeLens = .{ .refreshSupport = true }, .codeLens = .{ .refreshSupport = true },
.executeCommand = .{ .dynamicRegistration = true }, .executeCommand = .{ .dynamicRegistration = true },
.didChangeConfiguration = .{ .dynamicRegistration = true }, // .didChangeConfiguration = .{ .dynamicRegistration = true },
.workspaceFolders = true, .workspaceFolders = true,
.semanticTokens = .{ .refreshSupport = true }, .semanticTokens = .{ .refreshSupport = true },
.fileOperations = .{ .fileOperations = .{
@ -402,7 +511,7 @@ fn send_lsp_init_request(self: *Self, project_path: []const u8, project_basename
.parser = "marked", .parser = "marked",
.version = "1.1.0", .version = "1.1.0",
}, },
.positionEncodings = .{"utf-16"}, .positionEncodings = .{"utf-8"},
}, },
.notebookDocument = .{ .notebookDocument = .{
.synchronization = .{ .synchronization = .{

View file

@ -3,6 +3,7 @@ const tp = @import("thespian");
const cbor = @import("cbor"); const cbor = @import("cbor");
const log = @import("log"); const log = @import("log");
const tracy = @import("tracy"); const tracy = @import("tracy");
const FileType = @import("syntax").FileType;
const Project = @import("Project.zig"); const Project = @import("Project.zig");
@ -57,11 +58,12 @@ 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 did_open(file_path: []const u8, file_type: []const u8, version: usize, text: []const u8) tp.result { pub fn did_open(file_path: []const u8, file_type: *const FileType, version: usize, text: []const u8) tp.result {
const project = tp.env.get().str("project"); const project = tp.env.get().str("project");
if (project.len == 0) if (project.len == 0)
return tp.exit("No project"); return tp.exit("No project");
return (try get()).pid.send(.{ "did_open", project, file_path, file_type, version, @intFromPtr(text.ptr), text.len }); const text_ptr: usize = if (text.len > 0) @intFromPtr(text.ptr) else 0;
return (try get()).pid.send(.{ "did_open", project, file_path, file_type.name, file_type.language_server, version, text_ptr, text.len });
} }
pub fn goto_definition(file_path: []const u8, row: usize, col: usize) tp.result { pub fn goto_definition(file_path: []const u8, row: usize, col: usize) tp.result {
@ -119,6 +121,7 @@ const Process = struct {
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 file_type: []const u8 = undefined;
var language_server: []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;
@ -144,15 +147,16 @@ const Process = struct {
std.time.milliTimestamp() - project.open_time, std.time.milliTimestamp() - project.open_time,
}); });
} else if (try m.match(.{ "open", tp.extract(&project_directory) })) { } else if (try m.match(.{ "open", tp.extract(&project_directory) })) {
self.open(project_directory) catch |e| return from.send_raw(tp.exit_message(e)); self.open(project_directory) catch |e| return from.forward_error(e);
} else if (try m.match(.{ "request_recent_files", tp.extract(&project_directory), tp.extract(&max) })) { } else if (try m.match(.{ "request_recent_files", tp.extract(&project_directory), tp.extract(&max) })) {
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.forward_error(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.forward_error(e);
} else if (try m.match(.{ "did_open", tp.extract(&project_directory), tp.extract(&path), tp.extract(&file_type), tp.extract(&version), tp.extract(&text_ptr), tp.extract(&text_len) })) { } else if (try m.match(.{ "did_open", tp.extract(&project_directory), tp.extract(&path), tp.extract(&file_type), tp.extract_cbor(&language_server), tp.extract(&version), tp.extract(&text_ptr), tp.extract(&text_len) })) {
self.did_open(from, project_directory, path, file_type, version, @as([*]const u8, @ptrFromInt(text_ptr))[0..text_len]) catch |e| return from.send_raw(tp.exit_message(e)); const text = if (text_len > 0) @as([*]const u8, @ptrFromInt(text_ptr))[0..text_len] else "";
self.did_open(project_directory, path, file_type, language_server, version, text) catch |e| return from.forward_error(e);
} else if (try m.match(.{ "goto_definition", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) { } else if (try m.match(.{ "goto_definition", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) {
self.goto_definition(from, project_directory, path, row, col) catch |e| return from.send_raw(tp.exit_message(e)); self.goto_definition(from, project_directory, path, row, col) catch |e| return from.forward_error(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" });
@ -189,18 +193,18 @@ const Process = struct {
// 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 });
} }
fn did_open(self: *Process, from: tp.pid_ref, project_directory: []const u8, file_path: []const u8, file_type: []const u8, version: usize, text: []const u8) tp.result { fn did_open(self: *Process, project_directory: []const u8, file_path: []const u8, file_type: []const u8, language_server: []const u8, version: usize, text: []const u8) tp.result {
const frame = tracy.initZone(@src(), .{ .name = module_name ++ ".did_open" }); const frame = tracy.initZone(@src(), .{ .name = module_name ++ ".did_open" });
defer frame.deinit(); defer frame.deinit();
const project = if (self.projects.get(project_directory)) |p| p else return tp.exit("No project"); const project = if (self.projects.get(project_directory)) |p| p else return tp.exit("No project");
return project.did_open(from, file_path, file_type, version, text); return project.did_open(file_path, file_type, language_server, version, text);
} }
fn goto_definition(self: *Process, from: tp.pid_ref, project_directory: []const u8, file_path: []const u8, row: usize, col: usize) tp.result { fn goto_definition(self: *Process, from: tp.pid_ref, project_directory: []const u8, file_path: []const u8, row: usize, col: usize) tp.result {
const frame = tracy.initZone(@src(), .{ .name = module_name ++ ".goto_definition" }); const frame = tracy.initZone(@src(), .{ .name = module_name ++ ".goto_definition" });
defer frame.deinit(); defer frame.deinit();
const project = if (self.projects.get(project_directory)) |p| p else return tp.exit("No project"); const project = if (self.projects.get(project_directory)) |p| p else return tp.exit("No project");
return project.goto_definition(from, file_path, row, col); return project.goto_definition(from, file_path, row, col) catch |e| tp.exit_error(e);
} }
}; };

View file

@ -366,9 +366,8 @@ pub const Editor = struct {
break :syntax syntax.create_file_type(self.a, content.items, lang_override) catch null; break :syntax syntax.create_file_type(self.a, content.items, lang_override) catch null;
break :syntax syntax.create_guess_file_type(self.a, content.items, self.file_path) catch null; break :syntax syntax.create_guess_file_type(self.a, content.items, self.file_path) catch null;
}; };
// TODO: fix and enable if (self.syntax) |syn|
// if (self.syntax) |syn| project_manager.did_open(file_path, syn.file_type, self.lsp_version, try content.toOwnedSlice()) catch {};
// project_manager.did_open(file_path, syn.file_type.name, self.lsp_version, try content.toOwnedSlice()) catch {};
const ftn = if (self.syntax) |syn| syn.file_type.name else "text"; const ftn = if (self.syntax) |syn| syn.file_type.name else "text";
const fti = if (self.syntax) |syn| syn.file_type.icon else "🖹"; const fti = if (self.syntax) |syn| syn.file_type.icon else "🖹";