diff --git a/src/analysis.zig b/src/analysis.zig index ae35af75b..5ba3a5dbb 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -143,6 +143,37 @@ pub fn getFunctionSignature(tree: Ast, func: Ast.full.FnProto) []const u8 { return offsets.tokensToSlice(tree, first_token, last_token); } +/// Formats a function signature in markdown style with 4 space indent for cases where it's a nested +/// decl and is more than a single line. +/// +/// Otherwise it will write the signature as it's provided but in markdown style. +pub fn formatFunctionSignatureMarkdown(signature: []const u8, writer: anytype) @TypeOf(writer).Error!void { + var iter = std.mem.tokenizeScalar(u8, signature, '\n'); + + try writer.writeAll("```zig\n"); + + const first = iter.next() orelse unreachable; // Expected at least one. + try writer.writeAll(first); + + while (iter.next()) |slice| { + const trimmed = std.mem.trimLeft(u8, slice, " "); + + switch (trimmed[0]) { + ')', '}' => { + try writer.writeAll("\n"); + try writer.writeAll(trimmed); + }, + else => { + try writer.writeAll("\n"); + try writer.writeAll(" "); + try writer.writeAll(trimmed); + }, + } + } + + try writer.writeAll("\n```"); +} + fn formatSnippetPlaceholder( data: []const u8, comptime fmt: []const u8, diff --git a/src/features/hover.zig b/src/features/hover.zig index cd207437c..9f5c7bf23 100644 --- a/src/features/hover.zig +++ b/src/features/hover.zig @@ -152,7 +152,7 @@ fn hoverSymbolRecursive( for (doc_strings.items) |doc| try writer.print("{s}\n\n", .{doc}); if (is_fn) { - try writer.print("```zig\n{s}\n```", .{def_str}); + try Analyser.formatFunctionSignatureMarkdown(def_str, writer); } else { try writer.print("```zig\n{s}\n```\n```zig\n({s})\n```", .{ def_str, resolved_type_str }); } diff --git a/tests/lsp_features/hover.zig b/tests/lsp_features/hover.zig index 3b4787841..db4094e27 100644 --- a/tests/lsp_features/hover.zig +++ b/tests/lsp_features/hover.zig @@ -923,6 +923,33 @@ test "function parameter" { ); } +test "nested" { + try testHover( + \\const Error = error{InvalidBaz}; + \\pub fn FooResponse(comptime T: type) type { + \\ return struct {baz: T}; + \\} + \\ + \\pub fn Foo(comptime T: type) type { + \\ return struct { + \\ pub fn fooAndBar(self: *T, baz: u8) (Error || error{ + \\ InvalidFoo, + \\ InvalidBar, + \\ })!FooResponse(T) {} + \\ }; + \\} + , + \\```zig + \\fn fooAndBar(self: *T, baz: u8) (Error || error{ + \\ InvalidFoo, + \\ InvalidBar, + \\})!FooResponse(T) + \\``` + \\ + \\Go to [FooResponse](file:///test.zig#L2) + ); +} + test "optional" { try testHover( \\const S = struct { a: i32 };