Skip to content
This repository has been archived by the owner on Oct 10, 2018. It is now read-only.

Commit

Permalink
feat(parser): Upgrade typescript parser and update default import gen…
Browse files Browse the repository at this point in the history
…eration (#313)


Closes #227. Closes #305. Does update the typescript parser and adjusts to the breaking changes of the parser.
  • Loading branch information
buehler committed Oct 4, 2017
1 parent aed0e26 commit 14d1af3
Show file tree
Hide file tree
Showing 11 changed files with 245 additions and 592 deletions.
16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,30 +91,30 @@
"devDependencies": {
"@types/chai": "^4.0.4",
"@types/mocha": "^2.2.43",
"@types/node": "^8.0.28",
"@types/node": "^8.0.31",
"@types/reflect-metadata": "0.0.5",
"@types/sinon": "^2.3.4",
"@types/sinon": "^2.3.5",
"@types/sinon-chai": "^2.7.29",
"chai": "^4.1.2",
"del-cli": "^1.1.0",
"filewalker": "^0.1.3",
"mocha-testdata": "^1.2.0",
"semantic-release": "^8.0.0",
"semantic-release": "^8.0.3",
"semantic-release-visualstudio-marketplace-version": "^1.0.0",
"sinon": "^4.0.0",
"sinon-chai": "^2.13.0",
"sinon-chai": "^2.14.0",
"tslint": "^5.7.0",
"tslint-config-airbnb": "^5.3.0-beta",
"tsutils": "^2.8.2",
"tslint-config-airbnb": "^5.3.0",
"tsutils": "^2.11.1",
"vscode": "^1.1.5"
},
"dependencies": {
"inversify": "^4.3.0",
"inversify-inject-decorators": "^3.0.1",
"reflect-metadata": "^0.1.10",
"tslib": "^1.7.1",
"typescript": "~2.4.2",
"typescript-parser": "^1.3.3"
"typescript": "~2.5.3",
"typescript-parser": "^2.0.0"
},
"activationEvents": [
"onLanguage:typescript",
Expand Down
25 changes: 10 additions & 15 deletions src/common/helpers/DeclarationIndexHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
import { join, normalize, parse, relative } from 'path';
import {
DeclarationInfo,
DefaultDeclaration,
DefaultImport,
ExternalModuleImport,
Import,
NamedImport,
NamespaceImport,
} from 'typescript-parser';
import { DeclarationInfo, ExternalModuleImport, Import, NamedImport, NamespaceImport } from 'typescript-parser';

/**
* Calculates a list of declarationInfos filtered by the already imported ones in the given document.
* The result is a list of declarations that are not already imported by the document.
*
*
* @export
* @param {ResolveIndex} resolveIndex
* @param {string} documentPath
Expand All @@ -35,11 +27,14 @@ export function getDeclarationsFilteredByImports(
declarations = declarations
.filter(o => o.from !== importedLib || !(tsImport as NamedImport).specifiers
.some(s => s.specifier === o.declaration.name));
// if (tsImport.defaultAlias) {
// else if (tsImport instanceof DefaultImport) {
// declarations = declarations
// .filter(o => (!(o.declaration instanceof DefaultDeclaration) || importedLib !== o.from));
// }
// }
} else if (tsImport instanceof NamespaceImport || tsImport instanceof ExternalModuleImport) {
declarations = declarations.filter(o => o.from !== tsImport.libraryName);
} else if (tsImport instanceof DefaultImport) {
declarations = declarations
.filter(o => (!(o.declaration instanceof DefaultDeclaration) || importedLib !== o.from));
}
}

Expand All @@ -51,7 +46,7 @@ export function getDeclarationsFilteredByImports(
* If the library is a node module or a typings module, the name
* is returned. If the "lib" is in the local workspace, then the
* absolut path from the workspace root is returned.
*
*
* @param {string} library Name of the library
* @param {string} actualFilePath Filepath of the actually open file
* @param {string} [rootPath] Root path of the workspace
Expand All @@ -72,7 +67,7 @@ export function getAbsolutLibraryName(library: string, actualFilePath: string, r
* If the library is a node module or a typings module, the name
* is returned. If the "lib" is in the local workspace, then the
* relative path from the actual file is returned.
*
*
* @param {string} library Name of the library
* @param {string} actualFilePath Filepath of the actually open file
* @param {string} [rootPath] Root path of the workspace
Expand Down
24 changes: 3 additions & 21 deletions src/extension/extension.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import 'reflect-metadata';

import { DefaultImport, GENERATORS, NamedImport, SymbolSpecifier, TypescriptCodeGenerator } from 'typescript-parser';
import { GENERATORS, TypescriptCodeGenerator } from 'typescript-parser';
import { Disposable, ExtensionContext } from 'vscode';

import { KeywordImportGroup, RegexImportGroup, RemainImportGroup } from './import-grouping';
import { Container } from './IoC';
import { iocSymbols } from './IoCSymbols';
import { ImportProxy } from './proxy-objects/ImportProxy';
import { TypeScriptHero } from './TypeScriptHero';

let extension: Disposable;
Expand All @@ -25,28 +24,11 @@ function extendGenerator(generator: TypescriptCodeGenerator): void {
GENERATORS[KeywordImportGroup.name] = simpleGenerator;
GENERATORS[RegexImportGroup.name] = simpleGenerator;
GENERATORS[RemainImportGroup.name] = simpleGenerator;
GENERATORS[ImportProxy.name] = (proxy: ImportProxy) => {
if (proxy.specifiers.length <= 0 && (proxy.defaultAlias || proxy.defaultPurposal)) {
return generator.generate(
new DefaultImport(
proxy.libraryName, (proxy.defaultAlias || proxy.defaultPurposal)!, proxy.start, proxy.end,
),
);
}
if (proxy.defaultAlias) {
proxy.specifiers.push(new SymbolSpecifier('default', proxy.defaultAlias));
}
const named = new NamedImport(
proxy.libraryName, proxy.start, proxy.end,
);
named.specifiers = proxy.specifiers;
return generator.generate(named);
};
}

/**
* Activates TypeScript Hero
*
*
* @export
* @param {ExtensionContext} context
*/
Expand All @@ -61,7 +43,7 @@ export async function activate(context: ExtensionContext): Promise<void> {

/**
* Deactivates TypeScript Hero
*
*
* @export
*/
export function deactivate(): void {
Expand Down
97 changes: 51 additions & 46 deletions src/extension/managers/ImportManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {
DeclarationIndex,
DeclarationInfo,
DefaultDeclaration,
DefaultImport,
ExternalModuleImport,
File,
Import,
Expand All @@ -29,10 +28,21 @@ import { importRange } from '../helpers';
import { ImportGroup } from '../import-grouping';
import { Container } from '../IoC';
import { iocSymbols } from '../IoCSymbols';
import { ImportProxy } from '../proxy-objects/ImportProxy';
import { importSort, specifierSort } from '../utilities/utilityFunctions';
import { ObjectManager } from './ObjectManager';

function sameSpecifiers(specs1: SymbolSpecifier[], specs2: SymbolSpecifier[]): boolean {
for (const spec of specs1) {
const spec2 = specs2[specs1.indexOf(spec)];
if (!spec2 ||
spec.specifier !== spec2.specifier ||
spec.alias !== spec2.alias) {
return false;
}
}
return true;
}

/**
* Management class for the imports of a document. Can add and remove imports to the document
* and commit the virtual document to the TextEditor.
Expand Down Expand Up @@ -93,9 +103,6 @@ export class ImportManager implements ObjectManager {
*/
public static async create(document: TextDocument): Promise<ImportManager> {
const source = await ImportManager.parser.parseSource(document.getText());
source.imports = source.imports.map(
o => o instanceof NamedImport || o instanceof DefaultImport ? new ImportProxy(o) : o,
);
return new ImportManager(document, source);
}

Expand All @@ -121,44 +128,39 @@ export class ImportManager implements ObjectManager {
* @memberof ImportManager
*/
public addDeclarationImport(declarationInfo: DeclarationInfo): this {
// If there is something already imported, it must be a NamedImport or a DefaultImport
const alreadyImported: ImportProxy = this.imports.find(
// If there is something already imported, it must be a NamedImport
const alreadyImported: NamedImport = this.imports.find(
o => declarationInfo.from === getAbsolutLibraryName(
o.libraryName,
this.document.fileName,
ImportManager.rootPath,
) && o instanceof ImportProxy,
) as ImportProxy;
) && o instanceof NamedImport,
) as NamedImport;

if (alreadyImported) {
// If we found an import for this declaration, it's either a default import or a named import
// If we found an import for this declaration, it's named import (with a possible default declaration)
if (declarationInfo.declaration instanceof DefaultDeclaration) {
delete alreadyImported.defaultAlias;
alreadyImported.defaultPurposal = declarationInfo.declaration.name;
} else {
alreadyImported.addSpecifier(declarationInfo.declaration.name);
alreadyImported.defaultAlias = declarationInfo.declaration.name;
} else if (!alreadyImported.specifiers.some(o => o.specifier === declarationInfo.declaration.name)) {
alreadyImported.specifiers.push(new SymbolSpecifier(declarationInfo.declaration.name));
}
} else {
let imp: Import;
let imp: Import = new NamedImport(getRelativeLibraryName(
declarationInfo.from,
this.document.fileName,
ImportManager.rootPath,
));

if (declarationInfo.declaration instanceof ModuleDeclaration) {
imp = new NamespaceImport(
declarationInfo.from,
declarationInfo.declaration.name,
);
} else if (declarationInfo.declaration instanceof DefaultDeclaration) {
imp = new ImportProxy(getRelativeLibraryName(
declarationInfo.from,
this.document.fileName,
ImportManager.rootPath,
));
(imp as ImportProxy).defaultPurposal = declarationInfo.declaration.name;
(imp as NamedImport).defaultAlias = declarationInfo.declaration.name;
} else {
imp = new ImportProxy(getRelativeLibraryName(
declarationInfo.from,
this.document.fileName,
ImportManager.rootPath,
));
(imp as ImportProxy).specifiers.push(new SymbolSpecifier(declarationInfo.declaration.name));
(imp as NamedImport).specifiers.push(new SymbolSpecifier(declarationInfo.declaration.name));
}
this.imports.push(imp);
this.addImportsToGroups([imp]);
Expand Down Expand Up @@ -221,11 +223,11 @@ export class ImportManager implements ObjectManager {
if (this._parsedDocument.nonLocalUsages.indexOf(actImport.alias) > -1) {
keep.push(actImport);
}
} else if (actImport instanceof ImportProxy) {
} else if (actImport instanceof NamedImport) {
actImport.specifiers = actImport.specifiers
.filter(o => this._parsedDocument.nonLocalUsages.indexOf(o.alias || o.specifier) > -1)
.sort(specifierSort);
const defaultSpec = actImport.defaultAlias || actImport.defaultPurposal;
const defaultSpec = actImport.defaultAlias;
if (actImport.specifiers.length ||
(!!defaultSpec && this._parsedDocument.nonLocalUsages.indexOf(defaultSpec) >= 0)) {
keep.push(actImport);
Expand Down Expand Up @@ -273,9 +275,6 @@ export class ImportManager implements ObjectManager {
if (result) {
delete this.organize;
this._parsedDocument = await ImportManager.parser.parseSource(this.document.getText());
this._parsedDocument.imports = this._parsedDocument.imports.map(
o => o instanceof NamedImport || o instanceof DefaultImport ? new ImportProxy(o) : o,
);
this.imports = this._parsedDocument.imports.map(o => o.clone());
for (const group of this.importGroups) {
group.reset();
Expand Down Expand Up @@ -328,10 +327,14 @@ export class ImportManager implements ObjectManager {
edits.push(TextEdit.delete(importRange(this.document, imp.start, imp.end)));
}
}
const actualDocumentsProxies = this._parsedDocument.imports.filter(o => o instanceof ImportProxy);
const actualDocumentsNamed = this._parsedDocument.imports.filter(o => o instanceof NamedImport);
for (const imp of this.imports) {
if (imp instanceof ImportProxy &&
actualDocumentsProxies.some((o: ImportProxy) => o.isEqual(imp as ImportProxy))) {
if (imp instanceof NamedImport &&
actualDocumentsNamed.some((o: NamedImport) =>
o.libraryName === imp.libraryName &&
o.defaultAlias === imp.defaultAlias &&
o.specifiers.length === imp.specifiers.length &&
sameSpecifiers(o.specifiers, imp.specifiers))) {
continue;
}
if (imp.isNew) {
Expand Down Expand Up @@ -388,7 +391,7 @@ export class ImportManager implements ObjectManager {
.reduce(
(all, cur) => {
let specifiers = all;
if (cur instanceof ImportProxy) {
if (cur instanceof NamedImport) {
specifiers = specifiers.concat(cur.specifiers.map(o => o.alias || o.specifier));
if (cur.defaultAlias) {
specifiers.push(cur.defaultAlias);
Expand All @@ -399,7 +402,7 @@ export class ImportManager implements ObjectManager {
}
return specifiers;
},
<string[]>[],
[] as string[],
);

for (const decision of Object.keys(
Expand All @@ -418,12 +421,17 @@ export class ImportManager implements ObjectManager {
}
}

const proxies = this.imports.filter(o => o instanceof ImportProxy) as ImportProxy[];
const named = this.imports.filter(o => o instanceof NamedImport) as NamedImport[];

for (const imp of proxies) {
if (imp.defaultPurposal && !imp.defaultAlias) {
imp.defaultAlias = await this.getDefaultIdentifier(imp.defaultPurposal);
delete imp.defaultPurposal;
for (const imp of named) {
if (imp.defaultAlias) {
const specifiers = getSpecifiers();
if (
specifiers.filter(o => o === imp.defaultAlias).length > 1 &&
ImportManager.config.resolver.promptForSpecifiers
) {
imp.defaultAlias = await this.getDefaultIdentifier(imp.defaultAlias);
}
}

for (const spec of imp.specifiers) {
Expand Down Expand Up @@ -465,16 +473,13 @@ export class ImportManager implements ObjectManager {
* @memberof ImportManager
*/
private async getDefaultIdentifier(declarationName: string): Promise<string | undefined> {
if (!ImportManager.config.resolver.promptForSpecifiers) {
return declarationName;
}
const result = await this.vscodeInputBox({
placeHolder: 'Default export name',
prompt: 'Please enter a variable name for the default export...',
prompt: 'Please enter an alias name for the default export...',
validateInput: s => !!s ? '' : 'Please enter a variable name',
value: declarationName,
});
return !!result ? result : undefined;
return !!result ? result.replace(/[,.-_]/g, '') : undefined;
}

/**
Expand Down

0 comments on commit 14d1af3

Please sign in to comment.