Skip to content

Commit 645f728

Browse files
authored
Merge pull request #1517 from zigtools/doc-scope-dod
Data-oriented design DocumentScope
2 parents fd9d264 + 0e2c4f5 commit 645f728

15 files changed

+1545
-902
lines changed

src/DocumentScope.zig

+1,279
Large diffs are not rendered by default.

src/DocumentStore.zig

+6-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const ComptimeInterpreter = @import("ComptimeInterpreter.zig");
1616
const AstGen = @import("stage2/AstGen.zig");
1717
const Zir = @import("stage2/Zir.zig");
1818
const InternPool = @import("analyser/InternPool.zig");
19+
const DocumentScope = @import("DocumentScope.zig");
1920

2021
const DocumentStore = @This();
2122

@@ -68,7 +69,7 @@ pub const Handle = struct {
6869
} = .none,
6970
/// Not null if a ComptimeInterpreter is actually used
7071
interpreter: ?*ComptimeInterpreter = null,
71-
document_scope: analysis.DocumentScope,
72+
document_scope: DocumentScope,
7273
/// Contains one entry for every import in the document
7374
import_uris: std.ArrayListUnmanaged(Uri) = .{},
7475
/// Contains one entry for every cimport in the document
@@ -778,10 +779,12 @@ fn createDocument(self: *DocumentStore, uri: Uri, text: [:0]const u8, open: bool
778779
code.instructions = instructions.slice();
779780
}
780781

781-
var document_scope = try analysis.makeDocumentScope(self.allocator, tree);
782+
var document_scope = try DocumentScope.init(self.allocator, tree);
782783
errdefer document_scope.deinit(self.allocator);
783784

784785
// remove unused capacity
786+
document_scope.extra.shrinkAndFree(self.allocator, document_scope.extra.items.len);
787+
try document_scope.declarations.setCapacity(self.allocator, document_scope.declarations.len);
785788
try document_scope.scopes.setCapacity(self.allocator, document_scope.scopes.len);
786789

787790
break :blk Handle{
@@ -1167,7 +1170,7 @@ fn tagStoreCompletionItems(self: *DocumentStore, arena: std.mem.Allocator, handl
11671170
try self.collectDependenciesInternal(arena, handle, &dependencies, true);
11681171

11691172
// TODO Better solution for deciding what tags to include
1170-
var result_set = analysis.CompletionSet{};
1173+
var result_set = DocumentScope.CompletionSet{};
11711174

11721175
for (dependencies.items) |uri| {
11731176
// not every dependency is loaded which results in incomplete completion

src/Server.zig

+1-1
Original file line numberDiff line numberDiff line change
@@ -1484,7 +1484,7 @@ fn generalReferencesHandler(server: *Server, arena: std.mem.Allocator, request:
14841484
else => true,
14851485
};
14861486

1487-
const locations = if (decl.decl.* == .label_decl)
1487+
const locations = if (decl.decl == .label_decl)
14881488
try references.labelReferences(arena, decl, server.offset_encoding, include_decl)
14891489
else
14901490
try references.symbolReferences(

src/analysis.zig

+142-798
Large diffs are not rendered by default.

src/ast.zig

+45-58
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ const std = @import("std");
66
const offsets = @import("offsets.zig");
77
const Ast = std.zig.Ast;
88
const Node = Ast.Node;
9-
const TokenTag = std.zig.Token.Tag;
109
const full = Ast.full;
1110

1211
fn fullPtrTypeComponents(tree: Ast, info: full.PtrType.Components) full.PtrType {
@@ -55,41 +54,6 @@ fn fullPtrTypeComponents(tree: Ast, info: full.PtrType.Components) full.PtrType
5554
return result;
5655
}
5756

58-
fn findNextLBrace(token_tags: []const TokenTag, start: Ast.TokenIndex) ?Ast.TokenIndex {
59-
for (token_tags[start..], 0..) |tag, i| {
60-
if (tag == TokenTag.l_brace) {
61-
return start + @as(Ast.TokenIndex, @intCast(i));
62-
}
63-
}
64-
return null;
65-
}
66-
67-
/// Given an l_brace, find the corresponding r_brace.
68-
/// If no corresponding r_brace is found, return null.
69-
/// Useful for finding the extent of a block/scope if the syntax is valid.
70-
fn findMatchingRBrace(token_tags: []const TokenTag, l_brace: Ast.TokenIndex) ?Ast.TokenIndex {
71-
std.debug.assert(token_tags[l_brace] == TokenTag.l_brace);
72-
73-
const start = l_brace + 1;
74-
var depth: i32 = 0;
75-
var offset: Ast.TokenIndex = 0;
76-
77-
for (token_tags[start..], 0..) |tag, i| {
78-
if (tag == TokenTag.l_brace) {
79-
depth += 1;
80-
}
81-
if (tag == TokenTag.r_brace) {
82-
if (depth == 0) {
83-
offset = @intCast(i);
84-
break;
85-
}
86-
depth -= 1;
87-
}
88-
}
89-
90-
return if (depth == 0) start + offset else null;
91-
}
92-
9357
pub fn ptrTypeSimple(tree: Ast, node: Node.Index) full.PtrType {
9458
std.debug.assert(tree.nodes.items(.tag)[node] == .ptr_type);
9559
const data = tree.nodes.items(.data)[node];
@@ -353,6 +317,10 @@ pub fn fullFor(tree: Ast, node: Node.Index) ?full.For {
353317
};
354318
}
355319

320+
fn findMatchingRBrace(tree: Ast, start: Ast.TokenIndex) ?Ast.TokenIndex {
321+
return if (std.mem.indexOfScalarPos(std.zig.Token.Tag, tree.tokens.items(.tag), start, .r_brace)) |index| @intCast(index) else null;
322+
}
323+
356324
pub fn lastToken(tree: Ast, node: Ast.Node.Index) Ast.TokenIndex {
357325
const TokenIndex = Ast.TokenIndex;
358326
const tags = tree.nodes.items(.tag);
@@ -614,13 +582,15 @@ pub fn lastToken(tree: Ast, node: Ast.Node.Index) Ast.TokenIndex {
614582
}
615583
n = tree.extra_data[params.end - 1]; // last parameter
616584
},
617-
.switch_comma, .@"switch" => {
618-
const lhs = datas[n].lhs;
619-
var l_brace = tree.lastToken(lhs) + 2; // + 2 => (last) token of the condition + the .r_paren
620-
// If the condition within the switch is invalid, eg `switch (a.) {}`,
621-
// l_brace would be the index of the .r_paren of the switch ----^
622-
if (token_tags[l_brace] != .l_brace) l_brace += 1;
623-
return findMatchingRBrace(token_tags, l_brace) orelse @intCast(tree.tokens.len - 1);
585+
.@"switch" => {
586+
const cases = tree.extraData(datas[n].rhs, Node.SubRange);
587+
if (cases.end - cases.start == 0) {
588+
const token = tree.lastToken(datas[n].lhs) + 3; // rparen, lbrace, rbrace
589+
return end_offset + (findMatchingRBrace(tree, token) orelse token);
590+
} else {
591+
var token = tree.lastToken(tree.extra_data[cases.end - 1]) + 1; // for the rbrace
592+
return end_offset + (findMatchingRBrace(tree, token) orelse token);
593+
}
624594
},
625595
.@"asm" => {
626596
const extra = tree.extraData(datas[n].rhs, Node.Asm);
@@ -636,6 +606,7 @@ pub fn lastToken(tree: Ast, node: Ast.Node.Index) Ast.TokenIndex {
636606
},
637607
.array_init_comma,
638608
.struct_init_comma,
609+
.switch_comma,
639610
=> {
640611
if (datas[n].rhs != 0) {
641612
const members = tree.extraData(datas[n].rhs, Node.SubRange);
@@ -659,6 +630,8 @@ pub fn lastToken(tree: Ast, node: Ast.Node.Index) Ast.TokenIndex {
659630
},
660631
.array_init_dot_comma,
661632
.struct_init_dot_comma,
633+
.block_semicolon,
634+
.container_decl_trailing,
662635
.tagged_union_trailing,
663636
.builtin_call_comma,
664637
=> {
@@ -676,26 +649,38 @@ pub fn lastToken(tree: Ast, node: Ast.Node.Index) Ast.TokenIndex {
676649
}
677650
n = datas[n].rhs;
678651
},
679-
.block,
680-
.block_semicolon,
681-
.block_two_semicolon,
682-
.block_two,
683-
=> {
684-
return findMatchingRBrace(token_tags, main_tokens[n]) orelse @intCast(tree.tokens.len - 1);
652+
.block => {
653+
std.debug.assert(datas[n].rhs - datas[n].lhs > 0);
654+
const token = lastToken(tree, tree.extra_data[datas[n].rhs - 1]) + 1; // for the rbrace
655+
return end_offset + (findMatchingRBrace(tree, token) orelse token);
685656
},
686-
.container_decl_trailing,
687-
.container_decl_two_trailing,
688-
.container_decl_two,
689-
=> {
690-
// + 1 for the lbrace
691-
return findMatchingRBrace(token_tags, main_tokens[n] + 1) orelse @intCast(tree.tokens.len - 1);
657+
.block_two, .container_decl_two => {
658+
if (datas[n].rhs != 0) {
659+
const token = tree.lastToken(datas[n].rhs) + 1; // for the rparen/rbrace
660+
return end_offset + (findMatchingRBrace(tree, token) orelse token);
661+
} else if (datas[n].lhs != 0) {
662+
const token = tree.lastToken(datas[n].lhs) + 1; // for the rparen/rbrace
663+
return end_offset + (findMatchingRBrace(tree, token) orelse token);
664+
} else {
665+
const token: TokenIndex = switch (tags[n]) {
666+
.block_two => main_tokens[n] + 1, // rbrace
667+
.container_decl_two => main_tokens[n] + 2, // lbrace + rbrace
668+
else => unreachable,
669+
};
670+
return end_offset + (findMatchingRBrace(tree, token) orelse token);
671+
}
692672
},
693673
.container_decl_arg,
694674
.container_decl_arg_trailing,
695675
=> {
696-
// + 4 for the lparen, identifier, rparen, lbrace
697-
const l_brace = findNextLBrace(token_tags, main_tokens[n]) orelse return @intCast(tree.tokens.len - 1);
698-
return findMatchingRBrace(token_tags, l_brace) orelse @intCast(tree.tokens.len - 1);
676+
const members = tree.extraData(datas[n].rhs, Node.SubRange);
677+
if (members.end - members.start == 0) {
678+
const token = tree.lastToken(datas[n].lhs) + 3; // // for the rparen + lbrace + rbrace
679+
return end_offset + (findMatchingRBrace(tree, token) orelse token);
680+
} else {
681+
const token = tree.lastToken(tree.extra_data[members.end - 1]) + 1; // for the rbrace
682+
return end_offset + (findMatchingRBrace(tree, token) orelse token);
683+
}
699684
},
700685
.array_init_dot_two,
701686
.builtin_call_two,
@@ -726,8 +711,10 @@ pub fn lastToken(tree: Ast, node: Ast.Node.Index) Ast.TokenIndex {
726711
},
727712
.array_init_dot_two_comma,
728713
.builtin_call_two_comma,
714+
.block_two_semicolon,
729715
.struct_init_dot_two_comma,
730716
.tagged_union_two_trailing,
717+
.container_decl_two_trailing,
731718
=> {
732719
end_offset += 2; // for the comma/semicolon + rbrace/rparen
733720
if (datas[n].rhs != 0) {

src/debug.zig

+15-11
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const std = @import("std");
22

33
const analysis = @import("analysis.zig");
44
const offsets = @import("offsets.zig");
5+
const DocumentScope = @import("DocumentScope.zig");
56

67
pub fn printTree(tree: std.zig.Ast) void {
78
if (!std.debug.runtime_safety) @compileError("this function should only be used in debug mode!");
@@ -38,34 +39,37 @@ pub fn printTree(tree: std.zig.Ast) void {
3839
}
3940
}
4041

41-
pub fn printDocumentScope(doc_scope: analysis.DocumentScope) void {
42+
pub fn printDocumentScope(doc_scope: DocumentScope) void {
4243
if (!std.debug.runtime_safety) @compileError("this function should only be used in debug mode!");
4344

4445
for (0..doc_scope.scopes.len) |index| {
46+
const scope_index: DocumentScope.Scope.Index = @enumFromInt(index);
4547
const scope = doc_scope.scopes.get(index);
4648
if (index != 0) std.debug.print("\n\n", .{});
4749
std.debug.print(
4850
\\[{d}, {d}]
49-
\\ data: {}
51+
\\ tag: {}
52+
\\ ast node: {?}
5053
\\ parent: {}
5154
\\ child scopes: {any}
5255
\\ usingnamespaces: {any}
53-
\\ tests: {any}
5456
\\ decls:
5557
\\
5658
, .{
5759
scope.loc.start,
5860
scope.loc.end,
59-
scope.data,
60-
scope.parent,
61-
scope.child_scopes.items,
62-
scope.uses,
63-
scope.tests,
61+
scope.data.tag,
62+
doc_scope.getScopeAstNode(scope_index),
63+
doc_scope.getScopeParent(scope_index),
64+
doc_scope.getScopeChildScopesConst(scope_index),
65+
doc_scope.getScopeUsingnamespaceNodesConst(scope_index),
6466
});
6567

66-
var decl_it = scope.decls.iterator();
67-
while (decl_it.next()) |entry| {
68-
std.debug.print(" - {s:<8} {}\n", .{ entry.key_ptr.name, entry.value_ptr.* });
68+
for (doc_scope.getScopeDeclarationsConst(scope_index)) |decl| {
69+
std.debug.print(" - {s:<8} {}\n", .{
70+
doc_scope.declaration_lookup_map.keys()[@intFromEnum(decl)].name,
71+
doc_scope.declarations.get(@intFromEnum(decl)),
72+
});
6973
}
7074
}
7175
}

src/features/code_actions.zig

+2-2
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ fn handleUnusedFunctionParameter(builder: *Builder, actions: *std.ArrayListUnman
100100
loc.start,
101101
)) orelse return;
102102

103-
const payload = switch (decl.decl.*) {
103+
const payload = switch (decl.decl) {
104104
.param_payload => |pay| pay,
105105
else => return,
106106
};
@@ -147,7 +147,7 @@ fn handleUnusedVariableOrConstant(builder: *Builder, actions: *std.ArrayListUnma
147147
loc.start,
148148
)) orelse return;
149149

150-
const node = switch (decl.decl.*) {
150+
const node = switch (decl.decl) {
151151
.ast_node => |node| node,
152152
.assign_destructure => |payload| payload.node,
153153
else => return,

src/features/completions.zig

+7-6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const ast = @import("../ast.zig");
1111
const offsets = @import("../offsets.zig");
1212
const tracy = @import("../tracy.zig");
1313
const URI = @import("../uri.zig");
14+
const DocumentScope = @import("../DocumentScope.zig");
1415
const analyser_completions = @import("../analyser/completions.zig");
1516

1617
const data = @import("version_data");
@@ -91,7 +92,7 @@ fn completionDoc(
9192
arena: std.mem.Allocator,
9293
either_descriptor: ?[]const u8,
9394
doc_comments: ?[]const u8,
94-
) error{OutOfMemory}!@TypeOf(@as(types.CompletionItem, undefined).documentation) {
95+
) error{OutOfMemory}!std.meta.FieldType(types.CompletionItem, .documentation) {
9596
var list = std.ArrayList(u8).init(arena);
9697
const writer = list.writer();
9798

@@ -383,7 +384,7 @@ fn declToCompletion(context: DeclToCompletionContext, decl_handle: Analyser.Decl
383384
defer tracy_zone.end();
384385

385386
const tree = decl_handle.handle.tree;
386-
const decl = decl_handle.decl.*;
387+
const decl = decl_handle.decl;
387388

388389
const is_cimport = std.mem.eql(u8, std.fs.path.basename(decl_handle.handle.uri), "cimport.zig");
389390
if (is_cimport) {
@@ -401,7 +402,7 @@ fn declToCompletion(context: DeclToCompletionContext, decl_handle: Analyser.Decl
401402
if (exclusions.has(name)) return;
402403
}
403404

404-
switch (decl_handle.decl.*) {
405+
switch (decl_handle.decl) {
405406
.ast_node => |node| try nodeToCompletion(
406407
context.server,
407408
context.analyser,
@@ -846,7 +847,7 @@ fn completeFileSystemStringLiteral(
846847
handle: DocumentStore.Handle,
847848
pos_context: Analyser.PositionContext,
848849
) ![]types.CompletionItem {
849-
var completions: Analyser.CompletionSet = .{};
850+
var completions: DocumentScope.CompletionSet = .{};
850851

851852
const loc = pos_context.loc().?;
852853
var completing = handle.tree.source[loc.start + 1 .. loc.end - 1];
@@ -1296,7 +1297,7 @@ fn collectVarAccessContainerNodes(
12961297
.func = fn_proto_node,
12971298
.param_index = @intCast(dot_context.fn_arg_index),
12981299
} };
1299-
const fn_param_decl_with_handle = Analyser.DeclWithHandle{ .decl = &fn_param_decl, .handle = symbol_decl.handle };
1300+
const fn_param_decl_with_handle = Analyser.DeclWithHandle{ .decl = fn_param_decl, .handle = symbol_decl.handle };
13001301
const param_type = try fn_param_decl_with_handle.resolveType(analyser) orelse return;
13011302
try types_with_handles.append(arena, param_type);
13021303
return;
@@ -1330,7 +1331,7 @@ fn collectFieldAccessContainerNodesHelper(
13301331
return;
13311332
}
13321333
}
1333-
// XXX use-case: resolves `if (symbol_decl.decl.* != .`
1334+
// XXX use-case: resolves `if (symbol_decl.decl != .`
13341335
if (container.type.data == .other) {
13351336
const node_type = try analyser.resolveTypeOfNode(.{ .node = container.type.data.other, .handle = container.handle }) orelse return;
13361337
if (node_type.isEnumType() or node_type.isUnionType()) {

src/features/hover.zig

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub fn hoverSymbol(
3232

3333
var is_fn = false;
3434

35-
const def_str = switch (decl_handle.decl.*) {
35+
const def_str = switch (decl_handle.decl) {
3636
.ast_node => |node| def: {
3737
if (try analyser.resolveVarDeclAlias(.{ .node = node, .handle = handle })) |result| {
3838
return try hoverSymbol(analyser, arena, result, markup_kind, doc_str);

src/features/inlay_hints.zig

+2-2
Original file line numberDiff line numberDiff line change
@@ -101,15 +101,15 @@ fn writeCallHint(builder: *Builder, call: Ast.full.Call, decl_handle: Analyser.D
101101
const handle = builder.handle;
102102
const tree = handle.tree;
103103

104-
const node = switch (decl_handle.decl.*) {
104+
const node = switch (decl_handle.decl) {
105105
.ast_node => |node| node,
106106
else => return,
107107
};
108108

109109
const maybe_resolved_alias = try builder.analyser.resolveVarDeclAlias(.{ .node = node, .handle = decl_handle.handle });
110110
const resolved_decl_handle = if (maybe_resolved_alias) |resolved_decl| resolved_decl else decl_handle;
111111

112-
const fn_node = switch (resolved_decl_handle.decl.*) {
112+
const fn_node = switch (resolved_decl_handle.decl) {
113113
.ast_node => |fn_node| fn_node,
114114
else => return,
115115
};

0 commit comments

Comments
 (0)