Skip to content

Commit 2c34672

Browse files
committed
Merge branch 'master' into cacheUnnormalizedIntersections
2 parents 309ae22 + 907664c commit 2c34672

Some content is hidden

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

48 files changed

+1401
-30
lines changed

src/compiler/binder.ts

+22-16
Original file line numberDiff line numberDiff line change
@@ -2581,7 +2581,7 @@ namespace ts {
25812581
// Fix up parent pointers since we're going to use these nodes before we bind into them
25822582
node.left.parent = node;
25832583
node.right.parent = node;
2584-
if (isIdentifier(lhs.expression) && container === file && isNameOfExportsOrModuleExportsAliasDeclaration(file, lhs.expression)) {
2584+
if (isIdentifier(lhs.expression) && container === file && isExportsOrModuleExportsOrAlias(file, lhs.expression)) {
25852585
// This can be an alias for the 'exports' or 'module.exports' names, e.g.
25862586
// var util = module.exports;
25872587
// util.property = function ...
@@ -2975,21 +2975,27 @@ namespace ts {
29752975
}
29762976

29772977
export function isExportsOrModuleExportsOrAlias(sourceFile: SourceFile, node: Expression): boolean {
2978-
return isExportsIdentifier(node) ||
2979-
isModuleExportsPropertyAccessExpression(node) ||
2980-
isIdentifier(node) && isNameOfExportsOrModuleExportsAliasDeclaration(sourceFile, node);
2981-
}
2982-
2983-
function isNameOfExportsOrModuleExportsAliasDeclaration(sourceFile: SourceFile, node: Identifier): boolean {
2984-
const symbol = lookupSymbolForNameWorker(sourceFile, node.escapedText);
2985-
return !!symbol && !!symbol.valueDeclaration && isVariableDeclaration(symbol.valueDeclaration) &&
2986-
!!symbol.valueDeclaration.initializer && isExportsOrModuleExportsOrAliasOrAssignment(sourceFile, symbol.valueDeclaration.initializer);
2987-
}
2988-
2989-
function isExportsOrModuleExportsOrAliasOrAssignment(sourceFile: SourceFile, node: Expression): boolean {
2990-
return isExportsOrModuleExportsOrAlias(sourceFile, node) ||
2991-
(isAssignmentExpression(node, /*excludeCompoundAssignment*/ true) && (
2992-
isExportsOrModuleExportsOrAliasOrAssignment(sourceFile, node.left) || isExportsOrModuleExportsOrAliasOrAssignment(sourceFile, node.right)));
2978+
let i = 0;
2979+
const q = [node];
2980+
while (q.length && i < 100) {
2981+
i++;
2982+
node = q.shift()!;
2983+
if (isExportsIdentifier(node) || isModuleExportsPropertyAccessExpression(node)) {
2984+
return true;
2985+
}
2986+
else if (isIdentifier(node)) {
2987+
const symbol = lookupSymbolForNameWorker(sourceFile, node.escapedText);
2988+
if (!!symbol && !!symbol.valueDeclaration && isVariableDeclaration(symbol.valueDeclaration) && !!symbol.valueDeclaration.initializer) {
2989+
const init = symbol.valueDeclaration.initializer;
2990+
q.push(init);
2991+
if (isAssignmentExpression(init, /*excludeCompoundAssignment*/ true)) {
2992+
q.push(init.left);
2993+
q.push(init.right);
2994+
}
2995+
}
2996+
}
2997+
}
2998+
return false;
29932999
}
29943000

29953001
function lookupSymbolForNameWorker(container: Node, name: __String): Symbol | undefined {

src/harness/client.ts

+4
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,10 @@ namespace ts.server {
424424
return renameInfo;
425425
}
426426

427+
getSmartSelectionRange() {
428+
return notImplemented();
429+
}
430+
427431
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): RenameLocation[] {
428432
if (!this.lastRenameEntry ||
429433
this.lastRenameEntry.inputs.fileName !== fileName ||

src/harness/fourslash.ts

+44-8
Original file line numberDiff line numberDiff line change
@@ -1417,12 +1417,7 @@ Actual: ${stringify(fullActual)}`);
14171417
}
14181418

14191419
public baselineCurrentFileBreakpointLocations() {
1420-
let baselineFile = this.testData.globalOptions[MetadataOptionNames.baselineFile];
1421-
if (!baselineFile) {
1422-
baselineFile = this.activeFile.fileName.replace(this.basePath + "/breakpointValidation", "bpSpan");
1423-
baselineFile = baselineFile.replace(ts.Extension.Ts, ".baseline");
1424-
1425-
}
1420+
const baselineFile = this.getBaselineFileName().replace("breakpointValidation", "bpSpan");
14261421
Harness.Baseline.runBaseline(baselineFile, this.baselineCurrentFileLocations(pos => this.getBreakpointStatementLocation(pos)!));
14271422
}
14281423

@@ -1497,8 +1492,7 @@ Actual: ${stringify(fullActual)}`);
14971492
}
14981493

14991494
public baselineQuickInfo() {
1500-
const baselineFile = this.testData.globalOptions[MetadataOptionNames.baselineFile] ||
1501-
ts.getBaseFileName(this.activeFile.fileName).replace(ts.Extension.Ts, ".baseline");
1495+
const baselineFile = this.getBaselineFileName();
15021496
Harness.Baseline.runBaseline(
15031497
baselineFile,
15041498
stringify(
@@ -1508,6 +1502,39 @@ Actual: ${stringify(fullActual)}`);
15081502
}))));
15091503
}
15101504

1505+
public baselineSmartSelection() {
1506+
const n = "\n";
1507+
const baselineFile = this.getBaselineFileName();
1508+
const markers = this.getMarkers();
1509+
const fileContent = this.activeFile.content;
1510+
const text = markers.map(marker => {
1511+
const baselineContent = [fileContent.slice(0, marker.position) + "/**/" + fileContent.slice(marker.position) + n];
1512+
let selectionRange: ts.SelectionRange | undefined = this.languageService.getSmartSelectionRange(this.activeFile.fileName, marker.position);
1513+
while (selectionRange) {
1514+
const { textSpan } = selectionRange;
1515+
let masked = Array.from(fileContent).map((char, index) => {
1516+
const charCode = char.charCodeAt(0);
1517+
if (index >= textSpan.start && index < ts.textSpanEnd(textSpan)) {
1518+
return char === " " ? "•" : ts.isLineBreak(charCode) ? `↲${n}` : char;
1519+
}
1520+
return ts.isLineBreak(charCode) ? char : " ";
1521+
}).join("");
1522+
masked = masked.replace(/^\s*$\r?\n?/gm, ""); // Remove blank lines
1523+
const isRealCharacter = (char: string) => char !== "•" && char !== "↲" && !ts.isWhiteSpaceLike(char.charCodeAt(0));
1524+
const leadingWidth = Array.from(masked).findIndex(isRealCharacter);
1525+
const trailingWidth = ts.findLastIndex(Array.from(masked), isRealCharacter);
1526+
masked = masked.slice(0, leadingWidth)
1527+
+ masked.slice(leadingWidth, trailingWidth).replace(//g, " ").replace(//g, "")
1528+
+ masked.slice(trailingWidth);
1529+
baselineContent.push(masked);
1530+
selectionRange = selectionRange.parent;
1531+
}
1532+
return baselineContent.join(fileContent.includes("\n") ? n + n : n);
1533+
}).join(n.repeat(2) + "=".repeat(80) + n.repeat(2));
1534+
1535+
Harness.Baseline.runBaseline(baselineFile, text);
1536+
}
1537+
15111538
public printBreakpointLocation(pos: number) {
15121539
Harness.IO.log("\n**Pos: " + pos + " " + this.spanInfoToString(this.getBreakpointStatementLocation(pos)!, " "));
15131540
}
@@ -1562,6 +1589,11 @@ Actual: ${stringify(fullActual)}`);
15621589
Harness.IO.log(stringify(help.items[help.selectedItemIndex]));
15631590
}
15641591

1592+
private getBaselineFileName() {
1593+
return this.testData.globalOptions[MetadataOptionNames.baselineFile] ||
1594+
ts.getBaseFileName(this.activeFile.fileName).replace(ts.Extension.Ts, ".baseline");
1595+
}
1596+
15651597
private getSignatureHelp({ triggerReason }: FourSlashInterface.VerifySignatureHelpOptions): ts.SignatureHelpItems | undefined {
15661598
return this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition, {
15671599
triggerReason
@@ -3960,6 +3992,10 @@ namespace FourSlashInterface {
39603992
this.state.baselineQuickInfo();
39613993
}
39623994

3995+
public baselineSmartSelection() {
3996+
this.state.baselineSmartSelection();
3997+
}
3998+
39633999
public nameOrDottedNameSpanTextIs(text: string) {
39644000
this.state.verifyCurrentNameOrDottedNameSpanText(text);
39654001
}

src/harness/harnessLanguageService.ts

+3
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,9 @@ namespace Harness.LanguageService {
472472
getRenameInfo(fileName: string, position: number, options?: ts.RenameInfoOptions): ts.RenameInfo {
473473
return unwrapJSONCallResult(this.shim.getRenameInfo(fileName, position, options));
474474
}
475+
getSmartSelectionRange(fileName: string, position: number): ts.SelectionRange {
476+
return unwrapJSONCallResult(this.shim.getSmartSelectionRange(fileName, position));
477+
}
475478
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): ts.RenameLocation[] {
476479
return unwrapJSONCallResult(this.shim.findRenameLocations(fileName, position, findInStrings, findInComments, providePrefixAndSuffixTextForRename));
477480
}

src/server/protocol.ts

+22-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,10 @@ namespace ts.server.protocol {
130130
GetEditsForFileRename = "getEditsForFileRename",
131131
/* @internal */
132132
GetEditsForFileRenameFull = "getEditsForFileRename-full",
133-
ConfigurePlugin = "configurePlugin"
133+
ConfigurePlugin = "configurePlugin",
134+
SelectionRange = "selectionRange",
135+
/* @internal */
136+
SelectionRangeFull = "selectionRange-full",
134137

135138
// NOTE: If updating this, be sure to also update `allCommandNames` in `harness/unittests/session.ts`.
136139
}
@@ -1395,6 +1398,24 @@ namespace ts.server.protocol {
13951398
export interface ConfigurePluginResponse extends Response {
13961399
}
13971400

1401+
export interface SelectionRangeRequest extends FileRequest {
1402+
command: CommandTypes.SelectionRange;
1403+
arguments: SelectionRangeRequestArgs;
1404+
}
1405+
1406+
export interface SelectionRangeRequestArgs extends FileRequestArgs {
1407+
locations: Location[];
1408+
}
1409+
1410+
export interface SelectionRangeResponse extends Response {
1411+
body?: SelectionRange[];
1412+
}
1413+
1414+
export interface SelectionRange {
1415+
textSpan: TextSpan;
1416+
parent?: SelectionRange;
1417+
}
1418+
13981419
/**
13991420
* Information found in an "open" request.
14001421
*/

src/server/session.ts

+31-3
Original file line numberDiff line numberDiff line change
@@ -1318,11 +1318,11 @@ namespace ts.server {
13181318
this.projectService.openClientFileWithNormalizedPath(fileName, fileContent, scriptKind, /*hasMixedContent*/ false, projectRootPath);
13191319
}
13201320

1321-
private getPosition(args: protocol.FileLocationRequestArgs, scriptInfo: ScriptInfo): number {
1321+
private getPosition(args: protocol.Location & { position?: number }, scriptInfo: ScriptInfo): number {
13221322
return args.position !== undefined ? args.position : scriptInfo.lineOffsetToPosition(args.line, args.offset);
13231323
}
13241324

1325-
private getPositionInFile(args: protocol.FileLocationRequestArgs, file: NormalizedPath): number {
1325+
private getPositionInFile(args: protocol.Location & { position?: number }, file: NormalizedPath): number {
13261326
const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file)!;
13271327
return this.getPosition(args, scriptInfo);
13281328
}
@@ -2059,6 +2059,28 @@ namespace ts.server {
20592059
this.projectService.configurePlugin(args);
20602060
}
20612061

2062+
private getSmartSelectionRange(args: protocol.SelectionRangeRequestArgs, simplifiedResult: boolean) {
2063+
const { locations } = args;
2064+
const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args);
2065+
const scriptInfo = Debug.assertDefined(this.projectService.getScriptInfo(file));
2066+
2067+
return map(locations, location => {
2068+
const pos = this.getPosition(location, scriptInfo);
2069+
const selectionRange = languageService.getSmartSelectionRange(file, pos);
2070+
return simplifiedResult ? this.mapSelectionRange(selectionRange, scriptInfo) : selectionRange;
2071+
});
2072+
}
2073+
2074+
private mapSelectionRange(selectionRange: SelectionRange, scriptInfo: ScriptInfo): protocol.SelectionRange {
2075+
const result: protocol.SelectionRange = {
2076+
textSpan: this.toLocationTextSpan(selectionRange.textSpan, scriptInfo),
2077+
};
2078+
if (selectionRange.parent) {
2079+
result.parent = this.mapSelectionRange(selectionRange.parent, scriptInfo);
2080+
}
2081+
return result;
2082+
}
2083+
20622084
getCanonicalFileName(fileName: string) {
20632085
const name = this.host.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
20642086
return normalizePath(name);
@@ -2414,7 +2436,13 @@ namespace ts.server {
24142436
this.configurePlugin(request.arguments);
24152437
this.doOutput(/*info*/ undefined, CommandNames.ConfigurePlugin, request.seq, /*success*/ true);
24162438
return this.notRequired();
2417-
}
2439+
},
2440+
[CommandNames.SelectionRange]: (request: protocol.SelectionRangeRequest) => {
2441+
return this.requiredResponse(this.getSmartSelectionRange(request.arguments, /*simplifiedResult*/ true));
2442+
},
2443+
[CommandNames.SelectionRangeFull]: (request: protocol.SelectionRangeRequest) => {
2444+
return this.requiredResponse(this.getSmartSelectionRange(request.arguments, /*simplifiedResult*/ false));
2445+
},
24182446
});
24192447

24202448
public addProtocolHandler(command: string, handler: (request: protocol.Request) => HandlerResponse) {

src/services/services.ts

+5
Original file line numberDiff line numberDiff line change
@@ -2080,6 +2080,10 @@ namespace ts {
20802080
};
20812081
}
20822082

2083+
function getSmartSelectionRange(fileName: string, position: number): SelectionRange {
2084+
return SmartSelectionRange.getSmartSelectionRange(position, syntaxTreeCache.getCurrentSourceFile(fileName));
2085+
}
2086+
20832087
function getApplicableRefactors(fileName: string, positionOrRange: number | TextRange, preferences: UserPreferences = emptyOptions): ApplicableRefactorInfo[] {
20842088
synchronizeHostData();
20852089
const file = getValidSourceFile(fileName);
@@ -2127,6 +2131,7 @@ namespace ts {
21272131
getBreakpointStatementAtPosition,
21282132
getNavigateToItems,
21292133
getRenameInfo,
2134+
getSmartSelectionRange,
21302135
findRenameLocations,
21312136
getNavigationBarItems,
21322137
getNavigationTree,

src/services/shims.ts

+8
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ namespace ts {
165165
* { canRename: boolean, localizedErrorMessage: string, displayName: string, fullDisplayName: string, kind: string, kindModifiers: string, triggerSpan: { start; length } }
166166
*/
167167
getRenameInfo(fileName: string, position: number, options?: RenameInfoOptions): string;
168+
getSmartSelectionRange(fileName: string, position: number): string;
168169

169170
/**
170171
* Returns a JSON-encoded value of the type:
@@ -838,6 +839,13 @@ namespace ts {
838839
);
839840
}
840841

842+
public getSmartSelectionRange(fileName: string, position: number): string {
843+
return this.forwardJSONCall(
844+
`getSmartSelectionRange('${fileName}', ${position})`,
845+
() => this.languageService.getSmartSelectionRange(fileName, position)
846+
);
847+
}
848+
841849
public findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): string {
842850
return this.forwardJSONCall(
843851
`findRenameLocations('${fileName}', ${position}, ${findInStrings}, ${findInComments}, ${providePrefixAndSuffixTextForRename})`,

0 commit comments

Comments
 (0)