alm
Version:
The best IDE for TypeScript
106 lines (105 loc) • 5.15 kB
JavaScript
;
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;