You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The idea of letting the lsp be involved in copy/paste was first raised in #767. I'm opening a new issue to track a specific proposal of how this could be implement based on the VS Code document paste API
Overview
VS Code has a long standing proposed API that allows languages hook into copy and paste. This is already being used successfully for a number of flows:
Copying and pasting to bring along imports in JS/TS and in Markdown
Pasting files to insert relative or absolute paths
Pasting files to insert markdown links (also supports copying resources into the workspace)
Pasting text that looks like a url to insert a markdown link
Pasting files add url(...) in css files
This type of functionality is likely useful across many different editors. The VS Code paste API also has a few characteristics that I think it make well suited to the LSP:
Declarative: The api is similar to the code action API in that languages do not directly edit the document using it. On paste, a language returns edit objects. These edit objects include human readable descriptions of the edit that can be shown in the UI along with a workspace edit that can be applied by the editor
Abstract: The api uses simple data transfer objects to attach data on copy and read data on paste. This data transfer object was inspired by the dom DataTransfer API, but is abstract enough and simple enough that I think almost any editor could implement it
Driven by editors: We use a provider pattern instead of events, so the editor is ultimately responsible for deciding when the call out on copy or paste. Again this is very similar to how code actions are implemented
On copy, we invoke prepareDocumentPaste. This returns a modified data transfer object. This is an asynchronous operation and should not block copying in the UI
On paste, we make sure any relevant prepareDocumentPaste calls have completed. We then create a new data transfer by merging the data transfer provided by the editor with the data transfers from prepareDocumentPaste. We then invoke providePasteEdits which returns a set of zero or more edits that can apply the paste
The editor gets back a list of possible edits from all providers. It can then decided what to do with them. For VS Code, we always apply the first edit and then let users switch to any other edits provided. However an editor could instead always show a selector on paste
Once a paste edit needs to be applied, we call resolvePasteEdit. This extra step allows languages to defer the potentially expensive work of computing the actual edit to apply
exportinterfaceDocumentPasteOptionsextendsWorkDoneProgressOptions{// List of mime types that may be added on copy// If not provided, will not be invoked on copycopyMimeTypes?: string[];// List of edit kinds that paste may return// If not provided, this provided will not be invoked on pasteprovidedPasteEditKinds?: string[]// List of mime types that this provided will be invoked for on pastepasteMimeTypes?: string[]// If set, the provided can support an additional resolve step to fill in edit detailssupportsResolve?: boolean}
Prepare paste
This is invoked on copy. You can use it to attach data that will be picked up on paste
Request:
method: documentPaste/preparePaste
params: PreparePasteParams
interfacePreparePasteParams{// The document where the copy took place intextDocument: TextDocumentIdentifier;// The ranges being copied in the documentranges: Range[];// The data transfer associated with the copydataTransfer: DataTransfer;}
interfaceDataTransfer{// A map of mime types to their corresponding dataitems: {[mimeType: string]: string};}
Response:
result: DataTransfer The additions to the data transfer. (TODO: should this allow deleting something from the incoming data transfer?)
Provide paste edits
This is invoked before the user pastes into a text editor. Returned edits can replace the standard pasting behavior.
Request:
method: documentPaste/providePasteEdits
params: ProvidePasteEditsParams
interfaceProvidePasteEditsParams{// The document being pasted intotextDocument: TextDocumentIdentifier;// The ranges in the document to paste intoranges: Range[];// The data transfer associated with the pastedataTransfer: DataTransfer;// Additional context for the pastecontext: DocumentPasteEditContext;}interfaceDocumentPasteEditContext{// Requested kind of paste edits to returnonly?: string[];// The reason why paste edits were requestedtriggerKind: DocumentPasteTriggerKind;}enumDocumentPasteTriggerKind{Automatic=0,PasteAs=1,}
Response:
result: DocumentPasteEdit[]
interfaceDocumentPasteEdit{// Human readable label that describes the edittitle: string;// Kind of the editkind: string;// The text or snippet to insert at the pasted locationsinsertText: string|SnippetString;// An optional additional edit to apply on pasteadditionalEdit?: WorkspaceEdit;// Controls ordering when multiple paste edits can potentially be appliedyieldTo?: string[];}
ResolvePaste edit
This is an optional method which fills in DocumentPasteEdit .additionalEdit before the edit is applied. This is useful
interfaceResolvePasteEditParams{// The paste edit to resolvepasteEdit: DocumentPasteEdit;}
Response:
result: DocumentPasteEdit The filled in paste edit. Only changes to additionalEdits should be respected for now
Other notes
Drag and drop
Copy and pasting is very similar to draging and dropping content into an editor. In VS Code we implement these features using separate APIs however the APIs are almost identical
I have somewhat mixed feelings about this. Having separate APIs lets languages customize the behavior for each flow. It also lets the two apis evolve independently. However having two apis means duplicated implementation code and a lot of duplicated API/protocol
Files
In VS Code, the DataTransfer object also supports files/binary data. This is an important use case but also adds complexity
The main concern is that we don't want to transfer around the file data util it is needed. A single video file may be 10-100s of MBs which we really don't want to send across the wire, especially if all the paste provider does is say that the file should be renamed or moved
That means that reading the file content needs to be an asynchronous operation. For reference, here's what VS Code's data transfer object looks like:
exportinterfaceDataTransferFile{/** * The name of the file. */readonlyname: string;/** * The full file path of the file. * * May be `undefined` on web. */readonlyuri?: Uri;/** * Get the full file contents of the file. */data(): Thenable<Uint8Array>;}/** * Encapsulates data transferred during drag and drop operations. */exportclassDataTransferItem{/** * Get a string representation of this item. * * If {@linkcode DataTransferItem.value} is an object, this returns the result of json stringifying {@linkcode DataTransferItem.value} value. */asString(): Thenable<string>;/** * Try getting the {@link DataTransferFile file} associated with this data transfer item. * * Note that the file object is only valid for the scope of the drag and drop operation. * * @returns The file for the data transfer or `undefined` if the item is either not a file or the * file data cannot be accessed. */asFile(): DataTransferFile|undefined;/** * Custom data stored on this item. * * You can use `value` to share data across operations. The original object can be retrieved so long as the extension that * created the `DataTransferItem` runs in the same extension host. */readonlyvalue: any;/** * @param value Custom data stored on this item. Can be retrieved using {@linkcode DataTransferItem.value}. */constructor(value: any);}/** * A map containing a mapping of the mime type of the corresponding transferred data. * * Drag and drop controllers that implement {@link TreeDragAndDropController.handleDrag `handleDrag`} can add additional mime types to the * data transfer. These additional mime types will only be included in the `handleDrop` when the the drag was initiated from * an element in the same drag and drop controller. */exportclassDataTransferimplementsIterable<[mimeType: string,item: DataTransferItem]>{/** * Retrieves the data transfer item for a given mime type. * * @param mimeType The mime type to get the data transfer item for, such as `text/plain` or `image/png`. * Mimes type look ups are case-insensitive. * * Special mime types: * - `text/uri-list` — A string with `toString()`ed Uris separated by `\r\n`. To specify a cursor position in the file, * set the Uri's fragment to `L3,5`, where 3 is the line number and 5 is the column number. */get(mimeType: string): DataTransferItem|undefined;/** * Sets a mime type to data transfer item mapping. * * @param mimeType The mime type to set the data for. Mimes types stored in lower case, with case-insensitive looks up. * @param value The data transfer item for the given mime type. */set(mimeType: string,value: DataTransferItem): void;}
You can we see also made asString() async, which also allows deferring transferring any normal clipboard data until it is needed. Another important note is that extensions in VS Code cannot create file data transfer objects
My current proposal doesn't have the data transfer be asynchronous but I think we should strong consider doing this. A very basic client could get away with always transferring the full clipboard contents, with proper lazy loading being implemented as a future optimization
The text was updated successfully, but these errors were encountered:
The idea of letting the lsp be involved in copy/paste was first raised in #767. I'm opening a new issue to track a specific proposal of how this could be implement based on the VS Code document paste API
Overview
VS Code has a long standing proposed API that allows languages hook into copy and paste. This is already being used successfully for a number of flows:
url(...)
in css filesThis type of functionality is likely useful across many different editors. The VS Code paste API also has a few characteristics that I think it make well suited to the LSP:
Declarative: The api is similar to the code action API in that languages do not directly edit the document using it. On paste, a language returns edit objects. These edit objects include human readable descriptions of the edit that can be shown in the UI along with a workspace edit that can be applied by the editor
Abstract: The api uses simple data transfer objects to attach data on copy and read data on paste. This data transfer object was inspired by the dom
DataTransfer
API, but is abstract enough and simple enough that I think almost any editor could implement itDriven by editors: We use a provider pattern instead of events, so the editor is ultimately responsible for deciding when the call out on copy or paste. Again this is very similar to how code actions are implemented
Relevant links
Copy paste proposal in VS Code
Simple extension using this API
More complete example showing this API in used to update code on paste for markdown (extension side and server side)
Video showing the whole update imports on paste flow in action for JS/TS in VS Code
Proposal
This proposal is based on the current VS Code API document paste proposal
Basic flow
On copy, we invoke
prepareDocumentPaste
. This returns a modified data transfer object. This is an asynchronous operation and should not block copying in the UIOn paste, we make sure any relevant
prepareDocumentPaste
calls have completed. We then create a new data transfer by merging the data transfer provided by the editor with the data transfers fromprepareDocumentPaste
. We then invokeprovidePasteEdits
which returns a set of zero or more edits that can apply the pasteThe editor gets back a list of possible edits from all providers. It can then decided what to do with them. For VS Code, we always apply the first edit and then let users switch to any other edits provided. However an editor could instead always show a selector on paste
Once a paste edit needs to be applied, we call
resolvePasteEdit
. This extra step allows languages to defer the potentially expensive work of computing the actual edit to applyCapability
documentPasteEditProvider
DocumentPasteOptions
Prepare paste
This is invoked on copy. You can use it to attach data that will be picked up on paste
Request:
documentPaste/preparePaste
PreparePasteParams
Response:
DataTransfer
The additions to the data transfer. (TODO: should this allow deleting something from the incoming data transfer?)Provide paste edits
This is invoked before the user pastes into a text editor. Returned edits can replace the standard pasting behavior.
Request:
documentPaste/providePasteEdits
ProvidePasteEditsParams
Response:
DocumentPasteEdit[]
ResolvePaste edit
This is an optional method which fills in
DocumentPasteEdit .additionalEdit
before the edit is applied. This is usefulmethod: 'documentPaste/resolvePasteEdit'
params: ResolvePasteEditParams
Response:
DocumentPasteEdit
The filled in paste edit. Only changes toadditionalEdits
should be respected for nowOther notes
Drag and drop
Copy and pasting is very similar to draging and dropping content into an editor. In VS Code we implement these features using separate APIs however the APIs are almost identical
I have somewhat mixed feelings about this. Having separate APIs lets languages customize the behavior for each flow. It also lets the two apis evolve independently. However having two apis means duplicated implementation code and a lot of duplicated API/protocol
Files
In VS Code, the
DataTransfer
object also supports files/binary data. This is an important use case but also adds complexityThe main concern is that we don't want to transfer around the file data util it is needed. A single video file may be 10-100s of MBs which we really don't want to send across the wire, especially if all the paste provider does is say that the file should be renamed or moved
That means that reading the file content needs to be an asynchronous operation. For reference, here's what VS Code's data transfer object looks like:
You can we see also made
asString()
async, which also allows deferring transferring any normal clipboard data until it is needed. Another important note is that extensions in VS Code cannot create file data transfer objectsMy current proposal doesn't have the data transfer be asynchronous but I think we should strong consider doing this. A very basic client could get away with always transferring the full clipboard contents, with proper lazy loading being implemented as a future optimization
The text was updated successfully, but these errors were encountered: