mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-16 05:58:32 -06:00
added message handlers
This commit is contained in:
parent
bcf58bf9e8
commit
00f35d1934
@ -424,16 +424,6 @@ namespace ts.server {
|
||||
script.svc.reloadFromFile(tmpfilename, cb);
|
||||
}
|
||||
}
|
||||
|
||||
editScript(filename: string, start: number, end: number, newText: string) {
|
||||
const script = this.getScriptInfo(filename);
|
||||
if (script) {
|
||||
script.editContent(start, end, newText);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error("No script with name '" + filename + "'");
|
||||
}
|
||||
}
|
||||
|
||||
class InferredProject extends Project {
|
||||
@ -453,21 +443,19 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
interface Delta {
|
||||
addedFiles: string[];
|
||||
removedFiles: string[];
|
||||
replacedFiles: string[];
|
||||
projectName: string;
|
||||
version: number;
|
||||
function findVersionedProjectByFileName<T extends VersionedProject>(projectFileName: string, projects: T[]): T {
|
||||
for (const proj of projects) {
|
||||
if (proj.getProjectFileName() === projectFileName) {
|
||||
return proj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const id = (x: any) => x;
|
||||
|
||||
abstract class VersionedProject extends Project {
|
||||
|
||||
private lastReportedFileNames: Map<string>;
|
||||
private lastReportedVersion: number = 0;
|
||||
private currentVersion: number = 1;
|
||||
currentVersion: number = 1;
|
||||
|
||||
updateGraph() {
|
||||
const oldProgram = this.program;
|
||||
@ -479,55 +467,41 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
getDeltaFromVersion(lastKnownVersion?: number): Delta {
|
||||
getChangesSinceVersion(lastKnownVersion?: number): protocol.ExternalProjectFiles {
|
||||
const info = {
|
||||
projectFileName: this.getProjectFileName(),
|
||||
version: this.currentVersion
|
||||
};
|
||||
if (this.lastReportedVersion === this.currentVersion) {
|
||||
return {
|
||||
projectName: this.getProjectFileName(),
|
||||
addedFiles: [],
|
||||
removedFiles: [],
|
||||
version: this.currentVersion,
|
||||
replacedFiles: []
|
||||
};
|
||||
return { info };
|
||||
}
|
||||
if (this.lastReportedFileNames && lastKnownVersion === this.lastReportedVersion) {
|
||||
const lastReportedFileNames = this.lastReportedFileNames;
|
||||
const currentFiles = arrayToMap(this.getFileNames(), id);
|
||||
const currentFiles = arrayToMap(this.getFileNames(), x => x);
|
||||
|
||||
const addedFiles: string[] = [];
|
||||
const removedFiles: string[] = [];
|
||||
const added: string[] = [];
|
||||
const removed: string[] = [];
|
||||
for (const id in currentFiles) {
|
||||
if (hasProperty(currentFiles, id) && !hasProperty(lastReportedFileNames, id)) {
|
||||
addedFiles.push(id);
|
||||
added.push(id);
|
||||
}
|
||||
}
|
||||
for (const id in lastReportedFileNames) {
|
||||
if (hasProperty(lastReportedFileNames, id) && !hasProperty(currentFiles, id)) {
|
||||
removedFiles.push(id);
|
||||
removed.push(id);
|
||||
}
|
||||
}
|
||||
this.lastReportedFileNames = currentFiles;
|
||||
|
||||
this.lastReportedFileNames = currentFiles;
|
||||
this.lastReportedVersion = this.currentVersion;
|
||||
return {
|
||||
projectName: this.getProjectFileName(),
|
||||
addedFiles,
|
||||
removedFiles,
|
||||
version: this.currentVersion,
|
||||
replacedFiles: []
|
||||
};
|
||||
return { info, changes: { added, removed } };
|
||||
}
|
||||
else {
|
||||
// unknown version - return everything
|
||||
const projectFileNames = this.getFileNames();
|
||||
this.lastReportedFileNames = arrayToMap(projectFileNames, id);
|
||||
return {
|
||||
projectName: this.getProjectFileName(),
|
||||
addedFiles: [],
|
||||
removedFiles: [],
|
||||
version: this.currentVersion,
|
||||
replacedFiles: projectFileNames
|
||||
};
|
||||
this.lastReportedFileNames = arrayToMap(projectFileNames, x => x);
|
||||
return { info, files: projectFileNames };
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -627,6 +601,11 @@ namespace ts.server {
|
||||
**/
|
||||
openFileRoots: ScriptInfo[] = [];
|
||||
|
||||
/**
|
||||
* maps external project file name to list of config files that were the part of this project
|
||||
*/
|
||||
externalProjectToConfiguredProjectMap: ts.Map<string[]> = {};
|
||||
|
||||
/**
|
||||
* external projects (configuration and list of root files is not controlled by tsserver)
|
||||
*/
|
||||
@ -1078,12 +1057,11 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private findConfiguredProjectByConfigFile(configFileName: string) {
|
||||
for (const configuredProject of this.configuredProjects) {
|
||||
if (configuredProject.configFileName === configFileName) {
|
||||
return configuredProject;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
return findVersionedProjectByFileName(configFileName, this.configuredProjects);
|
||||
}
|
||||
|
||||
private findExternalProjectByProjectFileName(projectFileName: string) {
|
||||
return findVersionedProjectByFileName(projectFileName, this.externalProjects);
|
||||
}
|
||||
|
||||
private configFileToProjectOptions(configFilename: string): { succeeded: boolean, projectOptions?: ProjectOptions, errors?: Diagnostic[] } {
|
||||
@ -1284,13 +1262,6 @@ namespace ts.server {
|
||||
return info;
|
||||
}
|
||||
|
||||
openExternalProject(proj: protocol.ExternalProject) {
|
||||
}
|
||||
|
||||
closeExternalProject(proj: protocol.ExternalProject) {
|
||||
// TODO: save mapping from external project name to set of configured projects
|
||||
}
|
||||
|
||||
log(msg: string, type = "Err") {
|
||||
this.psLogger.msg(msg, type);
|
||||
}
|
||||
@ -1475,11 +1446,70 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
loadExternalProject(externalProject: protocol.ExternalProject): Delta[] {
|
||||
private addExternalProjectFilesForVersionedProjects(knownProjects: protocol.ExternalProjectInfo[], projects: VersionedProject[], result: protocol.ExternalProjectFiles[]): void {
|
||||
for (const proj of projects) {
|
||||
const knownProject = ts.forEach(knownProjects, p => p.projectFileName === proj.getProjectFileName() && p);
|
||||
result.push(proj.getChangesSinceVersion(knownProjects && knownProject.version));
|
||||
}
|
||||
}
|
||||
|
||||
synchronizeProjectList(knownProjects: protocol.ExternalProjectInfo[]): protocol.ExternalProjectFiles[] {
|
||||
const files: protocol.ExternalProjectFiles[] = [];
|
||||
this.addExternalProjectFilesForVersionedProjects(knownProjects, this.externalProjects, files);
|
||||
this.addExternalProjectFilesForVersionedProjects(knownProjects, this.configuredProjects, files);
|
||||
for (const inferredProject of this.inferredProjects) {
|
||||
files.push({ files: inferredProject.getFileNames() });
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
applyChangesInOpenFiles(openFiles: protocol.OpenFile[], closedFiles: string[]): void {
|
||||
for (const file of openFiles) {
|
||||
const scriptInfo = this.getScriptInfo(file.fileName);
|
||||
if (!scriptInfo) {
|
||||
Debug.assert(!!file.content);
|
||||
this.openClientFile(file.fileName, file.content);
|
||||
}
|
||||
else {
|
||||
Debug.assert(!!file.textChanges);
|
||||
for (const change of file.textChanges) {
|
||||
scriptInfo.editContent(change.span.start, change.span.start + change.span.length, change.newText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const file of closedFiles) {
|
||||
this.closeClientFile(file);
|
||||
}
|
||||
|
||||
this.updateProjectStructure();
|
||||
}
|
||||
|
||||
closeExternalProject(fileName: string): void {
|
||||
fileName = normalizePath(fileName);
|
||||
const configFiles = this.externalProjectToConfiguredProjectMap[fileName];
|
||||
if (configFiles) {
|
||||
for (const configFile of configFiles) {
|
||||
const configuredProject = this.findConfiguredProjectByConfigFile(configFile);
|
||||
if (configuredProject) {
|
||||
this.removeProject(configuredProject);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// close external project
|
||||
const externalProject = this.findExternalProjectByProjectFileName(fileName);
|
||||
if (externalProject) {
|
||||
this.removeProject(externalProject);
|
||||
}
|
||||
}
|
||||
this.updateProjectStructure();
|
||||
}
|
||||
|
||||
openExternalProject(externalProject: protocol.ExternalProject): void {
|
||||
const project = this.findConfiguredProjectByConfigFile(externalProject.projectFileName);
|
||||
if (project) {
|
||||
this.updateConfiguredProjectWorker(project, externalProject.rootFiles, externalProject.options);
|
||||
return [project.getDeltaFromVersion()];
|
||||
}
|
||||
else {
|
||||
let tsConfigFiles: string[];
|
||||
@ -1493,32 +1523,18 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
if (tsConfigFiles) {
|
||||
const deltas: Delta[] = [];
|
||||
for (const tsconfigFile of tsConfigFiles) {
|
||||
const { success, project, errors } = this.openConfigFile(tsconfigFile);
|
||||
if (success) {
|
||||
// keep project alive
|
||||
project.addOpenRef();
|
||||
deltas.push(project.getDeltaFromVersion());
|
||||
}
|
||||
}
|
||||
return deltas;
|
||||
}
|
||||
else {
|
||||
const { project, errors } = this.createAndAddExternalProject(externalProject.projectFileName, externalProject.rootFiles, externalProject.options);
|
||||
return [project.getDeltaFromVersion()];
|
||||
this.createAndAddExternalProject(externalProject.projectFileName, externalProject.rootFiles, externalProject.options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadExternalProjects(externalProjects: protocol.ExternalProject[], openFiles: protocol.OpenFile[]): void {
|
||||
for (const project of externalProjects) {
|
||||
this.loadExternalProject(project);
|
||||
}
|
||||
for (const openFile of openFiles) {
|
||||
this.getOrCreateScriptInfo(openFile.fileName, /*openedByClient*/ true, openFile.content);
|
||||
}
|
||||
// TODO: return diff
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
59
src/server/protocol.d.ts
vendored
59
src/server/protocol.d.ts
vendored
@ -424,9 +424,37 @@ declare namespace ts.server.protocol {
|
||||
options: CompilerOptions;
|
||||
}
|
||||
|
||||
export interface ExternalProjectInfo {
|
||||
projectFileName: string;
|
||||
version: number;
|
||||
}
|
||||
|
||||
export interface ExternalProjectChanges {
|
||||
added: string[];
|
||||
removed: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes set of files in the project.
|
||||
* info might be omitted in case of inferred projects
|
||||
* if files is set - then this is the entire set of files in the project
|
||||
* if changes is set - then this is the set of changes that should be applied to existing project
|
||||
* otherwise - assume that nothing is changed
|
||||
*/
|
||||
export interface ExternalProjectFiles {
|
||||
info?: ExternalProjectInfo;
|
||||
files?: string[];
|
||||
changes?: ExternalProjectChanges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a set of changes for open document with a given file name.
|
||||
* Either content of textChanges should be present.
|
||||
*/
|
||||
export interface OpenFile {
|
||||
fileName: string;
|
||||
content?: string;
|
||||
textChanges?: ts.TextChange[];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -548,14 +576,35 @@ declare namespace ts.server.protocol {
|
||||
arguments: OpenRequestArgs;
|
||||
}
|
||||
|
||||
type LoadExternalProjectArgs = ExternalProject;
|
||||
type OpenExternalProjectArgs = ExternalProject;
|
||||
|
||||
export interface LoadExternalProject extends Request {
|
||||
arguments: LoadExternalProjectArgs;
|
||||
export interface OpenExternalProjectRequest extends Request {
|
||||
arguments: OpenExternalProjectArgs;
|
||||
}
|
||||
|
||||
interface LoadExternalProjectResponse extends Response {
|
||||
files: string[];
|
||||
export interface CloseExternalProjectRequestArgs {
|
||||
projectFileName: string;
|
||||
}
|
||||
|
||||
export interface CloseExternalProjectRequest extends Request {
|
||||
arguments: CloseExternalProjectRequestArgs;
|
||||
}
|
||||
|
||||
export interface SynchronizeProjectListRequest extends Request {
|
||||
arguments: SynchronizeProjectListRequestArgs;
|
||||
}
|
||||
|
||||
export interface SynchronizeProjectListRequestArgs {
|
||||
knownProjects: protocol.ExternalProjectInfo[];
|
||||
}
|
||||
|
||||
export interface ApplyChangedToOpenFilesRequest extends Request {
|
||||
arguments: ApplyChangedToOpenFilesRequestArgs;
|
||||
}
|
||||
|
||||
export interface ApplyChangedToOpenFilesRequestArgs {
|
||||
openFiles: OpenFile[];
|
||||
closedFiles: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -127,7 +127,10 @@ namespace ts.server {
|
||||
export const ProjectInfo = "projectInfo";
|
||||
export const ReloadProjects = "reloadProjects";
|
||||
export const Unknown = "unknown";
|
||||
export const LoadExternalProject = "loadExternalProject";
|
||||
export const OpenExternalProject = "openExternalProject";
|
||||
export const CloseExternalProject = "closeExternalProject";
|
||||
export const SynchronizeProjectList = "synchronizeProjectList";
|
||||
export const ApplyChangedToOpenFiles = "applyChangedToOpenFiles";
|
||||
}
|
||||
|
||||
namespace Errors {
|
||||
@ -268,7 +271,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private updateProjectStructure(seq: number, matchSeq: (seq: number) => boolean, ms = 1500) {
|
||||
setTimeout(() => {
|
||||
this.host.setTimeout(() => {
|
||||
if (matchSeq(seq)) {
|
||||
this.projectService.updateProjectStructure();
|
||||
}
|
||||
@ -298,7 +301,7 @@ namespace ts.server {
|
||||
this.semanticCheck(checkSpec.fileName, checkSpec.project);
|
||||
this.immediateId = undefined;
|
||||
if (checkList.length > index) {
|
||||
this.errorTimer = setTimeout(checkOne, followMs);
|
||||
this.errorTimer = this.host.setTimeout(checkOne, followMs);
|
||||
}
|
||||
else {
|
||||
this.errorTimer = undefined;
|
||||
@ -308,7 +311,7 @@ namespace ts.server {
|
||||
}
|
||||
};
|
||||
if ((checkList.length > index) && (matchSeq(seq))) {
|
||||
this.errorTimer = setTimeout(checkOne, ms);
|
||||
this.errorTimer = this.host.setTimeout(checkOne, ms);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1028,30 +1031,50 @@ namespace ts.server {
|
||||
exit() {
|
||||
}
|
||||
|
||||
private notRequired() {
|
||||
return { responseRequired: false };
|
||||
}
|
||||
|
||||
private requiredResponse(response: any) {
|
||||
return { response, responseRequired: true };
|
||||
}
|
||||
|
||||
private handlers: Map<(request: protocol.Request) => { response?: any, responseRequired?: boolean }> = {
|
||||
[CommandNames.LoadExternalProject]: (request: protocol.Request) => {
|
||||
const deltas = this.projectService.loadExternalProject(request.arguments);
|
||||
return { responseRequired: true, response: { files: deltas } };
|
||||
[CommandNames.OpenExternalProject]: (request: protocol.OpenExternalProjectRequest) => {
|
||||
const deltas = this.projectService.openExternalProject(request.arguments);
|
||||
return this.notRequired();
|
||||
},
|
||||
[CommandNames.CloseExternalProject]: (request: protocol.CloseExternalProjectRequest) => {
|
||||
this.projectService.closeExternalProject(request.arguments.projectFileName);
|
||||
return this.notRequired();
|
||||
},
|
||||
[CommandNames.SynchronizeProjectList]: (request: protocol.SynchronizeProjectListRequest) => {
|
||||
const result = this.projectService.synchronizeProjectList(request.arguments.knownProjects);
|
||||
return this.requiredResponse(result);
|
||||
},
|
||||
[CommandNames.ApplyChangedToOpenFiles]: (request: protocol.ApplyChangedToOpenFilesRequest) => {
|
||||
this.projectService.applyChangesInOpenFiles(request.arguments.openFiles, request.arguments.closedFiles);
|
||||
return this.notRequired();
|
||||
},
|
||||
[CommandNames.Exit]: () => {
|
||||
this.exit();
|
||||
return { responseRequired: false };
|
||||
return this.notRequired();
|
||||
},
|
||||
[CommandNames.Definition]: (request: protocol.Request) => {
|
||||
const defArgs = <protocol.FileLocationRequestArgs>request.arguments;
|
||||
return { response: this.getDefinition(defArgs.line, defArgs.offset, defArgs.file), responseRequired: true };
|
||||
return this.requiredResponse(this.getDefinition(defArgs.line, defArgs.offset, defArgs.file));
|
||||
},
|
||||
[CommandNames.TypeDefinition]: (request: protocol.Request) => {
|
||||
const defArgs = <protocol.FileLocationRequestArgs>request.arguments;
|
||||
return { response: this.getTypeDefinition(defArgs.line, defArgs.offset, defArgs.file), responseRequired: true };
|
||||
return this.requiredResponse(this.getTypeDefinition(defArgs.line, defArgs.offset, defArgs.file));
|
||||
},
|
||||
[CommandNames.References]: (request: protocol.Request) => {
|
||||
const defArgs = <protocol.FileLocationRequestArgs>request.arguments;
|
||||
return { response: this.getReferences(defArgs.line, defArgs.offset, defArgs.file), responseRequired: true };
|
||||
return this.requiredResponse(this.getReferences(defArgs.line, defArgs.offset, defArgs.file));
|
||||
},
|
||||
[CommandNames.Rename]: (request: protocol.Request) => {
|
||||
const renameArgs = <protocol.RenameRequestArgs>request.arguments;
|
||||
return { response: this.getRenameLocations(renameArgs.line, renameArgs.offset, renameArgs.file, renameArgs.findInComments, renameArgs.findInStrings), responseRequired: true };
|
||||
return this.requiredResponse(this.getRenameLocations(renameArgs.line, renameArgs.offset, renameArgs.file, renameArgs.findInComments, renameArgs.findInStrings));
|
||||
},
|
||||
[CommandNames.Open]: (request: protocol.Request) => {
|
||||
const openArgs = <protocol.OpenRequestArgs>request.arguments;
|
||||
@ -1071,7 +1094,7 @@ namespace ts.server {
|
||||
break;
|
||||
}
|
||||
this.openClientFile(openArgs.file, openArgs.fileContent, scriptKind);
|
||||
return { responseRequired: false };
|
||||
return this.notRequired();
|
||||
},
|
||||
[CommandNames.Quickinfo]: (request: protocol.Request) => {
|
||||
const quickinfoArgs = <protocol.FileLocationRequestArgs>request.arguments;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user