Skip to content

Commit dbe9092

Browse files
andrewbranchtypescript-bot
authored andcommitted
Cherry-pick PR microsoft#38579 into release-3.9
Component commits: 0a696c9 Ensure formatter can always get a newline character ab09d67 Make FormatContext.host optional since it’s not necessary if format options are all applied 90923e2 Make FormattingHost required again
1 parent 8037e26 commit dbe9092

File tree

12 files changed

+61
-21
lines changed

12 files changed

+61
-21
lines changed

src/harness/fourslashImpl.ts

+6
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,12 @@ namespace FourSlash {
515515
}
516516
}
517517

518+
public verifyOrganizeImports(newContent: string) {
519+
const changes = this.languageService.organizeImports({ fileName: this.activeFile.fileName, type: "file" }, this.formatCodeSettings, ts.emptyOptions);
520+
this.applyChanges(changes);
521+
this.verifyFileContent(this.activeFile.fileName, newContent);
522+
}
523+
518524
private raiseError(message: string): never {
519525
throw new Error(this.messageAtLastKnownMarker(message));
520526
}

src/harness/fourslashInterfaceImpl.ts

+4
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,10 @@ namespace FourSlashInterface {
560560
public noMoveToNewFile(): void {
561561
this.state.noMoveToNewFile();
562562
}
563+
564+
public organizeImports(newContent: string) {
565+
this.state.verifyOrganizeImports(newContent);
566+
}
563567
}
564568

565569
export class Edit {

src/services/formatting/formatting.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ namespace ts.formatting {
33
export interface FormatContext {
44
readonly options: FormatCodeSettings;
55
readonly getRules: RulesMap;
6+
readonly host: FormattingHost;
67
}
78

89
export interface TextRangeWithKind<T extends SyntaxKind = SyntaxKind> extends TextRange {
@@ -394,7 +395,7 @@ namespace ts.formatting {
394395
initialIndentation: number,
395396
delta: number,
396397
formattingScanner: FormattingScanner,
397-
{ options, getRules }: FormatContext,
398+
{ options, getRules, host }: FormatContext,
398399
requestKind: FormattingRequestKind,
399400
rangeContainsError: (r: TextRange) => boolean,
400401
sourceFile: SourceFileLike): TextChange[] {
@@ -1193,7 +1194,7 @@ namespace ts.formatting {
11931194
previousRange: TextRangeWithKind,
11941195
previousStartLine: number,
11951196
currentRange: TextRangeWithKind,
1196-
currentStartLine: number,
1197+
currentStartLine: number
11971198
): LineAction {
11981199
const onLaterLine = currentStartLine !== previousStartLine;
11991200
switch (rule.action) {
@@ -1221,7 +1222,7 @@ namespace ts.formatting {
12211222
// edit should not be applied if we have one line feed between elements
12221223
const lineDelta = currentStartLine - previousStartLine;
12231224
if (lineDelta !== 1) {
1224-
recordReplace(previousRange.end, currentRange.pos - previousRange.end, options.newLineCharacter!);
1225+
recordReplace(previousRange.end, currentRange.pos - previousRange.end, getNewLineOrDefaultFromHost(host, options));
12251226
return onLaterLine ? LineAction.None : LineAction.LineAdded;
12261227
}
12271228
break;

src/services/formatting/rulesMap.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* @internal */
22
namespace ts.formatting {
3-
export function getFormatContext(options: FormatCodeSettings): FormatContext {
4-
return { options, getRules: getRulesMap() };
3+
export function getFormatContext(options: FormatCodeSettings, host: FormattingHost): FormatContext {
4+
return { options, getRules: getRulesMap(), host };
55
}
66

77
let rulesMapCache: RulesMap | undefined;

src/services/services.ts

+9-9
Original file line numberDiff line numberDiff line change
@@ -1480,7 +1480,7 @@ namespace ts {
14801480
position,
14811481
{ name, source },
14821482
host,
1483-
(formattingOptions && formatting.getFormatContext(formattingOptions))!, // TODO: GH#18217
1483+
(formattingOptions && formatting.getFormatContext(formattingOptions, host))!, // TODO: GH#18217
14841484
preferences,
14851485
cancellationToken,
14861486
);
@@ -1818,16 +1818,16 @@ namespace ts {
18181818

18191819
function getFormattingEditsForRange(fileName: string, start: number, end: number, options: FormatCodeOptions | FormatCodeSettings): TextChange[] {
18201820
const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
1821-
return formatting.formatSelection(start, end, sourceFile, formatting.getFormatContext(toEditorSettings(options)));
1821+
return formatting.formatSelection(start, end, sourceFile, formatting.getFormatContext(toEditorSettings(options), host));
18221822
}
18231823

18241824
function getFormattingEditsForDocument(fileName: string, options: FormatCodeOptions | FormatCodeSettings): TextChange[] {
1825-
return formatting.formatDocument(syntaxTreeCache.getCurrentSourceFile(fileName), formatting.getFormatContext(toEditorSettings(options)));
1825+
return formatting.formatDocument(syntaxTreeCache.getCurrentSourceFile(fileName), formatting.getFormatContext(toEditorSettings(options), host));
18261826
}
18271827

18281828
function getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions | FormatCodeSettings): TextChange[] {
18291829
const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
1830-
const formatContext = formatting.getFormatContext(toEditorSettings(options));
1830+
const formatContext = formatting.getFormatContext(toEditorSettings(options), host);
18311831

18321832
if (!isInComment(sourceFile, position)) {
18331833
switch (key) {
@@ -1849,7 +1849,7 @@ namespace ts {
18491849
synchronizeHostData();
18501850
const sourceFile = getValidSourceFile(fileName);
18511851
const span = createTextSpanFromBounds(start, end);
1852-
const formatContext = formatting.getFormatContext(formatOptions);
1852+
const formatContext = formatting.getFormatContext(formatOptions, host);
18531853

18541854
return flatMap(deduplicate<number>(errorCodes, equateValues, compareValues), errorCode => {
18551855
cancellationToken.throwIfCancellationRequested();
@@ -1861,7 +1861,7 @@ namespace ts {
18611861
synchronizeHostData();
18621862
Debug.assert(scope.type === "file");
18631863
const sourceFile = getValidSourceFile(scope.fileName);
1864-
const formatContext = formatting.getFormatContext(formatOptions);
1864+
const formatContext = formatting.getFormatContext(formatOptions, host);
18651865

18661866
return codefix.getAllFixes({ fixId, sourceFile, program, host, cancellationToken, formatContext, preferences });
18671867
}
@@ -1870,13 +1870,13 @@ namespace ts {
18701870
synchronizeHostData();
18711871
Debug.assert(scope.type === "file");
18721872
const sourceFile = getValidSourceFile(scope.fileName);
1873-
const formatContext = formatting.getFormatContext(formatOptions);
1873+
const formatContext = formatting.getFormatContext(formatOptions, host);
18741874

18751875
return OrganizeImports.organizeImports(sourceFile, formatContext, host, program, preferences);
18761876
}
18771877

18781878
function getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences = emptyOptions): readonly FileTextChanges[] {
1879-
return ts.getEditsForFileRename(getProgram()!, oldFilePath, newFilePath, host, formatting.getFormatContext(formatOptions), preferences, sourceMapper);
1879+
return ts.getEditsForFileRename(getProgram()!, oldFilePath, newFilePath, host, formatting.getFormatContext(formatOptions, host), preferences, sourceMapper);
18801880
}
18811881

18821882
function applyCodeActionCommand(action: CodeActionCommand, formatSettings?: FormatCodeSettings): Promise<ApplyCodeActionCommandResult>;
@@ -2119,7 +2119,7 @@ namespace ts {
21192119
endPosition,
21202120
program: getProgram()!,
21212121
host,
2122-
formatContext: formatting.getFormatContext(formatOptions!), // TODO: GH#18217
2122+
formatContext: formatting.getFormatContext(formatOptions!, host), // TODO: GH#18217
21232123
cancellationToken,
21242124
preferences,
21252125
};

src/services/types.ts

+5
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,11 @@ namespace ts {
201201
has(dependencyName: string, inGroups?: PackageJsonDependencyGroup): boolean;
202202
}
203203

204+
/** @internal */
205+
export interface FormattingHost {
206+
getNewLine?(): string;
207+
}
208+
204209
//
205210
// Public interface of the host of a language service instance.
206211
//

src/services/utilities.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -2085,9 +2085,9 @@ namespace ts {
20852085
/**
20862086
* The default is CRLF.
20872087
*/
2088-
export function getNewLineOrDefaultFromHost(host: LanguageServiceHost | LanguageServiceShimHost, formatSettings?: FormatCodeSettings) {
2089-
return (formatSettings && formatSettings.newLineCharacter) ||
2090-
(host.getNewLine && host.getNewLine()) ||
2088+
export function getNewLineOrDefaultFromHost(host: FormattingHost, formatSettings?: FormatCodeSettings) {
2089+
return formatSettings?.newLineCharacter ||
2090+
host.getNewLine?.() ||
20912091
carriageReturnLineFeed;
20922092
}
20932093

src/testRunner/unittests/services/convertToAsyncFunction.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ interface Array<T> {}`
306306
cancellationToken: { throwIfCancellationRequested: noop, isCancellationRequested: returnFalse },
307307
preferences: emptyOptions,
308308
host: notImplementedHost,
309-
formatContext: formatting.getFormatContext(testFormatSettings)
309+
formatContext: formatting.getFormatContext(testFormatSettings, notImplementedHost)
310310
};
311311

312312
const diagnostics = languageService.getSuggestionDiagnostics(f.path);

src/testRunner/unittests/services/extract/helpers.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ namespace ts {
102102
startPosition: selectionRange.pos,
103103
endPosition: selectionRange.end,
104104
host: notImplementedHost,
105-
formatContext: formatting.getFormatContext(testFormatSettings),
105+
formatContext: formatting.getFormatContext(testFormatSettings, notImplementedHost),
106106
preferences: emptyOptions,
107107
};
108108
const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromRange(selectionRange));
@@ -164,7 +164,7 @@ namespace ts {
164164
startPosition: selectionRange.pos,
165165
endPosition: selectionRange.end,
166166
host: notImplementedHost,
167-
formatContext: formatting.getFormatContext(testFormatSettings),
167+
formatContext: formatting.getFormatContext(testFormatSettings, notImplementedHost),
168168
preferences: emptyOptions,
169169
};
170170
const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromRange(selectionRange));

src/testRunner/unittests/services/textChanges.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace ts {
1919
const newLineCharacter = getNewLineCharacter(printerOptions);
2020

2121
function getRuleProvider(placeOpenBraceOnNewLineForFunctions: boolean): formatting.FormatContext {
22-
return formatting.getFormatContext(placeOpenBraceOnNewLineForFunctions ? { ...testFormatSettings, placeOpenBraceOnNewLineForFunctions: true } : testFormatSettings);
22+
return formatting.getFormatContext(placeOpenBraceOnNewLineForFunctions ? { ...testFormatSettings, placeOpenBraceOnNewLineForFunctions: true } : testFormatSettings, notImplementedHost);
2323
}
2424

2525
// validate that positions that were recovered from the printed text actually match positions that will be created if the same text is parsed.

tests/cases/fourslash/fourslash.ts

+2
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,8 @@ declare namespace FourSlashInterface {
394394
noMoveToNewFile(): void;
395395

396396
generateTypes(...options: GenerateTypesOptions[]): void;
397+
398+
organizeImports(newContent: string): void;
397399
}
398400
class edit {
399401
backspace(count?: number): void;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// #38548
4+
5+
////import {
6+
//// stat,
7+
//// statSync,
8+
////} from "fs";
9+
////export function fakeFn() {
10+
//// stat;
11+
//// statSync;
12+
////}
13+
14+
format.setFormatOptions({});
15+
16+
// Default newline is carriage return.
17+
// See `getNewLineOrDefaultFromHost()` in services/utilities.ts.
18+
verify.organizeImports(
19+
`import {\r\nstat,\r\nstatSync\r\n} from "fs";\r\nexport function fakeFn() {
20+
stat;
21+
statSync;
22+
}`);

0 commit comments

Comments
 (0)