Merge pull request #303 from InterplanetaryEngineer/master
Improve testing infrastructure, refactor makeScopeInternal and resolveTypeOfNodeInternal, and support "catch" scopes
This commit is contained in:
		
						commit
						09e6d9a4c4
					
				
							
								
								
									
										702
									
								
								src/analysis.zig
									
									
									
									
									
								
							
							
						
						
									
										702
									
								
								src/analysis.zig
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -138,8 +138,8 @@ fn fullIf(tree: Tree, info: full.If.Ast) full.If {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn ifFull(tree: Tree, node: Node.Index) full.If {
 | 
			
		||||
    assert(tree.nodes.items(.tag)[node] == .@"if");
 | 
			
		||||
    const data = tree.nodes.items(.data)[node];
 | 
			
		||||
    if (tree.nodes.items(.tag)[node] == .@"if") {
 | 
			
		||||
        const extra = tree.extraData(data.rhs, Node.If);
 | 
			
		||||
        return fullIf(tree, .{
 | 
			
		||||
            .cond_expr = data.lhs,
 | 
			
		||||
@ -147,17 +147,15 @@ pub fn ifFull(tree: Tree, node: Node.Index) full.If {
 | 
			
		||||
            .else_expr = extra.else_expr,
 | 
			
		||||
            .if_token = tree.nodes.items(.main_token)[node],
 | 
			
		||||
        });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn ifSimple(tree: Tree, node: Node.Index) full.If {
 | 
			
		||||
    } else {
 | 
			
		||||
        assert(tree.nodes.items(.tag)[node] == .if_simple);
 | 
			
		||||
    const data = tree.nodes.items(.data)[node];
 | 
			
		||||
        return fullIf(tree, .{
 | 
			
		||||
            .cond_expr = data.lhs,
 | 
			
		||||
            .then_expr = data.rhs,
 | 
			
		||||
            .else_expr = 0,
 | 
			
		||||
            .if_token = tree.nodes.items(.main_token)[node],
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn fullWhile(tree: Tree, info: full.While.Ast) full.While {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										122
									
								
								src/main.zig
									
									
									
									
									
								
							
							
						
						
									
										122
									
								
								src/main.zig
									
									
									
									
									
								
							@ -38,7 +38,7 @@ pub fn log(
 | 
			
		||||
    }
 | 
			
		||||
    // After shutdown, pipe output to stderr
 | 
			
		||||
    if (!keep_running) {
 | 
			
		||||
        std.debug.print("[{s}-{s}] " ++ format, .{ @tagName(message_level), @tagName(scope) } ++ args);
 | 
			
		||||
        std.debug.print("[{s}-{s}] " ++ format ++ "\n", .{ @tagName(message_level), @tagName(scope) } ++ args);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -619,37 +619,32 @@ fn hoverSymbol(
 | 
			
		||||
    const tree = handle.tree;
 | 
			
		||||
 | 
			
		||||
    const hover_kind: types.MarkupContent.Kind = if (client_capabilities.hover_supports_md) .Markdown else .PlainText;
 | 
			
		||||
    const md_string = switch (decl_handle.decl.*) {
 | 
			
		||||
        .ast_node => |node| ast_node: {
 | 
			
		||||
    var doc_str: ?[]const u8 = null;
 | 
			
		||||
 | 
			
		||||
    const def_str = switch (decl_handle.decl.*) {
 | 
			
		||||
        .ast_node => |node| def: {
 | 
			
		||||
            if (try analysis.resolveVarDeclAlias(&document_store, arena, .{ .node = node, .handle = handle })) |result| {
 | 
			
		||||
                return try hoverSymbol(id, arena, result);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const doc_str = if (try analysis.getDocComments(&arena.allocator, tree, node, hover_kind)) |str|
 | 
			
		||||
                str
 | 
			
		||||
            else
 | 
			
		||||
                "";
 | 
			
		||||
            doc_str = try analysis.getDocComments(&arena.allocator, tree, node, hover_kind);
 | 
			
		||||
 | 
			
		||||
            var buf: [1]std.zig.ast.Node.Index = undefined;
 | 
			
		||||
            const signature_str = if (analysis.varDecl(tree, node)) |var_decl| blk: {
 | 
			
		||||
                break :blk analysis.getVariableSignature(tree, var_decl);
 | 
			
		||||
            } else if (analysis.fnProto(tree, node, &buf)) |fn_proto| blk: {
 | 
			
		||||
                break :blk analysis.getFunctionSignature(tree, fn_proto);
 | 
			
		||||
            } else if (analysis.containerField(tree, node)) |field| blk: {
 | 
			
		||||
                break :blk analysis.getContainerFieldSignature(tree, field);
 | 
			
		||||
            } else analysis.nodeToString(tree, node) orelse
 | 
			
		||||
                return try respondGeneric(id, null_result_response);
 | 
			
		||||
 | 
			
		||||
            break :ast_node if (hover_kind == .Markdown)
 | 
			
		||||
                try std.fmt.allocPrint(&arena.allocator, "```zig\n{s}\n```\n{s}", .{ signature_str, doc_str })
 | 
			
		||||
            else
 | 
			
		||||
                try std.fmt.allocPrint(&arena.allocator, "{s}\n{s}", .{ signature_str, doc_str });
 | 
			
		||||
            if (analysis.varDecl(tree, node)) |var_decl| {
 | 
			
		||||
                break :def analysis.getVariableSignature(tree, var_decl);
 | 
			
		||||
            } else if (analysis.fnProto(tree, node, &buf)) |fn_proto| {
 | 
			
		||||
                break :def analysis.getFunctionSignature(tree, fn_proto);
 | 
			
		||||
            } else if (analysis.containerField(tree, node)) |field| {
 | 
			
		||||
                break :def analysis.getContainerFieldSignature(tree, field);
 | 
			
		||||
            } else {
 | 
			
		||||
                break :def analysis.nodeToString(tree, node) orelse
 | 
			
		||||
                    return try respondGeneric(id, null_result_response);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        .param_decl => |param| param_decl: {
 | 
			
		||||
            const doc_str = if (param.first_doc_comment) |doc_comments|
 | 
			
		||||
                try analysis.collectDocComments(&arena.allocator, handle.tree, doc_comments, hover_kind)
 | 
			
		||||
            else
 | 
			
		||||
                "";
 | 
			
		||||
        .param_decl => |param| def: {
 | 
			
		||||
            if (param.first_doc_comment) |doc_comments| {
 | 
			
		||||
                doc_str = try analysis.collectDocComments(&arena.allocator, handle.tree, doc_comments, hover_kind);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const first_token = param.first_doc_comment orelse
 | 
			
		||||
                param.comptime_noalias orelse
 | 
			
		||||
@ -659,42 +654,35 @@ fn hoverSymbol(
 | 
			
		||||
 | 
			
		||||
            const start = offsets.tokenLocation(tree, first_token).start;
 | 
			
		||||
            const end = offsets.tokenLocation(tree, last_token).end;
 | 
			
		||||
            const signature_str = tree.source[start..end];
 | 
			
		||||
            break :param_decl if (hover_kind == .Markdown)
 | 
			
		||||
                try std.fmt.allocPrint(&arena.allocator, "```zig\n{s}\n```\n{s}", .{ signature_str, doc_str })
 | 
			
		||||
            else
 | 
			
		||||
                try std.fmt.allocPrint(&arena.allocator, "{s}\n{s}", .{ signature_str, doc_str });
 | 
			
		||||
        },
 | 
			
		||||
        .pointer_payload => |payload| if (hover_kind == .Markdown)
 | 
			
		||||
            try std.fmt.allocPrint(&arena.allocator, "```zig\n{s}\n```", .{tree.tokenSlice(payload.name)})
 | 
			
		||||
        else
 | 
			
		||||
            try std.fmt.allocPrint(&arena.allocator, "{s}", .{tree.tokenSlice(payload.name)}),
 | 
			
		||||
        .array_payload => |payload| if (hover_kind == .Markdown)
 | 
			
		||||
            try std.fmt.allocPrint(&arena.allocator, "```zig\n{s}\n```", .{handle.tree.tokenSlice(payload.identifier)})
 | 
			
		||||
        else
 | 
			
		||||
            try std.fmt.allocPrint(&arena.allocator, "{s}", .{handle.tree.tokenSlice(payload.identifier)}),
 | 
			
		||||
        .array_index => |payload| if (hover_kind == .Markdown)
 | 
			
		||||
            try std.fmt.allocPrint(&arena.allocator, "```zig\n{s}\n```", .{handle.tree.tokenSlice(payload)})
 | 
			
		||||
        else
 | 
			
		||||
            try std.fmt.allocPrint(&arena.allocator, "{s}", .{handle.tree.tokenSlice(payload)}),
 | 
			
		||||
        .switch_payload => |payload| if (hover_kind == .Markdown)
 | 
			
		||||
            try std.fmt.allocPrint(&arena.allocator, "```zig\n{s}\n```", .{tree.tokenSlice(payload.node)})
 | 
			
		||||
        else
 | 
			
		||||
            try std.fmt.allocPrint(&arena.allocator, "{s}", .{tree.tokenSlice(payload.node)}),
 | 
			
		||||
        .label_decl => |label_decl| block: {
 | 
			
		||||
            const source = tree.tokenSlice(label_decl);
 | 
			
		||||
            break :block if (hover_kind == .Markdown)
 | 
			
		||||
                try std.fmt.allocPrint(&arena.allocator, "```zig\n{s}\n```", .{source})
 | 
			
		||||
            else
 | 
			
		||||
                try std.fmt.allocPrint(&arena.allocator, "```{s}```", .{source});
 | 
			
		||||
            break :def tree.source[start..end];
 | 
			
		||||
        },
 | 
			
		||||
        .pointer_payload => |payload| tree.tokenSlice(payload.name),
 | 
			
		||||
        .array_payload => |payload| handle.tree.tokenSlice(payload.identifier),
 | 
			
		||||
        .array_index => |payload| handle.tree.tokenSlice(payload),
 | 
			
		||||
        .switch_payload => |payload| tree.tokenSlice(payload.node),
 | 
			
		||||
        .label_decl => |label_decl| tree.tokenSlice(label_decl),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    var hover_text: []const u8 = undefined;
 | 
			
		||||
    if (hover_kind == .Markdown) {
 | 
			
		||||
        hover_text =
 | 
			
		||||
            if (doc_str) |doc|
 | 
			
		||||
            try std.fmt.allocPrint(&arena.allocator, "```zig\n{s}\n```\n{s}", .{ def_str, doc })
 | 
			
		||||
        else
 | 
			
		||||
            try std.fmt.allocPrint(&arena.allocator, "```zig\n{s}\n```", .{def_str});
 | 
			
		||||
    } else {
 | 
			
		||||
        hover_text =
 | 
			
		||||
            if (doc_str) |doc|
 | 
			
		||||
            try std.fmt.allocPrint(&arena.allocator, "{s}\n{s}", .{ def_str, doc })
 | 
			
		||||
        else
 | 
			
		||||
            def_str;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try send(arena, types.Response{
 | 
			
		||||
        .id = id,
 | 
			
		||||
        .result = .{
 | 
			
		||||
            .Hover = .{
 | 
			
		||||
                .contents = .{ .value = md_string },
 | 
			
		||||
                .contents = .{ .value = hover_text },
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
    });
 | 
			
		||||
@ -739,11 +727,13 @@ fn hoverDefinitionBuiltin(arena: *std.heap.ArenaAllocator, id: types.RequestId,
 | 
			
		||||
                .id = id,
 | 
			
		||||
                .result = .{
 | 
			
		||||
                    .Hover = .{
 | 
			
		||||
                        .contents = .{ .value = try std.fmt.allocPrint(
 | 
			
		||||
                        .contents = .{
 | 
			
		||||
                            .value = try std.fmt.allocPrint(
 | 
			
		||||
                                &arena.allocator,
 | 
			
		||||
                                "```zig\n{s}\n```\n{s}",
 | 
			
		||||
                                .{ builtin.signature, builtin.documentation },
 | 
			
		||||
                        ) },
 | 
			
		||||
                            ),
 | 
			
		||||
                        },
 | 
			
		||||
                    },
 | 
			
		||||
                },
 | 
			
		||||
            });
 | 
			
		||||
@ -1161,6 +1151,7 @@ fn completeError(
 | 
			
		||||
) !void {
 | 
			
		||||
    const completions = try document_store.errorCompletionItems(arena, handle);
 | 
			
		||||
    truncateCompletions(completions, config.max_detail_length);
 | 
			
		||||
    logger.debug("Completing error:", .{});
 | 
			
		||||
 | 
			
		||||
    try send(arena, types.Response{
 | 
			
		||||
        .id = id,
 | 
			
		||||
@ -1348,6 +1339,7 @@ fn openDocumentHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req
 | 
			
		||||
    const handle = try document_store.openDocument(req.params.textDocument.uri, req.params.textDocument.text);
 | 
			
		||||
    try publishDiagnostics(arena, handle.*, config);
 | 
			
		||||
 | 
			
		||||
    if (client_capabilities.supports_semantic_tokens)
 | 
			
		||||
        try semanticTokensFullHandler(arena, id, .{ .params = .{ .textDocument = .{ .uri = req.params.textDocument.uri } } }, config);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1374,10 +1366,10 @@ fn closeDocumentHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, re
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn semanticTokensFullHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requests.SemanticTokensFull, config: Config) (error{OutOfMemory} || std.fs.File.WriteError)!void {
 | 
			
		||||
    if (config.enable_semantic_tokens and client_capabilities.supports_semantic_tokens) {
 | 
			
		||||
    if (config.enable_semantic_tokens) blk: {
 | 
			
		||||
        const handle = document_store.getHandle(req.params.textDocument.uri) orelse {
 | 
			
		||||
            logger.warn("Trying to get semantic tokens of non existent document {s}", .{req.params.textDocument.uri});
 | 
			
		||||
            return try respondGeneric(id, no_semantic_tokens_response);
 | 
			
		||||
            break :blk;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const token_array = try semantic_tokens.writeAllSemanticTokens(arena, &document_store, handle, offset_encoding);
 | 
			
		||||
@ -1388,6 +1380,7 @@ fn semanticTokensFullHandler(arena: *std.heap.ArenaAllocator, id: types.RequestI
 | 
			
		||||
            .result = .{ .SemanticTokensFull = .{ .data = token_array } },
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    return try respondGeneric(id, no_semantic_tokens_response);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn completionHandler(
 | 
			
		||||
@ -1444,11 +1437,13 @@ fn signatureHelpHandler(
 | 
			
		||||
    )) |sig_info| {
 | 
			
		||||
        return try send(arena, types.Response{
 | 
			
		||||
            .id = id,
 | 
			
		||||
            .result = .{ .SignatureHelp = .{
 | 
			
		||||
            .result = .{
 | 
			
		||||
                .SignatureHelp = .{
 | 
			
		||||
                    .signatures = &[1]types.SignatureInformation{sig_info},
 | 
			
		||||
                    .activeSignature = 0,
 | 
			
		||||
                    .activeParameter = sig_info.activeParameter,
 | 
			
		||||
            } },
 | 
			
		||||
                },
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    return try respondGeneric(id, no_signatures_response);
 | 
			
		||||
@ -1717,12 +1712,16 @@ var gpa_state = std.heap.GeneralPurposeAllocator(.{ .stack_trace_frames = stack_
 | 
			
		||||
 | 
			
		||||
pub fn main() anyerror!void {
 | 
			
		||||
    defer _ = gpa_state.deinit();
 | 
			
		||||
    defer keep_running = false;
 | 
			
		||||
    allocator = &gpa_state.allocator;
 | 
			
		||||
 | 
			
		||||
    analysis.init(allocator);
 | 
			
		||||
    defer analysis.deinit();
 | 
			
		||||
 | 
			
		||||
    // Check arguments.
 | 
			
		||||
    var args_it = std.process.args();
 | 
			
		||||
    defer args_it.deinit();
 | 
			
		||||
    const prog_name = try args_it.next(allocator) orelse unreachable;
 | 
			
		||||
    const prog_name = try args_it.next(allocator) orelse @panic("Could not find self argument");
 | 
			
		||||
    allocator.free(prog_name);
 | 
			
		||||
 | 
			
		||||
    while (args_it.next(allocator)) |maybe_arg| {
 | 
			
		||||
@ -1732,7 +1731,6 @@ pub fn main() anyerror!void {
 | 
			
		||||
            actual_log_level = .debug;
 | 
			
		||||
            std.debug.print("Enabled debug logging\n", .{});
 | 
			
		||||
        } else if (std.mem.eql(u8, arg, "config")) {
 | 
			
		||||
            keep_running = false;
 | 
			
		||||
            try setup.wizard(allocator);
 | 
			
		||||
            return;
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
@ -244,7 +244,7 @@ fn symbolReferencesInternal(
 | 
			
		||||
        .@"if",
 | 
			
		||||
        .if_simple,
 | 
			
		||||
        => {
 | 
			
		||||
            const if_node: ast.full.If = if (node_tags[node] == .@"if") ifFull(tree, node) else ifSimple(tree, node);
 | 
			
		||||
            const if_node = ifFull(tree, node);
 | 
			
		||||
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = if_node.ast.cond_expr, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = if_node.ast.then_expr, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
 | 
			
		||||
@ -696,7 +696,7 @@ fn writeNodeTokens(
 | 
			
		||||
        .@"if",
 | 
			
		||||
        .if_simple,
 | 
			
		||||
        => {
 | 
			
		||||
            const if_node: ast.full.If = if (tag == .@"if") ifFull(tree, node) else ifSimple(tree, node);
 | 
			
		||||
            const if_node = ifFull(tree, node);
 | 
			
		||||
 | 
			
		||||
            try writeToken(builder, if_node.ast.if_token, .keyword);
 | 
			
		||||
            try await @asyncCall(child_frame, {}, writeNodeTokens, .{ builder, arena, store, if_node.ast.cond_expr });
 | 
			
		||||
 | 
			
		||||
@ -4,72 +4,142 @@ const headerPkg = @import("header");
 | 
			
		||||
const suffix = if (std.builtin.os.tag == .windows) ".exe" else "";
 | 
			
		||||
const allocator = std.heap.page_allocator;
 | 
			
		||||
 | 
			
		||||
const initialize_message =
 | 
			
		||||
    \\{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":6896,"clientInfo":{"name":"vscode","version":"1.46.1"},"rootPath":null,"rootUri":null,"capabilities":{"workspace":{"applyEdit":true,"workspaceEdit":{"documentChanges":true,"resourceOperations":["create","rename","delete"],"failureHandling":"textOnlyTransactional"},"didChangeConfiguration":{"dynamicRegistration":true},"didChangeWatchedFiles":{"dynamicRegistration":true},"symbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"tagSupport":{"valueSet":[1]}},"executeCommand":{"dynamicRegistration":true},"configuration":true,"workspaceFolders":true},"textDocument":{"publishDiagnostics":{"relatedInformation":true,"versionSupport":false,"tagSupport":{"valueSet":[1,2]},"complexDiagnosticCodeSupport":true},"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"contextSupport":true,"completionItem":{"snippetSupport":true,"commitCharactersSupport":true,"documentationFormat":["markdown","plaintext"],"deprecatedSupport":true,"preselectSupport":true,"tagSupport":{"valueSet":[1]},"insertReplaceSupport":true},"completionItemKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]}},"hover":{"dynamicRegistration":true,"contentFormat":["markdown","plaintext"]},"signatureHelp":{"dynamicRegistration":true,"signatureInformation":{"documentationFormat":["markdown","plaintext"],"parameterInformation":{"labelOffsetSupport":true}},"contextSupport":true},"definition":{"dynamicRegistration":true,"linkSupport":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"hierarchicalDocumentSymbolSupport":true,"tagSupport":{"valueSet":[1]}},"codeAction":{"dynamicRegistration":true,"isPreferredSupport":true,"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["","quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}}},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true,"prepareSupport":true},"documentLink":{"dynamicRegistration":true,"tooltipSupport":true},"typeDefinition":{"dynamicRegistration":true,"linkSupport":true},"implementation":{"dynamicRegistration":true,"linkSupport":true},"colorProvider":{"dynamicRegistration":true},"foldingRange":{"dynamicRegistration":true,"rangeLimit":5000,"lineFoldingOnly":true},"declaration":{"dynamicRegistration":true,"linkSupport":true},"selectionRange":{"dynamicRegistration":true},"semanticTokens":{"dynamicRegistration":true,"tokenTypes":["comment","keyword","number","regexp","operator","namespace","type","struct","class","interface","enum","typeParameter","function","member","macro","variable","parameter","property","label"],"tokenModifiers":["declaration","documentation","static","abstract","deprecated","readonly"]}},"window":{"workDoneProgress":true}},"trace":"off","workspaceFolders":[{"uri":"file://./tests", "name":"root"}]}}
 | 
			
		||||
const initialize_msg =
 | 
			
		||||
    \\{"processId":6896,"clientInfo":{"name":"vscode","version":"1.46.1"},"rootPath":null,"rootUri":null,"capabilities":{"workspace":{"applyEdit":true,"workspaceEdit":{"documentChanges":true,"resourceOperations":["create","rename","delete"],"failureHandling":"textOnlyTransactional"},"didChangeConfiguration":{"dynamicRegistration":true},"didChangeWatchedFiles":{"dynamicRegistration":true},"symbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"tagSupport":{"valueSet":[1]}},"executeCommand":{"dynamicRegistration":true},"configuration":true,"workspaceFolders":true},"textDocument":{"publishDiagnostics":{"relatedInformation":true,"versionSupport":false,"tagSupport":{"valueSet":[1,2]},"complexDiagnosticCodeSupport":true},"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"contextSupport":true,"completionItem":{"snippetSupport":true,"commitCharactersSupport":true,"documentationFormat":["markdown","plaintext"],"deprecatedSupport":true,"preselectSupport":true,"tagSupport":{"valueSet":[1]},"insertReplaceSupport":true},"completionItemKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]}},"hover":{"dynamicRegistration":true,"contentFormat":["markdown","plaintext"]},"signatureHelp":{"dynamicRegistration":true,"signatureInformation":{"documentationFormat":["markdown","plaintext"],"parameterInformation":{"labelOffsetSupport":true}},"contextSupport":true},"definition":{"dynamicRegistration":true,"linkSupport":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"hierarchicalDocumentSymbolSupport":true,"tagSupport":{"valueSet":[1]}},"codeAction":{"dynamicRegistration":true,"isPreferredSupport":true,"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["","quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}}},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true,"prepareSupport":true},"documentLink":{"dynamicRegistration":true,"tooltipSupport":true},"typeDefinition":{"dynamicRegistration":true,"linkSupport":true},"implementation":{"dynamicRegistration":true,"linkSupport":true},"colorProvider":{"dynamicRegistration":true},"foldingRange":{"dynamicRegistration":true,"rangeLimit":5000,"lineFoldingOnly":true},"declaration":{"dynamicRegistration":true,"linkSupport":true},"selectionRange":{"dynamicRegistration":true},"semanticTokens":{"dynamicRegistration":true,"tokenTypes":["comment","keyword","number","regexp","operator","namespace","type","struct","class","interface","enum","typeParameter","function","member","macro","variable","parameter","property","label"],"tokenModifiers":["declaration","documentation","static","abstract","deprecated","readonly"]}},"window":{"workDoneProgress":true}},"trace":"off","workspaceFolders":[{"uri":"file://./tests", "name":"root"}]}
 | 
			
		||||
;
 | 
			
		||||
const initialize_msg_offs =
 | 
			
		||||
    \\{"processId":6896,"clientInfo":{"name":"vscode","version":"1.46.1"},"rootPath":null,"rootUri":null,"capabilities":{"offsetEncoding":["utf-16", "utf-8"],"workspace":{"applyEdit":true,"workspaceEdit":{"documentChanges":true,"resourceOperations":["create","rename","delete"],"failureHandling":"textOnlyTransactional"},"didChangeConfiguration":{"dynamicRegistration":true},"didChangeWatchedFiles":{"dynamicRegistration":true},"symbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"tagSupport":{"valueSet":[1]}},"executeCommand":{"dynamicRegistration":true},"configuration":true,"workspaceFolders":true},"textDocument":{"publishDiagnostics":{"relatedInformation":true,"versionSupport":false,"tagSupport":{"valueSet":[1,2]},"complexDiagnosticCodeSupport":true},"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"contextSupport":true,"completionItem":{"snippetSupport":true,"commitCharactersSupport":true,"documentationFormat":["markdown","plaintext"],"deprecatedSupport":true,"preselectSupport":true,"tagSupport":{"valueSet":[1]},"insertReplaceSupport":true},"completionItemKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]}},"hover":{"dynamicRegistration":true,"contentFormat":["markdown","plaintext"]},"signatureHelp":{"dynamicRegistration":true,"signatureInformation":{"documentationFormat":["markdown","plaintext"],"parameterInformation":{"labelOffsetSupport":true}},"contextSupport":true},"definition":{"dynamicRegistration":true,"linkSupport":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"hierarchicalDocumentSymbolSupport":true,"tagSupport":{"valueSet":[1]}},"codeAction":{"dynamicRegistration":true,"isPreferredSupport":true,"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["","quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}}},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true,"prepareSupport":true},"documentLink":{"dynamicRegistration":true,"tooltipSupport":true},"typeDefinition":{"dynamicRegistration":true,"linkSupport":true},"implementation":{"dynamicRegistration":true,"linkSupport":true},"colorProvider":{"dynamicRegistration":true},"foldingRange":{"dynamicRegistration":true,"rangeLimit":5000,"lineFoldingOnly":true},"declaration":{"dynamicRegistration":true,"linkSupport":true},"selectionRange":{"dynamicRegistration":true},"semanticTokens":{"dynamicRegistration":true,"tokenTypes":["comment","keyword","number","regexp","operator","namespace","type","struct","class","interface","enum","typeParameter","function","member","macro","variable","parameter","property","label"],"tokenModifiers":["declaration","documentation","static","abstract","deprecated","readonly"]}},"window":{"workDoneProgress":true}},"trace":"off","workspaceFolders":null}
 | 
			
		||||
;
 | 
			
		||||
 | 
			
		||||
const initialized_message =
 | 
			
		||||
    \\{"jsonrpc":"2.0","method":"initialized","params":{}}
 | 
			
		||||
;
 | 
			
		||||
const Server = struct {
 | 
			
		||||
    process: *std.ChildProcess,
 | 
			
		||||
    request_id: u32 = 1,
 | 
			
		||||
 | 
			
		||||
const shutdown_message =
 | 
			
		||||
    \\{"jsonrpc":"2.0", "id":"STDWN", "method":"shutdown","params":{}}
 | 
			
		||||
;
 | 
			
		||||
    fn start(initialization: []const u8, expect: ?[]const u8) !Server {
 | 
			
		||||
        var server = Server{ .process = try startZls() };
 | 
			
		||||
 | 
			
		||||
fn sendRequest(req: []const u8, process: *std.ChildProcess) !void {
 | 
			
		||||
    try process.stdin.?.writer().print("Content-Length: {}\r\n\r\n", .{req.len});
 | 
			
		||||
    try process.stdin.?.writeAll(req);
 | 
			
		||||
}
 | 
			
		||||
        try server.request("initialize", initialization, expect);
 | 
			
		||||
        try server.request("initialized", "{}", null);
 | 
			
		||||
        return server;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn request(
 | 
			
		||||
        self: *Server,
 | 
			
		||||
        method: []const u8,
 | 
			
		||||
        params: []const u8,
 | 
			
		||||
        expect: ?[]const u8,
 | 
			
		||||
    ) !void {
 | 
			
		||||
        self.request_id += 1;
 | 
			
		||||
        const req = try std.fmt.allocPrint(allocator,
 | 
			
		||||
            \\{{"jsonrpc":"2.0","id":{},"method":"{s}","params":{s}}}
 | 
			
		||||
        , .{ self.request_id, method, params });
 | 
			
		||||
 | 
			
		||||
        const to_server = self.process.stdin.?.writer();
 | 
			
		||||
        try to_server.print("Content-Length: {}\r\n\r\n", .{req.len});
 | 
			
		||||
        try to_server.writeAll(req);
 | 
			
		||||
 | 
			
		||||
        const expected = expect orelse return;
 | 
			
		||||
        var from_server = self.process.stdout.?.reader();
 | 
			
		||||
 | 
			
		||||
fn readResponses(process: *std.ChildProcess, expected_responses: anytype) !void {
 | 
			
		||||
    var seen = std.mem.zeroes([expected_responses.len]bool);
 | 
			
		||||
        while (true) {
 | 
			
		||||
        const header = headerPkg.readRequestHeader(allocator, process.stdout.?.reader()) catch |err| {
 | 
			
		||||
            const header = headerPkg.readRequestHeader(allocator, from_server) catch |err| {
 | 
			
		||||
                switch (err) {
 | 
			
		||||
                    error.EndOfStream => break,
 | 
			
		||||
                    else => return err,
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            defer header.deinit(allocator);
 | 
			
		||||
 | 
			
		||||
        var stdout_mem = try allocator.alloc(u8, header.content_length);
 | 
			
		||||
        defer allocator.free(stdout_mem);
 | 
			
		||||
 | 
			
		||||
        const stdout_bytes = stdout_mem[0..try process.stdout.?.reader().readAll(stdout_mem)];
 | 
			
		||||
        inline for (expected_responses) |resp, idx| {
 | 
			
		||||
            if (std.mem.eql(u8, resp, stdout_bytes)) {
 | 
			
		||||
                if (seen[idx]) @panic("Expected response already received.");
 | 
			
		||||
                seen[idx] = true;
 | 
			
		||||
            var resonse_bytes = try allocator.alloc(u8, header.content_length);
 | 
			
		||||
            defer allocator.free(resonse_bytes);
 | 
			
		||||
            if ((try from_server.readAll(resonse_bytes)) != header.content_length) {
 | 
			
		||||
                return error.InvalidResponse;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        std.debug.print("GOT MESSAGE: {s}\n", .{stdout_bytes});
 | 
			
		||||
            // std.debug.print("{s}\n", .{resonse_bytes});
 | 
			
		||||
 | 
			
		||||
            const json_fmt = "{\"jsonrpc\":\"2.0\",\"id\":";
 | 
			
		||||
            if (!std.mem.startsWith(u8, resonse_bytes, json_fmt)) {
 | 
			
		||||
                try extractError(resonse_bytes);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
    comptime var idx = 0;
 | 
			
		||||
    inline while (idx < expected_responses.len) : (idx += 1) {
 | 
			
		||||
        if (!seen[idx]) {
 | 
			
		||||
            std.debug.print("Response `{s}` not received.", .{expected_responses[idx]});
 | 
			
		||||
            return error.ExpectedResponse;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
            const rest = resonse_bytes[json_fmt.len..];
 | 
			
		||||
            const id_end = std.mem.indexOfScalar(u8, rest, ',') orelse return error.InvalidResponse;
 | 
			
		||||
 | 
			
		||||
            const id = try std.fmt.parseInt(u32, rest[0..id_end], 10);
 | 
			
		||||
 | 
			
		||||
            if (id != self.request_id) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const result = ",\"result\":";
 | 
			
		||||
            const msg = rest[id_end + result.len .. rest.len - 1];
 | 
			
		||||
 | 
			
		||||
            if (std.mem.eql(u8, msg, expected)) {
 | 
			
		||||
                return;
 | 
			
		||||
            } else {
 | 
			
		||||
                const mismatch = std.mem.indexOfDiff(u8, expected, msg) orelse 0;
 | 
			
		||||
                std.debug.print("==> Expected:\n{s}\n==> Got: (Mismatch in position {})\n{s}\n", .{ expected, mismatch, msg });
 | 
			
		||||
                return error.InvalidResponse;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn extractError(msg: []const u8) !void {
 | 
			
		||||
        const log_request =
 | 
			
		||||
            \\"method":"window/logMessage","params":{"type":
 | 
			
		||||
        ;
 | 
			
		||||
        if (std.mem.indexOf(u8, msg, log_request)) |log_msg| {
 | 
			
		||||
            const rest = msg[log_msg + log_request.len ..];
 | 
			
		||||
            const level = rest[0];
 | 
			
		||||
            if (level <= '2') {
 | 
			
		||||
                std.debug.print("{s}\n", .{rest[13 .. rest.len - 3]});
 | 
			
		||||
                if (level <= '1') {
 | 
			
		||||
                    return error.ServerError;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn shutdown(self: *Server) void {
 | 
			
		||||
        self.request("shutdown", "{}", null) catch @panic("Could not send shutdown request");
 | 
			
		||||
        waitNoError(self.process) catch |err| @panic("Server error");
 | 
			
		||||
        self.process.deinit();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
fn startZls() !*std.ChildProcess {
 | 
			
		||||
    std.debug.print("\n", .{});
 | 
			
		||||
 | 
			
		||||
    var process = try std.ChildProcess.init(&[_][]const u8{"zig-cache/bin/zls" ++ suffix}, allocator);
 | 
			
		||||
    process.stdin_behavior = .Pipe;
 | 
			
		||||
    process.stdout_behavior = .Pipe;
 | 
			
		||||
    process.stderr_behavior = std.ChildProcess.StdIo.Inherit;
 | 
			
		||||
    process.stderr_behavior = .Pipe; //std.ChildProcess.StdIo.Inherit;
 | 
			
		||||
 | 
			
		||||
    process.spawn() catch |err| {
 | 
			
		||||
        std.log.debug("Failed to spawn zls process, error: {}\n", .{err});
 | 
			
		||||
        std.debug.print("Failed to spawn zls process, error: {}\n", .{err});
 | 
			
		||||
        return err;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return process;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn waitNoError(process: *std.ChildProcess) !void {
 | 
			
		||||
    const stderr = std.io.getStdErr().writer();
 | 
			
		||||
    const err_in = process.stderr.?.reader();
 | 
			
		||||
    var buf: [4096]u8 = undefined;
 | 
			
		||||
    while (true) {
 | 
			
		||||
        const line = err_in.readUntilDelimiterOrEof(&buf, '\n') catch |err| switch (err) {
 | 
			
		||||
            error.StreamTooLong => {
 | 
			
		||||
                std.debug.print("skipping very long line\n", .{});
 | 
			
		||||
                continue;
 | 
			
		||||
            },
 | 
			
		||||
            else => return err,
 | 
			
		||||
        } orelse break;
 | 
			
		||||
 | 
			
		||||
        if (std.mem.startsWith(u8, line, "[debug")) continue;
 | 
			
		||||
 | 
			
		||||
        try stderr.writeAll(line);
 | 
			
		||||
        try stderr.writeByte('\n');
 | 
			
		||||
    }
 | 
			
		||||
    const result = try process.wait();
 | 
			
		||||
 | 
			
		||||
    switch (result) {
 | 
			
		||||
        .Exited => |code| if (code == 0) {
 | 
			
		||||
            return;
 | 
			
		||||
@ -79,80 +149,49 @@ fn waitNoError(process: *std.ChildProcess) !void {
 | 
			
		||||
    return error.ShutdownWithError;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn consumeOutputAndWait(process: *std.ChildProcess, expected_responses: anytype) !void {
 | 
			
		||||
    process.stdin.?.close();
 | 
			
		||||
    process.stdin = null;
 | 
			
		||||
    try readResponses(process, expected_responses);
 | 
			
		||||
    try waitNoError(process);
 | 
			
		||||
    process.deinit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
test "Open file, ask for semantic tokens" {
 | 
			
		||||
    var process = try startZls();
 | 
			
		||||
    try sendRequest(initialize_message, process);
 | 
			
		||||
    try sendRequest(initialized_message, process);
 | 
			
		||||
    var server = try Server.start(initialize_msg, null);
 | 
			
		||||
    defer server.shutdown();
 | 
			
		||||
 | 
			
		||||
    const new_file_req =
 | 
			
		||||
        \\{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file://./tests/test.zig","languageId":"zig","version":420,"text":"const std = @import(\"std\");"}}}
 | 
			
		||||
    ;
 | 
			
		||||
    try sendRequest(new_file_req, process);
 | 
			
		||||
    const sem_toks_req =
 | 
			
		||||
        \\{"jsonrpc":"2.0","id":2,"method":"textDocument/semanticTokens/full","params":{"textDocument":{"uri":"file://./tests/test.zig"}}}
 | 
			
		||||
    ;
 | 
			
		||||
    try sendRequest(sem_toks_req, process);
 | 
			
		||||
    try sendRequest(shutdown_message, process);
 | 
			
		||||
    try consumeOutputAndWait(process, .{
 | 
			
		||||
        \\{"jsonrpc":"2.0","id":0,"result":{"data":[0,0,5,7,0,0,6,3,0,33,0,4,1,11,0,0,2,7,12,0,0,8,5,9,0]}}
 | 
			
		||||
    });
 | 
			
		||||
    try server.request("textDocument/didOpen",
 | 
			
		||||
        \\{"textDocument":{"uri":"file://./tests/test.zig","languageId":"zig","version":420,"text":"const std = @import(\"std\");"}}
 | 
			
		||||
    , null);
 | 
			
		||||
    try server.request("textDocument/semanticTokens/full",
 | 
			
		||||
        \\{"textDocument":{"uri":"file://./tests/test.zig"}}
 | 
			
		||||
    ,
 | 
			
		||||
        \\{"data":[0,0,5,7,0,0,6,3,0,33,0,4,1,11,0,0,2,7,12,0,0,8,5,9,0]}
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
test "Requesting a completion in an empty file" {
 | 
			
		||||
    var process = try startZls();
 | 
			
		||||
    try sendRequest(initialize_message, process);
 | 
			
		||||
    try sendRequest(initialized_message, process);
 | 
			
		||||
test "Request completion in an empty file" {
 | 
			
		||||
    var server = try Server.start(initialize_msg, null);
 | 
			
		||||
    defer server.shutdown();
 | 
			
		||||
 | 
			
		||||
    const new_file_req =
 | 
			
		||||
    try server.request("textDocument/didOpen",
 | 
			
		||||
        \\{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///test.zig","languageId":"zig","version":420,"text":""}}}
 | 
			
		||||
    ;
 | 
			
		||||
    try sendRequest(new_file_req, process);
 | 
			
		||||
    const completion_req =
 | 
			
		||||
        \\{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///test.zig"}, "position":{"line":0,"character":0}}}
 | 
			
		||||
    ;
 | 
			
		||||
    try sendRequest(completion_req, process);
 | 
			
		||||
    try sendRequest(shutdown_message, process);
 | 
			
		||||
    try consumeOutputAndWait(process, .{});
 | 
			
		||||
    , null);
 | 
			
		||||
    try server.request("textDocument/completion",
 | 
			
		||||
        \\{"textDocument":{"uri":"file:///test.zig"}, "position":{"line":0,"character":0}}
 | 
			
		||||
    , null);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
test "Requesting a completion with no trailing whitespace" {
 | 
			
		||||
    var process = try startZls();
 | 
			
		||||
    try sendRequest(initialize_message, process);
 | 
			
		||||
    try sendRequest(initialized_message, process);
 | 
			
		||||
test "Request completion with no trailing whitespace" {
 | 
			
		||||
    var server = try Server.start(initialize_msg, null);
 | 
			
		||||
    defer server.shutdown();
 | 
			
		||||
 | 
			
		||||
    const new_file_req =
 | 
			
		||||
        \\{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///test.zig","languageId":"zig","version":420,"text":"const std = @import(\"std\");\nc"}}}
 | 
			
		||||
    ;
 | 
			
		||||
    try sendRequest(new_file_req, process);
 | 
			
		||||
    const completion_req =
 | 
			
		||||
        \\{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///test.zig"}, "position":{"line":1,"character":1}}}
 | 
			
		||||
    ;
 | 
			
		||||
    try sendRequest(completion_req, process);
 | 
			
		||||
    try sendRequest(shutdown_message, process);
 | 
			
		||||
    try consumeOutputAndWait(process, .{
 | 
			
		||||
        \\{"jsonrpc":"2.0","id":2,"result":{"isIncomplete":false,"items":[{"label":"std","kind":21,"textEdit":null,"filterText":null,"insertText":"std","insertTextFormat":1,"detail":"const std = @import(\"std\")","documentation":null}]}}
 | 
			
		||||
    });
 | 
			
		||||
    try server.request("textDocument/didOpen",
 | 
			
		||||
        \\{"textDocument":{"uri":"file:///test.zig","languageId":"zig","version":420,"text":"const std = @import(\"std\");\nc"}}
 | 
			
		||||
    , null);
 | 
			
		||||
    try server.request("textDocument/completion",
 | 
			
		||||
        \\{"textDocument":{"uri":"file:///test.zig"}, "position":{"line":1,"character":1}}
 | 
			
		||||
    ,
 | 
			
		||||
        \\{"isIncomplete":false,"items":[{"label":"std","kind":21,"textEdit":null,"filterText":null,"insertText":"std","insertTextFormat":1,"detail":"const std = @import(\"std\")","documentation":null}]}
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const initialize_message_offs =
 | 
			
		||||
    \\{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":6896,"clientInfo":{"name":"vscode","version":"1.46.1"},"rootPath":null,"rootUri":null,"capabilities":{"offsetEncoding":["utf-16", "utf-8"],"workspace":{"applyEdit":true,"workspaceEdit":{"documentChanges":true,"resourceOperations":["create","rename","delete"],"failureHandling":"textOnlyTransactional"},"didChangeConfiguration":{"dynamicRegistration":true},"didChangeWatchedFiles":{"dynamicRegistration":true},"symbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"tagSupport":{"valueSet":[1]}},"executeCommand":{"dynamicRegistration":true},"configuration":true,"workspaceFolders":true},"textDocument":{"publishDiagnostics":{"relatedInformation":true,"versionSupport":false,"tagSupport":{"valueSet":[1,2]},"complexDiagnosticCodeSupport":true},"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"contextSupport":true,"completionItem":{"snippetSupport":true,"commitCharactersSupport":true,"documentationFormat":["markdown","plaintext"],"deprecatedSupport":true,"preselectSupport":true,"tagSupport":{"valueSet":[1]},"insertReplaceSupport":true},"completionItemKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]}},"hover":{"dynamicRegistration":true,"contentFormat":["markdown","plaintext"]},"signatureHelp":{"dynamicRegistration":true,"signatureInformation":{"documentationFormat":["markdown","plaintext"],"parameterInformation":{"labelOffsetSupport":true}},"contextSupport":true},"definition":{"dynamicRegistration":true,"linkSupport":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"hierarchicalDocumentSymbolSupport":true,"tagSupport":{"valueSet":[1]}},"codeAction":{"dynamicRegistration":true,"isPreferredSupport":true,"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["","quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}}},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true,"prepareSupport":true},"documentLink":{"dynamicRegistration":true,"tooltipSupport":true},"typeDefinition":{"dynamicRegistration":true,"linkSupport":true},"implementation":{"dynamicRegistration":true,"linkSupport":true},"colorProvider":{"dynamicRegistration":true},"foldingRange":{"dynamicRegistration":true,"rangeLimit":5000,"lineFoldingOnly":true},"declaration":{"dynamicRegistration":true,"linkSupport":true},"selectionRange":{"dynamicRegistration":true},"semanticTokens":{"dynamicRegistration":true,"tokenTypes":["comment","keyword","number","regexp","operator","namespace","type","struct","class","interface","enum","typeParameter","function","member","macro","variable","parameter","property","label"],"tokenModifiers":["declaration","documentation","static","abstract","deprecated","readonly"]}},"window":{"workDoneProgress":true}},"trace":"off","workspaceFolders":null}}
 | 
			
		||||
;
 | 
			
		||||
 | 
			
		||||
test "Requesting utf-8 offset encoding" {
 | 
			
		||||
    var process = try startZls();
 | 
			
		||||
    try sendRequest(initialize_message_offs, process);
 | 
			
		||||
    try sendRequest(initialized_message, process);
 | 
			
		||||
 | 
			
		||||
    try sendRequest(shutdown_message, process);
 | 
			
		||||
    try consumeOutputAndWait(process, .{
 | 
			
		||||
        \\{"jsonrpc":"2.0","id":0,"result":{"offsetEncoding":"utf-8","capabilities":{"signatureHelpProvider":{"triggerCharacters":["("],"retriggerCharacters":[","]},"textDocumentSync":1,"renameProvider":true,"completionProvider":{"resolveProvider":false,"triggerCharacters":[".",":","@"]},"documentHighlightProvider":false,"hoverProvider":true,"codeActionProvider":false,"declarationProvider":true,"definitionProvider":true,"typeDefinitionProvider":true,"implementationProvider":false,"referencesProvider":true,"documentSymbolProvider":true,"colorProvider":false,"documentFormattingProvider":true,"documentRangeFormattingProvider":false,"foldingRangeProvider":false,"selectionRangeProvider":false,"workspaceSymbolProvider":false,"rangeProvider":false,"documentProvider":true,"workspace":{"workspaceFolders":{"supported":false,"changeNotifications":false}},"semanticTokensProvider":{"full":true,"range":false,"legend":{"tokenTypes":["type","parameter","variable","enumMember","field","errorTag","function","keyword","comment","string","number","operator","builtin","label","keywordLiteral"],"tokenModifiers":["namespace","struct","enum","union","opaque","declaration","async","documentation","generic"]}}},"serverInfo":{"name":"zls","version":"0.1.0"}}}
 | 
			
		||||
    });
 | 
			
		||||
test "Request utf-8 offset encoding" {
 | 
			
		||||
    var server = try Server.start(initialize_msg_offs,
 | 
			
		||||
        \\{"offsetEncoding":"utf-8","capabilities":{"signatureHelpProvider":{"triggerCharacters":["("],"retriggerCharacters":[","]},"textDocumentSync":1,"renameProvider":true,"completionProvider":{"resolveProvider":false,"triggerCharacters":[".",":","@"]},"documentHighlightProvider":false,"hoverProvider":true,"codeActionProvider":false,"declarationProvider":true,"definitionProvider":true,"typeDefinitionProvider":true,"implementationProvider":false,"referencesProvider":true,"documentSymbolProvider":true,"colorProvider":false,"documentFormattingProvider":true,"documentRangeFormattingProvider":false,"foldingRangeProvider":false,"selectionRangeProvider":false,"workspaceSymbolProvider":false,"rangeProvider":false,"documentProvider":true,"workspace":{"workspaceFolders":{"supported":false,"changeNotifications":false}},"semanticTokensProvider":{"full":true,"range":false,"legend":{"tokenTypes":["type","parameter","variable","enumMember","field","errorTag","function","keyword","comment","string","number","operator","builtin","label","keywordLiteral"],"tokenModifiers":["namespace","struct","enum","union","opaque","declaration","async","documentation","generic"]}}},"serverInfo":{"name":"zls","version":"0.1.0"}}
 | 
			
		||||
    );
 | 
			
		||||
    server.shutdown();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user