From 2b153e046ce7ebd7266070b37d5878869388bca0 Mon Sep 17 00:00:00 2001 From: SuperAuguste Date: Wed, 27 May 2020 20:39:36 -0400 Subject: [PATCH] outlines, made JSON stdout memory dynamic --- src/analysis.zig | 90 +++++++++++++++++++++++++++++++++++++++++++++++- src/main.zig | 37 ++++++++++++++++---- src/types.zig | 48 ++++++++++++++++++++++++++ 3 files changed, 167 insertions(+), 8 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index 9199822..f732299 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -1,6 +1,7 @@ const std = @import("std"); const AnalysisContext = @import("document_store.zig").AnalysisContext; const ast = std.zig.ast; +const types = @import("types.zig"); /// REALLY BAD CODE, PLEASE DON'T USE THIS!!!!!!! (only for testing) pub fn getFunctionByName(tree: *ast.Tree, name: []const u8) ?*ast.Node.FnProto { @@ -907,7 +908,6 @@ pub fn getImportStr(tree: *ast.Tree, source_index: usize) ?[]const u8 { return null; } -const types = @import("types.zig"); pub const SourceRange = std.zig.Token.Loc; pub const PositionContext = union(enum) { @@ -1040,3 +1040,91 @@ pub fn documentPositionContext(allocator: *std.mem.Allocator, document: types.Te break :block .empty; }; } + +fn addOutlineNodes(allocator: *std.mem.Allocator, children: *std.ArrayList(types.DocumentSymbol), tree: *ast.Tree, child: *ast.Node) anyerror!void { + switch (child.id) { + .StringLiteral, .IntegerLiteral, .BuiltinCall, .Call, .Identifier, .InfixOp, + .PrefixOp, .SuffixOp, .ControlFlowExpression, .ArrayInitializerDot, .SwitchElse, + .SwitchCase, .For, .EnumLiteral, .PointerIndexPayload , .StructInitializerDot, + .PointerPayload, .While, .Switch, .Else, .BoolLiteral, .NullLiteral, .Defer, + .StructInitializer, .FieldInitializer, .If, .FnProto => return, + + .ContainerDecl => { + const decl = child.cast(ast.Node.ContainerDecl).?; + + for (decl.fieldsAndDecls()) |cchild| + try addOutlineNodes(allocator, children, tree, cchild); + // _ = try children.append(try getDocumentSymbolsInternal(allocator, tree, cchild)); + return; + }, + .Block => { + // const block = child.cast(ast.Node.Block).?; + + // for (block.statements()) |cchild| + // try addOutlineNodes(allocator, children, tree, cchild); + // _ = try children.append(try getDocumentSymbolsInternal(allocator, tree, cchild)); + return; + }, + else => {} + } + std.debug.warn("{}\n", .{child.id}); + _ = try children.append(try getDocumentSymbolsInternal(allocator, tree, child)); +} + +fn getDocumentSymbolsInternal(allocator: *std.mem.Allocator, tree: *ast.Tree, node: *ast.Node) anyerror!types.DocumentSymbol { + // const symbols = std.ArrayList(types.DocumentSymbol).init(allocator); + + const start_loc = tree.tokenLocation(0, node.firstToken()); + const end_loc = tree.tokenLocation(0, node.lastToken()); + const range = types.Range{ + .start = .{ + .line = @intCast(i64, start_loc.line), + .character = @intCast(i64, start_loc.column), + }, + .end = .{ + .line = @intCast(i64, end_loc.line), + .character = @intCast(i64, end_loc.column), + } + }; + + if (getDeclName(tree, node) == null) { + std.debug.warn("NULL NAME: {}\n", .{node.id}); + } + + // TODO: Get my lazy bum to fix detail newlines + return types.DocumentSymbol{ + .name = getDeclName(tree, node) orelse "no_name", + // .detail = (try getDocComments(allocator, tree, node)) orelse "", + .detail = "", + .kind = switch (node.id) { + .FnProto => .Function, + .VarDecl => .Variable, + .ContainerField => .Field, + else => .Variable + }, + .range = range, + .selectionRange = range, + .children = ch: { + var children = std.ArrayList(types.DocumentSymbol).init(allocator); + + var index: usize = 0; + while (node.iterate(index)) |child| : (index += 1) { + try addOutlineNodes(allocator, &children, tree, child); + } + + break :ch children.items; + }, + }; + + // return symbols.items; +} + +pub fn getDocumentSymbols(allocator: *std.mem.Allocator, tree: *ast.Tree) ![]types.DocumentSymbol { + var symbols = std.ArrayList(types.DocumentSymbol).init(allocator); + + for (tree.root_node.decls()) |node| { + _ = try symbols.append(try getDocumentSymbolsInternal(allocator, tree, node)); + } + + return symbols.items; +} diff --git a/src/main.zig b/src/main.zig index ce88b63..1d4d45e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -19,7 +19,7 @@ var document_store: DocumentStore = undefined; var workspace_folder_configs: std.StringHashMap(?Config) = undefined; const initialize_response = - \\,"result":{"capabilities":{"signatureHelpProvider":{"triggerCharacters":["(",","]},"textDocumentSync":1,"completionProvider":{"resolveProvider":false,"triggerCharacters":[".",":","@"]},"documentHighlightProvider":false,"hoverProvider":true,"codeActionProvider":false,"declarationProvider":true,"definitionProvider":true,"typeDefinitionProvider":true,"implementationProvider":false,"referencesProvider":false,"documentSymbolProvider":false,"colorProvider":false,"documentFormattingProvider":false,"documentRangeFormattingProvider":false,"foldingRangeProvider":false,"selectionRangeProvider":false,"workspaceSymbolProvider":false,"workspace":{"workspaceFolders":{"supported":true,"changeNotifications":true}}}}} + \\,"result":{"capabilities":{"signatureHelpProvider":{"triggerCharacters":["(",","]},"textDocumentSync":1,"completionProvider":{"resolveProvider":false,"triggerCharacters":[".",":","@"]},"documentHighlightProvider":false,"hoverProvider":true,"codeActionProvider":false,"declarationProvider":true,"definitionProvider":true,"typeDefinitionProvider":true,"implementationProvider":false,"referencesProvider":false,"documentSymbolProvider":true,"colorProvider":false,"documentFormattingProvider":false,"documentRangeFormattingProvider":false,"foldingRangeProvider":false,"selectionRangeProvider":false,"workspaceSymbolProvider":false,"workspace":{"workspaceFolders":{"supported":true,"changeNotifications":true}}}}} ; const not_implemented_response = @@ -44,14 +44,15 @@ const no_completions_response = /// Sends a request or response fn send(reqOrRes: var) !void { - // The most memory we'll probably need - var mem_buffer: [1024 * 128]u8 = undefined; - var fbs = std.io.fixedBufferStream(&mem_buffer); - try std.json.stringify(reqOrRes, std.json.StringifyOptions{}, fbs.outStream()); + var arena = std.heap.ArenaAllocator.init(allocator); + defer arena.deinit(); + + var arr = std.ArrayList(u8).init(&arena.allocator); + try std.json.stringify(reqOrRes, std.json.StringifyOptions{}, arr.outStream()); const stdout_stream = stdout.outStream(); - try stdout_stream.print("Content-Length: {}\r\n\r\n", .{fbs.pos}); - try stdout_stream.writeAll(fbs.getWritten()); + try stdout_stream.print("Content-Length: {}\r\n\r\n", .{arr.items.len}); + try stdout_stream.writeAll(arr.items); try stdout.flush(); } @@ -526,6 +527,18 @@ fn completeFieldAccess(id: i64, handle: *DocumentStore.Handle, position: types.P }); } +fn documentSymbol(id: i64, handle: *DocumentStore.Handle) !void { + var arena = std.heap.ArenaAllocator.init(allocator); + defer arena.deinit(); + + try send(types.Response{ + .id = .{ .Integer = id }, + .result = .{ + .DocumentSymbols = try analysis.getDocumentSymbols(&arena.allocator, handle.tree) + }, + }); +} + // Compute builtin completions at comptime. const builtin_completions = block: { @setEvalBranchQuota(3_500); @@ -834,6 +847,16 @@ fn processJsonRpc(parser: *std.json.Parser, json: []const u8, config: Config) !v } else { try respondGeneric(id, null_result_response); } + } else if (std.mem.eql(u8, method, "textDocument/documentSymbol")) { + const document = params.getValue("textDocument").?.Object; + const uri = document.getValue("uri").?.String; + + const handle = document_store.getHandle(uri) orelse { + std.debug.warn("Trying to got to definition in non existent document {}", .{uri}); + return try respondGeneric(id, null_result_response); + }; + + try documentSymbol(id, handle); } else if (root.Object.getValue("id")) |_| { std.debug.warn("Method with return value not implemented: {}", .{method}); try respondGeneric(id, not_implemented_response); diff --git a/src/types.zig b/src/types.zig index e47d923..0ed9471 100644 --- a/src/types.zig +++ b/src/types.zig @@ -62,6 +62,7 @@ pub const ResponseParams = union(enum) { CompletionList: CompletionList, Location: Location, Hover: Hover, + DocumentSymbols: []DocumentSymbol }; /// JSONRPC error @@ -292,3 +293,50 @@ pub const CompletionItem = struct { documentation: ?MarkupContent = null // filterText: String = .NotDefined, }; + +const SymbolKind = enum { + File = 1, + Module = 2, + Namespace = 3, + Package = 4, + Class = 5, + Method = 6, + Property = 7, + Field = 8, + Constructor = 9, + Enum = 10, + Interface = 11, + Function = 12, + Variable = 13, + Constant = 14, + String = 15, + Number = 16, + Boolean = 17, + Array = 18, + Object = 19, + Key = 20, + Null = 21, + EnumMember = 22, + Struct = 23, + Event = 24, + Operator = 25, + TypeParameter = 26, + + pub fn jsonStringify( + value: SymbolKind, + options: json.StringifyOptions, + out_stream: var, + ) !void { + try json.stringify(@enumToInt(value), options, out_stream); + } +}; + +pub const DocumentSymbol = struct { + name: []const u8, + detail: ?[]const u8 = null, + kind: SymbolKind, + deprecated: bool = false, + range: Range, + selectionRange: Range, + children: []DocumentSymbol = &[_]DocumentSymbol{} +};