Add option to truncate long completions

The detail entries for big structs such as std.zig.CrossTarget were
bricking the preview window in Sublime Text.
This commit is contained in:
Jonathan Hähne 2021-03-27 20:37:51 +01:00
parent 3e300e4d74
commit b33e26612a
3 changed files with 71 additions and 19 deletions

View File

@ -74,6 +74,12 @@ pub fn config(step: *std.build.Step) anyerror!void {
else =>
try zinput.askBool("Should the @ sign be included in completions of builtin functions?\nChange this later if `@inc` completes to `include` or `@@include`")
};
const max_detail_length: usize = switch (editor) {
.Sublime =>
256,
else =>
1024 * 1024
};
var dir = try std.fs.cwd().openDir(builder.exe_dir, .{});
defer dir.close();
@ -92,6 +98,7 @@ pub fn config(step: *std.build.Step) anyerror!void {
.enable_semantic_tokens = semantic_tokens,
.operator_completions = operator_completions,
.include_at_in_builtins = include_at_in_builtins,
.max_detail_length = max_detail_length,
}, std.json.StringifyOptions{}, out);
std.debug.warn("Successfully saved configuration options!\n", .{});

View File

@ -17,7 +17,7 @@ warn_style: bool = false,
/// Path to the build_runner.zig file.
build_runner_path: ?[]const u8 = null,
/// Path to a directory that will be used as cache when `zig run`ing the build runner
/// Path to a directory that will be used as cache when `zig run`ning the build runner
build_runner_cache_path: ?[]const u8 = null,
/// Semantic token support
@ -29,6 +29,9 @@ operator_completions: bool = true,
/// Whether the @ sign should be part of the completion of builtins
include_at_in_builtins: bool = false,
/// The detail field of completions is truncated to be no longer than this (in bytes).
max_detail_length: usize = 1024 * 1024,
/// Skips references to std. This will improve lookup speeds.
/// Going to definition however will continue to work
skip_std_references: bool = false,

View File

@ -138,6 +138,16 @@ fn send(arena: *std.heap.ArenaAllocator, reqOrRes: anytype) !void {
try stdout.flush();
}
fn truncateCompletions(list: []types.CompletionItem, max_detail_length: usize) void {
for (list) |*item| {
if (item.detail) |det| {
if (det.len > max_detail_length) {
item.detail = det[0..max_detail_length];
}
}
}
}
fn respondGeneric(id: types.RequestId, response: []const u8) !void {
const id_len = switch (id) {
.Integer => |id_val| blk: {
@ -998,6 +1008,7 @@ fn completeLabel(arena: *std.heap.ArenaAllocator, id: types.RequestId, pos_index
.orig_handle = handle,
};
try analysis.iterateLabels(handle, pos_index, declToCompletion, context);
truncateCompletions(completions.items, config.max_detail_length);
try send(arena, types.Response{
.id = id,
@ -1039,6 +1050,7 @@ fn completeBuiltin(arena: *std.heap.ArenaAllocator, id: types.RequestId, config:
else
insert_text[1..];
}
truncateCompletions(builtin_completions.?, config.max_detail_length);
}
try send(arena, types.Response{
@ -1062,6 +1074,7 @@ fn completeGlobal(arena: *std.heap.ArenaAllocator, id: types.RequestId, pos_inde
.orig_handle = handle,
};
try analysis.iterateSymbolsGlobal(&document_store, arena, handle, pos_index, declToCompletion, context);
truncateCompletions(completions.items, config.max_detail_length);
try send(arena, types.Response{
.id = id,
@ -1087,6 +1100,7 @@ fn completeFieldAccess(
if (try analysis.getFieldAccessType(&document_store, arena, handle, position.absolute_index, &tokenizer)) |result| {
try typeToCompletion(arena, &completions, result, handle, config);
}
truncateCompletions(completions.items, config.max_detail_length);
try send(arena, types.Response{
.id = id,
@ -1099,6 +1113,46 @@ fn completeFieldAccess(
});
}
fn completeError(
arena: *std.heap.ArenaAllocator,
id: types.RequestId,
handle: *DocumentStore.Handle,
config: Config
) !void {
const completions = try document_store.errorCompletionItems(arena, handle);
truncateCompletions(completions, config.max_detail_length);
try send(arena, types.Response{
.id = id,
.result = .{
.CompletionList = .{
.isIncomplete = false,
.items = completions,
},
},
});
}
fn completeDot(
arena: *std.heap.ArenaAllocator,
id: types.RequestId,
handle: *DocumentStore.Handle,
config: Config
) !void {
var completions = try document_store.enumCompletionItems(arena, handle);
truncateCompletions(completions, config.max_detail_length);
try send(arena, types.Response{
.id = id,
.result = .{
.CompletionList = .{
.isIncomplete = false,
.items = completions,
},
},
});
}
fn documentSymbol(arena: *std.heap.ArenaAllocator, id: types.RequestId, handle: *DocumentStore.Handle) !void {
try send(arena, types.Response{
.id = id,
@ -1117,6 +1171,9 @@ fn loadConfig(folder_path: []const u8) ?Config {
};
defer allocator.free(file_buf);
// TODO: Uh oh. Profile the actual build time impact
// of adding config options and consider alternatives (TOML?)
@setEvalBranchQuota(2000);
// TODO: Better errors? Doesn't seem like std.json can provide us positions or context.
var config = std.json.parse(Config, &std.json.TokenStream.init(file_buf), std.json.ParseOptions{ .allocator = allocator }) catch |err| {
logger.warn("Error while parsing configuration file: {}", .{err});
@ -1304,28 +1361,13 @@ fn completionHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req:
const doc_position = try offsets.documentPosition(handle.document, req.params.position, offset_encoding);
const pos_context = try analysis.documentPositionContext(arena, handle.document, doc_position);
const use_snippets = config.enable_snippets and client_capabilities.supports_snippets;
switch (pos_context) {
.builtin => try completeBuiltin(arena, id, config),
.var_access, .empty => try completeGlobal(arena, id, doc_position.absolute_index, handle, config),
.field_access => |range| try completeFieldAccess(arena, id, handle, doc_position, range, config),
.global_error_set => try send(arena, types.Response{
.id = id,
.result = .{
.CompletionList = .{
.isIncomplete = false,
.items = try document_store.errorCompletionItems(arena, handle),
},
},
}),
.enum_literal => try send(arena, types.Response{
.id = id,
.result = .{
.CompletionList = .{
.isIncomplete = false,
.items = try document_store.enumCompletionItems(arena, handle),
},
},
}),
.global_error_set => try completeError(arena, id, handle, config),
.enum_literal => try completeDot(arena, id, handle, config),
.label => try completeLabel(arena, id, doc_position.absolute_index, handle, config),
else => try respondGeneric(id, no_completions_response),
}