Fixes and completions

This commit is contained in:
Luuk de Gram 2021-03-01 16:02:24 +01:00
parent 1c9da7053c
commit 4645638434
No known key found for this signature in database
GPG Key ID: A002B174963DBB7D
4 changed files with 108 additions and 100 deletions

View File

@ -79,14 +79,14 @@ pub fn collectDocComments(
} }
/// Gets a function signature (keywords, name, return value) /// Gets a function signature (keywords, name, return value)
pub fn getFunctionSignature(tree: ast.Tree, func: *ast.full.FnProto) []const u8 { pub fn getFunctionSignature(tree: ast.Tree, func: ast.full.FnProto) []const u8 {
const start = tree.tokenLocation(func.ast.fn_token).line_start; const start = tree.tokenLocation(func.ast.fn_token).line_start;
const end = tree.tokenLocation(func.ast.return_type).line_end; const end = tree.tokenLocation(func.ast.return_type).line_end;
return tree.source[start..end]; return tree.source[start..end];
} }
/// Gets a function snippet insert text /// Gets a function snippet insert text
pub fn getFunctionSnippet(allocator: *std.mem.Allocator, tree: ast.Tree, func: *ast.full.FnProto, skip_self_param: bool) ![]const u8 { pub fn getFunctionSnippet(allocator: *std.mem.Allocator, tree: ast.Tree, func: ast.full.FnProto, skip_self_param: bool) ![]const u8 {
const name_index = func.name_token orelse unreachable; const name_index = func.name_token orelse unreachable;
var buffer = std.ArrayList(u8).init(allocator); var buffer = std.ArrayList(u8).init(allocator);
@ -143,14 +143,14 @@ pub fn getFunctionSnippet(allocator: *std.mem.Allocator, tree: ast.Tree, func: *
} }
/// Gets a function signature (keywords, name, return value) /// Gets a function signature (keywords, name, return value)
pub fn getVariableSignature(tree: ast.Tree, var_decl: *ast.full.VarDecl) []const u8 { pub fn getVariableSignature(tree: ast.Tree, var_decl: ast.full.VarDecl) []const u8 {
const start = tree.tokenLocation(0, var_decl.ast.mut_token).line_start; const start = tree.tokenLocation(0, var_decl.ast.mut_token).line_start;
const end = tree.tokenLocation(@truncate(u32, start), tree.lastToken(var_decl.ast.init_node)).line_end; const end = tree.tokenLocation(@truncate(u32, start), tree.lastToken(var_decl.ast.init_node)).line_end;
return tree.source[start..end]; return tree.source[start..end];
} }
// analysis.getContainerFieldSignature(handle.tree, field) // analysis.getContainerFieldSignature(handle.tree, field)
pub fn getContainerFieldSignature(tree: ast.Tree, field: *ast.full.ContainerField) []const u8 { pub fn getContainerFieldSignature(tree: ast.Tree, field: ast.full.ContainerField) []const u8 {
const start = tree.tokenLocation(0, field.ast.name_token).line_start; const start = tree.tokenLocation(0, field.ast.name_token).line_start;
const end = tree.tokenLocation(@truncate(u32, start), tree.lastToken(field.ast.value_expr)).line_start; const end = tree.tokenLocation(@truncate(u32, start), tree.lastToken(field.ast.value_expr)).line_start;
return tree.source[start..end]; return tree.source[start..end];
@ -430,7 +430,7 @@ fn resolveUnwrapErrorType(
return null; return null;
} }
fn isPtrType(tree: ast.Tree, node: ast.Node.Index) bool { pub fn isPtrType(tree: ast.Tree, node: ast.Node.Index) bool {
return switch (tree.nodes.items(.tag)[node]) { return switch (tree.nodes.items(.tag)[node]) {
.ptr_type, .ptr_type,
.ptr_type_aligned, .ptr_type_aligned,
@ -1807,7 +1807,7 @@ pub const DeclWithHandle = struct {
} }
}; };
fn containerField(tree: ast.Tree, node: ast.Node.Index) ?ast.full.ContainerField { pub fn containerField(tree: ast.Tree, node: ast.Node.Index) ?ast.full.ContainerField {
return switch (tree.nodes.items(.tag)[node]) { return switch (tree.nodes.items(.tag)[node]) {
.container_field => tree.containerField(node), .container_field => tree.containerField(node),
.container_field_init => tree.containerFieldInit(node), .container_field_init => tree.containerFieldInit(node),
@ -1816,6 +1816,16 @@ fn containerField(tree: ast.Tree, node: ast.Node.Index) ?ast.full.ContainerField
}; };
} }
pub fn ptrType(tree: ast.Tree, node: ast.Node.Index) ?ast.full.PtrType {
return switch (tree.nodes.items(.tag)[node]) {
.ptr_type => tree.ptrType(node),
.ptr_type_aligned => tree.ptrTypeAligned(node),
.ptr_type_bit_range => tree.ptrTypeBitRange(node),
.ptr_type_sentinel => tree.ptrTypeSentinel(node),
else => null,
};
}
fn findContainerScope(container_handle: NodeWithHandle) ?*Scope { fn findContainerScope(container_handle: NodeWithHandle) ?*Scope {
const container = container_handle.node; const container = container_handle.node;
const handle = container_handle.handle; const handle = container_handle.handle;
@ -2218,7 +2228,7 @@ fn nodeSourceRange(tree: ast.Tree, node: ast.Node.Index) SourceRange {
}; };
} }
fn isContainer(tag: ast.Node.Tag) bool { pub fn isContainer(tag: ast.Node.Tag) bool {
return switch (tag) { return switch (tag) {
.container_decl, .container_decl,
.container_decl_trailing, .container_decl_trailing,
@ -2265,7 +2275,7 @@ fn declMembers(tree: ast.Tree, tag: ast.Node.Tag, node_idx: ast.Node.Index) []co
/// Returns an `ast.full.VarDecl` for a given node index. /// Returns an `ast.full.VarDecl` for a given node index.
/// Returns null if the tag doesn't match /// Returns null if the tag doesn't match
fn varDecl(tree: ast.Tree, node_idx: ast.Node.Index) ?ast.full.VarDecl { pub fn varDecl(tree: ast.Tree, node_idx: ast.Node.Index) ?ast.full.VarDecl {
return switch (tree.nodes.items(.tag)[node_idx]) { return switch (tree.nodes.items(.tag)[node_idx]) {
.global_var_decl => tree.globalVarDecl(node_idx), .global_var_decl => tree.globalVarDecl(node_idx),
.local_var_decl => tree.localVarDecl(node_idx), .local_var_decl => tree.localVarDecl(node_idx),

View File

@ -332,6 +332,10 @@ fn nodeToCompletion(
) error{OutOfMemory}!void { ) error{OutOfMemory}!void {
const node = node_handle.node; const node = node_handle.node;
const handle = node_handle.handle; const handle = node_handle.handle;
const tree = handle.tree;
const node_tags = tree.nodes.items(.tag);
const datas = tree.nodes.items(.data);
const token_tags = tree.tokens.items(.tag);
const doc_kind: types.MarkupContent.Kind = if (client_capabilities.completion_doc_supports_md) const doc_kind: types.MarkupContent.Kind = if (client_capabilities.completion_doc_supports_md)
.Markdown .Markdown
@ -351,7 +355,7 @@ fn nodeToCompletion(
else else
null; null;
if (node.tag == .ErrorSetDecl or node.tag == .Root or node.tag == .ContainerDecl) { if (analysis.isContainer(node_tags[node])) {
const context = DeclToCompletionContext{ const context = DeclToCompletionContext{
.completions = list, .completions = list,
.config = &config, .config = &config,
@ -363,44 +367,43 @@ fn nodeToCompletion(
if (is_type_val) return; if (is_type_val) return;
switch (node.tag) { switch (node_tags[node]) {
.FnProto => { .fn_proto, .fn_proto_multi, .fn_proto_one, .fn_proto_multi, .fn_decl => {
const func = node.cast(std.zig.ast.Node.FnProto).?; var buf: [1]std.zig.ast.Node.Index = undefined;
if (func.getNameToken()) |name_token| { const func = analysis.fnProto(tree, node, &buf).?;
if (func.name_token) |name_token| {
const use_snippets = config.enable_snippets and client_capabilities.supports_snippets; const use_snippets = config.enable_snippets and client_capabilities.supports_snippets;
const insert_text = if (use_snippets) blk: { const insert_text = if (use_snippets) blk: {
// TODO Also check if we are dot accessing from a type val and dont skip in that case. // TODO Also check if we are dot accessing from a type val and dont skip in that case.
const skip_self_param = if (func.params_len > 0) param_check: { const skip_self_param = if (func.ast.params.len > 0) param_check: {
const in_container = analysis.innermostContainer(handle, handle.tree.token_locs[func.firstToken()].start); const in_container = analysis.innermostContainer(handle, tree.tokenLocation(0, func.ast.fn_token).line_start);
switch (func.paramsConst()[0].param_type) { var it = func.iterate(tree);
.type_expr => |type_node| { const param = it.next().?;
if (try analysis.resolveTypeOfNode(&document_store, arena, .{
.node = type_node,
.handle = handle,
})) |resolved_type| {
if (std.meta.eql(in_container, resolved_type))
break :param_check true;
}
if (type_node.castTag(.PtrType)) |ptr_type| { if (try analysis.resolveTypeOfNode(&document_store, arena, .{
if (try analysis.resolveTypeOfNode(&document_store, arena, .{ .node = param.type_expr,
.node = ptr_type.rhs, .handle = handle,
.handle = handle, })) |resolved_type| {
})) |resolved_prefix_op| { if (std.meta.eql(in_container, resolved_type))
if (std.meta.eql(in_container, resolved_prefix_op)) break :param_check true;
break :param_check true;
}
}
break :param_check false;
},
else => break :param_check false,
} }
if (analysis.isPtrType(tree, param.type_expr)) {
if (try analysis.resolveTypeOfNode(&document_store, arena, .{
.node = datas[param.type_expr].rhs,
.handle = handle,
})) |resolved_prefix_op| {
if (std.meta.eql(in_container, resolved_prefix_op))
break :param_check true;
}
}
break :param_check false;
} else false; } else false;
break :blk try analysis.getFunctionSnippet(&arena.allocator, handle.tree, func, skip_self_param); break :blk try analysis.getFunctionSnippet(&arena.allocator, tree, func, skip_self_param);
} else null; } else null;
const is_type_function = analysis.isTypeFunction(handle.tree, func); const is_type_function = analysis.isTypeFunction(handle.tree, func);
@ -415,9 +418,9 @@ fn nodeToCompletion(
}); });
} }
}, },
.VarDecl => { .global_var_decl, .local_var_decl, .aligned_var_decl, .simple_var_decl => {
const var_decl = node.cast(std.zig.ast.Node.VarDecl).?; const var_decl = analysis.varDecl(tree, node).?;
const is_const = handle.tree.token_ids[var_decl.mut_token] == .Keyword_const; const is_const = token_tags[var_decl.ast.mut_token] == .keyword_const;
if (try analysis.resolveVarDeclAlias(&document_store, arena, node_handle)) |result| { if (try analysis.resolveVarDeclAlias(&document_store, arena, node_handle)) |result| {
const context = DeclToCompletionContext{ const context = DeclToCompletionContext{
@ -430,57 +433,46 @@ fn nodeToCompletion(
} }
try list.append(.{ try list.append(.{
.label = handle.tree.tokenSlice(var_decl.name_token), .label = handle.tree.tokenSlice(var_decl.ast.mut_token + 1),
.kind = if (is_const) .Constant else .Variable, .kind = if (is_const) .Constant else .Variable,
.documentation = doc, .documentation = doc,
.detail = analysis.getVariableSignature(handle.tree, var_decl), .detail = analysis.getVariableSignature(tree, var_decl),
}); });
}, },
.ContainerField => { .container_field, .container_field_align, .container_field_init => {
const field = node.cast(std.zig.ast.Node.ContainerField).?; const field = analysis.containerField(tree, node).?;
try list.append(.{ try list.append(.{
.label = handle.tree.tokenSlice(field.name_token), .label = handle.tree.tokenSlice(field.ast.name_token),
.kind = .Field, .kind = .Field,
.documentation = doc, .documentation = doc,
.detail = analysis.getContainerFieldSignature(handle.tree, field), .detail = analysis.getContainerFieldSignature(handle.tree, field),
}); });
}, },
.SliceType => { .array_type, .array_type_sentinel => {
try list.append(.{
.label = "len",
.kind = .Field,
});
try list.append(.{
.label = "ptr",
.kind = .Field,
});
},
.ArrayType => {
try list.append(.{ try list.append(.{
.label = "len", .label = "len",
.kind = .Field, .kind = .Field,
}); });
}, },
.PtrType => { .ptr_type, .ptr_type_aligned, .ptr_type_bit_range, .ptr_type_sentinel => {
if (config.operator_completions) { const ptr_type = analysis.ptrType(tree, node).?;
try list.append(.{
.label = "*", switch (ptr_type.size) {
.kind = .Operator, .One, .C => if (config.operator_completions) {
}); try list.append(.{
.label = "*",
.kind = .Operator,
});
},
.Many, .Slice => return list.append(.{ .label = "len", .kind = .Field }),
} }
const ptr_type = node.castTag(.PtrType).?; if (unwrapped) |actual_type| {
if (ptr_type.rhs.castTag(.ArrayType) != null) {
try list.append(.{
.label = "len",
.kind = .Field,
});
} else if (unwrapped) |actual_type| {
try typeToCompletion(arena, list, .{ .original = actual_type }, orig_handle, config); try typeToCompletion(arena, list, .{ .original = actual_type }, orig_handle, config);
} }
return; return;
}, },
.OptionalType => { .optional_type => {
if (config.operator_completions) { if (config.operator_completions) {
try list.append(.{ try list.append(.{
.label = "?", .label = "?",
@ -489,7 +481,7 @@ fn nodeToCompletion(
} }
return; return;
}, },
.StringLiteral => { .string_literal => {
try list.append(.{ try list.append(.{
.label = "len", .label = "len",
.kind = .Field, .kind = .Field,
@ -500,7 +492,7 @@ fn nodeToCompletion(
.label = string, .label = string,
.kind = .Field, .kind = .Field,
.documentation = doc, .documentation = doc,
.detail = handle.tree.getNodeSource(node), .detail = tree.getNodeSource(node),
}); });
}, },
} }
@ -564,8 +556,13 @@ fn gotoDefinitionSymbol(id: types.RequestId, arena: *std.heap.ArenaAllocator, de
}); });
} }
fn hoverSymbol(id: types.RequestId, arena: *std.heap.ArenaAllocator, decl_handle: analysis.DeclWithHandle) (std.os.WriteError || error{OutOfMemory})!void { fn hoverSymbol(
id: types.RequestId,
arena: *std.heap.ArenaAllocator,
decl_handle: analysis.DeclWithHandle,
) (std.os.WriteError || error{OutOfMemory})!void {
const handle = decl_handle.handle; const handle = decl_handle.handle;
const tree = handle.tree;
const hover_kind: types.MarkupContent.Kind = if (client_capabilities.hover_supports_md) .Markdown else .PlainText; const hover_kind: types.MarkupContent.Kind = if (client_capabilities.hover_supports_md) .Markdown else .PlainText;
const md_string = switch (decl_handle.decl.*) { const md_string = switch (decl_handle.decl.*) {
@ -574,26 +571,20 @@ fn hoverSymbol(id: types.RequestId, arena: *std.heap.ArenaAllocator, decl_handle
return try hoverSymbol(id, arena, result); return try hoverSymbol(id, arena, result);
} }
const doc_str = if (try analysis.getDocComments(&arena.allocator, handle.tree, node, hover_kind)) |str| const doc_str = if (try analysis.getDocComments(&arena.allocator, tree, node, hover_kind)) |str|
str str
else else
""; "";
const signature_str = switch (node.tag) { var buf: [1]std.zig.ast.Node.Index = undefined;
.VarDecl => blk: { const signature_str = if (analysis.varDecl(tree, node)) |var_decl| blk: {
const var_decl = node.cast(std.zig.ast.Node.VarDecl).?; break :blk analysis.getVariableSignature(tree, var_decl);
break :blk analysis.getVariableSignature(handle.tree, var_decl); } else if (analysis.fnProto(tree, node, &buf)) |fn_proto| blk: {
}, break :blk analysis.getFunctionSignature(tree, fn_proto);
.FnProto => blk: { } else if (analysis.containerField(tree, node)) |field| blk: {
const fn_decl = node.cast(std.zig.ast.Node.FnProto).?; break :blk analysis.getContainerFieldSignature(tree, field);
break :blk analysis.getFunctionSignature(handle.tree, fn_decl); } else analysis.nodeToString(tree, node) orelse
}, return try respondGeneric(id, null_result_response);
.ContainerField => blk: {
const field = node.cast(std.zig.ast.Node.ContainerField).?;
break :blk analysis.getContainerFieldSignature(handle.tree, field);
},
else => analysis.nodeToString(handle.tree, node) orelse return try respondGeneric(id, null_result_response),
};
break :ast_node if (hover_kind == .Markdown) break :ast_node if (hover_kind == .Markdown)
try std.fmt.allocPrint(&arena.allocator, "```zig\n{s}\n```\n{s}", .{ signature_str, doc_str }) try std.fmt.allocPrint(&arena.allocator, "```zig\n{s}\n```\n{s}", .{ signature_str, doc_str })
@ -601,31 +592,38 @@ fn hoverSymbol(id: types.RequestId, arena: *std.heap.ArenaAllocator, decl_handle
try std.fmt.allocPrint(&arena.allocator, "{s}\n{s}", .{ signature_str, doc_str }); try std.fmt.allocPrint(&arena.allocator, "{s}\n{s}", .{ signature_str, doc_str });
}, },
.param_decl => |param| param_decl: { .param_decl => |param| param_decl: {
const doc_str = if (param.doc_comments) |doc_comments| const doc_str = if (param.first_doc_comment) |doc_comments|
try analysis.collectDocComments(&arena.allocator, handle.tree, doc_comments, hover_kind) try analysis.collectDocComments(&arena.allocator, handle.tree, doc_comments, hover_kind)
else else
""; "";
const signature_str = handle.tree.source[handle.tree.token_locs[param.firstToken()].start..handle.tree.token_locs[param.lastToken()].end]; const first_token = param.first_doc_comment orelse
param.comptime_noalias orelse
param.name_token orelse
param.anytype_ellipsis3 orelse
tree.firstToken(param.type_expr);
const last_token = tree.lastToken(param.type_expr);
const signature_str = tree.source[tree.tokenLocation(0, first_token).line_start..tree.tokenLocation(0, last_token).line_end];
break :param_decl if (hover_kind == .Markdown) break :param_decl if (hover_kind == .Markdown)
try std.fmt.allocPrint(&arena.allocator, "```zig\n{s}\n```\n{s}", .{ signature_str, doc_str }) try std.fmt.allocPrint(&arena.allocator, "```zig\n{s}\n```\n{s}", .{ signature_str, doc_str })
else else
try std.fmt.allocPrint(&arena.allocator, "{s}\n{s}", .{ signature_str, doc_str }); try std.fmt.allocPrint(&arena.allocator, "{s}\n{s}", .{ signature_str, doc_str });
}, },
.pointer_payload => |payload| if (hover_kind == .Markdown) .pointer_payload => |payload| if (hover_kind == .Markdown)
try std.fmt.allocPrint(&arena.allocator, "```zig\n{s}\n```", .{handle.tree.tokenSlice(payload.node.value_symbol.firstToken())}) try std.fmt.allocPrint(&arena.allocator, "```zig\n{s}\n```", .{tree.tokenSlice(payload.name)})
else else
try std.fmt.allocPrint(&arena.allocator, "{s}", .{handle.tree.tokenSlice(payload.node.value_symbol.firstToken())}), try std.fmt.allocPrint(&arena.allocator, "{s}", .{tree.tokenSlice(payload.name)}),
// .array_payload => |payload| if (hover_kind == .Markdown) // .array_payload => |payload| if (hover_kind == .Markdown)
// try std.fmt.allocPrint(&arena.allocator, "```zig\n{s}\n```", .{handle.tree.tokenSlice(payload.identifier.firstToken())}) // try std.fmt.allocPrint(&arena.allocator, "```zig\n{s}\n```", .{handle.tree.tokenSlice(payload.identifier.firstToken())})
// else // else
// try std.fmt.allocPrint(&arena.allocator, "{s}", .{handle.tree.tokenSlice(payload.identifier.firstToken())}), // try std.fmt.allocPrint(&arena.allocator, "{s}", .{handle.tree.tokenSlice(payload.identifier.firstToken())}),
.switch_payload => |payload| if (hover_kind == .Markdown) .switch_payload => |payload| if (hover_kind == .Markdown)
try std.fmt.allocPrint(&arena.allocator, "```zig\n{s}\n```", .{handle.tree.tokenSlice(payload.node.value_symbol.firstToken())}) try std.fmt.allocPrint(&arena.allocator, "```zig\n{s}\n```", .{tree.tokenSlice(payload.node)})
else else
try std.fmt.allocPrint(&arena.allocator, "{s}", .{handle.tree.tokenSlice(payload.node.value_symbol.firstToken())}), try std.fmt.allocPrint(&arena.allocator, "{s}", .{tree.tokenSlice(payload.node)}),
.label_decl => |label_decl| block: { .label_decl => |label_decl| block: {
const source = handle.tree.source[handle.tree.token_locs[label_decl.firstToken()].start..handle.tree.token_locs[label_decl.lastToken()].end]; const source = tree.tokenSlice(label_decl);
break :block if (hover_kind == .Markdown) break :block if (hover_kind == .Markdown)
try std.fmt.allocPrint(&arena.allocator, "```zig\n{s}\n```", .{source}) try std.fmt.allocPrint(&arena.allocator, "```zig\n{s}\n```", .{source})
else else

View File

@ -113,9 +113,9 @@ pub fn tokenLength(tree: std.zig.ast.Tree, token: std.zig.ast.TokenIndex, encodi
if (encoding == .utf8) if (encoding == .utf8)
return token_loc.line_end - token_loc.line_start; return token_loc.line_end - token_loc.line_start;
var i: usize = token_loc.start; var i: usize = token_loc.line_start;
var utf16_len: usize = 0; var utf16_len: usize = 0;
while (i < token_loc.end) { while (i < token_loc.line_end) {
const n = std.unicode.utf8ByteSequenceLength(tree.source[i]) catch unreachable; const n = std.unicode.utf8ByteSequenceLength(tree.source[i]) catch unreachable;
const codepoint = std.unicode.utf8Decode(tree.source[i .. i + n]) catch unreachable; const codepoint = std.unicode.utf8Decode(tree.source[i .. i + n]) catch unreachable;
if (codepoint < 0x10000) { if (codepoint < 0x10000) {

View File

@ -78,7 +78,7 @@ fn symbolReferencesInternal(
const node = node_handle.node; const node = node_handle.node;
const handle = node_handle.handle; const handle = node_handle.handle;
switch (node.tag) { switch (handle.tree.nodes.items(.tag)[node]) {
.ContainerDecl, .Root, .Block => { .ContainerDecl, .Root, .Block => {
var idx: usize = 0; var idx: usize = 0;
while (node.iterate(idx)) |child| : (idx += 1) { while (node.iterate(idx)) |child| : (idx += 1) {