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

Hook up prototype paste with imports for JS/TS #204665

Merged
merged 7 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion extensions/typescript-language-features/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"multiDocumentHighlightProvider",
"mappedEditsProvider",
"codeActionAI",
"codeActionRanges"
"codeActionRanges",
"documentPaste"
],
"capabilities": {
"virtualWorkspaces": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as vscode from 'vscode';
import { DocumentSelector } from '../configuration/documentSelector';
import * as typeConverters from '../typeConverters';
import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService';
import { conditionalRegistration, requireSomeCapability } from './util/dependentRegistration';

class CopyMetadata {
constructor(
readonly resource: vscode.Uri,
readonly ranges: readonly vscode.Range[],
) { }

toJSON() {
return JSON.stringify({
resource: this.resource.toJSON(),
ranges: this.ranges,
});
}

static fromJSON(str: string): CopyMetadata | undefined {
try {
const parsed = JSON.parse(str);
return new CopyMetadata(
vscode.Uri.from(parsed.resource),
parsed.ranges.map((r: any) => new vscode.Range(r[0].line, r[0].character, r[1].line, r[1].character)));
} catch {
// ignore
}
return undefined;
}
}

class DocumentPasteProvider implements vscode.DocumentPasteEditProvider {

static readonly metadataMimeType = 'application/vnd.code.jsts.metadata';

constructor(
private readonly _client: ITypeScriptServiceClient,
) { }

prepareDocumentPaste(document: vscode.TextDocument, ranges: readonly vscode.Range[], dataTransfer: vscode.DataTransfer, _token: vscode.CancellationToken) {
dataTransfer.set(DocumentPasteProvider.metadataMimeType,
new vscode.DataTransferItem(new CopyMetadata(document.uri, ranges).toJSON()));
}

async provideDocumentPasteEdits(document: vscode.TextDocument, ranges: readonly vscode.Range[], dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise<vscode.DocumentPasteEdit | undefined> {
const file = this._client.toOpenTsFilePath(document);
if (!file) {
return;
}

const text = await dataTransfer.get('text/plain')?.asString();
if (!text || token.isCancellationRequested) {
return;
}

// Get optional metadata
const metadata = await this.extractMetadata(dataTransfer, token);
if (token.isCancellationRequested) {
return;
}

const copyRange = metadata?.ranges.at(0);
const copyFile = metadata ? this._client.toTsFilePath(metadata.resource) : undefined;

const response = await this._client.execute('getPostPasteImportFixes', {
file,
pastes: ranges.map(range => ({ text, range: typeConverters.Range.toTextSpan(range) })),
copy: metadata && copyFile && copyRange
? { file: copyFile, ...typeConverters.Range.toTextSpan(copyRange) }
: undefined,
}, token);
if (response.type !== 'response' || !response.body || token.isCancellationRequested) {
return;
}

const edit = new vscode.DocumentPasteEdit('', vscode.l10n.t("Paste with imports"));
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This name should like come from TS

const additionalEdit = new vscode.WorkspaceEdit();
for (const edit of response.body.edits) {
additionalEdit.set(this._client.toResource(edit.fileName), edit.textChanges.map(typeConverters.TextEdit.fromCodeEdit));
}
edit.additionalEdit = additionalEdit;
return edit;
}

private async extractMetadata(dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise<CopyMetadata | undefined> {
const metadata = await dataTransfer.get(DocumentPasteProvider.metadataMimeType)?.asString();
if (token.isCancellationRequested) {
return undefined;
}

return metadata ? CopyMetadata.fromJSON(metadata) : undefined;
}
}

export function register(selector: DocumentSelector, client: ITypeScriptServiceClient) {
return conditionalRegistration([
requireSomeCapability(client, ClientCapability.Semantic),
], () => {
return vscode.languages.registerDocumentPasteEditProvider(selector.semantic, new DocumentPasteProvider(client), {
id: 'jsts.pasteWithImports',
copyMimeTypes: [DocumentPasteProvider.metadataMimeType],
pasteMimeTypes: ['text/plain'],
});
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export default class LanguageProvider extends Disposable {
import('./languageFeatures/codeLens/implementationsCodeLens').then(provider => this._register(provider.register(selector, this.description, this.client, cachedNavTreeResponse))),
import('./languageFeatures/codeLens/referencesCodeLens').then(provider => this._register(provider.register(selector, this.description, this.client, cachedNavTreeResponse))),
import('./languageFeatures/completions').then(provider => this._register(provider.register(selector, this.description, this.client, this.typingsStatus, this.fileConfigurationManager, this.commandManager, this.telemetryReporter, this.onCompletionAccepted))),
import('./languageFeatures/copyPaste').then(provider => this._register(provider.register(selector, this.client))),
import('./languageFeatures/definitions').then(provider => this._register(provider.register(selector, this.client))),
import('./languageFeatures/directiveCommentCompletions').then(provider => this._register(provider.register(selector, this.client))),
import('./languageFeatures/documentHighlight').then(provider => this._register(provider.register(selector, this.client))),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ declare module 'typescript/lib/tsserverlibrary' {
readonly _serverType?: ServerType;
}

//#region MapCode
export interface MapCodeRequestArgs {
/// The files and changes to try and apply/map.
mappings: MapCodeRequestDocumentMapping[];
Expand Down Expand Up @@ -55,6 +56,29 @@ declare module 'typescript/lib/tsserverlibrary' {
export interface MapCodeResponse extends Response {
body: FileCodeEdits[]
}
//#endregion

//#region Paste
export interface GetPostPasteImportFixesRequest extends Request {
command: 'getPostPasteImportFixes';
arguments: GetPostPasteImportFixesRequestArgs;
}
export type DocumentRange = FileRangeRequestArgs;
export type CopyRange = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the protocol. I believe we can remove the CopyRange and DocumentRange here now since FileSpan extends TextSpan, so we can get the start and end locations from there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the pull request @mjbvz microsoft/TypeScript#57262

start: Location;
end: Location;
}
export type GetPostPasteImportFixesRequestArgs = FileRequestArgs & {
pastes: Array<{ text: string; range: TextSpan }>,
copy?: FileSpan;
}
export interface GetPostPasteImportFixesResponse extends Response {
body: PostPasteImportAction;
}
export interface PostPasteImportAction {
edits: FileCodeEdits[];
}
//#endregion
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ interface StandardTsServerRequests {
'getMoveToRefactoringFileSuggestions': [Proto.GetMoveToRefactoringFileSuggestionsRequestArgs, Proto.GetMoveToRefactoringFileSuggestions];
'linkedEditingRange': [Proto.FileLocationRequestArgs, Proto.LinkedEditingRangeResponse];
'mapCode': [Proto.MapCodeRequestArgs, Proto.MapCodeResponse];
'getPostPasteImportFixes': [Proto.GetPostPasteImportFixesRequestArgs, Proto.GetPostPasteImportFixesResponse];
}

interface NoResponseTsServerRequests {
Expand Down
1 change: 1 addition & 0 deletions extensions/typescript-language-features/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@
"../../src/vscode-dts/vscode.proposed.mappedEditsProvider.d.ts",
"../../src/vscode-dts/vscode.proposed.multiDocumentHighlightProvider.d.ts",
"../../src/vscode-dts/vscode.proposed.workspaceTrust.d.ts",
"../../src/vscode-dts/vscode.proposed.documentPaste.d.ts",
]
}
Loading