Skip to content

Commit

Permalink
Added option to truncate completion label details to improve visibili…
Browse files Browse the repository at this point in the history
…ty in some editors.

Co-authored-by: Techatrix <19954306+Techatrix@users.noreply.github.com>
  • Loading branch information
diocletiann and Techatrix committed Feb 13, 2024
1 parent 6a45bb4 commit 7c733a6
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 74 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ The following options are currently available.
| `global_cache_path` | `?[]const u8` | `null` | Path to a directory that will be used as zig's cache. null is equivalent to `${KnownFolders.Cache}/zls` |
| `build_runner_global_cache_path` | `?[]const u8` | `null` | Path to a directory that will be used as the global cache path when executing a projects build.zig. null is equivalent to the path shown by `zig env` |
| `completions_with_replace` | `bool` | `true` | Completions confirm behavior. If 'true', replace the text after the cursor |
| `completion_label_details` | `bool` | `true` | When false, the function signature of completion results is hidden. Improves readability in some editors |
<!-- DO NOT EDIT -->

### Per-build Configuration Options
Expand Down
5 changes: 5 additions & 0 deletions schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@
"description": "Completions confirm behavior. If 'true', replace the text after the cursor",
"type": "boolean",
"default": true
},
"completion_label_details": {
"description": "When false, the function signature of completion results is hidden. Improves readability in some editors",
"type": "boolean",
"default": true
}
}
}
3 changes: 3 additions & 0 deletions src/Config.zig
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,7 @@ build_runner_global_cache_path: ?[]const u8 = null,
/// Completions confirm behavior. If 'true', replace the text after the cursor
completions_with_replace: bool = true,

/// When false, the function signature of completion results is hidden. Improves readability in some editors
completion_label_details: bool = true,

// DO NOT EDIT
127 changes: 73 additions & 54 deletions src/analysis.zig
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,14 @@ pub const FormatFunctionOptions = struct {
/// only included if available
include_name: bool,
skip_first_param: bool = false,
include_parameter_modifiers: bool,
include_parameter_names: bool,
include_parameter_types: bool,
parameters: union(enum) {
collapse,
show: struct {
include_modifiers: bool,
include_names: bool,
include_types: bool,
},
},
include_return_type: bool,
snippet_placeholders: bool,
};
Expand Down Expand Up @@ -197,69 +202,83 @@ pub fn formatFunction(
_ = ast.nextFnParam(&it);
}

var i: usize = 0;
while (ast.nextFnParam(&it)) |param| : (i += 1) {
if (i != 0) {
try writer.writeAll(", ");
}
switch (data.parameters) {
.collapse => {
const has_arguments = ast.nextFnParam(&it) != null;
if (has_arguments) {
if (data.snippet_placeholders) {
try writer.writeAll("${1:...}");
} else {
try writer.writeAll("...");
}
}
},
.show => |parameter_options| {
var i: usize = 0;
while (ast.nextFnParam(&it)) |param| : (i += 1) {
if (i != 0) {
try writer.writeAll(", ");
}

if (data.snippet_placeholders) {
try writer.print("${{{d}:", .{i + 1});
}
if (data.snippet_placeholders) {
try writer.print("${{{d}:", .{i + 1});
}

// Note that parameter doc comments are being skipped
// Note that parameter doc comments are being skipped

if (data.include_parameter_modifiers) {
if (param.comptime_noalias) |token_index| {
switch (token_tags[token_index]) {
.keyword_comptime => try writer.writeAll("comptime "),
.keyword_noalias => try writer.writeAll("noalias "),
else => unreachable,
if (parameter_options.include_modifiers) {
if (param.comptime_noalias) |token_index| {
switch (token_tags[token_index]) {
.keyword_comptime => try writer.writeAll("comptime "),
.keyword_noalias => try writer.writeAll("noalias "),
else => unreachable,
}
}
}
}
}

if (data.include_parameter_names) {
if (param.name_token) |name_token| {
const name = tree.tokenSlice(name_token);
if (data.snippet_placeholders) {
try writer.print("{}", .{fmtSnippetPlaceholder(name)});
} else {
try writer.writeAll(name);
if (parameter_options.include_names) {
if (param.name_token) |name_token| {
const name = tree.tokenSlice(name_token);
if (data.snippet_placeholders) {
try writer.print("{}", .{fmtSnippetPlaceholder(name)});
} else {
try writer.writeAll(name);
}
}
}
}
}

if (data.include_parameter_types) {
try writer.writeAll(": ");
if (parameter_options.include_types) {
try writer.writeAll(": ");

if (param.type_expr != 0) {
if (data.snippet_placeholders) {
var curr_token = tree.firstToken(param.type_expr);
const end_token = ast.lastToken(tree.*, param.type_expr);
while (curr_token <= end_token) : (curr_token += 1) {
const tag = token_tags[curr_token];
const is_comma = tag == .comma;

if (curr_token == end_token and is_comma) continue;
try writer.print("{}", .{fmtSnippetPlaceholder(tree.tokenSlice(curr_token))});
if (is_comma or tag == .keyword_const) try writer.writeByte(' ');
if (param.type_expr != 0) {
if (data.snippet_placeholders) {
var curr_token = tree.firstToken(param.type_expr);
const end_token = ast.lastToken(tree.*, param.type_expr);
while (curr_token <= end_token) : (curr_token += 1) {
const tag = token_tags[curr_token];
const is_comma = tag == .comma;

if (curr_token == end_token and is_comma) continue;
try writer.print("{}", .{fmtSnippetPlaceholder(tree.tokenSlice(curr_token))});
if (is_comma or tag == .keyword_const) try writer.writeByte(' ');
}
} else {
try writer.writeAll(offsets.nodeToSlice(tree.*, param.type_expr));
}
} else if (param.anytype_ellipsis3) |token_index| {
switch (token_tags[token_index]) {
.keyword_anytype => try writer.writeAll("anytype"),
.ellipsis3 => try writer.writeAll("..."),
else => unreachable,
}
}
} else {
try writer.writeAll(offsets.nodeToSlice(tree.*, param.type_expr));
}
} else if (param.anytype_ellipsis3) |token_index| {
switch (token_tags[token_index]) {
.keyword_anytype => try writer.writeAll("anytype"),
.ellipsis3 => try writer.writeAll("..."),
else => unreachable,

if (data.snippet_placeholders) {
try writer.writeByte('}');
}
}
}

if (data.snippet_placeholders) {
try writer.writeByte('}');
}
},
}
try writer.writeByte(')');

Expand Down
6 changes: 6 additions & 0 deletions src/config_gen/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,12 @@
"description": "Completions confirm behavior. If 'true', replace the text after the cursor",
"type": "bool",
"default": true
},
{
"name": "completion_label_details",
"description": "When false, the function signature of completion results is hidden. Improves readability in some editors",
"type": "bool",
"default": true
}
]
}
59 changes: 41 additions & 18 deletions src/features/completions.zig
Original file line number Diff line number Diff line change
Expand Up @@ -266,9 +266,11 @@ fn nodeToCompletion(
.include_fn_keyword = false,
.include_name = true,
.skip_first_param = skip_self_param,
.include_parameter_modifiers = false,
.include_parameter_names = true,
.include_parameter_types = true,
.parameters = .{ .show = .{
.include_modifiers = false,
.include_names = true,
.include_types = true,
} },
.include_return_type = false,
.snippet_placeholders = true,
})});
Expand Down Expand Up @@ -302,41 +304,62 @@ fn nodeToCompletion(
else
.Function;

const label_details: ?[]const u8 = if (use_label_details)
try std.fmt.allocPrint(arena, "{}", .{Analyser.fmtFunction(.{
const label_details: ?types.CompletionItemLabelDetails = blk: {
if (!use_label_details) break :blk null;

const detail = try std.fmt.allocPrint(arena, "{}", .{Analyser.fmtFunction(.{
.fn_proto = func,
.tree = &tree,

.include_fn_keyword = false,
.include_name = false,
.include_parameter_modifiers = true,
.include_parameter_names = true,
.include_parameter_types = true,
.skip_first_param = skip_self_param,
.parameters = if (server.config.completion_label_details)
.{ .show = .{
.include_modifiers = true,
.include_names = true,
.include_types = true,
} }
else
.collapse,
.include_return_type = false,
.snippet_placeholders = false,
})})
else
null;
})});

const description = description: {
if (func.ast.return_type == 0) break :description null;
const return_type_str = offsets.nodeToSlice(tree, func.ast.return_type);

break :description if (ast.hasInferredError(tree, func))
try std.fmt.allocPrint(arena, "!{s}", .{return_type_str})
else
return_type_str;
};

break :blk .{
.detail = detail,
.description = description,
};
};

const details = try std.fmt.allocPrint(arena, "{}", .{Analyser.fmtFunction(.{
.fn_proto = func,
.tree = &tree,

.include_fn_keyword = true,
.include_name = false,
.include_parameter_modifiers = true,
.include_parameter_names = true,
.include_parameter_types = true,
.parameters = .{ .show = .{
.include_modifiers = true,
.include_names = true,
.include_types = true,
} },
.include_return_type = true,
.snippet_placeholders = false,
})});

try list.append(arena, .{
.label = func_name,
.labelDetails = if (use_label_details) .{
.detail = label_details,
.description = null,
} else null,
.labelDetails = label_details,
.kind = kind,
.documentation = doc,
.detail = details,
Expand Down
49 changes: 47 additions & 2 deletions tests/lsp_features/completion.zig
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ test "completion - function" {
.label = "foo",
.labelDetails = .{
.detail = "(alpha: u32, beta: []const u8)",
.description = null,
.description = "void",
},
.kind = .Function,
.detail = "fn (alpha: u32, beta: []const u8) void",
Expand All @@ -206,7 +206,7 @@ test "completion - function" {
.label = "foo",
.labelDetails = .{
.detail = "(comptime T: type, value: anytype)",
.description = null,
.description = "void",
},
.kind = .Function,
.detail = "fn (comptime T: type, value: anytype) void",
Expand Down Expand Up @@ -2239,13 +2239,57 @@ test "completion - snippets disabled" {
});
}

test "completion - label details disabled" {
try testCompletionWithOptions(
\\const S = struct {
\\ fn f(self: S) void {}
\\};
\\const s = S{};
\\s.<cursor>
, &.{
.{
.label = "f",
.labelDetails = .{
.detail = "()",
.description = "void",
},
.kind = .Method,
.detail = "fn (self: S) void",
.insert_text = "f()",
},
}, .{
.completion_label_details = false,
});
try testCompletionWithOptions(
\\const S = struct {
\\ fn f(self: S, value: u32) !void {}
\\};
\\const s = S{};
\\s.<cursor>
, &.{
.{
.label = "f",
.labelDetails = .{
.detail = "(...)",
.description = "!void",
},
.kind = .Method,
.detail = "fn (self: S, value: u32) !void",
.insert_text = "f(${1:value: u32})",
},
}, .{
.completion_label_details = false,
});
}

fn testCompletion(source: []const u8, expected_completions: []const Completion) !void {
try testCompletionWithOptions(source, expected_completions, .{});
}

fn testCompletionWithOptions(source: []const u8, expected_completions: []const Completion, options: struct {
enable_argument_placeholders: bool = true,
enable_snippets: bool = true,
completion_label_details: bool = true,
}) !void {
const cursor_idx = std.mem.indexOf(u8, source, "<cursor>").?;
const text = try std.mem.concat(allocator, u8, &.{ source[0..cursor_idx], source[cursor_idx + "<cursor>".len ..] });
Expand All @@ -2262,6 +2306,7 @@ fn testCompletionWithOptions(source: []const u8, expected_completions: []const C

ctx.server.config.enable_argument_placeholders = options.enable_argument_placeholders;
ctx.server.config.enable_snippets = options.enable_snippets;
ctx.server.config.completion_label_details = options.completion_label_details;

const test_uri = try ctx.addDocument(text);

Expand Down

0 comments on commit 7c733a6

Please sign in to comment.