Skip to content

Commit aee78ac

Browse files
Add separate flag serverMode for server mode (#39735)
* Add separate flag serverMode for server mode to allow back compatibility * Addressed code review feedback. Co-authored-by: Daniel Rosenwasser <DanielRosenwasser@users.noreply.github.com>
1 parent 86a87c4 commit aee78ac

File tree

12 files changed

+470
-104
lines changed

12 files changed

+470
-104
lines changed

src/server/editorServices.ts

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,9 @@ namespace ts.server {
396396
pluginProbeLocations?: readonly string[];
397397
allowLocalPluginLoads?: boolean;
398398
typesMapLocation?: string;
399+
/** @deprecated use serverMode instead */
399400
syntaxOnly?: boolean;
401+
serverMode?: LanguageServiceMode;
400402
}
401403

402404
interface OriginalFileInfo { fileName: NormalizedPath; path: Path; }
@@ -683,7 +685,9 @@ namespace ts.server {
683685

684686
public readonly typesMapLocation: string | undefined;
685687

686-
public readonly syntaxOnly?: boolean;
688+
/** @deprecated use serverMode instead */
689+
public readonly syntaxOnly: boolean;
690+
public readonly serverMode: LanguageServiceMode;
687691

688692
/** Tracks projects that we have already sent telemetry for. */
689693
private readonly seenProjects = new Map<string, true>();
@@ -713,7 +717,18 @@ namespace ts.server {
713717
this.pluginProbeLocations = opts.pluginProbeLocations || emptyArray;
714718
this.allowLocalPluginLoads = !!opts.allowLocalPluginLoads;
715719
this.typesMapLocation = (opts.typesMapLocation === undefined) ? combinePaths(getDirectoryPath(this.getExecutingFilePath()), "typesMap.json") : opts.typesMapLocation;
716-
this.syntaxOnly = opts.syntaxOnly;
720+
if (opts.serverMode !== undefined) {
721+
this.serverMode = opts.serverMode;
722+
this.syntaxOnly = this.serverMode === LanguageServiceMode.SyntaxOnly;
723+
}
724+
else if (opts.syntaxOnly) {
725+
this.serverMode = LanguageServiceMode.SyntaxOnly;
726+
this.syntaxOnly = true;
727+
}
728+
else {
729+
this.serverMode = LanguageServiceMode.Semantic;
730+
this.syntaxOnly = false;
731+
}
717732

718733
Debug.assert(!!this.host.createHash, "'ServerHost.createHash' is required for ProjectService");
719734
if (this.host.realpath) {
@@ -749,7 +764,7 @@ namespace ts.server {
749764
this.logger.loggingEnabled() ? WatchLogLevel.TriggerOnly : WatchLogLevel.None;
750765
const log: (s: string) => void = watchLogLevel !== WatchLogLevel.None ? (s => this.logger.info(s)) : noop;
751766
this.packageJsonCache = createPackageJsonCache(this);
752-
this.watchFactory = this.syntaxOnly ?
767+
this.watchFactory = this.serverMode !== LanguageServiceMode.Semantic ?
753768
{
754769
watchFile: returnNoopFileWatcher,
755770
watchFilePath: returnNoopFileWatcher,
@@ -1727,7 +1742,7 @@ namespace ts.server {
17271742
* the newly opened file.
17281743
*/
17291744
private forEachConfigFileLocation(info: OpenScriptInfoOrClosedOrConfigFileInfo, action: (configFileName: NormalizedPath, canonicalConfigFilePath: string) => boolean | void) {
1730-
if (this.syntaxOnly) {
1745+
if (this.serverMode !== LanguageServiceMode.Semantic) {
17311746
return undefined;
17321747
}
17331748

@@ -3014,15 +3029,15 @@ namespace ts.server {
30143029
let retainProjects: ConfiguredProject[] | ConfiguredProject | undefined;
30153030
let projectForConfigFileDiag: ConfiguredProject | undefined;
30163031
let defaultConfigProjectIsCreated = false;
3017-
if (this.syntaxOnly) {
3032+
if (this.serverMode === LanguageServiceMode.ApproximateSemanticOnly) {
30183033
// Invalidate resolutions in the file since this file is now open
30193034
info.containingProjects.forEach(project => {
30203035
if (project.resolutionCache.removeRelativeNoResolveResolutionsOfFile(info.path)) {
30213036
project.markAsDirty();
30223037
}
30233038
});
30243039
}
3025-
else if (!project) { // Checking syntaxOnly is an optimization
3040+
else if (!project && this.serverMode === LanguageServiceMode.Semantic) { // Checking semantic mode is an optimization
30263041
configFileName = this.getConfigFileNameForFile(info);
30273042
if (configFileName) {
30283043
project = this.findConfiguredProjectByProjectName(configFileName);
@@ -3109,7 +3124,7 @@ namespace ts.server {
31093124
Debug.assert(this.openFiles.has(info.path));
31103125
this.assignOrphanScriptInfoToInferredProject(info, this.openFiles.get(info.path));
31113126
}
3112-
else if (this.syntaxOnly && info.cacheSourceFile?.sourceFile.referencedFiles.length) {
3127+
else if (this.serverMode === LanguageServiceMode.ApproximateSemanticOnly && info.cacheSourceFile?.sourceFile.referencedFiles.length) {
31133128
// This file was just opened and references in this file will previously not been resolved so schedule update
31143129
info.containingProjects.forEach(project => project.markAsDirty());
31153130
}
@@ -3325,7 +3340,7 @@ namespace ts.server {
33253340
}
33263341

33273342
private telemetryOnOpenFile(scriptInfo: ScriptInfo): void {
3328-
if (this.syntaxOnly || !this.eventHandler || !scriptInfo.isJavaScript() || !addToSeen(this.allJsFilesForOpenFileTelemetry, scriptInfo.path)) {
3343+
if (this.serverMode !== LanguageServiceMode.Semantic || !this.eventHandler || !scriptInfo.isJavaScript() || !addToSeen(this.allJsFilesForOpenFileTelemetry, scriptInfo.path)) {
33293344
return;
33303345
}
33313346

@@ -3637,7 +3652,7 @@ namespace ts.server {
36373652
for (const file of proj.rootFiles) {
36383653
const normalized = toNormalizedPath(file.fileName);
36393654
if (getBaseConfigFileName(normalized)) {
3640-
if (!this.syntaxOnly && this.host.fileExists(normalized)) {
3655+
if (this.serverMode === LanguageServiceMode.Semantic && this.host.fileExists(normalized)) {
36413656
(tsConfigFiles || (tsConfigFiles = [])).push(normalized);
36423657
}
36433658
}

src/server/project.ts

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -279,9 +279,21 @@ namespace ts.server {
279279
this.compilerOptions.allowNonTsExtensions = true;
280280
}
281281

282-
this.languageServiceEnabled = true;
283-
if (projectService.syntaxOnly) {
284-
this.compilerOptions.types = [];
282+
switch (projectService.serverMode) {
283+
case LanguageServiceMode.Semantic:
284+
this.languageServiceEnabled = true;
285+
break;
286+
case LanguageServiceMode.ApproximateSemanticOnly:
287+
this.languageServiceEnabled = true;
288+
this.compilerOptions.types = [];
289+
break;
290+
case LanguageServiceMode.SyntaxOnly:
291+
this.languageServiceEnabled = false;
292+
this.compilerOptions.noResolve = true;
293+
this.compilerOptions.types = [];
294+
break;
295+
default:
296+
Debug.assertNever(projectService.serverMode);
285297
}
286298

287299
this.setInternalCompilerOptionsForEmittingJsFiles();
@@ -298,10 +310,10 @@ namespace ts.server {
298310
this.resolutionCache = createResolutionCache(
299311
this,
300312
currentDirectory && this.currentDirectory,
301-
projectService.syntaxOnly ? ResolutionKind.RelativeReferencesInOpenFileOnly : ResolutionKind.All,
313+
projectService.serverMode === LanguageServiceMode.Semantic ? ResolutionKind.All : ResolutionKind.RelativeReferencesInOpenFileOnly,
302314
/*logChangesWhenResolvingModule*/ true
303315
);
304-
this.languageService = createLanguageService(this, this.documentRegistry, this.projectService.syntaxOnly);
316+
this.languageService = createLanguageService(this, this.documentRegistry, this.projectService.serverMode);
305317
if (lastFileExceededProgramSize) {
306318
this.disableLanguageService(lastFileExceededProgramSize);
307319
}
@@ -456,7 +468,16 @@ namespace ts.server {
456468

457469
/*@internal*/
458470
includeTripleslashReferencesFrom(containingFile: string) {
459-
return !this.projectService.syntaxOnly || this.fileIsOpen(this.toPath(containingFile));
471+
switch (this.projectService.serverMode) {
472+
case LanguageServiceMode.Semantic:
473+
return true;
474+
case LanguageServiceMode.ApproximateSemanticOnly:
475+
return this.fileIsOpen(this.toPath(containingFile));
476+
case LanguageServiceMode.SyntaxOnly:
477+
return false;
478+
default:
479+
Debug.assertNever(this.projectService.serverMode);
480+
}
460481
}
461482

462483
directoryExists(path: string): boolean {
@@ -656,7 +677,7 @@ namespace ts.server {
656677
}
657678

658679
enableLanguageService() {
659-
if (this.languageServiceEnabled) {
680+
if (this.languageServiceEnabled || this.projectService.serverMode === LanguageServiceMode.SyntaxOnly) {
660681
return;
661682
}
662683
this.languageServiceEnabled = true;
@@ -668,6 +689,7 @@ namespace ts.server {
668689
if (!this.languageServiceEnabled) {
669690
return;
670691
}
692+
Debug.assert(this.projectService.serverMode !== LanguageServiceMode.SyntaxOnly);
671693
this.languageService.cleanupSemanticCache();
672694
this.languageServiceEnabled = false;
673695
this.lastFileExceededProgramSize = lastFileExceededProgramSize;
@@ -997,7 +1019,7 @@ namespace ts.server {
9971019

9981020
// update builder only if language service is enabled
9991021
// otherwise tell it to drop its internal state
1000-
if (this.languageServiceEnabled && !this.projectService.syntaxOnly) {
1022+
if (this.languageServiceEnabled && this.projectService.serverMode === LanguageServiceMode.Semantic) {
10011023
// 1. no changes in structure, no changes in unresolved imports - do nothing
10021024
// 2. no changes in structure, unresolved imports were changed - collect unresolved imports for all files
10031025
// (can reuse cached imports for files that were not changed)
@@ -1128,7 +1150,7 @@ namespace ts.server {
11281150
}
11291151

11301152
// Watch the type locations that would be added to program as part of automatic type resolutions
1131-
if (this.languageServiceEnabled && !this.projectService.syntaxOnly) {
1153+
if (this.languageServiceEnabled && this.projectService.serverMode === LanguageServiceMode.Semantic) {
11321154
this.resolutionCache.updateTypeRootsWatch();
11331155
}
11341156
}

src/server/session.ts

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ namespace ts.server {
585585
undefined;
586586
}
587587

588-
const invalidSyntaxOnlyCommands: readonly CommandNames[] = [
588+
const invalidApproximateSemanticOnlyCommands: readonly CommandNames[] = [
589589
CommandNames.OpenExternalProject,
590590
CommandNames.OpenExternalProjects,
591591
CommandNames.CloseExternalProject,
@@ -621,6 +621,36 @@ namespace ts.server {
621621
CommandNames.ProvideCallHierarchyOutgoingCalls,
622622
];
623623

624+
const invalidSyntaxOnlyCommands: readonly CommandNames[] = [
625+
...invalidApproximateSemanticOnlyCommands,
626+
CommandNames.Definition,
627+
CommandNames.DefinitionFull,
628+
CommandNames.DefinitionAndBoundSpan,
629+
CommandNames.DefinitionAndBoundSpanFull,
630+
CommandNames.TypeDefinition,
631+
CommandNames.Implementation,
632+
CommandNames.ImplementationFull,
633+
CommandNames.References,
634+
CommandNames.ReferencesFull,
635+
CommandNames.Rename,
636+
CommandNames.RenameLocationsFull,
637+
CommandNames.RenameInfoFull,
638+
CommandNames.Quickinfo,
639+
CommandNames.QuickinfoFull,
640+
CommandNames.CompletionInfo,
641+
CommandNames.Completions,
642+
CommandNames.CompletionsFull,
643+
CommandNames.CompletionDetails,
644+
CommandNames.CompletionDetailsFull,
645+
CommandNames.SignatureHelp,
646+
CommandNames.SignatureHelpFull,
647+
CommandNames.Navto,
648+
CommandNames.NavtoFull,
649+
CommandNames.Occurrences,
650+
CommandNames.DocumentHighlights,
651+
CommandNames.DocumentHighlightsFull,
652+
];
653+
624654
export interface SessionOptions {
625655
host: ServerHost;
626656
cancellationToken: ServerCancellationToken;
@@ -637,7 +667,9 @@ namespace ts.server {
637667
eventHandler?: ProjectServiceEventHandler;
638668
/** Has no effect if eventHandler is also specified. */
639669
suppressDiagnosticEvents?: boolean;
670+
/** @deprecated use serverMode instead */
640671
syntaxOnly?: boolean;
672+
serverMode?: LanguageServiceMode;
641673
throttleWaitMilliseconds?: number;
642674
noGetErrOnBackgroundUpdate?: boolean;
643675

@@ -709,18 +741,32 @@ namespace ts.server {
709741
allowLocalPluginLoads: opts.allowLocalPluginLoads,
710742
typesMapLocation: opts.typesMapLocation,
711743
syntaxOnly: opts.syntaxOnly,
744+
serverMode: opts.serverMode,
712745
};
713746
this.projectService = new ProjectService(settings);
714747
this.projectService.setPerformanceEventHandler(this.performanceEventHandler.bind(this));
715748
this.gcTimer = new GcTimer(this.host, /*delay*/ 7000, this.logger);
716749

717-
// Make sure to setup handlers to throw error for not allowed commands on syntax server;
718-
if (this.projectService.syntaxOnly) {
719-
invalidSyntaxOnlyCommands.forEach(commandName =>
720-
this.handlers.set(commandName, request => {
721-
throw new Error(`Request: ${request.command} not allowed on syntaxServer`);
722-
})
723-
);
750+
// Make sure to setup handlers to throw error for not allowed commands on syntax server
751+
switch (this.projectService.serverMode) {
752+
case LanguageServiceMode.Semantic:
753+
break;
754+
case LanguageServiceMode.ApproximateSemanticOnly:
755+
invalidApproximateSemanticOnlyCommands.forEach(commandName =>
756+
this.handlers.set(commandName, request => {
757+
throw new Error(`Request: ${request.command} not allowed on approximate semantic only server`);
758+
})
759+
);
760+
break;
761+
case LanguageServiceMode.SyntaxOnly:
762+
invalidSyntaxOnlyCommands.forEach(commandName =>
763+
this.handlers.set(commandName, request => {
764+
throw new Error(`Request: ${request.command} not allowed on syntax only server`);
765+
})
766+
);
767+
break;
768+
default:
769+
Debug.assertNever(this.projectService.serverMode);
724770
}
725771
}
726772

src/services/services.ts

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,7 +1171,7 @@ namespace ts {
11711171
}
11721172
}
11731173

1174-
const invalidOperationsOnSyntaxOnly: readonly (keyof LanguageService)[] = [
1174+
const invalidOperationsOnApproximateSemanticOnly: readonly (keyof LanguageService)[] = [
11751175
"getSyntacticDiagnostics",
11761176
"getSemanticDiagnostics",
11771177
"getSuggestionDiagnostics",
@@ -1191,10 +1191,42 @@ namespace ts {
11911191
"provideCallHierarchyOutgoingCalls",
11921192
];
11931193

1194+
const invalidOperationsOnSyntaxOnly: readonly (keyof LanguageService)[] = [
1195+
...invalidOperationsOnApproximateSemanticOnly,
1196+
"getCompletionsAtPosition",
1197+
"getCompletionEntryDetails",
1198+
"getCompletionEntrySymbol",
1199+
"getSignatureHelpItems",
1200+
"getQuickInfoAtPosition",
1201+
"getDefinitionAtPosition",
1202+
"getDefinitionAndBoundSpan",
1203+
"getImplementationAtPosition",
1204+
"getTypeDefinitionAtPosition",
1205+
"getReferencesAtPosition",
1206+
"findReferences",
1207+
"getOccurrencesAtPosition",
1208+
"getDocumentHighlights",
1209+
"getNavigateToItems",
1210+
"getRenameInfo",
1211+
"findRenameLocations",
1212+
"getApplicableRefactors",
1213+
];
11941214
export function createLanguageService(
11951215
host: LanguageServiceHost,
11961216
documentRegistry: DocumentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames(), host.getCurrentDirectory()),
1197-
syntaxOnly = false): LanguageService {
1217+
syntaxOnlyOrLanguageServiceMode?: boolean | LanguageServiceMode,
1218+
): LanguageService {
1219+
let languageServiceMode: LanguageServiceMode;
1220+
if (syntaxOnlyOrLanguageServiceMode === undefined) {
1221+
languageServiceMode = LanguageServiceMode.Semantic;
1222+
}
1223+
else if (typeof syntaxOnlyOrLanguageServiceMode === "boolean") {
1224+
// languageServiceMode = SyntaxOnly
1225+
languageServiceMode = syntaxOnlyOrLanguageServiceMode ? LanguageServiceMode.SyntaxOnly : LanguageServiceMode.Semantic;
1226+
}
1227+
else {
1228+
languageServiceMode = syntaxOnlyOrLanguageServiceMode;
1229+
}
11981230

11991231
const syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host);
12001232
let program: Program;
@@ -1244,6 +1276,7 @@ namespace ts {
12441276
}
12451277

12461278
function synchronizeHostData(): void {
1279+
Debug.assert(languageServiceMode !== LanguageServiceMode.SyntaxOnly);
12471280
// perform fast check if host supports it
12481281
if (host.getProjectVersion) {
12491282
const hostProjectVersion = host.getProjectVersion();
@@ -1429,6 +1462,11 @@ namespace ts {
14291462

14301463
// TODO: GH#18217 frequently asserted as defined
14311464
function getProgram(): Program | undefined {
1465+
if (languageServiceMode === LanguageServiceMode.SyntaxOnly) {
1466+
Debug.assert(program === undefined);
1467+
return undefined;
1468+
}
1469+
14321470
synchronizeHostData();
14331471

14341472
return program;
@@ -2504,14 +2542,26 @@ namespace ts {
25042542
uncommentSelection,
25052543
};
25062544

2507-
if (syntaxOnly) {
2508-
invalidOperationsOnSyntaxOnly.forEach(key =>
2509-
ls[key] = () => {
2510-
throw new Error(`LanguageService Operation: ${key} not allowed on syntaxServer`);
2511-
}
2512-
);
2545+
switch (languageServiceMode) {
2546+
case LanguageServiceMode.Semantic:
2547+
break;
2548+
case LanguageServiceMode.ApproximateSemanticOnly:
2549+
invalidOperationsOnApproximateSemanticOnly.forEach(key =>
2550+
ls[key] = () => {
2551+
throw new Error(`LanguageService Operation: ${key} not allowed on approximate semantic only server`);
2552+
}
2553+
);
2554+
break;
2555+
case LanguageServiceMode.SyntaxOnly:
2556+
invalidOperationsOnSyntaxOnly.forEach(key =>
2557+
ls[key] = () => {
2558+
throw new Error(`LanguageService Operation: ${key} not allowed on syntax only server`);
2559+
}
2560+
);
2561+
break;
2562+
default:
2563+
Debug.assertNever(languageServiceMode);
25132564
}
2514-
25152565
return ls;
25162566
}
25172567

0 commit comments

Comments
 (0)