References improvements (#653)
This commit is contained in:
		
							parent
							
								
									e28549fa7d
								
							
						
					
					
						commit
						b76710ea5f
					
				
							
								
								
									
										366
									
								
								src/Server.zig
									
									
									
									
									
								
							
							
						
						
									
										366
									
								
								src/Server.zig
									
									
									
									
									
								
							@ -9,7 +9,6 @@ const types = @import("types.zig");
 | 
			
		||||
const analysis = @import("analysis.zig");
 | 
			
		||||
const ast = @import("ast.zig");
 | 
			
		||||
const references = @import("references.zig");
 | 
			
		||||
const rename = @import("rename.zig");
 | 
			
		||||
const offsets = @import("offsets.zig");
 | 
			
		||||
const semantic_tokens = @import("semantic_tokens.zig");
 | 
			
		||||
const inlay_hints = @import("inlay_hints.zig");
 | 
			
		||||
@ -986,206 +985,6 @@ fn gotoDefinitionString(
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn renameDefinitionGlobal(
 | 
			
		||||
    server: *Server,
 | 
			
		||||
    writer: anytype,
 | 
			
		||||
    id: types.RequestId,
 | 
			
		||||
    handle: *DocumentStore.Handle,
 | 
			
		||||
    pos_index: usize,
 | 
			
		||||
    new_name: []const u8,
 | 
			
		||||
) !void {
 | 
			
		||||
    const tracy_zone = tracy.trace(@src());
 | 
			
		||||
    defer tracy_zone.end();
 | 
			
		||||
 | 
			
		||||
    const decl = (try server.getSymbolGlobal(pos_index, handle)) orelse return try respondGeneric(writer, id, null_result_response);
 | 
			
		||||
 | 
			
		||||
    var workspace_edit = types.WorkspaceEdit{ .changes = .{} };
 | 
			
		||||
    try rename.renameSymbol(&server.arena, &server.document_store, decl, new_name, &workspace_edit.changes, server.offset_encoding);
 | 
			
		||||
 | 
			
		||||
    try send(writer, server.arena.allocator(), types.Response{
 | 
			
		||||
        .id = id,
 | 
			
		||||
        .result = .{ .WorkspaceEdit = workspace_edit },
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn renameDefinitionFieldAccess(
 | 
			
		||||
    server: *Server,
 | 
			
		||||
    writer: anytype,
 | 
			
		||||
    id: types.RequestId,
 | 
			
		||||
    handle: *DocumentStore.Handle,
 | 
			
		||||
    source_index: usize,
 | 
			
		||||
    loc: offsets.Loc,
 | 
			
		||||
    new_name: []const u8,
 | 
			
		||||
) !void {
 | 
			
		||||
    const tracy_zone = tracy.trace(@src());
 | 
			
		||||
    defer tracy_zone.end();
 | 
			
		||||
 | 
			
		||||
    const decl = (try server.getSymbolFieldAccess(handle, source_index, loc)) orelse return try respondGeneric(writer, id, null_result_response);
 | 
			
		||||
 | 
			
		||||
    var workspace_edit = types.WorkspaceEdit{ .changes = .{} };
 | 
			
		||||
    try rename.renameSymbol(&server.arena, &server.document_store, decl, new_name, &workspace_edit.changes, server.offset_encoding);
 | 
			
		||||
 | 
			
		||||
    try send(writer, server.arena.allocator(), types.Response{
 | 
			
		||||
        .id = id,
 | 
			
		||||
        .result = .{ .WorkspaceEdit = workspace_edit },
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn renameDefinitionLabel(
 | 
			
		||||
    server: *Server,
 | 
			
		||||
    writer: anytype,
 | 
			
		||||
    id: types.RequestId,
 | 
			
		||||
    handle: *DocumentStore.Handle,
 | 
			
		||||
    pos_index: usize,
 | 
			
		||||
    new_name: []const u8,
 | 
			
		||||
) !void {
 | 
			
		||||
    const tracy_zone = tracy.trace(@src());
 | 
			
		||||
    defer tracy_zone.end();
 | 
			
		||||
 | 
			
		||||
    const decl = (try getLabelGlobal(pos_index, handle)) orelse return try respondGeneric(writer, id, null_result_response);
 | 
			
		||||
 | 
			
		||||
    var workspace_edit = types.WorkspaceEdit{ .changes = .{} };
 | 
			
		||||
    try rename.renameLabel(&server.arena, decl, new_name, &workspace_edit.changes, server.offset_encoding);
 | 
			
		||||
 | 
			
		||||
    try send(writer, server.arena.allocator(), types.Response{
 | 
			
		||||
        .id = id,
 | 
			
		||||
        .result = .{ .WorkspaceEdit = workspace_edit },
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn referencesDefinitionGlobal(
 | 
			
		||||
    server: *Server,
 | 
			
		||||
    writer: anytype,
 | 
			
		||||
    id: types.RequestId,
 | 
			
		||||
    handle: *DocumentStore.Handle,
 | 
			
		||||
    pos_index: usize,
 | 
			
		||||
    include_decl: bool,
 | 
			
		||||
    comptime highlight: bool,
 | 
			
		||||
) !void {
 | 
			
		||||
    const tracy_zone = tracy.trace(@src());
 | 
			
		||||
    defer tracy_zone.end();
 | 
			
		||||
 | 
			
		||||
    const decl = (try server.getSymbolGlobal(pos_index, handle)) orelse return try respondGeneric(writer, id, null_result_response);
 | 
			
		||||
    var locs = std.ArrayList(types.Location).init(server.arena.allocator());
 | 
			
		||||
    try references.symbolReferences(
 | 
			
		||||
        &server.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 = std.ArrayListUnmanaged(types.DocumentHighlight){};
 | 
			
		||||
        try highlights.ensureTotalCapacity(server.arena.allocator(), locs.items.len);
 | 
			
		||||
        const uri = handle.uri();
 | 
			
		||||
        for (locs.items) |loc| {
 | 
			
		||||
            if (std.mem.eql(u8, loc.uri, uri)) {
 | 
			
		||||
                highlights.appendAssumeCapacity(.{
 | 
			
		||||
                    .range = loc.range,
 | 
			
		||||
                    .kind = .Text,
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        break :result .{ .DocumentHighlight = highlights.items };
 | 
			
		||||
    } else .{ .Locations = locs.items };
 | 
			
		||||
 | 
			
		||||
    try send(writer, server.arena.allocator(), types.Response{
 | 
			
		||||
        .id = id,
 | 
			
		||||
        .result = result,
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn referencesDefinitionFieldAccess(
 | 
			
		||||
    server: *Server,
 | 
			
		||||
    writer: anytype,
 | 
			
		||||
    id: types.RequestId,
 | 
			
		||||
    handle: *DocumentStore.Handle,
 | 
			
		||||
    source_index: usize,
 | 
			
		||||
    loc: offsets.Loc,
 | 
			
		||||
    include_decl: bool,
 | 
			
		||||
    comptime highlight: bool,
 | 
			
		||||
) !void {
 | 
			
		||||
    const tracy_zone = tracy.trace(@src());
 | 
			
		||||
    defer tracy_zone.end();
 | 
			
		||||
 | 
			
		||||
    var allocator = server.arena.allocator();
 | 
			
		||||
 | 
			
		||||
    const decl = (try server.getSymbolFieldAccess(handle, source_index, loc)) orelse return try respondGeneric(writer, id, null_result_response);
 | 
			
		||||
    var locations = std.ArrayList(types.Location).init(allocator);
 | 
			
		||||
    try references.symbolReferences(
 | 
			
		||||
        &server.arena,
 | 
			
		||||
        &server.document_store,
 | 
			
		||||
        decl,
 | 
			
		||||
        server.offset_encoding,
 | 
			
		||||
        include_decl,
 | 
			
		||||
        &locations,
 | 
			
		||||
        std.ArrayList(types.Location).append,
 | 
			
		||||
        server.config.skip_std_references,
 | 
			
		||||
        !highlight,
 | 
			
		||||
    );
 | 
			
		||||
    const result: types.ResponseParams = if (highlight) result: {
 | 
			
		||||
        var highlights = std.ArrayListUnmanaged(types.DocumentHighlight){};
 | 
			
		||||
        try highlights.ensureTotalCapacity(allocator, locations.items.len);
 | 
			
		||||
        const uri = handle.uri();
 | 
			
		||||
        for (locations.items) |location| {
 | 
			
		||||
            if (std.mem.eql(u8, location.uri, uri)) {
 | 
			
		||||
                highlights.appendAssumeCapacity(.{
 | 
			
		||||
                    .range = location.range,
 | 
			
		||||
                    .kind = .Text,
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        break :result .{ .DocumentHighlight = highlights.items };
 | 
			
		||||
    } else .{ .Locations = locations.items };
 | 
			
		||||
    try send(writer, allocator, types.Response{
 | 
			
		||||
        .id = id,
 | 
			
		||||
        .result = result,
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn referencesDefinitionLabel(
 | 
			
		||||
    server: *Server,
 | 
			
		||||
    writer: anytype,
 | 
			
		||||
    id: types.RequestId,
 | 
			
		||||
    handle: *DocumentStore.Handle,
 | 
			
		||||
    pos_index: usize,
 | 
			
		||||
    include_decl: bool,
 | 
			
		||||
    comptime highlight: bool,
 | 
			
		||||
) !void {
 | 
			
		||||
    const tracy_zone = tracy.trace(@src());
 | 
			
		||||
    defer tracy_zone.end();
 | 
			
		||||
 | 
			
		||||
    var allocator = server.arena.allocator();
 | 
			
		||||
 | 
			
		||||
    const decl = (try getLabelGlobal(pos_index, handle)) orelse return try respondGeneric(writer, id, null_result_response);
 | 
			
		||||
    var locs = std.ArrayList(types.Location).init(allocator);
 | 
			
		||||
    try references.labelReferences(&server.arena, decl, server.offset_encoding, include_decl, &locs, std.ArrayList(types.Location).append);
 | 
			
		||||
    const result: types.ResponseParams = if (highlight) result: {
 | 
			
		||||
        var highlights = std.ArrayListUnmanaged(types.DocumentHighlight){};
 | 
			
		||||
        try highlights.ensureTotalCapacity(allocator, locs.items.len);
 | 
			
		||||
        const uri = handle.uri();
 | 
			
		||||
        for (locs.items) |loc| {
 | 
			
		||||
            if (std.mem.eql(u8, loc.uri, uri)) {
 | 
			
		||||
                highlights.appendAssumeCapacity(.{
 | 
			
		||||
                    .range = loc.range,
 | 
			
		||||
                    .kind = .Text,
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        break :result .{ .DocumentHighlight = highlights.items };
 | 
			
		||||
    } else .{ .Locations = locs.items };
 | 
			
		||||
 | 
			
		||||
    try send(writer, allocator, types.Response{
 | 
			
		||||
        .id = id,
 | 
			
		||||
        .result = result,
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn hasComment(tree: Ast.Tree, start_token: Ast.TokenIndex, end_token: Ast.TokenIndex) bool {
 | 
			
		||||
    const tracy_zone = tracy.trace(@src());
 | 
			
		||||
    defer tracy_zone.end();
 | 
			
		||||
@ -2223,29 +2022,6 @@ fn formattingHandler(server: *Server, writer: anytype, id: types.RequestId, req:
 | 
			
		||||
    return try respondGeneric(writer, id, null_result_response);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn renameHandler(server: *Server, writer: anytype, id: types.RequestId, req: requests.Rename) !void {
 | 
			
		||||
    const tracy_zone = tracy.trace(@src());
 | 
			
		||||
    defer tracy_zone.end();
 | 
			
		||||
 | 
			
		||||
    const handle = server.document_store.getHandle(req.params.textDocument.uri) orelse {
 | 
			
		||||
        log.warn("Trying to rename in non existent document {s}", .{req.params.textDocument.uri});
 | 
			
		||||
        return try respondGeneric(writer, id, null_result_response);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (req.params.position.character >= 0) {
 | 
			
		||||
        const source_index = offsets.positionToIndex(handle.document.text, req.params.position, server.offset_encoding);
 | 
			
		||||
        const pos_context = try analysis.getPositionContext(server.arena.allocator(), handle.document, source_index);
 | 
			
		||||
        switch (pos_context) {
 | 
			
		||||
            .var_access => try server.renameDefinitionGlobal(writer, id, handle, source_index, req.params.newName),
 | 
			
		||||
            .field_access => |loc| try server.renameDefinitionFieldAccess(writer, id, handle, source_index, loc, req.params.newName),
 | 
			
		||||
            .label => try server.renameDefinitionLabel(writer, id, handle, source_index, req.params.newName),
 | 
			
		||||
            else => try respondGeneric(writer, id, null_result_response),
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        try respondGeneric(writer, id, null_result_response);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn didChangeConfigurationHandler(server: *Server, writer: anytype, id: types.RequestId, maybe_req: std.json.Value) !void {
 | 
			
		||||
    const tracy_zone = tracy.trace(@src());
 | 
			
		||||
    defer tracy_zone.end();
 | 
			
		||||
@ -2265,53 +2041,129 @@ fn didChangeConfigurationHandler(server: *Server, writer: anytype, id: types.Req
 | 
			
		||||
        try server.requestConfiguration(writer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn renameHandler(server: *Server, writer: anytype, id: types.RequestId, req: requests.Rename) !void {
 | 
			
		||||
    const tracy_zone = tracy.trace(@src());
 | 
			
		||||
    defer tracy_zone.end();
 | 
			
		||||
 | 
			
		||||
    try generalReferencesHandler(server, writer, id, .{ .rename = req });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn referencesHandler(server: *Server, writer: anytype, id: types.RequestId, req: requests.References) !void {
 | 
			
		||||
    const tracy_zone = tracy.trace(@src());
 | 
			
		||||
    defer tracy_zone.end();
 | 
			
		||||
 | 
			
		||||
    const handle = server.document_store.getHandle(req.params.textDocument.uri) orelse {
 | 
			
		||||
        log.warn("Trying to get references in non existent document {s}", .{req.params.textDocument.uri});
 | 
			
		||||
        return try respondGeneric(writer, id, null_result_response);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (req.params.position.character >= 0) {
 | 
			
		||||
        const source_index = offsets.positionToIndex(handle.document.text, req.params.position, server.offset_encoding);
 | 
			
		||||
        const pos_context = try analysis.getPositionContext(server.arena.allocator(), handle.document, source_index);
 | 
			
		||||
 | 
			
		||||
        const include_decl = req.params.context.includeDeclaration;
 | 
			
		||||
        switch (pos_context) {
 | 
			
		||||
            .var_access => try server.referencesDefinitionGlobal(writer, id, handle, source_index, include_decl, false),
 | 
			
		||||
            .field_access => |loc| try server.referencesDefinitionFieldAccess(writer, id, handle, source_index, loc, include_decl, false),
 | 
			
		||||
            .label => try server.referencesDefinitionLabel(writer, id, handle, source_index, include_decl, false),
 | 
			
		||||
            else => try respondGeneric(writer, id, null_result_response),
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        try respondGeneric(writer, id, null_result_response);
 | 
			
		||||
    }
 | 
			
		||||
    try generalReferencesHandler(server, writer, id, .{ .references = req });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn documentHighlightHandler(server: *Server, writer: anytype, 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 {
 | 
			
		||||
        log.warn("Trying to highlight references in non existent document {s}", .{req.params.textDocument.uri});
 | 
			
		||||
    try generalReferencesHandler(server, writer, id, .{ .highlight = req });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const GeneralReferencesRequest = union(enum) {
 | 
			
		||||
    rename: requests.Rename,
 | 
			
		||||
    references: requests.References,
 | 
			
		||||
    highlight: requests.DocumentHighlight,
 | 
			
		||||
 | 
			
		||||
    pub fn uri(self: @This()) []const u8 {
 | 
			
		||||
        return switch (self) {
 | 
			
		||||
            .rename => |rename| rename.params.textDocument.uri,
 | 
			
		||||
            .references => |ref| ref.params.textDocument.uri,
 | 
			
		||||
            .highlight => |highlight| highlight.params.textDocument.uri,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn position(self: @This()) types.Position {
 | 
			
		||||
        return switch (self) {
 | 
			
		||||
            .rename => |rename| rename.params.position,
 | 
			
		||||
            .references => |ref| ref.params.position,
 | 
			
		||||
            .highlight => |highlight| highlight.params.position,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn name(self: @This()) []const u8 {
 | 
			
		||||
        return switch (self) {
 | 
			
		||||
            .rename => "rename",
 | 
			
		||||
            .references => "references",
 | 
			
		||||
            .highlight => "highlight references",
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
fn generalReferencesHandler(server: *Server, writer: anytype, id: types.RequestId, req: GeneralReferencesRequest) !void {
 | 
			
		||||
    const tracy_zone = tracy.trace(@src());
 | 
			
		||||
    defer tracy_zone.end();
 | 
			
		||||
 | 
			
		||||
    const allocator = server.arena.allocator();
 | 
			
		||||
 | 
			
		||||
    const handle = server.document_store.getHandle(req.uri()) orelse {
 | 
			
		||||
        log.warn("Trying to get {s} in non existent document {s}", .{ req.name(), req.uri() });
 | 
			
		||||
        return try respondGeneric(writer, id, null_result_response);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (req.params.position.character >= 0) {
 | 
			
		||||
        const source_index = offsets.positionToIndex(handle.document.text, req.params.position, server.offset_encoding);
 | 
			
		||||
        const pos_context = try analysis.getPositionContext(server.arena.allocator(), handle.document, source_index);
 | 
			
		||||
    if (req.position().character <= 0) return try respondGeneric(writer, id, null_result_response);
 | 
			
		||||
 | 
			
		||||
        switch (pos_context) {
 | 
			
		||||
            .var_access => try server.referencesDefinitionGlobal(writer, id, handle, source_index, true, true),
 | 
			
		||||
            .field_access => |loc| try server.referencesDefinitionFieldAccess(writer, id, handle, source_index, loc, true, true),
 | 
			
		||||
            .label => try server.referencesDefinitionLabel(writer, id, handle, source_index, true, true),
 | 
			
		||||
            else => try respondGeneric(writer, id, null_result_response),
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        try respondGeneric(writer, id, null_result_response);
 | 
			
		||||
    }
 | 
			
		||||
    const source_index = offsets.positionToIndex(handle.document.text, req.position(), server.offset_encoding);
 | 
			
		||||
    const pos_context = try analysis.getPositionContext(server.arena.allocator(), handle.document, source_index);
 | 
			
		||||
 | 
			
		||||
    const decl = switch (pos_context) {
 | 
			
		||||
        .var_access => try server.getSymbolGlobal(source_index, handle),
 | 
			
		||||
        .field_access => |range| try server.getSymbolFieldAccess(handle, source_index, range),
 | 
			
		||||
        .label => try getLabelGlobal(source_index, handle),
 | 
			
		||||
        else => null,
 | 
			
		||||
    } orelse return try respondGeneric(writer, id, null_result_response);
 | 
			
		||||
 | 
			
		||||
    const include_decl = switch (req) {
 | 
			
		||||
        .references => |ref| ref.params.context.includeDeclaration,
 | 
			
		||||
        else => true,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const locations = if (pos_context == .label)
 | 
			
		||||
        try references.labelReferences(allocator, decl, server.offset_encoding, include_decl)
 | 
			
		||||
    else
 | 
			
		||||
        try references.symbolReferences(
 | 
			
		||||
            &server.arena,
 | 
			
		||||
            &server.document_store,
 | 
			
		||||
            decl,
 | 
			
		||||
            server.offset_encoding,
 | 
			
		||||
            include_decl,
 | 
			
		||||
            server.config.skip_std_references,
 | 
			
		||||
            req != .highlight, // scan the entire workspace except for highlight
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
    const result: types.ResponseParams = switch (req) {
 | 
			
		||||
        .rename => |rename| blk: {
 | 
			
		||||
            var edits: types.WorkspaceEdit = .{ .changes = .{} };
 | 
			
		||||
            for (locations.items) |loc| {
 | 
			
		||||
                const gop = try edits.changes.getOrPutValue(allocator, loc.uri, .{});
 | 
			
		||||
                try gop.value_ptr.append(allocator, .{
 | 
			
		||||
                    .range = loc.range,
 | 
			
		||||
                    .newText = rename.params.newName,
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            break :blk .{ .WorkspaceEdit = edits };
 | 
			
		||||
        },
 | 
			
		||||
        .references => .{ .Locations = locations.items },
 | 
			
		||||
        .highlight => blk: {
 | 
			
		||||
            var highlights = try std.ArrayListUnmanaged(types.DocumentHighlight).initCapacity(allocator, locations.items.len);
 | 
			
		||||
            const uri = handle.uri();
 | 
			
		||||
            for (locations.items) |loc| {
 | 
			
		||||
                if (!std.mem.eql(u8, loc.uri, uri)) continue;
 | 
			
		||||
                highlights.appendAssumeCapacity(.{
 | 
			
		||||
                    .range = loc.range,
 | 
			
		||||
                    .kind = .Text,
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            break :blk .{ .DocumentHighlight = highlights.items };
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    try send(writer, allocator, types.Response{
 | 
			
		||||
        .id = id,
 | 
			
		||||
        .result = result,
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn isPositionBefore(lhs: types.Position, rhs: types.Position) bool {
 | 
			
		||||
 | 
			
		||||
@ -7,16 +7,12 @@ const offsets = @import("offsets.zig");
 | 
			
		||||
const log = std.log.scoped(.references);
 | 
			
		||||
const ast = @import("ast.zig");
 | 
			
		||||
 | 
			
		||||
fn tokenReference(handle: *DocumentStore.Handle, tok: Ast.TokenIndex, encoding: offsets.Encoding, context: anytype, comptime handler: anytype) !void {
 | 
			
		||||
    try handler(context, types.Location{
 | 
			
		||||
        .uri = handle.uri(),
 | 
			
		||||
        .range = offsets.tokenToRange(handle.tree, tok, encoding),
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn labelReferences(arena: *std.heap.ArenaAllocator, decl: analysis.DeclWithHandle, encoding: offsets.Encoding, include_decl: bool, context: anytype, comptime handler: anytype) !void {
 | 
			
		||||
    _ = arena;
 | 
			
		||||
 | 
			
		||||
pub fn labelReferences(
 | 
			
		||||
    allocator: std.mem.Allocator,
 | 
			
		||||
    decl: analysis.DeclWithHandle,
 | 
			
		||||
    encoding: offsets.Encoding,
 | 
			
		||||
    include_decl: bool,
 | 
			
		||||
) error{OutOfMemory}!std.ArrayListUnmanaged(types.Location) {
 | 
			
		||||
    std.debug.assert(decl.decl.* == .label_decl);
 | 
			
		||||
    const handle = decl.handle;
 | 
			
		||||
    const tree = handle.tree;
 | 
			
		||||
@ -25,43 +21,88 @@ pub fn labelReferences(arena: *std.heap.ArenaAllocator, decl: analysis.DeclWithH
 | 
			
		||||
    // Find while / for / block from label -> iterate over children nodes, find break and continues, change their labels if they match.
 | 
			
		||||
    // This case can be implemented just by scanning tokens.
 | 
			
		||||
    const first_tok = tree.firstToken(decl.decl.label_decl);
 | 
			
		||||
    const last_tok = tree.firstToken(decl.decl.label_decl);
 | 
			
		||||
    const last_tok = ast.lastToken(tree, decl.decl.label_decl);
 | 
			
		||||
 | 
			
		||||
    var locations = std.ArrayListUnmanaged(types.Location){};
 | 
			
		||||
    errdefer locations.deinit(allocator);
 | 
			
		||||
 | 
			
		||||
    if (include_decl) {
 | 
			
		||||
        // The first token is always going to be the label
 | 
			
		||||
        try tokenReference(handle, first_tok, encoding, context, handler);
 | 
			
		||||
        try locations.append(allocator, .{
 | 
			
		||||
            .uri = handle.uri(),
 | 
			
		||||
            .range = offsets.tokenToRange(handle.tree, first_tok, encoding),
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var curr_tok = first_tok + 1;
 | 
			
		||||
    while (curr_tok < last_tok - 2) : (curr_tok += 1) {
 | 
			
		||||
        const curr_id = token_tags[curr_tok];
 | 
			
		||||
        if ((curr_id == .keyword_break or curr_id == .keyword_continue) and token_tags[curr_tok + 1] == .colon and
 | 
			
		||||
            token_tags[curr_tok + 2] == .identifier)
 | 
			
		||||
        {
 | 
			
		||||
            if (std.mem.eql(u8, tree.tokenSlice(curr_tok + 2), tree.tokenSlice(first_tok))) {
 | 
			
		||||
                try tokenReference(handle, first_tok, encoding, context, handler);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (curr_id != .keyword_break and curr_id != .keyword_continue) continue;
 | 
			
		||||
        if (token_tags[curr_tok + 1] != .colon) continue;
 | 
			
		||||
        if (token_tags[curr_tok + 2] != .identifier) continue;
 | 
			
		||||
 | 
			
		||||
        if (!std.mem.eql(u8, tree.tokenSlice(curr_tok + 2), tree.tokenSlice(first_tok))) continue;
 | 
			
		||||
 | 
			
		||||
        try locations.append(allocator, .{
 | 
			
		||||
            .uri = handle.uri(),
 | 
			
		||||
            .range = offsets.tokenToRange(handle.tree, curr_tok + 2, encoding),
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return locations;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn symbolReferencesInternal(arena: *std.heap.ArenaAllocator, store: *DocumentStore, node_handle: analysis.NodeWithHandle, decl: analysis.DeclWithHandle, encoding: offsets.Encoding, context: anytype, comptime handler: anytype) error{OutOfMemory}!void {
 | 
			
		||||
    const node = node_handle.node;
 | 
			
		||||
    const handle = node_handle.handle;
 | 
			
		||||
const Builder = struct {
 | 
			
		||||
    arena: *std.heap.ArenaAllocator,
 | 
			
		||||
    locations: std.ArrayListUnmanaged(types.Location),
 | 
			
		||||
    store: *DocumentStore,
 | 
			
		||||
    decl: analysis.DeclWithHandle,
 | 
			
		||||
    encoding: offsets.Encoding,
 | 
			
		||||
 | 
			
		||||
    pub fn init(arena: *std.heap.ArenaAllocator, store: *DocumentStore, decl: analysis.DeclWithHandle, encoding: offsets.Encoding) Builder {
 | 
			
		||||
        return Builder{
 | 
			
		||||
            .arena = arena,
 | 
			
		||||
            .locations = .{},
 | 
			
		||||
            .store = store,
 | 
			
		||||
            .decl = decl,
 | 
			
		||||
            .encoding = encoding,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn add(self: *Builder, handle: *DocumentStore.Handle, token_index: Ast.TokenIndex) !void {
 | 
			
		||||
        try self.locations.append(self.arena.allocator(), .{
 | 
			
		||||
            .uri = handle.uri(),
 | 
			
		||||
            .range = offsets.tokenToRange(handle.tree, token_index, self.encoding),
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
fn symbolReferencesInternal(
 | 
			
		||||
    builder: *Builder,
 | 
			
		||||
    node: Ast.Node.Index,
 | 
			
		||||
    handle: *DocumentStore.Handle,
 | 
			
		||||
) error{OutOfMemory}!void {
 | 
			
		||||
    const tree = handle.tree;
 | 
			
		||||
    if (node > tree.nodes.len) return;
 | 
			
		||||
 | 
			
		||||
    if (node == 0 or node > tree.nodes.len) return;
 | 
			
		||||
 | 
			
		||||
    const node_tags = tree.nodes.items(.tag);
 | 
			
		||||
    const datas = tree.nodes.items(.data);
 | 
			
		||||
    const main_tokens = tree.nodes.items(.main_token);
 | 
			
		||||
    const starts = tree.tokens.items(.start);
 | 
			
		||||
 | 
			
		||||
    switch (node_tags[node]) {
 | 
			
		||||
        .block, .block_semicolon, .block_two, .block_two_semicolon => {
 | 
			
		||||
        .block,
 | 
			
		||||
        .block_semicolon,
 | 
			
		||||
        .block_two,
 | 
			
		||||
        .block_two_semicolon,
 | 
			
		||||
        => {
 | 
			
		||||
            var buffer: [2]Ast.Node.Index = undefined;
 | 
			
		||||
            const statements = ast.blockStatements(tree, node, &buffer).?;
 | 
			
		||||
 | 
			
		||||
            for (statements) |stmt|
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = stmt, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
                try symbolReferencesInternal(builder, stmt, handle);
 | 
			
		||||
        },
 | 
			
		||||
        .container_decl,
 | 
			
		||||
        .container_decl_trailing,
 | 
			
		||||
@ -80,7 +121,7 @@ fn symbolReferencesInternal(arena: *std.heap.ArenaAllocator, store: *DocumentSto
 | 
			
		||||
        => {
 | 
			
		||||
            var buf: [2]Ast.Node.Index = undefined;
 | 
			
		||||
            for (ast.declMembers(tree, node, &buf)) |member|
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = member, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
                try symbolReferencesInternal(builder, member, handle);
 | 
			
		||||
        },
 | 
			
		||||
        .global_var_decl,
 | 
			
		||||
        .local_var_decl,
 | 
			
		||||
@ -88,34 +129,20 @@ fn symbolReferencesInternal(arena: *std.heap.ArenaAllocator, store: *DocumentSto
 | 
			
		||||
        .aligned_var_decl,
 | 
			
		||||
        => {
 | 
			
		||||
            const var_decl = ast.varDecl(tree, node).?;
 | 
			
		||||
            if (var_decl.ast.type_node != 0) {
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = var_decl.ast.type_node, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            }
 | 
			
		||||
            if (var_decl.ast.init_node != 0) {
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = var_decl.ast.init_node, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        .@"usingnamespace" => {
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            try symbolReferencesInternal(builder, var_decl.ast.type_node, handle);
 | 
			
		||||
            try symbolReferencesInternal(builder, var_decl.ast.init_node, handle);
 | 
			
		||||
        },
 | 
			
		||||
        .container_field,
 | 
			
		||||
        .container_field_align,
 | 
			
		||||
        .container_field_init,
 | 
			
		||||
        => {
 | 
			
		||||
            const field = ast.containerField(tree, node).?;
 | 
			
		||||
            if (field.ast.type_expr != 0) {
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = field.ast.type_expr, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            }
 | 
			
		||||
            if (field.ast.value_expr != 0) {
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = field.ast.value_expr, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            }
 | 
			
		||||
            try symbolReferencesInternal(builder, field.ast.type_expr, handle);
 | 
			
		||||
            try symbolReferencesInternal(builder, field.ast.value_expr, handle);
 | 
			
		||||
        },
 | 
			
		||||
        .identifier => {
 | 
			
		||||
            if (try analysis.lookupSymbolGlobal(store, arena, handle, offsets.nodeToSlice(tree, node), starts[main_tokens[node]])) |child| {
 | 
			
		||||
                if (std.meta.eql(decl, child)) {
 | 
			
		||||
                    try tokenReference(handle, main_tokens[node], encoding, context, handler);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            const child = (try analysis.lookupSymbolGlobal(builder.store, builder.arena, handle, tree.getNodeSource(node), starts[main_tokens[node]])) orelse return;
 | 
			
		||||
            if (std.meta.eql(builder.decl, child)) try builder.add(handle, main_tokens[node]);
 | 
			
		||||
        },
 | 
			
		||||
        .fn_proto,
 | 
			
		||||
        .fn_proto_multi,
 | 
			
		||||
@ -127,62 +154,39 @@ fn symbolReferencesInternal(arena: *std.heap.ArenaAllocator, store: *DocumentSto
 | 
			
		||||
            const fn_proto = ast.fnProto(tree, node, &buf).?;
 | 
			
		||||
            var it = fn_proto.iterate(&tree);
 | 
			
		||||
            while (ast.nextFnParam(&it)) |param| {
 | 
			
		||||
                if (param.type_expr != 0)
 | 
			
		||||
                    try symbolReferencesInternal(arena, store, .{ .node = param.type_expr, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
                try symbolReferencesInternal(builder, param.type_expr, handle);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (fn_proto.ast.return_type != 0) {
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = fn_proto.ast.return_type, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            }
 | 
			
		||||
            if (fn_proto.ast.align_expr != 0) {
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = fn_proto.ast.align_expr, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            }
 | 
			
		||||
            if (fn_proto.ast.section_expr != 0) {
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = fn_proto.ast.section_expr, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            }
 | 
			
		||||
            if (fn_proto.ast.callconv_expr != 0) {
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = fn_proto.ast.callconv_expr, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            }
 | 
			
		||||
            try symbolReferencesInternal(builder, fn_proto.ast.return_type, handle);
 | 
			
		||||
            try symbolReferencesInternal(builder, fn_proto.ast.align_expr, handle);
 | 
			
		||||
            try symbolReferencesInternal(builder, fn_proto.ast.section_expr, handle);
 | 
			
		||||
            try symbolReferencesInternal(builder, fn_proto.ast.callconv_expr, handle);
 | 
			
		||||
            if (node_tags[node] == .fn_decl) {
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
                try symbolReferencesInternal(builder, datas[node].rhs, handle);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        .anyframe_type => {
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
        },
 | 
			
		||||
        .@"defer" => {
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
        },
 | 
			
		||||
        .@"comptime" => {
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
        },
 | 
			
		||||
        .@"nosuspend" => {
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
        },
 | 
			
		||||
        .@"switch",
 | 
			
		||||
        .switch_comma,
 | 
			
		||||
        => {
 | 
			
		||||
            // TODO When renaming a union(enum) field, also rename switch items that refer to it.
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            try symbolReferencesInternal(builder, datas[node].lhs, handle);
 | 
			
		||||
            const extra = tree.extraData(datas[node].rhs, Ast.Node.SubRange);
 | 
			
		||||
            const cases = tree.extra_data[extra.start..extra.end];
 | 
			
		||||
            for (cases) |case| {
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = case, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
                try symbolReferencesInternal(builder, case, handle);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        .switch_case_one => {
 | 
			
		||||
            const case_one = tree.switchCaseOne(node);
 | 
			
		||||
            if (case_one.ast.target_expr != 0)
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = case_one.ast.target_expr, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            try symbolReferencesInternal(builder, case_one.ast.target_expr, handle);
 | 
			
		||||
            for (case_one.ast.values) |val|
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = val, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
                try symbolReferencesInternal(builder, val, handle);
 | 
			
		||||
        },
 | 
			
		||||
        .switch_case => {
 | 
			
		||||
            const case = tree.switchCase(node);
 | 
			
		||||
            if (case.ast.target_expr != 0)
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = case.ast.target_expr, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            try symbolReferencesInternal(builder, case.ast.target_expr, handle);
 | 
			
		||||
            for (case.ast.values) |val|
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = val, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
                try symbolReferencesInternal(builder, val, handle);
 | 
			
		||||
        },
 | 
			
		||||
        .@"while",
 | 
			
		||||
        .while_simple,
 | 
			
		||||
@ -191,31 +195,17 @@ fn symbolReferencesInternal(arena: *std.heap.ArenaAllocator, store: *DocumentSto
 | 
			
		||||
        .@"for",
 | 
			
		||||
        => {
 | 
			
		||||
            const loop = ast.whileAst(tree, node).?;
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = loop.ast.cond_expr, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            if (loop.ast.cont_expr != 0) {
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = loop.ast.cont_expr, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            }
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = loop.ast.then_expr, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            if (loop.ast.else_expr != 0) {
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = loop.ast.else_expr, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            }
 | 
			
		||||
            try symbolReferencesInternal(builder, loop.ast.cond_expr, handle);
 | 
			
		||||
            try symbolReferencesInternal(builder, loop.ast.then_expr, handle);
 | 
			
		||||
            try symbolReferencesInternal(builder, loop.ast.else_expr, handle);
 | 
			
		||||
        },
 | 
			
		||||
        .@"if",
 | 
			
		||||
        .if_simple,
 | 
			
		||||
        => {
 | 
			
		||||
            const if_node = ast.ifFull(tree, node);
 | 
			
		||||
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = if_node.ast.cond_expr, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = if_node.ast.then_expr, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            if (if_node.ast.else_expr != 0) {
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = if_node.ast.else_expr, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        .array_type,
 | 
			
		||||
        .array_type_sentinel,
 | 
			
		||||
        => {
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            try symbolReferencesInternal(builder, if_node.ast.cond_expr, handle);
 | 
			
		||||
            try symbolReferencesInternal(builder, if_node.ast.then_expr, handle);
 | 
			
		||||
            try symbolReferencesInternal(builder, if_node.ast.else_expr, handle);
 | 
			
		||||
        },
 | 
			
		||||
        .ptr_type,
 | 
			
		||||
        .ptr_type_aligned,
 | 
			
		||||
@ -225,26 +215,15 @@ fn symbolReferencesInternal(arena: *std.heap.ArenaAllocator, store: *DocumentSto
 | 
			
		||||
            const ptr_type = ast.ptrType(tree, node).?;
 | 
			
		||||
 | 
			
		||||
            if (ptr_type.ast.align_node != 0) {
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = ptr_type.ast.align_node, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
                try symbolReferencesInternal(builder, ptr_type.ast.align_node, handle);
 | 
			
		||||
                if (node_tags[node] == .ptr_type_bit_range) {
 | 
			
		||||
                    try symbolReferencesInternal(arena, store, .{
 | 
			
		||||
                        .node = ptr_type.ast.bit_range_start,
 | 
			
		||||
                        .handle = handle,
 | 
			
		||||
                    }, decl, encoding, context, handler);
 | 
			
		||||
                    try symbolReferencesInternal(arena, store, .{
 | 
			
		||||
                        .node = ptr_type.ast.bit_range_end,
 | 
			
		||||
                        .handle = handle,
 | 
			
		||||
                    }, decl, encoding, context, handler);
 | 
			
		||||
                    try symbolReferencesInternal(builder, ptr_type.ast.bit_range_start, handle);
 | 
			
		||||
                    try symbolReferencesInternal(builder, ptr_type.ast.bit_range_end, handle);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (ptr_type.ast.sentinel != 0) {
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = ptr_type.ast.sentinel, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = ptr_type.ast.child_type, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
        },
 | 
			
		||||
        .address_of, .@"await", .bit_not, .bool_not, .optional_type, .negation, .negation_wrap, .@"resume", .@"try" => {
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            try symbolReferencesInternal(builder, ptr_type.ast.sentinel, handle);
 | 
			
		||||
            try symbolReferencesInternal(builder, ptr_type.ast.child_type, handle);
 | 
			
		||||
        },
 | 
			
		||||
        .array_init,
 | 
			
		||||
        .array_init_comma,
 | 
			
		||||
@ -254,19 +233,18 @@ fn symbolReferencesInternal(arena: *std.heap.ArenaAllocator, store: *DocumentSto
 | 
			
		||||
        .array_init_one_comma,
 | 
			
		||||
        .array_init_dot_two,
 | 
			
		||||
        .array_init_dot_two_comma,
 | 
			
		||||
        => |n| {
 | 
			
		||||
        => |tag| {
 | 
			
		||||
            var buf: [2]Ast.Node.Index = undefined;
 | 
			
		||||
            const array_init = switch (n) {
 | 
			
		||||
            const array_init = switch (tag) {
 | 
			
		||||
                .array_init, .array_init_comma => tree.arrayInit(node),
 | 
			
		||||
                .array_init_dot, .array_init_dot_comma => tree.arrayInitDot(node),
 | 
			
		||||
                .array_init_one, .array_init_one_comma => tree.arrayInitOne(buf[0..1], node),
 | 
			
		||||
                .array_init_dot_two, .array_init_dot_two_comma => tree.arrayInitDotTwo(&buf, node),
 | 
			
		||||
                else => unreachable,
 | 
			
		||||
            };
 | 
			
		||||
            if (array_init.ast.type_expr != 0)
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = array_init.ast.type_expr, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            try symbolReferencesInternal(builder, array_init.ast.type_expr, handle);
 | 
			
		||||
            for (array_init.ast.elements) |e|
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = e, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
                try symbolReferencesInternal(builder, e, handle);
 | 
			
		||||
        },
 | 
			
		||||
        .struct_init,
 | 
			
		||||
        .struct_init_comma,
 | 
			
		||||
@ -276,19 +254,18 @@ fn symbolReferencesInternal(arena: *std.heap.ArenaAllocator, store: *DocumentSto
 | 
			
		||||
        .struct_init_dot_two_comma,
 | 
			
		||||
        .struct_init_one,
 | 
			
		||||
        .struct_init_one_comma,
 | 
			
		||||
        => |n| {
 | 
			
		||||
        => |tag| {
 | 
			
		||||
            var buf: [2]Ast.Node.Index = undefined;
 | 
			
		||||
            const struct_init: Ast.full.StructInit = switch (n) {
 | 
			
		||||
            const struct_init: Ast.full.StructInit = switch (tag) {
 | 
			
		||||
                .struct_init, .struct_init_comma => tree.structInit(node),
 | 
			
		||||
                .struct_init_dot, .struct_init_dot_comma => tree.structInitDot(node),
 | 
			
		||||
                .struct_init_one, .struct_init_one_comma => tree.structInitOne(buf[0..1], node),
 | 
			
		||||
                .struct_init_dot_two, .struct_init_dot_two_comma => tree.structInitDotTwo(&buf, node),
 | 
			
		||||
                else => unreachable,
 | 
			
		||||
            };
 | 
			
		||||
            if (struct_init.ast.type_expr != 0)
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = struct_init.ast.type_expr, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            try symbolReferencesInternal(builder, struct_init.ast.type_expr, handle);
 | 
			
		||||
            for (struct_init.ast.fields) |field|
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = field, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
                try symbolReferencesInternal(builder, field, handle);
 | 
			
		||||
        },
 | 
			
		||||
        .call,
 | 
			
		||||
        .call_comma,
 | 
			
		||||
@ -302,55 +279,27 @@ fn symbolReferencesInternal(arena: *std.heap.ArenaAllocator, store: *DocumentSto
 | 
			
		||||
            var buf: [1]Ast.Node.Index = undefined;
 | 
			
		||||
            const call = ast.callFull(tree, node, &buf).?;
 | 
			
		||||
 | 
			
		||||
            if (call.ast.fn_expr != 0)
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = call.ast.fn_expr, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            try symbolReferencesInternal(builder, call.ast.fn_expr, handle);
 | 
			
		||||
 | 
			
		||||
            for (call.ast.params) |param| {
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = param, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
                try symbolReferencesInternal(builder, param, handle);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        .slice,
 | 
			
		||||
        .slice_sentinel,
 | 
			
		||||
        .slice_open,
 | 
			
		||||
        => |s| {
 | 
			
		||||
            const slice: Ast.full.Slice = switch (s) {
 | 
			
		||||
        => |tag| {
 | 
			
		||||
            const slice: Ast.full.Slice = switch (tag) {
 | 
			
		||||
                .slice => tree.slice(node),
 | 
			
		||||
                .slice_open => tree.sliceOpen(node),
 | 
			
		||||
                .slice_sentinel => tree.sliceSentinel(node),
 | 
			
		||||
                else => unreachable,
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = slice.ast.sliced, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = slice.ast.start, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            if (slice.ast.end != 0)
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = slice.ast.end, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            if (slice.ast.sentinel != 0)
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = slice.ast.sentinel, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
        },
 | 
			
		||||
        .array_access => {
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
        },
 | 
			
		||||
        .deref,
 | 
			
		||||
        .unwrap_optional,
 | 
			
		||||
        => {
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
        },
 | 
			
		||||
        .grouped_expression => {
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
        },
 | 
			
		||||
        .@"return",
 | 
			
		||||
        .@"break",
 | 
			
		||||
        .@"continue",
 | 
			
		||||
        => {
 | 
			
		||||
            if (datas[node].lhs != 0) {
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        .@"suspend" => {
 | 
			
		||||
            if (datas[node].lhs != 0) {
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            }
 | 
			
		||||
            try symbolReferencesInternal(builder, slice.ast.sliced, handle);
 | 
			
		||||
            try symbolReferencesInternal(builder, slice.ast.start, handle);
 | 
			
		||||
            try symbolReferencesInternal(builder, slice.ast.end, handle);
 | 
			
		||||
            try symbolReferencesInternal(builder, slice.ast.sentinel, handle);
 | 
			
		||||
        },
 | 
			
		||||
        .builtin_call,
 | 
			
		||||
        .builtin_call_comma,
 | 
			
		||||
@ -361,33 +310,32 @@ fn symbolReferencesInternal(arena: *std.heap.ArenaAllocator, store: *DocumentSto
 | 
			
		||||
            const params = ast.builtinCallParams(tree, node, &buffer).?;
 | 
			
		||||
 | 
			
		||||
            for (params) |param|
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = param, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
                try symbolReferencesInternal(builder, param, handle);
 | 
			
		||||
        },
 | 
			
		||||
        .@"asm",
 | 
			
		||||
        .asm_simple,
 | 
			
		||||
        => |a| {
 | 
			
		||||
            const _asm: Ast.full.Asm = if (a == .@"asm") tree.asmFull(node) else tree.asmSimple(node);
 | 
			
		||||
            if (_asm.ast.items.len == 0)
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = _asm.ast.template, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
        => |tag| {
 | 
			
		||||
            const full_asm: Ast.full.Asm = if (tag == .@"asm") tree.asmFull(node) else tree.asmSimple(node);
 | 
			
		||||
            if (full_asm.ast.items.len == 0)
 | 
			
		||||
                try symbolReferencesInternal(builder, full_asm.ast.template, handle);
 | 
			
		||||
 | 
			
		||||
            for (_asm.inputs) |input|
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = input, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            for (full_asm.inputs) |input|
 | 
			
		||||
                try symbolReferencesInternal(builder, input, handle);
 | 
			
		||||
 | 
			
		||||
            for (_asm.outputs) |output|
 | 
			
		||||
                try symbolReferencesInternal(arena, store, .{ .node = output, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
        },
 | 
			
		||||
        .test_decl => {
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            for (full_asm.outputs) |output|
 | 
			
		||||
                try symbolReferencesInternal(builder, output, handle);
 | 
			
		||||
        },
 | 
			
		||||
        .asm_output => unreachable,
 | 
			
		||||
        .asm_input => unreachable,
 | 
			
		||||
        .field_access => {
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            try symbolReferencesInternal(builder, datas[node].lhs, handle);
 | 
			
		||||
 | 
			
		||||
            const rhs_str = ast.tokenSlice(tree, datas[node].rhs) catch return;
 | 
			
		||||
            var bound_type_params = analysis.BoundTypeParams{};
 | 
			
		||||
            const left_type = try analysis.resolveFieldAccessLhsType(
 | 
			
		||||
                store,
 | 
			
		||||
                arena,
 | 
			
		||||
                (try analysis.resolveTypeOfNodeInternal(store, arena, .{
 | 
			
		||||
                builder.store,
 | 
			
		||||
                builder.arena,
 | 
			
		||||
                (try analysis.resolveTypeOfNodeInternal(builder.store, builder.arena, .{
 | 
			
		||||
                    .node = datas[node].lhs,
 | 
			
		||||
                    .handle = handle,
 | 
			
		||||
                }, &bound_type_params)) orelse return,
 | 
			
		||||
@ -399,152 +347,198 @@ fn symbolReferencesInternal(arena: *std.heap.ArenaAllocator, store: *DocumentSto
 | 
			
		||||
                else => return,
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            if (try analysis.lookupSymbolContainer(
 | 
			
		||||
                store,
 | 
			
		||||
                arena,
 | 
			
		||||
            const child = (try analysis.lookupSymbolContainer(
 | 
			
		||||
                builder.store,
 | 
			
		||||
                builder.arena,
 | 
			
		||||
                .{ .node = left_type_node, .handle = left_type.handle },
 | 
			
		||||
                rhs_str,
 | 
			
		||||
                !left_type.type.is_type_val,
 | 
			
		||||
            )) |child| {
 | 
			
		||||
                if (std.meta.eql(child, decl)) {
 | 
			
		||||
                    try tokenReference(handle, datas[node].rhs, encoding, context, handler);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            )) orelse return;
 | 
			
		||||
 | 
			
		||||
            if (std.meta.eql(child, builder.decl)) try builder.add(handle, datas[node].rhs);
 | 
			
		||||
        },
 | 
			
		||||
        .add,
 | 
			
		||||
        .add_wrap,
 | 
			
		||||
        .array_cat,
 | 
			
		||||
        .array_mult,
 | 
			
		||||
        .assign,
 | 
			
		||||
        .assign_bit_and,
 | 
			
		||||
        .assign_bit_or,
 | 
			
		||||
        .assign_shl,
 | 
			
		||||
        .assign_shr,
 | 
			
		||||
        .assign_bit_xor,
 | 
			
		||||
        .assign_div,
 | 
			
		||||
        .assign_sub,
 | 
			
		||||
        .assign_sub_wrap,
 | 
			
		||||
        .assign_mod,
 | 
			
		||||
        .assign_add,
 | 
			
		||||
        .assign_add_wrap,
 | 
			
		||||
        .assign_mul,
 | 
			
		||||
        .assign_mul_wrap,
 | 
			
		||||
        .bang_equal,
 | 
			
		||||
        .bit_and,
 | 
			
		||||
        .bit_or,
 | 
			
		||||
        .shl,
 | 
			
		||||
        .shr,
 | 
			
		||||
        .bit_xor,
 | 
			
		||||
        .bool_or,
 | 
			
		||||
        .div,
 | 
			
		||||
        .@"usingnamespace",
 | 
			
		||||
        .unwrap_optional,
 | 
			
		||||
        .bool_not,
 | 
			
		||||
        .negation,
 | 
			
		||||
        .bit_not,
 | 
			
		||||
        .negation_wrap,
 | 
			
		||||
        .address_of,
 | 
			
		||||
        .@"try",
 | 
			
		||||
        .@"await",
 | 
			
		||||
        .optional_type,
 | 
			
		||||
        .deref,
 | 
			
		||||
        .@"suspend",
 | 
			
		||||
        .@"resume",
 | 
			
		||||
        .@"continue",
 | 
			
		||||
        .@"break",
 | 
			
		||||
        .@"return",
 | 
			
		||||
        .grouped_expression,
 | 
			
		||||
        .@"comptime",
 | 
			
		||||
        .@"nosuspend",
 | 
			
		||||
        => try symbolReferencesInternal(builder, datas[node].lhs, handle),
 | 
			
		||||
        .test_decl,
 | 
			
		||||
        .@"errdefer",
 | 
			
		||||
        .@"defer",
 | 
			
		||||
        .anyframe_type,
 | 
			
		||||
        => try symbolReferencesInternal(builder, datas[node].rhs, handle),
 | 
			
		||||
        .equal_equal,
 | 
			
		||||
        .error_union,
 | 
			
		||||
        .greater_or_equal,
 | 
			
		||||
        .bang_equal,
 | 
			
		||||
        .less_than,
 | 
			
		||||
        .greater_than,
 | 
			
		||||
        .less_or_equal,
 | 
			
		||||
        .less_than,
 | 
			
		||||
        .greater_or_equal,
 | 
			
		||||
        .assign_mul,
 | 
			
		||||
        .assign_div,
 | 
			
		||||
        .assign_mod,
 | 
			
		||||
        .assign_add,
 | 
			
		||||
        .assign_sub,
 | 
			
		||||
        .assign_shl,
 | 
			
		||||
        .assign_shl_sat,
 | 
			
		||||
        .assign_shr,
 | 
			
		||||
        .assign_bit_and,
 | 
			
		||||
        .assign_bit_xor,
 | 
			
		||||
        .assign_bit_or,
 | 
			
		||||
        .assign_mul_wrap,
 | 
			
		||||
        .assign_add_wrap,
 | 
			
		||||
        .assign_sub_wrap,
 | 
			
		||||
        .assign_mul_sat,
 | 
			
		||||
        .assign_add_sat,
 | 
			
		||||
        .assign_sub_sat,
 | 
			
		||||
        .assign,
 | 
			
		||||
        .merge_error_sets,
 | 
			
		||||
        .mod,
 | 
			
		||||
        .mul,
 | 
			
		||||
        .div,
 | 
			
		||||
        .mod,
 | 
			
		||||
        .array_mult,
 | 
			
		||||
        .mul_wrap,
 | 
			
		||||
        .switch_range,
 | 
			
		||||
        .mul_sat,
 | 
			
		||||
        .add,
 | 
			
		||||
        .sub,
 | 
			
		||||
        .array_cat,
 | 
			
		||||
        .add_wrap,
 | 
			
		||||
        .sub_wrap,
 | 
			
		||||
        .add_sat,
 | 
			
		||||
        .sub_sat,
 | 
			
		||||
        .shl,
 | 
			
		||||
        .shl_sat,
 | 
			
		||||
        .shr,
 | 
			
		||||
        .bit_and,
 | 
			
		||||
        .bit_xor,
 | 
			
		||||
        .bit_or,
 | 
			
		||||
        .@"orelse",
 | 
			
		||||
        .bool_and,
 | 
			
		||||
        .bool_or,
 | 
			
		||||
        .array_type,
 | 
			
		||||
        .array_type_sentinel,
 | 
			
		||||
        .array_access,
 | 
			
		||||
        .@"catch",
 | 
			
		||||
        .switch_range,
 | 
			
		||||
        .error_union,
 | 
			
		||||
        => {
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
 | 
			
		||||
            try symbolReferencesInternal(builder, datas[node].lhs, handle);
 | 
			
		||||
            try symbolReferencesInternal(builder, datas[node].rhs, handle);
 | 
			
		||||
        },
 | 
			
		||||
        else => {},
 | 
			
		||||
        .anyframe_literal,
 | 
			
		||||
        .char_literal,
 | 
			
		||||
        .number_literal,
 | 
			
		||||
        .unreachable_literal,
 | 
			
		||||
        .enum_literal,
 | 
			
		||||
        .string_literal,
 | 
			
		||||
        .multiline_string_literal,
 | 
			
		||||
        .error_value,
 | 
			
		||||
        => {},
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
pub fn symbolReferences(
 | 
			
		||||
    arena: *std.heap.ArenaAllocator,
 | 
			
		||||
    store: *DocumentStore,
 | 
			
		||||
    decl_handle: analysis.DeclWithHandle,
 | 
			
		||||
    encoding: offsets.Encoding,
 | 
			
		||||
    include_decl: bool,
 | 
			
		||||
    skip_std_references: bool,
 | 
			
		||||
    workspace: bool,
 | 
			
		||||
) error{OutOfMemory}!std.ArrayListUnmanaged(types.Location) {
 | 
			
		||||
    std.debug.assert(decl_handle.decl.* != .label_decl);
 | 
			
		||||
 | 
			
		||||
    var builder = Builder.init(arena, store, decl_handle, encoding);
 | 
			
		||||
 | 
			
		||||
    const curr_handle = decl_handle.handle;
 | 
			
		||||
    if (include_decl) {
 | 
			
		||||
        try tokenReference(curr_handle, decl_handle.nameToken(), encoding, context, handler);
 | 
			
		||||
    }
 | 
			
		||||
    if (include_decl) try builder.add(curr_handle, decl_handle.nameToken());
 | 
			
		||||
 | 
			
		||||
    switch (decl_handle.decl.*) {
 | 
			
		||||
        .ast_node => {
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = 0, .handle = curr_handle }, decl_handle, encoding, context, handler);
 | 
			
		||||
        .pointer_payload,
 | 
			
		||||
        .switch_payload,
 | 
			
		||||
        .array_payload,
 | 
			
		||||
        .array_index,
 | 
			
		||||
        .ast_node,
 | 
			
		||||
        => {
 | 
			
		||||
            try symbolReferencesInternal(&builder, 0, curr_handle);
 | 
			
		||||
 | 
			
		||||
            if (workspace) {
 | 
			
		||||
                var imports = std.ArrayListUnmanaged(*DocumentStore.Handle){};
 | 
			
		||||
            if (decl_handle.decl.* != .ast_node) return builder.locations;
 | 
			
		||||
            if (!workspace) return builder.locations;
 | 
			
		||||
 | 
			
		||||
                var handle_it = store.handles.iterator();
 | 
			
		||||
                while (handle_it.next()) |entry| {
 | 
			
		||||
                    if (skip_std_references and std.mem.indexOf(u8, entry.key_ptr.*, "std") != null) {
 | 
			
		||||
                        if (!include_decl or entry.value_ptr.* != curr_handle)
 | 
			
		||||
                            continue;
 | 
			
		||||
                    }
 | 
			
		||||
            var imports = std.ArrayListUnmanaged(*DocumentStore.Handle){};
 | 
			
		||||
 | 
			
		||||
                    // Check entry's transitive imports
 | 
			
		||||
                    try imports.append(arena.allocator(), entry.value_ptr.*);
 | 
			
		||||
                    var i: usize = 0;
 | 
			
		||||
                    blk: while (i < imports.items.len) : (i += 1) {
 | 
			
		||||
                        const import = imports.items[i];
 | 
			
		||||
                        for (import.imports_used.items) |uri| {
 | 
			
		||||
                            const h = store.getHandle(uri) orelse break;
 | 
			
		||||
            var handle_it = store.handles.iterator();
 | 
			
		||||
            while (handle_it.next()) |entry| {
 | 
			
		||||
                if (skip_std_references and std.mem.indexOf(u8, entry.key_ptr.*, "std") != null) {
 | 
			
		||||
                    if (!include_decl or entry.value_ptr.* != curr_handle)
 | 
			
		||||
                        continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                            if (h == curr_handle) {
 | 
			
		||||
                                // entry does import curr_handle
 | 
			
		||||
                                try symbolReferencesInternal(arena, store, .{ .node = 0, .handle = entry.value_ptr.* }, decl_handle, encoding, context, handler);
 | 
			
		||||
                                break :blk;
 | 
			
		||||
                            }
 | 
			
		||||
                // Check entry's transitive imports
 | 
			
		||||
                try imports.append(arena.allocator(), entry.value_ptr.*);
 | 
			
		||||
                var i: usize = 0;
 | 
			
		||||
                blk: while (i < imports.items.len) : (i += 1) {
 | 
			
		||||
                    const import = imports.items[i];
 | 
			
		||||
                    for (import.imports_used.items) |uri| {
 | 
			
		||||
                        const h = store.getHandle(uri) orelse break;
 | 
			
		||||
 | 
			
		||||
                            select: {
 | 
			
		||||
                                for (imports.items) |item| {
 | 
			
		||||
                                    if (item == h) {
 | 
			
		||||
                                        // already checked this import
 | 
			
		||||
                                        break :select;
 | 
			
		||||
                                    }
 | 
			
		||||
                        if (h == curr_handle) {
 | 
			
		||||
                            // entry does import curr_handle
 | 
			
		||||
                            try symbolReferencesInternal(&builder, 0, entry.value_ptr.*);
 | 
			
		||||
                            break :blk;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        select: {
 | 
			
		||||
                            for (imports.items) |item| {
 | 
			
		||||
                                if (item == h) {
 | 
			
		||||
                                    // already checked this import
 | 
			
		||||
                                    break :select;
 | 
			
		||||
                                }
 | 
			
		||||
                                try imports.append(arena.allocator(), h);
 | 
			
		||||
                            }
 | 
			
		||||
                            try imports.append(arena.allocator(), h);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    try imports.resize(arena.allocator(), 0);
 | 
			
		||||
                }
 | 
			
		||||
                try imports.resize(arena.allocator(), 0);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        .param_decl => |param| {
 | 
			
		||||
            // Rename the param tok.
 | 
			
		||||
            const fn_node: Ast.full.FnProto = loop: for (curr_handle.document_scope.scopes.items) |scope| {
 | 
			
		||||
                switch (scope.data) {
 | 
			
		||||
                    .function => |proto| {
 | 
			
		||||
                        var buf: [1]Ast.Node.Index = undefined;
 | 
			
		||||
                        const fn_proto = ast.fnProto(curr_handle.tree, proto, &buf).?;
 | 
			
		||||
                        var it = fn_proto.iterate(&curr_handle.tree);
 | 
			
		||||
                        while (ast.nextFnParam(&it)) |candidate| {
 | 
			
		||||
                            if (std.meta.eql(candidate, param)) {
 | 
			
		||||
                                if (curr_handle.tree.nodes.items(.tag)[proto] == .fn_decl) {
 | 
			
		||||
                                    try symbolReferencesInternal(
 | 
			
		||||
                                        arena,
 | 
			
		||||
                                        store,
 | 
			
		||||
                                        .{ .node = curr_handle.tree.nodes.items(.data)[proto].rhs, .handle = curr_handle },
 | 
			
		||||
                                        decl_handle,
 | 
			
		||||
                                        encoding,
 | 
			
		||||
                                        context,
 | 
			
		||||
                                        handler,
 | 
			
		||||
                                    );
 | 
			
		||||
                                }
 | 
			
		||||
                                break :loop fn_proto;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    else => {},
 | 
			
		||||
            for (curr_handle.document_scope.scopes.items) |scope| {
 | 
			
		||||
                if (scope.data != .function) continue;
 | 
			
		||||
 | 
			
		||||
                const proto = scope.data.function;
 | 
			
		||||
 | 
			
		||||
                var buf: [1]Ast.Node.Index = undefined;
 | 
			
		||||
                const fn_proto = ast.fnProto(curr_handle.tree, proto, &buf).?;
 | 
			
		||||
 | 
			
		||||
                var it = fn_proto.iterate(&curr_handle.tree);
 | 
			
		||||
                while (ast.nextFnParam(&it)) |candidate| {
 | 
			
		||||
                    if (!std.meta.eql(candidate, param)) continue;
 | 
			
		||||
 | 
			
		||||
                    if (curr_handle.tree.nodes.items(.tag)[proto] != .fn_decl) break;
 | 
			
		||||
                    try symbolReferencesInternal(&builder, curr_handle.tree.nodes.items(.data)[proto].rhs, curr_handle);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                log.warn("Could not find param decl's function", .{});
 | 
			
		||||
                return;
 | 
			
		||||
            };
 | 
			
		||||
            _ = fn_node;
 | 
			
		||||
            }
 | 
			
		||||
            log.warn("Could not find param decl's function", .{});
 | 
			
		||||
        },
 | 
			
		||||
        .pointer_payload, .switch_payload, .array_payload, .array_index => {
 | 
			
		||||
            try symbolReferencesInternal(arena, store, .{ .node = 0, .handle = curr_handle }, decl_handle, encoding, context, handler);
 | 
			
		||||
        },
 | 
			
		||||
        .label_decl => unreachable,
 | 
			
		||||
        .label_decl => unreachable, // handled separately by labelReferences
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return builder.locations;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,52 +0,0 @@
 | 
			
		||||
const std = @import("std");
 | 
			
		||||
const DocumentStore = @import("DocumentStore.zig");
 | 
			
		||||
const analysis = @import("analysis.zig");
 | 
			
		||||
const references = @import("references.zig");
 | 
			
		||||
const types = @import("types.zig");
 | 
			
		||||
const offsets = @import("offsets.zig");
 | 
			
		||||
 | 
			
		||||
// TODO Use a map to array lists and collect at the end instead?
 | 
			
		||||
const RefHandlerContext = struct {
 | 
			
		||||
    allocator: std.mem.Allocator,
 | 
			
		||||
    edits: *std.StringHashMapUnmanaged(std.ArrayListUnmanaged(types.TextEdit)),
 | 
			
		||||
    new_name: []const u8,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
fn refHandler(context: RefHandlerContext, loc: types.Location) !void {
 | 
			
		||||
    const gop = try context.edits.getOrPutValue(context.allocator, loc.uri, .{});
 | 
			
		||||
    try gop.value_ptr.append(context.allocator, .{
 | 
			
		||||
        .range = loc.range,
 | 
			
		||||
        .newText = context.new_name,
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn renameSymbol(
 | 
			
		||||
    arena: *std.heap.ArenaAllocator,
 | 
			
		||||
    store: *DocumentStore,
 | 
			
		||||
    decl_handle: analysis.DeclWithHandle,
 | 
			
		||||
    new_name: []const u8,
 | 
			
		||||
    edits: *std.StringHashMapUnmanaged(std.ArrayListUnmanaged(types.TextEdit)),
 | 
			
		||||
    encoding: offsets.Encoding,
 | 
			
		||||
) !void {
 | 
			
		||||
    std.debug.assert(decl_handle.decl.* != .label_decl);
 | 
			
		||||
    try references.symbolReferences(arena, store, decl_handle, encoding, true, RefHandlerContext{
 | 
			
		||||
        .edits = edits,
 | 
			
		||||
        .allocator = arena.allocator(),
 | 
			
		||||
        .new_name = new_name,
 | 
			
		||||
    }, refHandler, true, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn renameLabel(
 | 
			
		||||
    arena: *std.heap.ArenaAllocator,
 | 
			
		||||
    decl_handle: analysis.DeclWithHandle,
 | 
			
		||||
    new_name: []const u8,
 | 
			
		||||
    edits: *std.StringHashMapUnmanaged(std.ArrayListUnmanaged(types.TextEdit)),
 | 
			
		||||
    encoding: offsets.Encoding,
 | 
			
		||||
) !void {
 | 
			
		||||
    std.debug.assert(decl_handle.decl.* == .label_decl);
 | 
			
		||||
    try references.labelReferences(arena, decl_handle, encoding, true, RefHandlerContext{
 | 
			
		||||
        .edits = edits,
 | 
			
		||||
        .allocator = arena.allocator(),
 | 
			
		||||
        .new_name = new_name,
 | 
			
		||||
    }, refHandler);
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user