Skip to content
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

Make GetEditsForFileRenameRequestArgs not extend FileRequestArgs #25052

Merged
8 commits merged into from
Jun 25, 2018
15 changes: 15 additions & 0 deletions src/server/editorServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,13 @@ namespace ts.server {
return this.findExternalProjectByProjectName(projectName) || this.findConfiguredProjectByProjectName(toNormalizedPath(projectName));
}

/* @internal */
forEachProject(cb: (project: Project) => void) {
for (const p of this.inferredProjects) cb(p);
this.configuredProjects.forEach(cb);
this.externalProjects.forEach(cb);
}

getDefaultProjectForFile(fileName: NormalizedPath, ensureProject: boolean): Project | undefined {
let scriptInfo = this.getScriptInfoForNormalizedPath(fileName);
if (ensureProject && (!scriptInfo || scriptInfo.isOrphan())) {
Expand Down Expand Up @@ -742,6 +749,14 @@ namespace ts.server {
return info && info.getPreferences() || this.hostConfiguration.preferences;
}

getHostFormatCodeOptions(): FormatCodeSettings {
return this.hostConfiguration.formatCodeOptions;
}

getHostPreferences(): UserPreferences {
return this.hostConfiguration.preferences;
}

private onSourceFileChanged(fileName: string, eventKind: FileWatcherEventKind, path: Path) {
const info = this.getScriptInfoForPath(path);
if (!info) {
Expand Down
5 changes: 2 additions & 3 deletions src/server/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -627,9 +627,8 @@ namespace ts.server.protocol {
arguments: GetEditsForFileRenameRequestArgs;
}

// Note: The file from FileRequestArgs is just any file in the project.
// We will generate code changes for every file in that project, so the choice is arbitrary.
export interface GetEditsForFileRenameRequestArgs extends FileRequestArgs {
/** Note: Paths may also be directories. */
export interface GetEditsForFileRenameRequestArgs {
readonly oldFilePath: string;
readonly newFilePath: string;
}
Expand Down
45 changes: 37 additions & 8 deletions src/server/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1142,7 +1142,7 @@ namespace ts.server {
return this.getPosition(args, scriptInfo);
}

private getFileAndProject(args: protocol.FileRequestArgs): { file: NormalizedPath, project: Project } {
private getFileAndProject(args: protocol.FileRequestArgs): FileAndProject {
return this.getFileAndProjectWorker(args.file, args.projectFileName);
}

Expand Down Expand Up @@ -1738,9 +1738,23 @@ namespace ts.server {
}

private getEditsForFileRename(args: protocol.GetEditsForFileRenameRequestArgs, simplifiedResult: boolean): ReadonlyArray<protocol.FileCodeEdits> | ReadonlyArray<FileTextChanges> {
const { file, project } = this.getFileAndProject(args);
const changes = project.getLanguageService().getEditsForFileRename(toNormalizedPath(args.oldFilePath), toNormalizedPath(args.newFilePath), this.getFormatOptions(file), this.getPreferences(file));
return simplifiedResult ? this.mapTextChangesToCodeEdits(project, changes) : changes;
const oldPath = toNormalizedPath(args.oldFilePath);
const newPath = toNormalizedPath(args.newFilePath);
const formatOptions = this.getHostFormatOptions();
const preferences = this.getHostPreferences();

const changes: (protocol.FileCodeEdits | FileTextChanges)[] = [];
this.projectService.forEachProject(project => {
if (project.isOrphan() || !project.languageServiceEnabled) return;
for (const fileTextChanges of project.getLanguageService().getEditsForFileRename(oldPath, newPath, formatOptions, preferences)) {
// Subsequent projects may make conflicting edits to the same file -- just go with the first.
if (!changes.some(f => f.fileName === fileTextChanges.fileName)) {
changes.push(simplifiedResult ? this.mapTextChangeToCodeEdit(project, fileTextChanges) : fileTextChanges);
}
}
});

return changes as ReadonlyArray<protocol.FileCodeEdits> | ReadonlyArray<FileTextChanges>;
}

private getCodeFixes(args: protocol.CodeFixRequestArgs, simplifiedResult: boolean): ReadonlyArray<protocol.CodeFixAction> | ReadonlyArray<CodeFixAction> | undefined {
Expand Down Expand Up @@ -1810,10 +1824,12 @@ namespace ts.server {
}

private mapTextChangesToCodeEdits(project: Project, textChanges: ReadonlyArray<FileTextChanges>): protocol.FileCodeEdits[] {
return textChanges.map(change => {
const path = normalizedPathToPath(toNormalizedPath(change.fileName), this.host.getCurrentDirectory(), fileName => this.getCanonicalFileName(fileName));
return mapTextChangesToCodeEdits(change, project.getSourceFileOrConfigFile(path));
});
return textChanges.map(change => this.mapTextChangeToCodeEdit(project, change));
}

private mapTextChangeToCodeEdit(project: Project, change: FileTextChanges): protocol.FileCodeEdits {
const path = normalizedPathToPath(toNormalizedPath(change.fileName), this.host.getCurrentDirectory(), fileName => this.getCanonicalFileName(fileName));
return mapTextChangesToCodeEdits(change, project.getSourceFileOrConfigFile(path));
}

private convertTextChangeToCodeEdit(change: TextChange, scriptInfo: ScriptInfo): protocol.CodeEdit {
Expand Down Expand Up @@ -2303,6 +2319,19 @@ namespace ts.server {
private getPreferences(file: NormalizedPath): UserPreferences {
return this.projectService.getPreferences(file);
}

private getHostFormatOptions(): FormatCodeSettings {
return this.projectService.getHostFormatCodeOptions();
}

private getHostPreferences(): UserPreferences {
return this.projectService.getHostPreferences();
}
}

interface FileAndProject {
readonly file: NormalizedPath;
readonly project: Project;
}

function mapTextChangesToCodeEdits(textChanges: FileTextChanges, sourceFile: SourceFile | undefined): protocol.FileCodeEdits {
Expand Down
60 changes: 57 additions & 3 deletions src/testRunner/unittests/tsserverProjectSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,18 @@ namespace ts.projectSystem {
return createTextSpan(start, substring.length);
}

function protocolTextSpanFromSubstring(str: string, substring: string): protocol.TextSpan {
const start = str.indexOf(substring);
Debug.assert(start !== -1);
const lineStarts = computeLineStarts(str);
const toLocation = (pos: number) => lineAndCharacterToLocation(computeLineAndCharacterOfPosition(lineStarts, pos));
return { start: toLocation(start), end: toLocation(start + substring.length) };
}

function lineAndCharacterToLocation(lc: LineAndCharacter): protocol.Location {
return { line: lc.line + 1, offset: lc.character + 1 };
}

/**
* Test server cancellation token used to mock host token cancellation requests.
* The cancelAfterRequest constructor param specifies how many isCancellationRequested() calls
Expand Down Expand Up @@ -464,14 +476,13 @@ namespace ts.projectSystem {
}
}

export function makeSessionRequest<T>(command: string, args: T) {
const newRequest: protocol.Request = {
export function makeSessionRequest<T>(command: string, args: T): protocol.Request {
return {
seq: 0,
type: "request",
command,
arguments: args
};
return newRequest;
}

export function openFilesForSession(files: ReadonlyArray<File>, session: server.Session) {
Expand Down Expand Up @@ -8682,6 +8693,49 @@ export const x = 10;`
}],
}]);
});

it("works with multiple projects", () => {
const aUserTs: File = {
path: "/a/user.ts",
content: 'import { x } from "./old";',
};
const aOldTs: File = {
path: "/a/old.ts",
content: "export const x = 0;",
};
const aTsconfig: File = {
path: "/a/tsconfig.json",
content: "{}",
};
const bUserTs: File = {
path: "/b/user.ts",
content: 'import { x } from "../a/old";',
};
const bTsconfig: File = {
path: "/b/tsconfig.json",
content: "{}",
};

const host = createServerHost([aUserTs, aOldTs, aTsconfig, bUserTs, bTsconfig]);
const session = createSession(host);
openFilesForSession([aUserTs, bUserTs], session);

const renameRequest = makeSessionRequest<protocol.GetEditsForFileRenameRequestArgs>(CommandNames.GetEditsForFileRename, {
oldFilePath: "/a/old.ts",
newFilePath: "/a/new.ts",
});
const response = session.executeCommand(renameRequest).response as protocol.GetEditsForFileRenameResponse["body"];
assert.deepEqual(response, [
{
fileName: aUserTs.path,
textChanges: [{ ...protocolTextSpanFromSubstring(aUserTs.content, "./old"), newText: "./new" }],
},
{
fileName: bUserTs.path,
textChanges: [{ ...protocolTextSpanFromSubstring(bUserTs.content, "../a/old"), newText: "../a/new" }],
},
]);
});
});

describe("tsserverProjectSystem document registry in project service", () => {
Expand Down
8 changes: 7 additions & 1 deletion tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12455,7 +12455,7 @@ declare namespace ts.server.protocol {
command: CommandTypes.GetEditsForFileRename;
arguments: GetEditsForFileRenameRequestArgs;
}
interface GetEditsForFileRenameRequestArgs extends FileRequestArgs {
interface GetEditsForFileRenameRequestArgs {
readonly oldFilePath: string;
readonly newFilePath: string;
}
Expand Down Expand Up @@ -13827,11 +13827,14 @@ declare namespace ts.server {
private delayUpdateProjectGraphs;
setCompilerOptionsForInferredProjects(projectCompilerOptions: protocol.ExternalProjectCompilerOptions, projectRootPath?: string): void;
findProject(projectName: string): Project | undefined;
forEachProject(cb: (project: Project) => void): void;
getDefaultProjectForFile(fileName: NormalizedPath, ensureProject: boolean): Project | undefined;
getScriptInfoEnsuringProjectsUptoDate(uncheckedFileName: string): ScriptInfo | undefined;
private ensureProjectStructuresUptoDate;
getFormatCodeOptions(file: NormalizedPath): FormatCodeSettings;
getPreferences(file: NormalizedPath): UserPreferences;
getHostFormatCodeOptions(): FormatCodeSettings;
getHostPreferences(): UserPreferences;
private onSourceFileChanged;
private handleDeletedFile;
watchWildcardDirectory(directory: Path, flags: WatchDirectoryFlags, project: ConfiguredProject): FileWatcher;
Expand Down Expand Up @@ -14062,6 +14065,7 @@ declare namespace ts.server {
private mapCodeAction;
private mapCodeFixAction;
private mapTextChangesToCodeEdits;
private mapTextChangeToCodeEdit;
private convertTextChangeToCodeEdit;
private getBraceMatching;
private getDiagnosticsForProject;
Expand All @@ -14078,6 +14082,8 @@ declare namespace ts.server {
onMessage(message: string): void;
private getFormatOptions;
private getPreferences;
private getHostFormatOptions;
private getHostPreferences;
}
interface HandlerResponse {
response?: {};
Expand Down