Merge pull request #284 from zigtools/signature_help
Added signature help request handling.
This commit is contained in:
		
						commit
						42aefd2e67
					
				
							
								
								
									
										4
									
								
								.github/README.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/README.md
									
									
									
									
										vendored
									
									
								
							@ -66,14 +66,14 @@ zig build config # Configure ZLS
 | 
				
			|||||||
<!-- If this table grows too large, then delete this one and move it all over to the Wiki page about building from source. -->
 | 
					<!-- If this table grows too large, then delete this one and move it all over to the Wiki page about building from source. -->
 | 
				
			||||||
| Option | Type | Default Value | What it Does |
 | 
					| Option | Type | Default Value | What it Does |
 | 
				
			||||||
| --- | --- | --- | --- |
 | 
					| --- | --- | --- | --- |
 | 
				
			||||||
| `-Ddata_version` | `string` (master or 0.7.0) | master | The data file version. This selects the files in the `src/data` folder that correspond to the Zig version being served.|
 | 
					| `-Ddata_version` | `string` (master, 0.7.0 or 0.7.1) | master | The data file version. This selects the files in the `src/data` folder that correspond to the Zig version being served.|
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Configuration Options
 | 
					### Configuration Options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
You can configure zls by providing a zls.json file.  
 | 
					You can configure zls by providing a zls.json file.  
 | 
				
			||||||
zls will look for a zls.json configuration file in multiple locations with the following priority:  
 | 
					zls will look for a zls.json configuration file in multiple locations with the following priority:  
 | 
				
			||||||
- In the local configuration folder of your OS (as provided by [known-folders](https://github.com/ziglibs/known-folders#folder-list))  
 | 
					- In the local configuration folder of your OS (as provided by [known-folders](https://github.com/ziglibs/known-folders#folder-list))  
 | 
				
			||||||
- In the same directory as the executable  
 | 
					- In the global configuration folder of your OS (as provided by [known-folders](https://github.com/ziglibs/known-folders#folder-list))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The following options are currently available.  
 | 
					The following options are currently available.  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -64,14 +64,14 @@ zls config # Configure ZLS
 | 
				
			|||||||
<!-- If this table grows too large, then delete this one and move it all over to the Wiki page about building from source. -->
 | 
					<!-- If this table grows too large, then delete this one and move it all over to the Wiki page about building from source. -->
 | 
				
			||||||
| Option | Type | Default Value | What it Does |
 | 
					| Option | Type | Default Value | What it Does |
 | 
				
			||||||
| --- | --- | --- | --- |
 | 
					| --- | --- | --- | --- |
 | 
				
			||||||
| `-Ddata_version` | `string` (master or 0.7.0) | master | The data file version. This selects the files in the `src/data` folder that correspond to the Zig version being served.|
 | 
					| `-Ddata_version` | `string` (master, 0.7.0 or 0.7.1) | master | The data file version. This selects the files in the `src/data` folder that correspond to the Zig version being served.|
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Configuration Options
 | 
					### Configuration Options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
You can configure zls by running `zls config`.  
 | 
					You can configure zls by running `zls config`.  
 | 
				
			||||||
zls will look for a zls.json configuration file in multiple locations with the following priority:  
 | 
					zls will look for a zls.json configuration file in multiple locations with the following priority:  
 | 
				
			||||||
- In the local configuration folder of your OS (as provided by [known-folders](https://github.com/ziglibs/known-folders#folder-list))  
 | 
					- In the local configuration folder of your OS (as provided by [known-folders](https://github.com/ziglibs/known-folders#folder-list))  
 | 
				
			||||||
- In the same directory as the executable  
 | 
					- In the global configuration folder of your OS (as provided by [known-folders](https://github.com/ziglibs/known-folders#folder-list))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The following options are currently available.  
 | 
					The following options are currently available.  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,5 @@
 | 
				
			|||||||
const std = @import("std");
 | 
					const std = @import("std");
 | 
				
			||||||
const builtin = @import("builtin");
 | 
					const builtin = @import("builtin");
 | 
				
			||||||
// const build_options = @import("build_options")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
var builder: *std.build.Builder = undefined;
 | 
					var builder: *std.build.Builder = undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -10,11 +9,10 @@ pub fn build(b: *std.build.Builder) !void {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    const mode = b.standardReleaseOptions();
 | 
					    const mode = b.standardReleaseOptions();
 | 
				
			||||||
    const exe = b.addExecutable("zls", "src/main.zig");
 | 
					    const exe = b.addExecutable("zls", "src/main.zig");
 | 
				
			||||||
 | 
					 | 
				
			||||||
    exe.addBuildOption(
 | 
					    exe.addBuildOption(
 | 
				
			||||||
        []const u8,
 | 
					        []const u8,
 | 
				
			||||||
        "data_version",
 | 
					        "data_version",
 | 
				
			||||||
        b.option([]const u8, "data_version", "The data version - either 0.7.0 or master.") orelse "master",
 | 
					        b.option([]const u8, "data_version", "The data version - 0.7.0, 0.7.1 or master.") orelse "master",
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    exe.addPackage(.{ .name = "known-folders", .path = "src/known-folders/known-folders.zig" });
 | 
					    exe.addPackage(.{ .name = "known-folders", .path = "src/known-folders/known-folders.zig" });
 | 
				
			||||||
 | 
				
			|||||||
@ -171,6 +171,46 @@ pub fn getFunctionSnippet(
 | 
				
			|||||||
    return buffer.toOwnedSlice();
 | 
					    return buffer.toOwnedSlice();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Returns true if a function has a `self` parameter
 | 
				
			||||||
 | 
					pub fn hasSelfParam(
 | 
				
			||||||
 | 
					    arena: *std.heap.ArenaAllocator,
 | 
				
			||||||
 | 
					    document_store: *DocumentStore,
 | 
				
			||||||
 | 
					    handle: *DocumentStore.Handle,
 | 
				
			||||||
 | 
					    func: ast.full.FnProto,
 | 
				
			||||||
 | 
					) !bool {
 | 
				
			||||||
 | 
					    // Non-decl prototypes cannot have a self parameter.
 | 
				
			||||||
 | 
					    if (func.name_token == null) return false;
 | 
				
			||||||
 | 
					    if (func.ast.params.len == 0) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const tree = handle.tree;
 | 
				
			||||||
 | 
					    var it = func.iterate(tree);
 | 
				
			||||||
 | 
					    const param = it.next().?;
 | 
				
			||||||
 | 
					    if (param.type_expr == 0) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const token_starts = tree.tokens.items(.start);
 | 
				
			||||||
 | 
					    const token_data = tree.nodes.items(.data);
 | 
				
			||||||
 | 
					    const in_container = innermostContainer(handle, token_starts[func.ast.fn_token]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (try resolveTypeOfNode(document_store, arena, .{
 | 
				
			||||||
 | 
					        .node = param.type_expr,
 | 
				
			||||||
 | 
					        .handle = handle,
 | 
				
			||||||
 | 
					    })) |resolved_type| {
 | 
				
			||||||
 | 
					        if (std.meta.eql(in_container, resolved_type))
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (isPtrType(tree, param.type_expr)) {
 | 
				
			||||||
 | 
					        if (try resolveTypeOfNode(document_store, arena, .{
 | 
				
			||||||
 | 
					            .node = token_data[param.type_expr].rhs,
 | 
				
			||||||
 | 
					            .handle = handle,
 | 
				
			||||||
 | 
					        })) |resolved_prefix_op| {
 | 
				
			||||||
 | 
					            if (std.meta.eql(in_container, resolved_prefix_op))
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Gets a function signature (keywords, name, return value)
 | 
					/// Gets a function signature (keywords, name, return value)
 | 
				
			||||||
pub fn getVariableSignature(tree: ast.Tree, var_decl: ast.full.VarDecl) []const u8 {
 | 
					pub fn getVariableSignature(tree: ast.Tree, var_decl: ast.full.VarDecl) []const u8 {
 | 
				
			||||||
    const start = offsets.tokenLocation(tree, var_decl.ast.mut_token).start;
 | 
					    const start = offsets.tokenLocation(tree, var_decl.ast.mut_token).start;
 | 
				
			||||||
@ -376,22 +416,6 @@ fn isBlock(tree: ast.Tree, node: ast.Node.Index) bool {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Returns `true` when the given `node` is one of the call tags
 | 
					 | 
				
			||||||
fn isCall(tree: ast.Tree, node: ast.Node.Index) bool {
 | 
					 | 
				
			||||||
    return switch (tree.nodes.items(.tag)[node]) {
 | 
					 | 
				
			||||||
        .call,
 | 
					 | 
				
			||||||
        .call_comma,
 | 
					 | 
				
			||||||
        .call_one,
 | 
					 | 
				
			||||||
        .call_one_comma,
 | 
					 | 
				
			||||||
        .async_call,
 | 
					 | 
				
			||||||
        .async_call_comma,
 | 
					 | 
				
			||||||
        .async_call_one,
 | 
					 | 
				
			||||||
        .async_call_one_comma,
 | 
					 | 
				
			||||||
        => true,
 | 
					 | 
				
			||||||
        else => false,
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn findReturnStatementInternal(
 | 
					fn findReturnStatementInternal(
 | 
				
			||||||
    tree: ast.Tree,
 | 
					    tree: ast.Tree,
 | 
				
			||||||
    fn_decl: ast.full.FnProto,
 | 
					    fn_decl: ast.full.FnProto,
 | 
				
			||||||
@ -946,7 +970,7 @@ pub fn resolveTypeOfNodeInternal(
 | 
				
			|||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return TypeWithHandle{
 | 
					            return TypeWithHandle{
 | 
				
			||||||
                .type = .{ .data = .{ .pointer = rhs_node }, .is_type_val = false },
 | 
					                .type = .{ .data = .{ .pointer = rhs_node }, .is_type_val = rhs_type.type.is_type_val },
 | 
				
			||||||
                .handle = rhs_type.handle,
 | 
					                .handle = rhs_type.handle,
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
@ -2250,6 +2274,21 @@ pub fn iterateSymbolsGlobal(
 | 
				
			|||||||
    return try iterateSymbolsGlobalInternal(store, arena, handle, source_index, callback, context, &use_trail);
 | 
					    return try iterateSymbolsGlobalInternal(store, arena, handle, source_index, callback, context, &use_trail);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn innermostScope(handle: DocumentStore.Handle, source_index: usize) ast.Node.Index {
 | 
				
			||||||
 | 
					    var current = handle.document_scope.scopes[0].data.container;
 | 
				
			||||||
 | 
					    if (handle.document_scope.scopes.len == 1) return current;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (handle.document_scope.scopes[1..]) |scope| {
 | 
				
			||||||
 | 
					        if (source_index >= scope.range.start and source_index <= scope.range.end) {
 | 
				
			||||||
 | 
					            switch (scope.data) {
 | 
				
			||||||
 | 
					                .container, .function, .block => |node| current = node,
 | 
				
			||||||
 | 
					                else => {},
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return current;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn innermostContainer(handle: *DocumentStore.Handle, source_index: usize) TypeWithHandle {
 | 
					pub fn innermostContainer(handle: *DocumentStore.Handle, source_index: usize) TypeWithHandle {
 | 
				
			||||||
    var current = handle.document_scope.scopes[0].data.container;
 | 
					    var current = handle.document_scope.scopes[0].data.container;
 | 
				
			||||||
    if (handle.document_scope.scopes.len == 1) return TypeWithHandle.typeVal(.{ .node = current, .handle = handle });
 | 
					    if (handle.document_scope.scopes.len == 1) return TypeWithHandle.typeVal(.{ .node = current, .handle = handle });
 | 
				
			||||||
@ -3094,19 +3133,7 @@ fn makeScopeInternal(
 | 
				
			|||||||
        .async_call_one_comma,
 | 
					        .async_call_one_comma,
 | 
				
			||||||
        => {
 | 
					        => {
 | 
				
			||||||
            var buf: [1]ast.Node.Index = undefined;
 | 
					            var buf: [1]ast.Node.Index = undefined;
 | 
				
			||||||
            const call: ast.full.Call = switch (node_tag) {
 | 
					            const call = callFull(tree, node_idx, &buf).?;
 | 
				
			||||||
                .async_call,
 | 
					 | 
				
			||||||
                .async_call_comma,
 | 
					 | 
				
			||||||
                .call,
 | 
					 | 
				
			||||||
                .call_comma,
 | 
					 | 
				
			||||||
                => tree.callFull(node_idx),
 | 
					 | 
				
			||||||
                .async_call_one,
 | 
					 | 
				
			||||||
                .async_call_one_comma,
 | 
					 | 
				
			||||||
                .call_one,
 | 
					 | 
				
			||||||
                .call_one_comma,
 | 
					 | 
				
			||||||
                => tree.callOne(&buf, node_idx),
 | 
					 | 
				
			||||||
                else => unreachable,
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, call.ast.fn_expr);
 | 
					            try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, call.ast.fn_expr);
 | 
				
			||||||
            for (call.ast.params) |param|
 | 
					            for (call.ast.params) |param|
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										31
									
								
								src/ast.zig
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								src/ast.zig
									
									
									
									
									
								
							@ -857,6 +857,21 @@ pub fn isBuiltinCall(tree: ast.Tree, node: ast.Node.Index) bool {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn isCall(tree: ast.Tree, node: ast.Node.Index) bool {
 | 
				
			||||||
 | 
					    return switch (tree.nodes.items(.tag)[node]) {
 | 
				
			||||||
 | 
					        .call,
 | 
				
			||||||
 | 
					        .call_comma,
 | 
				
			||||||
 | 
					        .call_one,
 | 
				
			||||||
 | 
					        .call_one_comma,
 | 
				
			||||||
 | 
					        .async_call,
 | 
				
			||||||
 | 
					        .async_call_comma,
 | 
				
			||||||
 | 
					        .async_call_one,
 | 
				
			||||||
 | 
					        .async_call_one_comma,
 | 
				
			||||||
 | 
					        => true,
 | 
				
			||||||
 | 
					        else => false,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn fnProto(tree: ast.Tree, node: ast.Node.Index, buf: *[1]ast.Node.Index) ?ast.full.FnProto {
 | 
					pub fn fnProto(tree: ast.Tree, node: ast.Node.Index, buf: *[1]ast.Node.Index) ?ast.full.FnProto {
 | 
				
			||||||
    return switch (tree.nodes.items(.tag)[node]) {
 | 
					    return switch (tree.nodes.items(.tag)[node]) {
 | 
				
			||||||
        .fn_proto => tree.fnProto(node),
 | 
					        .fn_proto => tree.fnProto(node),
 | 
				
			||||||
@ -867,3 +882,19 @@ pub fn fnProto(tree: ast.Tree, node: ast.Node.Index, buf: *[1]ast.Node.Index) ?a
 | 
				
			|||||||
        else => null,
 | 
					        else => null,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn callFull(tree: ast.Tree, node: ast.Node.Index, buf: *[1]ast.Node.Index) ?ast.full.Call {
 | 
				
			||||||
 | 
					    return switch (tree.nodes.items(.tag)[node]) {
 | 
				
			||||||
 | 
					        .async_call,
 | 
				
			||||||
 | 
					        .async_call_comma,
 | 
				
			||||||
 | 
					        .call,
 | 
				
			||||||
 | 
					        .call_comma,
 | 
				
			||||||
 | 
					        => tree.callFull(node),
 | 
				
			||||||
 | 
					        .async_call_one,
 | 
				
			||||||
 | 
					        .async_call_one_comma,
 | 
				
			||||||
 | 
					        .call_one,
 | 
				
			||||||
 | 
					        .call_one_comma,
 | 
				
			||||||
 | 
					        => tree.callOne(buf, node),
 | 
				
			||||||
 | 
					        else => null,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1107
									
								
								src/data/0.7.0.zig
									
									
									
									
									
								
							
							
						
						
									
										1107
									
								
								src/data/0.7.0.zig
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1861
									
								
								src/data/0.7.1.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1861
									
								
								src/data/0.7.1.zig
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -41,6 +41,7 @@ console.log(
 | 
				
			|||||||
    signature: []const u8,
 | 
					    signature: []const u8,
 | 
				
			||||||
    snippet: []const u8,
 | 
					    snippet: []const u8,
 | 
				
			||||||
    documentation: []const u8,
 | 
					    documentation: []const u8,
 | 
				
			||||||
 | 
					    arguments: []const []const u8,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub const builtins = [_]Builtin{` +
 | 
					pub const builtins = [_]Builtin{` +
 | 
				
			||||||
@ -49,11 +50,13 @@ pub const builtins = [_]Builtin{` +
 | 
				
			|||||||
        const first_paren_idx = builtin.code.indexOf('(');
 | 
					        const first_paren_idx = builtin.code.indexOf('(');
 | 
				
			||||||
        var snippet = builtin.code.substr(0, first_paren_idx + 1);
 | 
					        var snippet = builtin.code.substr(0, first_paren_idx + 1);
 | 
				
			||||||
        var rest = builtin.code.substr(first_paren_idx + 1);
 | 
					        var rest = builtin.code.substr(first_paren_idx + 1);
 | 
				
			||||||
 | 
					        var args = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (rest[0] == ')') {
 | 
					        if (rest[0] == ')') {
 | 
				
			||||||
            snippet += ')';
 | 
					            snippet += ')';
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            snippet += "${1:"
 | 
					            snippet += "${1:"
 | 
				
			||||||
 | 
					            args.push("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var arg_idx = 2;
 | 
					            var arg_idx = 2;
 | 
				
			||||||
            var paren_depth = 1;
 | 
					            var paren_depth = 1;
 | 
				
			||||||
@ -69,10 +72,12 @@ pub const builtins = [_]Builtin{` +
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                } else if (char == '"') {
 | 
					                } else if (char == '"') {
 | 
				
			||||||
                    snippet += "\\\"";
 | 
					                    snippet += "\\\"";
 | 
				
			||||||
 | 
					                    args[args.length - 1] += "\\\"";
 | 
				
			||||||
                    continue;
 | 
					                    continue;
 | 
				
			||||||
                } else if (char == ',' && paren_depth == 1) {
 | 
					                } else if (char == ',' && paren_depth == 1) {
 | 
				
			||||||
                    snippet += "}, ${" + arg_idx + ':';
 | 
					                    snippet += "}, ${" + arg_idx + ':';
 | 
				
			||||||
                    arg_idx += 1;
 | 
					                    arg_idx += 1;
 | 
				
			||||||
 | 
					                    args.push("");
 | 
				
			||||||
                    skip_space = true;
 | 
					                    skip_space = true;
 | 
				
			||||||
                    continue;
 | 
					                    continue;
 | 
				
			||||||
                } else if (char == ' ' && skip_space) {
 | 
					                } else if (char == ' ' && skip_space) {
 | 
				
			||||||
@ -80,6 +85,7 @@ pub const builtins = [_]Builtin{` +
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                snippet += char;
 | 
					                snippet += char;
 | 
				
			||||||
 | 
					                args[args.length - 1] += char;
 | 
				
			||||||
                skip_space = false;
 | 
					                skip_space = false;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -89,6 +95,9 @@ pub const builtins = [_]Builtin{` +
 | 
				
			|||||||
        .signature = "${builtin.code.replaceAll('"', "\\\"")}",
 | 
					        .signature = "${builtin.code.replaceAll('"', "\\\"")}",
 | 
				
			||||||
        .snippet = "${snippet}",
 | 
					        .snippet = "${snippet}",
 | 
				
			||||||
        .documentation =
 | 
					        .documentation =
 | 
				
			||||||
        \\\\${builtin.documentation.split('\n').join("\n        \\\\") + '\n'}    },`;
 | 
					        \\\\${builtin.documentation.split('\n').join("\n        \\\\")}
 | 
				
			||||||
 | 
					        ,
 | 
				
			||||||
 | 
					        .arguments = &.{${args.map(x => "\n            \"" + x + "\"").join(",") + ((args.length > 0) ? ",\n        " : "")}},
 | 
				
			||||||
 | 
					    },`;
 | 
				
			||||||
    }).join('\n') + "\n};\n"
 | 
					    }).join('\n') + "\n};\n"
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1159
									
								
								src/data/master.zig
									
									
									
									
									
								
							
							
						
						
									
										1159
									
								
								src/data/master.zig
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										162
									
								
								src/main.zig
									
									
									
									
									
								
							
							
						
						
									
										162
									
								
								src/main.zig
									
									
									
									
									
								
							@ -124,6 +124,9 @@ const edit_not_applied_response =
 | 
				
			|||||||
const no_completions_response =
 | 
					const no_completions_response =
 | 
				
			||||||
    \\,"result":{"isIncomplete":false,"items":[]}}
 | 
					    \\,"result":{"isIncomplete":false,"items":[]}}
 | 
				
			||||||
;
 | 
					;
 | 
				
			||||||
 | 
					const no_signatures_response =
 | 
				
			||||||
 | 
					    \\,"result":{"signatures":[]}}
 | 
				
			||||||
 | 
					;
 | 
				
			||||||
const no_semantic_tokens_response =
 | 
					const no_semantic_tokens_response =
 | 
				
			||||||
    \\,"result":{"data":[]}}
 | 
					    \\,"result":{"data":[]}}
 | 
				
			||||||
;
 | 
					;
 | 
				
			||||||
@ -326,6 +329,7 @@ fn typeToCompletion(
 | 
				
			|||||||
                null,
 | 
					                null,
 | 
				
			||||||
                orig_handle,
 | 
					                orig_handle,
 | 
				
			||||||
                type_handle.type.is_type_val,
 | 
					                type_handle.type.is_type_val,
 | 
				
			||||||
 | 
					                null,
 | 
				
			||||||
                config,
 | 
					                config,
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
@ -336,6 +340,7 @@ fn typeToCompletion(
 | 
				
			|||||||
            field_access.unwrapped,
 | 
					            field_access.unwrapped,
 | 
				
			||||||
            orig_handle,
 | 
					            orig_handle,
 | 
				
			||||||
            type_handle.type.is_type_val,
 | 
					            type_handle.type.is_type_val,
 | 
				
			||||||
 | 
					            null,
 | 
				
			||||||
            config,
 | 
					            config,
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
        .primitive => {},
 | 
					        .primitive => {},
 | 
				
			||||||
@ -349,13 +354,13 @@ fn nodeToCompletion(
 | 
				
			|||||||
    unwrapped: ?analysis.TypeWithHandle,
 | 
					    unwrapped: ?analysis.TypeWithHandle,
 | 
				
			||||||
    orig_handle: *DocumentStore.Handle,
 | 
					    orig_handle: *DocumentStore.Handle,
 | 
				
			||||||
    is_type_val: bool,
 | 
					    is_type_val: bool,
 | 
				
			||||||
 | 
					    parent_is_type_val: ?bool,
 | 
				
			||||||
    config: Config,
 | 
					    config: Config,
 | 
				
			||||||
) error{OutOfMemory}!void {
 | 
					) error{OutOfMemory}!void {
 | 
				
			||||||
    const node = node_handle.node;
 | 
					    const node = node_handle.node;
 | 
				
			||||||
    const handle = node_handle.handle;
 | 
					    const handle = node_handle.handle;
 | 
				
			||||||
    const tree = handle.tree;
 | 
					    const tree = handle.tree;
 | 
				
			||||||
    const node_tags = tree.nodes.items(.tag);
 | 
					    const node_tags = tree.nodes.items(.tag);
 | 
				
			||||||
    const datas = tree.nodes.items(.data);
 | 
					 | 
				
			||||||
    const token_tags = tree.tokens.items(.tag);
 | 
					    const token_tags = tree.tokens.items(.tag);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const doc_kind: types.MarkupContent.Kind = if (client_capabilities.completion_doc_supports_md)
 | 
					    const doc_kind: types.MarkupContent.Kind = if (client_capabilities.completion_doc_supports_md)
 | 
				
			||||||
@ -382,8 +387,17 @@ fn nodeToCompletion(
 | 
				
			|||||||
            .config = &config,
 | 
					            .config = &config,
 | 
				
			||||||
            .arena = arena,
 | 
					            .arena = arena,
 | 
				
			||||||
            .orig_handle = orig_handle,
 | 
					            .orig_handle = orig_handle,
 | 
				
			||||||
 | 
					            .parent_is_type_val = is_type_val,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        try analysis.iterateSymbolsContainer(&document_store, arena, node_handle, orig_handle, declToCompletion, context, !is_type_val);
 | 
					        try analysis.iterateSymbolsContainer(
 | 
				
			||||||
 | 
					            &document_store,
 | 
				
			||||||
 | 
					            arena,
 | 
				
			||||||
 | 
					            node_handle,
 | 
				
			||||||
 | 
					            orig_handle,
 | 
				
			||||||
 | 
					            declToCompletion,
 | 
				
			||||||
 | 
					            context,
 | 
				
			||||||
 | 
					            !is_type_val,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (is_type_val) return;
 | 
					    if (is_type_val) return;
 | 
				
			||||||
@ -399,38 +413,9 @@ fn nodeToCompletion(
 | 
				
			|||||||
            const func = analysis.fnProto(tree, node, &buf).?;
 | 
					            const func = analysis.fnProto(tree, node, &buf).?;
 | 
				
			||||||
            if (func.name_token) |name_token| {
 | 
					            if (func.name_token) |name_token| {
 | 
				
			||||||
                const use_snippets = config.enable_snippets and client_capabilities.supports_snippets;
 | 
					                const use_snippets = config.enable_snippets and client_capabilities.supports_snippets;
 | 
				
			||||||
 | 
					 | 
				
			||||||
                const insert_text = if (use_snippets) blk: {
 | 
					                const insert_text = if (use_snippets) blk: {
 | 
				
			||||||
                    // TODO Also check if we are dot accessing from a type val and dont skip in that case.
 | 
					                    const skip_self_param = !(parent_is_type_val orelse true) and
 | 
				
			||||||
                    const skip_self_param = if (func.ast.params.len > 0) param_check: {
 | 
					                        try analysis.hasSelfParam(arena, &document_store, handle, func);
 | 
				
			||||||
                        const in_container = analysis.innermostContainer(handle, tree.tokens.items(.start)[func.ast.fn_token]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        var it = func.iterate(tree);
 | 
					 | 
				
			||||||
                        const param = it.next().?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        if (param.type_expr == 0) break :param_check false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        if (try analysis.resolveTypeOfNode(&document_store, arena, .{
 | 
					 | 
				
			||||||
                            .node = param.type_expr,
 | 
					 | 
				
			||||||
                            .handle = handle,
 | 
					 | 
				
			||||||
                        })) |resolved_type| {
 | 
					 | 
				
			||||||
                            if (std.meta.eql(in_container, resolved_type))
 | 
					 | 
				
			||||||
                                break :param_check true;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        if (analysis.isPtrType(tree, param.type_expr)) {
 | 
					 | 
				
			||||||
                            if (try analysis.resolveTypeOfNode(&document_store, arena, .{
 | 
					 | 
				
			||||||
                                .node = datas[param.type_expr].rhs,
 | 
					 | 
				
			||||||
                                .handle = handle,
 | 
					 | 
				
			||||||
                            })) |resolved_prefix_op| {
 | 
					 | 
				
			||||||
                                if (std.meta.eql(in_container, resolved_prefix_op))
 | 
					 | 
				
			||||||
                                    break :param_check true;
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        break :param_check false;
 | 
					 | 
				
			||||||
                    } else false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    break :blk try analysis.getFunctionSnippet(&arena.allocator, tree, func, skip_self_param);
 | 
					                    break :blk try analysis.getFunctionSnippet(&arena.allocator, tree, func, skip_self_param);
 | 
				
			||||||
                } else tree.tokenSlice(func.name_token.?);
 | 
					                } else tree.tokenSlice(func.name_token.?);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -567,7 +552,7 @@ fn nodeToCompletion(
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn identifierFromPosition(pos_index: usize, handle: DocumentStore.Handle) []const u8 {
 | 
					pub fn identifierFromPosition(pos_index: usize, handle: DocumentStore.Handle) []const u8 {
 | 
				
			||||||
    const text = handle.document.text;
 | 
					    const text = handle.document.text;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (pos_index + 1 >= text.len) return &[0]u8{};
 | 
					    if (pos_index + 1 >= text.len) return &[0]u8{};
 | 
				
			||||||
@ -754,7 +739,11 @@ fn hoverDefinitionBuiltin(arena: *std.heap.ArenaAllocator, id: types.RequestId,
 | 
				
			|||||||
                .id = id,
 | 
					                .id = id,
 | 
				
			||||||
                .result = .{
 | 
					                .result = .{
 | 
				
			||||||
                    .Hover = .{
 | 
					                    .Hover = .{
 | 
				
			||||||
                        .contents = .{ .value = try std.fmt.allocPrint(&arena.allocator, "```zig\n{s}\n```\n{s}", .{ builtin.signature, builtin.documentation }) },
 | 
					                        .contents = .{ .value = try std.fmt.allocPrint(
 | 
				
			||||||
 | 
					                            &arena.allocator,
 | 
				
			||||||
 | 
					                            "```zig\n{s}\n```\n{s}",
 | 
				
			||||||
 | 
					                            .{ builtin.signature, builtin.documentation },
 | 
				
			||||||
 | 
					                        ) },
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
@ -945,13 +934,6 @@ fn referencesDefinitionLabel(arena: *std.heap.ArenaAllocator, id: types.RequestI
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const DeclToCompletionContext = struct {
 | 
					 | 
				
			||||||
    completions: *std.ArrayList(types.CompletionItem),
 | 
					 | 
				
			||||||
    config: *const Config,
 | 
					 | 
				
			||||||
    arena: *std.heap.ArenaAllocator,
 | 
					 | 
				
			||||||
    orig_handle: *DocumentStore.Handle,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn hasComment(tree: ast.Tree, start_token: ast.TokenIndex, end_token: ast.TokenIndex) bool {
 | 
					fn hasComment(tree: ast.Tree, start_token: ast.TokenIndex, end_token: ast.TokenIndex) bool {
 | 
				
			||||||
    const token_starts = tree.tokens.items(.start);
 | 
					    const token_starts = tree.tokens.items(.start);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -961,6 +943,14 @@ fn hasComment(tree: ast.Tree, start_token: ast.TokenIndex, end_token: ast.TokenI
 | 
				
			|||||||
    return std.mem.indexOf(u8, tree.source[start..end], "//") != null;
 | 
					    return std.mem.indexOf(u8, tree.source[start..end], "//") != null;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const DeclToCompletionContext = struct {
 | 
				
			||||||
 | 
					    completions: *std.ArrayList(types.CompletionItem),
 | 
				
			||||||
 | 
					    config: *const Config,
 | 
				
			||||||
 | 
					    arena: *std.heap.ArenaAllocator,
 | 
				
			||||||
 | 
					    orig_handle: *DocumentStore.Handle,
 | 
				
			||||||
 | 
					    parent_is_type_val: ?bool = null,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn declToCompletion(context: DeclToCompletionContext, decl_handle: analysis.DeclWithHandle) !void {
 | 
					fn declToCompletion(context: DeclToCompletionContext, decl_handle: analysis.DeclWithHandle) !void {
 | 
				
			||||||
    const tree = decl_handle.handle.tree;
 | 
					    const tree = decl_handle.handle.tree;
 | 
				
			||||||
    switch (decl_handle.decl.*) {
 | 
					    switch (decl_handle.decl.*) {
 | 
				
			||||||
@ -971,6 +961,7 @@ fn declToCompletion(context: DeclToCompletionContext, decl_handle: analysis.Decl
 | 
				
			|||||||
            null,
 | 
					            null,
 | 
				
			||||||
            context.orig_handle,
 | 
					            context.orig_handle,
 | 
				
			||||||
            false,
 | 
					            false,
 | 
				
			||||||
 | 
					            context.parent_is_type_val,
 | 
				
			||||||
            context.config.*,
 | 
					            context.config.*,
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
        .param_decl => |param| {
 | 
					        .param_decl => |param| {
 | 
				
			||||||
@ -1280,7 +1271,8 @@ fn initializeHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req:
 | 
				
			|||||||
                },
 | 
					                },
 | 
				
			||||||
                .capabilities = .{
 | 
					                .capabilities = .{
 | 
				
			||||||
                    .signatureHelpProvider = .{
 | 
					                    .signatureHelpProvider = .{
 | 
				
			||||||
                        .triggerCharacters = &[_][]const u8{ "(", "," },
 | 
					                        .triggerCharacters = &.{"("},
 | 
				
			||||||
 | 
					                        .retriggerCharacters = &.{","},
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    .textDocumentSync = .Full,
 | 
					                    .textDocumentSync = .Full,
 | 
				
			||||||
                    .renameProvider = true,
 | 
					                    .renameProvider = true,
 | 
				
			||||||
@ -1398,39 +1390,77 @@ fn semanticTokensFullHandler(arena: *std.heap.ArenaAllocator, id: types.RequestI
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn completionHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requests.Completion, config: Config) !void {
 | 
					fn completionHandler(
 | 
				
			||||||
 | 
					    arena: *std.heap.ArenaAllocator,
 | 
				
			||||||
 | 
					    id: types.RequestId,
 | 
				
			||||||
 | 
					    req: requests.Completion,
 | 
				
			||||||
 | 
					    config: Config,
 | 
				
			||||||
 | 
					) !void {
 | 
				
			||||||
    const handle = document_store.getHandle(req.params.textDocument.uri) orelse {
 | 
					    const handle = document_store.getHandle(req.params.textDocument.uri) orelse {
 | 
				
			||||||
        logger.warn("Trying to complete in non existent document {s}", .{req.params.textDocument.uri});
 | 
					        logger.warn("Trying to complete in non existent document {s}", .{req.params.textDocument.uri});
 | 
				
			||||||
        return try respondGeneric(id, no_completions_response);
 | 
					        return try respondGeneric(id, no_completions_response);
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (req.params.position.character >= 0) {
 | 
					    if (req.params.position.character == 0)
 | 
				
			||||||
        const doc_position = try offsets.documentPosition(handle.document, req.params.position, offset_encoding);
 | 
					        return try respondGeneric(id, no_completions_response);
 | 
				
			||||||
        const pos_context = try analysis.documentPositionContext(arena, handle.document, doc_position);
 | 
					 | 
				
			||||||
        const use_snippets = config.enable_snippets and client_capabilities.supports_snippets;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        switch (pos_context) {
 | 
					    const doc_position = try offsets.documentPosition(handle.document, req.params.position, offset_encoding);
 | 
				
			||||||
            .builtin => try completeBuiltin(arena, id, config),
 | 
					    const pos_context = try analysis.documentPositionContext(arena, handle.document, doc_position);
 | 
				
			||||||
            .var_access, .empty => try completeGlobal(arena, id, doc_position.absolute_index, handle, config),
 | 
					    const use_snippets = config.enable_snippets and client_capabilities.supports_snippets;
 | 
				
			||||||
            .field_access => |range| try completeFieldAccess(arena, id, handle, doc_position, range, config),
 | 
					
 | 
				
			||||||
            .global_error_set => try completeError(arena, id, handle, config),
 | 
					    switch (pos_context) {
 | 
				
			||||||
            .enum_literal => try completeDot(arena, id, handle, config),
 | 
					        .builtin => try completeBuiltin(arena, id, config),
 | 
				
			||||||
            .label => try completeLabel(arena, id, doc_position.absolute_index, handle, config),
 | 
					        .var_access, .empty => try completeGlobal(arena, id, doc_position.absolute_index, handle, config),
 | 
				
			||||||
            else => try respondGeneric(id, no_completions_response),
 | 
					        .field_access => |range| try completeFieldAccess(arena, id, handle, doc_position, range, config),
 | 
				
			||||||
        }
 | 
					        .global_error_set => try completeError(arena, id, handle, config),
 | 
				
			||||||
    } else {
 | 
					        .enum_literal => try completeDot(arena, id, handle, config),
 | 
				
			||||||
        try respondGeneric(id, no_completions_response);
 | 
					        .label => try completeLabel(arena, id, doc_position.absolute_index, handle, config),
 | 
				
			||||||
 | 
					        else => try respondGeneric(id, no_completions_response),
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn signatureHelperHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, config: Config) !void {
 | 
					fn signatureHelpHandler(
 | 
				
			||||||
    // TODO Implement this
 | 
					    arena: *std.heap.ArenaAllocator,
 | 
				
			||||||
    try respondGeneric(id,
 | 
					    id: types.RequestId,
 | 
				
			||||||
        \\,"result":{"signatures":[]}}
 | 
					    req: requests.SignatureHelp,
 | 
				
			||||||
    );
 | 
					    config: Config,
 | 
				
			||||||
 | 
					) !void {
 | 
				
			||||||
 | 
					    const getSignatureInfo = @import("signature_help.zig").getSignatureInfo;
 | 
				
			||||||
 | 
					    const handle = document_store.getHandle(req.params.textDocument.uri) orelse {
 | 
				
			||||||
 | 
					        logger.warn("Trying to get signature help in non existent document {s}", .{req.params.textDocument.uri});
 | 
				
			||||||
 | 
					        return try respondGeneric(id, no_signatures_response);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (req.params.position.character == 0)
 | 
				
			||||||
 | 
					        return try respondGeneric(id, no_signatures_response);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const doc_position = try offsets.documentPosition(handle.document, req.params.position, offset_encoding);
 | 
				
			||||||
 | 
					    if (try getSignatureInfo(
 | 
				
			||||||
 | 
					        &document_store,
 | 
				
			||||||
 | 
					        arena,
 | 
				
			||||||
 | 
					        handle,
 | 
				
			||||||
 | 
					        doc_position.absolute_index,
 | 
				
			||||||
 | 
					        data,
 | 
				
			||||||
 | 
					    )) |sig_info| {
 | 
				
			||||||
 | 
					        return try send(arena, types.Response{
 | 
				
			||||||
 | 
					            .id = id,
 | 
				
			||||||
 | 
					            .result = .{ .SignatureHelp = .{
 | 
				
			||||||
 | 
					                .signatures = &[1]types.SignatureInformation{sig_info},
 | 
				
			||||||
 | 
					                .activeSignature = 0,
 | 
				
			||||||
 | 
					                .activeParameter = sig_info.activeParameter,
 | 
				
			||||||
 | 
					            } },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return try respondGeneric(id, no_signatures_response);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn gotoHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requests.GotoDefinition, config: Config, resolve_alias: bool) !void {
 | 
					fn gotoHandler(
 | 
				
			||||||
 | 
					    arena: *std.heap.ArenaAllocator,
 | 
				
			||||||
 | 
					    id: types.RequestId,
 | 
				
			||||||
 | 
					    req: requests.GotoDefinition,
 | 
				
			||||||
 | 
					    config: Config,
 | 
				
			||||||
 | 
					    resolve_alias: bool,
 | 
				
			||||||
 | 
					) !void {
 | 
				
			||||||
    const handle = document_store.getHandle(req.params.textDocument.uri) orelse {
 | 
					    const handle = document_store.getHandle(req.params.textDocument.uri) orelse {
 | 
				
			||||||
        logger.warn("Trying to go to definition in non existent document {s}", .{req.params.textDocument.uri});
 | 
					        logger.warn("Trying to go to definition in non existent document {s}", .{req.params.textDocument.uri});
 | 
				
			||||||
        return try respondGeneric(id, null_result_response);
 | 
					        return try respondGeneric(id, null_result_response);
 | 
				
			||||||
@ -1612,7 +1642,7 @@ fn processJsonRpc(arena: *std.heap.ArenaAllocator, parser: *std.json.Parser, jso
 | 
				
			|||||||
        .{ "textDocument/didClose", requests.CloseDocument, closeDocumentHandler },
 | 
					        .{ "textDocument/didClose", requests.CloseDocument, closeDocumentHandler },
 | 
				
			||||||
        .{ "textDocument/semanticTokens/full", requests.SemanticTokensFull, semanticTokensFullHandler },
 | 
					        .{ "textDocument/semanticTokens/full", requests.SemanticTokensFull, semanticTokensFullHandler },
 | 
				
			||||||
        .{ "textDocument/completion", requests.Completion, completionHandler },
 | 
					        .{ "textDocument/completion", requests.Completion, completionHandler },
 | 
				
			||||||
        .{ "textDocument/signatureHelp", void, signatureHelperHandler },
 | 
					        .{ "textDocument/signatureHelp", requests.SignatureHelp, signatureHelpHandler },
 | 
				
			||||||
        .{ "textDocument/definition", requests.GotoDefinition, gotoDefinitionHandler },
 | 
					        .{ "textDocument/definition", requests.GotoDefinition, gotoDefinitionHandler },
 | 
				
			||||||
        .{ "textDocument/typeDefinition", requests.GotoDefinition, gotoDefinitionHandler },
 | 
					        .{ "textDocument/typeDefinition", requests.GotoDefinition, gotoDefinitionHandler },
 | 
				
			||||||
        .{ "textDocument/implementation", requests.GotoDefinition, gotoDefinitionHandler },
 | 
					        .{ "textDocument/implementation", requests.GotoDefinition, gotoDefinitionHandler },
 | 
				
			||||||
 | 
				
			|||||||
@ -18,12 +18,22 @@ fn Default(comptime T: type, comptime default_value: T) type {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn ErrorUnwrappedReturnOf(comptime func: anytype) type {
 | 
				
			||||||
 | 
					    return switch (@typeInfo(@TypeOf(func))) {
 | 
				
			||||||
 | 
					        .Fn, .BoundFn => |fn_info| switch (@typeInfo(fn_info.return_type.?)) {
 | 
				
			||||||
 | 
					            .ErrorUnion => |err_union| err_union.payload,
 | 
				
			||||||
 | 
					            else => |T| return T,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        else => unreachable,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn Transform(comptime Original: type, comptime transform_fn: anytype) type {
 | 
					fn Transform(comptime Original: type, comptime transform_fn: anytype) type {
 | 
				
			||||||
    return struct {
 | 
					    return struct {
 | 
				
			||||||
        pub const original_type = Original;
 | 
					        pub const original_type = Original;
 | 
				
			||||||
        pub const transform = transform_fn;
 | 
					        pub const transform = transform_fn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        value: @TypeOf(transform(@as(Original, undefined))),
 | 
					        value: ErrorUnwrappedReturnOf(transform_fn),
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -101,26 +111,34 @@ fn fromDynamicTreeInternal(arena: *std.heap.ArenaAllocator, value: std.json.Valu
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    } else if (T == std.json.Value) {
 | 
					    } else if (T == std.json.Value) {
 | 
				
			||||||
        out.* = value;
 | 
					        out.* = value;
 | 
				
			||||||
    } else {
 | 
					    } else if (comptime std.meta.trait.is(.Enum)(T)) {
 | 
				
			||||||
        switch (T) {
 | 
					        const info = @typeInfo(T).Enum;
 | 
				
			||||||
            bool => {
 | 
					        if (info.layout != .Auto)
 | 
				
			||||||
                if (value != .Bool) return error.MalformedJson;
 | 
					            @compileError("Only auto layout enums are allowed");
 | 
				
			||||||
                out.* = value.Bool;
 | 
					
 | 
				
			||||||
            },
 | 
					        const TagType = info.tag_type;
 | 
				
			||||||
            i64 => {
 | 
					        if (value != .Integer) return error.MalformedJson;
 | 
				
			||||||
                if (value != .Integer) return error.MalformedJson;
 | 
					        out.* = std.meta.intToEnum(
 | 
				
			||||||
                out.* = value.Integer;
 | 
					            T,
 | 
				
			||||||
            },
 | 
					            std.math.cast(TagType, value.Integer) catch return error.MalformedJson,
 | 
				
			||||||
            f64 => {
 | 
					        ) catch return error.MalformedJson;
 | 
				
			||||||
                if (value != .Float) return error.MalformedJson;
 | 
					    } else if (comptime std.meta.trait.is(.Int)(T)) {
 | 
				
			||||||
                out.* = value.Float;
 | 
					        if (value != .Integer) return error.MalformedJson;
 | 
				
			||||||
            },
 | 
					        out.* = std.math.cast(T, value.Integer) catch return error.MalformedJson;
 | 
				
			||||||
            []const u8 => {
 | 
					    } else switch (T) {
 | 
				
			||||||
                if (value != .String) return error.MalformedJson;
 | 
					        bool => {
 | 
				
			||||||
                out.* = value.String;
 | 
					            if (value != .Bool) return error.MalformedJson;
 | 
				
			||||||
            },
 | 
					            out.* = value.Bool;
 | 
				
			||||||
            else => @compileError("Invalid type " ++ @typeName(T)),
 | 
					        },
 | 
				
			||||||
        }
 | 
					        f64 => {
 | 
				
			||||||
 | 
					            if (value != .Float) return error.MalformedJson;
 | 
				
			||||||
 | 
					            out.* = value.Float;
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        []const u8 => {
 | 
				
			||||||
 | 
					            if (value != .String) return error.MalformedJson;
 | 
				
			||||||
 | 
					            out.* = value.String;
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        else => @compileError("Invalid type " ++ @typeName(T)),
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -204,6 +222,23 @@ const TextDocumentIdentifierPositionRequest = struct {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub const SignatureHelp = struct {
 | 
				
			||||||
 | 
					    params: struct {
 | 
				
			||||||
 | 
					        textDocument: TextDocumentIdentifier,
 | 
				
			||||||
 | 
					        position: types.Position,
 | 
				
			||||||
 | 
					        context: ?struct {
 | 
				
			||||||
 | 
					            triggerKind: enum {
 | 
				
			||||||
 | 
					                invoked = 1,
 | 
				
			||||||
 | 
					                trigger_character = 2,
 | 
				
			||||||
 | 
					                content_change = 3,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            triggerCharacter: ?[]const u8,
 | 
				
			||||||
 | 
					            isRetrigger: bool,
 | 
				
			||||||
 | 
					            activeSignatureHelp: ?types.SignatureHelp,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub const Completion = TextDocumentIdentifierPositionRequest;
 | 
					pub const Completion = TextDocumentIdentifierPositionRequest;
 | 
				
			||||||
pub const GotoDefinition = TextDocumentIdentifierPositionRequest;
 | 
					pub const GotoDefinition = TextDocumentIdentifierPositionRequest;
 | 
				
			||||||
pub const GotoDeclaration = TextDocumentIdentifierPositionRequest;
 | 
					pub const GotoDeclaration = TextDocumentIdentifierPositionRequest;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										374
									
								
								src/signature_help.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										374
									
								
								src/signature_help.zig
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,374 @@
 | 
				
			|||||||
 | 
					const std = @import("std");
 | 
				
			||||||
 | 
					const analysis = @import("analysis.zig");
 | 
				
			||||||
 | 
					const offsets = @import("offsets.zig");
 | 
				
			||||||
 | 
					const DocumentStore = @import("document_store.zig");
 | 
				
			||||||
 | 
					const types = @import("types.zig");
 | 
				
			||||||
 | 
					const ast = std.zig.ast;
 | 
				
			||||||
 | 
					const Token = std.zig.Token;
 | 
				
			||||||
 | 
					const identifierFromPosition = @import("main.zig").identifierFromPosition;
 | 
				
			||||||
 | 
					usingnamespace @import("ast.zig");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn fnProtoToSignatureInfo(
 | 
				
			||||||
 | 
					    document_store: *DocumentStore,
 | 
				
			||||||
 | 
					    arena: *std.heap.ArenaAllocator,
 | 
				
			||||||
 | 
					    commas: u32,
 | 
				
			||||||
 | 
					    skip_self_param: bool,
 | 
				
			||||||
 | 
					    handle: *DocumentStore.Handle,
 | 
				
			||||||
 | 
					    fn_node: ast.Node.Index,
 | 
				
			||||||
 | 
					    proto: ast.full.FnProto,
 | 
				
			||||||
 | 
					) !types.SignatureInformation {
 | 
				
			||||||
 | 
					    const ParameterInformation = types.SignatureInformation.ParameterInformation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const tree = handle.tree;
 | 
				
			||||||
 | 
					    const token_starts = tree.tokens.items(.start);
 | 
				
			||||||
 | 
					    const alloc = &arena.allocator;
 | 
				
			||||||
 | 
					    const label = analysis.getFunctionSignature(tree, proto);
 | 
				
			||||||
 | 
					    const proto_comments = types.MarkupContent{ .value = if (try analysis.getDocComments(
 | 
				
			||||||
 | 
					        alloc,
 | 
				
			||||||
 | 
					        tree,
 | 
				
			||||||
 | 
					        fn_node,
 | 
				
			||||||
 | 
					        .Markdown,
 | 
				
			||||||
 | 
					    )) |dc|
 | 
				
			||||||
 | 
					        dc
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        "" };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const arg_idx = if (skip_self_param) blk: {
 | 
				
			||||||
 | 
					        const has_self_param = try analysis.hasSelfParam(arena, document_store, handle, proto);
 | 
				
			||||||
 | 
					        break :blk commas + @boolToInt(has_self_param);
 | 
				
			||||||
 | 
					    } else commas;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var params = std.ArrayListUnmanaged(ParameterInformation){};
 | 
				
			||||||
 | 
					    var param_it = proto.iterate(tree);
 | 
				
			||||||
 | 
					    while (param_it.next()) |param| {
 | 
				
			||||||
 | 
					        const param_comments = if (param.first_doc_comment) |dc|
 | 
				
			||||||
 | 
					            types.MarkupContent{ .value = try analysis.collectDocComments(
 | 
				
			||||||
 | 
					                alloc,
 | 
				
			||||||
 | 
					                tree,
 | 
				
			||||||
 | 
					                dc,
 | 
				
			||||||
 | 
					                .Markdown,
 | 
				
			||||||
 | 
					            ) }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var param_label_start: usize = 0;
 | 
				
			||||||
 | 
					        var param_label_end: usize = 0;
 | 
				
			||||||
 | 
					        if (param.comptime_noalias) |cn| {
 | 
				
			||||||
 | 
					            param_label_start = token_starts[cn];
 | 
				
			||||||
 | 
					            param_label_end = param_label_start + tree.tokenSlice(cn).len;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (param.name_token) |nt| {
 | 
				
			||||||
 | 
					            if (param_label_start == 0)
 | 
				
			||||||
 | 
					                param_label_start = token_starts[nt];
 | 
				
			||||||
 | 
					            param_label_end = token_starts[nt] + tree.tokenSlice(nt).len;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (param.anytype_ellipsis3) |ae| {
 | 
				
			||||||
 | 
					            if (param_label_start == 0)
 | 
				
			||||||
 | 
					                param_label_start = token_starts[ae];
 | 
				
			||||||
 | 
					            param_label_end = token_starts[ae] + tree.tokenSlice(ae).len;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (param.type_expr != 0) {
 | 
				
			||||||
 | 
					            if (param_label_start == 0)
 | 
				
			||||||
 | 
					                param_label_start = token_starts[tree.firstToken(param.type_expr)];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const last_param_tok = lastToken(tree, param.type_expr);
 | 
				
			||||||
 | 
					            param_label_end = token_starts[last_param_tok] + tree.tokenSlice(last_param_tok).len;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const param_label = tree.source[param_label_start..param_label_end];
 | 
				
			||||||
 | 
					        try params.append(alloc, .{
 | 
				
			||||||
 | 
					            .label = param_label,
 | 
				
			||||||
 | 
					            .documentation = param_comments,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return types.SignatureInformation{
 | 
				
			||||||
 | 
					        .label = label,
 | 
				
			||||||
 | 
					        .documentation = proto_comments,
 | 
				
			||||||
 | 
					        .parameters = params.items,
 | 
				
			||||||
 | 
					        .activeParameter = arg_idx,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn getSignatureInfo(
 | 
				
			||||||
 | 
					    document_store: *DocumentStore,
 | 
				
			||||||
 | 
					    arena: *std.heap.ArenaAllocator,
 | 
				
			||||||
 | 
					    handle: *DocumentStore.Handle,
 | 
				
			||||||
 | 
					    absolute_index: usize,
 | 
				
			||||||
 | 
					    comptime data: type,
 | 
				
			||||||
 | 
					) !?types.SignatureInformation {
 | 
				
			||||||
 | 
					    const innermost_block = analysis.innermostScope(handle.*, absolute_index);
 | 
				
			||||||
 | 
					    const tree = handle.tree;
 | 
				
			||||||
 | 
					    const token_tags = tree.tokens.items(.tag);
 | 
				
			||||||
 | 
					    const token_starts = tree.tokens.items(.start);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Use the innermost scope to determine the earliest token we would need
 | 
				
			||||||
 | 
					    //   to scan up to find a function or buitin call
 | 
				
			||||||
 | 
					    const first_token = tree.firstToken(innermost_block);
 | 
				
			||||||
 | 
					    // We start by finding the token that includes the current cursor position
 | 
				
			||||||
 | 
					    const last_token = blk: {
 | 
				
			||||||
 | 
					        if (token_starts[0] >= absolute_index)
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var i: u32 = 1;
 | 
				
			||||||
 | 
					        while (i < token_tags.len) : (i += 1) {
 | 
				
			||||||
 | 
					            if (token_starts[i] >= absolute_index) {
 | 
				
			||||||
 | 
					                break :blk i - 1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        break :blk @truncate(u32, token_tags.len - 1);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // We scan the tokens from last to first, adding and removing open and close
 | 
				
			||||||
 | 
					    //   delimiter tokens to a stack, while keeping track of commas corresponding
 | 
				
			||||||
 | 
					    //   to each of the blocks in a stack.
 | 
				
			||||||
 | 
					    // When we encounter a dangling left parenthesis token, we continue scanning
 | 
				
			||||||
 | 
					    //   backwards for a compatible possible function call lhs expression or a
 | 
				
			||||||
 | 
					    //   single builtin token.
 | 
				
			||||||
 | 
					    // When a function call expression is detected, it is resolved to a declaration
 | 
				
			||||||
 | 
					    //   or a function type and the resulting function prototype is converted into
 | 
				
			||||||
 | 
					    //   a signature information object.
 | 
				
			||||||
 | 
					    const StackSymbol = enum {
 | 
				
			||||||
 | 
					        l_paren,
 | 
				
			||||||
 | 
					        r_paren,
 | 
				
			||||||
 | 
					        l_brace,
 | 
				
			||||||
 | 
					        r_brace,
 | 
				
			||||||
 | 
					        l_bracket,
 | 
				
			||||||
 | 
					        r_bracket,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fn from(tag: Token.Tag) @This() {
 | 
				
			||||||
 | 
					            return switch (tag) {
 | 
				
			||||||
 | 
					                .l_paren => .l_paren,
 | 
				
			||||||
 | 
					                .r_paren => .r_paren,
 | 
				
			||||||
 | 
					                .l_brace => .l_brace,
 | 
				
			||||||
 | 
					                .r_brace => .r_brace,
 | 
				
			||||||
 | 
					                .l_bracket => .l_bracket,
 | 
				
			||||||
 | 
					                .r_bracket => .r_bracket,
 | 
				
			||||||
 | 
					                else => unreachable,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    const alloc = &arena.allocator;
 | 
				
			||||||
 | 
					    var symbol_stack = try std.ArrayListUnmanaged(StackSymbol).initCapacity(alloc, 8);
 | 
				
			||||||
 | 
					    var curr_commas: u32 = 0;
 | 
				
			||||||
 | 
					    var comma_stack = try std.ArrayListUnmanaged(u32).initCapacity(alloc, 4);
 | 
				
			||||||
 | 
					    var curr_token = last_token;
 | 
				
			||||||
 | 
					    while (curr_token >= first_token and curr_token != 0) : (curr_token -= 1) {
 | 
				
			||||||
 | 
					        switch (token_tags[curr_token]) {
 | 
				
			||||||
 | 
					            .comma => curr_commas += 1,
 | 
				
			||||||
 | 
					            .l_brace => {
 | 
				
			||||||
 | 
					                curr_commas = comma_stack.popOrNull() orelse 0;
 | 
				
			||||||
 | 
					                if (symbol_stack.items.len != 0) {
 | 
				
			||||||
 | 
					                    const peek_sym = symbol_stack.items[symbol_stack.items.len - 1];
 | 
				
			||||||
 | 
					                    switch (peek_sym) {
 | 
				
			||||||
 | 
					                        .r_brace => {
 | 
				
			||||||
 | 
					                            _ = symbol_stack.pop();
 | 
				
			||||||
 | 
					                            continue;
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        .r_bracket, .r_paren => {
 | 
				
			||||||
 | 
					                            return null;
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        else => {},
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                try symbol_stack.append(alloc, .l_brace);
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            .l_bracket => {
 | 
				
			||||||
 | 
					                curr_commas = comma_stack.popOrNull() orelse 0;
 | 
				
			||||||
 | 
					                if (symbol_stack.items.len != 0) {
 | 
				
			||||||
 | 
					                    const peek_sym = symbol_stack.items[symbol_stack.items.len - 1];
 | 
				
			||||||
 | 
					                    switch (peek_sym) {
 | 
				
			||||||
 | 
					                        .r_bracket => {
 | 
				
			||||||
 | 
					                            _ = symbol_stack.pop();
 | 
				
			||||||
 | 
					                            continue;
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        .r_brace, .r_paren => {
 | 
				
			||||||
 | 
					                            return null;
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        else => {},
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                try symbol_stack.append(alloc, .l_bracket);
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            .l_paren => {
 | 
				
			||||||
 | 
					                const paren_commas = curr_commas;
 | 
				
			||||||
 | 
					                curr_commas = comma_stack.popOrNull() orelse 0;
 | 
				
			||||||
 | 
					                if (symbol_stack.items.len != 0) {
 | 
				
			||||||
 | 
					                    const peek_sym = symbol_stack.items[symbol_stack.items.len - 1];
 | 
				
			||||||
 | 
					                    switch (peek_sym) {
 | 
				
			||||||
 | 
					                        .r_paren => {
 | 
				
			||||||
 | 
					                            _ = symbol_stack.pop();
 | 
				
			||||||
 | 
					                            continue;
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        .r_brace, .r_bracket => {
 | 
				
			||||||
 | 
					                            return null;
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        else => {},
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Try to find a function expression or a builtin identifier
 | 
				
			||||||
 | 
					                if (curr_token == first_token)
 | 
				
			||||||
 | 
					                    return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                const expr_last_token = curr_token - 1;
 | 
				
			||||||
 | 
					                if (token_tags[expr_last_token] == .builtin) {
 | 
				
			||||||
 | 
					                    // Builtin token, find the builtin and construct signature information.
 | 
				
			||||||
 | 
					                    for (data.builtins) |builtin| {
 | 
				
			||||||
 | 
					                        if (std.mem.eql(u8, builtin.name, tree.tokenSlice(expr_last_token))) {
 | 
				
			||||||
 | 
					                            const param_infos = try alloc.alloc(
 | 
				
			||||||
 | 
					                                types.SignatureInformation.ParameterInformation,
 | 
				
			||||||
 | 
					                                builtin.arguments.len,
 | 
				
			||||||
 | 
					                            );
 | 
				
			||||||
 | 
					                            for (param_infos) |*info, i| {
 | 
				
			||||||
 | 
					                                info.* = .{
 | 
				
			||||||
 | 
					                                    .label = builtin.arguments[i],
 | 
				
			||||||
 | 
					                                    .documentation = null,
 | 
				
			||||||
 | 
					                                };
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            return types.SignatureInformation{
 | 
				
			||||||
 | 
					                                .label = builtin.signature,
 | 
				
			||||||
 | 
					                                .documentation = .{
 | 
				
			||||||
 | 
					                                    .value = builtin.documentation,
 | 
				
			||||||
 | 
					                                },
 | 
				
			||||||
 | 
					                                .parameters = param_infos,
 | 
				
			||||||
 | 
					                                .activeParameter = paren_commas,
 | 
				
			||||||
 | 
					                            };
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    return null;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                // Scan for a function call lhs expression.
 | 
				
			||||||
 | 
					                var state: union(enum) {
 | 
				
			||||||
 | 
					                    any,
 | 
				
			||||||
 | 
					                    in_bracket: u32,
 | 
				
			||||||
 | 
					                    in_paren: u32,
 | 
				
			||||||
 | 
					                } = .any;
 | 
				
			||||||
 | 
					                var i = expr_last_token;
 | 
				
			||||||
 | 
					                const expr_first_token = while (i > first_token) : (i -= 1) {
 | 
				
			||||||
 | 
					                    switch (state) {
 | 
				
			||||||
 | 
					                        .in_bracket => |*count| if (token_tags[i] == .r_bracket) {
 | 
				
			||||||
 | 
					                            count.* += 1;
 | 
				
			||||||
 | 
					                        } else if (token_tags[i] == .l_bracket) {
 | 
				
			||||||
 | 
					                            count.* -= 1;
 | 
				
			||||||
 | 
					                            if (count.* == 0)
 | 
				
			||||||
 | 
					                                state = .any;
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        .in_paren => |*count| if (token_tags[i] == .r_paren) {
 | 
				
			||||||
 | 
					                            count.* += 1;
 | 
				
			||||||
 | 
					                        } else if (token_tags[i] == .l_paren) {
 | 
				
			||||||
 | 
					                            count.* -= 1;
 | 
				
			||||||
 | 
					                            if (count.* == 0)
 | 
				
			||||||
 | 
					                                state = .any;
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        .any => switch (token_tags[i]) {
 | 
				
			||||||
 | 
					                            .r_bracket => state = .{ .in_bracket = 1 },
 | 
				
			||||||
 | 
					                            .r_paren => state = .{ .in_paren = 1 },
 | 
				
			||||||
 | 
					                            .identifier,
 | 
				
			||||||
 | 
					                            .period,
 | 
				
			||||||
 | 
					                            .period_asterisk,
 | 
				
			||||||
 | 
					                            => {},
 | 
				
			||||||
 | 
					                            else => break i + 1,
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else first_token + 1;
 | 
				
			||||||
 | 
					                if (state != .any or expr_first_token > expr_last_token) {
 | 
				
			||||||
 | 
					                    try symbol_stack.append(alloc, .l_paren);
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                const expr_start = token_starts[expr_first_token];
 | 
				
			||||||
 | 
					                const last_token_slice = tree.tokenSlice(expr_last_token);
 | 
				
			||||||
 | 
					                const expr_end = token_starts[expr_last_token] + last_token_slice.len;
 | 
				
			||||||
 | 
					                const expr_source = tree.source[expr_start..expr_end];
 | 
				
			||||||
 | 
					                // Resolve the expression.
 | 
				
			||||||
 | 
					                var tokenizer = std.zig.Tokenizer.init(expr_source);
 | 
				
			||||||
 | 
					                if (try analysis.getFieldAccessType(
 | 
				
			||||||
 | 
					                    document_store,
 | 
				
			||||||
 | 
					                    arena,
 | 
				
			||||||
 | 
					                    handle,
 | 
				
			||||||
 | 
					                    expr_start,
 | 
				
			||||||
 | 
					                    &tokenizer,
 | 
				
			||||||
 | 
					                )) |result| {
 | 
				
			||||||
 | 
					                    const type_handle = result.unwrapped orelse result.original;
 | 
				
			||||||
 | 
					                    var node = switch (type_handle.type.data) {
 | 
				
			||||||
 | 
					                        .other => |n| n,
 | 
				
			||||||
 | 
					                        else => {
 | 
				
			||||||
 | 
					                            try symbol_stack.append(alloc, .l_paren);
 | 
				
			||||||
 | 
					                            continue;
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    var buf: [1]ast.Node.Index = undefined;
 | 
				
			||||||
 | 
					                    if (fnProto(type_handle.handle.tree, node, &buf)) |proto| {
 | 
				
			||||||
 | 
					                        return try fnProtoToSignatureInfo(
 | 
				
			||||||
 | 
					                            document_store,
 | 
				
			||||||
 | 
					                            arena,
 | 
				
			||||||
 | 
					                            paren_commas,
 | 
				
			||||||
 | 
					                            false,
 | 
				
			||||||
 | 
					                            type_handle.handle,
 | 
				
			||||||
 | 
					                            node,
 | 
				
			||||||
 | 
					                            proto,
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    const name = identifierFromPosition(expr_end - 1, handle.*);
 | 
				
			||||||
 | 
					                    if (name.len == 0) {
 | 
				
			||||||
 | 
					                        try symbol_stack.append(alloc, .l_paren);
 | 
				
			||||||
 | 
					                        continue;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    const skip_self_param = !type_handle.type.is_type_val;
 | 
				
			||||||
 | 
					                    const decl_handle = (try analysis.lookupSymbolContainer(
 | 
				
			||||||
 | 
					                        document_store,
 | 
				
			||||||
 | 
					                        arena,
 | 
				
			||||||
 | 
					                        .{ .node = node, .handle = type_handle.handle },
 | 
				
			||||||
 | 
					                        name,
 | 
				
			||||||
 | 
					                        true,
 | 
				
			||||||
 | 
					                    )) orelse {
 | 
				
			||||||
 | 
					                        try symbol_stack.append(alloc, .l_paren);
 | 
				
			||||||
 | 
					                        continue;
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
 | 
					                    var res_handle = decl_handle.handle;
 | 
				
			||||||
 | 
					                    node = switch (decl_handle.decl.*) {
 | 
				
			||||||
 | 
					                        .ast_node => |n| n,
 | 
				
			||||||
 | 
					                        else => {
 | 
				
			||||||
 | 
					                            try symbol_stack.append(alloc, .l_paren);
 | 
				
			||||||
 | 
					                            continue;
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (try analysis.resolveVarDeclAlias(
 | 
				
			||||||
 | 
					                        document_store,
 | 
				
			||||||
 | 
					                        arena,
 | 
				
			||||||
 | 
					                        .{ .node = node, .handle = decl_handle.handle },
 | 
				
			||||||
 | 
					                    )) |resolved| {
 | 
				
			||||||
 | 
					                        switch (resolved.decl.*) {
 | 
				
			||||||
 | 
					                            .ast_node => |n| {
 | 
				
			||||||
 | 
					                                res_handle = resolved.handle;
 | 
				
			||||||
 | 
					                                node = n;
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                            else => {},
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (fnProto(res_handle.tree, node, &buf)) |proto| {
 | 
				
			||||||
 | 
					                        return try fnProtoToSignatureInfo(
 | 
				
			||||||
 | 
					                            document_store,
 | 
				
			||||||
 | 
					                            arena,
 | 
				
			||||||
 | 
					                            paren_commas,
 | 
				
			||||||
 | 
					                            skip_self_param,
 | 
				
			||||||
 | 
					                            res_handle,
 | 
				
			||||||
 | 
					                            node,
 | 
				
			||||||
 | 
					                            proto,
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            .r_brace, .r_paren, .r_bracket => |tag| {
 | 
				
			||||||
 | 
					                try comma_stack.append(alloc, curr_commas);
 | 
				
			||||||
 | 
					                curr_commas = 0;
 | 
				
			||||||
 | 
					                try symbol_stack.append(alloc, StackSymbol.from(tag));
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            else => {},
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -31,6 +31,7 @@ pub const Hover = struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/// Params of a response (result)
 | 
					/// Params of a response (result)
 | 
				
			||||||
pub const ResponseParams = union(enum) {
 | 
					pub const ResponseParams = union(enum) {
 | 
				
			||||||
 | 
					    SignatureHelp: SignatureHelp,
 | 
				
			||||||
    CompletionList: CompletionList,
 | 
					    CompletionList: CompletionList,
 | 
				
			||||||
    Location: Location,
 | 
					    Location: Location,
 | 
				
			||||||
    Hover: Hover,
 | 
					    Hover: Hover,
 | 
				
			||||||
@ -289,12 +290,32 @@ pub const WorkspaceFolder = struct {
 | 
				
			|||||||
    name: []const u8,
 | 
					    name: []const u8,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub const SignatureInformation = struct {
 | 
				
			||||||
 | 
					    pub const ParameterInformation = struct {
 | 
				
			||||||
 | 
					        // TODO Can also send a pair of encoded offsets
 | 
				
			||||||
 | 
					        label: []const u8,
 | 
				
			||||||
 | 
					        documentation: ?MarkupContent,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    label: []const u8,
 | 
				
			||||||
 | 
					    documentation: ?MarkupContent,
 | 
				
			||||||
 | 
					    parameters: ?[]const ParameterInformation,
 | 
				
			||||||
 | 
					    activeParameter: ?u32,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub const SignatureHelp = struct {
 | 
				
			||||||
 | 
					    signatures: ?[]const SignatureInformation,
 | 
				
			||||||
 | 
					    activeSignature: ?u32,
 | 
				
			||||||
 | 
					    activeParameter: ?u32,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Only includes options we set in our initialize result.
 | 
					// Only includes options we set in our initialize result.
 | 
				
			||||||
const InitializeResult = struct {
 | 
					const InitializeResult = struct {
 | 
				
			||||||
    offsetEncoding: []const u8,
 | 
					    offsetEncoding: []const u8,
 | 
				
			||||||
    capabilities: struct {
 | 
					    capabilities: struct {
 | 
				
			||||||
        signatureHelpProvider: struct {
 | 
					        signatureHelpProvider: struct {
 | 
				
			||||||
            triggerCharacters: []const []const u8,
 | 
					            triggerCharacters: []const []const u8,
 | 
				
			||||||
 | 
					            retriggerCharacters: []const []const u8,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        textDocumentSync: enum {
 | 
					        textDocumentSync: enum {
 | 
				
			||||||
            None = 0,
 | 
					            None = 0,
 | 
				
			||||||
 | 
				
			|||||||
@ -153,6 +153,6 @@ test "Requesting utf-8 offset encoding" {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    try sendRequest(shutdown_message, process);
 | 
					    try sendRequest(shutdown_message, process);
 | 
				
			||||||
    try consumeOutputAndWait(process, .{
 | 
					    try consumeOutputAndWait(process, .{
 | 
				
			||||||
        \\{"jsonrpc":"2.0","id":0,"result":{"offsetEncoding":"utf-8","capabilities":{"signatureHelpProvider":{"triggerCharacters":["(",","]},"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"}}}
 | 
					        \\{"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"}}}
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user