Skip to content

Commit 3c0fb4c

Browse files
committed
Merge branch 'master' into nodeFactory
2 parents 9821326 + 540c219 commit 3c0fb4c

File tree

17 files changed

+262
-24
lines changed

17 files changed

+262
-24
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# TypeScript
33

44
[![Build Status](https://travis-ci.org/microsoft/TypeScript.svg?branch=master)](https://travis-ci.org/microsoft/TypeScript)
5-
[![VSTS Build Status](https://dev.azure.com/typescript/TypeScript/_apis/build/status/Typescript/node10)](https://dev.azure.com/typescript/TypeScript/_build/latest?definitionId=4&view=logs)
5+
[![Devops Build Status](https://dev.azure.com/typescript/TypeScript/_apis/build/status/Typescript/node10)](https://dev.azure.com/typescript/TypeScript/_build?definitionId=7)
66
[![npm version](https://badge.fury.io/js/typescript.svg)](https://www.npmjs.com/package/typescript)
77
[![Downloads](https://img.shields.io/npm/dm/typescript.svg)](https://www.npmjs.com/package/typescript)
88

src/compiler/sys.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1231,7 +1231,7 @@ namespace ts {
12311231
enableCPUProfiler,
12321232
disableCPUProfiler,
12331233
realpath,
1234-
debugMode: !!process.env.NODE_INSPECTOR_IPC || some(<string[]>process.execArgv, arg => /^--(inspect|debug)(-brk)?(=\d+)?$/i.test(arg)),
1234+
debugMode: !!process.env.NODE_INSPECTOR_IPC || !!process.env.VSCODE_INSPECTOR_OPTIONS || some(<string[]>process.execArgv, arg => /^--(inspect|debug)(-brk)?(=\d+)?$/i.test(arg)),
12351235
tryEnableSourceMapsForHost() {
12361236
try {
12371237
require("source-map-support").install();

src/compiler/utilities.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2595,8 +2595,8 @@ namespace ts {
25952595
switch (parent.kind) {
25962596
case SyntaxKind.BinaryExpression:
25972597
const binaryOperator = (<BinaryExpression>parent).operatorToken.kind;
2598-
return isAssignmentOperator(binaryOperator) && !isLogicalOrCoalescingAssignmentOperator(binaryOperator) && (<BinaryExpression>parent).left === node ?
2599-
binaryOperator === SyntaxKind.EqualsToken ? AssignmentKind.Definite : AssignmentKind.Compound :
2598+
return isAssignmentOperator(binaryOperator) && (<BinaryExpression>parent).left === node ?
2599+
binaryOperator === SyntaxKind.EqualsToken || isLogicalOrCoalescingAssignmentOperator(binaryOperator) ? AssignmentKind.Definite : AssignmentKind.Compound :
26002600
AssignmentKind.None;
26012601
case SyntaxKind.PrefixUnaryExpression:
26022602
case SyntaxKind.PostfixUnaryExpression:

src/compiler/watch.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -234,13 +234,14 @@ namespace ts {
234234
}
235235

236236
export const noopFileWatcher: FileWatcher = { close: noop };
237+
export const returnNoopFileWatcher = () => noopFileWatcher;
237238

238239
export function createWatchHost(system = sys, reportWatchStatus?: WatchStatusReporter): WatchHost {
239240
const onWatchStatusChange = reportWatchStatus || createWatchStatusReporter(system);
240241
return {
241242
onWatchStatusChange,
242-
watchFile: maybeBind(system, system.watchFile) || (() => noopFileWatcher),
243-
watchDirectory: maybeBind(system, system.watchDirectory) || (() => noopFileWatcher),
243+
watchFile: maybeBind(system, system.watchFile) || returnNoopFileWatcher,
244+
watchDirectory: maybeBind(system, system.watchDirectory) || returnNoopFileWatcher,
244245
setTimeout: maybeBind(system, system.setTimeout) || noop,
245246
clearTimeout: maybeBind(system, system.clearTimeout) || noop
246247
};

src/lib/es2020.intl.d.ts

+8
Original file line numberDiff line numberDiff line change
@@ -261,4 +261,12 @@ declare namespace Intl {
261261
options?: RelativeTimeFormatOptions,
262262
): BCP47LanguageTag[];
263263
};
264+
265+
interface NumberFormatOptions {
266+
notation?: string;
267+
}
268+
269+
interface ResolvedNumberFormatOptions {
270+
notation?: string;
271+
}
264272
}

src/server/editorServices.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -725,7 +725,13 @@ namespace ts.server {
725725
const watchLogLevel = this.logger.hasLevel(LogLevel.verbose) ? WatchLogLevel.Verbose :
726726
this.logger.loggingEnabled() ? WatchLogLevel.TriggerOnly : WatchLogLevel.None;
727727
const log: (s: string) => void = watchLogLevel !== WatchLogLevel.None ? (s => this.logger.info(s)) : noop;
728-
this.watchFactory = getWatchFactory(watchLogLevel, log, getDetailWatchInfo);
728+
this.watchFactory = this.syntaxOnly ?
729+
{
730+
watchFile: returnNoopFileWatcher,
731+
watchFilePath: returnNoopFileWatcher,
732+
watchDirectory: returnNoopFileWatcher,
733+
} :
734+
getWatchFactory(watchLogLevel, log, getDetailWatchInfo);
729735
}
730736

731737
toPath(fileName: string) {

src/server/project.ts

+8-6
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,10 @@ namespace ts.server {
279279
this.compilerOptions.allowNonTsExtensions = true;
280280
}
281281

282-
this.languageServiceEnabled = !projectService.syntaxOnly;
282+
this.languageServiceEnabled = true;
283+
if (projectService.syntaxOnly) {
284+
this.compilerOptions.noResolve = true;
285+
}
283286

284287
this.setInternalCompilerOptionsForEmittingJsFiles();
285288
const host = this.projectService.host;
@@ -293,7 +296,7 @@ namespace ts.server {
293296

294297
// Use the current directory as resolution root only if the project created using current directory string
295298
this.resolutionCache = createResolutionCache(this, currentDirectory && this.currentDirectory, /*logChangesWhenResolvingModule*/ true);
296-
this.languageService = createLanguageService(this, this.documentRegistry, projectService.syntaxOnly);
299+
this.languageService = createLanguageService(this, this.documentRegistry, this.projectService.syntaxOnly);
297300
if (lastFileExceededProgramSize) {
298301
this.disableLanguageService(lastFileExceededProgramSize);
299302
}
@@ -642,7 +645,7 @@ namespace ts.server {
642645
}
643646

644647
enableLanguageService() {
645-
if (this.languageServiceEnabled || this.projectService.syntaxOnly) {
648+
if (this.languageServiceEnabled) {
646649
return;
647650
}
648651
this.languageServiceEnabled = true;
@@ -654,7 +657,6 @@ namespace ts.server {
654657
if (!this.languageServiceEnabled) {
655658
return;
656659
}
657-
Debug.assert(!this.projectService.syntaxOnly);
658660
this.languageService.cleanupSemanticCache();
659661
this.languageServiceEnabled = false;
660662
this.lastFileExceededProgramSize = lastFileExceededProgramSize;
@@ -970,7 +972,7 @@ namespace ts.server {
970972

971973
// update builder only if language service is enabled
972974
// otherwise tell it to drop its internal state
973-
if (this.languageServiceEnabled) {
975+
if (this.languageServiceEnabled && !this.projectService.syntaxOnly) {
974976
// 1. no changes in structure, no changes in unresolved imports - do nothing
975977
// 2. no changes in structure, unresolved imports were changed - collect unresolved imports for all files
976978
// (can reuse cached imports for files that were not changed)
@@ -1092,7 +1094,7 @@ namespace ts.server {
10921094
}
10931095

10941096
// Watch the type locations that would be added to program as part of automatic type resolutions
1095-
if (this.languageServiceEnabled) {
1097+
if (this.languageServiceEnabled && !this.projectService.syntaxOnly) {
10961098
this.resolutionCache.updateTypeRootsWatch();
10971099
}
10981100
}

src/server/session.ts

+47-2
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,42 @@ namespace ts.server {
575575
undefined;
576576
}
577577

578+
const invalidSyntaxOnlyCommands: readonly CommandNames[] = [
579+
CommandNames.OpenExternalProject,
580+
CommandNames.OpenExternalProjects,
581+
CommandNames.CloseExternalProject,
582+
CommandNames.SynchronizeProjectList,
583+
CommandNames.EmitOutput,
584+
CommandNames.CompileOnSaveAffectedFileList,
585+
CommandNames.CompileOnSaveEmitFile,
586+
CommandNames.CompilerOptionsDiagnosticsFull,
587+
CommandNames.EncodedSemanticClassificationsFull,
588+
CommandNames.SemanticDiagnosticsSync,
589+
CommandNames.SyntacticDiagnosticsSync,
590+
CommandNames.SuggestionDiagnosticsSync,
591+
CommandNames.Geterr,
592+
CommandNames.GeterrForProject,
593+
CommandNames.Reload,
594+
CommandNames.ReloadProjects,
595+
CommandNames.GetCodeFixes,
596+
CommandNames.GetCodeFixesFull,
597+
CommandNames.GetCombinedCodeFix,
598+
CommandNames.GetCombinedCodeFixFull,
599+
CommandNames.ApplyCodeActionCommand,
600+
CommandNames.GetSupportedCodeFixes,
601+
CommandNames.GetApplicableRefactors,
602+
CommandNames.GetEditsForRefactor,
603+
CommandNames.GetEditsForRefactorFull,
604+
CommandNames.OrganizeImports,
605+
CommandNames.OrganizeImportsFull,
606+
CommandNames.GetEditsForFileRename,
607+
CommandNames.GetEditsForFileRenameFull,
608+
CommandNames.ConfigurePlugin,
609+
CommandNames.PrepareCallHierarchy,
610+
CommandNames.ProvideCallHierarchyIncomingCalls,
611+
CommandNames.ProvideCallHierarchyOutgoingCalls,
612+
];
613+
578614
export interface SessionOptions {
579615
host: ServerHost;
580616
cancellationToken: ServerCancellationToken;
@@ -667,6 +703,15 @@ namespace ts.server {
667703
this.projectService = new ProjectService(settings);
668704
this.projectService.setPerformanceEventHandler(this.performanceEventHandler.bind(this));
669705
this.gcTimer = new GcTimer(this.host, /*delay*/ 7000, this.logger);
706+
707+
// Make sure to setup handlers to throw error for not allowed commands on syntax server;
708+
if (this.projectService.syntaxOnly) {
709+
invalidSyntaxOnlyCommands.forEach(commandName =>
710+
this.handlers.set(commandName, request => {
711+
throw new Error(`Request: ${request.command} not allowed on syntaxServer`);
712+
})
713+
);
714+
}
670715
}
671716

672717
private sendRequestCompletedEvent(requestId: number): void {
@@ -1253,9 +1298,9 @@ namespace ts.server {
12531298
}
12541299

12551300
private getJsxClosingTag(args: protocol.JsxClosingTagRequestArgs): TextInsertion | undefined {
1256-
const { file, project } = this.getFileAndProject(args);
1301+
const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args);
12571302
const position = this.getPositionInFile(args, file);
1258-
const tag = project.getLanguageService().getJsxClosingTagAtPosition(file, position);
1303+
const tag = languageService.getJsxClosingTagAtPosition(file, position);
12591304
return tag === undefined ? undefined : { newText: tag.newText, caretOffset: 0 };
12601305
}
12611306

src/services/services.ts

+31-8
Original file line numberDiff line numberDiff line change
@@ -1171,6 +1171,26 @@ namespace ts {
11711171
}
11721172
}
11731173

1174+
const invalidOperationsOnSyntaxOnly: readonly (keyof LanguageService)[] = [
1175+
"getSyntacticDiagnostics",
1176+
"getSemanticDiagnostics",
1177+
"getSuggestionDiagnostics",
1178+
"getCompilerOptionsDiagnostics",
1179+
"getSemanticClassifications",
1180+
"getEncodedSemanticClassifications",
1181+
"getCodeFixesAtPosition",
1182+
"getCombinedCodeFix",
1183+
"applyCodeActionCommand",
1184+
"organizeImports",
1185+
"getEditsForFileRename",
1186+
"getEmitOutput",
1187+
"getApplicableRefactors",
1188+
"getEditsForRefactor",
1189+
"prepareCallHierarchy",
1190+
"provideCallHierarchyIncomingCalls",
1191+
"provideCallHierarchyOutgoingCalls",
1192+
];
1193+
11741194
export function createLanguageService(
11751195
host: LanguageServiceHost,
11761196
documentRegistry: DocumentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames(), host.getCurrentDirectory()),
@@ -1224,8 +1244,6 @@ namespace ts {
12241244
}
12251245

12261246
function synchronizeHostData(): void {
1227-
Debug.assert(!syntaxOnly);
1228-
12291247
// perform fast check if host supports it
12301248
if (host.getProjectVersion) {
12311249
const hostProjectVersion = host.getProjectVersion();
@@ -1419,11 +1437,6 @@ namespace ts {
14191437

14201438
// TODO: GH#18217 frequently asserted as defined
14211439
function getProgram(): Program | undefined {
1422-
if (syntaxOnly) {
1423-
Debug.assert(program === undefined);
1424-
return undefined;
1425-
}
1426-
14271440
synchronizeHostData();
14281441

14291442
return program;
@@ -2199,7 +2212,7 @@ namespace ts {
21992212
return declaration ? CallHierarchy.getOutgoingCalls(program, declaration) : [];
22002213
}
22012214

2202-
return {
2215+
const ls: LanguageService = {
22032216
dispose,
22042217
cleanupSemanticCache,
22052218
getSyntacticDiagnostics,
@@ -2259,6 +2272,16 @@ namespace ts {
22592272
provideCallHierarchyIncomingCalls,
22602273
provideCallHierarchyOutgoingCalls
22612274
};
2275+
2276+
if (syntaxOnly) {
2277+
invalidOperationsOnSyntaxOnly.forEach(key =>
2278+
ls[key] = () => {
2279+
throw new Error(`LanguageService Operation: ${key} not allowed on syntaxServer`);
2280+
}
2281+
);
2282+
}
2283+
2284+
return ls;
22622285
}
22632286

22642287
/* @internal */

src/testRunner/tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@
181181
"unittests/tsserver/reload.ts",
182182
"unittests/tsserver/rename.ts",
183183
"unittests/tsserver/resolutionCache.ts",
184+
"unittests/tsserver/semanticOperationsOnSyntaxServer.ts",
184185
"unittests/tsserver/smartSelection.ts",
185186
"unittests/tsserver/session.ts",
186187
"unittests/tsserver/skipLibCheck.ts",

src/testRunner/unittests/tsserver/inferredProjects.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ namespace ts.projectSystem {
8686
const proj = projectService.inferredProjects[0];
8787
assert.isDefined(proj);
8888

89-
assert.isFalse(proj.languageServiceEnabled);
89+
assert.isTrue(proj.languageServiceEnabled);
9090
});
9191

9292
it("project settings for inferred projects", () => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
namespace ts.projectSystem {
2+
describe("unittests:: tsserver:: Semantic operations on Syntax server", () => {
3+
function setup() {
4+
const file1: File = {
5+
path: `${tscWatch.projectRoot}/a.ts`,
6+
content: `import { y } from "./b";
7+
class c { prop = "hello"; foo() { return this.prop; } }`
8+
};
9+
const file2: File = {
10+
path: `${tscWatch.projectRoot}/b.ts`,
11+
content: "export const y = 10;"
12+
};
13+
const configFile: File = {
14+
path: `${tscWatch.projectRoot}/tsconfig.json`,
15+
content: "{}"
16+
};
17+
const host = createServerHost([file1, file2, libFile, configFile]);
18+
const session = createSession(host, { syntaxOnly: true, useSingleInferredProject: true });
19+
return { host, session, file1, file2, configFile };
20+
}
21+
22+
it("open files are added to inferred project even if config file is present and semantic operations succeed", () => {
23+
const { host, session, file1, file2 } = setup();
24+
const service = session.getProjectService();
25+
openFilesForSession([file1], session);
26+
checkNumberOfProjects(service, { inferredProjects: 1 });
27+
const project = service.inferredProjects[0];
28+
checkProjectActualFiles(project, [libFile.path, file1.path]); // Import is not resolved
29+
verifyCompletions();
30+
31+
openFilesForSession([file2], session);
32+
checkNumberOfProjects(service, { inferredProjects: 1 });
33+
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path]);
34+
verifyCompletions();
35+
36+
function verifyCompletions() {
37+
assert.isTrue(project.languageServiceEnabled);
38+
checkWatchedFiles(host, emptyArray);
39+
checkWatchedDirectories(host, emptyArray, /*recursive*/ true);
40+
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
41+
const response = session.executeCommandSeq<protocol.CompletionsRequest>({
42+
command: protocol.CommandTypes.Completions,
43+
arguments: protocolFileLocationFromSubstring(file1, "prop", { index: 1 })
44+
}).response as protocol.CompletionEntry[];
45+
assert.deepEqual(response, [
46+
completionEntry("foo", ScriptElementKind.memberFunctionElement),
47+
completionEntry("prop", ScriptElementKind.memberVariableElement),
48+
]);
49+
}
50+
51+
function completionEntry(name: string, kind: ScriptElementKind): protocol.CompletionEntry {
52+
return {
53+
name,
54+
kind,
55+
kindModifiers: "",
56+
sortText: Completions.SortText.LocationPriority,
57+
hasAction: undefined,
58+
insertText: undefined,
59+
isRecommended: undefined,
60+
replacementSpan: undefined,
61+
source: undefined
62+
};
63+
}
64+
});
65+
66+
it("throws on unsupported commands", () => {
67+
const { session, file1 } = setup();
68+
const service = session.getProjectService();
69+
openFilesForSession([file1], session);
70+
let hasException = false;
71+
const request: protocol.SemanticDiagnosticsSyncRequest = {
72+
type: "request",
73+
seq: 1,
74+
command: protocol.CommandTypes.SemanticDiagnosticsSync,
75+
arguments: { file: file1.path }
76+
};
77+
try {
78+
session.executeCommand(request);
79+
}
80+
catch (e) {
81+
assert.equal(e.message, `Request: semanticDiagnosticsSync not allowed on syntaxServer`);
82+
hasException = true;
83+
}
84+
assert.isTrue(hasException);
85+
86+
hasException = false;
87+
const project = service.inferredProjects[0];
88+
try {
89+
project.getLanguageService().getSemanticDiagnostics(file1.path);
90+
}
91+
catch (e) {
92+
assert.equal(e.message, `LanguageService Operation: getSemanticDiagnostics not allowed on syntaxServer`);
93+
hasException = true;
94+
}
95+
assert.isTrue(hasException);
96+
});
97+
});
98+
}

0 commit comments

Comments
 (0)