UNPKG

alm

Version:

The best IDE for TypeScript

106 lines (105 loc) 5.15 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var ast = require("../../modules/astUtils"); var EOL = '\n'; function getIdentifierAndClassNames(error) { var errorText = error.messageText; if (typeof errorText !== 'string') { console.error('I have no idea what this is:', errorText); return undefined; } ; // see https://github.com/Microsoft/TypeScript/blob/6637f49209ceb5ed719573998381eab010fa48c9/src/compiler/diagnosticMessages.json#L842 var match = errorText.match(/Property \'(\w+)\' does not exist on type \'(\w+)\'./); // Happens when the type name is an alias. We can't refactor in this case anyways if (!match) return; var identifierName = match[1], className = match[2]; return { identifierName: identifierName, className: className }; } /** foo.a => a */ function getLastNameAfterDot(text) { return text.substr(text.lastIndexOf('.') + 1); } function getTypeStringForNode(node, typeChecker) { var type = typeChecker.getTypeAtLocation(node); /** Discoverd from review of `services.getQuickInfoAtPosition` */ return ts.displayPartsToString(ts.typeToDisplayParts(typeChecker, type)).replace(/\s+/g, ' '); } var AddClassMember = /** @class */ (function () { function AddClassMember() { this.key = AddClassMember.name; } AddClassMember.prototype.canProvideFix = function (info) { var relevantError = info.positionErrors.filter(function (x) { return x.code == ts.Diagnostics.Property_0_does_not_exist_on_type_1.code; })[0]; if (!relevantError) return; if (info.positionNode.kind !== ts.SyntaxKind.Identifier) return; // TODO: use type checker to see if item of `.` before hand is a class // But for now just run with it. var match = getIdentifierAndClassNames(relevantError); if (!match) return; var identifierName = match.identifierName, className = match.className; return { display: "Add " + identifierName + " to " + className }; }; AddClassMember.prototype.provideFix = function (info) { var relevantError = info.positionErrors.filter(function (x) { return x.code == ts.Diagnostics.Property_0_does_not_exist_on_type_1.code; })[0]; var identifier = info.positionNode; var identifierName = identifier.text; var className = getIdentifierAndClassNames(relevantError).className; // Get the type of the stuff on the right if its an assignment var typeString = 'any'; var parentOfParent = identifier.parent.parent; if (parentOfParent.kind == ts.SyntaxKind.BinaryExpression && parentOfParent.operatorToken.getText().trim() == '=') { var binaryExpression = parentOfParent; typeString = getTypeStringForNode(binaryExpression.right, info.typeChecker); } else if (parentOfParent.kind == ts.SyntaxKind.CallExpression) { var callExp = parentOfParent; var typeStringParts = ['(']; // Find the number of arguments var args_1 = []; callExp.arguments.forEach(function (arg) { var argName = (getLastNameAfterDot(arg.getText())); var argType = getTypeStringForNode(arg, info.typeChecker); args_1.push(argName + ": " + argType); }); typeStringParts.push(args_1.join(', ')); // TODO: infer the return type as well if the next parent is an assignment // Currently its `any` typeStringParts.push(') => any'); typeString = typeStringParts.join(''); } // Find the containing class declaration var memberTarget = ast.getNodeByKindAndName(info.program, ts.SyntaxKind.ClassDeclaration, className); if (!memberTarget) { // Find the containing interface declaration memberTarget = ast.getNodeByKindAndName(info.program, ts.SyntaxKind.InterfaceDeclaration, className); } if (!memberTarget) { return []; } // The following code will be same (and typesafe) for either class or interface var targetDeclaration = memberTarget; // Then the first brace var firstBrace = targetDeclaration.getChildren().filter(function (x) { return x.kind == ts.SyntaxKind.OpenBraceToken; })[0]; // And the correct indent var indentLength = info.service.getIndentationAtPosition(memberTarget.getSourceFile().fileName, firstBrace.end, info.project.configFile.project.formatCodeOptions); var indent = Array(indentLength + info.formatOptions.indentSize + 1).join(' '); // And add stuff after the first brace var refactoring = { span: { start: firstBrace.end, length: 0 }, newText: "" + EOL + indent + identifierName + ": " + typeString + ";", filePath: targetDeclaration.getSourceFile().fileName }; return [refactoring]; }; return AddClassMember; }()); exports.AddClassMember = AddClassMember;