From b33e26612a00dbace66a4be4fda2ab5804dd0b4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20H=C3=A4hne?= Date: Sat, 27 Mar 2021 20:37:51 +0100 Subject: [PATCH] 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. --- build.zig | 7 +++++ src/config.zig | 5 +++- src/main.zig | 78 ++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 71 insertions(+), 19 deletions(-) diff --git a/build.zig b/build.zig index 69ff75d..458ac18 100644 --- a/build.zig +++ b/build.zig @@ -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", .{}); diff --git a/src/config.zig b/src/config.zig index 93a92d3..7616cdb 100644 --- a/src/config.zig +++ b/src/config.zig @@ -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, diff --git a/src/main.zig b/src/main.zig index 45bbea7..685b25b 100644 --- a/src/main.zig +++ b/src/main.zig @@ -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), }