Files
sourcekit-lsp/Sources/SemanticIndex/IndexStatusManager.swift
Alex Hoppen e295a4e95a Split up-to-date status tracking and index progress tracking
We were mixing the up-to-date status and in-progress status of an index task in `SemanticIndexManager`. This meant that a single `QueuedTask` in the task scheduler could be needed for eg. both preparation for editor functionality in a file of that target and to re-index a file in that target. This dual ownership made it unclear, which caller would be entitled to cancel the task. Furthermore, we needed to duplicate some logic from the preparation task dependencies in `SemanticIndexManager.prepare`.

To simplify things:
- Split the up-to-date status and the in-progress status into two different data structures
- Make the caller of `prepare` and `scheduleIndex` responsible for cancellation of the task it has scheduled. `TaskScheduler` might receive more scheduled tasks this way but the additional tasks should all be no-ops because the status is known to be up-to-date when they execute.
2024-05-20 21:01:40 -07:00

71 lines
2.1 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 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 Foundation
import SKCore
/// Keeps track of whether an item (a target or file to index) is up-to-date.
actor IndexUpToDateStatusManager<Item: Hashable> {
private enum Status {
/// The item is up-to-date.
case upToDate
/// The target or file has been marked out-of-date at the given date.
///
/// Keeping track of the date is necessary so that we don't mark a target as up-to-date if we have the following
/// ordering of events:
/// - Preparation started
/// - Target marked out of date
/// - Preparation finished
case outOfDate(Date)
}
private var status: [Item: Status] = [:]
/// Mark the target or file as up-to-date from a preparation/update-indexstore operation started at
/// `updateOperationStartDate`.
///
/// See comment on `Status.outOfDate` why `updateOperationStartDate` needs to be passed.
func markUpToDate(_ items: [Item], updateOperationStartDate: Date) {
for item in items {
switch status[item] {
case .upToDate:
break
case .outOfDate(let markedOutOfDate):
if markedOutOfDate < updateOperationStartDate {
status[item] = .upToDate
}
case nil:
status[item] = .upToDate
}
}
}
func markOutOfDate(_ items: some Collection<Item>) {
let date = Date()
for item in items {
status[item] = .outOfDate(date)
}
}
func markAllOutOfDate() {
markOutOfDate(status.keys)
}
func isUpToDate(_ item: Item) -> Bool {
if case .upToDate = status[item] {
return true
}
return false
}
}