Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit e68161a

Browse filesBrowse files
authored
when language service is disabled - build program using only open files (microsoft#12809)
1 parent c71e6cc commit e68161a
Copy full SHA for e68161a

6 files changed

+132-108Lines changed: 132 additions & 108 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/harness/unittests/tsserverProjectSystem.ts‎

Copy file name to clipboardExpand all lines: src/harness/unittests/tsserverProjectSystem.ts
+44Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1238,6 +1238,7 @@ namespace ts.projectSystem {
12381238
projectService.closeExternalProject(externalProjectName);
12391239
checkNumberOfProjects(projectService, { configuredProjects: 0 });
12401240
});
1241+
12411242
it("external project with included config file opened after configured project and then closed", () => {
12421243
const file1 = {
12431244
path: "/a/b/f1.ts",
@@ -1797,6 +1798,49 @@ namespace ts.projectSystem {
17971798
checkNumberOfProjects(projectService, { configuredProjects: 0 });
17981799
});
17991800

1801+
it("language service disabled state is updated in external projects", () => {
1802+
debugger
1803+
const f1 = {
1804+
path: "/a/app.js",
1805+
content: "var x = 1"
1806+
};
1807+
const f2 = {
1808+
path: "/a/largefile.js",
1809+
content: ""
1810+
};
1811+
const host = createServerHost([f1, f2]);
1812+
const originalGetFileSize = host.getFileSize;
1813+
host.getFileSize = (filePath: string) =>
1814+
filePath === f2.path ? server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath);
1815+
1816+
const service = createProjectService(host);
1817+
const projectFileName = "/a/proj.csproj";
1818+
1819+
service.openExternalProject({
1820+
projectFileName,
1821+
rootFiles: toExternalFiles([f1.path, f2.path]),
1822+
options: {}
1823+
});
1824+
service.checkNumberOfProjects({ externalProjects: 1 });
1825+
assert.isFalse(service.externalProjects[0].languageServiceEnabled, "language service should be disabled - 1");
1826+
1827+
service.openExternalProject({
1828+
projectFileName,
1829+
rootFiles: toExternalFiles([f1.path]),
1830+
options: {}
1831+
});
1832+
service.checkNumberOfProjects({ externalProjects: 1 });
1833+
assert.isTrue(service.externalProjects[0].languageServiceEnabled, "language service should be enabled");
1834+
1835+
service.openExternalProject({
1836+
projectFileName,
1837+
rootFiles: toExternalFiles([f1.path, f2.path]),
1838+
options: {}
1839+
});
1840+
service.checkNumberOfProjects({ externalProjects: 1 });
1841+
assert.isFalse(service.externalProjects[0].languageServiceEnabled, "language service should be disabled - 2");
1842+
});
1843+
18001844
it("language service disabled events are triggered", () => {
18011845
const f1 = {
18021846
path: "/a/app.js",
Collapse file

‎src/server/builder.ts‎

Copy file name to clipboardExpand all lines: src/server/builder.ts
+26-6Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,32 @@ namespace ts.server {
7575
getFilesAffectedBy(scriptInfo: ScriptInfo): string[];
7676
onProjectUpdateGraph(): void;
7777
emitFile(scriptInfo: ScriptInfo, writeFile: (path: string, data: string, writeByteOrderMark?: boolean) => void): boolean;
78+
clear(): void;
7879
}
7980

8081
abstract class AbstractBuilder<T extends BuilderFileInfo> implements Builder {
8182

82-
private fileInfos = createFileMap<T>();
83+
/**
84+
* stores set of files from the project.
85+
* NOTE: this field is created on demand and should not be accessed directly.
86+
* Use 'getFileInfos' instead.
87+
*/
88+
private fileInfos_doNotAccessDirectly: FileMap<T>;
8389

8490
constructor(public readonly project: Project, private ctor: { new (scriptInfo: ScriptInfo, project: Project): T }) {
8591
}
8692

93+
private getFileInfos() {
94+
return this.fileInfos_doNotAccessDirectly || (this.fileInfos_doNotAccessDirectly = createFileMap<T>());
95+
}
96+
97+
public clear() {
98+
// drop the existing list - it will be re-created as necessary
99+
this.fileInfos_doNotAccessDirectly = undefined;
100+
}
101+
87102
protected getFileInfo(path: Path): T {
88-
return this.fileInfos.get(path);
103+
return this.getFileInfos().get(path);
89104
}
90105

91106
protected getOrCreateFileInfo(path: Path): T {
@@ -99,19 +114,19 @@ namespace ts.server {
99114
}
100115

101116
protected getFileInfoPaths(): Path[] {
102-
return this.fileInfos.getKeys();
117+
return this.getFileInfos().getKeys();
103118
}
104119

105120
protected setFileInfo(path: Path, info: T) {
106-
this.fileInfos.set(path, info);
121+
this.getFileInfos().set(path, info);
107122
}
108123

109124
protected removeFileInfo(path: Path) {
110-
this.fileInfos.remove(path);
125+
this.getFileInfos().remove(path);
111126
}
112127

113128
protected forEachFileInfo(action: (fileInfo: T) => any) {
114-
this.fileInfos.forEachValue((_path, value) => action(value));
129+
this.getFileInfos().forEachValue((_path, value) => action(value));
115130
}
116131

117132
abstract getFilesAffectedBy(scriptInfo: ScriptInfo): string[];
@@ -231,6 +246,11 @@ namespace ts.server {
231246

232247
private projectVersionForDependencyGraph: string;
233248

249+
public clear() {
250+
this.projectVersionForDependencyGraph = undefined;
251+
super.clear();
252+
}
253+
234254
private getReferencedFileInfos(fileInfo: ModuleBuilderFileInfo): ModuleBuilderFileInfo[] {
235255
if (!fileInfo.isExternalModuleOrHasOnlyAmbientExternalModules()) {
236256
return [];
Collapse file

‎src/server/editorServices.ts‎

Copy file name to clipboardExpand all lines: src/server/editorServices.ts
+33-8Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ namespace ts.server {
126126
}
127127

128128
export interface OpenConfiguredProjectResult {
129-
configFileName?: string;
129+
configFileName?: NormalizedPath;
130130
configFileErrors?: Diagnostic[];
131131
}
132132

@@ -659,6 +659,13 @@ namespace ts.server {
659659
// open file in inferred project
660660
(projectsToRemove || (projectsToRemove = [])).push(p);
661661
}
662+
663+
if (!p.languageServiceEnabled) {
664+
// if project language service is disabled then we create a program only for open files.
665+
// this means that project should be marked as dirty to force rebuilding of the program
666+
// on the next request
667+
p.markAsDirty();
668+
}
662669
}
663670
if (projectsToRemove) {
664671
for (const project of projectsToRemove) {
@@ -1052,9 +1059,7 @@ namespace ts.server {
10521059
project.stopWatchingDirectory();
10531060
}
10541061
else {
1055-
if (!project.languageServiceEnabled) {
1056-
project.enableLanguageService();
1057-
}
1062+
project.enableLanguageService();
10581063
this.watchConfigDirectoryForProject(project, projectOptions);
10591064
this.updateNonInferredProject(project, projectOptions.files, fileNamePropertyReader, projectOptions.compilerOptions, projectOptions.typeAcquisition, projectOptions.compileOnSave, configFileErrors);
10601065
}
@@ -1226,9 +1231,22 @@ namespace ts.server {
12261231
}
12271232

12281233
openClientFileWithNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean): OpenConfiguredProjectResult {
1229-
const { configFileName = undefined, configFileErrors = undefined }: OpenConfiguredProjectResult = this.findContainingExternalProject(fileName)
1230-
? {}
1231-
: this.openOrUpdateConfiguredProjectForFile(fileName);
1234+
let configFileName: NormalizedPath;
1235+
let configFileErrors: Diagnostic[];
1236+
1237+
let project: ConfiguredProject | ExternalProject = this.findContainingExternalProject(fileName);
1238+
if (!project) {
1239+
({ configFileName, configFileErrors } = this.openOrUpdateConfiguredProjectForFile(fileName));
1240+
if (configFileName) {
1241+
project = this.findConfiguredProjectByProjectName(configFileName);
1242+
}
1243+
}
1244+
if (project && !project.languageServiceEnabled) {
1245+
// if project language service is disabled then we create a program only for open files.
1246+
// this means that project should be marked as dirty to force rebuilding of the program
1247+
// on the next request
1248+
project.markAsDirty();
1249+
}
12321250

12331251
// at this point if file is the part of some configured/external project then this project should be created
12341252
const info = this.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ true, fileContent, scriptKind, hasMixedContent);
@@ -1392,8 +1410,15 @@ namespace ts.server {
13921410
let exisingConfigFiles: string[];
13931411
if (externalProject) {
13941412
if (!tsConfigFiles) {
1413+
const compilerOptions = convertCompilerOptions(proj.options);
1414+
if (this.exceededTotalSizeLimitForNonTsFiles(compilerOptions, proj.rootFiles, externalFilePropertyReader)) {
1415+
externalProject.disableLanguageService();
1416+
}
1417+
else {
1418+
externalProject.enableLanguageService();
1419+
}
13951420
// external project already exists and not config files were added - update the project and return;
1396-
this.updateNonInferredProject(externalProject, proj.rootFiles, externalFilePropertyReader, convertCompilerOptions(proj.options), proj.typeAcquisition, proj.options.compileOnSave, /*configFileErrors*/ undefined);
1421+
this.updateNonInferredProject(externalProject, proj.rootFiles, externalFilePropertyReader, compilerOptions, proj.typeAcquisition, proj.options.compileOnSave, /*configFileErrors*/ undefined);
13971422
return;
13981423
}
13991424
// some config files were added to external project (that previously were not there)
Collapse file

‎src/server/project.ts‎

Copy file name to clipboardExpand all lines: src/server/project.ts
+21-94Lines changed: 21 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -90,87 +90,6 @@ namespace ts.server {
9090
}
9191
}
9292

93-
const emptyResult: any[] = [];
94-
const getEmptyResult = () => emptyResult;
95-
const getUndefined = () => <any>undefined;
96-
const emptyEncodedSemanticClassifications = { spans: emptyResult, endOfLineState: EndOfLineState.None };
97-
98-
export function createNoSemanticFeaturesWrapper(realLanguageService: LanguageService): LanguageService {
99-
return {
100-
cleanupSemanticCache: noop,
101-
getSyntacticDiagnostics: (fileName) =>
102-
fileName ? realLanguageService.getSyntacticDiagnostics(fileName) : emptyResult,
103-
getSemanticDiagnostics: getEmptyResult,
104-
getCompilerOptionsDiagnostics: () =>
105-
realLanguageService.getCompilerOptionsDiagnostics(),
106-
getSyntacticClassifications: (fileName, span) =>
107-
realLanguageService.getSyntacticClassifications(fileName, span),
108-
getEncodedSyntacticClassifications: (fileName, span) =>
109-
realLanguageService.getEncodedSyntacticClassifications(fileName, span),
110-
getSemanticClassifications: getEmptyResult,
111-
getEncodedSemanticClassifications: () =>
112-
emptyEncodedSemanticClassifications,
113-
getCompletionsAtPosition: getUndefined,
114-
findReferences: getEmptyResult,
115-
getCompletionEntryDetails: getUndefined,
116-
getQuickInfoAtPosition: getUndefined,
117-
findRenameLocations: getEmptyResult,
118-
getNameOrDottedNameSpan: (fileName, startPos, endPos) =>
119-
realLanguageService.getNameOrDottedNameSpan(fileName, startPos, endPos),
120-
getBreakpointStatementAtPosition: (fileName, position) =>
121-
realLanguageService.getBreakpointStatementAtPosition(fileName, position),
122-
getBraceMatchingAtPosition: (fileName, position) =>
123-
realLanguageService.getBraceMatchingAtPosition(fileName, position),
124-
getSignatureHelpItems: getUndefined,
125-
getDefinitionAtPosition: getEmptyResult,
126-
getRenameInfo: () => ({
127-
canRename: false,
128-
localizedErrorMessage: getLocaleSpecificMessage(Diagnostics.Language_service_is_disabled),
129-
displayName: undefined,
130-
fullDisplayName: undefined,
131-
kind: undefined,
132-
kindModifiers: undefined,
133-
triggerSpan: undefined
134-
}),
135-
getTypeDefinitionAtPosition: getUndefined,
136-
getReferencesAtPosition: getEmptyResult,
137-
getDocumentHighlights: getEmptyResult,
138-
getOccurrencesAtPosition: getEmptyResult,
139-
getNavigateToItems: getEmptyResult,
140-
getNavigationBarItems: fileName =>
141-
realLanguageService.getNavigationBarItems(fileName),
142-
getNavigationTree: fileName =>
143-
realLanguageService.getNavigationTree(fileName),
144-
getOutliningSpans: fileName =>
145-
realLanguageService.getOutliningSpans(fileName),
146-
getTodoComments: getEmptyResult,
147-
getIndentationAtPosition: (fileName, position, options) =>
148-
realLanguageService.getIndentationAtPosition(fileName, position, options),
149-
getFormattingEditsForRange: (fileName, start, end, options) =>
150-
realLanguageService.getFormattingEditsForRange(fileName, start, end, options),
151-
getFormattingEditsForDocument: (fileName, options) =>
152-
realLanguageService.getFormattingEditsForDocument(fileName, options),
153-
getFormattingEditsAfterKeystroke: (fileName, position, key, options) =>
154-
realLanguageService.getFormattingEditsAfterKeystroke(fileName, position, key, options),
155-
getDocCommentTemplateAtPosition: (fileName, position) =>
156-
realLanguageService.getDocCommentTemplateAtPosition(fileName, position),
157-
isValidBraceCompletionAtPosition: (fileName, position, openingBrace) =>
158-
realLanguageService.isValidBraceCompletionAtPosition(fileName, position, openingBrace),
159-
getEmitOutput: getUndefined,
160-
getProgram: () =>
161-
realLanguageService.getProgram(),
162-
getNonBoundSourceFile: fileName =>
163-
realLanguageService.getNonBoundSourceFile(fileName),
164-
dispose: () =>
165-
realLanguageService.dispose(),
166-
getCompletionEntrySymbol: getUndefined,
167-
getImplementationAtPosition: getEmptyResult,
168-
getSourceFile: fileName =>
169-
realLanguageService.getSourceFile(fileName),
170-
getCodeFixesAtPosition: getEmptyResult
171-
};
172-
}
173-
17493
export abstract class Project {
17594
private rootFiles: ScriptInfo[] = [];
17695
private rootFilesMap: FileMap<ScriptInfo> = createFileMap<ScriptInfo>();
@@ -181,8 +100,6 @@ namespace ts.server {
181100
private lastCachedUnresolvedImportsList: SortedReadonlyArray<string>;
182101

183102
private readonly languageService: LanguageService;
184-
// wrapper over the real language service that will suppress all semantic operations
185-
private readonly noSemanticFeaturesLanguageService: LanguageService;
186103

187104
public languageServiceEnabled = true;
188105

@@ -258,7 +175,6 @@ namespace ts.server {
258175
this.lsHost.setCompilationSettings(this.compilerOptions);
259176

260177
this.languageService = ts.createLanguageService(this.lsHost, this.documentRegistry);
261-
this.noSemanticFeaturesLanguageService = createNoSemanticFeaturesWrapper(this.languageService);
262178

263179
if (!languageServiceEnabled) {
264180
this.disableLanguageService();
@@ -282,9 +198,7 @@ namespace ts.server {
282198
if (ensureSynchronized) {
283199
this.updateGraph();
284200
}
285-
return this.languageServiceEnabled
286-
? this.languageService
287-
: this.noSemanticFeaturesLanguageService;
201+
return this.languageService;
288202
}
289203

290204
getCompileOnSaveAffectedFileList(scriptInfo: ScriptInfo): string[] {
@@ -373,7 +287,10 @@ namespace ts.server {
373287
const result: string[] = [];
374288
if (this.rootFiles) {
375289
for (const f of this.rootFiles) {
376-
result.push(f.fileName);
290+
if (this.languageServiceEnabled || f.isScriptOpen()) {
291+
// if language service is disabled - process only files that are open
292+
result.push(f.fileName);
293+
}
377294
}
378295
if (this.typingFiles) {
379296
for (const f of this.typingFiles) {
@@ -389,6 +306,10 @@ namespace ts.server {
389306
}
390307

391308
getScriptInfos() {
309+
if (!this.languageServiceEnabled) {
310+
// if language service is not enabled - return just root files
311+
return this.rootFiles;
312+
}
392313
return map(this.program.getSourceFiles(), sourceFile => {
393314
const scriptInfo = this.projectService.getScriptInfoForPath(sourceFile.path);
394315
if (!scriptInfo) {
@@ -529,10 +450,6 @@ namespace ts.server {
529450
* @returns: true if set of files in the project stays the same and false - otherwise.
530451
*/
531452
updateGraph(): boolean {
532-
if (!this.languageServiceEnabled) {
533-
return true;
534-
}
535-
536453
this.lsHost.startRecordingFilesWithChangedResolutions();
537454

538455
let hasChanges = this.updateGraphWorker();
@@ -564,6 +481,16 @@ namespace ts.server {
564481
if (this.setTypings(cachedTypings)) {
565482
hasChanges = this.updateGraphWorker() || hasChanges;
566483
}
484+
485+
// update builder only if language service is enabled
486+
// otherwise tell it to drop its internal state
487+
if (this.languageServiceEnabled) {
488+
this.builder.onProjectUpdateGraph();
489+
}
490+
else {
491+
this.builder.clear();
492+
}
493+
567494
if (hasChanges) {
568495
this.projectStructureVersion++;
569496
}
@@ -602,7 +529,6 @@ namespace ts.server {
602529
}
603530
}
604531
}
605-
this.builder.onProjectUpdateGraph();
606532
return hasChanges;
607533
}
608534

@@ -673,7 +599,8 @@ namespace ts.server {
673599
projectName: this.getProjectName(),
674600
version: this.projectStructureVersion,
675601
isInferred: this.projectKind === ProjectKind.Inferred,
676-
options: this.getCompilerOptions()
602+
options: this.getCompilerOptions(),
603+
languageServiceDisabled: !this.languageServiceEnabled
677604
};
678605
const updatedFileNames = this.updatedFileNames;
679606
this.updatedFileNames = undefined;

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.