//===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// import Csourcekitd @_spi(SourceKitLSP) import LanguageServerProtocol @_spi(SourceKitLSP) import SKLogging import SourceKitD import SourceKitLSP struct RelatedIdentifier { let range: Range let usage: RenameLocation.Usage } extension RenameLocation.Usage { fileprivate init?(_ uid: sourcekitd_api_uid_t?, _ values: sourcekitd_api_values) { switch uid { case values.definition: self = .definition case values.reference: self = .reference case values.call: self = .call case values.unknown: self = .unknown default: return nil } } func uid(values: sourcekitd_api_values) -> sourcekitd_api_uid_t { switch self { case .definition: return values.definition case .reference: return values.reference case .call: return values.call case .unknown: return values.unknown } } } struct RelatedIdentifiersResponse { let relatedIdentifiers: [RelatedIdentifier] /// The compound decl name at the requested location. This can be used as `name` parameter to a /// `find-syntactic-rename-ranges` request. /// /// `nil` if `sourcekitd` is too old and doesn't return the `name` as part of the related identifiers request or /// `relatedIdentifiers` is empty (eg. when performing a related identifiers request on `self`). let name: String? } extension SwiftLanguageService { func relatedIdentifiers( at position: Position, in snapshot: DocumentSnapshot, includeNonEditableBaseNames: Bool ) async throws -> RelatedIdentifiersResponse { let skreq = sourcekitd.dictionary([ keys.cancelOnSubsequentRequest: 0, keys.offset: snapshot.utf8Offset(of: position), keys.sourceFile: snapshot.uri.sourcekitdSourceFile, keys.primaryFile: snapshot.uri.primaryFile?.pseudoPath, keys.includeNonEditableBaseNames: includeNonEditableBaseNames ? 1 : 0, keys.compilerArgs: await self.compileCommand(for: snapshot.uri, fallbackAfterTimeout: true)?.compilerArgs as [any SKDRequestValue]?, ]) let dict = try await send(sourcekitdRequest: \.relatedIdents, skreq, snapshot: snapshot) guard let results: SKDResponseArray = dict[self.keys.results] else { throw ResponseError.internalError("sourcekitd response did not contain results") } let name: String? = dict[self.keys.name] try Task.checkCancellation() var relatedIdentifiers: [RelatedIdentifier] = [] // swift-format-ignore: ReplaceForEachWithForLoop // Reference is to `SKDResponseArray.forEach`, not `Array.forEach`. results.forEach { _, value in guard let offset: Int = value[keys.offset], let length: Int = value[keys.length] else { return true // continue } let start = snapshot.positionOf(utf8Offset: offset) let end = snapshot.positionOf(utf8Offset: offset + length) let usage = RenameLocation.Usage(value[keys.nameType], values) ?? .unknown relatedIdentifiers.append(RelatedIdentifier(range: start..