Skip to content

Commit 80c214b

Browse files
committed
Change CheckedIndex to use DocumentURI instead of URL
1 parent 98718d4 commit 80c214b

File tree

7 files changed

+67
-62
lines changed

7 files changed

+67
-62
lines changed

Sources/LanguageServerProtocol/SupportTypes/DocumentURI.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ public struct DocumentURI: Codable, Hashable, Sendable {
7575
assert(self.storage.scheme != nil, "Received invalid URI without a scheme '\(self.storage.absoluteString)'")
7676
}
7777

78+
public init(filePath: String, isDirectory: Bool) {
79+
self.init(URL(fileURLWithPath: filePath, isDirectory: isDirectory))
80+
}
81+
7882
public init(from decoder: Decoder) throws {
7983
try self.init(string: decoder.singleValueContainer().decode(String.self))
8084
}

Sources/SKTestSupport/SkipUnless.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,14 @@ public actor SkipUnless {
7070
)
7171
} else if toolchainSwiftVersion == requiredSwiftVersion {
7272
logger.info("Checking if feature '\(featureName)' is supported")
73+
defer {
74+
logger.info("Done checking if feature '\(featureName)' is supported")
75+
}
7376
if try await !featureCheck() {
7477
return .featureUnsupported(skipMessage: "Skipping because toolchain doesn't contain \(featureName)")
7578
} else {
7679
return .featureSupported
7780
}
78-
logger.info("Done checking if feature '\(featureName)' is supported")
7981
} else {
8082
return .featureSupported
8183
}

Sources/SemanticIndex/CheckedIndex.swift

Lines changed: 52 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ import LanguageServerProtocol
1919
///
2020
/// Protocol is needed because the `SemanticIndex` module is lower-level than the `SourceKitLSP` module.
2121
public protocol InMemoryDocumentManager {
22-
/// Returns true if the file at the given URL has a different content in the document manager than on-disk. This is
22+
/// Returns true if the file at the given URI has a different content in the document manager than on-disk. This is
2323
/// the case if the user made edits to the file but didn't save them yet.
24-
func fileHasInMemoryModifications(_ url: URL) -> Bool
24+
func fileHasInMemoryModifications(_ uri: DocumentURI) -> Bool
2525
}
2626

2727
public enum IndexCheckLevel {
@@ -105,7 +105,7 @@ public final class CheckedIndex {
105105
}
106106

107107
public func symbols(inFilePath path: String) -> [Symbol] {
108-
guard self.hasUpToDateUnit(for: URL(fileURLWithPath: path, isDirectory: false)) else {
108+
guard self.hasUpToDateUnit(for: DocumentURI(filePath: path, isDirectory: false)) else {
109109
return []
110110
}
111111
return index.symbols(inFilePath: path)
@@ -121,11 +121,11 @@ public final class CheckedIndex {
121121
/// If `crossLanguage` is set to `true`, Swift files that import a header through a module will also be reported.
122122
public func mainFilesContainingFile(uri: DocumentURI, crossLanguage: Bool = false) -> [DocumentURI] {
123123
return index.mainFilesContainingFile(path: uri.pseudoPath, crossLanguage: crossLanguage).compactMap {
124-
let url = URL(fileURLWithPath: $0)
125-
guard checker.indexHasUpToDateUnit(for: url, mainFile: nil, index: self.index) else {
124+
let uri = DocumentURI(filePath: $0, isDirectory: false)
125+
guard checker.indexHasUpToDateUnit(for: uri, mainFile: nil, index: self.index) else {
126126
return nil
127127
}
128-
return DocumentURI(url)
128+
return uri
129129
}
130130
}
131131

@@ -141,16 +141,16 @@ public final class CheckedIndex {
141141
/// If `mainFile` is passed, then `url` is a header file that won't have a unit associated with it. `mainFile` is
142142
/// assumed to be a file that imports `url`. To check that `url` has an up-to-date unit, check that the latest unit
143143
/// for `mainFile` is newer than the mtime of the header file at `url`.
144-
public func hasUpToDateUnit(for url: URL, mainFile: URL? = nil) -> Bool {
145-
return checker.indexHasUpToDateUnit(for: url, mainFile: mainFile, index: index)
144+
public func hasUpToDateUnit(for uri: DocumentURI, mainFile: DocumentURI? = nil) -> Bool {
145+
return checker.indexHasUpToDateUnit(for: uri, mainFile: mainFile, index: index)
146146
}
147147

148-
/// Returns true if the file at the given URL has a different content in the document manager than on-disk. This is
148+
/// Returns true if the file at the given URI has a different content in the document manager than on-disk. This is
149149
/// the case if the user made edits to the file but didn't save them yet.
150150
///
151151
/// - Important: This must only be called on a `CheckedIndex` with a `checkLevel` of `inMemoryModifiedFiles`
152-
public func fileHasInMemoryModifications(_ url: URL) -> Bool {
153-
return checker.fileHasInMemoryModifications(url)
152+
public func fileHasInMemoryModifications(_ uri: DocumentURI) -> Bool {
153+
return checker.fileHasInMemoryModifications(uri)
154154
}
155155
}
156156

@@ -212,14 +212,14 @@ private struct IndexOutOfDateChecker {
212212
}
213213
}
214214

215-
/// Caches whether a file URL has modifications in `documentManager` that haven't been saved to disk yet.
216-
private var fileHasInMemoryModificationsCache: [URL: Bool] = [:]
215+
/// Caches whether a document has modifications in `documentManager` that haven't been saved to disk yet.
216+
private var fileHasInMemoryModificationsCache: [DocumentURI: Bool] = [:]
217217

218-
/// File URLs to modification times that have already been computed.
219-
private var modTimeCache: [URL: ModificationTime] = [:]
218+
/// Document URIs to modification times that have already been computed.
219+
private var modTimeCache: [DocumentURI: ModificationTime] = [:]
220220

221-
/// File URLs to whether they exist on the file system
222-
private var fileExistsCache: [URL: Bool] = [:]
221+
/// Document URIs to whether they exist on the file system
222+
private var fileExistsCache: [DocumentURI: Bool] = [:]
223223

224224
init(checkLevel: IndexCheckLevel) {
225225
self.checkLevel = checkLevel
@@ -230,16 +230,16 @@ private struct IndexOutOfDateChecker {
230230
/// Returns `true` if the source file for the given symbol location exists and has not been modified after it has been
231231
/// indexed.
232232
mutating func isUpToDate(_ symbolLocation: SymbolLocation) -> Bool {
233-
let url = URL(fileURLWithPath: symbolLocation.path, isDirectory: false)
233+
let uri = DocumentURI(filePath: symbolLocation.path, isDirectory: false)
234234
switch checkLevel {
235235
case .inMemoryModifiedFiles(let documentManager):
236-
if fileHasInMemoryModifications(url, documentManager: documentManager) {
236+
if fileHasInMemoryModifications(uri, documentManager: documentManager) {
237237
return false
238238
}
239239
fallthrough
240240
case .modifiedFiles:
241241
do {
242-
let sourceFileModificationDate = try modificationDate(of: url)
242+
let sourceFileModificationDate = try modificationDate(of: uri)
243243
switch sourceFileModificationDate {
244244
case .fileDoesNotExist:
245245
return false
@@ -251,7 +251,7 @@ private struct IndexOutOfDateChecker {
251251
return true
252252
}
253253
case .deletedFiles:
254-
return fileExists(at: url)
254+
return fileExists(at: uri)
255255
}
256256
}
257257

@@ -262,7 +262,7 @@ private struct IndexOutOfDateChecker {
262262
/// If `mainFile` is passed, then `filePath` is a header file that won't have a unit associated with it. `mainFile` is
263263
/// assumed to be a file that imports `url`. To check that `url` has an up-to-date unit, check that the latest unit
264264
/// for `mainFile` is newer than the mtime of the header file at `url`.
265-
mutating func indexHasUpToDateUnit(for filePath: URL, mainFile: URL?, index: IndexStoreDB) -> Bool {
265+
mutating func indexHasUpToDateUnit(for filePath: DocumentURI, mainFile: DocumentURI?, index: IndexStoreDB) -> Bool {
266266
switch checkLevel {
267267
case .inMemoryModifiedFiles(let documentManager):
268268
if fileHasInMemoryModifications(filePath, documentManager: documentManager) {
@@ -273,7 +273,9 @@ private struct IndexOutOfDateChecker {
273273
// If there are no in-memory modifications check if there are on-disk modifications.
274274
fallthrough
275275
case .modifiedFiles:
276-
guard let lastUnitDate = index.dateOfLatestUnitFor(filePath: (mainFile ?? filePath).path) else {
276+
guard let fileURL = (mainFile ?? filePath).fileURL,
277+
let lastUnitDate = index.dateOfLatestUnitFor(filePath: fileURL.path)
278+
else {
277279
return false
278280
}
279281
do {
@@ -300,23 +302,25 @@ private struct IndexOutOfDateChecker {
300302
/// `documentManager` must always be the same between calls to `hasFileInMemoryModifications` since it is not part of
301303
/// the cache key. This is fine because we always assume the `documentManager` to come from the associated value of
302304
/// `CheckLevel.imMemoryModifiedFiles`, which is constant.
303-
private mutating func fileHasInMemoryModifications(_ url: URL, documentManager: InMemoryDocumentManager) -> Bool {
304-
if let cached = fileHasInMemoryModificationsCache[url] {
305+
private mutating func fileHasInMemoryModifications(_ uri: DocumentURI, documentManager: InMemoryDocumentManager)
306+
-> Bool
307+
{
308+
if let cached = fileHasInMemoryModificationsCache[uri] {
305309
return cached
306310
}
307-
let hasInMemoryModifications = documentManager.fileHasInMemoryModifications(url)
308-
fileHasInMemoryModificationsCache[url] = hasInMemoryModifications
311+
let hasInMemoryModifications = documentManager.fileHasInMemoryModifications(uri)
312+
fileHasInMemoryModificationsCache[uri] = hasInMemoryModifications
309313
return hasInMemoryModifications
310314
}
311315

312-
/// Returns true if the file at the given URL has a different content in the document manager than on-disk. This is
316+
/// Returns true if the file at the given URI has a different content in the document manager than on-disk. This is
313317
/// the case if the user made edits to the file but didn't save them yet.
314318
///
315319
/// - Important: This must only be called on an `IndexOutOfDateChecker` with a `checkLevel` of `inMemoryModifiedFiles`
316-
mutating func fileHasInMemoryModifications(_ url: URL) -> Bool {
320+
mutating func fileHasInMemoryModifications(_ uri: DocumentURI) -> Bool {
317321
switch checkLevel {
318322
case .inMemoryModifiedFiles(let documentManager):
319-
return fileHasInMemoryModifications(url, documentManager: documentManager)
323+
return fileHasInMemoryModifications(uri, documentManager: documentManager)
320324
case .modifiedFiles, .deletedFiles:
321325
logger.fault(
322326
"fileHasInMemoryModifications(at:) must only be called on an `IndexOutOfDateChecker` with check level .inMemoryModifiedFiles"
@@ -325,9 +329,12 @@ private struct IndexOutOfDateChecker {
325329
}
326330
}
327331

328-
private func modificationDateUncached(of url: URL) throws -> ModificationTime {
332+
private func modificationDateUncached(of uri: DocumentURI) throws -> ModificationTime {
329333
do {
330-
let attributes = try FileManager.default.attributesOfItem(atPath: url.resolvingSymlinksInPath().path)
334+
guard let fileURL = uri.fileURL else {
335+
return .fileDoesNotExist
336+
}
337+
let attributes = try FileManager.default.attributesOfItem(atPath: fileURL.resolvingSymlinksInPath().path)
331338
guard let modificationDate = attributes[FileAttributeKey.modificationDate] as? Date else {
332339
throw Error.fileAttributesDontHaveModificationDate
333340
}
@@ -337,21 +344,26 @@ private struct IndexOutOfDateChecker {
337344
}
338345
}
339346

340-
private mutating func modificationDate(of url: URL) throws -> ModificationTime {
341-
if let cached = modTimeCache[url] {
347+
private mutating func modificationDate(of uri: DocumentURI) throws -> ModificationTime {
348+
if let cached = modTimeCache[uri] {
342349
return cached
343350
}
344-
let modTime = try modificationDateUncached(of: url)
345-
modTimeCache[url] = modTime
351+
let modTime = try modificationDateUncached(of: uri)
352+
modTimeCache[uri] = modTime
346353
return modTime
347354
}
348355

349-
private mutating func fileExists(at url: URL) -> Bool {
350-
if let cached = fileExistsCache[url] {
356+
private mutating func fileExists(at uri: DocumentURI) -> Bool {
357+
if let cached = fileExistsCache[uri] {
351358
return cached
352359
}
353-
let fileExists = FileManager.default.fileExists(atPath: url.path)
354-
fileExistsCache[url] = fileExists
360+
let fileExists =
361+
if let fileUrl = uri.fileURL {
362+
FileManager.default.fileExists(atPath: fileUrl.path)
363+
} else {
364+
false
365+
}
366+
fileExistsCache[uri] = fileExists
355367
return fileExists
356368
}
357369
}

Sources/SemanticIndex/SemanticIndexManager.swift

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -260,13 +260,7 @@ public final actor SemanticIndexManager {
260260
await testHooks.buildGraphGenerationDidFinish?()
261261
let index = index.checked(for: .modifiedFiles)
262262
let filesToIndex = await self.buildSystemManager.sourceFiles().lazy.map(\.uri)
263-
.filter { uri in
264-
guard let url = uri.fileURL else {
265-
// The URI is not a file, so there's nothing we can index.
266-
return false
267-
}
268-
return !index.hasUpToDateUnit(for: url)
269-
}
263+
.filter { !index.hasUpToDateUnit(for: $0) }
270264
await scheduleBackgroundIndex(files: filesToIndex)
271265
generateBuildGraphTask = nil
272266
}

Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -206,11 +206,7 @@ public struct UpdateIndexStoreTaskDescription: IndexTaskDescription {
206206
// If we know that the file is up-to-date without having ot hit the index, do that because it's fastest.
207207
return
208208
}
209-
guard let sourceFileUrl = file.sourceFile.fileURL else {
210-
// The URI is not a file, so there's nothing we can index.
211-
return
212-
}
213-
guard !index.checked(for: .modifiedFiles).hasUpToDateUnit(for: sourceFileUrl, mainFile: file.mainFile.fileURL)
209+
guard !index.checked(for: .modifiedFiles).hasUpToDateUnit(for: file.sourceFile, mainFile: file.mainFile)
214210
else {
215211
logger.debug("Not indexing \(file.forLogging) because index has an up-to-date unit")
216212
// We consider a file's index up-to-date if we have any up-to-date unit. Changing build settings does not

Sources/SourceKitLSP/DocumentManager.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,12 @@ public final class DocumentManager: InMemoryDocumentManager, Sendable {
191191
}
192192
}
193193

194-
public func fileHasInMemoryModifications(_ url: URL) -> Bool {
195-
guard let document = try? latestSnapshot(DocumentURI(url)) else {
194+
public func fileHasInMemoryModifications(_ uri: DocumentURI) -> Bool {
195+
guard let document = try? latestSnapshot(uri), let fileURL = uri.fileURL else {
196196
return false
197197
}
198198

199-
guard let onDiskFileContents = try? String(contentsOf: url, encoding: .utf8) else {
199+
guard let onDiskFileContents = try? String(contentsOf: fileURL, encoding: .utf8) else {
200200
// If we can't read the file on disk, it can't match any on-disk state, so it's in-memory state
201201
return true
202202
}

Sources/SourceKitLSP/TestDiscovery.swift

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -204,12 +204,9 @@ extension SourceKitLSPServer {
204204
let index = workspace.index(checkedFor: .inMemoryModifiedFiles(documentManager))
205205

206206
let filesWithInMemoryState = documentManager.documents.keys.filter { uri in
207-
guard let url = uri.fileURL else {
208-
return true
209-
}
210207
// Use the index to check for in-memory modifications so we can re-use its cache. If no index exits, ask the
211208
// document manager directly.
212-
return index?.fileHasInMemoryModifications(url) ?? documentManager.fileHasInMemoryModifications(url)
209+
return index?.fileHasInMemoryModifications(uri) ?? documentManager.fileHasInMemoryModifications(uri)
213210
}
214211

215212
let testsFromFilesWithInMemoryState = await filesWithInMemoryState.concurrentMap { (uri) -> [AnnotatedTestItem] in
@@ -254,7 +251,7 @@ extension SourceKitLSPServer {
254251
// up-to-date. Include the tests from `testsFromFilesWithInMemoryState`.
255252
return nil
256253
}
257-
if let fileUrl = testItem.location.uri.fileURL, index?.hasUpToDateUnit(for: fileUrl) ?? false {
254+
if index?.hasUpToDateUnit(for: testItem.location.uri) ?? false {
258255
// We don't have a test for this file in the semantic index but an up-to-date unit file. This means that the
259256
// index is up-to-date and has more knowledge that identifies a `TestItem` as not actually being a test, eg.
260257
// because it starts with `test` but doesn't appear in a class inheriting from `XCTestCase`.
@@ -341,7 +338,7 @@ extension SourceKitLSPServer {
341338
}
342339
) + syntacticSwiftTestingTests
343340
}
344-
if let fileURL = mainFileUri.fileURL, index.hasUpToDateUnit(for: fileURL) {
341+
if index.hasUpToDateUnit(for: mainFileUri) {
345342
// The semantic index is up-to-date and doesn't contain any tests. We don't need to do a syntactic fallback for
346343
// XCTest. We do still need to return swift-testing tests which don't have a semantic index.
347344
return syntacticSwiftTestingTests

0 commit comments

Comments
 (0)