Skip to content

Commit cae01c6

Browse files
committed
Apply code review fixes for copied file handling
- Remove async from workspaceEditAdjustedForCopiedFiles - Refactor to use uriAdjustedForCopiedFiles helper - Update dictionary update logic with += - Adjust LocationLink creation to use adjusted ranges - Ensure selectionRange adjustment in prepareCallHierarchy - Provide default WorkspaceEdit in ClangLanguageService - Revert asyncMap to map and remove await in SourceKitLSPServer - Chain workspace and index retrieval in incomingCalls - Use indexToLSPCallHierarchyItem and shared helper for CallHierarchyItem - Fix indentation and remove duplicated detail setting - Use shared helper for TypeHierarchyItem - Remove .sort() from expected array in tests - Enable testFindImplementationInCopiedHeader - Add await for actor-isolated BuildServerManager calls
1 parent 70d900e commit cae01c6

File tree

4 files changed

+84
-133
lines changed

4 files changed

+84
-133
lines changed

Sources/BuildServerIntegration/BuildServerManager.swift

Lines changed: 26 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -962,17 +962,22 @@ package actor BuildServerManager: QueueBasedMessageHandler {
962962
return locations.map { locationAdjustedForCopiedFiles($0) }
963963
}
964964

965-
package func workspaceEditAdjustedForCopiedFiles(_ workspaceEdit: WorkspaceEdit?) async -> WorkspaceEdit? {
965+
private func uriAdjustedForCopiedFiles(_ uri: DocumentURI) -> DocumentURI {
966+
guard let originalUri = cachedCopiedFileMap[uri] else {
967+
return uri
968+
}
969+
return originalUri
970+
}
971+
972+
package func workspaceEditAdjustedForCopiedFiles(_ workspaceEdit: WorkspaceEdit?) -> WorkspaceEdit? {
966973
guard var edit = workspaceEdit else {
967974
return nil
968975
}
969976
if let changes = edit.changes {
970977
var newChanges: [DocumentURI: [TextEdit]] = [:]
971978
for (uri, edits) in changes {
972-
let newUri = self.locationAdjustedForCopiedFiles(
973-
Location(uri: uri, range: Position(line: 0, utf16index: 0)..<Position(line: 0, utf16index: 0))
974-
).uri
975-
newChanges[newUri, default: []].append(contentsOf: edits)
979+
let newUri = self.uriAdjustedForCopiedFiles(uri)
980+
newChanges[newUri, default: []] += edits
976981
}
977982
edit.changes = newChanges
978983
}
@@ -981,32 +986,17 @@ package actor BuildServerManager: QueueBasedMessageHandler {
981986
for change in documentChanges {
982987
switch change {
983988
case .textDocumentEdit(var textEdit):
984-
let newUri = self.locationAdjustedForCopiedFiles(
985-
Location(uri: textEdit.textDocument.uri, range: Position(line: 0, utf16index: 0)..<Position(line: 0, utf16index: 0))
986-
).uri
987-
textEdit.textDocument.uri = newUri
989+
textEdit.textDocument.uri = self.uriAdjustedForCopiedFiles(textEdit.textDocument.uri)
988990
newDocumentChanges.append(.textDocumentEdit(textEdit))
989991
case .createFile(var create):
990-
let newUri = self.locationAdjustedForCopiedFiles(
991-
Location(uri: create.uri, range: Position(line: 0, utf16index: 0)..<Position(line: 0, utf16index: 0))
992-
).uri
993-
create.uri = newUri
992+
create.uri = self.uriAdjustedForCopiedFiles(create.uri)
994993
newDocumentChanges.append(.createFile(create))
995994
case .renameFile(var rename):
996-
let newOldUri = self.locationAdjustedForCopiedFiles(
997-
Location(uri: rename.oldUri, range: Position(line: 0, utf16index: 0)..<Position(line: 0, utf16index: 0))
998-
).uri
999-
let newNewUri = self.locationAdjustedForCopiedFiles(
1000-
Location(uri: rename.newUri, range: Position(line: 0, utf16index: 0)..<Position(line: 0, utf16index: 0))
1001-
).uri
1002-
rename.oldUri = newOldUri
1003-
rename.newUri = newNewUri
995+
rename.oldUri = self.uriAdjustedForCopiedFiles(rename.oldUri)
996+
rename.newUri = self.uriAdjustedForCopiedFiles(rename.newUri)
1004997
newDocumentChanges.append(.renameFile(rename))
1005998
case .deleteFile(var delete):
1006-
let newUri = self.locationAdjustedForCopiedFiles(
1007-
Location(uri: delete.uri, range: Position(line: 0, utf16index: 0)..<Position(line: 0, utf16index: 0))
1008-
).uri
1009-
delete.uri = newUri
999+
delete.uri = self.uriAdjustedForCopiedFiles(delete.uri)
10101000
newDocumentChanges.append(.deleteFile(delete))
10111001
}
10121002
}
@@ -1015,39 +1005,41 @@ package actor BuildServerManager: QueueBasedMessageHandler {
10151005
return edit
10161006
}
10171007

1018-
package func locationsOrLocationLinksAdjustedForCopiedFiles(_ response: LocationsOrLocationLinksResponse?) async -> LocationsOrLocationLinksResponse? {
1008+
package func locationsOrLocationLinksAdjustedForCopiedFiles(_ response: LocationsOrLocationLinksResponse?) -> LocationsOrLocationLinksResponse? {
10191009
guard let response = response else {
10201010
return nil
10211011
}
10221012
switch response {
10231013
case .locations(let locations):
1024-
let remappedLocations = await self.locationsAdjustedForCopiedFiles(locations)
1014+
let remappedLocations = self.locationsAdjustedForCopiedFiles(locations)
10251015
return .locations(remappedLocations)
10261016
case .locationLinks(let locationLinks):
10271017
var remappedLinks: [LocationLink] = []
10281018
for link in locationLinks {
1029-
let newUri = self.locationAdjustedForCopiedFiles(Location(uri: link.targetUri, range: link.targetRange)).uri
1019+
let adjustedTargetLocation = self.locationAdjustedForCopiedFiles(Location(uri: link.targetUri, range: link.targetRange))
1020+
let adjustedTargetSelectionLocation = self.locationAdjustedForCopiedFiles(Location(uri: link.targetUri, range: link.targetSelectionRange))
10301021
remappedLinks.append(LocationLink(
10311022
originSelectionRange: link.originSelectionRange,
1032-
targetUri: newUri,
1033-
targetRange: link.targetRange,
1034-
targetSelectionRange: link.targetSelectionRange
1023+
targetUri: adjustedTargetLocation.uri,
1024+
targetRange: adjustedTargetLocation.range,
1025+
targetSelectionRange: adjustedTargetSelectionLocation.range
10351026
))
10361027
}
10371028
return .locationLinks(remappedLinks)
10381029
}
10391030
}
10401031

1041-
package func typeHierarchyItemAdjustedForCopiedFiles(_ item: TypeHierarchyItem) async -> TypeHierarchyItem {
1042-
let adjustedLocation = await self.locationAdjustedForCopiedFiles(Location(uri: item.uri, range: item.range))
1032+
package func typeHierarchyItemAdjustedForCopiedFiles(_ item: TypeHierarchyItem) -> TypeHierarchyItem {
1033+
let adjustedLocation = self.locationAdjustedForCopiedFiles(Location(uri: item.uri, range: item.range))
1034+
let adjustedSelectionLocation = self.locationAdjustedForCopiedFiles(Location(uri: item.uri, range: item.selectionRange))
10431035
return TypeHierarchyItem(
10441036
name: item.name,
10451037
kind: item.kind,
10461038
tags: item.tags,
10471039
detail: item.detail,
10481040
uri: adjustedLocation.uri,
10491041
range: adjustedLocation.range,
1050-
selectionRange: item.selectionRange,
1042+
selectionRange: adjustedSelectionLocation.range,
10511043
data: item.data
10521044
)
10531045
}

Sources/ClangLanguageService/ClangLanguageService.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -642,9 +642,9 @@ extension ClangLanguageService {
642642
position: renameRequest.position
643643
)
644644
let symbolDetail = try await forwardRequestToClangd(symbolInfoRequest).only
645-
let workspaceEdit = try await edits
645+
let workspaceEdit = try await edits ?? WorkspaceEdit()
646646
guard let workspace = self.workspace.value else {
647-
return (workspaceEdit ?? WorkspaceEdit(), symbolDetail?.usr)
647+
return (workspaceEdit, symbolDetail?.usr)
648648
}
649649
let remappedEdit = await workspace.buildServerManager.workspaceEditAdjustedForCopiedFiles(workspaceEdit)
650650
return (remappedEdit ?? WorkspaceEdit(), symbolDetail?.usr)

Sources/SourceKitLSP/SourceKitLSPServer.swift

Lines changed: 55 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -2260,6 +2260,36 @@ extension SourceKitLSPServer {
22602260
)
22612261
}
22622262

2263+
private func callHierarchyItemAdjustedForCopiedFiles(
2264+
_ item: CallHierarchyItem,
2265+
workspace: Workspace
2266+
) async -> CallHierarchyItem {
2267+
let adjustedLocation = await workspace.buildServerManager.locationAdjustedForCopiedFiles(
2268+
Location(uri: item.uri, range: item.range)
2269+
)
2270+
let adjustedSelectionLocation = await workspace.buildServerManager.locationAdjustedForCopiedFiles(
2271+
Location(uri: item.uri, range: item.selectionRange)
2272+
)
2273+
return CallHierarchyItem(
2274+
name: item.name,
2275+
kind: item.kind,
2276+
tags: item.tags,
2277+
detail: item.detail,
2278+
uri: adjustedLocation.uri,
2279+
range: adjustedLocation.range,
2280+
selectionRange: adjustedSelectionLocation.range,
2281+
data: .dictionary([
2282+
"usr": item.data.flatMap { data in
2283+
if case let .dictionary(dict) = data {
2284+
return dict["usr"]
2285+
}
2286+
return nil
2287+
} ?? .null,
2288+
"uri": .string(adjustedLocation.uri.stringValue),
2289+
])
2290+
)
2291+
}
2292+
22632293
func prepareCallHierarchy(
22642294
_ req: CallHierarchyPrepareRequest,
22652295
workspace: Workspace,
@@ -2284,31 +2314,11 @@ extension SourceKitLSPServer {
22842314
guard let definition = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: usr) else {
22852315
continue
22862316
}
2287-
let location = indexToLSPLocation(definition.location)
2288-
guard let originalLocation = location else {
2317+
guard let item = indexToLSPCallHierarchyItem(definition: definition, index: index) else {
22892318
continue
22902319
}
2291-
let remappedLocation = await workspace.buildServerManager.locationAdjustedForCopiedFiles(originalLocation)
2292-
2293-
// Create a new CallHierarchyItem with the remapped location (similar to how we handle TypeHierarchyItem)
2294-
let name = index.fullyQualifiedName(of: definition)
2295-
let symbol = definition.symbol
2296-
2297-
let item = CallHierarchyItem(
2298-
name: name,
2299-
kind: symbol.kind.asLspSymbolKind(),
2300-
tags: nil,
2301-
detail: nil,
2302-
uri: remappedLocation.uri,
2303-
range: remappedLocation.range,
2304-
selectionRange: remappedLocation.range,
2305-
// We encode usr and uri for incoming/outgoing call lookups in the implementation-specific data field
2306-
data: .dictionary([
2307-
"usr": .string(symbol.usr),
2308-
"uri": .string(remappedLocation.uri.stringValue),
2309-
])
2310-
)
2311-
callHierarchyItems.append(item)
2320+
let adjustedItem = await callHierarchyItemAdjustedForCopiedFiles(item, workspace: workspace)
2321+
callHierarchyItems.append(adjustedItem)
23122322
}
23132323
callHierarchyItems.sort(by: { Location(uri: $0.uri, range: $0.range) < Location(uri: $1.uri, range: $1.range) })
23142324

@@ -2358,7 +2368,7 @@ extension SourceKitLSPServer {
23582368
var callersToCalls: [Symbol: [SymbolOccurrence]] = [:]
23592369

23602370
for call in callOccurrences {
2361-
// Callers are all `calledBy` relations of a call to a USR in `callableUSrs`, ie. all the functions that contain a
2371+
// Callers are all `containedBy` relations of a call to a USR in `callableUSrs`, ie. all the functions that contain a
23622372
// call to a USR in callableUSRs. In practice, this should always be a single item.
23632373
let callers = call.relations.filter { $0.roles.contains(.containedBy) }.map(\.symbol)
23642374
for caller in callers {
@@ -2506,32 +2516,22 @@ extension SourceKitLSPServer {
25062516
}
25072517

25082518
let symbol = definition.symbol
2509-
switch symbol.kind {
2510-
case .extension:
2511-
// Query the conformance added by this extension
2512-
let conformances = index.occurrences(relatedToUSR: symbol.usr, roles: .baseOf)
2513-
if conformances.isEmpty {
2514-
name = symbol.name
2515-
} else {
2516-
name = "\(symbol.name): \(conformances.map(\.symbol.name).sorted().joined(separator: ", "))"
2517-
}
2518-
// Add the file name and line to the detail string
2519-
if let url = remappedLocation.uri.fileURL,
2520-
let basename = (try? AbsolutePath(validating: url.filePath))?.basename
2521-
{
2522-
detail = "Extension at \(basename):\(remappedLocation.range.lowerBound.line + 1)"
2523-
} else if !definition.location.moduleName.isEmpty {
2524-
detail = "Extension in \(definition.location.moduleName)"
2525-
} else {
2526-
detail = "Extension"
2527-
}
2519+
switch symbol.kind {
2520+
case .extension:
2521+
// Query the conformance added by this extension
2522+
let conformances = index.occurrences(relatedToUSR: symbol.usr, roles: .baseOf)
2523+
if conformances.isEmpty {
2524+
name = symbol.name
2525+
} else {
2526+
name = "\(symbol.name): \(conformances.map(\.symbol.name).sorted().joined(separator: ", "))"
2527+
}
25282528
// Add the file name and line to the detail string
25292529
if let url = location.uri.fileURL,
25302530
let basename = (try? AbsolutePath(validating: url.filePath))?.basename
25312531
{
25322532
detail = "Extension at \(basename):\(location.range.lowerBound.line + 1)"
2533-
} else if let moduleName = moduleName {
2534-
detail = "Extension in \(moduleName)"
2533+
} else if !definition.location.moduleName.isEmpty {
2534+
detail = "Extension in \(definition.location.moduleName)"
25352535
} else {
25362536
detail = "Extension"
25372537
}
@@ -2602,55 +2602,16 @@ extension SourceKitLSPServer {
26022602
}
26032603

26042604
let location = indexToLSPLocation(info.location)
2605-
guard let originalLocation = location else {
2605+
guard location != nil else {
26062606
continue
26072607
}
2608-
let remappedLocation = await workspace.buildServerManager.locationAdjustedForCopiedFiles(originalLocation)
2609-
2610-
// Create a new TypeHierarchyItem with the original location, then adjust for copied files
2611-
let name: String
2612-
let detail: String?
2613-
let symbol = info.symbol
2614-
switch symbol.kind {
2615-
case .extension:
2616-
// Query the conformance added by this extension
2617-
let conformances = index.occurrences(relatedToUSR: symbol.usr, roles: .baseOf)
2618-
if conformances.isEmpty {
2619-
name = symbol.name
2620-
} else {
2621-
name = "\(symbol.name): \(conformances.map(\.symbol.name).sorted().joined(separator: ", "))"
2622-
}
2623-
// Add the file name and line to the detail string
2624-
if let url = remappedLocation.uri.fileURL,
2625-
let basename = (try? AbsolutePath(validating: url.filePath))?.basename
2626-
{
2627-
detail = "Extension at \(basename):\(remappedLocation.range.lowerBound.line + 1)"
2628-
} else if !info.location.moduleName.isEmpty {
2629-
detail = "Extension in \(info.location.moduleName)"
2630-
} else {
2631-
detail = "Extension"
2632-
}
2633-
default:
2634-
name = index.fullyQualifiedName(of: info)
2635-
detail = info.location.moduleName
2636-
}
2637-
2638-
let item = TypeHierarchyItem(
2639-
name: name,
2640-
kind: symbol.kind.asLspSymbolKind(),
2641-
tags: nil,
2642-
detail: detail,
2643-
uri: originalLocation.uri,
2644-
range: originalLocation.range,
2645-
selectionRange: originalLocation.range,
2646-
// We encode usr and uri for incoming/outgoing type lookups in the implementation-specific data field
2647-
data: .dictionary([
2648-
"usr": .string(symbol.usr),
2649-
"uri": .string(originalLocation.uri.stringValue),
2650-
])
2651-
)
2652-
let remappedItem = await workspace.buildServerManager.typeHierarchyItemAdjustedForCopiedFiles(item)
2653-
typeHierarchyItems.append(remappedItem)
2608+
2609+
let moduleName = info.location.moduleName.isEmpty ? nil : info.location.moduleName
2610+
guard let item = indexToLSPTypeHierarchyItem(definition: info, moduleName: moduleName, index: index) else {
2611+
continue
2612+
}
2613+
let remappedItem = await workspace.buildServerManager.typeHierarchyItemAdjustedForCopiedFiles(item)
2614+
typeHierarchyItems.append(remappedItem)
26542615
}
26552616
typeHierarchyItems.sort(by: { $0.name < $1.name })
26562617

@@ -2754,10 +2715,10 @@ extension SourceKitLSPServer {
27542715
name = "\(symbol.name): \(conformances.map(\.symbol.name).sorted().joined(separator: ", "))"
27552716
}
27562717
// Add the file name and line to the detail string
2757-
if let url = adjustedLocation.uri.fileURL,
2718+
if let url = remappedLocation.uri.fileURL,
27582719
let basename = (try? AbsolutePath(validating: url.filePath))?.basename
27592720
{
2760-
detail = "Extension at \(basename):\(adjustedLocation.range.lowerBound.line + 1)"
2721+
detail = "Extension at \(basename):\(remappedLocation.range.lowerBound.line + 1)"
27612722
} else if !definition.location.moduleName.isEmpty {
27622723
detail = "Extension in \(definition.location.moduleName)"
27632724
} else {

Tests/SourceKitLSPTests/CopiedHeaderTests.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,16 +120,14 @@ class CopiedHeaderTests: SourceKitLSPTestCase {
120120
context: ReferencesContext(includeDeclaration: true)
121121
)
122122
)
123-
response.sort()
124123
var expected = [
125124
try project.location(from: "1️⃣", to: "1️⃣", in: "Test.h"),
126125
try project.location(from: "2️⃣", to: "2️⃣", in: "Test.c"),
127126
]
128-
expected.sort()
129127
XCTAssertEqual(response, expected)
130128
}
131129

132-
func DISABLED_testFindImplementationInCopiedHeader() async throws {
130+
func testFindImplementationInCopiedHeader() async throws {
133131
let project = try await CustomBuildServerTestProject(
134132
files: [
135133
"Test.h": """

0 commit comments

Comments
 (0)