Skip to content

Fix deterministic workspace indexing and search#2178

Open
jkaunert wants to merge 1 commit into
CodeEditApp:mainfrom
jkaunert:fix/issue-1993-indexing-search-contracts
Open

Fix deterministic workspace indexing and search#2178
jkaunert wants to merge 1 commit into
CodeEditApp:mainfrom
jkaunert:fix/issue-1993-indexing-search-contracts

Conversation

@jkaunert

Copy link
Copy Markdown

Description

This PR makes workspace search indexing and search result publication deterministic.

It keeps the existing addProjectToIndex() startup behavior as fire-and-forget, but moves the actual indexing work into an awaitable indexProject() path. That lets tests and explicit re-indexing callers wait until the index has been flushed instead of polling indexStatus with timing loops.

The patch also replaces the search path's DispatchGroup plus nested Task completion handling with structured concurrency. search(_:) now awaits result evaluation before publishing final search results, so callers do not observe stale or partially populated state.

This is intentionally narrower than #1993. It does not replace the task notification system or introduce the broader activity manager refactor. The goal is to land the lifecycle contract fix first so indexing/search behavior is stable before any notification or activity API cleanup continues.

Main changes:

  • Add cancellable indexing task ownership to WorkspaceDocument.SearchState
  • Keep addProjectToIndex() non-blocking for startup
  • Add indexProject() async for deterministic indexing and tests
  • Publish indexing progress from main-actor helpers
  • Make search(_:) await result evaluation before setSearchResults()
  • Remove timing-based polling from workspace search/index tests

Related Issues

Checklist

  • I read and understood the contributing guide as well as the code of conduct
  • The issues this PR addresses are related to each other
  • My changes generate no new warnings
  • My code builds and runs on my machine
  • My changes are all related to the related issue above
  • I documented my code

Validation

Ran with XcodeBuildMCP on macOS using -skipPackagePluginValidation.

  • Focused tests: 22/22 passed
    • CodeEditTests/TaskNotificationHandlerTests
    • CodeEditTests/WorkspaceDocumentIndexTests
    • CodeEditTests/FindTests
    • CodeEditTests/FindAndReplaceTests
  • Full CodeEditTests: 140 passed, 0 failed, 2 skipped
  • git diff --check: clean

Note: the test run still reports an existing trailing-whitespace warning in CodeEdit/WorkspaceView.swift:90, which is outside this diff.

Screenshots

Not applicable. This PR changes workspace indexing/search lifecycle behavior and tests, not UI presentation.

Copilot AI review requested due to automatic review settings June 12, 2026 07:47

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR refactors workspace search indexing to use structured concurrency with cancellable tasks, and updates tests to use the new async indexing API.

Changes:

  • Introduced a cancellable indexingTask and new async indexProject() API, while keeping addProjectToIndex() as a non-blocking entry point.
  • Reworked indexing progress publishing into MainActor-isolated helper methods and added cancellation cleanup.
  • Simplified tests by awaiting indexing completion instead of polling with timeouts.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
CodeEdit/Features/Documents/WorkspaceDocument/WorkspaceDocument+SearchState.swift Adds indexingTask lifecycle management and cancels indexing on deinit.
CodeEdit/Features/Documents/WorkspaceDocument/WorkspaceDocument+Index.swift Refactors indexing into cancellable async flows; adds indexProject() and progress publishers.
CodeEdit/Features/Documents/WorkspaceDocument/WorkspaceDocument+Find.swift Switches result reset/search evaluation to structured concurrency and MainActor-safe updates.
CodeEditTests/Features/Documents/WorkspaceDocument+SearchState+IndexTests.swift Updates tests to await indexProject() and assert completion state.
CodeEditTests/Features/Documents/WorkspaceDocument+SearchState+FindTests.swift Updates tests to await indexProject() and assert completion state.
CodeEditTests/Features/Documents/WorkspaceDocument+SearchState+FindAndReplaceTests.swift Updates tests to await indexProject() and removes polling waits.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +22 to +25
indexingTask?.cancel()
indexingTask = Task { [weak self] in
await self?.indexProject(indexer: indexer, url: url)
}
Comment on lines +116 to 123
@MainActor
private func publishIndexingCancelled(id: String) {
let deleteInfo: [String: Any] = [
"id": id,
"action": "delete"
]
NotificationCenter.default.post(name: .taskNotification, object: nil, userInfo: deleteInfo)
}

@MainActor
final class FindAndReplaceTests: XCTestCase { // swiftlint:disable:this type_body_length
final class FindAndReplaceTests: XCTestCase {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants