-
Notifications
You must be signed in to change notification settings - Fork 29.3k
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
Add events for copy/paste to allow language extensions to bring using/import statements along #30066
Comments
It would actually work out better if we could also attach additional data into the clipboard during copy (to avoid there being a mismatch between the import data we stashed and the clipboard, which might have been replaced externally since) and get it back during paste; but I don't know enough about how clipboards work to know how feasible that is! |
Having copy/paste bring along imports is a good motivating use case for designing this api. Here is some of what makes it interesting:
Some goals:
Sketches(note that these are not API proposals, just quick ideas on how this could work) Source ActionsTry to reuse source actions for implement an "editor.codeActionsOnPaste": [
"source.organizeImports"
] Would run organize imports source action whenever the user pastes. I don't see this as being enough on its own, except for super simple use cases such as organize imports. We need some way for extensions to store state from a copy and use that to produce a set of text edits on paste. Source actions don't provide enough infrastructure on their own Extend clipboard apiLet extensions be notified of clipboard copy and paste events. Let extensions store metadata on the clipboard. vscode.env.clipboard.onCopy(async entry => {
const metadata = await getMetadataForSelection();
entry.addMetadata('myExtension', metadata);
});
vscode.env.clipboard.onPaste(async entry => {
const metadata = entry.getMetadata('myExtension';
if (metadata) {
// convert metadata into text edits and apply these
}
}); This leaves the implementation largely up to individual extensions. There are a few big open questions here too:
Paste Action ProviderDefine a contract that extensions can implement to hook into copy/paste. This basically just adds a contract on top of the clipboard api sketch above: class AddImportsOnPasteProvider implements vscode.PasteActionProvider {
async providePasteActions(document: vsocode.TextDocument, copiedRange: vscode.Range): Promise<vscode.PasteAction[]> {
// Get metadata about copied text
const metadata = await getMetadataForSelection(document, copiedRange);
// Return command that VS Code applies on paste
return [{
id: 'typescript.moveImports',
command: <vscode.Command>{
title: 'Move Imports',
command: 'typescript.doMoveImports',
args: [metadata]
}
}];
}
} Instead of using commands, we could have have an |
So, the extended proposed is somewhat analogue to format on save which happens automatically and the onWillSave-event for less uniform cases, right? A critical piece in this would be those clipboard events. E.g. when copying from a website and pasting into the editor, then I don't believe that we have a chance to know that copy has happened. Not even sure if we know that inside the editor, e.g when copy/pasting between two files... |
If I understand correctly, in the "Paste Action Provider" example it looks like the metadata is collected during the paste operation: // Get metadata about copied text
const metadata = await getMetadataForSelection(document, copiedRange); I think that would fall down if a user copies text, the modifies it/deletes it/hits save (which formats). I think the metadata would have to be collected during the copy to be reliable (so the provider would probably need to have hooks for both copy and paste?). Is this something that could also be supported for LSP? (if so, it may be worth considering how the API would translate to that). |
@jrieken I originally thought this issue would be as simple as adding an We can probably implement these concepts on desktop but I'm not sure about web. I think we should be able to use the @DanTup The extension would need to save off the metadata in such a way that, on paste, the extension can handle any changes that happened since the copy A provider based approach is a better fit for the LSP. The goal is to implement it in VS Code first, then add it to the LSP once it has been stabilized |
Yep, that's what I have in mind - but it needs the Copy event to do this too (the provider example above doesn't seem sufficient). My expectation is that when you Copy, the extension would collect a list of all imports required for that code, then when you paste, it would generate the correct code to import any that are not already imported in the new file (and account for relative paths). That shouldn't be affected by any changes happening in between. |
Another use case for copy/paste is auto-escape string on paste. For example, pasting some text to a Java string literal, auto escape the characters such as The current PasteActionProvider seems to only consider copy/paste between editors. if copying a piece of content from outside (for example, copy a file path from File Explorer), is there a way to make the extension participate in the paste actions? |
Didn't know about those events. Seems to be working for desktop and web. Also, us moving to a node-free-renderer means that it should really work with web. It seems tho that they don't work with |
Definitely some limitations with // @ts-check
/**
* Use this to save off a async resolved clipboard entry.
*
* In the example we resolve this eagerly but you could also resolve it lazily instead
*
* @type {{ handle: string, value?: string } | undefined}
*/
let clipboardItem;
document.addEventListener('copy', e => {
const handle = '' + Date.now();
// Save off a handle pointing to data that VS Code maintains.
e.clipboardData.setData('x-vscode/id', handle);
clipboardItem = { handle: handle }
// Simulate the extension resolving the clipboard data asynchronously
setTimeout(() => {
// Make sure we are still on the same copy
if (clipboardItem?.handle === handle) {
clipboardItem.value = 'my custom value'
}
}, 500);
// Call prevent default to prevent out new clipboard data from being overwritten (is this really required?)
e.preventDefault();
// And then fill in raw text again since we prevented default
e.clipboardData.setData('text/plain', document.getSelection()?.toString() ?? '');
});
document.addEventListener('paste', e => {
// Check to see if the copy for this paste came from VS Code
const id = e.clipboardData.getData('x-vscode/id');
// If it did, make sure our clipboard data still belongs to the copy that generated it.
if (id === clipboardItem?.handle) {
const value = clipboardItem.value;
// Handle the case where the clipboard has not been resolved yet
if (typeof value === 'undefined') {
// Reset
clipboardItem = undefined;
// Note that we could wait on a Promise or do something else here...
} else {
// Our clipboard item has resolved and is still revevant!
e.preventDefault();
// Modify the document based on it
/** @type {HTMLTextAreaElement | undefined} */
const element = e.target;
const selectionStart = element.selectionStart || 0;
const selectionEnd = element.selectionEnd || 0;
element.value = `${element.value.substring(0, selectionStart)}${value}${element.value.substring(selectionEnd, element.value.length)}`;
element.selectionStart = selectionStart + value.length;
element.selectionEnd = element.selectionStart;
}
}
}) This shows that we should be able to :
|
What concerns me is that this flow only works if you copy from within the editor. No treatment when copying from external sources like stackoverflow - which is likely in more need of post-paste-cleanup. |
Yes this was just an experiment to see if I could implement what this api will require. For the next step, I'm going to try implementing a VS Code API that is closer to this: interface CopyPasteActionProvider<T = unknown> {
/**
* Optional method invoked after the user copies some text in a file.
*
* @param document Document where the copy took place.
* @param selection Selection being copied in the `document`
* @param clipboard Information about the clipboard state after the copy.
*
* @return Optional metadata that is passed to `onWillPaste`.
*/
onDidCopy?(
document: vscode.TextDocument,
selection: vscode.Selection,
clipboard: { readonly text: string },
): Promise<{ readonly data: T } | undefined>;
/**
* Invoked before the user pastes into a document.
*
* @param document Document being pasted into
* @param selection Current selection in the document.
* @param clipboard Information about the clipboard state. This may contain the metadata from `onDidCopy`.
*
* @return Optional workspace edit that applies the paste (TODO: right now always requires implementer to also implement basic paste)
*/
onWillPaste(
document: vscode.TextDocument,
selection: vscode.Selection,
clipboard: {
readonly text: string;
readonly data?: T;
},
): Promise<WorkspaceEdit | undefined>;
}
interface CopyPasteActionProviderMetadata {
/**
* Identifies the type of paste action being returned, such as `moveImports`. (maybe this should just be a simple string)
*/
readonly kind: CodeActionKind;
}
function registerCopyPasteActionProvider(
selector: vscode.DocumentSelector,
provider: CopyPasteActionProvider,
metadata: CopyPasteActionProviderMetadata
): Disposable; |
For microsoft#30066 This command shows a quick pick that lets you select how to paste content
For #30066 This command shows a quick pick that lets you select how to paste content
For microsoft#179430, microsoft#30066 This lets us call just the provider we are interested in
I saw this appear in the typescript 5.3 iteration plan.. Since no-one has mentioned it, I use a extension that does this already: "Copy With Imports" - id: stringham.copy-with-imports Its a awesome time saver. I particularly like that copy pasting a exported symbol will add a import to that export. |
There was a mention of markdown paste link in the latest release of vscode, which implemented through this API. Are there any plans to stabilize it soon? |
This comment has been minimized.
This comment has been minimized.
Any ETA on finalizing this API? |
# Context The PR adds three major context sources useful for autocomplete and next-edit-suggestion. ### Recent Copy retriever. Developers often copy/cut and paste context and before pasting we show autocomplete suggestions to the user. The PR leverages the copied content on the clipboard by the user as a context source. I wasn't able to find any vscode api event exposed which triggers when user `copy` or `cut` text in the editor. This seems like an open issue: microsoft/vscode#30066 Another alternative I think of is to use a keybinding of `Ctrl+x` and `Ctrl+c`, but not fully sure about its implications, since this is one of the most common shortcuts. As a workaround, The way current PR accomplishes the same is: 1. Tracks the selection made by the user in the last `1 minutes` and keeps tracks of upto `100` most recent selections. 3. At the time of retrieval, checks if the current clipboard content matches the selection items in the list. ### Recent View Ports This context source captures and utilizes the recently viewed portions of code in the editor. It keeps track of the visible areas of code that the developer has scrolled through or focused on within a specified time frame. ### Diagnostics The Diagnostics context source leverages the diagnostic information provided by VS Code and language servers. It collects and utilizes information about errors, for a file as a context source. ## Test plan 1. Automated test - Added CI tests for each of the retrievers 2. Manual test - Override the setting `"cody.autocomplete.experimental.graphContext": "recent-copy"` in vscode settings. - Observe the context events using `Autocomplete Trace View`
…default Fixes microsoft#184871 For microsoft#30066 Adds new settings that let you configure the default way to paste/drop. Also enables js/ts paste with imports by default for 5.7+. However will not apply by default. Instead it will be shown as an option after pasting. You can then use the `editor.pasteAs.preferences` setting to make it apply automatically or use the `javascript.updateImportsOnPaste.enabled` settings to disable the feature entirely
…default (#233031) Fixes #184871 For #30066 Adds new settings that let you configure the default way to paste/drop. Also enables js/ts paste with imports by default for 5.7+. However will not apply by default. Instead it will be shown as an option after pasting. You can then use the `editor.pasteAs.preferences` setting to make it apply automatically or use the `javascript.updateImportsOnPaste.enabled` settings to disable the feature entirely
Apologies if this already exists, I can't seem to find anything about it (I expected it'd be listed in complex commands if anywhere).
The language service I use for Dart is getting a new feature that will allow me to send a command to get data about imports/using statements that apply to a subset of code. It's designed to be run when a user copies a chunk of code to the clipboard. When pasting, this data can be exchanged for a bunch of edits that would add the equivalent imports in the new file to avoid the user ending up having to fire a bunch of "import xxx" code actions after pasting.
In order to do this I need to know when the user has copied something into their clipboard (including the file and offset/range) and also again after they have pasted (just the file) so I can add some additional edits.
The text was updated successfully, but these errors were encountered: