Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/Server.zig
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ pub fn getSymbolGlobal(

return try server.analyser.lookupSymbolGlobalAdvanced(handle, name, pos_index, .{
.skip_container_fields = false,
.skip_labels = false,
});
}

Expand Down Expand Up @@ -1079,7 +1080,7 @@ pub fn generalReferencesHandler(server: *Server, request: GeneralReferencesReque
else => true,
};

const locations = if (pos_context == .label)
const locations = if (decl.decl.* == .label_decl)
try references.labelReferences(allocator, decl, server.offset_encoding, include_decl)
else
try references.symbolReferences(
Expand Down
107 changes: 92 additions & 15 deletions src/analysis.zig
Original file line number Diff line number Diff line change
Expand Up @@ -258,10 +258,45 @@ pub fn hasSelfParam(analyser: *Analyser, handle: *const DocumentStore.Handle, fu
return false;
}

pub fn getVariableSignature(tree: Ast, var_decl: Ast.full.VarDecl) []const u8 {
const start = offsets.tokenToIndex(tree, var_decl.ast.mut_token);
const end = offsets.tokenToLoc(tree, ast.lastToken(tree, var_decl.ast.init_node)).end;
return tree.source[start..end];
pub fn getVariableSignature(allocator: std.mem.Allocator, tree: Ast, var_decl: Ast.full.VarDecl) error{OutOfMemory}![]const u8 {
const node_tags = tree.nodes.items(.tag);

const start_token = var_decl.ast.mut_token;
const end_token = blk: {
const init_node = var_decl.ast.init_node;
if (init_node == 0)
break :blk start_token + 1;

if (node_tags[init_node] == .merge_error_sets)
return try std.fmt.allocPrint(allocator, "{s} error", .{
offsets.tokensToSlice(tree, start_token, tree.firstToken(init_node) - 1),
});

if (node_tags[init_node] == .error_set_decl)
break :blk tree.firstToken(init_node);

var buf: [2]Ast.Node.Index = undefined;
const container_decl = tree.fullContainerDecl(&buf, init_node) orelse
break :blk ast.lastToken(tree, init_node);

var token = container_decl.ast.main_token;
var offset: Ast.TokenIndex = 0;

// Tagged union: union(enum)
if (container_decl.ast.enum_token) |enum_token| {
token = enum_token;
offset += 1;
}

// Backing integer: struct(u32), union(enum(u32))
if (container_decl.ast.arg != 0) {
token = ast.lastToken(tree, container_decl.ast.arg);
offset += 1;
}

break :blk token + offset;
};
return offsets.tokensToSlice(tree, start_token, end_token);
}

pub fn getContainerFieldSignature(tree: Ast, field: Ast.full.ContainerField) []const u8 {
Expand Down Expand Up @@ -650,14 +685,15 @@ fn allDigits(str: []const u8) bool {
return true;
}

const primitive_types = std.ComptimeStringMap([]const u8, .{
.{ "true", "bool" },
.{ "false", "bool" },
.{ "null", "@TypeOf(null)" },
.{ "undefined", "@TypeOf(undefined)" },
});

pub fn isValueIdent(text: []const u8) bool {
const PrimitiveTypes = std.ComptimeStringMap(void, .{
.{"true"},
.{"false"},
.{"null"},
.{"undefined"},
});
return PrimitiveTypes.has(text);
return primitive_types.has(text);
}

pub fn isTypeIdent(text: []const u8) bool {
Expand Down Expand Up @@ -755,6 +791,13 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e
};
}

if (isValueIdent(name)) {
return TypeWithHandle{
.type = .{ .data = .{ .other = node }, .is_type_val = false },
.handle = handle,
};
}

if (try analyser.lookupSymbolGlobal(
handle,
name,
Expand Down Expand Up @@ -1101,6 +1144,8 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) e
.string_literal,
.char_literal,
.number_literal,
.enum_literal,
.error_value,
=> return TypeWithHandle{
.type = .{ .data = .{ .other = node }, .is_type_val = false },
.handle = handle,
Expand Down Expand Up @@ -2543,12 +2588,17 @@ pub fn lookupSymbolGlobal(analyser: *Analyser, handle: *const DocumentStore.Hand
return analyser.lookupSymbolGlobalAdvanced(handle, symbol, source_index, .{});
}

pub const LookupSymbolGlobalOptions = struct {
skip_container_fields: bool = true,
skip_labels: bool = true,
};

pub fn lookupSymbolGlobalAdvanced(
analyser: *Analyser,
handle: *const DocumentStore.Handle,
symbol: []const u8,
source_index: usize,
comptime options: struct { skip_container_fields: bool = true },
comptime options: LookupSymbolGlobalOptions,
) error{OutOfMemory}!?DeclWithHandle {
const scope_parents = handle.document_scope.scopes.items(.parent);
const scope_decls = handle.document_scope.scopes.items(.decls);
Expand All @@ -2566,7 +2616,7 @@ pub fn lookupSymbolGlobalAdvanced(
.ast_node => |node| {
if (options.skip_container_fields and node_tags[node].isContainerField()) continue;
},
.label_decl => continue,
.label_decl => if (options.skip_labels) continue,
else => {},
}
return DeclWithHandle{ .decl = candidate, .handle = handle };
Expand Down Expand Up @@ -2979,6 +3029,20 @@ const ScopeContext = struct {

try context.doc_scope.scopes.items(.decls)[@intFromEnum(scope)].put(context.allocator, name, decl_index);
}

fn putDeclLoopLabel(
context: ScopeContext,
tree: Ast,
label: Ast.TokenIndex,
node_idx: Ast.Node.Index,
) error{OutOfMemory}![]const u8 {
const label_scope = try context.pushScope(offsets.tokenToLoc(tree, label), .other);
context.popScope();

const name = tree.tokenSlice(label);
try context.putDecl(label_scope, name, .{ .label_decl = .{ .label = label, .block = node_idx } });
return name;
}
};

fn makeInnerScope(
Expand Down Expand Up @@ -3320,7 +3384,7 @@ fn makeScopeAt(
if (while_node.label_token) |label| {
std.debug.assert(token_tags[label] == .identifier);

const name = tree.tokenSlice(label);
const name = try context.putDeclLoopLabel(tree, label, node_idx);
try context.putDecl(then_scope, name, .{ .label_decl = .{ .label = label, .block = while_node.ast.then_expr } });
if (else_scope) |index| {
try context.putDecl(index, name, .{ .label_decl = .{ .label = label, .block = while_node.ast.else_expr } });
Expand Down Expand Up @@ -3380,7 +3444,7 @@ fn makeScopeAt(
if (for_node.label_token) |label| {
std.debug.assert(token_tags[label] == .identifier);

const name = tree.tokenSlice(label);
const name = try context.putDeclLoopLabel(tree, label, node_idx);
try context.putDecl(
then_scope,
name,
Expand Down Expand Up @@ -3601,6 +3665,7 @@ fn addReferencedTypes(
.container_decl_two,
.container_decl_two_trailing,
.error_set_decl,
.merge_error_sets,
.tagged_union,
.tagged_union_trailing,
.tagged_union_two,
Expand Down Expand Up @@ -3768,6 +3833,18 @@ fn addReferencedTypes(

.number_literal, .char_literal => return "comptime_int",

.enum_literal => return "@TypeOf(.enum_literal)",

.error_value => {
const identifier = tree.tokenSlice(datas[p].rhs);
return try std.fmt.allocPrint(allocator, "error{{{s}}}", .{identifier});
},

.identifier => {
const name = offsets.nodeToSlice(tree, p);
return primitive_types.get(name);
},

else => {}, // TODO: Implement more "other" type expressions; better safe than sorry
},

Expand Down
2 changes: 1 addition & 1 deletion src/features/completions.zig
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ fn nodeToCompletion(
.label = handle.tree.tokenSlice(var_decl.ast.mut_token + 1),
.kind = if (is_const) .Constant else .Variable,
.documentation = doc,
.detail = Analyser.getVariableSignature(tree, var_decl),
.detail = try Analyser.getVariableSignature(allocator, tree, var_decl),
.insertText = tree.tokenSlice(var_decl.ast.mut_token + 1),
.insertTextFormat = .PlainText,
});
Expand Down
6 changes: 3 additions & 3 deletions src/features/hover.zig
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ pub fn hoverSymbol(server: *Server, decl_handle: Analyser.DeclWithHandle, markup
&reference_collector,
);

break :def Analyser.getVariableSignature(tree, var_decl);
break :def try Analyser.getVariableSignature(server.arena.allocator(), tree, var_decl);
} else if (tree.fullFnProto(&buf, node)) |fn_proto| {
break :def Analyser.getFunctionSignature(tree, fn_proto);
} else if (tree.fullContainerField(node)) |field| {
Expand Down Expand Up @@ -111,12 +111,12 @@ pub fn hoverSymbol(server: *Server, decl_handle: Analyser.DeclWithHandle, markup
if (doc_str) |doc|
try writer.print("\n{s}", .{doc});
if (referenced_types.len > 0)
try writer.print("\n\n", .{});
try writer.print("\n\n" ++ "Go to ", .{});
for (referenced_types, 0..) |ref, index| {
if (index > 0)
try writer.print(" | ", .{});
const loc = offsets.tokenToPosition(ref.handle.tree, ref.token, server.offset_encoding);
try writer.print("Go to [{s}]({s}#L{d})", .{ ref.str, ref.handle.uri, loc.line + 1 });
try writer.print("[{s}]({s}#L{d})", .{ ref.str, ref.handle.uri, loc.line + 1 });
}
} else {
try writer.print("{s} ({s})", .{ def_str, resolved_type_str });
Expand Down
4 changes: 4 additions & 0 deletions src/offsets.zig
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@ pub fn tokenToSlice(tree: Ast, token_index: Ast.TokenIndex) []const u8 {
return locToSlice(tree.source, tokenToLoc(tree, token_index));
}

pub fn tokensToSlice(tree: Ast, first_token: Ast.TokenIndex, last_token: Ast.TokenIndex) []const u8 {
return locToSlice(tree.source, tokensToLoc(tree, first_token, last_token));
}

pub fn tokenToPosition(tree: Ast, token_index: Ast.TokenIndex, encoding: Encoding) types.Position {
const start = tokenToIndex(tree, token_index);
return indexToPosition(tree.source, start, encoding);
Expand Down