Skip to content

Commit

Permalink
CodeMapper support (#55406)
Browse files Browse the repository at this point in the history
  • Loading branch information
zkat committed May 22, 2024
1 parent 8537bb7 commit ddf43cd
Show file tree
Hide file tree
Showing 64 changed files with 1,931 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 @@ -789,6 +789,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
51 changes: 51 additions & 0 deletions src/harness/fourslashImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4510,6 +4510,57 @@ export class TestState {

this.verifyCurrentFileContent(newFileContent);
}

public baselineMapCode(
ranges: Range[][],
changes: string[] = [],
): void {
const fileName = this.activeFile.fileName;
const focusLocations = ranges.map(r =>
r.map(({ pos, end }) => {
return { start: pos, length: end - pos };
})
);
let before = this.getFileContent(fileName);
const edits = this.languageService.mapCode(
fileName,
// We trim the leading whitespace stuff just so our test cases can be more readable.
changes,
focusLocations,
this.formatCodeSettings,
{},
);
this.applyChanges(edits);
focusLocations.forEach(r => {
r.sort((a, b) => a.start - b.start);
});
focusLocations.sort((a, b) => a[0].start - b[0].start);
for (const subLoc of focusLocations) {
for (const { start, length } of subLoc) {
let offset = 0;
for (const sl2 of focusLocations) {
for (const { start: s2, length: l2 } of sl2) {
if (s2 < start) {
offset += 4;
if ((s2 + l2) > start) {
offset -= 2;
}
}
}
}
before = before.slice(0, start + offset) + "[|" + before.slice(start + offset, start + offset + length) + "|]" + before.slice(start + offset + length);
}
}
const after = this.getFileContent(fileName);
const baseline = `
// === ORIGINAL ===
${before}
// === INCOMING CHANGES ===
${changes.join("\n// ---\n")}
// === MAPPED ===
${after}`;
this.baseline("mapCode", baseline, ".mapCode.ts");
}
}

function updateTextRangeForTextChanges({ pos, end }: ts.TextRange, textChanges: readonly ts.TextChange[]): ts.TextRange {
Expand Down
4 changes: 4 additions & 0 deletions src/harness/fourslashInterfaceImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,10 @@ export class VerifyNegatable {
public uncommentSelection(newFileContent: string) {
this.state.uncommentSelection(newFileContent);
}

public baselineMapCode(ranges: FourSlash.Range[][], changes: string[] = []): void {
this.state.baselineMapCode(ranges, changes);
}
}

export class Verify extends VerifyNegatable {
Expand Down
33 changes: 33 additions & 0 deletions src/server/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ export const enum CommandTypes {
ProvideCallHierarchyOutgoingCalls = "provideCallHierarchyOutgoingCalls",
ProvideInlayHints = "provideInlayHints",
WatchChange = "watchChange",
MapCode = "mapCode",
}

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

export interface MapCodeRequestArgs extends FileRequestArgs {
/**
* The files and changes to try and apply/map.
*/
mapping: MapCodeRequestDocumentMapping;
}

export interface MapCodeRequestDocumentMapping {
/**
* 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?: TextSpan[][];
}

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

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

/**
* Synchronous request for semantic diagnostics of one file.
*/
Expand Down
23 changes: 23 additions & 0 deletions src/server/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1906,6 +1906,26 @@ export class Session<TMessage = string> implements EventSender {
});
}

private mapCode(args: protocol.MapCodeRequestArgs): protocol.FileCodeEdits[] {
const formatOptions = this.getHostFormatOptions();
const preferences = this.getHostPreferences();
const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args);
const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file)!;
const focusLocations = args.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,
};
});
});

const changes = languageService.mapCode(file, args.mapping.contents, focusLocations, formatOptions, preferences);
return this.mapTextChangesToCodeEdits(changes);
}

private setCompilerOptionsForInferredProjects(args: protocol.SetCompilerOptionsForInferredProjectsArgs): void {
this.projectService.setCompilerOptionsForInferredProjects(args.options, args.projectRootPath);
}
Expand Down Expand Up @@ -3610,6 +3630,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.js";
2 changes: 2 additions & 0 deletions src/services/_namespaces/ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export { GoToDefinition };
import * as InlayHints from "./ts.InlayHints.js";
export { InlayHints };
import * as JsDoc from "./ts.JsDoc.js";
import * as MapCode from "./ts.MapCode.js";
export { MapCode };
export { JsDoc };
import * as NavigateTo from "./ts.NavigateTo.js";
export { NavigateTo };
Expand Down
Loading

0 comments on commit ddf43cd

Please sign in to comment.