mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-16 15:45:27 -05:00
Merge branch 'master' into builderApi
This commit is contained in:
@@ -46,7 +46,7 @@ function createCancellationToken(args: string[]): ServerCancellationToken {
|
||||
let perRequestPipeName: string;
|
||||
let currentRequestId: number;
|
||||
return {
|
||||
isCancellationRequested: () => perRequestPipeName !== undefined && pipeExists(perRequestPipeName),
|
||||
isCancellationRequested: () => perRequestPipeName !== undefined && pipeExists(perRequestPipeName),
|
||||
setRequest(requestId: number) {
|
||||
currentRequestId = requestId;
|
||||
perRequestPipeName = namePrefix + requestId;
|
||||
|
||||
@@ -547,9 +547,11 @@ namespace ts.server {
|
||||
}
|
||||
switch (response.kind) {
|
||||
case ActionSet:
|
||||
project.resolutionCache.clear();
|
||||
this.typingsCache.updateTypingsForProject(response.projectName, response.compilerOptions, response.typeAcquisition, response.unresolvedImports, response.typings);
|
||||
break;
|
||||
case ActionInvalidate:
|
||||
project.resolutionCache.clear();
|
||||
this.typingsCache.deleteTypingsForProject(response.projectName);
|
||||
break;
|
||||
}
|
||||
@@ -1774,9 +1776,10 @@ namespace ts.server {
|
||||
const path = normalizedPathToPath(fileName, currentDirectory, this.toCanonicalFileName);
|
||||
let info = this.getScriptInfoForPath(path);
|
||||
if (!info) {
|
||||
Debug.assert(isRootedDiskPath(fileName) || openedByClient, "Script info with relative file name can only be open script info");
|
||||
Debug.assert(!isRootedDiskPath(fileName) || this.currentDirectory === currentDirectory || !this.openFilesWithNonRootedDiskPath.has(this.toCanonicalFileName(fileName)), "Open script files with non rooted disk path opened with current directory context cannot have same canonical names");
|
||||
const isDynamic = isDynamicFileName(fileName);
|
||||
Debug.assert(isRootedDiskPath(fileName) || isDynamic || openedByClient, "Script info with non-dynamic relative file name can only be open script info");
|
||||
Debug.assert(!isRootedDiskPath(fileName) || this.currentDirectory === currentDirectory || !this.openFilesWithNonRootedDiskPath.has(this.toCanonicalFileName(fileName)), "Open script files with non rooted disk path opened with current directory context cannot have same canonical names");
|
||||
Debug.assert(!isDynamic || this.currentDirectory === currentDirectory, "Dynamic files must always have current directory context since containing external project name will always match the script info name.");
|
||||
// If the file is not opened by client and the file doesnot exist on the disk, return
|
||||
if (!openedByClient && !isDynamic && !(hostToQueryFileExistsOn || this.host).fileExists(fileName)) {
|
||||
return;
|
||||
|
||||
@@ -121,6 +121,7 @@ namespace ts.server {
|
||||
private program: Program;
|
||||
private externalFiles: SortedReadonlyArray<string>;
|
||||
private missingFilesMap: Map<FileWatcher>;
|
||||
private plugins: PluginModule[] = [];
|
||||
|
||||
private cachedUnresolvedImportsPerFile = new UnresolvedImportsMap();
|
||||
private lastCachedUnresolvedImportsList: SortedReadonlyArray<string>;
|
||||
@@ -516,7 +517,18 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getExternalFiles(): SortedReadonlyArray<string> {
|
||||
return emptyArray as SortedReadonlyArray<string>;
|
||||
return toSortedArray(flatMap(this.plugins, plugin => {
|
||||
if (typeof plugin.getExternalFiles !== "function") return;
|
||||
try {
|
||||
return plugin.getExternalFiles(this);
|
||||
}
|
||||
catch (e) {
|
||||
this.projectService.logger.info(`A plugin threw an exception in getExternalFiles: ${e}`);
|
||||
if (e.stack) {
|
||||
this.projectService.logger.info(e.stack);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
getSourceFile(path: Path) {
|
||||
@@ -1024,6 +1036,84 @@ namespace ts.server {
|
||||
orderedRemoveItem(this.rootFiles, info);
|
||||
this.rootFilesMap.delete(info.path);
|
||||
}
|
||||
|
||||
protected enableGlobalPlugins() {
|
||||
const host = this.projectService.host;
|
||||
const options = this.getCompilationSettings();
|
||||
|
||||
if (!host.require) {
|
||||
this.projectService.logger.info("Plugins were requested but not running in environment that supports 'require'. Nothing will be loaded");
|
||||
return;
|
||||
}
|
||||
|
||||
// Search our peer node_modules, then any globally-specified probe paths
|
||||
// ../../.. to walk from X/node_modules/typescript/lib/tsserver.js to X/node_modules/
|
||||
const searchPaths = [combinePaths(this.projectService.getExecutingFilePath(), "../../.."), ...this.projectService.pluginProbeLocations];
|
||||
|
||||
if (this.projectService.globalPlugins) {
|
||||
// Enable global plugins with synthetic configuration entries
|
||||
for (const globalPluginName of this.projectService.globalPlugins) {
|
||||
// Skip empty names from odd commandline parses
|
||||
if (!globalPluginName) continue;
|
||||
|
||||
// Skip already-locally-loaded plugins
|
||||
if (options.plugins && options.plugins.some(p => p.name === globalPluginName)) continue;
|
||||
|
||||
// Provide global: true so plugins can detect why they can't find their config
|
||||
this.projectService.logger.info(`Loading global plugin ${globalPluginName}`);
|
||||
this.enablePlugin({ name: globalPluginName, global: true } as PluginImport, searchPaths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected enablePlugin(pluginConfigEntry: PluginImport, searchPaths: string[]) {
|
||||
this.projectService.logger.info(`Enabling plugin ${pluginConfigEntry.name} from candidate paths: ${searchPaths.join(",")}`);
|
||||
|
||||
const log = (message: string) => {
|
||||
this.projectService.logger.info(message);
|
||||
};
|
||||
|
||||
for (const searchPath of searchPaths) {
|
||||
const resolvedModule = <PluginModuleFactory>Project.resolveModule(pluginConfigEntry.name, searchPath, this.projectService.host, log);
|
||||
if (resolvedModule) {
|
||||
this.enableProxy(resolvedModule, pluginConfigEntry);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.projectService.logger.info(`Couldn't find ${pluginConfigEntry.name}`);
|
||||
}
|
||||
|
||||
private enableProxy(pluginModuleFactory: PluginModuleFactory, configEntry: PluginImport) {
|
||||
try {
|
||||
if (typeof pluginModuleFactory !== "function") {
|
||||
this.projectService.logger.info(`Skipped loading plugin ${configEntry.name} because it did expose a proper factory function`);
|
||||
return;
|
||||
}
|
||||
|
||||
const info: PluginCreateInfo = {
|
||||
config: configEntry,
|
||||
project: this,
|
||||
languageService: this.languageService,
|
||||
languageServiceHost: this,
|
||||
serverHost: this.projectService.host
|
||||
};
|
||||
|
||||
const pluginModule = pluginModuleFactory({ typescript: ts });
|
||||
const newLS = pluginModule.create(info);
|
||||
for (const k of Object.keys(this.languageService)) {
|
||||
if (!(k in newLS)) {
|
||||
this.projectService.logger.info(`Plugin activation warning: Missing proxied method ${k} in created LS. Patching.`);
|
||||
(newLS as any)[k] = (this.languageService as any)[k];
|
||||
}
|
||||
}
|
||||
this.projectService.logger.info(`Plugin validation succeded`);
|
||||
this.languageService = newLS;
|
||||
this.plugins.push(pluginModule);
|
||||
}
|
||||
catch (e) {
|
||||
this.projectService.logger.info(`Plugin activation failed: ${e}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1087,6 +1177,7 @@ namespace ts.server {
|
||||
projectService.host,
|
||||
currentDirectory);
|
||||
this.projectRootPath = projectRootPath && projectService.toCanonicalFileName(projectRootPath);
|
||||
this.enableGlobalPlugins();
|
||||
}
|
||||
|
||||
addRoot(info: ScriptInfo) {
|
||||
@@ -1148,8 +1239,6 @@ namespace ts.server {
|
||||
/*@internal*/
|
||||
configFileSpecs: ConfigFileSpecs;
|
||||
|
||||
private plugins: PluginModule[] = [];
|
||||
|
||||
/** Ref count to the project when opened from external project */
|
||||
private externalProjectRefCount = 0;
|
||||
|
||||
@@ -1231,69 +1320,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.projectService.globalPlugins) {
|
||||
// Enable global plugins with synthetic configuration entries
|
||||
for (const globalPluginName of this.projectService.globalPlugins) {
|
||||
// Skip empty names from odd commandline parses
|
||||
if (!globalPluginName) continue;
|
||||
|
||||
// Skip already-locally-loaded plugins
|
||||
if (options.plugins && options.plugins.some(p => p.name === globalPluginName)) continue;
|
||||
|
||||
// Provide global: true so plugins can detect why they can't find their config
|
||||
this.projectService.logger.info(`Loading global plugin ${globalPluginName}`);
|
||||
this.enablePlugin({ name: globalPluginName, global: true } as PluginImport, searchPaths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enablePlugin(pluginConfigEntry: PluginImport, searchPaths: string[]) {
|
||||
this.projectService.logger.info(`Enabling plugin ${pluginConfigEntry.name} from candidate paths: ${searchPaths.join(",")}`);
|
||||
|
||||
const log = (message: string) => {
|
||||
this.projectService.logger.info(message);
|
||||
};
|
||||
|
||||
for (const searchPath of searchPaths) {
|
||||
const resolvedModule = <PluginModuleFactory>Project.resolveModule(pluginConfigEntry.name, searchPath, this.projectService.host, log);
|
||||
if (resolvedModule) {
|
||||
this.enableProxy(resolvedModule, pluginConfigEntry);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.projectService.logger.info(`Couldn't find ${pluginConfigEntry.name}`);
|
||||
}
|
||||
|
||||
private enableProxy(pluginModuleFactory: PluginModuleFactory, configEntry: PluginImport) {
|
||||
try {
|
||||
if (typeof pluginModuleFactory !== "function") {
|
||||
this.projectService.logger.info(`Skipped loading plugin ${configEntry.name} because it did expose a proper factory function`);
|
||||
return;
|
||||
}
|
||||
|
||||
const info: PluginCreateInfo = {
|
||||
config: configEntry,
|
||||
project: this,
|
||||
languageService: this.languageService,
|
||||
languageServiceHost: this,
|
||||
serverHost: this.projectService.host
|
||||
};
|
||||
|
||||
const pluginModule = pluginModuleFactory({ typescript: ts });
|
||||
const newLS = pluginModule.create(info);
|
||||
for (const k of Object.keys(this.languageService)) {
|
||||
if (!(k in newLS)) {
|
||||
this.projectService.logger.info(`Plugin activation warning: Missing proxied method ${k} in created LS. Patching.`);
|
||||
(newLS as any)[k] = (this.languageService as any)[k];
|
||||
}
|
||||
}
|
||||
this.projectService.logger.info(`Plugin validation succeded`);
|
||||
this.languageService = newLS;
|
||||
this.plugins.push(pluginModule);
|
||||
}
|
||||
catch (e) {
|
||||
this.projectService.logger.info(`Plugin activation failed: ${e}`);
|
||||
}
|
||||
this.enableGlobalPlugins();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1322,21 +1349,6 @@ namespace ts.server {
|
||||
return this.typeAcquisition;
|
||||
}
|
||||
|
||||
getExternalFiles(): SortedReadonlyArray<string> {
|
||||
return toSortedArray(flatMap(this.plugins, plugin => {
|
||||
if (typeof plugin.getExternalFiles !== "function") return;
|
||||
try {
|
||||
return plugin.getExternalFiles(this);
|
||||
}
|
||||
catch (e) {
|
||||
this.projectService.logger.info(`A plugin threw an exception in getExternalFiles: ${e}`);
|
||||
if (e.stack) {
|
||||
this.projectService.logger.info(e.stack);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
watchWildcards(wildcardDirectories: Map<WatchDirectoryFlags>) {
|
||||
updateWatchingWildcardDirectories(
|
||||
|
||||
@@ -566,7 +566,7 @@ namespace ts.server.protocol {
|
||||
|
||||
// TODO: GH#20538
|
||||
/* @internal */
|
||||
export interface GetCombinedCodeFixResponse extends Response {
|
||||
export interface GetCombinedCodeFixResponse extends Response {
|
||||
body: CombinedCodeActions;
|
||||
}
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@ namespace ts.server {
|
||||
export class ScriptVersionCache {
|
||||
private changes: TextChange[] = [];
|
||||
private readonly versions: LineIndexSnapshot[] = new Array<LineIndexSnapshot>(ScriptVersionCache.maxVersions);
|
||||
private minVersion = 0; // no versions earlier than min version will maintain change history
|
||||
private minVersion = 0; // no versions earlier than min version will maintain change history
|
||||
|
||||
private currentVersion = 0;
|
||||
|
||||
|
||||
@@ -979,6 +979,10 @@ namespace ts.server {
|
||||
allowLocalPluginLoads
|
||||
};
|
||||
|
||||
logger.info(`Starting TS Server`);
|
||||
logger.info(`Version: ${versionMajorMinor}`);
|
||||
logger.info(`Arguments: ${process.argv.join(" ")}`);
|
||||
|
||||
const ioSession = new IOSession(options);
|
||||
process.on("uncaughtException", err => {
|
||||
ioSession.logError(err, "unknown");
|
||||
|
||||
@@ -1230,7 +1230,7 @@ namespace ts.server {
|
||||
return project.getLanguageService().getCompletionEntryDetails(file, position, name, formattingOptions, source);
|
||||
});
|
||||
return simplifiedResult
|
||||
? result.map(details => ({ ...details, codeActions: map(details.codeActions, action => this.mapCodeAction(action, scriptInfo)) }))
|
||||
? result.map(details => ({ ...details, codeActions: map(details.codeActions, action => this.mapCodeAction(project, action)) }))
|
||||
: result;
|
||||
}
|
||||
|
||||
@@ -1560,7 +1560,7 @@ namespace ts.server {
|
||||
return undefined;
|
||||
}
|
||||
if (simplifiedResult) {
|
||||
return codeActions.map(codeAction => this.mapCodeAction(codeAction, scriptInfo));
|
||||
return codeActions.map(codeAction => this.mapCodeAction(project, codeAction));
|
||||
}
|
||||
else {
|
||||
return codeActions;
|
||||
@@ -1613,8 +1613,8 @@ namespace ts.server {
|
||||
return { startPosition, endPosition };
|
||||
}
|
||||
|
||||
private mapCodeAction({ description, changes: unmappedChanges, commands }: CodeAction, scriptInfo: ScriptInfo): protocol.CodeAction {
|
||||
const changes = unmappedChanges.map(change => this.mapTextChangesToCodeEditsUsingScriptinfo(change, scriptInfo));
|
||||
private mapCodeAction(project: Project, { description, changes: unmappedChanges, commands }: CodeAction): protocol.CodeAction {
|
||||
const changes = unmappedChanges.map(change => this.mapTextChangesToCodeEditsUsingScriptinfo(change, project.getScriptInfoForNormalizedPath(toNormalizedPath(change.fileName))));
|
||||
return { description, changes, commands };
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user