renameLocations

This commit is contained in:
Vladimir Matveev 2016-06-20 11:25:31 -07:00
parent 0b7fb0dba8
commit bdccb8d0eb
3 changed files with 127 additions and 113 deletions

View file

@ -395,7 +395,7 @@ namespace ts.server {
}
getScriptInfo(fileName: string) {
const scriptInfo = this.projectService.getOrCreateScriptInfo(fileName, /*openedByClient*/ false);
const scriptInfo = this.projectService.getOrCreateScriptInfo(fileName, /*openedByClient*/ false, this);
if (scriptInfo && !scriptInfo.defaultProject) {
scriptInfo.defaultProject = this;
}
@ -1157,7 +1157,7 @@ namespace ts.server {
let errors: Diagnostic[];
for (const rootFilename of files) {
if (this.host.fileExists(rootFilename)) {
const info = this.getOrCreateScriptInfo(rootFilename, /*openedByClient*/ clientFileName == rootFilename);
const info = this.getOrCreateScriptInfo(rootFilename, /*openedByClient*/ clientFileName == rootFilename, project);
project.addRoot(info);
}
else {
@ -1195,7 +1195,7 @@ namespace ts.server {
for (const fileName of fileNamesToAdd) {
let info = this.getScriptInfo(fileName);
if (!info) {
info = this.getOrCreateScriptInfo(fileName, /*openedByClient*/ false);
info = this.getOrCreateScriptInfo(fileName, /*openedByClient*/ false, project);
}
else {
// if the root file was opened by client, it would belong to either
@ -1268,7 +1268,7 @@ namespace ts.server {
* @param filename is absolute pathname
* @param fileContent is a known version of the file content that is more up to date than the one on disk
*/
getOrCreateScriptInfo(fileName: string, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind) {
getOrCreateScriptInfo(fileName: string, openedByClient: boolean, containingProject: Project, fileContent?: string, scriptKind?: ScriptKind) {
fileName = ts.normalizePath(fileName);
let info = ts.lookUp(this.filenameToScriptInfo, fileName);
if (!info) {
@ -1284,6 +1284,7 @@ namespace ts.server {
if (content !== undefined) {
info = new ScriptInfo(this.host, fileName, content, openedByClient);
info.scriptKind = scriptKind;
info.defaultProject = containingProject;
info.setFormatOptions(toEditorSettings(this.getFormatCodeOptions()));
this.filenameToScriptInfo[fileName] = info;
if (!info.isOpen) {
@ -1333,27 +1334,27 @@ namespace ts.server {
findReferencingProjects(info: ScriptInfo, excludedProject?: Project) {
const referencingProjects: Project[] = [];
info.defaultProject = undefined;
for (let i = 0, len = this.inferredProjects.length; i < len; i++) {
const inferredProject = this.inferredProjects[i];
inferredProject.updateGraph();
if (inferredProject !== excludedProject) {
if (inferredProject.containsScriptInfo(info)) {
info.defaultProject = inferredProject;
referencingProjects.push(inferredProject);
}
}
}
for (let i = 0, len = this.configuredProjects.length; i < len; i++) {
const configuredProject = this.configuredProjects[i];
configuredProject.updateGraph();
if (configuredProject.containsScriptInfo(info)) {
info.defaultProject = configuredProject;
referencingProjects.push(configuredProject);
}
this.collectContainingProjects(info, referencingProjects, this.inferredProjects, excludedProject);
this.collectContainingProjects(info, referencingProjects, this.configuredProjects);
this.collectContainingProjects(info, referencingProjects, this.externalProjects);
if (referencingProjects.length) {
info.defaultProject = referencingProjects[0];
}
return referencingProjects;
}
private collectContainingProjects(info: ScriptInfo, result: Project[], projects: Project[], excludedProject?: Project) {
for (const p of projects) {
if (p === excludedProject) {
continue;
}
p.updateGraph();
if (p.containsScriptInfo(info)) {
result.push(p);
}
}
}
/**
* This function rebuilds the project for every file opened by the client
*/
@ -1464,7 +1465,7 @@ namespace ts.server {
if (!this.findContainingExternalProject(fileName)) {
({ configFileName, configFileErrors } = this.openOrUpdateConfiguredProjectForFile(fileName));
}
const info = this.getOrCreateScriptInfo(fileName, /*openedByClient*/ true, fileContent, scriptKind);
const info = this.getOrCreateScriptInfo(fileName, /*openedByClient*/ true, /*containingProject*/ undefined, fileContent, scriptKind);
this.addOpenFile(info);
this.printProjects();
return { configFileName, configFileErrors };

View file

@ -135,6 +135,7 @@ namespace ts.server {
export const Reload = "reload";
export const Rename = "rename";
export const RenameInfoFull = "rename-full";
export const RenameLocationsFull = "renameLocations-full";
export const Saveto = "saveto";
export const SignatureHelp = "signatureHelp";
export const SignatureHelpFull = "signatureHelp-full";
@ -512,7 +513,7 @@ namespace ts.server {
}
const scriptInfo = project.getScriptInfo(fileName);
const position = this.getPosition(args, scriptInfo);
const position = this.getPosition(args, scriptInfo);
const documentHighlights = project.languageService.getDocumentHighlights(fileName, position, args.filesToSearch);
@ -563,72 +564,109 @@ namespace ts.server {
private getRenameInfo(args: protocol.FileLocationRequestArgs) {
const { file, project } = this.getFileAndProject(args.file);
const scriptInfo = project.getScriptInfo(file);
const position = this.getPosition(args, scriptInfo)
const position = this.getPosition(args, scriptInfo);
return project.languageService.getRenameInfo(file, position);
}
private getRenameLocations(line: number, offset: number, fileName: string, findInComments: boolean, findInStrings: boolean): protocol.RenameResponseBody {
const file = ts.normalizePath(fileName);
const info = this.projectService.getScriptInfo(file);
const projects = this.projectService.findReferencingProjects(info);
if (!projects.length) {
private getProjects(args: protocol.FileRequestArgs) {
let projects: Project[];
if (args.projectFileName) {
const project = this.getProject(args.projectFileName);
if (project) {
projects = [project];
}
}
else {
const file = normalizePath(args.file);
const info = this.projectService.getScriptInfo(file);
projects = this.projectService.findReferencingProjects(info);
}
if (!projects || !projects.length) {
throw Errors.NoProject;
}
return projects;
}
const defaultProject = projects[0];
// The rename info should be the same for every project
const scriptInfo = defaultProject.getScriptInfo(file);
const position = scriptInfo.lineOffsetToPosition(line, offset);
const renameInfo = defaultProject.languageService.getRenameInfo(file, position);
if (!renameInfo) {
return undefined;
private getRenameLocations(args: protocol.RenameRequestArgs, simplifiedResult: boolean): protocol.RenameResponseBody | RenameLocation[] {
const file = ts.normalizePath(args.file);
const info = this.projectService.getScriptInfo(file);
const position = this.getPosition(args, info);
const projects = this.getProjects(args);
if (simplifiedResult) {
const defaultProject = projects[0];
// The rename info should be the same for every project
const renameInfo = defaultProject.languageService.getRenameInfo(file, position);
if (!renameInfo) {
return undefined;
}
if (!renameInfo.canRename) {
return {
info: renameInfo,
locs: []
};
}
const fileSpans = combineProjectOutput(
projects,
(project: Project) => {
const renameLocations = project.languageService.findRenameLocations(file, position, args.findInStrings, args.findInComments);
if (!renameLocations) {
return [];
}
return renameLocations.map(location => {
const locationScriptInfo = project.getScriptInfo(location.fileName);
return <protocol.FileSpan>{
file: location.fileName,
start: locationScriptInfo.positionToLineOffset(location.textSpan.start),
end: locationScriptInfo.positionToLineOffset(ts.textSpanEnd(location.textSpan)),
};
});
},
compareRenameLocation,
(a, b) => a.file === b.file && a.start.line === b.start.line && a.start.offset === b.start.offset
);
const locs = fileSpans.reduce<protocol.SpanGroup[]>((accum, cur) => {
let curFileAccum: protocol.SpanGroup;
if (accum.length > 0) {
curFileAccum = accum[accum.length - 1];
if (curFileAccum.file !== cur.file) {
curFileAccum = undefined;
}
}
if (!curFileAccum) {
curFileAccum = { file: cur.file, locs: [] };
accum.push(curFileAccum);
}
curFileAccum.locs.push({ start: cur.start, end: cur.end });
return accum;
}, []);
return { info: renameInfo, locs };
}
else {
return combineProjectOutput(
projects,
p => p.languageService.findRenameLocations(file, position, args.findInStrings, args.findInComments),
/*comparer*/ undefined,
renameLocationIsEqualTo
);
}
if (!renameInfo.canRename) {
return {
info: renameInfo,
locs: []
};
function renameLocationIsEqualTo(a: RenameLocation, b: RenameLocation) {
if (a === b) {
return true;
}
if (!a || !b) {
return false;
}
return a.fileName === b.fileName &&
a.textSpan.start === b.textSpan.start &&
a.textSpan.length === b.textSpan.length;
}
const fileSpans = combineProjectOutput(
projects,
(project: Project) => {
const renameLocations = project.languageService.findRenameLocations(file, position, findInStrings, findInComments);
if (!renameLocations) {
return [];
}
return renameLocations.map(location => {
const locationScriptInfo = project.getScriptInfo(location.fileName);
return <protocol.FileSpan>{
file: location.fileName,
start: locationScriptInfo.positionToLineOffset(location.textSpan.start),
end: locationScriptInfo.positionToLineOffset(ts.textSpanEnd(location.textSpan)),
};
});
},
compareRenameLocation,
(a, b) => a.file === b.file && a.start.line === b.start.line && a.start.offset === b.start.offset
);
const locs = fileSpans.reduce<protocol.SpanGroup[]>((accum, cur) => {
let curFileAccum: protocol.SpanGroup;
if (accum.length > 0) {
curFileAccum = accum[accum.length - 1];
if (curFileAccum.file !== cur.file) {
curFileAccum = undefined;
}
}
if (!curFileAccum) {
curFileAccum = { file: cur.file, locs: [] };
accum.push(curFileAccum);
}
curFileAccum.locs.push({ start: cur.start, end: cur.end });
return accum;
}, []);
return { info: renameInfo, locs };
function compareRenameLocation(a: protocol.FileSpan, b: protocol.FileSpan) {
if (a.file < b.file) {
return -1;
@ -651,22 +689,9 @@ namespace ts.server {
}
}
private getReferences(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.ReferencesResponseBody | ReferencedSymbol[] {
private getReferences(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.ReferencesResponseBody | ReferencedSymbol[] {
const file = ts.normalizePath(args.file);
let projects: Project[];
if (args.projectFileName) {
const project = this.getProject(args.projectFileName);
if (project) {
projects = [project];
}
}
else {
const info = this.projectService.getScriptInfo(file);
projects = this.projectService.findReferencingProjects(info);
}
if (!projects || !projects.length) {
throw Errors.NoProject;
}
const projects = this.getProjects(args);
const defaultProject = projects[0];
const scriptInfo = defaultProject.getScriptInfo(file);
@ -1133,21 +1158,7 @@ namespace ts.server {
}
private getNavigateToItems(args: protocol.NavtoRequestArgs, simplifiedResult: boolean): protocol.NavtoItem[] | NavigateToItem[] {
let projects: Project[];
if (args.projectFileName) {
const project = this.getProject(args.projectFileName);
if (project) {
projects = [project];
}
}
else {
const file = normalizePath(args.file);
const info = this.projectService.getScriptInfo(file);
projects = this.projectService.findReferencingProjects(info);
}
if (!projects || !projects.length) {
throw Errors.NoProject;
}
const projects = this.getProjects(args);
if (simplifiedResult) {
return combineProjectOutput(
@ -1352,8 +1363,10 @@ namespace ts.server {
return this.requiredResponse(this.getReferences(request.arguments, /*simplifiedResult*/ false));
},
[CommandNames.Rename]: (request: protocol.Request) => {
const renameArgs = <protocol.RenameRequestArgs>request.arguments;
return this.requiredResponse(this.getRenameLocations(renameArgs.line, renameArgs.offset, renameArgs.file, renameArgs.findInComments, renameArgs.findInStrings));
return this.requiredResponse(this.getRenameLocations(request.arguments, /*simplifiedResult*/ true));
},
[CommandNames.RenameLocationsFull]: (request: protocol.RenameRequest) => {
return this.requiredResponse(this.getRenameLocations(request.arguments, /*simplifiedResult*/ false));
},
[CommandNames.RenameInfoFull]: (request: protocol.FileLocationRequest) => {
return this.requiredResponse(this.getRenameInfo(request.arguments));
@ -1476,7 +1489,7 @@ namespace ts.server {
[CommandNames.Reload]: (request: protocol.Request) => {
const reloadArgs = <protocol.ReloadRequestArgs>request.arguments;
this.reload(reloadArgs.file, reloadArgs.tmpfile, request.seq);
return {response: { reloadFinished: true }, responseRequired: true};
return { response: { reloadFinished: true }, responseRequired: true };
},
[CommandNames.Saveto]: (request: protocol.Request) => {
const savetoArgs = <protocol.SavetoRequestArgs>request.arguments;

View file

@ -80,7 +80,7 @@ namespace ts {
};
const projectService = new server.ProjectService(serverHost, logger, { isCancellationRequested: () => false });
const rootScriptInfo = projectService.getOrCreateScriptInfo(rootFile, /* openedByClient */true);
const rootScriptInfo = projectService.getOrCreateScriptInfo(rootFile, /* openedByClient */true, /*containingProject*/ undefined);
const project = projectService.createAndAddInferredProject(rootScriptInfo);
project.setCompilerOptions({ module: ts.ModuleKind.AMD } );
return {