Correctly handle inferred error sets, add self argument support when evaluating bound type params, semanticToken scaffolding
This commit is contained in:
parent
5c78e88b19
commit
112f6d735a
@ -277,7 +277,14 @@ fn resolveReturnType(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return switch (fn_decl.return_type) {
|
return switch (fn_decl.return_type) {
|
||||||
.Explicit, .InferErrorSet => |return_type| try resolveTypeOfNodeInternal(store, arena, .{
|
.InferErrorSet => |return_type| block: {
|
||||||
|
const child_type = (try resolveTypeOfNodeInternal(store, arena, .{
|
||||||
|
.node = return_type,
|
||||||
|
.handle = handle,
|
||||||
|
}, bound_type_params)) orelse return null;
|
||||||
|
break :block NodeWithHandle{ .node = try makeErrorUnionType(arena, child_type.node), .handle = child_type.handle };
|
||||||
|
},
|
||||||
|
.Explicit => |return_type| try resolveTypeOfNodeInternal(store, arena, .{
|
||||||
.node = return_type,
|
.node = return_type,
|
||||||
.handle = handle,
|
.handle = handle,
|
||||||
}, bound_type_params),
|
}, bound_type_params),
|
||||||
@ -345,9 +352,22 @@ fn resolveDerefType(
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn makeSliceType(arena: *std.heap.ArenaAllocator, child_type: *ast.Node) ?*ast.Node {
|
fn makeErrorUnionType(arena: *std.heap.ArenaAllocator, child_type: *ast.Node) !*ast.Node {
|
||||||
// TODO: Better values for fields, better way to do this?
|
// TODO: Better values for fields, better way to do this?
|
||||||
var slice_type = arena.allocator.create(ast.Node.PrefixOp) catch return null;
|
var error_union_type = try arena.allocator.create(ast.Node.InfixOp);
|
||||||
|
error_union_type.* = .{
|
||||||
|
.op_token = child_type.firstToken(),
|
||||||
|
.op = .ErrorUnion,
|
||||||
|
.lhs = child_type, // This is a dummy value
|
||||||
|
.rhs = child_type,
|
||||||
|
};
|
||||||
|
|
||||||
|
return &error_union_type.base;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn makeSliceType(arena: *std.heap.ArenaAllocator, child_type: *ast.Node) !*ast.Node {
|
||||||
|
// TODO: Better values for fields, better way to do this?
|
||||||
|
var slice_type = try arena.allocator.create(ast.Node.PrefixOp);
|
||||||
slice_type.* = .{
|
slice_type.* = .{
|
||||||
.op_token = child_type.firstToken(),
|
.op_token = child_type.firstToken(),
|
||||||
.op = .{
|
.op = .{
|
||||||
@ -389,7 +409,7 @@ fn resolveBracketAccessType(
|
|||||||
.node = pop.rhs,
|
.node = pop.rhs,
|
||||||
.handle = lhs.handle,
|
.handle = lhs.handle,
|
||||||
}, bound_type_params);
|
}, bound_type_params);
|
||||||
return NodeWithHandle{ .node = makeSliceType(arena, pop.rhs) orelse return null, .handle = lhs.handle };
|
return NodeWithHandle{ .node = try makeSliceType(arena, pop.rhs), .handle = lhs.handle };
|
||||||
},
|
},
|
||||||
.PtrType => {
|
.PtrType => {
|
||||||
if (pop.rhs.cast(std.zig.ast.Node.PrefixOp)) |child_pop| {
|
if (pop.rhs.cast(std.zig.ast.Node.PrefixOp)) |child_pop| {
|
||||||
@ -455,9 +475,17 @@ fn resolveTypeOfNodeInternal(
|
|||||||
|
|
||||||
const decl = (try resolveTypeOfNodeInternal(store, arena, .{ .node = call.lhs, .handle = handle }, bound_type_params)) orelse return null;
|
const decl = (try resolveTypeOfNodeInternal(store, arena, .{ .node = call.lhs, .handle = handle }, bound_type_params)) orelse return null;
|
||||||
if (decl.node.cast(ast.Node.FnProto)) |fn_decl| {
|
if (decl.node.cast(ast.Node.FnProto)) |fn_decl| {
|
||||||
|
var has_self_param: u8 = 0;
|
||||||
|
if (call.lhs.cast(ast.Node.InfixOp)) |lhs_infix_op| {
|
||||||
|
if (lhs_infix_op.op == .Period) {
|
||||||
|
has_self_param = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Bidn type params to the expressions passed in the calls.
|
// Bidn type params to the expressions passed in the calls.
|
||||||
const param_len = std.math.min(call.params_len, fn_decl.params_len);
|
const param_len = std.math.min(call.params_len + has_self_param, fn_decl.params_len);
|
||||||
for (fn_decl.paramsConst()) |*decl_param, param_idx| {
|
for (fn_decl.paramsConst()) |*decl_param, param_idx| {
|
||||||
|
if (param_idx < has_self_param) continue;
|
||||||
if (param_idx >= param_len) break;
|
if (param_idx >= param_len) break;
|
||||||
|
|
||||||
const type_param = switch (decl_param.param_type) {
|
const type_param = switch (decl_param.param_type) {
|
||||||
@ -467,11 +495,11 @@ fn resolveTypeOfNodeInternal(
|
|||||||
if (!type_param) continue;
|
if (!type_param) continue;
|
||||||
|
|
||||||
const call_param_type = (try resolveTypeOfNodeInternal(store, arena, .{
|
const call_param_type = (try resolveTypeOfNodeInternal(store, arena, .{
|
||||||
.node = call.paramsConst()[param_idx],
|
.node = call.paramsConst()[param_idx - has_self_param],
|
||||||
.handle = handle,
|
.handle = handle,
|
||||||
}, bound_type_params)) orelse continue;
|
}, bound_type_params)) orelse continue;
|
||||||
|
|
||||||
try bound_type_params.putNoClobber(decl_param, call_param_type);
|
_ = try bound_type_params.put(decl_param, call_param_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
return try resolveReturnType(store, arena, fn_decl, decl.handle, bound_type_params);
|
return try resolveReturnType(store, arena, fn_decl, decl.handle, bound_type_params);
|
||||||
|
22
src/main.zig
22
src/main.zig
@ -28,7 +28,7 @@ const ClientCapabilities = struct {
|
|||||||
var client_capabilities = ClientCapabilities{};
|
var client_capabilities = ClientCapabilities{};
|
||||||
|
|
||||||
const initialize_response =
|
const initialize_response =
|
||||||
\\,"result":{"capabilities":{"signatureHelpProvider":{"triggerCharacters":["(",","]},"textDocumentSync":1,"completionProvider":{"resolveProvider":false,"triggerCharacters":[".",":","@"]},"documentHighlightProvider":false,"hoverProvider":true,"codeActionProvider":false,"declarationProvider":true,"definitionProvider":true,"typeDefinitionProvider":true,"implementationProvider":false,"referencesProvider":false,"documentSymbolProvider":true,"colorProvider":false,"documentFormattingProvider":false,"documentRangeFormattingProvider":false,"foldingRangeProvider":false,"selectionRangeProvider":false,"workspaceSymbolProvider":false,"semanticTokensProvider":{"legend":{"tokenTypes":["type","struct","enum","parameter","variable","enumMember","function","member","keyword","modifier","comment","string","number","operator"],"tokenModifiers":["definition","async","documentation"]},"rangeProvider":false,"documentProvider":true},"workspace":{"workspaceFolders":{"supported":true,"changeNotifications":true}}}}}
|
\\,"result":{"capabilities":{"signatureHelpProvider":{"triggerCharacters":["(",","]},"textDocumentSync":1,"completionProvider":{"resolveProvider":false,"triggerCharacters":[".",":","@"]},"documentHighlightProvider":false,"hoverProvider":true,"codeActionProvider":false,"declarationProvider":true,"definitionProvider":true,"typeDefinitionProvider":true,"implementationProvider":false,"referencesProvider":false,"documentSymbolProvider":true,"colorProvider":false,"documentFormattingProvider":false,"documentRangeFormattingProvider":false,"foldingRangeProvider":false,"selectionRangeProvider":false,"workspaceSymbolProvider":false,"rangeProvider":false,"documentProvider":true},"workspace":{"workspaceFolders":{"supported":true,"changeNotifications":true}},"semanticTokensProvider":{"documentProvider":true, "legend":{"tokenTypes":["type","struct","enum","union","parameter","variable","tagField","field","function","keyword","modifier","comment","string","number","operator"],"tokenModifiers":["definition","async","documentation"]}}}}
|
||||||
;
|
;
|
||||||
|
|
||||||
const not_implemented_response =
|
const not_implemented_response =
|
||||||
@ -947,8 +947,24 @@ fn processJsonRpc(parser: *std.json.Parser, json: []const u8, config: Config) !v
|
|||||||
// Semantic highlighting
|
// Semantic highlighting
|
||||||
else if (std.mem.eql(u8, method, "textDocument/semanticTokens")) {
|
else if (std.mem.eql(u8, method, "textDocument/semanticTokens")) {
|
||||||
const params = root.Object.getValue("params").?.Object;
|
const params = root.Object.getValue("params").?.Object;
|
||||||
// TODO Implement this (we dont get here from vscode atm even when we get the client capab.)
|
const document = params.getValue("textDocument").?.Object;
|
||||||
return try respondGeneric(id, empty_array_response);
|
const uri = document.getValue("uri").?.String;
|
||||||
|
|
||||||
|
const handle = document_store.getHandle(uri) orelse {
|
||||||
|
std.debug.warn("Trying to complete in non existent document {}", .{uri});
|
||||||
|
return try respondGeneric(id, no_completions_response);
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO Actually test this in some editor, VSCode won't send me requests -_-'.
|
||||||
|
const semantic_tokens = @import("semantic_tokens.zig");
|
||||||
|
|
||||||
|
const token_array = try semantic_tokens.writeAllSemanticTokens(allocator, handle.*);
|
||||||
|
defer allocator.free(token_array);
|
||||||
|
|
||||||
|
return try send(types.Response{
|
||||||
|
.id = id,
|
||||||
|
.result = .{ .SemanticTokens = token_array },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// Autocomplete / Signatures
|
// Autocomplete / Signatures
|
||||||
else if (std.mem.eql(u8, method, "textDocument/completion")) {
|
else if (std.mem.eql(u8, method, "textDocument/completion")) {
|
||||||
|
90
src/semantic_tokens.zig
Normal file
90
src/semantic_tokens.zig
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const DocumentStore = @import("document_store.zig");
|
||||||
|
const ast = std.zig.ast;
|
||||||
|
|
||||||
|
// ["type","struct","enum","union","parameter","variable","tagField","field","function","keyword","modifier","comment","string","number","operator"]
|
||||||
|
const TokenType = enum(u32) {
|
||||||
|
type,
|
||||||
|
@"struct",
|
||||||
|
@"enum",
|
||||||
|
@"union",
|
||||||
|
parameter,
|
||||||
|
variable,
|
||||||
|
tagField,
|
||||||
|
field,
|
||||||
|
function,
|
||||||
|
keyword,
|
||||||
|
modifier,
|
||||||
|
comment,
|
||||||
|
string,
|
||||||
|
number,
|
||||||
|
operator,
|
||||||
|
};
|
||||||
|
|
||||||
|
const TokenModifiers = packed struct {
|
||||||
|
definition: bool = false,
|
||||||
|
@"async": bool = false,
|
||||||
|
documentation: bool = false,
|
||||||
|
|
||||||
|
pub fn toInt(value: u32) TokenModifiers {
|
||||||
|
return @bitCast(TokenModifiers, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toInt(self: TokenModifiers) u32 {
|
||||||
|
return @bitCast(u32, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with(lhs: TokenModifiers, rhs: TokenModifiers) TokenModifiers {
|
||||||
|
return fromInt(toInt(lhs) | toInt(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn intersect(lhs: TokenModifiers, rhs: TokenModifiers) TokenModifiers {
|
||||||
|
return fromInt(toInt(lhs) & toInt(rhs));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Builder = struct {
|
||||||
|
tree: *ast.Tree,
|
||||||
|
current_token: ?ast.TokenIndex,
|
||||||
|
arr: std.ArrayList(u32),
|
||||||
|
|
||||||
|
fn printToken(start_idx: usize, token: ast.TokenIndex, token_type: TokenType, token_modifiers: TokenModifiers) !void {
|
||||||
|
const delta_loc = self.tree.tokenLocationLoc(start_idx, token_loc);
|
||||||
|
try out_stream.print(prefix ++ "{},{},{},{},{}", .{
|
||||||
|
// TODO Is +1 on the column here correct? I think so.
|
||||||
|
delta_loc.line, delta_loc.column + 1,
|
||||||
|
token_loc.end - token_loc.start, @enumToInt(token_type),
|
||||||
|
token_modifiers.toInt(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create(allocator: *std.mem.Allocator, tree: *ast.Tree) Builder {
|
||||||
|
return Builder{
|
||||||
|
.tree = tree,
|
||||||
|
.current_token = null,
|
||||||
|
.arr = std.ArrayList(u32).init(allocator),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add(self: *Builder, out_stream: var, token: ast.TokenIndex, token_type: TokenType, token_modifiers: TokenModifiers) !void {
|
||||||
|
if (self.current_token) |current_token| {
|
||||||
|
std.debug.assert(token > current_token);
|
||||||
|
try out_stream.print(",");
|
||||||
|
try printToken(self.tree.token_locs[current_token].end, token, token_type, token_modifiers);
|
||||||
|
} else {
|
||||||
|
try printToken(0, token, token_type, token_modifiers);
|
||||||
|
}
|
||||||
|
self.current_token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toOwnedSlice(self: *Builder) []u32 {
|
||||||
|
return self.arr.toOwnedSlice();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn writeAllSemanticTokens(allocator: *std.mem.Allocator, handle: DocumentStore.Handle) ![]u32 {
|
||||||
|
// TODO Actual implementation
|
||||||
|
var builder = Builder.create(allocator, handle.tree);
|
||||||
|
|
||||||
|
return builder.toOwnedSlice();
|
||||||
|
}
|
@ -58,7 +58,8 @@ pub const ResponseParams = union(enum) {
|
|||||||
CompletionList: CompletionList,
|
CompletionList: CompletionList,
|
||||||
Location: Location,
|
Location: Location,
|
||||||
Hover: Hover,
|
Hover: Hover,
|
||||||
DocumentSymbols: []DocumentSymbol
|
DocumentSymbols: []DocumentSymbol,
|
||||||
|
SemanticTokens: []u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// JSONRPC error
|
/// JSONRPC error
|
||||||
|
Loading…
Reference in New Issue
Block a user