Merge pull request #484 from gpanders/documentHighlight

Implement textDocument/documentHighlight
This commit is contained in:
Auguste Rame 2022-07-23 05:30:57 -04:00 committed by GitHub
commit 39e4a561b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 129 additions and 39 deletions

View File

@ -1135,6 +1135,7 @@ fn referencesDefinitionGlobal(
handle: *DocumentStore.Handle, handle: *DocumentStore.Handle,
pos_index: usize, pos_index: usize,
include_decl: bool, include_decl: bool,
comptime highlight: bool,
) !void { ) !void {
const tracy_zone = tracy.trace(@src()); const tracy_zone = tracy.trace(@src());
defer tracy_zone.end(); defer tracy_zone.end();
@ -1150,10 +1151,23 @@ fn referencesDefinitionGlobal(
&locs, &locs,
std.ArrayList(types.Location).append, std.ArrayList(types.Location).append,
server.config.skip_std_references, server.config.skip_std_references,
!highlight,
); );
const result: types.ResponseParams = if (highlight) result: {
var highlights = try std.ArrayList(types.DocumentHighlight).initCapacity(arena.allocator(), locs.items.len);
for (locs.items) |loc| {
highlights.appendAssumeCapacity(.{
.range = loc.range,
.kind = .Text,
});
}
break :result .{ .DocumentHighlight = highlights.items };
} else .{ .Locations = locs.items };
try send(arena, types.Response{ try send(arena, types.Response{
.id = id, .id = id,
.result = .{ .Locations = locs.items }, .result = result,
}); });
} }
@ -1165,16 +1179,37 @@ fn referencesDefinitionFieldAccess(
position: offsets.DocumentPosition, position: offsets.DocumentPosition,
range: analysis.SourceRange, range: analysis.SourceRange,
include_decl: bool, include_decl: bool,
comptime highlight: bool,
) !void { ) !void {
const tracy_zone = tracy.trace(@src()); const tracy_zone = tracy.trace(@src());
defer tracy_zone.end(); defer tracy_zone.end();
const decl = (try server.getSymbolFieldAccess(handle, arena, position, range)) orelse return try respondGeneric(id, null_result_response); const decl = (try server.getSymbolFieldAccess(handle, arena, position, range)) orelse return try respondGeneric(id, null_result_response);
var locs = std.ArrayList(types.Location).init(arena.allocator()); var locs = std.ArrayList(types.Location).init(arena.allocator());
try references.symbolReferences(arena, &server.document_store, decl, server.offset_encoding, include_decl, &locs, std.ArrayList(types.Location).append, server.config.skip_std_references); try references.symbolReferences(
arena,
&server.document_store,
decl,
server.offset_encoding,
include_decl,
&locs,
std.ArrayList(types.Location).append,
server.config.skip_std_references,
!highlight,
);
const result: types.ResponseParams = if (highlight) result: {
var highlights = try std.ArrayList(types.DocumentHighlight).initCapacity(arena.allocator(), locs.items.len);
for (locs.items) |loc| {
highlights.appendAssumeCapacity(.{
.range = loc.range,
.kind = .Text,
});
}
break :result .{ .DocumentHighlight = highlights.items };
} else .{ .Locations = locs.items };
try send(arena, types.Response{ try send(arena, types.Response{
.id = id, .id = id,
.result = .{ .Locations = locs.items }, .result = result,
}); });
} }
@ -1185,6 +1220,7 @@ fn referencesDefinitionLabel(
handle: *DocumentStore.Handle, handle: *DocumentStore.Handle,
pos_index: usize, pos_index: usize,
include_decl: bool, include_decl: bool,
comptime highlight: bool,
) !void { ) !void {
const tracy_zone = tracy.trace(@src()); const tracy_zone = tracy.trace(@src());
defer tracy_zone.end(); defer tracy_zone.end();
@ -1192,9 +1228,19 @@ fn referencesDefinitionLabel(
const decl = (try getLabelGlobal(pos_index, handle)) orelse return try respondGeneric(id, null_result_response); const decl = (try getLabelGlobal(pos_index, handle)) orelse return try respondGeneric(id, null_result_response);
var locs = std.ArrayList(types.Location).init(arena.allocator()); var locs = std.ArrayList(types.Location).init(arena.allocator());
try references.labelReferences(arena, decl, server.offset_encoding, include_decl, &locs, std.ArrayList(types.Location).append); try references.labelReferences(arena, decl, server.offset_encoding, include_decl, &locs, std.ArrayList(types.Location).append);
const result: types.ResponseParams = if (highlight) result: {
var highlights = try std.ArrayList(types.DocumentHighlight).initCapacity(arena.allocator(), locs.items.len);
for (locs.items) |loc| {
highlights.appendAssumeCapacity(.{
.range = loc.range,
.kind = .Text,
});
}
break :result .{ .DocumentHighlight = highlights.items };
} else .{ .Locations = locs.items };
try send(arena, types.Response{ try send(arena, types.Response{
.id = id, .id = id,
.result = .{ .Locations = locs.items }, .result = result,
}); });
} }
@ -1747,7 +1793,7 @@ fn initializeHandler(server: *Server, arena: *std.heap.ArenaAllocator, id: types
.textDocumentSync = .Full, .textDocumentSync = .Full,
.renameProvider = true, .renameProvider = true,
.completionProvider = .{ .resolveProvider = false, .triggerCharacters = &[_][]const u8{ ".", ":", "@" }, .completionItem = .{ .labelDetailsSupport = true } }, .completionProvider = .{ .resolveProvider = false, .triggerCharacters = &[_][]const u8{ ".", ":", "@" }, .completionItem = .{ .labelDetailsSupport = true } },
.documentHighlightProvider = false, .documentHighlightProvider = true,
.hoverProvider = true, .hoverProvider = true,
.codeActionProvider = false, .codeActionProvider = false,
.declarationProvider = true, .declarationProvider = true,
@ -2268,9 +2314,33 @@ fn referencesHandler(server: *Server, arena: *std.heap.ArenaAllocator, id: types
const include_decl = req.params.context.includeDeclaration; const include_decl = req.params.context.includeDeclaration;
switch (pos_context) { switch (pos_context) {
.var_access => try server.referencesDefinitionGlobal(arena, id, handle, doc_position.absolute_index, include_decl), .var_access => try server.referencesDefinitionGlobal(arena, id, handle, doc_position.absolute_index, include_decl, false),
.field_access => |range| try server.referencesDefinitionFieldAccess(arena, id, handle, doc_position, range, include_decl), .field_access => |range| try server.referencesDefinitionFieldAccess(arena, id, handle, doc_position, range, include_decl, false),
.label => try server.referencesDefinitionLabel(arena, id, handle, doc_position.absolute_index, include_decl), .label => try server.referencesDefinitionLabel(arena, id, handle, doc_position.absolute_index, include_decl, false),
else => try respondGeneric(id, null_result_response),
}
} else {
try respondGeneric(id, null_result_response);
}
}
fn documentHighlightHandler(server: *Server, arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requests.DocumentHighlight) !void {
const tracy_zone = tracy.trace(@src());
defer tracy_zone.end();
const handle = server.document_store.getHandle(req.params.textDocument.uri) orelse {
logger.warn("Trying to highlight references in non existent document {s}", .{req.params.textDocument.uri});
return try respondGeneric(id, null_result_response);
};
if (req.params.position.character >= 0) {
const doc_position = try offsets.documentPosition(handle.document, req.params.position, server.offset_encoding);
const pos_context = try analysis.documentPositionContext(arena, handle.document, doc_position);
switch (pos_context) {
.var_access => try server.referencesDefinitionGlobal(arena, id, handle, doc_position.absolute_index, true, true),
.field_access => |range| try server.referencesDefinitionFieldAccess(arena, id, handle, doc_position, range, true, true),
.label => try server.referencesDefinitionLabel(arena, id, handle, doc_position.absolute_index, true, true),
else => try respondGeneric(id, null_result_response), else => try respondGeneric(id, null_result_response),
} }
} else { } else {
@ -2375,6 +2445,7 @@ fn processJsonRpc(server: *Server, arena: *std.heap.ArenaAllocator, parser: *std
.{ "textDocument/formatting", requests.Formatting, formattingHandler }, .{ "textDocument/formatting", requests.Formatting, formattingHandler },
.{ "textDocument/rename", requests.Rename, renameHandler }, .{ "textDocument/rename", requests.Rename, renameHandler },
.{ "textDocument/references", requests.References, referencesHandler }, .{ "textDocument/references", requests.References, referencesHandler },
.{ "textDocument/documentHighlight", requests.DocumentHighlight, documentHighlightHandler },
.{ "workspace/didChangeConfiguration", std.json.Value, didChangeConfigurationHandler }, .{ "workspace/didChangeConfiguration", std.json.Value, didChangeConfigurationHandler },
}; };
@ -2414,7 +2485,6 @@ fn processJsonRpc(server: *Server, arena: *std.heap.ArenaAllocator, parser: *std
// needs a response) or false if the method is a notification (in which // needs a response) or false if the method is a notification (in which
// case it should be silently ignored) // case it should be silently ignored)
const unimplemented_map = std.ComptimeStringMap(bool, .{ const unimplemented_map = std.ComptimeStringMap(bool, .{
.{ "textDocument/documentHighlight", true },
.{ "textDocument/codeAction", true }, .{ "textDocument/codeAction", true },
.{ "textDocument/codeLens", true }, .{ "textDocument/codeLens", true },
.{ "textDocument/documentLink", true }, .{ "textDocument/documentLink", true },

View File

@ -492,7 +492,7 @@ fn symbolReferencesInternal(arena: *std.heap.ArenaAllocator, store: *DocumentSto
} }
} }
pub fn symbolReferences(arena: *std.heap.ArenaAllocator, store: *DocumentStore, decl_handle: analysis.DeclWithHandle, encoding: offsets.Encoding, include_decl: bool, context: anytype, comptime handler: anytype, skip_std_references: bool) !void { pub fn symbolReferences(arena: *std.heap.ArenaAllocator, store: *DocumentStore, decl_handle: analysis.DeclWithHandle, encoding: offsets.Encoding, include_decl: bool, context: anytype, comptime handler: anytype, skip_std_references: bool, workspace: bool) !void {
std.debug.assert(decl_handle.decl.* != .label_decl); std.debug.assert(decl_handle.decl.* != .label_decl);
const curr_handle = decl_handle.handle; const curr_handle = decl_handle.handle;
if (include_decl) { if (include_decl) {
@ -503,41 +503,43 @@ pub fn symbolReferences(arena: *std.heap.ArenaAllocator, store: *DocumentStore,
.ast_node => { .ast_node => {
try symbolReferencesInternal(arena, store, .{ .node = 0, .handle = curr_handle }, decl_handle, encoding, context, handler); try symbolReferencesInternal(arena, store, .{ .node = 0, .handle = curr_handle }, decl_handle, encoding, context, handler);
var imports = std.ArrayList(*DocumentStore.Handle).init(arena.allocator()); if (workspace) {
var imports = std.ArrayList(*DocumentStore.Handle).init(arena.allocator());
var handle_it = store.handles.iterator(); var handle_it = store.handles.iterator();
while (handle_it.next()) |entry| { while (handle_it.next()) |entry| {
if (skip_std_references and std.mem.indexOf(u8, entry.key_ptr.*, "std") != null) { if (skip_std_references and std.mem.indexOf(u8, entry.key_ptr.*, "std") != null) {
if (!include_decl or entry.value_ptr.* != curr_handle) if (!include_decl or entry.value_ptr.* != curr_handle)
continue; continue;
} }
// Check entry's transitive imports // Check entry's transitive imports
try imports.append(entry.value_ptr.*); try imports.append(entry.value_ptr.*);
var i: usize = 0; var i: usize = 0;
blk: while (i < imports.items.len) : (i += 1) { blk: while (i < imports.items.len) : (i += 1) {
const import = imports.items[i]; const import = imports.items[i];
for (import.imports_used.items) |uri| { for (import.imports_used.items) |uri| {
const h = store.getHandle(uri) orelse break; const h = store.getHandle(uri) orelse break;
if (h == curr_handle) { if (h == curr_handle) {
// entry does import curr_handle // entry does import curr_handle
try symbolReferencesInternal(arena, store, .{ .node = 0, .handle = entry.value_ptr.* }, decl_handle, encoding, context, handler); try symbolReferencesInternal(arena, store, .{ .node = 0, .handle = entry.value_ptr.* }, decl_handle, encoding, context, handler);
break :blk; break :blk;
} }
select: { select: {
for (imports.items) |item| { for (imports.items) |item| {
if (item == h) { if (item == h) {
// already checked this import // already checked this import
break :select; break :select;
} }
}
try imports.append(h);
} }
try imports.append(h);
} }
} }
try imports.resize(0);
} }
try imports.resize(0);
} }
}, },
.param_decl => |param| { .param_decl => |param| {

View File

@ -31,7 +31,7 @@ pub fn renameSymbol(arena: *std.heap.ArenaAllocator, store: *DocumentStore, decl
.edits = edits, .edits = edits,
.allocator = arena.allocator(), .allocator = arena.allocator(),
.new_name = new_name, .new_name = new_name,
}, refHandler, true); }, refHandler, true, true);
} }
pub fn renameLabel(arena: *std.heap.ArenaAllocator, decl_handle: analysis.DeclWithHandle, new_name: []const u8, edits: *std.StringHashMap([]types.TextEdit), encoding: offsets.Encoding) !void { pub fn renameLabel(arena: *std.heap.ArenaAllocator, decl_handle: analysis.DeclWithHandle, new_name: []const u8, edits: *std.StringHashMap([]types.TextEdit), encoding: offsets.Encoding) !void {

View File

@ -165,6 +165,7 @@ pub const Initialize = struct {
documentationFormat: MaybeStringArray, documentationFormat: MaybeStringArray,
}, },
}, },
documentHighlight: Exists,
}, },
offsetEncoding: MaybeStringArray, offsetEncoding: MaybeStringArray,
}; };
@ -244,6 +245,7 @@ pub const GotoDeclaration = TextDocumentIdentifierPositionRequest;
pub const Hover = TextDocumentIdentifierPositionRequest; pub const Hover = TextDocumentIdentifierPositionRequest;
pub const DocumentSymbols = TextDocumentIdentifierRequest; pub const DocumentSymbols = TextDocumentIdentifierRequest;
pub const Formatting = TextDocumentIdentifierRequest; pub const Formatting = TextDocumentIdentifierRequest;
pub const DocumentHighlight = TextDocumentIdentifierPositionRequest;
pub const Rename = struct { pub const Rename = struct {
params: struct { params: struct {
textDocument: TextDocumentIdentifier, textDocument: TextDocumentIdentifier,

View File

@ -45,6 +45,7 @@ pub const ResponseParams = union(enum) {
InitializeResult: InitializeResult, InitializeResult: InitializeResult,
ConfigurationParams: ConfigurationParams, ConfigurationParams: ConfigurationParams,
RegistrationParams: RegistrationParams, RegistrationParams: RegistrationParams,
DocumentHighlight: []DocumentHighlight,
}; };
/// JSONRPC notifications /// JSONRPC notifications
@ -412,3 +413,18 @@ pub const RegistrationParams = struct {
// registerOptions?: LSPAny; // registerOptions?: LSPAny;
}; };
}; };
pub const DocumentHighlightKind = enum(u8) {
Text = 1,
Read = 2,
Write = 3,
pub fn jsonStringify(value: DocumentHighlightKind, options: std.json.StringifyOptions, out_stream: anytype) !void {
try std.json.stringify(@enumToInt(value), options, out_stream);
}
};
pub const DocumentHighlight = struct {
range: Range,
kind: ?DocumentHighlightKind,
};