@@ -1359,60 +1359,139 @@ namespace ts {
13591359 } ;
13601360 }
13611361
1362- /*
1363- const classMembers: TypeElement[]; // TODO: this
1364- const implementedInterfaceMembers: TypeElement[] = [];
1365- const parentAbstractMembers: TypeElement[] = []
1366- const missingMembers: TypeElement[] = [];
1367- // const typeWiththis = checker.getTypeWithThisArgument(classType);
1368- // const staticType = <ObjectType>checker.getTypeOfSymbol(symbol);
1369- // const classType = <InterfaceType>checker.getTypeAtLocation(classDecl);
1370- */
1371-
1372- export function getUnimplementedMemberChanges ( classDecl : ClassDeclaration , checker : TypeChecker ) : TextChange [ ] {
1362+ /*
1363+ const classMembers: TypeElement[]; // TODO: this
1364+ const implementedInterfaceMembers: TypeElement[] = [];
1365+ const parentAbstractMembers: TypeElement[] = []
1366+ const missingMembers: TypeElement[] = [];
1367+ // const typeWiththis = checker.getTypeWithThisArgument(classType);
1368+ // const staticType = <ObjectType>checker.getTypeOfSymbol(symbol);
1369+ // const classType = <InterfaceType>checker.getTypeAtLocation(classDecl);
1370+ */
1371+
1372+ // TODO: (arozga) Get changes for interface as well.
1373+ // const implementedTypeNodes = getClassImplementsHeritageClauseElements(classDecl);
1374+ export function getMissingAbstractMemberInsertion ( classDecl : ClassDeclaration , checker : TypeChecker , newlineChar : string ) : string {
13731375 const baseTypeNode : ExpressionWithTypeArguments = getClassExtendsHeritageClauseElement ( classDecl ) ;
13741376
1375- if ( ! baseTypeNode )
1376- {
1377- return [ ] ;
1377+ if ( ! baseTypeNode ) {
1378+ return "" ; // TODO: (arozga) undefined?
13781379 }
1379- const classSymbol = checker . getSymbolAtLocation ( classDecl ) ;
1380- const classType = < InterfaceType > checker . getDeclaredTypeOfSymbol ( classSymbol ) ;
1381-
1382- const baseTypes = checker . getBaseTypes ( classType ) ;
1383- Debug . assert ( baseTypes . length === 1 ) ;
1384- const baseType = baseTypes [ 0 ] ;
1380+ const classSymbol = checker . getSymbolOfNode ( classDecl ) ;
1381+ // TODO: (arozga) Should this be getTypeOfSymbol?
1382+ // We want the once that gets the members. I think that's the instance, so we want typeofsymbol.
1383+ const classType = < InterfaceType > checker . getTypeOfSymbol ( classSymbol ) ;
1384+
1385+ const baseTypes = checker . getBaseTypes ( classType ) ;
1386+ Debug . assert ( baseTypes . length === 1 ) ;
1387+ const baseType = baseTypes [ 0 ] ;
1388+
1389+ // TODO: (arozga) Does this give us the correct instantiations for generics?
1390+ // TODO: (arozga) If not, how do we get them?
1391+ const resolvedClassType = checker . resolveStructuredTypeMembers ( classType ) ;
1392+ const resolvedBaseType = checker . resolveStructuredTypeMembers ( baseType ) ;
13851393
1386- const resolvedClassType = checker . resolveStructuredTypeMembers ( classType ) ;
1387- const resolvedBaseType = checker . resolveStructuredTypeMembers ( baseType ) ;
1394+ // TODO: (arozga) handle private members as well.
1395+ const missingMembers = filterMissingMembers ( filterAbstract ( resolvedBaseType . members ) , resolvedClassType . members ) ;
13881396
1389- const missingMembers = filterMissingMembers ( resolvedClassType . members , resolvedBaseType . members ) ;
1390- return insertionsForMembers ( missingMembers ) ;
1397+ return insertionsForMembers ( missingMembers , newlineChar ) ;
1398+ }
1399+
1400+ function filterSymbolMapByDecl ( symbolMap : Map < Symbol > , pred : ( decl : Declaration ) => boolean ) : Map < Symbol > {
1401+ let result = createMap < Symbol > ( ) ;
1402+ for ( const key in symbolMap ) {
1403+ const decl = symbolMap [ key ] . getDeclarations ( ) ;
1404+ Debug . assert ( ! ! ( decl && decl . length ) ) ;
1405+ if ( pred ( decl [ 0 ] ) ) {
1406+ result [ key ] = symbolMap [ key ] ;
1407+ }
13911408 }
1409+ return result ;
1410+ }
1411+
1412+ function filterAbstract ( symbolMap : Map < Symbol > ) {
1413+ return filterSymbolMapByDecl ( symbolMap , decl => ! ! ( getModifierFlags ( decl ) & ModifierFlags . Abstract ) ) ;
1414+ }
13921415
1393- // TODO: (arozga) Get changes for interface as well.
1394- // const implementedTypeNodes = getClassImplementsHeritageClauseElements(classDecl );
1416+ function filterNonPrivate ( symbolMap : Map < Symbol > ) {
1417+ return filterSymbolMapByDecl ( symbolMap , decl => ! ( getModifierFlags ( decl ) & ModifierFlags . Private ) ) ;
13951418 }
13961419
1397- function insertionsForMembers ( symbols : Symbol [ ] ) : TextChange [ ] {
1398- let changes : TextChange [ ] = [ ] ;
1420+ function insertionsForMembers ( symbols : Symbol [ ] , newlineChar : string ) : string {
1421+ let insertion = "" ;
13991422 for ( const symbol of symbols ) {
1400- const decl = getdeclaration
1401- switch ( member . kind ) {
1402- case SyntaxKind . PropertySignature :
1403- case SyntaxKind . PropertyDeclaration :
1404- break ;
1405- case SyntaxKind . MethodSignature :
1406- case SyntaxKind . MethodDeclaration :
1407- break ;
1408- default :
1409- break ;
1423+ const decls = symbol . getDeclarations ( ) ;
1424+ if ( ! ( decls && decls . length ) ) {
1425+ return "" ;
1426+ }
1427+ insertion = insertion . concat ( getInsertion ( decls [ 0 ] , newlineChar ) ) ;
1428+ }
1429+ return insertion ;
1430+ }
1431+
1432+ const stubMethodBody = "{\nthrow new Error('Method not Implemented');\n}" ;
1433+ const functionPrefix = "function " ;
1434+
1435+ // TODO: (arozga) needs a re-write that takes the symbol and type (with type parameter instantiations)
1436+ // and figures out how to print it.
1437+ function getInsertion ( decl : Declaration , newlineChar : string ) : string {
1438+ let insertion = "" ;
1439+ switch ( decl . kind ) {
1440+ case SyntaxKind . PropertySignature :
1441+ case SyntaxKind . PropertyDeclaration :
1442+ insertion = decl . getText ( ) ;
1443+
1444+ // TODO: (arozga) need to remove trailing comma
1445+ if ( insertion . length && insertion . charAt ( insertion . length - 1 ) !== ";" ) {
1446+ insertion += ";" ;
1447+ }
1448+ return insertion ;
1449+ case SyntaxKind . MethodSignature :
1450+ case SyntaxKind . MethodDeclaration :
1451+ const method = decl as MethodSignature | MethodDeclaration ;
1452+ // TODO: (arozga) figure out how to do proper instantiation of generic values based on heritage clause.
1453+ const typeParameters = method . typeParameters && method . typeParameters . length > 0 ?
1454+ getCommaSeparatedString ( method . typeParameters . map ( param => param . getText ( ) ) , "<" , ">" ) : "" ;
1455+ const parameters = getCommaSeparatedString ( method . parameters . map ( param => param . getText ( ) ) , "(" , ")" ) ;
1456+ insertion += functionPrefix + method . name + typeParameters + parameters + stubMethodBody ;
1457+ break ;
1458+ default :
1459+ break ;
1460+ }
1461+
1462+ return insertion += newlineChar ;
1463+
1464+ /**
1465+ * Flattens params into a comma-separated list, sandwiched by prefix
1466+ * and suffix on either end.
1467+ */
1468+ function getCommaSeparatedString ( params : string [ ] , prefix : string , suffix : string ) {
1469+ let result = prefix ;
1470+ for ( let i = 0 ; params && i < params . length ; ++ i ) {
1471+ result += ( i > 0 ? "," : "" ) + params [ i ] ;
1472+ }
1473+ return result + suffix ;
1474+ }
1475+ }
1476+
1477+ /**
1478+ * Finds the symbols in source but not target.
1479+ */
1480+ function filterMissingMembers ( sourceSymbols : Map < Symbol > , targetSymbols : Map < Symbol > ) : Symbol [ ] {
1481+ let result : Symbol [ ] = [ ] ;
1482+ outer:
1483+ for ( const sourceName in sourceSymbols ) {
1484+ for ( const targetName in targetSymbols ) {
1485+ if ( sourceName === targetName ) {
1486+ continue outer;
1487+ }
14101488 }
1489+ result . push ( sourceSymbols [ sourceName ] ) ;
14111490 }
1412- return changes ;
1491+ return result ;
14131492 }
14141493
1415- // TODO: (arozga) simplify to quadratic time solution.
1494+ /*
14161495 function filterMissingMembers(sourceSymbols: Map<Symbol>, targetSymbols: Map<Symbol>): Symbol[] {
14171496 let missingMembers: Symbol[] = [];
14181497 const sortedSourceKeys = Object.keys(sourceSymbols).sort();
@@ -1439,6 +1518,7 @@ namespace ts {
14391518
14401519 return missingMembers;
14411520 }
1521+ */
14421522
14431523 /**
14441524 * Generates codefix changes to insert
@@ -1456,19 +1536,22 @@ namespace ts {
14561536 for ( const member of missingMembers ) {
14571537 if ( member . kind === SyntaxKind . PropertySignature || member . kind === SyntaxKind . PropertyDeclaration ) {
14581538 const interfaceProperty = < PropertySignature > member ;
1459- if ( trackingAddedMembers . indexOf ( interfaceProperty . name . getText ( ) ) === - 1 ) {
1460- let propertyText = "" ;
1461- if ( reference ) {
1462- propertyText = `${ interfaceProperty . name . getText ( ) } : ${ getDefaultValue ( interfaceProperty . type . kind ) } ,${ newLineCharacter } ` ;
1463- }
1464- else {
1465- propertyText = interfaceProperty . getText ( ) ;
1466- const stringToAdd = ! ( propertyText . match ( / ; $ / ) ) ? `;${ newLineCharacter } ` : newLineCharacter ;
1467- propertyText += stringToAdd ;
1468- }
1469- changesArray . push ( { newText : propertyText , span : { start : startPos , length : 0 } } ) ;
1470- trackingAddedMembers . push ( interfaceProperty . name . getText ( ) ) ;
1539+ if ( trackingAddedMembers . indexOf ( interfaceProperty . name . getText ( ) ) !== - 1 ) {
1540+ continue ;
1541+ }
1542+
1543+ let propertyText = "" ;
1544+ if ( reference ) {
1545+ propertyText = `${ interfaceProperty . name . getText ( ) } : ${ getDefaultValue ( interfaceProperty . type . kind ) } ,${ newLineCharacter } ` ;
14711546 }
1547+ else {
1548+ propertyText = interfaceProperty . getText ( ) ;
1549+ const stringToAdd = ! ( propertyText . match ( / ; $ / ) ) ? `;${ newLineCharacter } ` : newLineCharacter ;
1550+ propertyText += stringToAdd ;
1551+ }
1552+ changesArray . push ( { newText : propertyText , span : { start : startPos , length : 0 } } ) ;
1553+ trackingAddedMembers . push ( interfaceProperty . name . getText ( ) ) ;
1554+
14721555 }
14731556 else if ( member . kind === SyntaxKind . MethodSignature || member . kind === SyntaxKind . MethodDeclaration ) {
14741557 const interfaceMethod = < MethodSignature > member ;
0 commit comments