Added textDocument/foldingRange (#718)

* Added textDocument/foldingRange

* Added support for code regions

* Fixed warning message in foldingRangeHandler

Co-authored-by: LucaSas <sas.luca.alex@gmail.com>
This commit is contained in:
Luca Sas 2022-10-25 15:35:16 +01:00 committed by GitHub
parent 5ddbf24d11
commit 2a17590bf4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 97 additions and 2 deletions

View File

@ -1574,7 +1574,7 @@ fn initializeHandler(server: *Server, writer: anytype, id: types.RequestId, req:
.colorProvider = false, .colorProvider = false,
.documentFormattingProvider = true, .documentFormattingProvider = true,
.documentRangeFormattingProvider = false, .documentRangeFormattingProvider = false,
.foldingRangeProvider = false, .foldingRangeProvider = true,
.selectionRangeProvider = false, .selectionRangeProvider = false,
.workspaceSymbolProvider = false, .workspaceSymbolProvider = false,
.rangeProvider = false, .rangeProvider = false,
@ -2339,6 +2339,89 @@ fn codeActionHandler(server: *Server, writer: anytype, id: types.RequestId, req:
}); });
} }
fn foldingRangeHandler(server: *Server, writer: anytype, id: types.RequestId, req: requests.FoldingRange) !void
{
const Tag = std.zig.Token.Tag;
const allocator = server.arena.allocator();
const handle = server.document_store.getHandle(req.params.textDocument.uri) orelse {
log.warn("Trying to get folding ranges of non existent document {s}", .{req.params.textDocument.uri});
return try respondGeneric(writer, id, null_result_response);
};
// Used to store the result
var ranges = std.ArrayList(types.FoldingRange).init(allocator);
// We add opened curly braces to a stack as we go and pop one off when we find a closing brace.
// As an optimization we start with a capacity of 10 which should work well in most cases since
// people will almost never have more than 10 levels deep of nested braces.
var stack = try std.ArrayList(usize).initCapacity(allocator, 10);
// Iterate over the token tags and look for pairs of braces
for (handle.tree.tokens.items(.tag)) |tag, i| {
const token_index = @intCast(Ast.TokenIndex, i);
// If we found a `{` we add it to our stack
if (tag == Tag.l_brace) {
const line = handle.tree.tokenLocation(0, token_index).line;
try stack.append(line);
}
// If we found a close `}` we have a matching pair
if (tag == Tag.r_brace and stack.items.len > 0) {
const start_line = stack.pop();
const end_line = handle.tree.tokenLocation(0, token_index).line;
// Add brace pairs but discard those from the same line, no need to waste memory on them
if (start_line != end_line)
{
try ranges.append(.{
.startLine = start_line,
.endLine = end_line,
});
}
}
}
// Iterate over the source code and look for code regions with #region #endregion
{
// We will reuse the stack
stack.clearRetainingCapacity();
var i: usize = 0;
var lines_count: usize = 0;
while (i < handle.tree.source.len) : (i += 1) {
const slice = handle.tree.source[i..];
if (slice[0] == '\n') {
lines_count += 1;
}
if (std.mem.startsWith(u8, slice, "//#region")) {
try stack.append(lines_count);
}
if (std.mem.startsWith(u8, slice, "//#endregion") and stack.items.len > 0) {
const start_line = stack.pop();
const end_line = lines_count;
// Add brace pairs but discard those from the same line, no need to waste memory on them
if (start_line != end_line)
{
try ranges.append(.{
.startLine = start_line,
.endLine = end_line,
});
}
}
}
}
try send(writer, allocator, types.Response {
.id = id,
.result = .{ .FoldingRange = ranges.items },
});
}
// Needed for the hack seen below. // Needed for the hack seen below.
fn extractErr(val: anytype) anyerror { fn extractErr(val: anytype) anyerror {
val catch |e| return e; val catch |e| return e;
@ -2475,6 +2558,7 @@ pub fn processJsonRpc(server: *Server, writer: anytype, json: []const u8) !void
.{ "textDocument/documentHighlight", requests.DocumentHighlight, documentHighlightHandler }, .{ "textDocument/documentHighlight", requests.DocumentHighlight, documentHighlightHandler },
.{ "textDocument/codeAction", requests.CodeAction, codeActionHandler }, .{ "textDocument/codeAction", requests.CodeAction, codeActionHandler },
.{ "workspace/didChangeConfiguration", Config.DidChangeConfigurationParams, didChangeConfigurationHandler }, .{ "workspace/didChangeConfiguration", Config.DidChangeConfigurationParams, didChangeConfigurationHandler },
.{ "textDocument/foldingRange", requests.FoldingRange, foldingRangeHandler }
}; };
if (zig_builtin.zig_backend == .stage1) { if (zig_builtin.zig_backend == .stage1) {
@ -2539,7 +2623,6 @@ pub fn processJsonRpc(server: *Server, writer: anytype, json: []const u8) !void
.{ "textDocument/rangeFormatting", true }, .{ "textDocument/rangeFormatting", true },
.{ "textDocument/onTypeFormatting", true }, .{ "textDocument/onTypeFormatting", true },
.{ "textDocument/prepareRename", true }, .{ "textDocument/prepareRename", true },
.{ "textDocument/foldingRange", true },
.{ "textDocument/selectionRange", true }, .{ "textDocument/selectionRange", true },
.{ "textDocument/semanticTokens/range", true }, .{ "textDocument/semanticTokens/range", true },
.{ "workspace/didChangeWorkspaceFolders", false }, .{ "workspace/didChangeWorkspaceFolders", false },

View File

@ -288,3 +288,9 @@ pub const CodeAction = struct {
}, },
}, },
}; };
pub const FoldingRange = struct {
params: struct {
textDocument: TextDocumentIdentifier,
},
};

View File

@ -43,6 +43,7 @@ pub const ResponseParams = union(enum) {
DocumentHighlight: []DocumentHighlight, DocumentHighlight: []DocumentHighlight,
CodeAction: []CodeAction, CodeAction: []CodeAction,
ApplyEdit: ApplyWorkspaceEditParams, ApplyEdit: ApplyWorkspaceEditParams,
FoldingRange: []FoldingRange,
}; };
pub const Response = struct { pub const Response = struct {
@ -520,3 +521,8 @@ pub const DocumentHighlight = struct {
range: Range, range: Range,
kind: ?DocumentHighlightKind, kind: ?DocumentHighlightKind,
}; };
pub const FoldingRange = struct {
startLine: usize,
endLine: usize,
};