Skip to content

Commit

Permalink
CodeMapper support
Browse files Browse the repository at this point in the history
  • Loading branch information
zkat committed Feb 14, 2024
1 parent e2bf8b4 commit d49995b
Show file tree
Hide file tree
Showing 9 changed files with 537 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/harness/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,8 @@ export class SessionClient implements LanguageService {
});
}

mapCode = notImplemented;

private createFileLocationOrRangeRequestArgs(positionOrRange: number | TextRange, fileName: string): protocol.FileLocationOrRangeRequestArgs {
return typeof positionOrRange === "number"
? this.createFileLocationRequestArgs(fileName, positionOrRange)
Expand Down
37 changes: 37 additions & 0 deletions src/server/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ export const enum CommandTypes {
ProvideCallHierarchyOutgoingCalls = "provideCallHierarchyOutgoingCalls",
ProvideInlayHints = "provideInlayHints",
WatchChange = "watchChange",
MapCode = "mapCode",
}

/**
Expand Down Expand Up @@ -2700,6 +2701,42 @@ export interface InlayHintsResponse extends Response {
body?: InlayHintItem[];
}

export interface MapCodeRequestArgs {
/// The files and changes to try and apply/map.
mappings: MapCodeRequestDocumentMapping[];

/// Edits to apply to the current workspace before performing the mapping.
updates?: FileCodeEdits[];
}

export interface MapCodeRequestDocumentMapping {
/// The file for the request (absolute pathname required). Null/undefined
/// if specific file is unknown.
file?: string;

/// Optional name of project that contains file
projectFileName?: string;

/// The specific code to map/insert/replace in the file.
contents: string[];

/// Areas of "focus" to inform the code mapper with. For example, cursor
/// location, current selection, viewport, etc. Nested arrays denote
/// priority: toplevel arrays are more important than inner arrays, and
/// inner array priorities are based on items within that array. Items
/// earlier in the arrays have higher priority.
focusLocations?: FileSpan[][];
}

export interface MapCodeRequest extends Request {
command: CommandTypes.MapCode;
arguments: MapCodeRequestArgs;
}

export interface MapCodeResponse extends Response {
body: FileCodeEdits[];
}

/**
* Synchronous request for semantic diagnostics of one file.
*/
Expand Down
69 changes: 69 additions & 0 deletions src/server/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ import {
LineAndCharacter,
LinkedEditingInfo,
map,
MapCodeDocumentMapping,
mapDefined,
mapDefinedIterator,
mapIterator,
Expand Down Expand Up @@ -1905,6 +1906,71 @@ export class Session<TMessage = string> implements EventSender {
});
}

private mapCode(args: protocol.MapCodeRequestArgs): protocol.FileCodeEdits[] {
const formatOptions = this.getHostFormatOptions();
const preferences = this.getHostPreferences();
const projects = new Map<Project, MapCodeDocumentMapping[]>();
args.mappings.forEach(mapping => {
if (!mapping.file) {
return { contents: mapping.contents };
}
const { file, project } = this.getFileAndProjectWorker(mapping.file, mapping.projectFileName);
const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file)!;
const focusLocations = mapping.focusLocations?.map(spans => {
return spans.map(loc => {
const start = scriptInfo.lineOffsetToPosition(loc.start.line, loc.start.offset);
const end = scriptInfo.lineOffsetToPosition(loc.end.line, loc.end.offset);
return {
start,
length: end - start,
};
});
});
if (!projects.has(project)) {
projects.set(project, []);
}
projects.get(project)!.push({
contents: mapping.contents,
fileName: file,
focusLocations,
});
});
const updates = args.updates?.map(edit => {
const file = this.getFileAndProjectWorker(edit.fileName, /*projectFileName*/ undefined).file;
const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file)!;
return {
fileName: edit.fileName,
textChanges: edit.textChanges.map(({ start, end, newText }) => {
const newStart = scriptInfo.lineOffsetToPosition(start.line, start.offset);
const newEnd = scriptInfo.lineOffsetToPosition(end.line, end.offset);
return {
span: { start: newStart, length: newEnd - newStart },
newText,
};
}),
};
});

return [...projects.entries()].map(([project, mappings]) => {
return project.getLanguageService().mapCode(mappings, formatOptions, preferences, updates);
}).filter(x => x).flatMap(changes => {
return changes.map(change => {
const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(toNormalizedPath(change.fileName))!;
return {
fileName: change.fileName,
textChanges: change.textChanges.map(({ span, newText }) => {
const newSpan = toProtocolTextSpan(span, scriptInfo);
return {
start: newSpan.start,
end: newSpan.end,
newText,
};
}),
};
});
});
}

private setCompilerOptionsForInferredProjects(args: protocol.SetCompilerOptionsForInferredProjectsArgs): void {
this.projectService.setCompilerOptionsForInferredProjects(args.options, args.projectRootPath);
}
Expand Down Expand Up @@ -3583,6 +3649,9 @@ export class Session<TMessage = string> implements EventSender {
[protocol.CommandTypes.ProvideInlayHints]: (request: protocol.InlayHintsRequest) => {
return this.requiredResponse(this.provideInlayHints(request.arguments));
},
[protocol.CommandTypes.MapCode]: (request: protocol.MapCodeRequest) => {
return this.requiredResponse(this.mapCode(request.arguments));
},
}));

public addProtocolHandler(command: string, handler: (request: protocol.Request) => HandlerResponse) {
Expand Down
3 changes: 3 additions & 0 deletions src/services/_namespaces/ts.MapCode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/* Generated file to emulate the ts.MapCode namespace. */

export * from "../mapCode";
2 changes: 2 additions & 0 deletions src/services/_namespaces/ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import * as GoToDefinition from "./ts.GoToDefinition";
export { GoToDefinition };
import * as InlayHints from "./ts.InlayHints";
export { InlayHints };
import * as MapCode from "./ts.MapCode";
export { MapCode };
import * as JsDoc from "./ts.JsDoc";
export { JsDoc };
import * as NavigateTo from "./ts.NavigateTo";
Expand Down
Loading

0 comments on commit d49995b

Please sign in to comment.