Skip to content

Add separate flag serverMode for server mode #39735

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 24 additions & 9 deletions src/server/editorServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,9 @@ namespace ts.server {
pluginProbeLocations?: readonly string[];
allowLocalPluginLoads?: boolean;
typesMapLocation?: string;
/** @deprecated use serverMode instead */
syntaxOnly?: boolean;
serverMode?: LanguageServiceMode;
}

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

public readonly typesMapLocation: string | undefined;

public readonly syntaxOnly?: boolean;
/** @deprecated use serverMode instead */
public readonly syntaxOnly: boolean;
public readonly serverMode: LanguageServiceMode;

/** Tracks projects that we have already sent telemetry for. */
private readonly seenProjects = new Map<string, true>();
Expand Down Expand Up @@ -713,7 +717,18 @@ namespace ts.server {
this.pluginProbeLocations = opts.pluginProbeLocations || emptyArray;
this.allowLocalPluginLoads = !!opts.allowLocalPluginLoads;
this.typesMapLocation = (opts.typesMapLocation === undefined) ? combinePaths(getDirectoryPath(this.getExecutingFilePath()), "typesMap.json") : opts.typesMapLocation;
this.syntaxOnly = opts.syntaxOnly;
if (opts.serverMode !== undefined) {
this.serverMode = opts.serverMode;
this.syntaxOnly = this.serverMode === LanguageServiceMode.SyntaxOnly;
}
else if (opts.syntaxOnly) {
this.serverMode = LanguageServiceMode.SyntaxOnly;
this.syntaxOnly = true;
}
else {
this.serverMode = LanguageServiceMode.Semantic;
this.syntaxOnly = false;
}

Debug.assert(!!this.host.createHash, "'ServerHost.createHash' is required for ProjectService");
if (this.host.realpath) {
Expand Down Expand Up @@ -749,7 +764,7 @@ namespace ts.server {
this.logger.loggingEnabled() ? WatchLogLevel.TriggerOnly : WatchLogLevel.None;
const log: (s: string) => void = watchLogLevel !== WatchLogLevel.None ? (s => this.logger.info(s)) : noop;
this.packageJsonCache = createPackageJsonCache(this);
this.watchFactory = this.syntaxOnly ?
this.watchFactory = this.serverMode !== LanguageServiceMode.Semantic ?
{
watchFile: returnNoopFileWatcher,
watchFilePath: returnNoopFileWatcher,
Expand Down Expand Up @@ -1727,7 +1742,7 @@ namespace ts.server {
* the newly opened file.
*/
private forEachConfigFileLocation(info: OpenScriptInfoOrClosedOrConfigFileInfo, action: (configFileName: NormalizedPath, canonicalConfigFilePath: string) => boolean | void) {
if (this.syntaxOnly) {
if (this.serverMode !== LanguageServiceMode.Semantic) {
return undefined;
}

Expand Down Expand Up @@ -3014,15 +3029,15 @@ namespace ts.server {
let retainProjects: ConfiguredProject[] | ConfiguredProject | undefined;
let projectForConfigFileDiag: ConfiguredProject | undefined;
let defaultConfigProjectIsCreated = false;
if (this.syntaxOnly) {
if (this.serverMode === LanguageServiceMode.ApproximateSemanticOnly) {
// Invalidate resolutions in the file since this file is now open
info.containingProjects.forEach(project => {
if (project.resolutionCache.removeRelativeNoResolveResolutionsOfFile(info.path)) {
project.markAsDirty();
}
});
}
else if (!project) { // Checking syntaxOnly is an optimization
else if (!project && this.serverMode === LanguageServiceMode.Semantic) { // Checking semantic mode is an optimization
configFileName = this.getConfigFileNameForFile(info);
if (configFileName) {
project = this.findConfiguredProjectByProjectName(configFileName);
Expand Down Expand Up @@ -3109,7 +3124,7 @@ namespace ts.server {
Debug.assert(this.openFiles.has(info.path));
this.assignOrphanScriptInfoToInferredProject(info, this.openFiles.get(info.path));
}
else if (this.syntaxOnly && info.cacheSourceFile?.sourceFile.referencedFiles.length) {
else if (this.serverMode === LanguageServiceMode.ApproximateSemanticOnly && info.cacheSourceFile?.sourceFile.referencedFiles.length) {
// This file was just opened and references in this file will previously not been resolved so schedule update
info.containingProjects.forEach(project => project.markAsDirty());
}
Expand Down Expand Up @@ -3325,7 +3340,7 @@ namespace ts.server {
}

private telemetryOnOpenFile(scriptInfo: ScriptInfo): void {
if (this.syntaxOnly || !this.eventHandler || !scriptInfo.isJavaScript() || !addToSeen(this.allJsFilesForOpenFileTelemetry, scriptInfo.path)) {
if (this.serverMode !== LanguageServiceMode.Semantic || !this.eventHandler || !scriptInfo.isJavaScript() || !addToSeen(this.allJsFilesForOpenFileTelemetry, scriptInfo.path)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably an issue for a future PR, but I think the assumption here was that syntax server telemetry would be uninteresting. I'm not sure the same is true for the partial semantic server.

return;
}

Expand Down Expand Up @@ -3637,7 +3652,7 @@ namespace ts.server {
for (const file of proj.rootFiles) {
const normalized = toNormalizedPath(file.fileName);
if (getBaseConfigFileName(normalized)) {
if (!this.syntaxOnly && this.host.fileExists(normalized)) {
if (this.serverMode === LanguageServiceMode.Semantic && this.host.fileExists(normalized)) {
(tsConfigFiles || (tsConfigFiles = [])).push(normalized);
}
}
Expand Down
40 changes: 31 additions & 9 deletions src/server/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,9 +279,21 @@ namespace ts.server {
this.compilerOptions.allowNonTsExtensions = true;
}

this.languageServiceEnabled = true;
if (projectService.syntaxOnly) {
this.compilerOptions.types = [];
switch (projectService.serverMode) {
case LanguageServiceMode.Semantic:
this.languageServiceEnabled = true;
break;
case LanguageServiceMode.ApproximateSemanticOnly:
this.languageServiceEnabled = true;
this.compilerOptions.types = [];
break;
case LanguageServiceMode.SyntaxOnly:
this.languageServiceEnabled = false;
this.compilerOptions.noResolve = true;
this.compilerOptions.types = [];
break;
default:
Debug.assertNever(projectService.serverMode);
}

this.setInternalCompilerOptionsForEmittingJsFiles();
Expand All @@ -298,10 +310,10 @@ namespace ts.server {
this.resolutionCache = createResolutionCache(
this,
currentDirectory && this.currentDirectory,
projectService.syntaxOnly ? ResolutionKind.RelativeReferencesInOpenFileOnly : ResolutionKind.All,
projectService.serverMode === LanguageServiceMode.Semantic ? ResolutionKind.All : ResolutionKind.RelativeReferencesInOpenFileOnly,
/*logChangesWhenResolvingModule*/ true
);
this.languageService = createLanguageService(this, this.documentRegistry, this.projectService.syntaxOnly);
this.languageService = createLanguageService(this, this.documentRegistry, this.projectService.serverMode);
if (lastFileExceededProgramSize) {
this.disableLanguageService(lastFileExceededProgramSize);
}
Expand Down Expand Up @@ -456,7 +468,16 @@ namespace ts.server {

/*@internal*/
includeTripleslashReferencesFrom(containingFile: string) {
return !this.projectService.syntaxOnly || this.fileIsOpen(this.toPath(containingFile));
switch (this.projectService.serverMode) {
case LanguageServiceMode.Semantic:
return true;
case LanguageServiceMode.ApproximateSemanticOnly:
return this.fileIsOpen(this.toPath(containingFile));
case LanguageServiceMode.SyntaxOnly:
return false;
default:
Debug.assertNever(this.projectService.serverMode);
}
}

directoryExists(path: string): boolean {
Expand Down Expand Up @@ -656,7 +677,7 @@ namespace ts.server {
}

enableLanguageService() {
if (this.languageServiceEnabled) {
if (this.languageServiceEnabled || this.projectService.serverMode === LanguageServiceMode.SyntaxOnly) {
return;
}
this.languageServiceEnabled = true;
Expand All @@ -668,6 +689,7 @@ namespace ts.server {
if (!this.languageServiceEnabled) {
return;
}
Debug.assert(this.projectService.serverMode !== LanguageServiceMode.SyntaxOnly);
this.languageService.cleanupSemanticCache();
this.languageServiceEnabled = false;
this.lastFileExceededProgramSize = lastFileExceededProgramSize;
Expand Down Expand Up @@ -997,7 +1019,7 @@ namespace ts.server {

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

// Watch the type locations that would be added to program as part of automatic type resolutions
if (this.languageServiceEnabled && !this.projectService.syntaxOnly) {
if (this.languageServiceEnabled && this.projectService.serverMode === LanguageServiceMode.Semantic) {
this.resolutionCache.updateTypeRootsWatch();
}
}
Expand Down
62 changes: 54 additions & 8 deletions src/server/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ namespace ts.server {
undefined;
}

const invalidSyntaxOnlyCommands: readonly CommandNames[] = [
const invalidApproximateSemanticOnlyCommands: readonly CommandNames[] = [
CommandNames.OpenExternalProject,
CommandNames.OpenExternalProjects,
CommandNames.CloseExternalProject,
Expand Down Expand Up @@ -621,6 +621,36 @@ namespace ts.server {
CommandNames.ProvideCallHierarchyOutgoingCalls,
];

const invalidSyntaxOnlyCommands: readonly CommandNames[] = [
...invalidApproximateSemanticOnlyCommands,
CommandNames.Definition,
CommandNames.DefinitionFull,
CommandNames.DefinitionAndBoundSpan,
CommandNames.DefinitionAndBoundSpanFull,
CommandNames.TypeDefinition,
CommandNames.Implementation,
CommandNames.ImplementationFull,
CommandNames.References,
CommandNames.ReferencesFull,
CommandNames.Rename,
CommandNames.RenameLocationsFull,
CommandNames.RenameInfoFull,
CommandNames.Quickinfo,
CommandNames.QuickinfoFull,
CommandNames.CompletionInfo,
CommandNames.Completions,
CommandNames.CompletionsFull,
CommandNames.CompletionDetails,
CommandNames.CompletionDetailsFull,
CommandNames.SignatureHelp,
CommandNames.SignatureHelpFull,
CommandNames.Navto,
CommandNames.NavtoFull,
CommandNames.Occurrences,
CommandNames.DocumentHighlights,
CommandNames.DocumentHighlightsFull,
];

export interface SessionOptions {
host: ServerHost;
cancellationToken: ServerCancellationToken;
Expand All @@ -637,7 +667,9 @@ namespace ts.server {
eventHandler?: ProjectServiceEventHandler;
/** Has no effect if eventHandler is also specified. */
suppressDiagnosticEvents?: boolean;
/** @deprecated use serverMode instead */
syntaxOnly?: boolean;
serverMode?: LanguageServiceMode;
throttleWaitMilliseconds?: number;
noGetErrOnBackgroundUpdate?: boolean;

Expand Down Expand Up @@ -709,18 +741,32 @@ namespace ts.server {
allowLocalPluginLoads: opts.allowLocalPluginLoads,
typesMapLocation: opts.typesMapLocation,
syntaxOnly: opts.syntaxOnly,
serverMode: opts.serverMode,
};
this.projectService = new ProjectService(settings);
this.projectService.setPerformanceEventHandler(this.performanceEventHandler.bind(this));
this.gcTimer = new GcTimer(this.host, /*delay*/ 7000, this.logger);

// Make sure to setup handlers to throw error for not allowed commands on syntax server;
if (this.projectService.syntaxOnly) {
invalidSyntaxOnlyCommands.forEach(commandName =>
this.handlers.set(commandName, request => {
throw new Error(`Request: ${request.command} not allowed on syntaxServer`);
})
);
// Make sure to setup handlers to throw error for not allowed commands on syntax server
switch (this.projectService.serverMode) {
case LanguageServiceMode.Semantic:
break;
case LanguageServiceMode.ApproximateSemanticOnly:
invalidApproximateSemanticOnlyCommands.forEach(commandName =>
this.handlers.set(commandName, request => {
throw new Error(`Request: ${request.command} not allowed on approximate semantic only server`);
})
);
break;
case LanguageServiceMode.SyntaxOnly:
invalidSyntaxOnlyCommands.forEach(commandName =>
this.handlers.set(commandName, request => {
throw new Error(`Request: ${request.command} not allowed on syntax only server`);
})
);
break;
default:
Debug.assertNever(this.projectService.serverMode);
}
}

Expand Down
68 changes: 59 additions & 9 deletions src/services/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1171,7 +1171,7 @@ namespace ts {
}
}

const invalidOperationsOnSyntaxOnly: readonly (keyof LanguageService)[] = [
const invalidOperationsOnApproximateSemanticOnly: readonly (keyof LanguageService)[] = [
"getSyntacticDiagnostics",
"getSemanticDiagnostics",
"getSuggestionDiagnostics",
Expand All @@ -1191,10 +1191,42 @@ namespace ts {
"provideCallHierarchyOutgoingCalls",
];

const invalidOperationsOnSyntaxOnly: readonly (keyof LanguageService)[] = [
...invalidOperationsOnApproximateSemanticOnly,
"getCompletionsAtPosition",
"getCompletionEntryDetails",
"getCompletionEntrySymbol",
"getSignatureHelpItems",
"getQuickInfoAtPosition",
"getDefinitionAtPosition",
"getDefinitionAndBoundSpan",
"getImplementationAtPosition",
"getTypeDefinitionAtPosition",
"getReferencesAtPosition",
"findReferences",
"getOccurrencesAtPosition",
"getDocumentHighlights",
"getNavigateToItems",
"getRenameInfo",
"findRenameLocations",
"getApplicableRefactors",
];
export function createLanguageService(
host: LanguageServiceHost,
documentRegistry: DocumentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames(), host.getCurrentDirectory()),
syntaxOnly = false): LanguageService {
syntaxOnlyOrLanguageServiceMode?: boolean | LanguageServiceMode,
): LanguageService {
let languageServiceMode: LanguageServiceMode;
if (syntaxOnlyOrLanguageServiceMode === undefined) {
languageServiceMode = LanguageServiceMode.Semantic;
}
else if (typeof syntaxOnlyOrLanguageServiceMode === "boolean") {
// languageServiceMode = SyntaxOnly
languageServiceMode = syntaxOnlyOrLanguageServiceMode ? LanguageServiceMode.SyntaxOnly : LanguageServiceMode.Semantic;
}
else {
languageServiceMode = syntaxOnlyOrLanguageServiceMode;
}

const syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host);
let program: Program;
Expand Down Expand Up @@ -1244,6 +1276,7 @@ namespace ts {
}

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

// TODO: GH#18217 frequently asserted as defined
function getProgram(): Program | undefined {
if (languageServiceMode === LanguageServiceMode.SyntaxOnly) {
Debug.assert(program === undefined);
return undefined;
}

synchronizeHostData();

return program;
Expand Down Expand Up @@ -2504,14 +2542,26 @@ namespace ts {
uncommentSelection,
};

if (syntaxOnly) {
invalidOperationsOnSyntaxOnly.forEach(key =>
ls[key] = () => {
throw new Error(`LanguageService Operation: ${key} not allowed on syntaxServer`);
}
);
switch (languageServiceMode) {
case LanguageServiceMode.Semantic:
break;
case LanguageServiceMode.ApproximateSemanticOnly:
invalidOperationsOnApproximateSemanticOnly.forEach(key =>
ls[key] = () => {
throw new Error(`LanguageService Operation: ${key} not allowed on approximate semantic only server`);
}
);
break;
case LanguageServiceMode.SyntaxOnly:
invalidOperationsOnSyntaxOnly.forEach(key =>
ls[key] = () => {
throw new Error(`LanguageService Operation: ${key} not allowed on syntax only server`);
}
);
break;
default:
Debug.assertNever(languageServiceMode);
}

return ls;
}

Expand Down
Loading