Skip to content

Commit

Permalink
feat(language-service): port provideDocumentDropEdits (#97)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsoncodehk authored Nov 28, 2023
1 parent 7b2eff1 commit e40b986
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 3 deletions.
40 changes: 39 additions & 1 deletion packages/language-server/lib/register/registerEditorFeatures.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import type { CodeInformation, Mapping, Stack, VirtualFile } from '@volar/language-core';
import type * as ts from 'typescript/lib/tsserverlibrary';
import type * as vscode from 'vscode-languageserver';
import { GetMatchTsConfigRequest, GetVirtualFileRequest, GetVirtualFilesRequest, LoadedTSFilesMetaRequest, ReloadProjectNotification, WriteVirtualFilesNotification } from '../../protocol';
import {
GetMatchTsConfigRequest,
GetVirtualFileRequest,
GetVirtualFilesRequest,
LoadedTSFilesMetaRequest,
ReloadProjectNotification,
WriteVirtualFilesNotification,
DocumentDropRequest,
DocumentDrop_DataTransferItemAsStringRequest,
DocumentDrop_DataTransferItemFileDataRequest
} from '../../protocol';
import type { ServerProjectProvider, ServerRuntimeEnvironment } from '../types';
import type { DataTransferItem } from '@volar/language-service';

export function registerEditorFeatures(
connection: vscode.Connection,
Expand All @@ -13,6 +24,33 @@ export function registerEditorFeatures(
const scriptVersions = new Map<string, number>();
const scriptVersionSnapshots = new WeakSet<ts.IScriptSnapshot>();

connection.onRequest(DocumentDropRequest.type, async ({ textDocument, position, dataTransfer }, token) => {

const dataTransferMap = new Map<string, DataTransferItem>();

for (const item of dataTransfer) {
dataTransferMap.set(item.mimeType, {
value: item.value,
asString() {
return connection.sendRequest(DocumentDrop_DataTransferItemAsStringRequest.type, { mimeType: item.mimeType });
},
asFile() {
if (item.file) {
return {
name: item.file.name,
uri: item.file.uri,
data() {
return connection.sendRequest(DocumentDrop_DataTransferItemFileDataRequest.type, { mimeType: item.mimeType });
},
};
}
},
});
}

const languageService = (await projectProvider.getProject(textDocument.uri)).getLanguageService();
return languageService.doDocumentDrop(textDocument.uri, position, dataTransferMap, token);
});
connection.onRequest(GetMatchTsConfigRequest.type, async params => {
const languageService = (await projectProvider.getProject(params.uri)).getLanguageService();
const configFileName = languageService.context.project.typescript?.configFileName;
Expand Down
40 changes: 39 additions & 1 deletion packages/language-server/protocol.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { CodeInformation, Mapping, Stack, VirtualFile } from '@volar/language-core';
import type { FileStat, FileType } from '@volar/language-service';
import type { FileStat, FileType, DocumentDropEdit } from '@volar/language-service';
import * as vscode from 'vscode-languageserver-protocol';

/**
Expand Down Expand Up @@ -86,3 +86,41 @@ export namespace GetVirtualFileRequest {
export type ErrorType = never;
export const type = new vscode.RequestType<ParamsType, ResponseType, ErrorType>('volar/client/virtualFile');
}

/**
* Document Drop
*/

export namespace DocumentDropRequest {
export type ParamsType = vscode.TextDocumentPositionParams & {
dataTransfer: {
mimeType: string;
value: any;
file?: {
name: string;
uri?: string;
};
}[];
};
export type ResponseType = DocumentDropEdit | null | undefined;
export type ErrorType = never;
export const type = new vscode.RequestType<ParamsType, ResponseType, ErrorType>('volar/client/documentDrop');
}

export namespace DocumentDrop_DataTransferItemAsStringRequest {
export type ParamsType = {
mimeType: string;
};
export type ResponseType = string;
export type ErrorType = never;
export const type = new vscode.RequestType<ParamsType, ResponseType, ErrorType>('volar/client/documentDrop/asString');
}

export namespace DocumentDrop_DataTransferItemFileDataRequest {
export type ParamsType = {
mimeType: string;
};
export type ResponseType = Uint8Array;
export type ErrorType = never;
export const type = new vscode.RequestType<ParamsType, ResponseType, ErrorType>('volar/client/documentDrop/fileData');
}
38 changes: 38 additions & 0 deletions packages/language-service/lib/languageFeatures/documentDrop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type * as vscode from 'vscode-languageserver-protocol';
import type { ServiceContext, DataTransferItem } from '../types';
import { languageFeatureWorker } from '../utils/featureWorkers';
import { NoneCancellationToken } from '../utils/cancellation';
import { transformWorkspaceEdit } from '../utils/transform';

export function register(context: ServiceContext) {

return (uri: string, position: vscode.Position, dataTransfer: Map<string, DataTransferItem>, token = NoneCancellationToken) => {

return languageFeatureWorker(
context,
uri,
() => position,
function* (map) {
for (const mappedPosition of map.toGeneratedPositions(position)) {
yield mappedPosition;
}
},
(service, document, arg) => {
if (token.isCancellationRequested) {
return;
}
return service.provideDocumentDropEdits?.(document, arg, dataTransfer, token);
},
(edit) => {
if (edit.additionalEdit) {
edit.additionalEdit = transformWorkspaceEdit(
edit.additionalEdit,
context,
undefined,
);
}
return edit;
},
);
};
}
2 changes: 2 additions & 0 deletions packages/language-service/lib/languageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import * as renamePrepare from './languageFeatures/renamePrepare';
import * as signatureHelp from './languageFeatures/signatureHelp';
import * as diagnostics from './languageFeatures/validation';
import * as workspaceSymbol from './languageFeatures/workspaceSymbols';
import * as documentDrop from './languageFeatures/documentDrop';
import type { Service, ServiceContext, ServiceEnvironment, SharedModules } from './types';

import type * as vscode from 'vscode-languageserver-protocol';
Expand Down Expand Up @@ -84,6 +85,7 @@ export function createLanguageService(
doDocumentLinkResolve: documentLinkResolve.register(context),
findWorkspaceSymbols: workspaceSymbol.register(context),
doAutoInsert: autoInsert.register(context),
doDocumentDrop: documentDrop.register(context),
getInlayHints: inlayHints.register(context),
doInlayHintResolve: inlayHintResolve.register(context),
callHierarchy: callHierarchy.register(context),
Expand Down
19 changes: 19 additions & 0 deletions packages/language-service/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ export interface Service<P = any> {
provideAutoInsertionEdit?(document: TextDocument, position: vscode.Position, context: AutoInsertionContext, token: vscode.CancellationToken): NullableResult<string | vscode.TextEdit>; // volar specific
provideFileRenameEdits?(oldUri: string, newUri: string, token: vscode.CancellationToken): NullableResult<vscode.WorkspaceEdit>; // volar specific
provideFormattingIndentSensitiveLines?(document: TextDocument, token: vscode.CancellationToken): NullableResult<number[]>; // volar specific
provideDocumentDropEdits?(document: TextDocument, position: vscode.Position, dataTransfer: Map<string, DataTransferItem>, token: vscode.CancellationToken): NullableResult<DocumentDropEdit>; // volar specific
resolveCodeLens?(codeLens: vscode.CodeLens, token: vscode.CancellationToken): Result<vscode.CodeLens>;
resolveCodeAction?(codeAction: vscode.CodeAction, token: vscode.CancellationToken): Result<vscode.CodeAction>;
resolveCompletionItem?(item: vscode.CompletionItem, token: vscode.CancellationToken): Result<vscode.CompletionItem>,
Expand All @@ -134,6 +135,24 @@ export interface Service<P = any> {
} & ServiceProvide<P>;
}

export interface DocumentDropEdit {
insertText: string;
insertTextFormat: vscode.InsertTextFormat;
additionalEdit?: vscode.WorkspaceEdit;
}

export interface DataTransferItem {
value: any;
asString(): Thenable<string>;
asFile(): DataTransferFile | undefined;
}

export interface DataTransferFile {
name: string;
uri?: string;
data(): Thenable<Uint8Array>;
}

export interface AutoInsertionContext {
lastChange: {
range: vscode.Range;
Expand Down
2 changes: 1 addition & 1 deletion packages/language-service/lib/utils/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ export function transformWorkspaceSymbol(symbol: vscode.WorkspaceSymbol, getOthe
export function transformWorkspaceEdit(
edit: vscode.WorkspaceEdit,
{ documents, project }: ServiceContext,
mode: 'fileName' | 'rename' | 'codeAction' | 'format',
mode: 'fileName' | 'rename' | 'codeAction' | undefined,
versions: Record<string, number> = {},
) {

Expand Down
1 change: 1 addition & 0 deletions packages/vscode/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as vscode from 'vscode';

export { activate as activateAutoInsertion } from './lib/features/autoInsertion';
export { activate as activateDocumentDropEdit } from './lib/features/documentDropEdits';
export { activate as activateWriteVirtualFiles } from './lib/features/writeVirtualFiles';
export { activate as activateFindFileReferences } from './lib/features/fileReferences';
export { activate as activateReloadProjects } from './lib/features/reloadProject';
Expand Down
52 changes: 52 additions & 0 deletions packages/vscode/lib/features/documentDropEdits.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import * as vscode from 'vscode';
import type { BaseLanguageClient } from 'vscode-languageclient';
import { DocumentDropRequest, DocumentDrop_DataTransferItemAsStringRequest, DocumentDrop_DataTransferItemFileDataRequest } from '@volar/language-server/protocol';

export function activate(selector: vscode.DocumentSelector, client: BaseLanguageClient) {

let lastDataTransfer: vscode.DataTransfer;

return vscode.Disposable.from(
client.onRequest(DocumentDrop_DataTransferItemAsStringRequest.type, async ({ mimeType }) => {
const item = lastDataTransfer.get(mimeType);
return await item?.asString() ?? '';
}),
client.onRequest(DocumentDrop_DataTransferItemFileDataRequest.type, async ({ mimeType }) => {
const item = lastDataTransfer.get(mimeType);
return await item?.asFile()?.data() ?? new Uint8Array();
}),
vscode.languages.registerDocumentDropEditProvider(
selector,
{
async provideDocumentDropEdits(document, position, dataTransfer) {

lastDataTransfer = dataTransfer;

const result = await client.sendRequest(DocumentDropRequest.type, {
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
position: client.code2ProtocolConverter.asPosition(position),
dataTransfer: [...dataTransfer].map(([mimeType, item]) => {
const file = item.asFile();
return {
mimeType,
value: item.value,
file: file ? {
name: file.name,
uri: file.uri ? client.code2ProtocolConverter.asUri(file.uri) : undefined,
} : undefined,
};
})
});

if (result) {
const edit = new vscode.DocumentDropEdit(result.insertTextFormat === 2 /* InsertTextMode.Snippet */ ? new vscode.SnippetString(result.insertText) : result.insertText);
if (result.additionalEdit) {
edit.additionalEdit = await client.protocol2CodeConverter.asWorkspaceEdit(result.additionalEdit);
}
return edit;
}
},
}
),
);
}

0 comments on commit e40b986

Please sign in to comment.