diff --git a/Sources/SourceKitLSP/SourceKitLSPServer.swift b/Sources/SourceKitLSP/SourceKitLSPServer.swift index 9faa61977..4512e4239 100644 --- a/Sources/SourceKitLSP/SourceKitLSPServer.swift +++ b/Sources/SourceKitLSP/SourceKitLSPServer.swift @@ -1850,10 +1850,24 @@ extension SourceKitLSPServer { languageService: LanguageService ) async throws -> [Location] { // If this symbol is a module then generate a textual interface - if symbol.kind == .module, let name = symbol.name { + if symbol.kind == .module { + // For module symbols, prefer using systemModule information if available + let moduleName: String + let groupName: String? + + if let systemModule = symbol.systemModule { + moduleName = systemModule.moduleName + groupName = systemModule.groupName + } else if let name = symbol.name { + moduleName = name + groupName = nil + } else { + return [] + } + let interfaceLocation = try await self.definitionInInterface( - moduleName: name, - groupName: nil, + moduleName: moduleName, + groupName: groupName, symbolUSR: nil, originatorUri: uri, languageService: languageService @@ -2051,9 +2065,12 @@ extension SourceKitLSPServer { originatorUri: DocumentURI, languageService: LanguageService ) async throws -> Location { + // Let openGeneratedInterface handle all the logic, including checking if we're already in the right interface + let documentForBuildSettings = originatorUri.buildSettingsFile + guard let interfaceDetails = try await languageService.openGeneratedInterface( - document: originatorUri, + document: documentForBuildSettings, moduleName: moduleName, groupName: groupName, symbolUSR: symbolUSR diff --git a/Sources/SourceKitLSP/Swift/GeneratedInterfaceDocumentURLData.swift b/Sources/SourceKitLSP/Swift/GeneratedInterfaceDocumentURLData.swift index bfa6522df..55ff28341 100644 --- a/Sources/SourceKitLSP/Swift/GeneratedInterfaceDocumentURLData.swift +++ b/Sources/SourceKitLSP/Swift/GeneratedInterfaceDocumentURLData.swift @@ -67,7 +67,7 @@ package struct GeneratedInterfaceDocumentURLData: Hashable, ReferenceURLData { self.moduleName = moduleName self.groupName = groupName self.sourcekitdDocumentName = sourcekitdDocumentName - self.buildSettingsFrom = primaryFile + self.buildSettingsFrom = primaryFile.buildSettingsFile } init(queryItems: [URLQueryItem]) throws { diff --git a/Sources/SourceKitLSP/Swift/OpenInterface.swift b/Sources/SourceKitLSP/Swift/OpenInterface.swift index 973643712..941101fb6 100644 --- a/Sources/SourceKitLSP/Swift/OpenInterface.swift +++ b/Sources/SourceKitLSP/Swift/OpenInterface.swift @@ -21,10 +21,16 @@ extension SwiftLanguageService { groupName: String?, symbolUSR symbol: String? ) async throws -> GeneratedInterfaceDetails? { + // Include build settings context to distinguish different versions/configurations + let buildSettingsFileHash = "\(abs(document.buildSettingsFile.stringValue.hashValue))" + let sourcekitdDocumentName = [moduleName, groupName, buildSettingsFileHash].compactMap(\.self).joined( + separator: "." + ) + let urlData = GeneratedInterfaceDocumentURLData( moduleName: moduleName, groupName: groupName, - sourcekitdDocumentName: "\(moduleName)-\(UUID())", + sourcekitdDocumentName: sourcekitdDocumentName, primaryFile: document ) let position: Position? = diff --git a/Sources/SourceKitLSP/Swift/ReferenceDocumentURL.swift b/Sources/SourceKitLSP/Swift/ReferenceDocumentURL.swift index 8b5d86320..ab9e9f4f6 100644 --- a/Sources/SourceKitLSP/Swift/ReferenceDocumentURL.swift +++ b/Sources/SourceKitLSP/Swift/ReferenceDocumentURL.swift @@ -170,8 +170,4 @@ extension DocumentURI { package struct ReferenceDocumentURLError: Error, CustomStringConvertible { package var description: String - - init(description: String) { - self.description = description - } } diff --git a/Tests/SourceKitLSPTests/SwiftInterfaceTests.swift b/Tests/SourceKitLSPTests/SwiftInterfaceTests.swift index 4997c5942..beeb2bbb9 100644 --- a/Tests/SourceKitLSPTests/SwiftInterfaceTests.swift +++ b/Tests/SourceKitLSPTests/SwiftInterfaceTests.swift @@ -319,6 +319,63 @@ final class SwiftInterfaceTests: XCTestCase { ) XCTAssertEqual(diagnostics.fullReport?.items, []) } + + func testFoundationImportNavigation() async throws { + let testClient = try await TestSourceKitLSPClient( + capabilities: ClientCapabilities(experimental: [ + GetReferenceDocumentRequest.method: .dictionary(["supported": .bool(true)]) + ]) + ) + let uri = DocumentURI(for: .swift) + + let positions = testClient.openDocument( + """ + import 1️⃣Foundation + """, + uri: uri, + language: .swift + ) + + // Test navigation to Foundation module + let foundationDefinition = try await testClient.send( + DefinitionRequest(textDocument: TextDocumentIdentifier(uri), position: positions["1️⃣"]) + ) + let foundationLocation = try XCTUnwrap(foundationDefinition?.locations?.only) + XCTAssertTrue(foundationLocation.uri.scheme == "sourcekit-lsp") + XCTAssertTrue(foundationLocation.uri.pseudoPath.contains("Foundation.swiftinterface")) + } + + func testFoundationSubmoduleNavigation() async throws { + let testClient = try await TestSourceKitLSPClient( + capabilities: ClientCapabilities(experimental: [ + GetReferenceDocumentRequest.method: .dictionary(["supported": .bool(true)]) + ]) + ) + let uri = DocumentURI(for: .swift) + + let positions = testClient.openDocument( + """ + import 1️⃣Foundation.2️⃣NSAffineTransform + """, + uri: uri + ) + + let foundationDefinition = try await testClient.send( + DefinitionRequest(textDocument: TextDocumentIdentifier(uri), position: positions["1️⃣"]) + ) + let foundationLocation = try XCTUnwrap(foundationDefinition?.locations?.only) + XCTAssertTrue(foundationLocation.uri.pseudoPath.contains("Foundation.swiftinterface")) + XCTAssertTrue(foundationLocation.uri.scheme == "sourcekit-lsp") + + // Test navigation to NSAffineTransform + let transformDefinition = try await testClient.send( + DefinitionRequest(textDocument: TextDocumentIdentifier(uri), position: positions["2️⃣"]) + ) + let transformLocation = try XCTUnwrap(transformDefinition?.locations?.only) + // Verify we can identify this as a swiftinterface file + XCTAssertTrue(transformLocation.uri.pseudoPath.contains("Foundation.NSAffineTransform.swiftinterface")) + XCTAssertTrue(transformLocation.uri.scheme == "sourcekit-lsp") + } } private func assertSystemSwiftInterface(