Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7271ec1

Browse files
author
Andy
authored
Add 'move to new file' refactor (microsoft#23726)
* Add 'move to new file' refactor* Code review, and support commonjs* Compute movedSymbols completely before using, and support `export import`* Fix assertion error: sort empty change before non-empty change* Remove extra newline* Add allowTextChangesInNewFiles preference* Add the new file to 'files' in tsconfig* Avoid parameter initializer* Update API baselines* Use path relative to tsconfig.json* Code review* Fix error where node in tsconfig file was missing a source file
1 parent6149b41 commit7271ec1

File tree

45 files changed

+1254
-116
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1254
-116
lines changed

‎src/compiler/core.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -311,8 +311,8 @@ namespace ts {
311311
}
312312

313313
/** Works like Array.prototype.findIndex, returning `-1` if no element satisfying the predicate is found. */
314-
exportfunctionfindIndex<T>(array:ReadonlyArray<T>,predicate:(element:T,index:number)=>boolean):number{
315-
for(leti=0;i<array.length;i++){
314+
exportfunctionfindIndex<T>(array:ReadonlyArray<T>,predicate:(element:T,index:number)=>boolean,startIndex?:number):number{
315+
for(leti=startIndex||0;i<array.length;i++){
316316
if(predicate(array[i],i)){
317317
returni;
318318
}
@@ -2294,20 +2294,24 @@ namespace ts {
22942294
return["", ...relative, ...components];
22952295
}
22962296

2297+
exportfunctiongetRelativePathFromFile(from:string,to:string,getCanonicalFileName:GetCanonicalFileName){
2298+
returnensurePathIsNonModuleName(getRelativePathFromDirectory(getDirectoryPath(from),to,getCanonicalFileName));
2299+
}
2300+
22972301
/**
22982302
* Gets a relative path that can be used to traverse between `from` and `to`.
22992303
*/
2300-
exportfunctiongetRelativePath(from:string,to:string,ignoreCase:boolean):string;
2304+
exportfunctiongetRelativePathFromDirectory(from:string,to:string,ignoreCase:boolean):string;
23012305
/**
23022306
* Gets a relative path that can be used to traverse between `from` and `to`.
23032307
*/
23042308
// tslint:disable-next-line:unified-signatures
2305-
exportfunctiongetRelativePath(from:string,to:string,getCanonicalFileName:GetCanonicalFileName):string;
2306-
exportfunctiongetRelativePath(from:string,to:string,getCanonicalFileNameOrIgnoreCase:GetCanonicalFileName|boolean){
2307-
Debug.assert((getRootLength(from)>0)===(getRootLength(to)>0),"Paths must either both be absolute or both be relative");
2309+
exportfunctiongetRelativePathFromDirectory(fromDirectory:string,to:string,getCanonicalFileName:GetCanonicalFileName):string;
2310+
exportfunctiongetRelativePathFromDirectory(fromDirectory:string,to:string,getCanonicalFileNameOrIgnoreCase:GetCanonicalFileName|boolean){
2311+
Debug.assert((getRootLength(fromDirectory)>0)===(getRootLength(to)>0),"Paths must either both be absolute or both be relative");
23082312
constgetCanonicalFileName=typeofgetCanonicalFileNameOrIgnoreCase==="function" ?getCanonicalFileNameOrIgnoreCase :identity;
23092313
constignoreCase=typeofgetCanonicalFileNameOrIgnoreCase==="boolean" ?getCanonicalFileNameOrIgnoreCase :false;
2310-
constpathComponents=getPathComponentsRelativeTo(from,to,ignoreCase ?equateStringsCaseInsensitive :equateStringsCaseSensitive,getCanonicalFileName);
2314+
constpathComponents=getPathComponentsRelativeTo(fromDirectory,to,ignoreCase ?equateStringsCaseInsensitive :equateStringsCaseSensitive,getCanonicalFileName);
23112315
returngetPathFromPathComponents(pathComponents);
23122316
}
23132317

‎src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4245,5 +4245,9 @@
42454245
"Convert all 'require' to 'import'": {
42464246
"category":"Message",
42474247
"code":95048
4248+
},
4249+
"Move to a new file": {
4250+
"category":"Message",
4251+
"code":95049
42484252
}
42494253
}

‎src/compiler/factory.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,6 +1505,13 @@ namespace ts {
15051505
returnblock;
15061506
}
15071507

1508+
/*@internal */
1509+
exportfunctioncreateExpressionStatement(expression:Expression):ExpressionStatement{
1510+
constnode=<ExpressionStatement>createSynthesizedNode(SyntaxKind.ExpressionStatement);
1511+
node.expression=expression;
1512+
returnnode;
1513+
}
1514+
15081515
exportfunctionupdateBlock(node:Block,statements:ReadonlyArray<Statement>){
15091516
returnnode.statements!==statements
15101517
?updateNode(createBlock(statements,node.multiLine),node)
@@ -1531,9 +1538,7 @@ namespace ts {
15311538
}
15321539

15331540
exportfunctioncreateStatement(expression:Expression){
1534-
constnode=<ExpressionStatement>createSynthesizedNode(SyntaxKind.ExpressionStatement);
1535-
node.expression=parenthesizeExpressionForExpressionStatement(expression);
1536-
returnnode;
1541+
returncreateExpressionStatement(parenthesizeExpressionForExpressionStatement(expression));
15371542
}
15381543

15391544
exportfunctionupdateStatement(node:ExpressionStatement,expression:Expression){

‎src/harness/fourslash.ts

Lines changed: 61 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3005,9 +3005,7 @@ Actual: ${stringify(fullActual)}`);
30053005
}
30063006

30073007
publicverifyApplicableRefactorAvailableAtMarker(negative:boolean,markerName:string){
3008-
constmarker=this.getMarkerByName(markerName);
3009-
constapplicableRefactors=this.languageService.getApplicableRefactors(this.activeFile.fileName,marker.position,ts.defaultPreferences);
3010-
constisAvailable=applicableRefactors&&applicableRefactors.length>0;
3008+
constisAvailable=this.getApplicableRefactors(this.getMarkerByName(markerName).position).length>0;
30113009
if(negative&&isAvailable){
30123010
this.raiseError(`verifyApplicableRefactorAvailableAtMarker failed - expected no refactor at marker${markerName} but found some.`);
30133011
}
@@ -3024,9 +3022,7 @@ Actual: ${stringify(fullActual)}`);
30243022
}
30253023

30263024
publicverifyRefactorAvailable(negative:boolean,name:string,actionName?:string){
3027-
constselection=this.getSelection();
3028-
3029-
letrefactors=this.languageService.getApplicableRefactors(this.activeFile.fileName,selection,ts.defaultPreferences)||[];
3025+
letrefactors=this.getApplicableRefactors(this.getSelection());
30303026
refactors=refactors.filter(r=>r.name===name&&(actionName===undefined||r.actions.some(a=>a.name===actionName)));
30313027
constisAvailable=refactors.length>0;
30323028

@@ -3046,10 +3042,7 @@ Actual: ${stringify(fullActual)}`);
30463042
}
30473043

30483044
publicverifyRefactor({ name, actionName, refactors}:FourSlashInterface.VerifyRefactorOptions){
3049-
constselection=this.getSelection();
3050-
3051-
constactualRefactors=(this.languageService.getApplicableRefactors(this.activeFile.fileName,selection,ts.defaultPreferences)||ts.emptyArray)
3052-
.filter(r=>r.name===name&&r.actions.some(a=>a.name===actionName));
3045+
constactualRefactors=this.getApplicableRefactors(this.getSelection()).filter(r=>r.name===name&&r.actions.some(a=>a.name===actionName));
30533046
this.assertObjectsEqual(actualRefactors,refactors);
30543047
}
30553048

@@ -3059,8 +3052,7 @@ Actual: ${stringify(fullActual)}`);
30593052
thrownewError("Exactly one refactor range is allowed per test.");
30603053
}
30613054

3062-
constapplicableRefactors=this.languageService.getApplicableRefactors(this.activeFile.fileName,ts.first(ranges),ts.defaultPreferences);
3063-
constisAvailable=applicableRefactors&&applicableRefactors.length>0;
3055+
constisAvailable=this.getApplicableRefactors(ts.first(ranges)).length>0;
30643056
if(negative&&isAvailable){
30653057
this.raiseError(`verifyApplicableRefactorAvailableForRange failed - expected no refactor but found some.`);
30663058
}
@@ -3071,7 +3063,7 @@ Actual: ${stringify(fullActual)}`);
30713063

30723064
publicapplyRefactor({ refactorName, actionName, actionDescription,newContent:newContentWithRenameMarker}:FourSlashInterface.ApplyRefactorOptions){
30733065
constrange=this.getSelection();
3074-
constrefactors=this.languageService.getApplicableRefactors(this.activeFile.fileName,range,ts.defaultPreferences);
3066+
constrefactors=this.getApplicableRefactors(range);
30753067
constrefactorsWithName=refactors.filter(r=>r.name===refactorName);
30763068
if(refactorsWithName.length===0){
30773069
this.raiseError(`The expected refactor:${refactorName} is not available at the marker location.\nAvailable refactors:${refactors.map(r=>r.name)}`);
@@ -3117,7 +3109,48 @@ Actual: ${stringify(fullActual)}`);
31173109
return{ renamePosition, newContent};
31183110
}
31193111
}
3112+
}
31203113

3114+
publicnoMoveToNewFile(){
3115+
for(constrangeofthis.getRanges()){
3116+
for(constrefactorofthis.getApplicableRefactors(range,{allowTextChangesInNewFiles:true})){
3117+
if(refactor.name==="Move to a new file"){
3118+
ts.Debug.fail("Did not expect to get 'move to a new file' refactor");
3119+
}
3120+
}
3121+
}
3122+
}
3123+
3124+
publicmoveToNewFile(options:FourSlashInterface.MoveToNewFileOptions):void{
3125+
assert(this.getRanges().length===1);
3126+
constrange=this.getRanges()[0];
3127+
constrefactor=ts.find(this.getApplicableRefactors(range,{allowTextChangesInNewFiles:true}),r=>r.name==="Move to a new file");
3128+
assert(refactor.actions.length===1);
3129+
constaction=ts.first(refactor.actions);
3130+
assert(action.name==="Move to a new file"&&action.description==="Move to a new file");
3131+
3132+
consteditInfo=this.languageService.getEditsForRefactor(this.activeFile.fileName,this.formatCodeSettings,range,refactor.name,action.name,ts.defaultPreferences);
3133+
for(consteditofeditInfo.edits){
3134+
constnewContent=options.newFileContents[edit.fileName];
3135+
if(newContent===undefined){
3136+
this.raiseError(`There was an edit in${edit.fileName} but new content was not specified.`);
3137+
}
3138+
if(this.testData.files.some(f=>f.fileName===edit.fileName)){
3139+
this.applyEdits(edit.fileName,edit.textChanges,/*isFormattingEdit*/false);
3140+
this.openFile(edit.fileName);
3141+
this.verifyCurrentFileContent(newContent);
3142+
}
3143+
else{
3144+
assert(edit.textChanges.length===1);
3145+
constchange=ts.first(edit.textChanges);
3146+
assert.deepEqual(change.span,ts.createTextSpan(0,0));
3147+
assert.equal(change.newText,newContent,`Content for${edit.fileName}`);
3148+
}
3149+
}
3150+
3151+
for(constfileNameinoptions.newFileContents){
3152+
assert(editInfo.edits.some(e=>e.fileName===fileName));
3153+
}
31213154
}
31223155

31233156
publicverifyFileAfterApplyingRefactorAtMarker(
@@ -3333,6 +3366,10 @@ Actual: ${stringify(fullActual)}`);
33333366
this.verifyCurrentFileContent(options.newFileContents[fileName]);
33343367
}
33353368
}
3369+
3370+
privategetApplicableRefactors(positionOrRange:number|ts.TextRange,preferences=ts.defaultPreferences):ReadonlyArray<ts.ApplicableRefactorInfo>{
3371+
returnthis.languageService.getApplicableRefactors(this.activeFile.fileName,positionOrRange,preferences)||ts.emptyArray;
3372+
}
33363373
}
33373374

33383375
exportfunctionrunFourSlashTest(basePath:string,testType:FourSlashTestType,fileName:string){
@@ -4430,6 +4467,13 @@ namespace FourSlashInterface {
44304467
publicgetEditsForFileRename(options:GetEditsForFileRenameOptions){
44314468
this.state.getEditsForFileRename(options);
44324469
}
4470+
4471+
publicmoveToNewFile(options:MoveToNewFileOptions):void{
4472+
this.state.moveToNewFile(options);
4473+
}
4474+
publicnoMoveToNewFile():void{
4475+
this.state.noMoveToNewFile();
4476+
}
44334477
}
44344478

44354479
exportclassEdit{
@@ -4803,4 +4847,8 @@ namespace FourSlashInterface {
48034847
readonlynewPath:string;
48044848
readonlynewFileContents:{readonly[fileName:string]:string};
48054849
}
4850+
4851+
exportinterfaceMoveToNewFileOptions{
4852+
readonlynewFileContents:{readonly[fileName:string]:string};
4853+
}
48064854
}

‎src/harness/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
"../services/codefixes/useDefaultImport.ts",
117117
"../services/refactors/extractSymbol.ts",
118118
"../services/refactors/generateGetAccessorAndSetAccessor.ts",
119+
"../services/refactors/moveToNewFile.ts",
119120
"../services/sourcemaps.ts",
120121
"../services/services.ts",
121122
"../services/breakpoints.ts",

‎src/harness/unittests/paths.ts

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -268,25 +268,25 @@ describe("core paths", () => {
268268
assert.strictEqual(ts.resolvePath("a","b","../c"),"a/c");
269269
});
270270
it("getPathRelativeTo",()=>{
271-
assert.strictEqual(ts.getRelativePath("/","/",/*ignoreCase*/false),"");
272-
assert.strictEqual(ts.getRelativePath("/a","/a",/*ignoreCase*/false),"");
273-
assert.strictEqual(ts.getRelativePath("/a/","/a",/*ignoreCase*/false),"");
274-
assert.strictEqual(ts.getRelativePath("/a","/",/*ignoreCase*/false),"..");
275-
assert.strictEqual(ts.getRelativePath("/a","/b",/*ignoreCase*/false),"../b");
276-
assert.strictEqual(ts.getRelativePath("/a/b","/b",/*ignoreCase*/false),"../../b");
277-
assert.strictEqual(ts.getRelativePath("/a/b/c","/b",/*ignoreCase*/false),"../../../b");
278-
assert.strictEqual(ts.getRelativePath("/a/b/c","/b/c",/*ignoreCase*/false),"../../../b/c");
279-
assert.strictEqual(ts.getRelativePath("/a/b/c","/a/b",/*ignoreCase*/false),"..");
280-
assert.strictEqual(ts.getRelativePath("c:","d:",/*ignoreCase*/false),"d:/");
281-
assert.strictEqual(ts.getRelativePath("file:///","file:///",/*ignoreCase*/false),"");
282-
assert.strictEqual(ts.getRelativePath("file:///a","file:///a",/*ignoreCase*/false),"");
283-
assert.strictEqual(ts.getRelativePath("file:///a/","file:///a",/*ignoreCase*/false),"");
284-
assert.strictEqual(ts.getRelativePath("file:///a","file:///",/*ignoreCase*/false),"..");
285-
assert.strictEqual(ts.getRelativePath("file:///a","file:///b",/*ignoreCase*/false),"../b");
286-
assert.strictEqual(ts.getRelativePath("file:///a/b","file:///b",/*ignoreCase*/false),"../../b");
287-
assert.strictEqual(ts.getRelativePath("file:///a/b/c","file:///b",/*ignoreCase*/false),"../../../b");
288-
assert.strictEqual(ts.getRelativePath("file:///a/b/c","file:///b/c",/*ignoreCase*/false),"../../../b/c");
289-
assert.strictEqual(ts.getRelativePath("file:///a/b/c","file:///a/b",/*ignoreCase*/false),"..");
290-
assert.strictEqual(ts.getRelativePath("file:///c:","file:///d:",/*ignoreCase*/false),"file:///d:/");
271+
assert.strictEqual(ts.getRelativePathFromDirectory("/","/",/*ignoreCase*/false),"");
272+
assert.strictEqual(ts.getRelativePathFromDirectory("/a","/a",/*ignoreCase*/false),"");
273+
assert.strictEqual(ts.getRelativePathFromDirectory("/a/","/a",/*ignoreCase*/false),"");
274+
assert.strictEqual(ts.getRelativePathFromDirectory("/a","/",/*ignoreCase*/false),"..");
275+
assert.strictEqual(ts.getRelativePathFromDirectory("/a","/b",/*ignoreCase*/false),"../b");
276+
assert.strictEqual(ts.getRelativePathFromDirectory("/a/b","/b",/*ignoreCase*/false),"../../b");
277+
assert.strictEqual(ts.getRelativePathFromDirectory("/a/b/c","/b",/*ignoreCase*/false),"../../../b");
278+
assert.strictEqual(ts.getRelativePathFromDirectory("/a/b/c","/b/c",/*ignoreCase*/false),"../../../b/c");
279+
assert.strictEqual(ts.getRelativePathFromDirectory("/a/b/c","/a/b",/*ignoreCase*/false),"..");
280+
assert.strictEqual(ts.getRelativePathFromDirectory("c:","d:",/*ignoreCase*/false),"d:/");
281+
assert.strictEqual(ts.getRelativePathFromDirectory("file:///","file:///",/*ignoreCase*/false),"");
282+
assert.strictEqual(ts.getRelativePathFromDirectory("file:///a","file:///a",/*ignoreCase*/false),"");
283+
assert.strictEqual(ts.getRelativePathFromDirectory("file:///a/","file:///a",/*ignoreCase*/false),"");
284+
assert.strictEqual(ts.getRelativePathFromDirectory("file:///a","file:///",/*ignoreCase*/false),"..");
285+
assert.strictEqual(ts.getRelativePathFromDirectory("file:///a","file:///b",/*ignoreCase*/false),"../b");
286+
assert.strictEqual(ts.getRelativePathFromDirectory("file:///a/b","file:///b",/*ignoreCase*/false),"../../b");
287+
assert.strictEqual(ts.getRelativePathFromDirectory("file:///a/b/c","file:///b",/*ignoreCase*/false),"../../../b");
288+
assert.strictEqual(ts.getRelativePathFromDirectory("file:///a/b/c","file:///b/c",/*ignoreCase*/false),"../../../b/c");
289+
assert.strictEqual(ts.getRelativePathFromDirectory("file:///a/b/c","file:///a/b",/*ignoreCase*/false),"..");
290+
assert.strictEqual(ts.getRelativePathFromDirectory("file:///c:","file:///d:",/*ignoreCase*/false),"file:///d:/");
291291
});
292292
});

‎src/harness/vpath.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace vpath {
1818
exportimportdirname=ts.getDirectoryPath;
1919
exportimportbasename=ts.getBaseFileName;
2020
exportimportextname=ts.getAnyExtensionFromPath;
21-
exportimportrelative=ts.getRelativePath;
21+
exportimportrelative=ts.getRelativePathFromDirectory;
2222
exportimportbeneath=ts.containsPath;
2323
exportimportchangeExtension=ts.changeAnyExtension;
2424
exportimportisTypeScript=ts.hasTypeScriptFileExtension;

‎src/server/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
"../services/codefixes/useDefaultImport.ts",
113113
"../services/refactors/extractSymbol.ts",
114114
"../services/refactors/generateGetAccessorAndSetAccessor.ts",
115+
"../services/refactors/moveToNewFile.ts",
115116
"../services/sourcemaps.ts",
116117
"../services/services.ts",
117118
"../services/breakpoints.ts",

‎src/server/tsconfig.library.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@
118118
"../services/codefixes/useDefaultImport.ts",
119119
"../services/refactors/extractSymbol.ts",
120120
"../services/refactors/generateGetAccessorAndSetAccessor.ts",
121+
"../services/refactors/moveToNewFile.ts",
121122
"../services/sourcemaps.ts",
122123
"../services/services.ts",
123124
"../services/breakpoints.ts",

‎src/services/breakpoints.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ namespace ts.BreakpointResolver {
4141
}
4242

4343
functiontextSpanEndingAtNextToken(startNode:Node,previousTokenToFindNextEndToken:Node):TextSpan{
44-
returntextSpan(startNode,findNextToken(previousTokenToFindNextEndToken,previousTokenToFindNextEndToken.parent));
44+
returntextSpan(startNode,findNextToken(previousTokenToFindNextEndToken,previousTokenToFindNextEndToken.parent,sourceFile));
4545
}
4646

4747
functionspanInNodeIfStartsOnSameLine(node:Node,otherwiseOnNode?:Node):TextSpan{
@@ -60,7 +60,7 @@ namespace ts.BreakpointResolver {
6060
}
6161

6262
functionspanInNextNode(node:Node):TextSpan{
63-
returnspanInNode(findNextToken(node,node.parent));
63+
returnspanInNode(findNextToken(node,node.parent,sourceFile));
6464
}
6565

6666
functionspanInNode(node:Node):TextSpan{

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp