Skip to content

Commit

Permalink
refactors
Browse files Browse the repository at this point in the history
  • Loading branch information
cancerberoSgx committed Nov 1, 2018
1 parent 9b9e01a commit 603de58
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 86 deletions.
2 changes: 2 additions & 0 deletions notes/todo-ideas.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ Each project has its own TODO but here are some general ones:

## plugin ideas - (refactor - code fix)

* wrap selection with try-catch

* check this: https://github.com/Microsoft/TypeScript/pull/13940 and if its possible to add custom transforms fromplugins add one that let user embedd raw text from fs at compile time.

* reorder members alphabetically , by type(constructors first) or by modifiers (public first). Let the user stablish these rules.
Expand Down
13 changes: 9 additions & 4 deletions ts-simple-ast-extra/spec/refactorsSpec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import Project, { SourceFile, NamedNode, ArrowFunction, Node, TypeGuards } from 'ts-simple-ast';
import { applyTextChanges, createTextChanges } from '../src';
import { moveToNewFile, addBracesToArrowFunction, removeBracesFromArrowFunction, convertToEs6Module, fixUnusedIdentifiers } from '../src/refactors';
import Project, { TypeGuards } from 'ts-simple-ast';
import { addBracesToArrowFunction, convertToEs6Module, fixUnusedIdentifiers, moveToNewFile, removeBracesFromArrowFunction } from '../src/refactors';

describe('fileSpec', ()=>{

it('moveToNewFile refactor', ()=>{
const project = new Project()
const code = `
Expand All @@ -11,11 +11,16 @@ const c = new Class1()
`
const f = project.createSourceFile('f1.ts', code)

moveToNewFile(project, f.getClass('Class1'))
const result = moveToNewFile(project, [f.getClass('Class1')])
// console.log(result);

expect(f.getText()).not.toContain('class Class1')
expect(f.getText()).toContain('import { Class1 } from "./Class1";')
const newFile = project.getSourceFile('Class1.ts')
expect (newFile.getText()).toContain('export class Class1')

expect(result.created[0].getBaseName()).toBe('Class1.ts')
expect(result.modified[0].getBaseName()).toBe('f1.ts')

})

Expand Down
66 changes: 66 additions & 0 deletions ts-simple-ast-extra/src/changes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import Project, { TextChange, SourceFile } from 'ts-simple-ast';
import * as ts from 'typescript'
import { flat } from './misc';

export function createTextChanges(textChanges: ts.TextChange[]): TextChange[] {
return textChanges.map(compilerNode => {
return new (TextChange as any)(compilerNode) // Hack because this constructor in internal
})
}

export function applyTextChanges(code: string, textChanges: ts.TextChange[]): string {
const simpleTextChanges = createTextChanges(textChanges)
const sourceFile = applyTextChangesGetSourceFile()
sourceFile.replaceWithText(code)
sourceFile.applyTextChanges(simpleTextChanges)
return sourceFile.getText()
}

export interface ApplyFileTextChangesResult {modified: SourceFile[], removed: SourceFile[], created: SourceFile[]}
export function applyFileTextChanges(project: Project, fileTextChanges: ts.FileTextChanges[], removeEmpty: boolean = false): ApplyFileTextChangesResult {
const result: ApplyFileTextChangesResult = {
modified: [],
removed: [],
created: []
}
fileTextChanges.forEach(ftc =>{
let file = project.getSourceFile(ftc.fileName)
if(ftc.isNewFile && file) {
throw new Error('FileTextChanges instructed to create file '+file+' but it already exists on the project. Aborting!')
}
if(!file && !ftc.isNewFile) {
throw new Error('FileTextChanges instructed to modify existing file '+file+' but it doesn\'t exist. Refusing to create it. Aborting!')
}
let created
if(!file){
file = project.createSourceFile(ftc.fileName)
result.created.push(file)
created=true
}
file.applyTextChanges(createTextChanges(ftc.textChanges))
if(!file.getText().trim() && removeEmpty) {
project.removeSourceFile(file)
result.removed.push(file)
}
else if(!created) {
result.modified.push(file)
}
})
return result
}


export function applyCodeFixes(project: Project, codeFixes: ReadonlyArray<ts.CodeFixAction>){
return applyFileTextChanges(project, flat(codeFixes.map(f=>f.changes)))
}


let applyTextChangesSourceFile: SourceFile

function applyTextChangesGetSourceFile() {
if (!applyTextChangesSourceFile) {
const project = new Project({ useVirtualFileSystem: true })
applyTextChangesSourceFile = project.createSourceFile('tmp.ts', '')
}
return applyTextChangesSourceFile
}
25 changes: 0 additions & 25 deletions ts-simple-ast-extra/src/file.ts

This file was deleted.

2 changes: 1 addition & 1 deletion ts-simple-ast-extra/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ export * from "./project"
export * from './codeFix'
export * from './test'
export * from './node'
export * from './file'
export * from './changes'
4 changes: 4 additions & 0 deletions ts-simple-ast-extra/src/misc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

export function flat<T>(arr: T[][]): T[] {
return arr.reduce((a, b) => a.concat(b))
}
106 changes: 50 additions & 56 deletions ts-simple-ast-extra/src/refactors.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,52 @@
import Project, { ArrowFunction, NamedNode, Node, SourceFile, ImportDeclaration } from 'ts-simple-ast';
import Project, { ArrowFunction, ImportDeclaration, Node, SourceFile, Statement } from 'ts-simple-ast';
import { createTextChanges } from '../src';



export * from '../src'

import { applyCodeFixes, applyFileTextChanges, ApplyFileTextChangesResult } from './changes';

export function addBracesToArrowFunction(project: Project, arrowFunction: ArrowFunction) {
// project.getLanguageService().compilerObject
const range = {pos: arrowFunction.getStart(), end: arrowFunction.getEnd()}
const edits = project.getLanguageService().compilerObject.getEditsForRefactor(arrowFunction.getSourceFile().getFilePath(),{}, range, 'Add or remove braces in an arrow function', 'Add braces to arrow function', {})
arrowFunction.getSourceFile().applyTextChanges(createTextChanges(edits.edits[0].textChanges))
const range = { pos: arrowFunction.getStart(), end: arrowFunction.getEnd() }
const edits = project.getLanguageService().compilerObject.getEditsForRefactor(arrowFunction.getSourceFile().getFilePath(), {}, range, 'Add or remove braces in an arrow function', 'Add braces to arrow function', {})
// arrowFunction.getSourceFile().applyTextChanges(createTextChanges(edits.edits[0].textChanges))
return applyFileTextChanges(project, edits.edits)
}


export function removeBracesFromArrowFunction(project: Project, arrowFunction: ArrowFunction) {
const range = {pos: arrowFunction.getStart(), end: arrowFunction.getEnd()}
const edits = project.getLanguageService().compilerObject.getEditsForRefactor(arrowFunction.getSourceFile().getFilePath(),{}, range, 'Add or remove braces in an arrow function', 'Remove braces from arrow function', {})
arrowFunction.getSourceFile().applyTextChanges(createTextChanges(edits.edits[0].textChanges))
const range = { pos: arrowFunction.getStart(), end: arrowFunction.getEnd() }
const edits = project.getLanguageService().compilerObject.getEditsForRefactor(arrowFunction.getSourceFile().getFilePath(), {}, range, 'Add or remove braces in an arrow function', 'Remove braces from arrow function', {})
// arrowFunction.getSourceFile().applyTextChanges(createTextChanges(edits.edits[0].textChanges))
return applyFileTextChanges(project, edits.edits)
}

export function moveToNewFile(project: Project, declaration: NamedNode&Node, newFilePath?: string){
const range = {pos: declaration.getNameNode().getStart(), end: declaration.getNameNode().getEnd()}
const edits = project.getLanguageService().compilerObject.getEditsForRefactor(declaration.getSourceFile().getFilePath(),{}, range, 'Move to a new file', 'Move to a new file', {})
const newFileName = newFilePath ||edits.edits[1].fileName
let destFile: SourceFile = project.getSourceFiles().find(f=>f.getFilePath()===newFileName)
if(!destFile){
destFile = project.createSourceFile(newFileName, '')
}
else {
// probably is an error
export function moveToNewFile(project: Project, statements: Statement[]): ApplyFileTextChangesResult {
if(!statements.length){
return
}
const range = { pos: statements[0].getStart(), end: statements[statements.length-1].getEnd() }
const edits = project.getLanguageService().compilerObject.getEditsForRefactor(statements[0].getSourceFile().getFilePath(), {}, range, 'Move to a new file', 'Move to a new file', {})
return applyFileTextChanges(project, edits.edits)
// const newFileName = edits.edits[1].fileName
// let destFile: SourceFile = project.getSourceFiles().find(f => f.getFilePath() === newFileName)
// if (!destFile) {
// destFile = project.createSourceFile(newFileName, '')
// }
// else {
// // probably is an error
// }

// const destFileTextChanges = createTextChanges(edits.edits[1].textChanges)
// destFile.applyTextChanges(destFileTextChanges)
// statements[0].getSourceFile().applyTextChanges(createTextChanges(edits.edits[0].textChanges))

const destFileTextChanges = createTextChanges(edits.edits[1].textChanges)
destFile.applyTextChanges(destFileTextChanges)
declaration.getSourceFile().applyTextChanges(createTextChanges(edits.edits[0].textChanges))

}


export function convertToEs6Module(project: Project, file: SourceFile, importDeclaration?: ImportDeclaration) {
// project.getLanguageService().compilerObject
// const range =
const fixes = project.getLanguageService().compilerObject.getCodeFixesAtPosition(file.getFilePath(), importDeclaration ? importDeclaration.getStart() : 0, importDeclaration ? importDeclaration.getEnd() : file.getEnd() , [80001], {}, {})

fixes.forEach(fix=>{
fix.changes.forEach(change=>{

file.applyTextChanges(createTextChanges(change.textChanges))
})
})
const fixes = project.getLanguageService().compilerObject.getCodeFixesAtPosition(file.getFilePath(), importDeclaration ? importDeclaration.getStart() : 0, importDeclaration ? importDeclaration.getEnd() : file.getEnd(), [80001], {}, {})
return applyCodeFixes(project, fixes)
// fixes.forEach(fix => {
// fix.changes.forEach(change => {
// file.applyTextChanges(createTextChanges(change.textChanges))
// })
// })
// const range = {pos: arrowFunction.getStart(), end: arrowFunction.getEnd()}
// const edits = project.getLanguageService().compilerObject.getEditsForRefactor(arrowFunction.getSourceFile().getFilePath(),{}, range, 'Add or remove braces in an arrow function', 'Add braces to arrow function', {})
// arrowFunction.getSourceFile().applyTextChanges(createTextChanges(edits.edits[0].textChanges))
Expand All @@ -60,25 +57,22 @@ fixes.forEach(fix=>{
export function fixUnusedIdentifiers(project: Project, file: SourceFile, rootNode?: Node) {
// project.getLanguageService().compilerObject
// const range =
const fixes = project.getLanguageService().compilerObject.getCodeFixesAtPosition(file.getFilePath(), rootNode ? rootNode.getStart() : 0, rootNode ? rootNode.getEnd() : file.getEnd() ,
[
6138,
6196,
6138,
// 6192, 6198, 6199, 6205
], {}, {})

// console.log(fixes);

// let textChanges: ts.TextChange[] = []
fixes.forEach(fix=>{
fix.changes.forEach(change=>{

file.applyTextChanges(createTextChanges(change.textChanges))
// textChanges = textChanges.concat(change.textChanges)
const fixes = project.getLanguageService().compilerObject.getCodeFixesAtPosition(file.getFilePath(), rootNode ? rootNode.getStart() : 0, rootNode ? rootNode.getEnd() : file.getEnd(),
[
6138,
6196,
6138,
// 6192, 6198, 6199, 6205
], {}, {})

fixes.forEach(fix => {
fix.changes.forEach(change => {

file.applyTextChanges(createTextChanges(change.textChanges))
// textChanges = textChanges.concat(change.textChanges)
})
})
})
// console.log(textChanges);
// console.log(textChanges);

// const range = {pos: arrowFunction.getStart(), end: arrowFunction.getEnd()}
// const edits = project.getLanguageService().compilerObject.getEditsForRefactor(arrowFunction.getSourceFile().getFilePath(),{}, range, 'Add or remove braces in an arrow function', 'Add braces to arrow function', {})
Expand Down

0 comments on commit 603de58

Please sign in to comment.