Skip to content

Commit 6805bb6

Browse files
authored
Hook up mapped code edits experiment for TS (#198478)
For microsoft/TypeScript#55406
1 parent 77dc879 commit 6805bb6

7 files changed

Lines changed: 109 additions & 0 deletions

File tree

extensions/typescript-language-features/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"license": "MIT",
99
"aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255",
1010
"enabledApiProposals": [
11+
"mappedEditsProvider",
1112
"workspaceTrust"
1213
],
1314
"capabilities": {
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as vscode from 'vscode';
7+
import { API } from '../tsServer/api';
8+
import { FileSpan } from '../tsServer/protocol/protocol';
9+
import { ITypeScriptServiceClient } from '../typescriptService';
10+
import { conditionalRegistration, requireMinVersion } from './util/dependentRegistration';
11+
import { Range, WorkspaceEdit } from '../typeConverters';
12+
import { DocumentSelector } from '../configuration/documentSelector';
13+
14+
class TsMappedEditsProvider implements vscode.MappedEditsProvider {
15+
constructor(
16+
private readonly client: ITypeScriptServiceClient
17+
) { }
18+
19+
async provideMappedEdits(document: vscode.TextDocument, codeBlocks: string[], context: vscode.MappedEditsContext, token: vscode.CancellationToken): Promise<vscode.WorkspaceEdit | undefined> {
20+
if (!this.isEnabled()) {
21+
return;
22+
}
23+
24+
const file = this.client.toOpenTsFilePath(document);
25+
if (!file) {
26+
return;
27+
}
28+
29+
const response = await this.client.execute('mapCode', {
30+
mappings: [{
31+
file,
32+
contents: codeBlocks,
33+
focusLocations: context.documents.map(documents => {
34+
return documents.flatMap((contextItem): FileSpan[] => {
35+
const file = this.client.toTsFilePath(contextItem.uri);
36+
if (!file) {
37+
return [];
38+
}
39+
return contextItem.ranges.map((range): FileSpan => ({ file, ...Range.toTextSpan(range) }));
40+
});
41+
}),
42+
}],
43+
}, token);
44+
if (response.type !== 'response' || !response.body) {
45+
return;
46+
}
47+
48+
return WorkspaceEdit.fromFileCodeEdits(this.client, response.body);
49+
}
50+
51+
private isEnabled(): boolean {
52+
return vscode.workspace.getConfiguration('typescript').get<boolean>('experimental.mappedCodeEdits.enabled', false);
53+
}
54+
}
55+
56+
export function register(
57+
selector: DocumentSelector,
58+
client: ITypeScriptServiceClient,
59+
) {
60+
return conditionalRegistration([
61+
requireMinVersion(client, API.v540)
62+
], () => {
63+
const provider = new TsMappedEditsProvider(client);
64+
return vscode.chat.registerMappedEditsProvider(selector.semantic, provider);
65+
});
66+
}

extensions/typescript-language-features/src/languageProvider.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export default class LanguageProvider extends Disposable {
7777
import('./languageFeatures/inlayHints').then(provider => this._register(provider.register(selector, this.description, this.client, this.fileConfigurationManager, this.telemetryReporter))),
7878
import('./languageFeatures/jsDocCompletions').then(provider => this._register(provider.register(selector, this.description, this.client, this.fileConfigurationManager))),
7979
import('./languageFeatures/linkedEditing').then(provider => this._register(provider.register(selector, this.client))),
80+
import('./languageFeatures/mappedCodeEditProvider').then(provider => this._register(provider.register(selector, this.client))),
8081
import('./languageFeatures/organizeImports').then(provider => this._register(provider.register(selector, this.client, this.commandManager, this.fileConfigurationManager, this.telemetryReporter))),
8182
import('./languageFeatures/quickFix').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.client.diagnosticsManager, this.telemetryReporter))),
8283
import('./languageFeatures/refactor').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.telemetryReporter))),

extensions/typescript-language-features/src/tsServer/api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export class API {
3535
public static readonly v500 = API.fromSimpleString('5.0.0');
3636
public static readonly v510 = API.fromSimpleString('5.1.0');
3737
public static readonly v520 = API.fromSimpleString('5.2.0');
38+
public static readonly v540 = API.fromSimpleString('5.4.0');
3839

3940
public static fromVersionString(versionString: string): API {
4041
let version = semver.valid(versionString);

extensions/typescript-language-features/src/tsServer/protocol/protocol.d.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,43 @@ declare module 'typescript/lib/tsserverlibrary' {
1919
interface Response {
2020
readonly _serverType?: ServerType;
2121
}
22+
23+
export interface MapCodeRequestArgs {
24+
/// The files and changes to try and apply/map.
25+
mappings: MapCodeRequestDocumentMapping[];
26+
27+
/// Edits to apply to the current workspace before performing the mapping.
28+
updates?: FileCodeEdits[]
29+
}
30+
31+
export interface MapCodeRequestDocumentMapping {
32+
/// The file for the request (absolute pathname required). Null/undefined
33+
/// if specific file is unknown.
34+
file?: string;
35+
36+
/// Optional name of project that contains file
37+
projectFileName?: string;
38+
39+
/// The specific code to map/insert/replace in the file.
40+
contents: string[];
41+
42+
/// Areas of "focus" to inform the code mapper with. For example, cursor
43+
/// location, current selection, viewport, etc. Nested arrays denote
44+
/// priority: toplevel arrays are more important than inner arrays, and
45+
/// inner array priorities are based on items within that array. Items
46+
/// earlier in the arrays have higher priority.
47+
focusLocations?: FileSpan[][];
48+
}
49+
50+
export interface MapCodeRequest extends Request {
51+
command: 'mapCode',
52+
arguments: MapCodeRequestArgs;
53+
}
54+
55+
export interface MapCodeResponse extends Response {
56+
body: FileCodeEdits[]
57+
}
2258
}
2359
}
60+
61+

extensions/typescript-language-features/src/typescriptService.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ interface StandardTsServerRequests {
7676
'findSourceDefinition': [Proto.FileLocationRequestArgs, Proto.DefinitionResponse];
7777
'getMoveToRefactoringFileSuggestions': [Proto.GetMoveToRefactoringFileSuggestionsRequestArgs, Proto.GetMoveToRefactoringFileSuggestions];
7878
'linkedEditingRange': [Proto.FileLocationRequestArgs, Proto.LinkedEditingRangeResponse];
79+
'mapCode': [Proto.MapCodeRequestArgs, Proto.MapCodeResponse];
7980
}
8081

8182
interface NoResponseTsServerRequests {

extensions/typescript-language-features/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"include": [
1212
"src/**/*",
1313
"../../src/vscode-dts/vscode.d.ts",
14+
"../../src/vscode-dts/vscode.proposed.mappedEditsProvider.d.ts",
1415
"../../src/vscode-dts/vscode.proposed.workspaceTrust.d.ts"
1516
]
1617
}

0 commit comments

Comments
 (0)