From 112f6d735ab911c250d7af694218ee6c9dd5268c Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Sat, 13 Jun 2020 21:20:04 +0300 Subject: [PATCH] Correctly handle inferred error sets, add self argument support when evaluating bound type params, semanticToken scaffolding --- src/analysis.zig | 42 +++++++++++++++---- src/main.zig | 22 ++++++++-- src/semantic_tokens.zig | 90 +++++++++++++++++++++++++++++++++++++++++ src/types.zig | 3 +- 4 files changed, 146 insertions(+), 11 deletions(-) create mode 100644 src/semantic_tokens.zig diff --git a/src/analysis.zig b/src/analysis.zig index 73466d9..1dc409c 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -277,7 +277,14 @@ fn resolveReturnType( } 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, .handle = handle, }, bound_type_params), @@ -345,9 +352,22 @@ fn resolveDerefType( 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? - 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.* = .{ .op_token = child_type.firstToken(), .op = .{ @@ -389,7 +409,7 @@ fn resolveBracketAccessType( .node = pop.rhs, .handle = lhs.handle, }, 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 => { 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; 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. - 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| { + if (param_idx < has_self_param) continue; if (param_idx >= param_len) break; const type_param = switch (decl_param.param_type) { @@ -467,11 +495,11 @@ fn resolveTypeOfNodeInternal( if (!type_param) continue; const call_param_type = (try resolveTypeOfNodeInternal(store, arena, .{ - .node = call.paramsConst()[param_idx], + .node = call.paramsConst()[param_idx - has_self_param], .handle = handle, }, 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); diff --git a/src/main.zig b/src/main.zig index 5678a34..351ef10 100644 --- a/src/main.zig +++ b/src/main.zig @@ -28,7 +28,7 @@ const ClientCapabilities = struct { var client_capabilities = ClientCapabilities{}; 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 = @@ -947,8 +947,24 @@ fn processJsonRpc(parser: *std.json.Parser, json: []const u8, config: Config) !v // Semantic highlighting else if (std.mem.eql(u8, method, "textDocument/semanticTokens")) { const params = root.Object.getValue("params").?.Object; - // TODO Implement this (we dont get here from vscode atm even when we get the client capab.) - return try respondGeneric(id, empty_array_response); + const document = params.getValue("textDocument").?.Object; + 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 else if (std.mem.eql(u8, method, "textDocument/completion")) { diff --git a/src/semantic_tokens.zig b/src/semantic_tokens.zig new file mode 100644 index 0000000..fc653ec --- /dev/null +++ b/src/semantic_tokens.zig @@ -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(); +} diff --git a/src/types.zig b/src/types.zig index 2162bac..8270170 100644 --- a/src/types.zig +++ b/src/types.zig @@ -58,7 +58,8 @@ pub const ResponseParams = union(enum) { CompletionList: CompletionList, Location: Location, Hover: Hover, - DocumentSymbols: []DocumentSymbol + DocumentSymbols: []DocumentSymbol, + SemanticTokens: []u32, }; /// JSONRPC error