Merge pull request #111 from alexnask/master

Add usingnamespace support
This commit is contained in:
Alexandros Naskos 2020-06-11 14:25:35 +03:00 committed by GitHub
commit e5656b9651
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 1098 additions and 742 deletions

File diff suppressed because it is too large Load Diff

View File

@ -21,6 +21,7 @@ pub const Handle = struct {
count: usize,
import_uris: std.ArrayList([]const u8),
tree: *std.zig.ast.Tree,
document_scope: analysis.DocumentScope,
associated_build_file: ?*BuildFile,
is_build_file: ?*BuildFile,
@ -77,6 +78,7 @@ handles: std.StringHashMap(*Handle),
zig_exe_path: ?[]const u8,
build_files: std.ArrayListUnmanaged(*BuildFile),
build_runner_path: []const u8,
std_uri: ?[]const u8,
error_completions: TagStore,
enum_completions: TagStore,
@ -86,12 +88,14 @@ pub fn init(
allocator: *std.mem.Allocator,
zig_exe_path: ?[]const u8,
build_runner_path: []const u8,
zig_lib_path: ?[]const u8,
) !void {
self.allocator = allocator;
self.handles = std.StringHashMap(*Handle).init(allocator);
self.zig_exe_path = zig_exe_path;
self.build_files = .{};
self.build_runner_path = build_runner_path;
self.std_uri = try stdUriFromLibPath(allocator, zig_lib_path);
self.error_completions = TagStore.init(allocator);
self.enum_completions = TagStore.init(allocator);
}
@ -191,6 +195,12 @@ fn newDocument(self: *DocumentStore, uri: []const u8, text: []u8) anyerror!*Hand
var handle = try self.allocator.create(Handle);
errdefer self.allocator.destroy(handle);
const tree = try std.zig.parse(self.allocator, text);
errdefer tree.deinit();
const document_scope = try analysis.makeDocumentScope(self.allocator, tree);
errdefer document_scope.deinit(self.allocator);
handle.* = Handle{
.count = 1,
.import_uris = std.ArrayList([]const u8).init(self.allocator),
@ -199,7 +209,8 @@ fn newDocument(self: *DocumentStore, uri: []const u8, text: []u8) anyerror!*Hand
.text = text,
.mem = text,
},
.tree = try std.zig.parse(self.allocator, text),
.tree = tree,
.document_scope = document_scope,
.associated_build_file = null,
.is_build_file = null,
};
@ -362,6 +373,9 @@ fn refreshDocument(self: *DocumentStore, handle: *Handle, zig_lib_path: ?[]const
handle.tree.deinit();
handle.tree = try std.zig.parse(self.allocator, handle.document.text);
handle.document_scope.deinit(self.allocator);
handle.document_scope = try analysis.makeDocumentScope(self.allocator, handle.tree);
// TODO: Better algorithm or data structure?
// Removing the imports is costly since they live in an array list
// Perhaps we should use an AutoHashMap([]const u8, {}) ?
@ -382,7 +396,7 @@ fn refreshDocument(self: *DocumentStore, handle: *Handle, zig_lib_path: ?[]const
const std_uri = try stdUriFromLibPath(&arena.allocator, zig_lib_path);
for (import_strs.items) |str| {
const uri = (try uriFromImportStr(self, &arena.allocator, handle.*, str, std_uri)) orelse continue;
const uri = (try self.uriFromImportStr(&arena.allocator, handle.*, str)) orelse continue;
var idx: usize = 0;
exists_loop: while (idx < still_exist.len) : (idx += 1) {
@ -485,14 +499,13 @@ pub fn applyChanges(
}
pub fn uriFromImportStr(
store: *DocumentStore,
self: *DocumentStore,
allocator: *std.mem.Allocator,
handle: Handle,
import_str: []const u8,
std_uri: ?[]const u8,
) !?[]const u8 {
if (std.mem.eql(u8, import_str, "std")) {
if (std_uri) |uri| return try std.mem.dupe(allocator, u8, uri) else {
if (self.std_uri) |uri| return try std.mem.dupe(allocator, u8, uri) else {
std.debug.warn("Cannot resolve std library import, path is null.\n", .{});
return null;
}
@ -523,49 +536,12 @@ pub fn uriFromImportStr(
}
}
pub const AnalysisContext = struct {
store: *DocumentStore,
handle: *Handle,
// This arena is used for temporary allocations while analyzing,
// not for the tree allocations.
arena: *std.heap.ArenaAllocator,
scope_nodes: []*std.zig.ast.Node,
in_container: *std.zig.ast.Node,
std_uri: ?[]const u8,
error_completions: *TagStore,
enum_completions: *TagStore,
pub fn tree(self: AnalysisContext) *std.zig.ast.Tree {
return self.handle.tree;
}
fn refreshScopeNodes(self: *AnalysisContext) !void {
var scope_nodes = std.ArrayList(*std.zig.ast.Node).init(&self.arena.allocator);
try analysis.addChildrenNodes(&scope_nodes, self.tree(), &self.tree().root_node.base);
self.scope_nodes = scope_nodes.items;
self.in_container = &self.tree().root_node.base;
}
pub fn onContainer(self: *AnalysisContext, container: *std.zig.ast.Node) !void {
std.debug.assert(container.id == .ContainerDecl or container.id == .Root);
if (self.in_container != container) {
self.in_container = container;
var scope_nodes = std.ArrayList(*std.zig.ast.Node).fromOwnedSlice(&self.arena.allocator, self.scope_nodes);
try analysis.addChildrenNodes(&scope_nodes, self.tree(), container);
self.scope_nodes = scope_nodes.items;
}
}
pub fn onImport(self: *AnalysisContext, import_str: []const u8) !?*std.zig.ast.Node {
const allocator = self.store.allocator;
const final_uri = (try uriFromImportStr(
self.store,
self.store.allocator,
self.handle.*,
pub fn resolveImport(self: *DocumentStore, handle: *Handle, import_str: []const u8) !?*Handle {
const allocator = self.allocator;
const final_uri = (try self.uriFromImportStr(
self.allocator,
handle.*,
import_str,
self.std_uri,
)) orelse return null;
std.debug.warn("Import final URI: {}\n", .{final_uri});
@ -573,27 +549,23 @@ pub const AnalysisContext = struct {
defer if (!consumed_final_uri) allocator.free(final_uri);
// Check if we already imported this.
for (self.handle.import_uris.items) |uri| {
for (handle.import_uris.items) |uri| {
// If we did, set our new handle and return the parsed tree root node.
if (std.mem.eql(u8, uri, final_uri)) {
self.handle = self.store.getHandle(final_uri) orelse return null;
try self.refreshScopeNodes();
return &self.tree().root_node.base;
return self.getHandle(final_uri);
}
}
// New import.
// Check if the import is already opened by others.
if (self.store.getHandle(final_uri)) |new_handle| {
if (self.getHandle(final_uri)) |new_handle| {
// If it is, append it to our imports, increment the count, set our new handle
// and return the parsed tree root node.
try self.handle.import_uris.append(final_uri);
try handle.import_uris.append(final_uri);
consumed_final_uri = true;
new_handle.count += 1;
self.handle = new_handle;
try self.refreshScopeNodes();
return &self.tree().root_node.base;
return new_handle;
}
// New document, read the file then call into openDocument.
@ -618,37 +590,18 @@ pub const AnalysisContext = struct {
};
// Add to import table of current handle.
try self.handle.import_uris.append(final_uri);
try handle.import_uris.append(final_uri);
consumed_final_uri = true;
// Swap handles.
// This takes ownership of the passed uri and text.
const duped_final_uri = try std.mem.dupe(allocator, u8, final_uri);
errdefer allocator.free(duped_final_uri);
self.handle = try newDocument(self.store, duped_final_uri, file_contents);
return try self.newDocument(duped_final_uri, file_contents);
}
}
try self.refreshScopeNodes();
return &self.tree().root_node.base;
}
pub fn clone(self: *AnalysisContext) !AnalysisContext {
// Copy the cope nodes, the rest are references
// that are not owned by the context.
return AnalysisContext{
.store = self.store,
.handle = self.handle,
.arena = self.arena,
.scope_nodes = try std.mem.dupe(&self.arena.allocator, *std.zig.ast.Node, self.scope_nodes),
.in_container = self.in_container,
.std_uri = self.std_uri,
.error_completions = self.error_completions,
.enum_completions = self.enum_completions,
};
}
};
pub fn stdUriFromLibPath(allocator: *std.mem.Allocator, zig_lib_path: ?[]const u8) !?[]const u8 {
fn stdUriFromLibPath(allocator: *std.mem.Allocator, zig_lib_path: ?[]const u8) !?[]const u8 {
if (zig_lib_path) |zpath| {
const std_path = std.fs.path.resolve(allocator, &[_][]const u8{
zpath, "./std/std.zig",
@ -665,29 +618,6 @@ pub fn stdUriFromLibPath(allocator: *std.mem.Allocator, zig_lib_path: ?[]const u
return null;
}
pub fn analysisContext(
self: *DocumentStore,
handle: *Handle,
arena: *std.heap.ArenaAllocator,
position: usize,
zig_lib_path: ?[]const u8,
) !AnalysisContext {
var scope_nodes = std.ArrayList(*std.zig.ast.Node).init(&arena.allocator);
const in_container = try analysis.declsFromIndex(arena, &scope_nodes, handle.tree, position);
const std_uri = try stdUriFromLibPath(&arena.allocator, zig_lib_path);
return AnalysisContext{
.store = self,
.handle = handle,
.arena = arena,
.scope_nodes = scope_nodes.items,
.in_container = in_container,
.std_uri = std_uri,
.error_completions = &self.error_completions,
.enum_completions = &self.enum_completions,
};
}
pub fn deinit(self: *DocumentStore) void {
var entry_iterator = self.handles.iterator();
while (entry_iterator.next()) |entry| {
@ -700,6 +630,8 @@ pub fn deinit(self: *DocumentStore) void {
entry.value.import_uris.deinit();
self.allocator.free(entry.key);
self.allocator.destroy(entry.value);
entry.value.document_scope.deinit(self.allocator);
}
self.handles.deinit();
@ -713,6 +645,10 @@ pub fn deinit(self: *DocumentStore) void {
self.allocator.destroy(build_file);
}
if (self.std_uri) |std_uri| {
self.allocator.free(std_uri);
}
self.build_files.deinit(self.allocator);
self.error_completions.deinit();
self.enum_completions.deinit();

View File

@ -98,7 +98,7 @@ fn showMessage(@"type": types.MessageType, message: []const u8) !void {
.params = .{
.ShowMessageParams = .{
.@"type" = @"type",
.message = message
.message = message,
},
},
});
@ -196,33 +196,14 @@ fn publishDiagnostics(handle: DocumentStore.Handle, config: Config) !void {
});
}
fn containerToCompletion(
list: *std.ArrayList(types.CompletionItem),
analysis_ctx: *DocumentStore.AnalysisContext,
orig_handle: *DocumentStore.Handle,
container: *std.zig.ast.Node,
config: Config,
) !void {
var child_idx: usize = 0;
while (container.iterate(child_idx)) |child_node| : (child_idx += 1) {
// Declarations in the same file do not need to be public.
if (orig_handle == analysis_ctx.handle or analysis.isNodePublic(analysis_ctx.tree(), child_node)) {
try nodeToCompletion(list, analysis_ctx, orig_handle, child_node, config);
}
}
}
fn resolveVarDeclFnAlias(arena: *std.heap.ArenaAllocator, decl_handle: analysis.NodeWithHandle) !analysis.NodeWithHandle {
const decl = decl_handle.node;
const handle = decl_handle.handle;
const ResolveVarDeclFnAliasRewsult = struct {
decl: *std.zig.ast.Node,
analysis_ctx: DocumentStore.AnalysisContext,
};
fn resolveVarDeclFnAlias(analysis_ctx: *DocumentStore.AnalysisContext, decl: *std.zig.ast.Node) !ResolveVarDeclFnAliasRewsult {
var child_analysis_context = try analysis_ctx.clone();
if (decl.cast(std.zig.ast.Node.VarDecl)) |var_decl| {
const child_node = block: {
if (var_decl.type_node) |type_node| {
if (std.mem.eql(u8, "type", analysis_ctx.tree().tokenSlice(type_node.firstToken()))) {
if (std.mem.eql(u8, "type", handle.tree.tokenSlice(type_node.firstToken()))) {
break :block var_decl.init_node orelse type_node;
}
break :block type_node;
@ -230,29 +211,29 @@ fn resolveVarDeclFnAlias(analysis_ctx: *DocumentStore.AnalysisContext, decl: *st
break :block var_decl.init_node.?;
};
if (analysis.resolveTypeOfNode(&child_analysis_context, child_node)) |resolved_node| {
if (resolved_node.id == .FnProto) {
return ResolveVarDeclFnAliasRewsult{
.decl = resolved_node,
.analysis_ctx = child_analysis_context,
};
if (try analysis.resolveTypeOfNode(&document_store, arena, .{ .node = child_node, .handle = handle })) |resolved_node| {
// TODO Just return it anyway?
// This would allow deep goto definition etc.
// Try it out.
if (resolved_node.node.id == .FnProto) {
return resolved_node;
}
}
}
return ResolveVarDeclFnAliasRewsult{
.decl = decl,
.analysis_ctx = analysis_ctx.*,
};
return decl_handle;
}
fn nodeToCompletion(
arena: *std.heap.ArenaAllocator,
list: *std.ArrayList(types.CompletionItem),
analysis_ctx: *DocumentStore.AnalysisContext,
node_handle: analysis.NodeWithHandle,
orig_handle: *DocumentStore.Handle,
node: *std.zig.ast.Node,
config: Config,
) error{OutOfMemory}!void {
const doc = if (try analysis.getDocComments(list.allocator, analysis_ctx.tree(), node)) |doc_comments|
const node = node_handle.node;
const handle = node_handle.handle;
const doc = if (try analysis.getDocComments(list.allocator, handle.tree, node)) |doc_comments|
types.MarkupContent{
.kind = .Markdown,
.value = doc_comments,
@ -262,7 +243,13 @@ fn nodeToCompletion(
switch (node.id) {
.ErrorSetDecl, .Root, .ContainerDecl => {
try containerToCompletion(list, analysis_ctx, orig_handle, node, config);
const context = DeclToCompletionContext{
.completions = list,
.config = &config,
.arena = arena,
.orig_handle = orig_handle,
};
try analysis.iterateSymbolsContainer(&document_store, arena, node_handle, orig_handle, declToCompletion, context);
},
.FnProto => {
const func = node.cast(std.zig.ast.Node.FnProto).?;
@ -271,30 +258,47 @@ fn nodeToCompletion(
const insert_text = if (use_snippets) blk: {
const skip_self_param = if (func.params_len > 0) param_check: {
var child_analysis_ctx = try analysis_ctx.clone();
break :param_check switch (func.paramsConst()[0].param_type) {
.type_expr => |type_node| if (analysis_ctx.in_container == analysis.resolveTypeOfNode(&child_analysis_ctx, type_node))
true
else if (type_node.cast(std.zig.ast.Node.PrefixOp)) |prefix_op|
prefix_op.op == .PtrType and analysis_ctx.in_container == analysis.resolveTypeOfNode(&child_analysis_ctx, prefix_op.rhs)
else
false,
else => false,
};
const in_container = analysis.innermostContainer(handle, handle.tree.token_locs[func.firstToken()].start);
switch (func.paramsConst()[0].param_type) {
.type_expr => |type_node| {
if (try analysis.resolveTypeOfNode(&document_store, arena, .{
.node = type_node,
.handle = handle,
})) |resolved_type| {
if (in_container.node == resolved_type.node)
break :param_check true;
}
if (type_node.cast(std.zig.ast.Node.PrefixOp)) |prefix_op| {
if (prefix_op.op == .PtrType) {
if (try analysis.resolveTypeOfNode(&document_store, arena, .{
.node = prefix_op.rhs,
.handle = handle,
})) |resolved_prefix_op| {
if (in_container.node == resolved_prefix_op.node)
break :param_check true;
}
}
}
break :param_check false;
},
else => break :param_check false,
}
} else
false;
break :blk try analysis.getFunctionSnippet(list.allocator, analysis_ctx.tree(), func, skip_self_param);
break :blk try analysis.getFunctionSnippet(&arena.allocator, handle.tree, func, skip_self_param);
} else
null;
const is_type_function = analysis.isTypeFunction(analysis_ctx.tree(), func);
const is_type_function = analysis.isTypeFunction(handle.tree, func);
try list.append(.{
.label = analysis_ctx.tree().tokenSlice(name_token),
.label = handle.tree.tokenSlice(name_token),
.kind = if (is_type_function) .Struct else .Function,
.documentation = doc,
.detail = analysis.getFunctionSignature(analysis_ctx.tree(), func),
.detail = analysis.getFunctionSignature(handle.tree, func),
.insertText = insert_text,
.insertTextFormat = if (use_snippets) .Snippet else .PlainText,
});
@ -302,18 +306,27 @@ fn nodeToCompletion(
},
.VarDecl => {
const var_decl = node.cast(std.zig.ast.Node.VarDecl).?;
const is_const = analysis_ctx.tree().token_ids[var_decl.mut_token] == .Keyword_const;
const is_const = handle.tree.token_ids[var_decl.mut_token] == .Keyword_const;
var result = try resolveVarDeclFnAlias(analysis_ctx, node);
if (result.decl != node) {
return try nodeToCompletion(list, &result.analysis_ctx, orig_handle, result.decl, config);
const result = try resolveVarDeclFnAlias(arena, node_handle);
if (result.node != node) {
return try nodeToCompletion(arena, list, result, orig_handle, config);
}
try list.append(.{
.label = analysis_ctx.tree().tokenSlice(var_decl.name_token),
.label = handle.tree.tokenSlice(var_decl.name_token),
.kind = if (is_const) .Constant else .Variable,
.documentation = doc,
.detail = analysis.getVariableSignature(analysis_ctx.tree(), var_decl),
.detail = analysis.getVariableSignature(handle.tree, var_decl),
});
},
.ContainerField => {
const field = node.cast(std.zig.ast.Node.ContainerField).?;
try list.append(.{
.label = handle.tree.tokenSlice(field.name_token),
.kind = .Field,
.documentation = doc,
.detail = analysis.getContainerFieldSignature(handle.tree, field),
});
},
.PrefixOp => {
@ -346,12 +359,12 @@ fn nodeToCompletion(
.kind = .Field,
});
},
else => if (analysis.nodeToString(analysis_ctx.tree(), node)) |string| {
else => if (analysis.nodeToString(handle.tree, node)) |string| {
try list.append(.{
.label = string,
.kind = .Field,
.documentation = doc,
.detail = analysis_ctx.tree().getNodeSource(node)
.detail = handle.tree.getNodeSource(node),
});
},
}
@ -376,44 +389,94 @@ fn identifierFromPosition(pos_index: usize, handle: DocumentStore.Handle) []cons
return text[start_idx + 1 .. end_idx];
}
fn gotoDefinitionSymbol(id: types.RequestId, analysis_ctx: *DocumentStore.AnalysisContext, decl: *std.zig.ast.Node) !void {
const result = try resolveVarDeclFnAlias(analysis_ctx, decl);
fn gotoDefinitionSymbol(id: types.RequestId, arena: *std.heap.ArenaAllocator, decl_handle: analysis.DeclWithHandle) !void {
var handle = decl_handle.handle;
const name_token = analysis.getDeclNameToken(result.analysis_ctx.tree(), result.decl) orelse
const location = switch (decl_handle.decl.*) {
.ast_node => |node| block: {
const result = try resolveVarDeclFnAlias(arena, .{ .node = node, .handle = handle });
handle = result.handle;
const name_token = analysis.getDeclNameToken(result.handle.tree, result.node) orelse
return try respondGeneric(id, null_result_response);
break :block result.handle.tree.tokenLocation(0, name_token);
},
else => decl_handle.location(),
};
try send(types.Response{
.id = id,
.result = .{
.Location = .{
.uri = result.analysis_ctx.handle.document.uri,
.range = astLocationToRange(result.analysis_ctx.tree().tokenLocation(0, name_token)),
.uri = handle.document.uri,
.range = astLocationToRange(location),
},
},
});
}
fn hoverSymbol(id: types.RequestId, analysis_ctx: *DocumentStore.AnalysisContext, decl: *std.zig.ast.Node) !void {
const result = try resolveVarDeclFnAlias(analysis_ctx, decl);
fn hoverSymbol(id: types.RequestId, arena: *std.heap.ArenaAllocator, decl_handle: analysis.DeclWithHandle) !void {
const handle = decl_handle.handle;
const doc_str = if (try analysis.getDocComments(&analysis_ctx.arena.allocator, result.analysis_ctx.tree(), result.decl)) |str|
const md_string = switch (decl_handle.decl.*) {
.ast_node => |node| ast_node: {
const result = try resolveVarDeclFnAlias(arena, .{ .node = node, .handle = handle });
const doc_str = if (try analysis.getDocComments(&arena.allocator, result.handle.tree, result.node)) |str|
str
else
"";
const signature_str = switch (result.decl.id) {
const signature_str = switch (result.node.id) {
.VarDecl => blk: {
const var_decl = result.decl.cast(std.zig.ast.Node.VarDecl).?;
break :blk analysis.getVariableSignature(result.analysis_ctx.tree(), var_decl);
const var_decl = result.node.cast(std.zig.ast.Node.VarDecl).?;
break :blk analysis.getVariableSignature(result.handle.tree, var_decl);
},
.FnProto => blk: {
const fn_decl = result.decl.cast(std.zig.ast.Node.FnProto).?;
break :blk analysis.getFunctionSignature(result.analysis_ctx.tree(), fn_decl);
const fn_decl = result.node.cast(std.zig.ast.Node.FnProto).?;
break :blk analysis.getFunctionSignature(result.handle.tree, fn_decl);
},
else => analysis.nodeToString(result.analysis_ctx.tree(), result.decl) orelse return try respondGeneric(id, null_result_response),
.ContainerField => blk: {
const field = node.cast(std.zig.ast.Node.ContainerField).?;
break :blk analysis.getContainerFieldSignature(result.handle.tree, field);
},
else => analysis.nodeToString(result.handle.tree, result.node) orelse return try respondGeneric(id, null_result_response),
};
break :ast_node try std.fmt.allocPrint(&arena.allocator, "```zig\n{}\n```\n{}", .{ signature_str, doc_str });
},
.param_decl => |param| param_decl: {
const doc_str = if (param.doc_comments) |doc_comments|
try analysis.collectDocComments(&arena.allocator, handle.tree, doc_comments)
else
"";
break :param_decl try std.fmt.allocPrint(
&arena.allocator,
"```zig\n{}\n```\n{}",
.{
handle.tree.source[handle.tree.token_locs[param.firstToken()].start..handle.tree.token_locs[param.lastToken()].end],
doc_str,
},
);
},
.pointer_payload => |payload| try std.fmt.allocPrint(
&arena.allocator,
"```zig\n{}\n```",
.{handle.tree.tokenSlice(payload.node.value_symbol.firstToken())},
),
.array_payload => |payload| try std.fmt.allocPrint(
&arena.allocator,
"```zig\n{}\n```",
.{handle.tree.tokenSlice(payload.identifier.firstToken())},
),
.switch_payload => |payload| try std.fmt.allocPrint(
&arena.allocator,
"```zig\n{}\n```",
.{handle.tree.tokenSlice(payload.node.value_symbol.firstToken())},
),
};
const md_string = try std.fmt.allocPrint(&analysis_ctx.arena.allocator, "```zig\n{}\n```\n{}", .{ signature_str, doc_str });
try send(types.Response{
.id = id,
.result = .{
@ -424,50 +487,45 @@ fn hoverSymbol(id: types.RequestId, analysis_ctx: *DocumentStore.AnalysisContext
});
}
fn getSymbolGlobal(arena: *std.heap.ArenaAllocator, pos_index: usize, handle: DocumentStore.Handle) !?*std.zig.ast.Node {
const name = identifierFromPosition(pos_index, handle);
fn getSymbolGlobal(arena: *std.heap.ArenaAllocator, pos_index: usize, handle: *DocumentStore.Handle) !?analysis.DeclWithHandle {
const name = identifierFromPosition(pos_index, handle.*);
if (name.len == 0) return null;
var decl_nodes = std.ArrayList(*std.zig.ast.Node).init(&arena.allocator);
_ = try analysis.declsFromIndex(arena, &decl_nodes, handle.tree, pos_index);
return analysis.getChildOfSlice(handle.tree, decl_nodes.items, name);
return try analysis.lookupSymbolGlobal(&document_store, arena, handle, name, pos_index);
}
fn gotoDefinitionGlobal(id: types.RequestId, pos_index: usize, handle: *DocumentStore.Handle, config: Config) !void {
var arena = std.heap.ArenaAllocator.init(allocator);
defer arena.deinit();
const decl = (try getSymbolGlobal(&arena, pos_index, handle.*)) orelse return try respondGeneric(id, null_result_response);
var analysis_ctx = try document_store.analysisContext(handle, &arena, pos_index, config.zig_lib_path);
return try gotoDefinitionSymbol(id, &analysis_ctx, decl);
const decl = (try getSymbolGlobal(&arena, pos_index, handle)) orelse return try respondGeneric(id, null_result_response);
return try gotoDefinitionSymbol(id, &arena, decl);
}
fn hoverDefinitionGlobal(id: types.RequestId, pos_index: usize, handle: *DocumentStore.Handle, config: Config) !void {
var arena = std.heap.ArenaAllocator.init(allocator);
defer arena.deinit();
const decl = (try getSymbolGlobal(&arena, pos_index, handle.*)) orelse return try respondGeneric(id, null_result_response);
var analysis_ctx = try document_store.analysisContext(handle, &arena, pos_index, config.zig_lib_path);
return try hoverSymbol(id, &analysis_ctx, decl);
const decl = (try getSymbolGlobal(&arena, pos_index, handle)) orelse return try respondGeneric(id, null_result_response);
return try hoverSymbol(id, &arena, decl);
}
fn getSymbolFieldAccess(
analysis_ctx: *DocumentStore.AnalysisContext,
handle: *DocumentStore.Handle,
arena: *std.heap.ArenaAllocator,
position: types.Position,
range: analysis.SourceRange,
config: Config,
) !?*std.zig.ast.Node {
const pos_index = try analysis_ctx.handle.document.positionToIndex(position);
var name = identifierFromPosition(pos_index, analysis_ctx.handle.*);
) !?analysis.DeclWithHandle {
const pos_index = try handle.document.positionToIndex(position);
const name = identifierFromPosition(pos_index, handle.*);
if (name.len == 0) return null;
const line = try analysis_ctx.handle.document.getLine(@intCast(usize, position.line));
const line = try handle.document.getLine(@intCast(usize, position.line));
var tokenizer = std.zig.Tokenizer.init(line[range.start..range.end]);
name = try std.mem.dupe(&analysis_ctx.arena.allocator, u8, name);
if (analysis.getFieldAccessTypeNode(analysis_ctx, &tokenizer)) |container| {
return analysis.getChild(analysis_ctx.tree(), container, name);
if (try analysis.getFieldAccessTypeNode(&document_store, arena, handle, pos_index, &tokenizer)) |container_handle| {
return try analysis.lookupSymbolContainer(&document_store, arena, container_handle, name, true);
}
return null;
}
@ -482,9 +540,8 @@ fn gotoDefinitionFieldAccess(
var arena = std.heap.ArenaAllocator.init(allocator);
defer arena.deinit();
var analysis_ctx = try document_store.analysisContext(handle, &arena, try handle.document.positionToIndex(position), config.zig_lib_path);
const decl = (try getSymbolFieldAccess(&analysis_ctx, position, range, config)) orelse return try respondGeneric(id, null_result_response);
return try gotoDefinitionSymbol(id, &analysis_ctx, decl);
const decl = (try getSymbolFieldAccess(handle, &arena, position, range, config)) orelse return try respondGeneric(id, null_result_response);
return try gotoDefinitionSymbol(id, &arena, decl);
}
fn hoverDefinitionFieldAccess(
@ -497,9 +554,8 @@ fn hoverDefinitionFieldAccess(
var arena = std.heap.ArenaAllocator.init(allocator);
defer arena.deinit();
var analysis_ctx = try document_store.analysisContext(handle, &arena, try handle.document.positionToIndex(position), config.zig_lib_path);
const decl = (try getSymbolFieldAccess(&analysis_ctx, position, range, config)) orelse return try respondGeneric(id, null_result_response);
return try hoverSymbol(id, &analysis_ctx, decl);
const decl = (try getSymbolFieldAccess(handle, &arena, position, range, config)) orelse return try respondGeneric(id, null_result_response);
return try hoverSymbol(id, &arena, decl);
}
fn gotoDefinitionString(id: types.RequestId, pos_index: usize, handle: *DocumentStore.Handle, config: Config) !void {
@ -513,7 +569,6 @@ fn gotoDefinitionString(id: types.RequestId, pos_index: usize, handle: *Document
&arena.allocator,
handle.*,
import_str,
try DocumentStore.stdUriFromLibPath(&arena.allocator, config.zig_lib_path),
)) orelse return try respondGeneric(id, null_result_response);
try send(types.Response{
@ -530,6 +585,55 @@ fn gotoDefinitionString(id: types.RequestId, pos_index: usize, handle: *Document
});
}
const DeclToCompletionContext = struct {
completions: *std.ArrayList(types.CompletionItem),
config: *const Config,
arena: *std.heap.ArenaAllocator,
orig_handle: *DocumentStore.Handle,
};
fn declToCompletion(context: DeclToCompletionContext, decl_handle: analysis.DeclWithHandle) !void {
const tree = decl_handle.handle.tree;
switch (decl_handle.decl.*) {
.ast_node => |node| try nodeToCompletion(context.arena, context.completions, .{ .node = node, .handle = decl_handle.handle }, context.orig_handle, context.config.*),
.param_decl => |param| {
const doc = if (param.doc_comments) |doc_comments|
types.MarkupContent{
.kind = .Markdown,
.value = try analysis.collectDocComments(&context.arena.allocator, tree, doc_comments),
}
else
null;
try context.completions.append(.{
.label = tree.tokenSlice(param.name_token.?),
.kind = .Constant,
.documentation = doc,
.detail = tree.source[tree.token_locs[param.firstToken()].start..tree.token_locs[param.lastToken()].end],
});
},
.pointer_payload => |payload| {
try context.completions.append(.{
.label = tree.tokenSlice(payload.node.value_symbol.firstToken()),
.kind = .Variable,
});
},
.array_payload => |payload| {
try context.completions.append(.{
.label = tree.tokenSlice(payload.identifier.firstToken()),
.kind = .Variable,
});
},
.switch_payload => |payload| {
try context.completions.append(.{
.label = tree.tokenSlice(payload.node.value_symbol.firstToken()),
.kind = .Variable,
});
},
}
}
fn completeGlobal(id: types.RequestId, pos_index: usize, handle: *DocumentStore.Handle, config: Config) !void {
// We use a local arena allocator to deallocate all temporary data without iterating
var arena = std.heap.ArenaAllocator.init(allocator);
@ -537,16 +641,13 @@ fn completeGlobal(id: types.RequestId, pos_index: usize, handle: *DocumentStore.
// Deallocate all temporary data.
defer arena.deinit();
var analysis_ctx = try document_store.analysisContext(handle, &arena, pos_index, config.zig_lib_path);
for (analysis_ctx.scope_nodes) |decl_ptr| {
var decl = decl_ptr.*;
if (decl.id == .Use) {
std.debug.warn("Found use!", .{});
continue;
}
try nodeToCompletion(&completions, &analysis_ctx, handle, decl_ptr, config);
}
const context = DeclToCompletionContext{
.completions = &completions,
.config = &config,
.arena = &arena,
.orig_handle = handle,
};
try analysis.iterateSymbolsGlobal(&document_store, &arena, handle, pos_index, declToCompletion, context);
try send(types.Response{
.id = id,
@ -563,15 +664,16 @@ fn completeFieldAccess(id: types.RequestId, handle: *DocumentStore.Handle, posit
var arena = std.heap.ArenaAllocator.init(allocator);
defer arena.deinit();
var analysis_ctx = try document_store.analysisContext(handle, &arena, try handle.document.positionToIndex(position), config.zig_lib_path);
var completions = std.ArrayList(types.CompletionItem).init(&arena.allocator);
const line = try handle.document.getLine(@intCast(usize, position.line));
var tokenizer = std.zig.Tokenizer.init(line[range.start..range.end]);
if (analysis.getFieldAccessTypeNode(&analysis_ctx, &tokenizer)) |node| {
try nodeToCompletion(&completions, &analysis_ctx, handle, node, config);
const pos_index = try handle.document.positionToIndex(position);
if (try analysis.getFieldAccessTypeNode(&document_store, &arena, handle, pos_index, &tokenizer)) |node| {
try nodeToCompletion(&arena, &completions, node, handle, config);
}
try send(types.Response{
.id = id,
.result = .{
@ -808,7 +910,7 @@ fn processJsonRpc(parser: *std.json.Parser, json: []const u8, config: Config) !v
}
// Semantic highlighting
else if (std.mem.eql(u8, method, "textDocument/semanticTokens")) {
// @TODO Implement this (we dont get here from vscode atm even when we get the client capab.)
// TODO Implement this (we dont get here from vscode atm even when we get the client capab.)
return try respondGeneric(id, empty_array_response);
}
// Autocomplete / Signatures
@ -1085,13 +1187,13 @@ pub fn main() anyerror!void {
}
if (config.build_runner_path) |build_runner_path| {
try document_store.init(allocator, zig_exe_path, try std.mem.dupe(allocator, u8, build_runner_path));
try document_store.init(allocator, zig_exe_path, try std.mem.dupe(allocator, u8, build_runner_path), config.zig_lib_path);
} else {
var exe_dir_bytes: [std.fs.MAX_PATH_BYTES]u8 = undefined;
const exe_dir_path = try std.fs.selfExeDirPath(&exe_dir_bytes);
const build_runner_path = try std.fs.path.resolve(allocator, &[_][]const u8{ exe_dir_path, "build_runner.zig" });
try document_store.init(allocator, zig_exe_path, build_runner_path);
try document_store.init(allocator, zig_exe_path, build_runner_path, config.zig_lib_path);
}
defer document_store.deinit();