Add language format and code action edits

This commit is contained in:
slhx agent
2026-06-21 14:21:09 +02:00
parent e4d6837f78
commit 616eea670b
4 changed files with 460 additions and 2 deletions
+91
View File
@@ -23,6 +23,8 @@ pub const Error = error{
InvalidProvider,
MissingProvider,
AmbiguousProvider,
StaleEdit,
OverlappingEdit,
};
pub const ProviderKind = enum {
@@ -209,6 +211,95 @@ pub fn compactSignatureCardAlloc(allocator: std.mem.Allocator, row: []const u8)
return std.fmt.allocPrint(allocator, "[{s}] {s}", .{ provider, body });
}
pub const ProviderEdit = struct {
provider: []const u8,
capability: ProviderCapability,
version: u64,
start: usize,
end: usize,
replacement: []const u8,
};
pub fn providerCapabilityFromName(name: []const u8) !ProviderCapability {
if (std.mem.eql(u8, name, "format")) return .format;
if (std.mem.eql(u8, name, "organize_imports")) return .organize_imports;
if (std.mem.eql(u8, name, "code_action")) return .code_action;
return Error.InvalidProvider;
}
pub fn editRowAlloc(
allocator: std.mem.Allocator,
provider: []const u8,
capability: ProviderCapability,
version: u64,
start: usize,
end: usize,
replacement: []const u8,
) ![]u8 {
if (provider.len == 0 or start > end) return Error.InvalidEdit;
return std.fmt.allocPrint(allocator, "provider:{s}:edit:{s}:{d}:{d}:{d}:{s}", .{ provider, @tagName(capability), version, start, end, replacement });
}
pub fn parseProviderEditRow(row: []const u8) !ProviderEdit {
const provider = try providerIdFromRow(row);
const payload = try providerPayloadFromRow(row);
if (!std.mem.startsWith(u8, payload, "edit:")) return Error.InvalidEdit;
var parts = std.mem.splitScalar(u8, payload, ':');
_ = parts.next() orelse return Error.InvalidEdit;
const capability_name = parts.next() orelse return Error.InvalidEdit;
const version_text = parts.next() orelse return Error.InvalidEdit;
const start_text = parts.next() orelse return Error.InvalidEdit;
const end_text = parts.next() orelse return Error.InvalidEdit;
const replacement = parts.rest();
const start = std.fmt.parseUnsigned(usize, start_text, 10) catch return Error.InvalidEdit;
const end = std.fmt.parseUnsigned(usize, end_text, 10) catch return Error.InvalidEdit;
if (start > end) return Error.InvalidEdit;
return .{
.provider = provider,
.capability = try providerCapabilityFromName(capability_name),
.version = std.fmt.parseUnsigned(u64, version_text, 10) catch return Error.InvalidEdit,
.start = start,
.end = end,
.replacement = replacement,
};
}
pub fn validateEditBatch(rows: []const []const u8, document_version: u64, document_len: usize) !void {
var previous_start: ?usize = null;
var previous_end: ?usize = null;
for (rows) |row| {
const edit = try parseProviderEditRow(row);
if (edit.version != document_version) return Error.StaleEdit;
if (edit.end > document_len) return Error.InvalidEdit;
if (previous_start) |start| {
if (!(edit.end <= start or edit.start >= previous_end.?)) return Error.OverlappingEdit;
}
previous_start = edit.start;
previous_end = edit.end;
}
}
pub fn actionPanelRowAlloc(allocator: std.mem.Allocator, row: []const u8) ![]const u8 {
const edit = try parseProviderEditRow(row);
return std.fmt.allocPrint(allocator, "action:{s}:provider:{s}:scope:{d}-{d}:apply", .{ @tagName(edit.capability), edit.provider, edit.start, edit.end });
}
pub fn providerFromActionPanelRow(row: []const u8) ![]const u8 {
const marker = ":provider:";
const start = std.mem.indexOf(u8, row, marker) orelse return Error.InvalidProvider;
const rest = row[start + marker.len ..];
const end = std.mem.indexOfScalar(u8, rest, ':') orelse return Error.InvalidProvider;
if (end == 0) return Error.InvalidProvider;
return rest[0..end];
}
pub fn capabilityFromActionPanelRow(row: []const u8) !ProviderCapability {
if (!std.mem.startsWith(u8, row, "action:")) return Error.InvalidProvider;
const rest = row[7..];
const end = std.mem.indexOfScalar(u8, rest, ':') orelse return Error.InvalidProvider;
return providerCapabilityFromName(rest[0..end]);
}
pub fn providerPickerRowsAlloc(allocator: std.mem.Allocator, capability: ProviderCapability, providers: []const Provider) ![][]const u8 {
var rows = std.ArrayList([]const u8).empty;
errdefer {