@xml-tools/simple-schema
Version:
XML Simple Schema
97 lines (91 loc) • 3.18 kB
JavaScript
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,
};