1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 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 CompletionScoring
import Foundation
struct CompletionSorting {
private let session: CompletionSession
// private let items: [ASTCompletionItem]
// private let filterCandidates: CandidateBatch
private let pattern: Pattern
struct Match {
let score: CompletionScore
let index: Int
}
init(
filterText: String,
in session: CompletionSession
) {
self.session = session
self.pattern = Pattern(text: filterText)
}
/// Invoke `callback` with the top `maxResults` results and their scores
/// The buffer passed to `callback` is only valid for the duration of `callback`.
/// Returns the return value of `callback`.
func withScoredAndFilter<T>(maxResults: Int, _ callback: (UnsafeBufferPointer<Match>) -> T) -> T {
var matches: UnsafeMutableBufferPointer<Match>
defer { matches.deinitializeAllAndDeallocate() }
if pattern.text.isEmpty {
matches = .allocate(capacity: session.items.count)
for (index, item) in session.items.enumerated() {
matches.initialize(
index: index,
to: Match(
score: CompletionScore(textComponent: 1, semanticComponent: item.semanticScore(in: session)),
index: index
)
)
}
} else {
let candidateMatches = pattern.scoredMatches(in: session.filterCandidates, precision: .fast)
matches = .allocate(capacity: candidateMatches.count)
for (index, match) in candidateMatches.enumerated() {
let semanticScore = session.items[match.candidateIndex].semanticScore(in: session)
matches.initialize(
index: index,
to: Match(
score: CompletionScore(textComponent: match.textScore, semanticComponent: semanticScore),
index: match.candidateIndex
)
)
}
}
"".withCString { emptyCString in
matches.selectTopKAndTruncate(min(maxResults, matches.count)) {
if $0.score != $1.score {
return $0.score > $1.score
} else {
// Secondary sort by name. This is important to do early since when the
// filter text is empty there will be many tied scores and we do not
// want non-deterministic results in top-level completions.
let lhs = session.items[$0.index].filterNameCString ?? emptyCString
let rhs = session.items[$1.index].filterNameCString ?? emptyCString
return strcmp(lhs, rhs) < 0
}
}
}
return callback(UnsafeBufferPointer(matches))
}
}
|