File: CompletionSorting.swift

package info (click to toggle)
swiftlang 6.2.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,856,264 kB
  • sloc: cpp: 9,995,718; ansic: 2,234,019; asm: 1,092,167; python: 313,940; objc: 82,726; f90: 80,126; lisp: 38,373; pascal: 25,580; sh: 20,378; ml: 5,058; perl: 4,751; makefile: 4,725; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (84 lines) | stat: -rw-r--r-- 3,035 bytes parent folder | download
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))
  }
}