Merge pull request #238 from Luukdegram/std-fixes

Update to Zig std's new AST format
This commit is contained in:
Auguste Rame 2021-03-12 12:57:03 -05:00 committed by GitHub
commit 2b488347ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 3284 additions and 1943 deletions

View File

@ -63,6 +63,7 @@ The following options are currently available.
| `build_runner_cache_path` | `?[]const u8` | `null` | Path to a directroy that will be used as zig's cache when running `zig run build_runner.zig ...`. `null` is equivalent to `${KnownFloders.Cache}/zls` | | `build_runner_cache_path` | `?[]const u8` | `null` | Path to a directroy that will be used as zig's cache when running `zig run build_runner.zig ...`. `null` is equivalent to `${KnownFloders.Cache}/zls` |
| `enable_semantic_tokens` | `bool` | `true` | Enables semantic token support when the client also supports it. | | `enable_semantic_tokens` | `bool` | `true` | Enables semantic token support when the client also supports it. |
| `operator_completions` | `bool` | `true` | Enables `*` and `?` operators in completion lists. | | `operator_completions` | `bool` | `true` | Enables `*` and `?` operators in completion lists. |
| `skip_std_references` | `bool` | `false` | When true, skips searching for references in std. Improves lookup speed for functions in user's code. Renaming and go-to-definition will continue to work as is.
## Features ## Features

File diff suppressed because it is too large Load Diff

View File

@ -25,3 +25,7 @@ enable_semantic_tokens: bool = true,
/// Whether to enable `*` and `?` operators in completion lists /// Whether to enable `*` and `?` operators in completion lists
operator_completions: bool = true, operator_completions: bool = true,
/// Skips references to std. This will improve lookup speeds.
/// Going to definition however will continue to work
skip_std_references: bool = false,

View File

@ -22,7 +22,7 @@ pub const Handle = struct {
document: types.TextDocument, document: types.TextDocument,
count: usize, count: usize,
import_uris: std.ArrayList([]const u8), import_uris: std.ArrayList([]const u8),
tree: *std.zig.ast.Tree, tree: std.zig.ast.Tree,
document_scope: analysis.DocumentScope, document_scope: analysis.DocumentScope,
associated_build_file: ?*BuildFile, associated_build_file: ?*BuildFile,
@ -143,8 +143,8 @@ fn newDocument(self: *DocumentStore, uri: []const u8, text: []u8) anyerror!*Hand
var handle = try self.allocator.create(Handle); var handle = try self.allocator.create(Handle);
errdefer self.allocator.destroy(handle); errdefer self.allocator.destroy(handle);
const tree = try std.zig.parse(self.allocator, text); var tree = try std.zig.parse(self.allocator, text);
errdefer tree.deinit(); errdefer tree.deinit(self.allocator);
const document_scope = try analysis.makeDocumentScope(self.allocator, tree); const document_scope = try analysis.makeDocumentScope(self.allocator, tree);
errdefer document_scope.deinit(self.allocator); errdefer document_scope.deinit(self.allocator);
@ -326,7 +326,7 @@ fn decrementCount(self: *DocumentStore, uri: []const u8) void {
self.decrementBuildFileRefs(build_file); self.decrementBuildFileRefs(build_file);
} }
entry.value.tree.deinit(); entry.value.tree.deinit(self.allocator);
self.allocator.free(entry.value.document.mem); self.allocator.free(entry.value.document.mem);
for (entry.value.import_uris.items) |import_uri| { for (entry.value.import_uris.items) |import_uri| {
@ -354,7 +354,7 @@ pub fn getHandle(self: *DocumentStore, uri: []const u8) ?*Handle {
// Check if the document text is now sane, move it to sane_text if so. // Check if the document text is now sane, move it to sane_text if so.
fn refreshDocument(self: *DocumentStore, handle: *Handle, zig_lib_path: ?[]const u8) !void { fn refreshDocument(self: *DocumentStore, handle: *Handle, zig_lib_path: ?[]const u8) !void {
log.debug("New text for document {s}", .{handle.uri()}); log.debug("New text for document {s}", .{handle.uri()});
handle.tree.deinit(); handle.tree.deinit(self.allocator);
handle.tree = try std.zig.parse(self.allocator, handle.document.text); handle.tree = try std.zig.parse(self.allocator, handle.document.text);
handle.document_scope.deinit(self.allocator); handle.document_scope.deinit(self.allocator);
@ -613,7 +613,7 @@ pub fn deinit(self: *DocumentStore) void {
var entry_iterator = self.handles.iterator(); var entry_iterator = self.handles.iterator();
while (entry_iterator.next()) |entry| { while (entry_iterator.next()) |entry| {
entry.value.document_scope.deinit(self.allocator); entry.value.document_scope.deinit(self.allocator);
entry.value.tree.deinit(); entry.value.tree.deinit(self.allocator);
self.allocator.free(entry.value.document.mem); self.allocator.free(entry.value.document.mem);
for (entry.value.import_uris.items) |uri| { for (entry.value.import_uris.items) |uri| {

View File

@ -199,8 +199,8 @@ fn publishDiagnostics(arena: *std.heap.ArenaAllocator, handle: DocumentStore.Han
var diagnostics = std.ArrayList(types.Diagnostic).init(&arena.allocator); var diagnostics = std.ArrayList(types.Diagnostic).init(&arena.allocator);
for (tree.errors) |*err| { for (tree.errors) |err| {
const loc = tree.tokenLocation(0, err.loc()); const loc = tree.tokenLocation(0, err.token);
var mem_buffer: [256]u8 = undefined; var mem_buffer: [256]u8 = undefined;
var fbs = std.io.fixedBufferStream(&mem_buffer); var fbs = std.io.fixedBufferStream(&mem_buffer);
@ -209,7 +209,7 @@ fn publishDiagnostics(arena: *std.heap.ArenaAllocator, handle: DocumentStore.Han
try diagnostics.append(.{ try diagnostics.append(.{
.range = astLocationToRange(loc), .range = astLocationToRange(loc),
.severity = .Error, .severity = .Error,
.code = @tagName(err.*), .code = @tagName(err.tag),
.source = "zls", .source = "zls",
.message = try std.mem.dupe(&arena.allocator, u8, fbs.getWritten()), .message = try std.mem.dupe(&arena.allocator, u8, fbs.getWritten()),
// .relatedInformation = undefined // .relatedInformation = undefined
@ -217,16 +217,21 @@ fn publishDiagnostics(arena: *std.heap.ArenaAllocator, handle: DocumentStore.Han
} }
if (tree.errors.len == 0) { if (tree.errors.len == 0) {
for (tree.root_node.decls()) |decl| { for (tree.rootDecls()) |decl_idx| {
switch (decl.tag) { const decl = tree.nodes.items(.tag)[decl_idx];
.FnProto => blk: { switch (decl) {
const func = decl.cast(std.zig.ast.Node.FnProto).?; .fn_proto,
const is_extern = func.getExternExportInlineToken() != null; .fn_proto_multi,
if (is_extern) .fn_proto_one,
break :blk; .fn_proto_simple,
.fn_decl,
=> blk: {
var buf: [1]std.zig.ast.Node.Index = undefined;
const func = analysis.fnProto(tree, decl_idx, &buf).?;
if (func.extern_export_token != null) break :blk;
if (config.warn_style) { if (config.warn_style) {
if (func.getNameToken()) |name_token| { if (func.name_token) |name_token| {
const loc = tree.tokenLocation(0, name_token); const loc = tree.tokenLocation(0, name_token);
const is_type_function = analysis.isTypeFunction(tree, func); const is_type_function = analysis.isTypeFunction(tree, func);
@ -331,6 +336,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
@ -350,7 +359,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,
@ -362,30 +371,39 @@ fn nodeToCompletion(
if (is_type_val) return; if (is_type_val) return;
switch (node.tag) { switch (node_tags[node]) {
.FnProto => { .fn_proto,
const func = node.cast(std.zig.ast.Node.FnProto).?; .fn_proto_multi,
if (func.getNameToken()) |name_token| { .fn_proto_one,
.fn_proto_simple,
.fn_decl,
=> {
var buf: [1]std.zig.ast.Node.Index = undefined;
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.tokens.items(.start)[func.ast.fn_token]);
var it = func.iterate(tree);
const param = it.next().?;
if (param.type_expr == 0) break :param_check false;
switch (func.paramsConst()[0].param_type) {
.type_expr => |type_node| {
if (try analysis.resolveTypeOfNode(&document_store, arena, .{ if (try analysis.resolveTypeOfNode(&document_store, arena, .{
.node = type_node, .node = param.type_expr,
.handle = handle, .handle = handle,
})) |resolved_type| { })) |resolved_type| {
if (std.meta.eql(in_container, resolved_type)) if (std.meta.eql(in_container, resolved_type))
break :param_check true; break :param_check true;
} }
if (type_node.castTag(.PtrType)) |ptr_type| { if (analysis.isPtrType(tree, param.type_expr)) {
if (try analysis.resolveTypeOfNode(&document_store, arena, .{ if (try analysis.resolveTypeOfNode(&document_store, arena, .{
.node = ptr_type.rhs, .node = datas[param.type_expr].rhs,
.handle = handle, .handle = handle,
})) |resolved_prefix_op| { })) |resolved_prefix_op| {
if (std.meta.eql(in_container, resolved_prefix_op)) if (std.meta.eql(in_container, resolved_prefix_op))
@ -394,15 +412,10 @@ fn nodeToCompletion(
} }
break :param_check false; break :param_check false;
}, } else false;
else => break :param_check 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 } else null;
null;
const is_type_function = analysis.isTypeFunction(handle.tree, func); const is_type_function = analysis.isTypeFunction(handle.tree, func);
@ -416,9 +429,13 @@ fn nodeToCompletion(
}); });
} }
}, },
.VarDecl => { .global_var_decl,
const var_decl = node.cast(std.zig.ast.Node.VarDecl).?; .local_var_decl,
const is_const = handle.tree.token_ids[var_decl.mut_token] == .Keyword_const; .aligned_var_decl,
.simple_var_decl,
=> {
const var_decl = analysis.varDecl(tree, node).?;
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{
@ -431,57 +448,59 @@ 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,
const field = node.cast(std.zig.ast.Node.ContainerField).?; .container_field_align,
.container_field_init,
=> {
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,
try list.append(.{ .array_type_sentinel,
.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,
if (config.operator_completions) { .ptr_type_aligned,
.ptr_type_bit_range,
.ptr_type_sentinel,
=> {
const ptr_type = analysis.ptrType(tree, node).?;
switch (ptr_type.size) {
.One, .C, .Many => if (config.operator_completions) {
try list.append(.{ try list.append(.{
.label = "*", .label = "*",
.kind = .Operator, .kind = .Operator,
}); });
},
.Slice => {
try list.append(.{ .label = "ptr", .kind = .Field });
try list.append(.{ .label = "len", .kind = .Field });
return;
},
} }
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 = "?",
@ -490,18 +509,18 @@ fn nodeToCompletion(
} }
return; return;
}, },
.StringLiteral => { .string_literal => {
try list.append(.{ try list.append(.{
.label = "len", .label = "len",
.kind = .Field, .kind = .Field,
}); });
}, },
else => if (analysis.nodeToString(handle.tree, node)) |string| { else => if (analysis.nodeToString(tree, node)) |string| {
try list.append(.{ try list.append(.{
.label = string, .label = string,
.kind = .Field, .kind = .Field,
.documentation = doc, .documentation = doc,
.detail = handle.tree.getNodeSource(node), .detail = tree.getNodeSource(node),
}); });
}, },
} }
@ -540,7 +559,7 @@ fn gotoDefinitionSymbol(id: types.RequestId, arena: *std.heap.ArenaAllocator, de
const name_token = analysis.getDeclNameToken(handle.tree, node) orelse const name_token = analysis.getDeclNameToken(handle.tree, node) orelse
return try respondGeneric(id, null_result_response); return try respondGeneric(id, null_result_response);
break :block offsets.tokenRelativeLocation(handle.tree, 0, name_token, offset_encoding) catch return; break :block offsets.tokenRelativeLocation(handle.tree, 0, handle.tree.tokens.items(.start)[name_token], offset_encoding) catch return;
}, },
else => decl_handle.location(offset_encoding) catch return, else => decl_handle.location(offset_encoding) catch return,
}; };
@ -565,8 +584,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.*) {
@ -575,26 +599,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 })
@ -602,31 +620,43 @@ 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
tree.firstToken(param.type_expr); // extern fn
const last_token = param.anytype_ellipsis3 orelse tree.lastToken(param.type_expr);
const start = offsets.tokenLocation(tree, first_token).start;
const end = offsets.tokenLocation(tree, last_token).end;
const signature_str = tree.source[start..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)})
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)}),
.array_index => |payload| if (hover_kind == .Markdown)
try std.fmt.allocPrint(&arena.allocator, "```zig\n{s}\n```", .{handle.tree.tokenSlice(payload)})
else
try std.fmt.allocPrint(&arena.allocator, "{s}", .{handle.tree.tokenSlice(payload)}),
.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
@ -752,7 +782,7 @@ fn hoverDefinitionFieldAccess(
fn gotoDefinitionString(arena: *std.heap.ArenaAllocator, id: types.RequestId, pos_index: usize, handle: *DocumentStore.Handle, config: Config) !void { fn gotoDefinitionString(arena: *std.heap.ArenaAllocator, id: types.RequestId, pos_index: usize, handle: *DocumentStore.Handle, config: Config) !void {
const tree = handle.tree; const tree = handle.tree;
const import_str = analysis.getImportStr(tree, pos_index) orelse return try respondGeneric(id, null_result_response); const import_str = analysis.getImportStr(tree, 0, pos_index) orelse return try respondGeneric(id, null_result_response);
const uri = (try document_store.uriFromImportStr( const uri = (try document_store.uriFromImportStr(
&arena.allocator, &arena.allocator,
handle.*, handle.*,
@ -820,10 +850,26 @@ fn renameDefinitionLabel(arena: *std.heap.ArenaAllocator, id: types.RequestId, h
}); });
} }
fn referencesDefinitionGlobal(arena: *std.heap.ArenaAllocator, id: types.RequestId, handle: *DocumentStore.Handle, pos_index: usize, include_decl: bool) !void { fn referencesDefinitionGlobal(
arena: *std.heap.ArenaAllocator,
id: types.RequestId,
handle: *DocumentStore.Handle,
pos_index: usize,
include_decl: bool,
skip_std_references: bool,
) !void {
const decl = (try getSymbolGlobal(arena, pos_index, handle)) orelse return try respondGeneric(id, null_result_response); const decl = (try getSymbolGlobal(arena, pos_index, handle)) orelse return try respondGeneric(id, null_result_response);
var locs = std.ArrayList(types.Location).init(&arena.allocator); var locs = std.ArrayList(types.Location).init(&arena.allocator);
try references.symbolReferences(arena, &document_store, decl, offset_encoding, include_decl, &locs, std.ArrayList(types.Location).append); try references.symbolReferences(
arena,
&document_store,
decl,
offset_encoding,
include_decl,
&locs,
std.ArrayList(types.Location).append,
skip_std_references,
);
try send(arena, types.Response{ try send(arena, types.Response{
.id = id, .id = id,
.result = .{ .Locations = locs.items }, .result = .{ .Locations = locs.items },
@ -841,7 +887,7 @@ fn referencesDefinitionFieldAccess(
) !void { ) !void {
const decl = (try getSymbolFieldAccess(handle, arena, position, range, config)) orelse return try respondGeneric(id, null_result_response); const decl = (try getSymbolFieldAccess(handle, arena, position, range, config)) orelse return try respondGeneric(id, null_result_response);
var locs = std.ArrayList(types.Location).init(&arena.allocator); var locs = std.ArrayList(types.Location).init(&arena.allocator);
try references.symbolReferences(arena, &document_store, decl, offset_encoding, include_decl, &locs, std.ArrayList(types.Location).append); try references.symbolReferences(arena, &document_store, decl, offset_encoding, include_decl, &locs, std.ArrayList(types.Location).append, config.skip_std_references);
try send(arena, types.Response{ try send(arena, types.Response{
.id = id, .id = id,
.result = .{ .Locations = locs.items }, .result = .{ .Locations = locs.items },
@ -865,14 +911,30 @@ const DeclToCompletionContext = struct {
orig_handle: *DocumentStore.Handle, orig_handle: *DocumentStore.Handle,
}; };
fn hasComment(tree: ast.Tree, start_token: ast.TokenIndex, end_token: ast.TokenIndex) bool {
const token_starts = tree.tokens.items(.start);
const start = token_starts[start_token];
const end = token_starts[end_token];
return std.mem.indexOf(u8, tree.source[start..end], "//") != null;
}
fn declToCompletion(context: DeclToCompletionContext, decl_handle: analysis.DeclWithHandle) !void { fn declToCompletion(context: DeclToCompletionContext, decl_handle: analysis.DeclWithHandle) !void {
const tree = decl_handle.handle.tree; const tree = decl_handle.handle.tree;
switch (decl_handle.decl.*) { switch (decl_handle.decl.*) {
.ast_node => |node| try nodeToCompletion(context.arena, context.completions, .{ .node = node, .handle = decl_handle.handle }, null, context.orig_handle, false, context.config.*), .ast_node => |node| try nodeToCompletion(
context.arena,
context.completions,
.{ .node = node, .handle = decl_handle.handle },
null,
context.orig_handle,
false,
context.config.*,
),
.param_decl => |param| { .param_decl => |param| {
const doc_kind: types.MarkupContent.Kind = if (client_capabilities.completion_doc_supports_md) .Markdown else .PlainText; const doc_kind: types.MarkupContent.Kind = if (client_capabilities.completion_doc_supports_md) .Markdown else .PlainText;
const doc = if (param.doc_comments) |doc_comments| const doc = if (param.first_doc_comment) |doc_comments|
types.MarkupContent{ types.MarkupContent{
.kind = doc_kind, .kind = doc_kind,
.value = try analysis.collectDocComments(&context.arena.allocator, tree, doc_comments, doc_kind), .value = try analysis.collectDocComments(&context.arena.allocator, tree, doc_comments, doc_kind),
@ -880,34 +942,46 @@ fn declToCompletion(context: DeclToCompletionContext, decl_handle: analysis.Decl
else else
null; null;
const first_token = param.first_doc_comment orelse
param.comptime_noalias orelse
param.name_token orelse
tree.firstToken(param.type_expr);
const last_token = param.anytype_ellipsis3 orelse tree.lastToken(param.type_expr);
try context.completions.append(.{ try context.completions.append(.{
.label = tree.tokenSlice(param.name_token.?), .label = tree.tokenSlice(param.name_token.?),
.kind = .Constant, .kind = .Constant,
.documentation = doc, .documentation = doc,
.detail = tree.source[tree.token_locs[param.firstToken()].start..tree.token_locs[param.lastToken()].end], .detail = tree.source[offsets.tokenLocation(tree, first_token).start..offsets.tokenLocation(tree, last_token).end],
}); });
}, },
.pointer_payload => |payload| { .pointer_payload => |payload| {
try context.completions.append(.{ try context.completions.append(.{
.label = tree.tokenSlice(payload.node.value_symbol.firstToken()), .label = tree.tokenSlice(payload.name),
.kind = .Variable, .kind = .Variable,
}); });
}, },
.array_payload => |payload| { .array_payload => |payload| {
try context.completions.append(.{ try context.completions.append(.{
.label = tree.tokenSlice(payload.identifier.firstToken()), .label = tree.tokenSlice(payload.identifier),
.kind = .Variable,
});
},
.array_index => |payload| {
try context.completions.append(.{
.label = tree.tokenSlice(payload),
.kind = .Variable, .kind = .Variable,
}); });
}, },
.switch_payload => |payload| { .switch_payload => |payload| {
try context.completions.append(.{ try context.completions.append(.{
.label = tree.tokenSlice(payload.node.value_symbol.firstToken()), .label = tree.tokenSlice(payload.node),
.kind = .Variable, .kind = .Variable,
}); });
}, },
.label_decl => |label_decl| { .label_decl => |label_decl| {
try context.completions.append(.{ try context.completions.append(.{
.label = tree.tokenSlice(label_decl.firstToken()), .label = tree.tokenSlice(label_decl),
.kind = .Variable, .kind = .Variable,
}); });
}, },
@ -1223,7 +1297,6 @@ fn completionHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req:
if (req.params.position.character >= 0) { if (req.params.position.character >= 0) {
const doc_position = try offsets.documentPosition(handle.document, req.params.position, offset_encoding); 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 pos_context = try analysis.documentPositionContext(arena, handle.document, doc_position);
const use_snippets = config.enable_snippets and client_capabilities.supports_snippets; const use_snippets = config.enable_snippets and client_capabilities.supports_snippets;
switch (pos_context) { switch (pos_context) {
.builtin => try completeBuiltin(arena, id, config), .builtin => try completeBuiltin(arena, id, config),
@ -1301,7 +1374,6 @@ fn hoverHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: reque
if (req.params.position.character >= 0) { if (req.params.position.character >= 0) {
const doc_position = try offsets.documentPosition(handle.document, req.params.position, offset_encoding); 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 pos_context = try analysis.documentPositionContext(arena, handle.document, doc_position);
switch (pos_context) { switch (pos_context) {
.builtin => try hoverDefinitionBuiltin(arena, id, doc_position.absolute_index, handle), .builtin => try hoverDefinitionBuiltin(arena, id, doc_position.absolute_index, handle),
.var_access => try hoverDefinitionGlobal(arena, id, doc_position.absolute_index, handle, config), .var_access => try hoverDefinitionGlobal(arena, id, doc_position.absolute_index, handle, config),
@ -1398,7 +1470,7 @@ fn referencesHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req:
const include_decl = req.params.context.includeDeclaration; const include_decl = req.params.context.includeDeclaration;
switch (pos_context) { switch (pos_context) {
.var_access => try referencesDefinitionGlobal(arena, id, handle, doc_position.absolute_index, include_decl), .var_access => try referencesDefinitionGlobal(arena, id, handle, doc_position.absolute_index, include_decl, config.skip_std_references),
.field_access => |range| try referencesDefinitionFieldAccess(arena, id, handle, doc_position, range, include_decl, config), .field_access => |range| try referencesDefinitionFieldAccess(arena, id, handle, doc_position, range, include_decl, config),
.label => try referencesDefinitionLabel(arena, id, handle, doc_position.absolute_index, include_decl), .label => try referencesDefinitionLabel(arena, id, handle, doc_position.absolute_index, include_decl),
else => try respondGeneric(id, null_result_response), else => try respondGeneric(id, null_result_response),

View File

@ -1,5 +1,6 @@
const std = @import("std"); const std = @import("std");
const types = @import("types.zig"); const types = @import("types.zig");
const ast = std.zig.ast;
pub const Encoding = enum { pub const Encoding = enum {
utf8, utf8,
@ -70,15 +71,15 @@ pub const TokenLocation = struct {
} }
}; };
pub fn tokenRelativeLocation(tree: *std.zig.ast.Tree, start_index: usize, token: std.zig.ast.TokenIndex, encoding: Encoding) !TokenLocation { pub fn tokenRelativeLocation(tree: ast.Tree, start_index: usize, next_token_index: usize, encoding: Encoding) !TokenLocation {
const token_loc = tree.token_locs[token]; const start = next_token_index;
var loc = TokenLocation{ var loc = TokenLocation{
.line = 0, .line = 0,
.column = 0, .column = 0,
.offset = 0, .offset = 0,
}; };
const token_start = token_loc.start; const token_start = start;
const source = tree.source[start_index..]; const source = tree.source[start_index..];
var i: usize = 0; var i: usize = 0;
while (i + start_index < token_start) { while (i + start_index < token_start) {
@ -108,8 +109,8 @@ pub fn tokenRelativeLocation(tree: *std.zig.ast.Tree, start_index: usize, token:
} }
/// Asserts the token is comprised of valid utf8 /// Asserts the token is comprised of valid utf8
pub fn tokenLength(tree: *std.zig.ast.Tree, token: std.zig.ast.TokenIndex, encoding: Encoding) usize { pub fn tokenLength(tree: ast.Tree, token: ast.TokenIndex, encoding: Encoding) usize {
const token_loc = tree.token_locs[token]; const token_loc = tokenLocation(tree, token);
if (encoding == .utf8) if (encoding == .utf8)
return token_loc.end - token_loc.start; return token_loc.end - token_loc.start;
@ -128,6 +129,28 @@ pub fn tokenLength(tree: *std.zig.ast.Tree, token: std.zig.ast.TokenIndex, encod
return utf16_len; return utf16_len;
} }
/// Token location inside source
pub const Loc = struct {
start: usize,
end: usize,
};
pub fn tokenLocation(tree: ast.Tree, token_index: ast.TokenIndex) Loc {
const start = tree.tokens.items(.start)[token_index];
const tag = tree.tokens.items(.tag)[token_index];
// For some tokens, re-tokenization is needed to find the end.
var tokenizer: std.zig.Tokenizer = .{
.buffer = tree.source,
.index = start,
.pending_invalid_token = null,
};
const token = tokenizer.next();
std.debug.assert(token.tag == tag);
return .{ .start = token.loc.start, .end = token.loc.end };
}
pub fn documentRange(doc: types.TextDocument, encoding: Encoding) !types.Range { pub fn documentRange(doc: types.TextDocument, encoding: Encoding) !types.Range {
var line_idx: i64 = 0; var line_idx: i64 = 0;
var curr_line: []const u8 = doc.text; var curr_line: []const u8 = doc.text;

View File

@ -14,7 +14,7 @@ fn tokenReference(
context: anytype, context: anytype,
comptime handler: anytype, comptime handler: anytype,
) !void { ) !void {
const loc = offsets.tokenRelativeLocation(handle.tree, 0, tok, encoding) catch return; const loc = offsets.tokenRelativeLocation(handle.tree, 0, handle.tree.tokens.items(.start)[tok], encoding) catch return;
try handler(context, types.Location{ try handler(context, types.Location{
.uri = handle.uri(), .uri = handle.uri(),
.range = .{ .range = .{
@ -40,11 +40,13 @@ pub fn labelReferences(
) !void { ) !void {
std.debug.assert(decl.decl.* == .label_decl); std.debug.assert(decl.decl.* == .label_decl);
const handle = decl.handle; const handle = decl.handle;
const tree = handle.tree;
const token_tags = tree.tokens.items(.tag);
// Find while / for / block from label -> iterate over children nodes, find break and continues, change their labels if they match. // Find while / for / block from label -> iterate over children nodes, find break and continues, change their labels if they match.
// This case can be implemented just by scanning tokens. // This case can be implemented just by scanning tokens.
const first_tok = decl.decl.label_decl.firstToken(); const first_tok = tree.firstToken(decl.decl.label_decl);
const last_tok = decl.decl.label_decl.lastToken(); const last_tok = tree.firstToken(decl.decl.label_decl);
if (include_decl) { if (include_decl) {
// The first token is always going to be the label // The first token is always going to be the label
@ -53,11 +55,11 @@ pub fn labelReferences(
var curr_tok = first_tok + 1; var curr_tok = first_tok + 1;
while (curr_tok < last_tok - 2) : (curr_tok += 1) { while (curr_tok < last_tok - 2) : (curr_tok += 1) {
const curr_id = handle.tree.token_ids[curr_tok]; const curr_id = token_tags[curr_tok];
if ((curr_id == .Keyword_break or curr_id == .Keyword_continue) and handle.tree.token_ids[curr_tok + 1] == .Colon and if ((curr_id == .keyword_break or curr_id == .keyword_continue) and token_tags[curr_tok + 1] == .colon and
handle.tree.token_ids[curr_tok + 2] == .Identifier) token_tags[curr_tok + 2] == .identifier)
{ {
if (std.mem.eql(u8, handle.tree.tokenSlice(curr_tok + 2), handle.tree.tokenSlice(first_tok))) { if (std.mem.eql(u8, tree.tokenSlice(curr_tok + 2), tree.tokenSlice(first_tok))) {
try tokenReference(handle, first_tok, encoding, context, handler); try tokenReference(handle, first_tok, encoding, context, handler);
} }
} }
@ -75,263 +77,377 @@ fn symbolReferencesInternal(
) 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;
if (node > tree.nodes.len) return;
const node_tags = tree.nodes.items(.tag);
const datas = tree.nodes.items(.data);
const main_tokens = tree.nodes.items(.main_token);
const starts = tree.tokens.items(.start);
switch (node.tag) { switch (node_tags[node]) {
.ContainerDecl, .Root, .Block => { .block, .block_semicolon, .block_two, .block_two_semicolon => {
var idx: usize = 0; const statements: []const ast.Node.Index = switch (node_tags[node]) {
while (node.iterate(idx)) |child| : (idx += 1) { .block, .block_semicolon => tree.extra_data[datas[node].lhs..datas[node].rhs],
try symbolReferencesInternal(arena, store, .{ .node = child, .handle = handle }, decl, encoding, context, handler); .block_two, .block_two_semicolon => blk: {
const statements = &[_]ast.Node.Index{ datas[node].lhs, datas[node].rhs };
const len: usize = if (datas[node].lhs == 0)
@as(usize, 0)
else if (datas[node].rhs == 0)
@as(usize, 1)
else
@as(usize, 2);
break :blk statements[0..len];
},
else => unreachable,
};
for (statements) |stmt|
try symbolReferencesInternal(arena, store, .{ .node = stmt, .handle = handle }, decl, encoding, context, handler);
},
.container_decl,
.container_decl_trailing,
.container_decl_arg,
.container_decl_arg_trailing,
.container_decl_two,
.container_decl_two_trailing,
.tagged_union,
.tagged_union_trailing,
.tagged_union_two,
.tagged_union_two_trailing,
.tagged_union_enum_tag,
.tagged_union_enum_tag_trailing,
.root,
.error_set_decl,
=> {
var buf: [2]ast.Node.Index = undefined;
for (analysis.declMembers(tree, node_tags[node], node, &buf)) |member|
try symbolReferencesInternal(arena, store, .{ .node = member, .handle = handle }, decl, encoding, context, handler);
},
.global_var_decl,
.local_var_decl,
.simple_var_decl,
.aligned_var_decl,
=> {
const var_decl = analysis.varDecl(tree, node).?;
if (var_decl.ast.type_node != 0) {
try symbolReferencesInternal(arena, store, .{ .node = var_decl.ast.type_node, .handle = handle }, decl, encoding, context, handler);
}
if (var_decl.ast.init_node != 0) {
try symbolReferencesInternal(arena, store, .{ .node = var_decl.ast.init_node, .handle = handle }, decl, encoding, context, handler);
} }
}, },
.VarDecl => { .@"usingnamespace" => {
const var_decl = node.cast(ast.Node.VarDecl).?; try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
if (var_decl.getTypeNode()) |type_node| { },
try symbolReferencesInternal(arena, store, .{ .node = type_node, .handle = handle }, decl, encoding, context, handler); .container_field,
.container_field_align,
.container_field_init,
=> {
const field = analysis.containerField(tree, node).?;
if (field.ast.type_expr != 0) {
try symbolReferencesInternal(arena, store, .{ .node = field.ast.type_expr, .handle = handle }, decl, encoding, context, handler);
} }
if (var_decl.getInitNode()) |init_node| { if (field.ast.value_expr != 0) {
try symbolReferencesInternal(arena, store, .{ .node = init_node, .handle = handle }, decl, encoding, context, handler); try symbolReferencesInternal(arena, store, .{ .node = field.ast.value_expr, .handle = handle }, decl, encoding, context, handler);
} }
}, },
.Use => { .identifier => {
const use = node.cast(ast.Node.Use).?; if (try analysis.lookupSymbolGlobal(store, arena, handle, tree.getNodeSource(node), starts[main_tokens[node]])) |child| {
try symbolReferencesInternal(arena, store, .{ .node = use.expr, .handle = handle }, decl, encoding, context, handler);
},
.ContainerField => {
const field = node.cast(ast.Node.ContainerField).?;
if (field.type_expr) |type_node| {
try symbolReferencesInternal(arena, store, .{ .node = type_node, .handle = handle }, decl, encoding, context, handler);
}
if (field.value_expr) |init_node| {
try symbolReferencesInternal(arena, store, .{ .node = init_node, .handle = handle }, decl, encoding, context, handler);
}
},
.Identifier => {
if (try analysis.lookupSymbolGlobal(store, arena, handle, handle.tree.getNodeSource(node), handle.tree.token_locs[node.firstToken()].start)) |child| {
if (std.meta.eql(decl, child)) { if (std.meta.eql(decl, child)) {
try tokenReference(handle, node.firstToken(), encoding, context, handler); try tokenReference(handle, main_tokens[node], encoding, context, handler);
} }
} }
}, },
.FnProto => { .fn_proto,
const fn_proto = node.cast(ast.Node.FnProto).?; .fn_proto_multi,
for (fn_proto.paramsConst()) |param| { .fn_proto_one,
switch (param.param_type) { .fn_proto_simple,
.type_expr => |type_node| { .fn_decl,
try symbolReferencesInternal(arena, store, .{ .node = type_node, .handle = handle }, decl, encoding, context, handler); => {
}, var buf: [1]ast.Node.Index = undefined;
else => {}, const fn_proto = analysis.fnProto(tree, node, &buf).?;
var it = fn_proto.iterate(tree);
while (it.next()) |param| {
if (param.type_expr != 0)
try symbolReferencesInternal(arena, store, .{ .node = param.type_expr, .handle = handle }, decl, encoding, context, handler);
} }
if (fn_proto.ast.return_type != 0) {
try symbolReferencesInternal(arena, store, .{ .node = fn_proto.ast.return_type, .handle = handle }, decl, encoding, context, handler);
} }
switch (fn_proto.return_type) { if (fn_proto.ast.align_expr != 0) {
.Explicit, .InferErrorSet => |type_node| { try symbolReferencesInternal(arena, store, .{ .node = fn_proto.ast.align_expr, .handle = handle }, decl, encoding, context, handler);
try symbolReferencesInternal(arena, store, .{ .node = type_node, .handle = handle }, decl, encoding, context, handler);
},
else => {},
} }
if (fn_proto.getAlignExpr()) |align_expr| { if (fn_proto.ast.section_expr != 0) {
try symbolReferencesInternal(arena, store, .{ .node = align_expr, .handle = handle }, decl, encoding, context, handler); try symbolReferencesInternal(arena, store, .{ .node = fn_proto.ast.section_expr, .handle = handle }, decl, encoding, context, handler);
} }
if (fn_proto.getSectionExpr()) |section_expr| { if (fn_proto.ast.callconv_expr != 0) {
try symbolReferencesInternal(arena, store, .{ .node = section_expr, .handle = handle }, decl, encoding, context, handler); try symbolReferencesInternal(arena, store, .{ .node = fn_proto.ast.callconv_expr, .handle = handle }, decl, encoding, context, handler);
} }
if (fn_proto.getCallconvExpr()) |callconv_expr| { if (node_tags[node] == .fn_decl) {
try symbolReferencesInternal(arena, store, .{ .node = callconv_expr, .handle = handle }, decl, encoding, context, handler); try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
}
if (fn_proto.getBodyNode()) |body| {
try symbolReferencesInternal(arena, store, .{ .node = body, .handle = handle }, decl, encoding, context, handler);
} }
}, },
.AnyFrameType => { .anyframe_type => {
const anyframe_type = node.cast(ast.Node.AnyFrameType).?; try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
if (anyframe_type.result) |result| {
try symbolReferencesInternal(arena, store, .{ .node = result.return_type, .handle = handle }, decl, encoding, context, handler);
}
}, },
.Defer => { .@"defer" => {
const defer_node = node.cast(ast.Node.Defer).?; try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
try symbolReferencesInternal(arena, store, .{ .node = defer_node.expr, .handle = handle }, decl, encoding, context, handler);
}, },
.Comptime => { .@"comptime" => {
const comptime_node = node.cast(ast.Node.Comptime).?; try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
try symbolReferencesInternal(arena, store, .{ .node = comptime_node.expr, .handle = handle }, decl, encoding, context, handler);
}, },
.Nosuspend => { .@"nosuspend" => {
const nosuspend_node = node.cast(ast.Node.Nosuspend).?; try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
try symbolReferencesInternal(arena, store, .{ .node = nosuspend_node.expr, .handle = handle }, decl, encoding, context, handler);
}, },
.Switch => { .@"switch",
.switch_comma,
=> {
// TODO When renaming a union(enum) field, also rename switch items that refer to it. // TODO When renaming a union(enum) field, also rename switch items that refer to it.
const switch_node = node.cast(ast.Node.Switch).?; try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
try symbolReferencesInternal(arena, store, .{ .node = switch_node.expr, .handle = handle }, decl, encoding, context, handler); const extra = tree.extraData(datas[node].rhs, ast.Node.SubRange);
for (switch_node.casesConst()) |case| { const cases = tree.extra_data[extra.start..extra.end];
if (case.*.cast(ast.Node.SwitchCase)) |case_node| { for (cases) |case| {
try symbolReferencesInternal(arena, store, .{ .node = case_node.expr, .handle = handle }, decl, encoding, context, handler); try symbolReferencesInternal(arena, store, .{ .node = case, .handle = handle }, decl, encoding, context, handler);
}
} }
}, },
.While => { .switch_case_one => {
const while_node = node.cast(ast.Node.While).?; const case_one = tree.switchCaseOne(node);
try symbolReferencesInternal(arena, store, .{ .node = while_node.condition, .handle = handle }, decl, encoding, context, handler); if (case_one.ast.target_expr != 0)
if (while_node.continue_expr) |cont_expr| { try symbolReferencesInternal(arena, store, .{ .node = case_one.ast.target_expr, .handle = handle }, decl, encoding, context, handler);
try symbolReferencesInternal(arena, store, .{ .node = cont_expr, .handle = handle }, decl, encoding, context, handler); for (case_one.ast.values) |val|
try symbolReferencesInternal(arena, store, .{ .node = val, .handle = handle }, decl, encoding, context, handler);
},
.switch_case => {
const case = tree.switchCase(node);
if (case.ast.target_expr != 0)
try symbolReferencesInternal(arena, store, .{ .node = case.ast.target_expr, .handle = handle }, decl, encoding, context, handler);
for (case.ast.values) |val|
try symbolReferencesInternal(arena, store, .{ .node = val, .handle = handle }, decl, encoding, context, handler);
},
.@"while",
.while_simple,
.while_cont,
.for_simple,
.@"for",
=> {
const loop: ast.full.While = switch (node_tags[node]) {
.@"while" => tree.whileFull(node),
.while_simple => tree.whileSimple(node),
.while_cont => tree.whileCont(node),
.for_simple => tree.forSimple(node),
.@"for" => tree.forFull(node),
else => unreachable,
};
try symbolReferencesInternal(arena, store, .{ .node = loop.ast.cond_expr, .handle = handle }, decl, encoding, context, handler);
if (loop.ast.cont_expr != 0) {
try symbolReferencesInternal(arena, store, .{ .node = loop.ast.cont_expr, .handle = handle }, decl, encoding, context, handler);
} }
try symbolReferencesInternal(arena, store, .{ .node = while_node.body, .handle = handle }, decl, encoding, context, handler); try symbolReferencesInternal(arena, store, .{ .node = loop.ast.then_expr, .handle = handle }, decl, encoding, context, handler);
if (while_node.@"else") |else_node| { if (loop.ast.else_expr != 0) {
try symbolReferencesInternal(arena, store, .{ .node = else_node.body, .handle = handle }, decl, encoding, context, handler); try symbolReferencesInternal(arena, store, .{ .node = loop.ast.else_expr, .handle = handle }, decl, encoding, context, handler);
} }
}, },
.For => { .@"if",
const for_node = node.cast(ast.Node.For).?; .if_simple,
try symbolReferencesInternal(arena, store, .{ .node = for_node.array_expr, .handle = handle }, decl, encoding, context, handler); => {
try symbolReferencesInternal(arena, store, .{ .node = for_node.body, .handle = handle }, decl, encoding, context, handler); const if_node: ast.full.If = if (node_tags[node] == .@"if") tree.ifFull(node) else tree.ifSimple(node);
if (for_node.@"else") |else_node| {
try symbolReferencesInternal(arena, store, .{ .node = else_node.body, .handle = handle }, decl, encoding, context, handler); try symbolReferencesInternal(arena, store, .{ .node = if_node.ast.cond_expr, .handle = handle }, decl, encoding, context, handler);
try symbolReferencesInternal(arena, store, .{ .node = if_node.ast.then_expr, .handle = handle }, decl, encoding, context, handler);
if (if_node.ast.else_expr != 0) {
try symbolReferencesInternal(arena, store, .{ .node = if_node.ast.else_expr, .handle = handle }, decl, encoding, context, handler);
} }
}, },
.If => { .array_type,
const if_node = node.cast(ast.Node.If).?; .array_type_sentinel,
try symbolReferencesInternal(arena, store, .{ .node = if_node.condition, .handle = handle }, decl, encoding, context, handler); => {
try symbolReferencesInternal(arena, store, .{ .node = if_node.body, .handle = handle }, decl, encoding, context, handler); try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
if (if_node.@"else") |else_node| { try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
try symbolReferencesInternal(arena, store, .{ .node = else_node.body, .handle = handle }, decl, encoding, context, handler); },
.ptr_type,
.ptr_type_aligned,
.ptr_type_bit_range,
.ptr_type_sentinel,
=> {
const ptr_type = analysis.ptrType(tree, node).?;
if (ptr_type.ast.align_node != 0) {
try symbolReferencesInternal(arena, store, .{ .node = ptr_type.ast.align_node, .handle = handle }, decl, encoding, context, handler);
if (node_tags[node] == .ptr_type_bit_range) {
try symbolReferencesInternal(arena, store, .{
.node = ptr_type.ast.bit_range_start,
.handle = handle,
}, decl, encoding, context, handler);
try symbolReferencesInternal(arena, store, .{
.node = ptr_type.ast.bit_range_end,
.handle = handle,
}, decl, encoding, context, handler);
}
}
if (ptr_type.ast.sentinel != 0) {
try symbolReferencesInternal(arena, store, .{ .node = ptr_type.ast.sentinel, .handle = handle }, decl, encoding, context, handler);
}
try symbolReferencesInternal(arena, store, .{ .node = ptr_type.ast.child_type, .handle = handle }, decl, encoding, context, handler);
},
.address_of, .@"await", .bit_not, .bool_not, .optional_type, .negation, .negation_wrap, .@"resume", .@"try" => {
try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
},
.array_init,
.array_init_comma,
.array_init_dot,
.array_init_dot_comma,
.array_init_one,
.array_init_one_comma,
.array_init_dot_two,
.array_init_dot_two_comma,
=> |n| {
var buf: [2]ast.Node.Index = undefined;
const array_init = switch (n) {
.array_init, .array_init_comma => tree.arrayInit(node),
.array_init_dot, .array_init_dot_comma => tree.arrayInitDot(node),
.array_init_one, .array_init_one_comma => tree.arrayInitOne(buf[0..1], node),
.array_init_dot_two, .array_init_dot_two_comma => tree.arrayInitDotTwo(&buf, node),
else => unreachable,
};
if (array_init.ast.type_expr != 0)
try symbolReferencesInternal(arena, store, .{ .node = array_init.ast.type_expr, .handle = handle }, decl, encoding, context, handler);
for (array_init.ast.elements) |e|
try symbolReferencesInternal(arena, store, .{ .node = e, .handle = handle }, decl, encoding, context, handler);
},
.struct_init,
.struct_init_comma,
.struct_init_dot,
.struct_init_dot_comma,
.struct_init_dot_two,
.struct_init_dot_two_comma,
.struct_init_one,
.struct_init_one_comma,
=> |n| {
var buf: [2]ast.Node.Index = undefined;
const struct_init: ast.full.StructInit = switch (n) {
.struct_init, .struct_init_comma => tree.structInit(node),
.struct_init_dot, .struct_init_dot_comma => tree.structInitDot(node),
.struct_init_one, .struct_init_one_comma => tree.structInitOne(buf[0..1], node),
.struct_init_dot_two, .struct_init_dot_two_comma => tree.structInitDotTwo(&buf, node),
else => unreachable,
};
if (struct_init.ast.type_expr != 0)
try symbolReferencesInternal(arena, store, .{ .node = struct_init.ast.type_expr, .handle = handle }, decl, encoding, context, handler);
for (struct_init.ast.fields) |field|
try symbolReferencesInternal(arena, store, .{ .node = field, .handle = handle }, decl, encoding, context, handler);
},
.call,
.call_comma,
.call_one,
.call_one_comma,
.async_call,
.async_call_comma,
.async_call_one,
.async_call_one_comma,
=> |c| {
var buf: [1]ast.Node.Index = undefined;
const call: ast.full.Call = switch (c) {
.call, .call_comma, .async_call, .async_call_comma => tree.callFull(node),
.call_one, .call_one_comma, .async_call_one, .async_call_one_comma => tree.callOne(&buf, node),
else => unreachable,
};
if (call.ast.fn_expr != 0)
try symbolReferencesInternal(arena, store, .{ .node = call.ast.fn_expr, .handle = handle }, decl, encoding, context, handler);
for (call.ast.params) |param| {
try symbolReferencesInternal(arena, store, .{ .node = param, .handle = handle }, decl, encoding, context, handler);
} }
}, },
.ArrayType => { .slice,
const info = node.castTag(.ArrayType).?; .slice_sentinel,
try symbolReferencesInternal(arena, store, .{ .node = info.len_expr, .handle = handle }, decl, encoding, context, handler); .slice_open,
try symbolReferencesInternal(arena, store, .{ .node = info.rhs, .handle = handle }, decl, encoding, context, handler); => |s| {
}, const slice: ast.full.Slice = switch (s) {
.ArrayTypeSentinel => { .slice => tree.slice(node),
const info = node.castTag(.ArrayTypeSentinel).?; .slice_open => tree.sliceOpen(node),
try symbolReferencesInternal(arena, store, .{ .node = info.len_expr, .handle = handle }, decl, encoding, context, handler); .slice_sentinel => tree.sliceSentinel(node),
try symbolReferencesInternal(arena, store, .{ .node = info.sentinel, .handle = handle }, decl, encoding, context, handler);
try symbolReferencesInternal(arena, store, .{ .node = info.rhs, .handle = handle }, decl, encoding, context, handler);
},
.PtrType, .SliceType => {
const info = switch (node.tag) {
.PtrType => node.castTag(.PtrType).?.ptr_info,
.SliceType => node.castTag(.SliceType).?.ptr_info,
else => unreachable, else => unreachable,
}; };
if (info.align_info) |align_info| { try symbolReferencesInternal(arena, store, .{ .node = slice.ast.sliced, .handle = handle }, decl, encoding, context, handler);
try symbolReferencesInternal(arena, store, .{ .node = align_info.node, .handle = handle }, decl, encoding, context, handler); try symbolReferencesInternal(arena, store, .{ .node = slice.ast.start, .handle = handle }, decl, encoding, context, handler);
if (align_info.bit_range) |range| { if (slice.ast.end != 0)
try symbolReferencesInternal(arena, store, .{ .node = range.start, .handle = handle }, decl, encoding, context, handler); try symbolReferencesInternal(arena, store, .{ .node = slice.ast.end, .handle = handle }, decl, encoding, context, handler);
try symbolReferencesInternal(arena, store, .{ .node = range.end, .handle = handle }, decl, encoding, context, handler); if (slice.ast.sentinel != 0)
try symbolReferencesInternal(arena, store, .{ .node = slice.ast.sentinel, .handle = handle }, decl, encoding, context, handler);
},
.array_access => {
try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
},
.deref,
.unwrap_optional,
=> {
try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
},
.grouped_expression => {
try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
},
.@"return",
.@"break",
.@"continue",
=> {
if (datas[node].lhs != 0) {
try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
} }
},
.@"suspend" => {
if (datas[node].lhs != 0) {
try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
} }
if (info.sentinel) |sentinel| { },
try symbolReferencesInternal(arena, store, .{ .node = sentinel, .handle = handle }, decl, encoding, context, handler); .builtin_call,
} .builtin_call_comma,
switch (node.tag) { .builtin_call_two,
.PtrType => try symbolReferencesInternal(arena, store, .{ .node = node.castTag(.PtrType).?.rhs, .handle = handle }, decl, encoding, context, handler), .builtin_call_two_comma,
.SliceType => try symbolReferencesInternal(arena, store, .{ .node = node.castTag(.SliceType).?.rhs, .handle = handle }, decl, encoding, context, handler), => |builtin_tag| {
const data = datas[node];
const params = switch (builtin_tag) {
.builtin_call, .builtin_call_comma => tree.extra_data[data.lhs..data.rhs],
.builtin_call_two, .builtin_call_two_comma => if (data.lhs == 0)
&[_]ast.Node.Index{}
else if (data.rhs == 0)
&[_]ast.Node.Index{data.lhs}
else
&[_]ast.Node.Index{ data.lhs, data.rhs },
else => unreachable, else => unreachable,
} };
},
.AddressOf, .Await, .BitNot, .BoolNot, .OptionalType, .Negation, .NegationWrap, .Resume, .Try => {
const prefix_op = node.cast(ast.Node.SimplePrefixOp).?;
try symbolReferencesInternal(arena, store, .{ .node = prefix_op.rhs, .handle = handle }, decl, encoding, context, handler);
},
.FieldInitializer => {
// TODO Rename field initializer names when needed
const field_init = node.cast(ast.Node.FieldInitializer).?;
try symbolReferencesInternal(arena, store, .{ .node = field_init.expr, .handle = handle }, decl, encoding, context, handler);
},
.ArrayInitializer => {
const array_init = node.cast(ast.Node.ArrayInitializer).?;
try symbolReferencesInternal(arena, store, .{ .node = array_init.lhs, .handle = handle }, decl, encoding, context, handler);
for (array_init.listConst()) |child| {
try symbolReferencesInternal(arena, store, .{ .node = child, .handle = handle }, decl, encoding, context, handler);
}
},
.ArrayInitializerDot => {
const array_init = node.cast(ast.Node.ArrayInitializerDot).?;
for (array_init.listConst()) |child| {
try symbolReferencesInternal(arena, store, .{ .node = child, .handle = handle }, decl, encoding, context, handler);
}
},
.StructInitializer => {
// TODO Rename field initializer names when needed
const struct_init = node.cast(ast.Node.StructInitializer).?;
try symbolReferencesInternal(arena, store, .{ .node = struct_init.lhs, .handle = handle }, decl, encoding, context, handler);
for (struct_init.listConst()) |child| {
try symbolReferencesInternal(arena, store, .{ .node = child, .handle = handle }, decl, encoding, context, handler);
}
},
.StructInitializerDot => {
const struct_init = node.cast(ast.Node.StructInitializerDot).?;
for (struct_init.listConst()) |child| {
try symbolReferencesInternal(arena, store, .{ .node = child, .handle = handle }, decl, encoding, context, handler);
}
},
.Call => {
const call = node.cast(ast.Node.Call).?;
try symbolReferencesInternal(arena, store, .{ .node = call.lhs, .handle = handle }, decl, encoding, context, handler);
for (call.paramsConst()) |param| {
try symbolReferencesInternal(arena, store, .{ .node = param, .handle = handle }, decl, encoding, context, handler);
}
},
.Slice => {
const slice = node.castTag(.Slice).?;
try symbolReferencesInternal(arena, store, .{ .node = slice.lhs, .handle = handle }, decl, encoding, context, handler);
try symbolReferencesInternal(arena, store, .{ .node = slice.start, .handle = handle }, decl, encoding, context, handler);
if (slice.end) |end| {
try symbolReferencesInternal(arena, store, .{ .node = end, .handle = handle }, decl, encoding, context, handler);
}
if (slice.sentinel) |sentinel| {
try symbolReferencesInternal(arena, store, .{ .node = sentinel, .handle = handle }, decl, encoding, context, handler);
}
},
.ArrayAccess => {
const arr_acc = node.castTag(.ArrayAccess).?;
try symbolReferencesInternal(arena, store, .{ .node = arr_acc.lhs, .handle = handle }, decl, encoding, context, handler);
try symbolReferencesInternal(arena, store, .{ .node = arr_acc.index_expr, .handle = handle }, decl, encoding, context, handler);
},
.Deref, .UnwrapOptional => {
const suffix = node.cast(ast.Node.SimpleSuffixOp).?;
try symbolReferencesInternal(arena, store, .{ .node = suffix.lhs, .handle = handle }, decl, encoding, context, handler);
},
.GroupedExpression => {
const grouped = node.cast(ast.Node.GroupedExpression).?;
try symbolReferencesInternal(arena, store, .{ .node = grouped.expr, .handle = handle }, decl, encoding, context, handler);
},
.Return, .Break, .Continue => {
const cfe = node.cast(ast.Node.ControlFlowExpression).?;
if (cfe.getRHS()) |rhs| {
try symbolReferencesInternal(arena, store, .{ .node = rhs, .handle = handle }, decl, encoding, context, handler);
}
},
.Suspend => {
const suspend_node = node.cast(ast.Node.Suspend).?;
if (suspend_node.body) |body| {
try symbolReferencesInternal(arena, store, .{ .node = body, .handle = handle }, decl, encoding, context, handler);
}
},
.BuiltinCall => {
const builtin_call = node.cast(ast.Node.BuiltinCall).?;
for (builtin_call.paramsConst()) |param| {
try symbolReferencesInternal(arena, store, .{ .node = param, .handle = handle }, decl, encoding, context, handler);
}
},
// TODO Inline asm expr
.TestDecl => {
const test_decl = node.cast(ast.Node.TestDecl).?;
try symbolReferencesInternal(arena, store, .{ .node = test_decl.body_node, .handle = handle }, decl, encoding, context, handler);
},
.Period => {
const infix_op = node.cast(ast.Node.SimpleInfixOp).?;
try symbolReferencesInternal(arena, store, .{ .node = infix_op.lhs, .handle = handle }, decl, encoding, context, handler); for (params) |param|
try symbolReferencesInternal(arena, store, .{ .node = param, .handle = handle }, decl, encoding, context, handler);
},
.@"asm",
.asm_simple,
=> |a| {
const _asm: ast.full.Asm = if (a == .@"asm") tree.asmFull(node) else tree.asmSimple(node);
if (_asm.ast.items.len == 0)
try symbolReferencesInternal(arena, store, .{ .node = _asm.ast.template, .handle = handle }, decl, encoding, context, handler);
const rhs_str = analysis.nodeToString(handle.tree, infix_op.rhs) orelse return; for (_asm.inputs) |input|
try symbolReferencesInternal(arena, store, .{ .node = input, .handle = handle }, decl, encoding, context, handler);
for (_asm.outputs) |output|
try symbolReferencesInternal(arena, store, .{ .node = output, .handle = handle }, decl, encoding, context, handler);
},
.test_decl => {
try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
},
.field_access => {
try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
const rhs_str = tree.tokenSlice(datas[node].rhs);
var bound_type_params = analysis.BoundTypeParams.init(&arena.allocator); var bound_type_params = analysis.BoundTypeParams.init(&arena.allocator);
const left_type = try analysis.resolveFieldAccessLhsType( const left_type = try analysis.resolveFieldAccessLhsType(
store, store,
arena, arena,
(try analysis.resolveTypeOfNodeInternal(store, arena, .{ (try analysis.resolveTypeOfNodeInternal(store, arena, .{
.node = infix_op.lhs, .node = datas[node].lhs,
.handle = handle, .handle = handle,
}, &bound_type_params)) orelse return, }, &bound_type_params)) orelse return,
&bound_type_params, &bound_type_params,
@ -350,15 +466,53 @@ fn symbolReferencesInternal(
!left_type.type.is_type_val, !left_type.type.is_type_val,
)) |child| { )) |child| {
if (std.meta.eql(child, decl)) { if (std.meta.eql(child, decl)) {
try tokenReference(handle, infix_op.rhs.firstToken(), encoding, context, handler); try tokenReference(handle, datas[node].rhs, encoding, context, handler);
} }
} }
}, },
.Add, .AddWrap, .ArrayCat, .ArrayMult, .Assign, .AssignBitAnd, .AssignBitOr, .AssignBitShiftLeft, .AssignBitShiftRight, .AssignBitXor, .AssignDiv, .AssignSub, .AssignSubWrap, .AssignMod, .AssignAdd, .AssignAddWrap, .AssignMul, .AssignMulWrap, .BangEqual, .BitAnd, .BitOr, .BitShiftLeft, .BitShiftRight, .BitXor, .BoolOr, .Div, .EqualEqual, .ErrorUnion, .GreaterOrEqual, .GreaterThan, .LessOrEqual, .LessThan, .MergeErrorSets, .Mod, .Mul, .MulWrap, .Range, .Sub, .SubWrap, .OrElse => { .add,
const infix_op = node.cast(ast.Node.SimpleInfixOp).?; .add_wrap,
.array_cat,
try symbolReferencesInternal(arena, store, .{ .node = infix_op.lhs, .handle = handle }, decl, encoding, context, handler); .array_mult,
try symbolReferencesInternal(arena, store, .{ .node = infix_op.rhs, .handle = handle }, decl, encoding, context, handler); .assign,
.assign_bit_and,
.assign_bit_or,
.assign_bit_shift_left,
.assign_bit_shift_right,
.assign_bit_xor,
.assign_div,
.assign_sub,
.assign_sub_wrap,
.assign_mod,
.assign_add,
.assign_add_wrap,
.assign_mul,
.assign_mul_wrap,
.bang_equal,
.bit_and,
.bit_or,
.bit_shift_left,
.bit_shift_right,
.bit_xor,
.bool_or,
.div,
.equal_equal,
.error_union,
.greater_or_equal,
.greater_than,
.less_or_equal,
.less_than,
.merge_error_sets,
.mod,
.mul,
.mul_wrap,
.switch_range,
.sub,
.sub_wrap,
.@"orelse",
=> {
try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
}, },
else => {}, else => {},
} }
@ -372,6 +526,7 @@ pub fn symbolReferences(
include_decl: bool, include_decl: bool,
context: anytype, context: anytype,
comptime handler: anytype, comptime handler: anytype,
skip_std_references: bool,
) !void { ) !void {
std.debug.assert(decl_handle.decl.* != .label_decl); std.debug.assert(decl_handle.decl.* != .label_decl);
const curr_handle = decl_handle.handle; const curr_handle = decl_handle.handle;
@ -381,6 +536,10 @@ pub fn symbolReferences(
var handles = std.ArrayList(*DocumentStore.Handle).init(&arena.allocator); var handles = std.ArrayList(*DocumentStore.Handle).init(&arena.allocator);
var handle_it = store.handles.iterator(); var handle_it = store.handles.iterator();
while (handle_it.next()) |entry| { while (handle_it.next()) |entry| {
if (skip_std_references and std.mem.indexOf(u8, entry.key, "std") != null) {
if (!include_decl or entry.value != curr_handle)
continue;
}
try handles.append(entry.value); try handles.append(entry.value);
} }
for (handles.items) |handle| { for (handles.items) |handle| {
@ -388,7 +547,7 @@ pub fn symbolReferences(
try tokenReference(curr_handle, decl_handle.nameToken(), encoding, context, handler); try tokenReference(curr_handle, decl_handle.nameToken(), encoding, context, handler);
} }
try symbolReferencesInternal(arena, store, .{ .node = &handle.tree.root_node.base, .handle = handle }, decl_handle, encoding, context, handler); try symbolReferencesInternal(arena, store, .{ .node = 0, .handle = handle }, decl_handle, encoding, context, handler);
} }
}, },
.param_decl => |param| { .param_decl => |param| {
@ -396,14 +555,28 @@ pub fn symbolReferences(
if (include_decl) { if (include_decl) {
try tokenReference(curr_handle, decl_handle.nameToken(), encoding, context, handler); try tokenReference(curr_handle, decl_handle.nameToken(), encoding, context, handler);
} }
const fn_node = loop: for (curr_handle.document_scope.scopes) |scope| { const fn_node: ast.full.FnProto = loop: for (curr_handle.document_scope.scopes) |scope| {
switch (scope.data) { switch (scope.data) {
.function => |proto| { .function => |proto| {
const fn_proto = proto.cast(std.zig.ast.Node.FnProto).?; var buf: [1]ast.Node.Index = undefined;
for (fn_proto.paramsConst()) |*candidate| { const fn_proto = analysis.fnProto(curr_handle.tree, proto, &buf).?;
if (candidate == param) var it = fn_proto.iterate(curr_handle.tree);
while (it.next()) |candidate| {
if (std.meta.eql(candidate, param)) {
if (curr_handle.tree.nodes.items(.tag)[proto] == .fn_decl) {
try symbolReferencesInternal(
arena,
store,
.{ .node = curr_handle.tree.nodes.items(.data)[proto].rhs, .handle = curr_handle },
decl_handle,
encoding,
context,
handler,
);
}
break :loop fn_proto; break :loop fn_proto;
} }
}
}, },
else => {}, else => {},
} }
@ -411,15 +584,12 @@ pub fn symbolReferences(
log.warn("Could not find param decl's function", .{}); log.warn("Could not find param decl's function", .{});
return; return;
}; };
if (fn_node.getBodyNode()) |body| {
try symbolReferencesInternal(arena, store, .{ .node = body, .handle = curr_handle }, decl_handle, encoding, context, handler);
}
}, },
.pointer_payload, .array_payload, .switch_payload => { .pointer_payload, .switch_payload, .array_payload, .array_index => {
if (include_decl) { if (include_decl) {
try tokenReference(curr_handle, decl_handle.nameToken(), encoding, context, handler); try tokenReference(curr_handle, decl_handle.nameToken(), encoding, context, handler);
} }
try symbolReferencesInternal(arena, store, .{ .node = &curr_handle.tree.root_node.base, .handle = curr_handle }, decl_handle, encoding, context, handler); try symbolReferencesInternal(arena, store, .{ .node = 0, .handle = curr_handle }, decl_handle, encoding, context, handler);
}, },
.label_decl => unreachable, .label_decl => unreachable,
} }

View File

@ -40,7 +40,7 @@ pub fn renameSymbol(
.edits = edits, .edits = edits,
.allocator = &arena.allocator, .allocator = &arena.allocator,
.new_name = new_name, .new_name = new_name,
}, refHandler); }, refHandler, true);
} }
pub fn renameLabel( pub fn renameLabel(

File diff suppressed because it is too large Load Diff

View File

@ -4,13 +4,16 @@ const headerPkg = @import("header");
const suffix = if (std.builtin.os.tag == .windows) ".exe" else ""; const suffix = if (std.builtin.os.tag == .windows) ".exe" else "";
const allocator = std.heap.page_allocator; const allocator = std.heap.page_allocator;
const initialize_message = \\{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":6896,"clientInfo":{"name":"vscode","version":"1.46.1"},"rootPath":null,"rootUri":null,"capabilities":{"workspace":{"applyEdit":true,"workspaceEdit":{"documentChanges":true,"resourceOperations":["create","rename","delete"],"failureHandling":"textOnlyTransactional"},"didChangeConfiguration":{"dynamicRegistration":true},"didChangeWatchedFiles":{"dynamicRegistration":true},"symbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"tagSupport":{"valueSet":[1]}},"executeCommand":{"dynamicRegistration":true},"configuration":true,"workspaceFolders":true},"textDocument":{"publishDiagnostics":{"relatedInformation":true,"versionSupport":false,"tagSupport":{"valueSet":[1,2]},"complexDiagnosticCodeSupport":true},"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"contextSupport":true,"completionItem":{"snippetSupport":true,"commitCharactersSupport":true,"documentationFormat":["markdown","plaintext"],"deprecatedSupport":true,"preselectSupport":true,"tagSupport":{"valueSet":[1]},"insertReplaceSupport":true},"completionItemKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]}},"hover":{"dynamicRegistration":true,"contentFormat":["markdown","plaintext"]},"signatureHelp":{"dynamicRegistration":true,"signatureInformation":{"documentationFormat":["markdown","plaintext"],"parameterInformation":{"labelOffsetSupport":true}},"contextSupport":true},"definition":{"dynamicRegistration":true,"linkSupport":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"hierarchicalDocumentSymbolSupport":true,"tagSupport":{"valueSet":[1]}},"codeAction":{"dynamicRegistration":true,"isPreferredSupport":true,"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["","quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}}},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true,"prepareSupport":true},"documentLink":{"dynamicRegistration":true,"tooltipSupport":true},"typeDefinition":{"dynamicRegistration":true,"linkSupport":true},"implementation":{"dynamicRegistration":true,"linkSupport":true},"colorProvider":{"dynamicRegistration":true},"foldingRange":{"dynamicRegistration":true,"rangeLimit":5000,"lineFoldingOnly":true},"declaration":{"dynamicRegistration":true,"linkSupport":true},"selectionRange":{"dynamicRegistration":true},"semanticTokens":{"dynamicRegistration":true,"tokenTypes":["comment","keyword","number","regexp","operator","namespace","type","struct","class","interface","enum","typeParameter","function","member","macro","variable","parameter","property","label"],"tokenModifiers":["declaration","documentation","static","abstract","deprecated","readonly"]}},"window":{"workDoneProgress":true}},"trace":"off","workspaceFolders":[{"uri":"file://./tests", "name":"root"}]}} const initialize_message =
\\{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":6896,"clientInfo":{"name":"vscode","version":"1.46.1"},"rootPath":null,"rootUri":null,"capabilities":{"workspace":{"applyEdit":true,"workspaceEdit":{"documentChanges":true,"resourceOperations":["create","rename","delete"],"failureHandling":"textOnlyTransactional"},"didChangeConfiguration":{"dynamicRegistration":true},"didChangeWatchedFiles":{"dynamicRegistration":true},"symbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"tagSupport":{"valueSet":[1]}},"executeCommand":{"dynamicRegistration":true},"configuration":true,"workspaceFolders":true},"textDocument":{"publishDiagnostics":{"relatedInformation":true,"versionSupport":false,"tagSupport":{"valueSet":[1,2]},"complexDiagnosticCodeSupport":true},"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"contextSupport":true,"completionItem":{"snippetSupport":true,"commitCharactersSupport":true,"documentationFormat":["markdown","plaintext"],"deprecatedSupport":true,"preselectSupport":true,"tagSupport":{"valueSet":[1]},"insertReplaceSupport":true},"completionItemKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]}},"hover":{"dynamicRegistration":true,"contentFormat":["markdown","plaintext"]},"signatureHelp":{"dynamicRegistration":true,"signatureInformation":{"documentationFormat":["markdown","plaintext"],"parameterInformation":{"labelOffsetSupport":true}},"contextSupport":true},"definition":{"dynamicRegistration":true,"linkSupport":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"hierarchicalDocumentSymbolSupport":true,"tagSupport":{"valueSet":[1]}},"codeAction":{"dynamicRegistration":true,"isPreferredSupport":true,"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["","quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}}},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true,"prepareSupport":true},"documentLink":{"dynamicRegistration":true,"tooltipSupport":true},"typeDefinition":{"dynamicRegistration":true,"linkSupport":true},"implementation":{"dynamicRegistration":true,"linkSupport":true},"colorProvider":{"dynamicRegistration":true},"foldingRange":{"dynamicRegistration":true,"rangeLimit":5000,"lineFoldingOnly":true},"declaration":{"dynamicRegistration":true,"linkSupport":true},"selectionRange":{"dynamicRegistration":true},"semanticTokens":{"dynamicRegistration":true,"tokenTypes":["comment","keyword","number","regexp","operator","namespace","type","struct","class","interface","enum","typeParameter","function","member","macro","variable","parameter","property","label"],"tokenModifiers":["declaration","documentation","static","abstract","deprecated","readonly"]}},"window":{"workDoneProgress":true}},"trace":"off","workspaceFolders":[{"uri":"file://./tests", "name":"root"}]}}
; ;
const initialized_message = \\{"jsonrpc":"2.0","method":"initialized","params":{}} const initialized_message =
\\{"jsonrpc":"2.0","method":"initialized","params":{}}
; ;
const shutdown_message = \\{"jsonrpc":"2.0", "id":"STDWN", "method":"shutdown","params":{}} const shutdown_message =
\\{"jsonrpc":"2.0", "id":"STDWN", "method":"shutdown","params":{}}
; ;
fn sendRequest(req: []const u8, process: *std.ChildProcess) !void { fn sendRequest(req: []const u8, process: *std.ChildProcess) !void {
@ -89,10 +92,12 @@ test "Open file, ask for semantic tokens" {
try sendRequest(initialize_message, process); try sendRequest(initialize_message, process);
try sendRequest(initialized_message, process); try sendRequest(initialized_message, process);
const new_file_req = \\{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file://./tests/test.zig","languageId":"zig","version":420,"text":"const std = @import(\"std\");"}}} const new_file_req =
\\{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file://./tests/test.zig","languageId":"zig","version":420,"text":"const std = @import(\"std\");"}}}
; ;
try sendRequest(new_file_req, process); try sendRequest(new_file_req, process);
const sem_toks_req = \\{"jsonrpc":"2.0","id":2,"method":"textDocument/semanticTokens/full","params":{"textDocument":{"uri":"file://./tests/test.zig"}}} const sem_toks_req =
\\{"jsonrpc":"2.0","id":2,"method":"textDocument/semanticTokens/full","params":{"textDocument":{"uri":"file://./tests/test.zig"}}}
; ;
try sendRequest(sem_toks_req, process); try sendRequest(sem_toks_req, process);
try sendRequest(shutdown_message, process); try sendRequest(shutdown_message, process);
@ -106,10 +111,12 @@ test "Requesting a completion in an empty file" {
try sendRequest(initialize_message, process); try sendRequest(initialize_message, process);
try sendRequest(initialized_message, process); try sendRequest(initialized_message, process);
const new_file_req = \\{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///test.zig","languageId":"zig","version":420,"text":""}}} const new_file_req =
\\{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///test.zig","languageId":"zig","version":420,"text":""}}}
; ;
try sendRequest(new_file_req, process); try sendRequest(new_file_req, process);
const completion_req = \\{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///test.zig"}, "position":{"line":0,"character":0}}} const completion_req =
\\{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///test.zig"}, "position":{"line":0,"character":0}}}
; ;
try sendRequest(completion_req, process); try sendRequest(completion_req, process);
try sendRequest(shutdown_message, process); try sendRequest(shutdown_message, process);
@ -121,19 +128,22 @@ test "Requesting a completion with no trailing whitespace" {
try sendRequest(initialize_message, process); try sendRequest(initialize_message, process);
try sendRequest(initialized_message, process); try sendRequest(initialized_message, process);
const new_file_req = \\{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///test.zig","languageId":"zig","version":420,"text":"const std = @import(\"std\");\nc"}}} const new_file_req =
\\{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///test.zig","languageId":"zig","version":420,"text":"const std = @import(\"std\");\nc"}}}
; ;
try sendRequest(new_file_req, process); try sendRequest(new_file_req, process);
const completion_req = \\{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///test.zig"}, "position":{"line":1,"character":1}}} const completion_req =
\\{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///test.zig"}, "position":{"line":1,"character":1}}}
; ;
try sendRequest(completion_req, process); try sendRequest(completion_req, process);
try sendRequest(shutdown_message, process); try sendRequest(shutdown_message, process);
try consumeOutputAndWait(process, .{ try consumeOutputAndWait(process, .{
\\{"jsonrpc":"2.0","id":2,"result":{"isIncomplete":false,"items":[]}} \\{"jsonrpc":"2.0","id":2,"result":{"isIncomplete":false,"items":[{"label":"std","kind":21,"textEdit":null,"filterText":null,"insertText":null,"insertTextFormat":1,"detail":"const std = @import(\"std\")","documentation":null}]}}
}); });
} }
const initialize_message_offs = \\{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":6896,"clientInfo":{"name":"vscode","version":"1.46.1"},"rootPath":null,"rootUri":null,"capabilities":{"offsetEncoding":["utf-16", "utf-8"],"workspace":{"applyEdit":true,"workspaceEdit":{"documentChanges":true,"resourceOperations":["create","rename","delete"],"failureHandling":"textOnlyTransactional"},"didChangeConfiguration":{"dynamicRegistration":true},"didChangeWatchedFiles":{"dynamicRegistration":true},"symbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"tagSupport":{"valueSet":[1]}},"executeCommand":{"dynamicRegistration":true},"configuration":true,"workspaceFolders":true},"textDocument":{"publishDiagnostics":{"relatedInformation":true,"versionSupport":false,"tagSupport":{"valueSet":[1,2]},"complexDiagnosticCodeSupport":true},"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"contextSupport":true,"completionItem":{"snippetSupport":true,"commitCharactersSupport":true,"documentationFormat":["markdown","plaintext"],"deprecatedSupport":true,"preselectSupport":true,"tagSupport":{"valueSet":[1]},"insertReplaceSupport":true},"completionItemKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]}},"hover":{"dynamicRegistration":true,"contentFormat":["markdown","plaintext"]},"signatureHelp":{"dynamicRegistration":true,"signatureInformation":{"documentationFormat":["markdown","plaintext"],"parameterInformation":{"labelOffsetSupport":true}},"contextSupport":true},"definition":{"dynamicRegistration":true,"linkSupport":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"hierarchicalDocumentSymbolSupport":true,"tagSupport":{"valueSet":[1]}},"codeAction":{"dynamicRegistration":true,"isPreferredSupport":true,"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["","quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}}},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true,"prepareSupport":true},"documentLink":{"dynamicRegistration":true,"tooltipSupport":true},"typeDefinition":{"dynamicRegistration":true,"linkSupport":true},"implementation":{"dynamicRegistration":true,"linkSupport":true},"colorProvider":{"dynamicRegistration":true},"foldingRange":{"dynamicRegistration":true,"rangeLimit":5000,"lineFoldingOnly":true},"declaration":{"dynamicRegistration":true,"linkSupport":true},"selectionRange":{"dynamicRegistration":true},"semanticTokens":{"dynamicRegistration":true,"tokenTypes":["comment","keyword","number","regexp","operator","namespace","type","struct","class","interface","enum","typeParameter","function","member","macro","variable","parameter","property","label"],"tokenModifiers":["declaration","documentation","static","abstract","deprecated","readonly"]}},"window":{"workDoneProgress":true}},"trace":"off","workspaceFolders":null}} const initialize_message_offs =
\\{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":6896,"clientInfo":{"name":"vscode","version":"1.46.1"},"rootPath":null,"rootUri":null,"capabilities":{"offsetEncoding":["utf-16", "utf-8"],"workspace":{"applyEdit":true,"workspaceEdit":{"documentChanges":true,"resourceOperations":["create","rename","delete"],"failureHandling":"textOnlyTransactional"},"didChangeConfiguration":{"dynamicRegistration":true},"didChangeWatchedFiles":{"dynamicRegistration":true},"symbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"tagSupport":{"valueSet":[1]}},"executeCommand":{"dynamicRegistration":true},"configuration":true,"workspaceFolders":true},"textDocument":{"publishDiagnostics":{"relatedInformation":true,"versionSupport":false,"tagSupport":{"valueSet":[1,2]},"complexDiagnosticCodeSupport":true},"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"contextSupport":true,"completionItem":{"snippetSupport":true,"commitCharactersSupport":true,"documentationFormat":["markdown","plaintext"],"deprecatedSupport":true,"preselectSupport":true,"tagSupport":{"valueSet":[1]},"insertReplaceSupport":true},"completionItemKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]}},"hover":{"dynamicRegistration":true,"contentFormat":["markdown","plaintext"]},"signatureHelp":{"dynamicRegistration":true,"signatureInformation":{"documentationFormat":["markdown","plaintext"],"parameterInformation":{"labelOffsetSupport":true}},"contextSupport":true},"definition":{"dynamicRegistration":true,"linkSupport":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"hierarchicalDocumentSymbolSupport":true,"tagSupport":{"valueSet":[1]}},"codeAction":{"dynamicRegistration":true,"isPreferredSupport":true,"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["","quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}}},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true,"prepareSupport":true},"documentLink":{"dynamicRegistration":true,"tooltipSupport":true},"typeDefinition":{"dynamicRegistration":true,"linkSupport":true},"implementation":{"dynamicRegistration":true,"linkSupport":true},"colorProvider":{"dynamicRegistration":true},"foldingRange":{"dynamicRegistration":true,"rangeLimit":5000,"lineFoldingOnly":true},"declaration":{"dynamicRegistration":true,"linkSupport":true},"selectionRange":{"dynamicRegistration":true},"semanticTokens":{"dynamicRegistration":true,"tokenTypes":["comment","keyword","number","regexp","operator","namespace","type","struct","class","interface","enum","typeParameter","function","member","macro","variable","parameter","property","label"],"tokenModifiers":["declaration","documentation","static","abstract","deprecated","readonly"]}},"window":{"workDoneProgress":true}},"trace":"off","workspaceFolders":null}}
; ;
test "Requesting utf-8 offset encoding" { test "Requesting utf-8 offset encoding" {