flow/src/Project.zig

139 lines
4.5 KiB
Zig

const std = @import("std");
const tp = @import("thespian");
const cbor = @import("cbor");
const root = @import("root");
const LSP = @import("LSP.zig");
a: std.mem.Allocator,
name: []const u8,
files: std.ArrayList(File),
open_time: i64,
lsp: ?LSP = null,
lsp_name: [:0]const u8,
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(),
.lsp_name = "zls",
};
}
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(.{self.lsp_name}), self.lsp_name);
const uri = try self.make_URI(null);
defer self.a.free(uri);
const response = try self.lsp.?.send_request(self.a, "initialize", .{
.processId = std.os.linux.getpid(),
.rootUri = uri,
.clientInfo = .{ .name = root.application_name },
.capabilities = .{
.workspace = .{
.applyEdit = true,
.codeLens = .{ .refreshSupport = true },
.configuration = true,
.diagnostics = .{ .refreshSupport = true },
.fileOperations = .{
.didCreate = true,
.didDelete = true,
.didRename = true,
.willCreate = true,
.willDelete = true,
.willRename = true,
},
},
},
});
defer self.a.free(response.buf);
return self.lsp.?;
}
fn make_URI(self: *Self, file_path: ?[]const u8) ![]const u8 {
var buf = std.ArrayList(u8).init(self.a);
if (file_path) |path|
try buf.writer().print("file:/{s}/{s}", .{ self.name, path })
else
try buf.writer().print("file:/{s}", .{self.name});
return buf.toOwnedSlice();
}
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 did_open(self: *Self, from: tp.pid_ref, file_path: []const u8, file_type: []const u8, version: usize, text: []const u8) tp.result {
const lsp = self.get_lsp() 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);
const response = try lsp.send_request(self.a, "textDocument/didOpen", .{
.textDocument = .{
.uri = uri,
.languageId = file_type,
.version = version,
.text = text,
},
});
defer self.a.free(response.buf);
_ = from;
}
pub fn goto_definition(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usize, col: usize) tp.result {
const lsp = self.get_lsp() 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);
const response = try lsp.send_request(self.a, "textDocument/definition", .{
.textDocument = .{ .uri = uri },
.position = .{ .line = row, .character = col },
});
defer self.a.free(response.buf);
_ = from;
}