import { map , filter , includes } from "lodash" ; import { XMLElement } from "@xml-tools/ast" ; import { UI5Cardinality , UI5Class , UI5Interface , UI5SemanticModel , UI5Type , UI5Aggregation , } from "@ui5-language-assistant/semantic-model-types" ; import { findClassesMatchingType , getUI5ClassByXMLElement , ui5NodeToFQN , getUI5AggregationByXMLElement , splitQNameByNamespace , resolveXMLNSFromPrefix , } from "@ui5-language-assistant/logic-utils" ; import { UI5ClassesInXMLTagNameCompletion } from "../../../api" ; import { UI5ElementNameCompletionOptions } from "./index" ; const UNRESOLVED_PREFIX_URI = null ; const NOT_FOUND = null ; export function classesSuggestions ( opts ) { const classSuggestionContext = computeClassSuggestionContext ( { xmlElement : opts . element , model : opts . context , } ) ; if ( classSuggestionContext === NOT_FOUND ) { return [ ] ; } if ( classSuggestionContext . cardinality === "0..1" && classSuggestionContext . parentXMLTag !== undefined && classSuggestionContext . parentXMLTag . subElements . length > 1 ) { return [ ] ; } const classesMatchingType = findClassesMatchingType ( { type : classSuggestionContext . allowedType , model : opts . context , } ) ; const prefixParts = getPrefixParts ( opts . prefix , opts . element ) ; if ( prefixParts === UNRESOLVED_PREFIX_URI ) { return [ ] ; } const classesMatchingPrefix = filter ( classesMatchingType , ( _ ) => { if ( prefixParts . ns !== "" ) { return ( _ . parent !== undefined && ui5NodeToFQN ( _ . parent ) === prefixParts . ns && includes ( _ . name , prefixParts . base ) ) ; } else { return includes ( ui5NodeToFQN ( _ ) , prefixParts . base ) ; } } ) ; const concreteClassesMatchingPrefix = filter ( classesMatchingPrefix , ( _ ) => ! _ . abstract ) ; return map ( concreteClassesMatchingPrefix , ( _ ) => ( { type : "UI5ClassesInXMLTagName" , ui5Node : _ , astNode : opts . element , } ) ) ; } type classSuggestionContext = { allowedType : UI5Interface | UI5Class ; cardinality : UI5Cardinality ; parentXMLTag ? : XMLElement ; } ; function computeClassSuggestionContext ( { xmlElement , model , } ) { const parentXMLElement = xmlElement . parent ; if ( parentXMLElement . type === "XMLDocument" ) { return { allowedType : model . classes [ "sap.ui.core.Control" ] , cardinality : "0..1" , } ; } const parentUI5Class = getUI5ClassByXMLElement ( parentXMLElement , model ) ; if ( parentUI5Class !== undefined ) { return handleDefaultAggregationScenario ( { parentXMLElement , parentUI5Class , } ) ; } const parentUI5Aggregation = getUI5AggregationByXMLElement ( parentXMLElement , model ) ; if ( parentUI5Aggregation !== undefined ) { return handleInsideExplicitAggregationScenario ( { parentXMLElement , parentUI5Aggregation , } ) ; } return NOT_FOUND ; } function handleInsideExplicitAggregationScenario ( { parentXMLElement , parentUI5Aggregation , } ) { if ( isClassOrInterfaceType ( parentUI5Aggregation . type ) ) { return { allowedType : parentUI5Aggregation . type , cardinality : parentUI5Aggregation . cardinality , parentXMLTag : parentXMLElement , } ; } else { return NOT_FOUND ; } } function handleDefaultAggregationScenario ( { parentXMLElement , parentUI5Class , } ) { const defaultAggregation = parentUI5Class . defaultAggregation ; if ( defaultAggregation === undefined ) { return NOT_FOUND ; } if ( isClassOrInterfaceType ( defaultAggregation . type ) ) { return { allowedType : defaultAggregation . type , cardinality : defaultAggregation . cardinality , parentXMLTag : parentXMLElement , } ; } else { return NOT_FOUND ; } } function getPrefixParts ( originalPrefix , xmlElement ) { const { prefix , localName } = splitQNameByNamespace ( originalPrefix ?? "" ) ; let resolvedNS = "" ; if ( prefix !== undefined ) { resolvedNS = resolveXMLNSFromPrefix ( prefix , xmlElement ) ; if ( resolvedNS === undefined ) { return UNRESOLVED_PREFIX_URI ; } } return { ns : resolvedNS , base : localName } ; } function isClassOrInterfaceType ( type ) { return includes ( [ "UI5Class" , "UI5Interface" ] , type ?. kind ) ; }