UNPKG

@xml-tools/simple-schema

Version:
97 lines (91 loc) 3.18 kB
const { difference, map, filter, has, pickBy } = require("lodash"); const { DEFAULT_NS } = require("@xml-tools/ast"); // https://www.w3.org/TR/2009/REC-xml-names-20091208/#NT-PrefixedName const NAMESPACE_PATTERN = /^(?:([^:]*):)?([^:]*)$/; /** * * Note that the Element (XML/XSS) are of the parent node of the element * in which content assist was requested. * * @param {XMLElement} elementNode * @param {XSSElement} xssElement * * @returns {CompletionSuggestion[]} */ function elementNameCompletion(elementNode, xssElement, prefix = "") { const match = prefix.match(NAMESPACE_PATTERN); if (match === null) { return []; } // If there is no prefix, use the default namespace prefix const namespacePrefix = match[1] ? match[1] : DEFAULT_NS; const elementNamespaceUri = elementNode.namespaces[namespacePrefix]; const possibleElements = filter( xssElement.elements, (_) => has(_, "namespace") === false || (_.namespace && _.namespace === elementNamespaceUri) ); const possibleSuggestionsWithoutExistingSingular = applicableElements( xssElement.elements, elementNode.subElements, possibleElements ); const suggestions = map(possibleSuggestionsWithoutExistingSingular, (_) => { return { text: _, label: _, }; }); if (namespacePrefix === undefined || namespacePrefix === DEFAULT_NS) { // Can't really suggest anything for the `implicit` default namespace... const namespacesWithoutDefault = pickBy( elementNode.namespaces, (uri, prefix) => prefix !== DEFAULT_NS ); const applicableNamespaces = pickBy(namespacesWithoutDefault, (uri) => { const possibleElements = filter( xssElement.elements, (element) => has(element, "namespace") === true && element.namespace === uri ); const possibleSuggestionsWithoutExistingSingular = applicableElements( xssElement.elements, elementNode.subElements, possibleElements ); const namespaceHasApplicableElements = possibleSuggestionsWithoutExistingSingular.length > 0; return namespaceHasApplicableElements; }); const namespaceSuggestions = map(applicableNamespaces, (uri, prefix) => ({ text: prefix, label: prefix, commitCharacter: ":", isNamespace: true, })); return [...namespaceSuggestions, ...suggestions]; } return suggestions; } function applicableElements(xssElements, subElements, possibleElements) { const allPossibleSuggestions = map( possibleElements, (element) => element.name ); const notSingularElem = filter( xssElements, (element) => element.cardinality === "many" ); const notSingularElemNames = map(notSingularElem, (element) => element.name); const existingElemNames = map(subElements, (element) => element.name); const existingSingular = difference(existingElemNames, notSingularElemNames); const possibleSuggestionsWithoutExistingSingular = difference( allPossibleSuggestions, existingSingular ); return possibleSuggestionsWithoutExistingSingular; } module.exports = { elementNameCompletion: elementNameCompletion, };