Files
2025-10-31 14:11:11 -07:00

632 lines
25 KiB
Swift

//===----------------------------------------------------------------------===//
//
// 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 Csourcekitd
import Foundation
@_spi(SourceKitLSP) import SKLogging
import SourceKitD
import SwiftExtensions
/// A single code completion result returned from sourcekitd + additional information. This is effectively a wrapper
/// around `swiftide_api_completion_item_t` that caches the properties which have already been retrieved.
/// - Note: There can many be `ASTCompletionItem` instances (e.g. global completion has ~100k items), make sure to check
/// layout when adding new fields to ensure we're not wasting a bunch of space.
/// (i.e.,`heap --showInternalFragmentation process_name`)
final class ASTCompletionItem {
let impl: swiftide_api_completion_item_t
/// The string that should be used to match against what the user type.
var filterName: String {
return _filterName.cachedValueOrCompute {
filterNameCString != nil ? String(cString: filterNameCString!) : ""
}
}
let filterNameCString: UnsafePointer<CChar>?
private var _filterName: LazyValue<String> = .uninitialized
/// The label with which the item should be displayed in an IDE
func label(in session: CompletionSession) -> String {
return _label.cachedValueOrCompute {
var value: String?
session.sourcekitd.ideApi.completion_item_get_label(session.response, impl, session.options.annotateResults) {
value = String(cString: $0!)
}
return value!
}
}
private var _label: LazyValue<String> = .uninitialized
func sourceText(in session: CompletionSession) -> String {
return _sourceText.cachedValueOrCompute {
var value: String?
session.sourcekitd.ideApi.completion_item_get_source_text(session.response, impl) {
value = String(cString: $0!)
}
return value!
}
}
private var _sourceText: LazyValue<String> = .uninitialized
/// The type that the code completion item produces.
///
/// Eg. the type of a variable or the return type of a function. `nil` for completions that don't have a type, like
/// keywords.
func typeName(in session: CompletionSession) -> String? {
return _typeName.cachedValueOrCompute {
var value: String?
session.sourcekitd.ideApi.completion_item_get_type_name(session.response, impl, session.options.annotateResults) {
if let cstr = $0 {
value = String(cString: cstr)
}
}
return value
}
}
private var _typeName: LazyValue<String?> = .uninitialized
/// The module that defines the code completion item or `nil` if the item is not defined in a module, like a keyword.
func moduleName(in session: CompletionSession) -> String? {
return _moduleName.cachedValueOrCompute {
var value: String?
session.sourcekitd.ideApi.completion_item_get_module_name(session.response, impl) {
if let cstr = $0 {
value = String(cString: cstr)
} else {
value = nil
}
}
if value == "" {
return nil
}
return value
}
}
private var _moduleName: LazyValue<String?> = .uninitialized
func priorityBucket(in session: CompletionSession) -> CompletionItem.PriorityBucket {
return _priorityBucket.cachedValueOrCompute {
CompletionItem.PriorityBucket(self, in: session)
}
}
private var _priorityBucket: LazyValue<CompletionItem.PriorityBucket> = .uninitialized
let completionKind: CompletionContext.Kind
let index: UInt32
func semanticScore(in session: CompletionSession) -> Double {
return _semanticScore.cachedValueOrCompute {
let semanticClassification = semanticClassification(in: session)
self.semanticClassification = semanticClassification
return semanticClassification.score
}
}
private var _semanticScore: LazyValue<Double> = .uninitialized
private func semanticClassification(in session: CompletionSession) -> SemanticClassification {
var module = moduleName(in: session)
if let baseModule = module?.split(separator: ".", maxSplits: 1).first {
// `PopularityIndex` is keyed on base module names.
// For example: "AppKit.NSImage" -> "AppKit".
module = String(baseModule)
}
let popularity = session.popularity(
ofSymbol: filterName,
inModule: module
)
return SemanticClassification(
availability: availability(in: session),
completionKind: semanticScoreCompletionKind(in: session),
flair: flair(in: session),
moduleProximity: moduleProximity(in: session),
popularity: popularity ?? .none,
scopeProximity: scopeProximity(in: session),
structuralProximity: structuralProximity(in: session),
synchronicityCompatibility: synchronicityCompatibility(in: session),
typeCompatibility: typeCompatibility(in: session)
)
}
var semanticClassification: SemanticClassification? = nil
var kind: CompletionItem.ItemKind
func semanticContext(in session: CompletionSession) -> CompletionItem.SemanticContext {
.init(
swiftide_api_completion_semantic_context_t(session.sourcekitd.ideApi.completion_item_get_semantic_context(impl))
)
}
func typeRelation(in session: CompletionSession) -> CompletionItem.TypeRelation {
.init(swiftide_api_completion_type_relation_t(session.sourcekitd.ideApi.completion_item_get_type_relation(impl)))
}
func numBytesToErase(in session: CompletionSession) -> Int {
Int(session.sourcekitd.ideApi.completion_item_get_num_bytes_to_erase(impl))
}
func notRecommended(in session: CompletionSession) -> Bool {
session.sourcekitd.ideApi.completion_item_is_not_recommended(impl)
}
func notRecommendedReason(in session: CompletionSession) -> NotRecommendedReason? {
guard notRecommended(in: session) else {
return nil
}
return NotRecommendedReason(impl, sourcekitd: session.sourcekitd)
}
func isSystem(in session: CompletionSession) -> Bool { session.sourcekitd.ideApi.completion_item_is_system(impl) }
func hasDiagnostic(in session: CompletionSession) -> Bool {
session.sourcekitd.ideApi.completion_item_has_diagnostic(impl)
}
init(
_ cresult: swiftide_api_completion_item_t,
filterName: UnsafePointer<CChar>?,
completionKind: CompletionContext.Kind,
index: UInt32,
sourcekitd: SourceKitD
) {
self.impl = cresult
self.filterNameCString = filterName
self.completionKind = completionKind
self.index = index
self.kind = .init(
swiftide_api_completion_item_kind_t(sourcekitd.ideApi.completion_item_get_kind(impl)),
associatedKind: sourcekitd.ideApi.completion_item_get_associated_kind(impl)
)
}
enum NotRecommendedReason {
case softDeprecated
case deprecated
case redundantImport
case redundantImportImplicit
case invalidAsyncContext
case crossActorReference
case variableUsedInOwnDefinition
case nonAsyncAlternativeUsedInAsyncContext
init?(_ item: swiftide_api_completion_item_t, sourcekitd: SourceKitD) {
let rawReason = sourcekitd.ideApi.completion_item_not_recommended_reason(item)
switch swiftide_api_completion_not_recommended_reason_t(rawReason) {
case SWIFTIDE_COMPLETION_NOT_RECOMMENDED_NONE:
return nil
case SWIFTIDE_COMPLETION_NOT_RECOMMENDED_REDUNDANT_IMPORT:
self = .redundantImport
case SWIFTIDE_COMPLETION_NOT_RECOMMENDED_DEPRECATED:
self = .deprecated
case SWIFTIDE_COMPLETION_NOT_RECOMMENDED_INVALID_ASYNC_CONTEXT:
self = .invalidAsyncContext
case SWIFTIDE_COMPLETION_NOT_RECOMMENDED_CROSS_ACTOR_REFERENCE:
self = .crossActorReference
case SWIFTIDE_COMPLETION_NOT_RECOMMENDED_VARIABLE_USED_IN_OWN_DEFINITION:
self = .variableUsedInOwnDefinition
case SWIFTIDE_COMPLETION_NOT_RECOMMENDED_SOFTDEPRECATED:
self = .softDeprecated
case SWIFTIDE_COMPLETION_NOT_RECOMMENDED_REDUNDANT_IMPORT_INDIRECT:
self = .redundantImportImplicit
case SWIFTIDE_COMPLETION_NOT_RECOMMENDED_NON_ASYNC_ALTERNATIVE_USED_IN_ASYNC_CONTEXT:
self = .nonAsyncAlternativeUsedInAsyncContext
default:
return nil
}
}
}
}
extension ASTCompletionItem {
private func semanticScoreCompletionKind(in session: CompletionSession) -> CompletionKind {
if session.sourcekitd.ideApi.completion_item_get_flair(impl) & SWIFTIDE_COMPLETION_FLAIR_ARGUMENTLABELS.rawValue
!= 0
{
return .argumentLabels
}
switch kind {
case .module:
return .module
case .class, .actor, .struct, .enum, .protocol, .associatedType, .typeAlias, .genericTypeParam, .precedenceGroup:
return .type
case .enumElement:
return .enumCase
case .constructor:
return .initializer
case .destructor:
// FIXME: add a "deinit" kind.
return .function
case .subscript:
// FIXME: add a "subscript" kind.
return .function
case .staticMethod, .instanceMethod, .freeFunction:
return .function
case .operator, .prefixOperatorFunction, .postfixOperatorFunction, .infixOperatorFunction:
// FIXME: add an "operator kind".
return .other
case .staticVar, .instanceVar, .localVar, .globalVar:
return .variable
case .keyword:
return .keyword
case .literal:
// FIXME: add a "literal" kind?
return .other
case .pattern:
// FIXME: figure out a kind for this.
return .other
case .macro:
// FIXME: add a "macro" kind?
return .type
case .unknown:
return .unknown
}
}
private func flair(in session: CompletionSession) -> Flair {
var result: Flair = []
let skFlair = session.sourcekitd.ideApi.completion_item_get_flair(impl)
if skFlair & SWIFTIDE_COMPLETION_FLAIR_EXPRESSIONSPECIFIC.rawValue != 0 {
result.insert(.oldExpressionSpecific_pleaseAddSpecificCaseToThisEnum)
}
if skFlair & SWIFTIDE_COMPLETION_FLAIR_SUPERCHAIN.rawValue != 0 {
result.insert(.chainedCallToSuper)
}
if skFlair & SWIFTIDE_COMPLETION_FLAIR_COMMONKEYWORDATCURRENTPOSITION.rawValue != 0 {
result.insert(.commonKeywordAtCurrentPosition)
}
if skFlair & SWIFTIDE_COMPLETION_FLAIR_RAREKEYWORDATCURRENTPOSITION.rawValue != 0 {
result.insert(.rareKeywordAtCurrentPosition)
}
if skFlair & SWIFTIDE_COMPLETION_FLAIR_RARETYPEATCURRENTPOSITION.rawValue != 0 {
result.insert(.rareKeywordAtCurrentPosition)
}
if skFlair & SWIFTIDE_COMPLETION_FLAIR_EXPRESSIONATNONSCRIPTORMAINFILESCOPE.rawValue != 0 {
result.insert(.expressionAtNonScriptOrMainFileScope)
}
return result
}
private func moduleProximity(in session: CompletionSession) -> ModuleProximity {
switch semanticContext(in: session) {
case .none:
return .inapplicable
case .local, .currentNominal, .outsideNominal:
return .imported(distance: 0)
case .super:
// FIXME: we don't know whether the super class is from this module or another.
return .unspecified
case .currentModule:
return .imported(distance: 0)
case .otherModule:
let depth = session.sourcekitd.ideApi.completion_item_import_depth(session.response, self.impl)
if depth == ~0 {
return .unknown
} else {
return .imported(distance: Int(depth))
}
}
}
private func scopeProximity(in session: CompletionSession) -> ScopeProximity {
switch semanticContext(in: session) {
case .none:
return .inapplicable
case .local:
return .local
case .currentNominal:
return .container
case .super:
return .inheritedContainer
case .outsideNominal:
return .outerContainer
case .currentModule, .otherModule:
return .global
}
}
private func structuralProximity(in session: CompletionSession) -> StructuralProximity {
switch kind {
case .keyword, .literal:
return .inapplicable
default:
return isSystem(in: session) ? .sdk : .project(fileSystemHops: nil)
}
}
func synchronicityCompatibility(in session: CompletionSession) -> SynchronicityCompatibility {
return notRecommendedReason(in: session) == .invalidAsyncContext ? .incompatible : .compatible
}
func typeCompatibility(in session: CompletionSession) -> TypeCompatibility {
switch typeRelation(in: session) {
case .identical: return .compatible
case .convertible: return .compatible
case .notApplicable: return .inapplicable
case .unrelated: return .unrelated
// Note: currently `unknown` in sourcekit usually means there is no context (e.g. statement level), which is
// equivalent to `inapplicable`. For now, map it that way to avoid spurious penalties.
case .unknown: return .inapplicable
case .invalid: return .invalid
}
}
func availability(in session: CompletionSession) -> Availability {
switch notRecommendedReason(in: session) {
case .deprecated:
return .deprecated
case .softDeprecated:
return .softDeprecated
case .invalidAsyncContext, .crossActorReference:
return .available
case .redundantImport, .variableUsedInOwnDefinition:
return .softDeprecated
case .redundantImportImplicit:
return .available
case .nonAsyncAlternativeUsedInAsyncContext:
return .softDeprecated
case nil:
return .available
}
}
}
extension CompletionItem {
init(
_ astItem: ASTCompletionItem,
score: CompletionScore,
in session: CompletionSession,
completionReplaceRange: Range<Position>,
groupID: (_ baseName: String) -> Int
) {
self.label = astItem.label(in: session)
self.filterText = astItem.filterName
self.module = astItem.moduleName(in: session)
self.typeName = astItem.typeName(in: session)
var editRange = completionReplaceRange
if astItem.numBytesToErase(in: session) > 0 {
let newCol = editRange.lowerBound.utf8Column - astItem.numBytesToErase(in: session)
if newCol >= 1 {
editRange = Position(line: editRange.lowerBound.line, utf8Column: newCol)..<editRange.upperBound
} else {
session.logger.error("num_bytes_to_erase crosses line boundary. Resetting num_bytes_to_erase to 0.")
}
}
self.textEdit = TextEdit(range: editRange, newText: astItem.sourceText(in: session))
self.kind = astItem.kind
self.isSystem = astItem.isSystem(in: session)
self.textMatchScore = score.textComponent
self.priorityBucket = astItem.priorityBucket(in: session)
self.semanticScore = score.semanticComponent
self.semanticClassification = astItem.semanticClassification
self.id = Identifier(index: astItem.index)
self.hasDiagnostic = astItem.hasDiagnostic(in: session)
let needsGroupID =
switch kind {
case .staticVar, .instanceVar, .localVar, .globalVar:
// We don't want to group variables with functions that have an equal base name.
false
case .keyword:
false
default:
true
}
// Set groupId using the name before '('.
// This allows top-level completions to be grouped together
// (including the actual type completion).
// For example:
// ```
// MyClass
// MyClass(test:)
// MyClass(other:)
// ```
if needsGroupID {
var baseName = filterText
if let parenIdx = baseName.firstIndex(of: "(") {
baseName = String(baseName[..<parenIdx])
}
self.groupID = groupID(baseName)
} else {
self.groupID = nil
}
}
}
extension CompletionItem.ItemKind {
init(_ ckind: swiftide_api_completion_item_kind_t, associatedKind: UInt32) {
switch ckind {
case SWIFTIDE_COMPLETION_ITEM_KIND_DECLARATION:
switch swiftide_api_completion_item_decl_kind_t(associatedKind) {
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_MODULE: self = .module
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_CLASS: self = .class
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_ACTOR: self = .actor
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_STRUCT: self = .struct
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_ENUM: self = .enum
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_ENUMELEMENT: self = .enumElement
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_PROTOCOL: self = .protocol
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_ASSOCIATEDTYPE: self = .associatedType
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_TYPEALIAS: self = .typeAlias
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_GENERICTYPEPARAM: self = .genericTypeParam
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_CONSTRUCTOR: self = .constructor
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_DESTRUCTOR: self = .destructor
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_SUBSCRIPT: self = .subscript
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_STATICMETHOD: self = .staticMethod
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_INSTANCEMETHOD: self = .instanceMethod
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_PREFIXOPERATORFUNCTION: self = .prefixOperatorFunction
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_POSTFIXOPERATORFUNCTION: self = .postfixOperatorFunction
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_INFIXOPERATORFUNCTION: self = .infixOperatorFunction
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_FREEFUNCTION: self = .freeFunction
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_STATICVAR: self = .staticVar
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_INSTANCEVAR: self = .instanceVar
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_LOCALVAR: self = .localVar
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_GLOBALVAR: self = .globalVar
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_PRECEDENCEGROUP: self = .precedenceGroup
case SWIFTIDE_COMPLETION_ITEM_DECL_KIND_MACRO: self = .macro
default: self = .unknown
}
case SWIFTIDE_COMPLETION_ITEM_KIND_KEYWORD:
self = .keyword
case SWIFTIDE_COMPLETION_ITEM_KIND_PATTERN:
self = .pattern
case SWIFTIDE_COMPLETION_ITEM_KIND_LITERAL:
self = .literal
case SWIFTIDE_COMPLETION_ITEM_KIND_BUILTINOPERATOR:
self = .operator
default: self = .unknown
}
}
}
extension CompletionContext.Kind {
init(_ ckind: swiftide_api_completion_kind_t) {
switch ckind {
case SWIFTIDE_COMPLETION_KIND_NONE: self = .none
case SWIFTIDE_COMPLETION_KIND_IMPORT: self = .import
case SWIFTIDE_COMPLETION_KIND_UNRESOLVEDMEMBER: self = .unresolvedMember
case SWIFTIDE_COMPLETION_KIND_DOTEXPR: self = .dotExpr
case SWIFTIDE_COMPLETION_KIND_STMTOREXPR: self = .stmtOrExpr
case SWIFTIDE_COMPLETION_KIND_POSTFIXEXPRBEGINNING: self = .postfixExprBeginning
case SWIFTIDE_COMPLETION_KIND_POSTFIXEXPR: self = .postfixExpr
case SWIFTIDE_COMPLETION_KIND_POSTFIXEXPRPAREN: self = .postfixExprParen
case SWIFTIDE_COMPLETION_KIND_KEYPATHEXPROBJC: self = .keyPathExprObjC
case SWIFTIDE_COMPLETION_KIND_KEYPATHEXPRSWIFT: self = .keyPathExprSwift
case SWIFTIDE_COMPLETION_KIND_TYPEDECLRESULTBEGINNING: self = .typeDeclResultBeginning
case SWIFTIDE_COMPLETION_KIND_TYPESIMPLEBEGINNING: self = .typeSimpleBeginning
case SWIFTIDE_COMPLETION_KIND_TYPEIDENTIFIERWITHDOT: self = .typeIdentifierWithDot
case SWIFTIDE_COMPLETION_KIND_TYPEIDENTIFIERWITHOUTDOT: self = .typeIdentifierWithoutDot
case SWIFTIDE_COMPLETION_KIND_CASESTMTKEYWORD: self = .caseStmtKeyword
case SWIFTIDE_COMPLETION_KIND_CASESTMTBEGINNING: self = .caseStmtBeginning
case SWIFTIDE_COMPLETION_KIND_NOMINALMEMBERBEGINNING: self = .nominalMemberBeginning
case SWIFTIDE_COMPLETION_KIND_ACCESSORBEGINNING: self = .accessorBeginning
case SWIFTIDE_COMPLETION_KIND_ATTRIBUTEBEGIN: self = .attributeBegin
case SWIFTIDE_COMPLETION_KIND_ATTRIBUTEDECLPAREN: self = .attributeDeclParen
case SWIFTIDE_COMPLETION_KIND_POUNDAVAILABLEPLATFORM: self = .poundAvailablePlatform
case SWIFTIDE_COMPLETION_KIND_CALLARG: self = .callArg
case SWIFTIDE_COMPLETION_KIND_LABELEDTRAILINGCLOSURE: self = .labeledTrailingClosure
case SWIFTIDE_COMPLETION_KIND_RETURNSTMTEXPR: self = .returnStmtExpr
case SWIFTIDE_COMPLETION_KIND_YIELDSTMTEXPR: self = .yieldStmtExpr
case SWIFTIDE_COMPLETION_KIND_FOREACHSEQUENCE: self = .forEachSequence
case SWIFTIDE_COMPLETION_KIND_AFTERPOUNDEXPR: self = .afterPoundExpr
case SWIFTIDE_COMPLETION_KIND_AFTERPOUNDDIRECTIVE: self = .afterPoundDirective
case SWIFTIDE_COMPLETION_KIND_PLATFORMCONDITON: self = .platformConditon
case SWIFTIDE_COMPLETION_KIND_AFTERIFSTMTELSE: self = .afterIfStmtElse
case SWIFTIDE_COMPLETION_KIND_GENERICREQUIREMENT: self = .genericRequirement
case SWIFTIDE_COMPLETION_KIND_PRECEDENCEGROUP: self = .precedenceGroup
case SWIFTIDE_COMPLETION_KIND_STMTLABEL: self = .stmtLabel
case SWIFTIDE_COMPLETION_KIND_EFFECTSSPECIFIER: self = .effectsSpecifier
case SWIFTIDE_COMPLETION_KIND_FOREACHPATTERNBEGINNING: self = .forEachPatternBeginning
case SWIFTIDE_COMPLETION_KIND_TYPEATTRBEGINNING: self = .typeAttrBeginning
case SWIFTIDE_COMPLETION_KIND_OPTIONALBINDING: self = .optionalBinding
case SWIFTIDE_COMPLETION_KIND_FOREACHKWIN: self = .forEachKeywordIn
case SWIFTIDE_COMPLETION_KIND_THENSTMTEXPR: self = .thenStmtExpr
default: self = .none
}
}
}
extension CompletionItem.TypeRelation {
init(_ crelation: swiftide_api_completion_type_relation_t) {
switch crelation {
case SWIFTIDE_COMPLETION_TYPE_RELATION_NOTAPPLICABLE: self = .notApplicable
case SWIFTIDE_COMPLETION_TYPE_RELATION_UNKNOWN: self = .unknown
case SWIFTIDE_COMPLETION_TYPE_RELATION_UNRELATED: self = .unrelated
case SWIFTIDE_COMPLETION_TYPE_RELATION_INVALID: self = .invalid
case SWIFTIDE_COMPLETION_TYPE_RELATION_CONVERTIBLE: self = .convertible
case SWIFTIDE_COMPLETION_TYPE_RELATION_IDENTICAL: self = .identical
default: self = .unknown
}
}
}
extension CompletionItem.SemanticContext {
init(_ ccontext: swiftide_api_completion_semantic_context_t) {
switch ccontext {
case SWIFTIDE_COMPLETION_SEMANTIC_CONTEXT_NONE: self = .none
case SWIFTIDE_COMPLETION_SEMANTIC_CONTEXT_LOCAL: self = .local
case SWIFTIDE_COMPLETION_SEMANTIC_CONTEXT_CURRENTNOMINAL: self = .currentNominal
case SWIFTIDE_COMPLETION_SEMANTIC_CONTEXT_SUPER: self = .super
case SWIFTIDE_COMPLETION_SEMANTIC_CONTEXT_OUTSIDENOMINAL: self = .outsideNominal
case SWIFTIDE_COMPLETION_SEMANTIC_CONTEXT_CURRENTMODULE: self = .currentModule
case SWIFTIDE_COMPLETION_SEMANTIC_CONTEXT_OTHERMODULE: self = .otherModule
default: self = .none
}
}
}
extension CompletionItem.PriorityBucket {
init(_ item: ASTCompletionItem, in session: CompletionSession) {
if item.completionKind == .unresolvedMember {
switch item.kind {
case .enum:
self = .unresolvedMember_EnumElement
case .class, .actor, .staticVar:
self = .unresolvedMember_Var
case .staticMethod:
self = .unresolvedMember_Func
case .constructor:
self = .unresolvedMember_Constructor
default:
self = .unresolvedMember_Other
}
} else if item.kind == .constructor {
self = .constructor
} else if item.typeRelation(in: session) == .invalid {
self = .invalidTypeMatch
} else {
let skFlair = session.sourcekitd.ideApi.completion_item_get_flair(item.impl)
if skFlair & SWIFTIDE_COMPLETION_FLAIR_EXPRESSIONSPECIFIC.rawValue != 0
|| skFlair & SWIFTIDE_COMPLETION_FLAIR_SUPERCHAIN.rawValue != 0
{
self = .exprSpecific
return
}
// let typeRelation = item.typeRelation(in: CompletionSession)
let typeMatchPriorityBoost =
switch item.typeRelation(in: session) {
case .identical, .convertible: item.kind != .globalVar && item.kind != .keyword
default: false
}
switch (typeMatchPriorityBoost, item.semanticContext(in: session)) {
case (false, .none):
self = .noContext_TypeMismatch
case (false, .otherModule):
self = .otherModule_TypeMismatch
case (false, .currentModule):
self = .thisModule_TypeMismatch
case (false, .super):
self = .superClass_TypeMismatch
case (false, .currentNominal):
self = .thisClass_TypeMismatch
case (false, .local):
self = .local_TypeMismatch
case (false, .outsideNominal):
self = .otherClass_TypeMismatch
case (true, .none):
self = .noContext_TypeMatch
case (true, .otherModule):
self = .otherClass_TypeMatch
case (true, .currentModule):
self = .thisModule_TypeMatch
case (true, .super):
self = .superClass_TypeMatch
case (true, .currentNominal):
self = .thisClass_TypeMatch
case (true, .local):
self = .local_TypeMatch
case (true, .outsideNominal):
self = .otherClass_TypeMatch
}
}
}
}