ui5plugin-parser
Version:
292 lines (291 loc) • 15.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.InnerPropertiesStrategy = void 0;
const ParserPool_1 = require("../../../../parser/pool/ParserPool");
const CustomJSClass_1 = require("../../ui5class/js/CustomJSClass");
const FieldPropertyMethodGetterStrategy_1 = require("./abstraction/FieldPropertyMethodGetterStrategy");
const FieldsAndMethodForPositionBeforeCurrentStrategy_1 = require("./FieldsAndMethodForPositionBeforeCurrentStrategy");
class InnerPropertiesStrategy extends FieldPropertyMethodGetterStrategy_1.FieldPropertyMethodGetterStrategy {
constructor(syntaxAnalyser, parser) {
super(parser);
this.syntaxAnalyser = syntaxAnalyser;
}
getFieldsAndMethods(document, position) {
const fieldsAndMethods = this._acornGetPropertiesForParamsInCurrentPosition(document, position);
return fieldsAndMethods;
}
_acornGetPropertiesForParamsInCurrentPosition(document, position) {
let fieldsAndMethods;
const currentClassName = this.parser.fileReader.getClassNameFromPath(document.fileName);
if (currentClassName) {
const stack = this.getStackOfNodesForInnerParamsForPosition(currentClassName, position);
if (stack.length === 1 && stack[0].type === "NewExpression") {
fieldsAndMethods = this._getFieldsAndMethodsForNewExpression(stack[0], document, position);
}
else if (stack.length === 1 && stack[0].type === "CallExpression") {
fieldsAndMethods = this._getFieldsAndMethodsForCallExpression(stack[0], document, position);
}
}
return fieldsAndMethods;
}
_getFieldsAndMethodsForNewExpression(newExpression, document, position) {
let fieldsAndMethods;
const currentClassName = this.parser.fileReader.getClassNameFromPath(document.fileName);
if (position && currentClassName) {
const argument = this.syntaxAnalyser.findAcornNode(newExpression.arguments, position);
const indexOfArgument = newExpression.arguments.indexOf(argument);
if (argument && argument.type === "ObjectExpression") {
const positionBeforeCurrentStrategy = new FieldsAndMethodForPositionBeforeCurrentStrategy_1.FieldsAndMethodForPositionBeforeCurrentStrategy(this.syntaxAnalyser, this.parser);
const stack = positionBeforeCurrentStrategy.getStackOfNodesForPosition(currentClassName, newExpression.end + 1);
const classNameOfCurrentNewExpression = this.syntaxAnalyser.findClassNameForStack(stack, currentClassName);
if (classNameOfCurrentNewExpression) {
const node = this.parser.nodeDAO.findNode(classNameOfCurrentNewExpression);
const constructorParameters = node?.getMetadata()?.getRawMetadata()?.constructor?.parameters;
if (constructorParameters) {
const settings = constructorParameters.find((parameter) => parameter.name === "mSettings");
if (settings) {
const indexOfSettings = constructorParameters.indexOf(settings);
if (indexOfSettings === indexOfArgument) {
fieldsAndMethods = this._generatePropertyFieldsFor(classNameOfCurrentNewExpression);
}
}
}
}
}
}
return fieldsAndMethods;
}
_getFieldsAndMethodsForCallExpression(callExpression, document, position) {
let fieldsAndMethods;
const currentClassName = this.parser.fileReader.getClassNameFromPath(document.fileName);
if (currentClassName && position) {
const argument = this.syntaxAnalyser.findAcornNode(callExpression.arguments, position);
const indexOfArgument = callExpression.arguments.indexOf(argument);
if (argument?.type === "ObjectExpression") {
const positionBeforeCurrentStrategy = new FieldsAndMethodForPositionBeforeCurrentStrategy_1.FieldsAndMethodForPositionBeforeCurrentStrategy(this.syntaxAnalyser, this.parser);
const stack = positionBeforeCurrentStrategy.getStackOfNodesForPosition(currentClassName, callExpression.callee.end);
const classNameOfCurrentObjectExpression = this.syntaxAnalyser.findClassNameForStack(stack, currentClassName);
if (classNameOfCurrentObjectExpression) {
const methodName = callExpression.callee?.property?.name;
if (methodName) {
const UIClass = (this.parser.classFactory.getUIClass(classNameOfCurrentObjectExpression));
const UIMethod = UIClass.methods.find(method => method.name === methodName);
if (UIMethod?.acornParams) {
const acornParam = UIMethod.acornParams[indexOfArgument];
const fields = this._generateFieldsFromArgument(argument, position);
const objectForCompletionItems = this._getObjectFromObject(acornParam.customData, fields);
if (acornParam && typeof objectForCompletionItems === "object") {
fieldsAndMethods = {
className: acornParam.jsType,
methods: [],
fields: Object.keys(objectForCompletionItems).map(key => {
return {
description: key,
name: key,
type: typeof objectForCompletionItems[key] === "string"
? objectForCompletionItems[key]
: typeof objectForCompletionItems[key],
visibility: "public",
owner: "",
abstract: false,
static: false,
deprecated: false
};
})
};
}
}
}
}
}
else if (argument?.type === "Literal" && indexOfArgument === 0) {
const positionBeforeCurrentStrategy = new FieldsAndMethodForPositionBeforeCurrentStrategy_1.FieldsAndMethodForPositionBeforeCurrentStrategy(this.syntaxAnalyser, this.parser);
const className = positionBeforeCurrentStrategy.acornGetClassName(currentClassName, callExpression.callee.end);
if (className === "sap.ui.base.Event" && callExpression.callee?.property?.name === "getParameter") {
const eventHandlerData = this.syntaxAnalyser.getEventHandlerData(callExpression, currentClassName);
if (eventHandlerData) {
const parameters = this.syntaxAnalyser.getParametersOfTheEvent(eventHandlerData.eventName, eventHandlerData.className);
if (parameters) {
fieldsAndMethods = {
className: "generic",
methods: [],
fields: parameters.map(parameter => {
return {
name: parameter.name,
description: `${eventHandlerData.eventName} - ${parameter.name}: ${parameter.type}`,
type: parameter.type,
visibility: "public",
owner: "",
abstract: false,
static: false,
deprecated: false
};
})
};
}
}
}
else if (className && callExpression.callee?.property?.name === "getModel") {
let models = this._getManifestModels(document);
const classModels = this._getCurrentClassModels(currentClassName);
models.push(...classModels);
models = models.reduce((accumulator, model) => {
const modelAlreadyAdded = !!accumulator.find(modelInArray => modelInArray.name === model.name);
if (!modelAlreadyAdded) {
accumulator.push(model);
}
return accumulator;
}, []);
fieldsAndMethods = {
className: "generic",
methods: [],
fields: models.map(model => {
return {
name: model.name,
description: model.type,
type: "string",
visibility: "public",
owner: "",
abstract: false,
static: false,
deprecated: false
};
})
};
}
}
}
return fieldsAndMethods;
}
_getManifestModels(document) {
let models = [];
const fileName = document.fileName;
const currentClassName = fileName && this.parser.fileReader.getClassNameFromPath(fileName);
if (currentClassName) {
const manifest = ParserPool_1.default.getManifestForClass(currentClassName);
if (manifest && manifest.content["sap.ui5"]?.models) {
models = Object.keys(manifest.content["sap.ui5"]?.models).map(key => ({
type: manifest.content["sap.ui5"]?.models[key].type,
name: key
}));
}
}
return models;
}
_getCurrentClassModels(currentClassName) {
let models = [];
if (currentClassName) {
const UIClass = this.parser.classFactory.getUIClass(currentClassName);
if (UIClass instanceof CustomJSClass_1.CustomJSClass) {
const callExpressions = UIClass.methods.reduce((accumulator, UIMethod) => {
if (UIMethod.node) {
const callExpressions = this.syntaxAnalyser
.expandAllContent(UIMethod.node)
.filter((node) => node.type === "CallExpression");
accumulator.push(...callExpressions);
}
return accumulator;
}, []);
const setModelCallExpressions = callExpressions.filter((callExpression) => callExpression.callee?.property?.name === "setModel" &&
callExpression.arguments &&
callExpression.arguments[1]?.value);
models = setModelCallExpressions.map((callExpression) => {
const modelName = callExpression.arguments[1].value;
const modelClassName = this.syntaxAnalyser.getClassNameFromSingleAcornNode(callExpression.arguments[0], UIClass);
return {
type: modelClassName,
name: modelName
};
});
}
}
return models;
}
_generateFieldsFromArgument(argument, position) {
let fields = [];
if (position && argument.type === "ObjectExpression" && argument.properties) {
const property = this.syntaxAnalyser.findAcornNode(argument.properties, position);
if (property) {
fields.push(property.key.name);
if (property && property.value?.type === "ObjectExpression") {
fields = fields.concat(this._generateFieldsFromArgument(property.value, position));
}
}
}
return fields;
}
_getObjectFromObject(object, fields) {
let objectToReturn;
const field = fields.shift();
if (field) {
objectToReturn = object[field];
objectToReturn = this._getObjectFromObject(objectToReturn, fields);
}
else {
objectToReturn = object;
}
return objectToReturn;
}
_generatePropertyFieldsFor(className, fieldsAndMethods = {
className: "generic",
fields: [],
methods: []
}) {
const UIClass = this.parser.classFactory.getUIClass(className);
fieldsAndMethods.fields = fieldsAndMethods.fields.concat(UIClass.properties.map(property => ({
name: property.name,
type: property.type,
description: property.description,
visibility: property.visibility,
owner: "",
abstract: false,
static: false,
deprecated: false
})));
if (UIClass.parentClassNameDotNotation) {
this._generatePropertyFieldsFor(UIClass.parentClassNameDotNotation, fieldsAndMethods);
}
return fieldsAndMethods;
}
getStackOfNodesForInnerParamsForPosition(className, position, checkForLastPosition = false) {
const stack = [];
const UIClass = this.parser.classFactory.getUIClass(className);
if (UIClass instanceof CustomJSClass_1.CustomJSClass) {
const methodNode = UIClass.acornMethodsAndFields.find((node) => {
return node.start < position && node.end >= position;
})?.value;
if (methodNode) {
const methodBody = methodNode.body;
const content = methodBody && this.syntaxAnalyser.expandAllContent(methodBody);
const nodeWithCurrentPosition = content &&
this.syntaxAnalyser.findAcornNode(content.filter((node) => node.type === "CallExpression").reverse(), position);
if (nodeWithCurrentPosition) {
this._generateStackOfNodesForInnerPosition(nodeWithCurrentPosition, position, stack, checkForLastPosition);
}
}
}
return stack;
}
_generateStackOfNodesForInnerPosition(node, position, stack, checkForLastPosition = false) {
const nodeTypesToUnshift = [
"CallExpression",
"MemberExpression",
"ThisExpression",
"NewExpression",
"Identifier"
];
const positionIsCorrect = node.start < position && (checkForLastPosition ? node.end >= position : node.end > position);
if (node &&
positionIsCorrect &&
nodeTypesToUnshift.indexOf(node.type) > -1 &&
node.property?.name !== "✖" &&
node.property?.name !== "prototype") {
stack.unshift(node);
}
const innerNode = this.syntaxAnalyser.findInnerNode(node, position);
if (innerNode) {
this._generateStackOfNodesForInnerPosition(innerNode, position, stack, checkForLastPosition);
}
}
}
exports.InnerPropertiesStrategy = InnerPropertiesStrategy;