Skip to content

Commit

Permalink
Add setting to disable paste as functionality and paste widget (#18…
Browse files Browse the repository at this point in the history
…1375)

For #30066

This removes the `editor.experimental.pasteActions.enabled` setting in favor of `editor.pasteAs.enabled` (which also defaults to on)
  • Loading branch information
mjbvz authored May 3, 2023
1 parent d72fdc2 commit 6384b9b
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 156 deletions.
4 changes: 2 additions & 2 deletions extensions/markdown-language-features/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@
"configuration.markdown.suggest.paths.includeWorkspaceHeaderCompletions.never": "Disable workspace header suggestions.",
"configuration.markdown.suggest.paths.includeWorkspaceHeaderCompletions.onDoubleHash": "Enable workspace header suggestions after typing `##` in a path, for example `[link text](##`.",
"configuration.markdown.suggest.paths.includeWorkspaceHeaderCompletions.onSingleOrDoubleHash": "Enable workspace header suggestions after typing either `##` or `#` in a path, for example `[link text](#` or `[link text](##`.",
"configuration.markdown.editor.drop.enabled": "Enable dropping files into a Markdown editor while holding Shift. Requires enabling `#editor.dropIntoEditor.enabled#`.",
"configuration.markdown.editor.pasteLinks.enabled": "Enable pasting files into a Markdown editor inserts Markdown links. Requires enabling `#editor.experimental.pasteActions.enabled#`.",
"configuration.markdown.editor.drop.enabled": "Enable dropping files into a Markdown editor while holding Shift. Requires enabling `#editor.pasteAs.enabled#`.",
"configuration.markdown.editor.pasteLinks.enabled": "Enable pasting files into a Markdown editor inserts Markdown links. Requires enabling `#editor.pasteAs.enabled#`.",
"configuration.markdown.validate.enabled.description": "Enable all error reporting in Markdown files.",
"configuration.markdown.validate.referenceLinks.enabled.description": "Validate reference links in Markdown files, for example `[link][ref]`. Requires enabling `#markdown.validate.enabled#`.",
"configuration.markdown.validate.fragmentLinks.enabled.description": "Validate fragment links to headers in the current Markdown file, for example `[link](#header)`. Requires enabling `#markdown.validate.enabled#`.",
Expand Down
74 changes: 74 additions & 0 deletions src/vs/editor/common/config/editorOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,11 @@ export interface IEditorOptions {
*/
dropIntoEditor?: IDropIntoEditorOptions;

/**
* Controls support for changing how content is pasted into the editor.
*/
pasteAs?: IPasteAsOptions;

/**
* Controls whether the editor receives tabs or defers them to the workbench for navigation.
*/
Expand Down Expand Up @@ -4794,6 +4799,73 @@ class EditorDropIntoEditor extends BaseEditorOption<EditorOption.dropIntoEditor,

//#endregion

//#region pasteAs

/**
* Configuration options for editor pasting as into behavior
*/
export interface IPasteAsOptions {
/**
* Enable paste as functionality in editors.
* Defaults to true.
*/
enabled?: boolean;

/**
* Controls if a widget is shown after a drop.
* Defaults to 'afterPaste'.
*/
showPasteSelector?: 'afterPaste' | 'never';
}

/**
* @internal
*/
export type EditorPasteAsOptions = Readonly<Required<IPasteAsOptions>>;

class EditorPasteAs extends BaseEditorOption<EditorOption.pasteAs, IPasteAsOptions, EditorPasteAsOptions> {

constructor() {
const defaults: EditorPasteAsOptions = { enabled: true, showPasteSelector: 'afterPaste' };
super(
EditorOption.pasteAs, 'pasteAs', defaults,
{
'editor.PasteAs.enabled': {
type: 'boolean',
default: defaults.enabled,
markdownDescription: nls.localize('pasteAs.enabled', "Controls whether you can paste content in different ways."),
},
'editor.PasteAs.showPasteSelector': {
type: 'string',
markdownDescription: nls.localize('pasteAs.showPasteSelector', "Controls if a widget is shown when pasting content in to the editor. This widget lets you control how the file is pasted."),
enum: [
'afterPaste',
'never'
],
enumDescriptions: [
nls.localize('pasteAs.showPasteSelector.afterPaste', "Show the paste selector widget after content is pasted into the editor."),
nls.localize('pasteAs.showPasteSelector.never', "Never show the paste selector widget. Instead the default pasting behavior is always used."),
],
default: 'afterPaste',
},
}
);
}

public validate(_input: any): EditorPasteAsOptions {
if (!_input || typeof _input !== 'object') {
return this.defaultValue;
}
const input = _input as IPasteAsOptions;
return {
enabled: boolean(input.enabled, this.defaultValue.enabled),
showPasteSelector: stringSet(input.showPasteSelector, this.defaultValue.showPasteSelector, ['afterPaste', 'never']),
};
}
}

//#endregion

const DEFAULT_WINDOWS_FONT_FAMILY = 'Consolas, \'Courier New\', monospace';
const DEFAULT_MAC_FONT_FAMILY = 'Menlo, Monaco, \'Courier New\', monospace';
const DEFAULT_LINUX_FONT_FAMILY = '\'Droid Sans Mono\', \'monospace\', monospace';
Expand Down Expand Up @@ -4906,6 +4978,7 @@ export const enum EditorOption {
overviewRulerBorder,
overviewRulerLanes,
padding,
pasteAs,
parameterHints,
peekWidgetDefaultFocus,
definitionLinkOpensInPeek,
Expand Down Expand Up @@ -5382,6 +5455,7 @@ export const EditorOptions = {
3, 0, 3
)),
padding: register(new EditorPadding()),
pasteAs: register(new EditorPasteAs()),
parameterHints: register(new EditorParameterHints()),
peekWidgetDefaultFocus: register(new EditorStringEnumOption(
EditorOption.peekWidgetDefaultFocus, 'peekWidgetDefaultFocus',
Expand Down
121 changes: 61 additions & 60 deletions src/vs/editor/common/standalone/standaloneEnums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,66 +255,67 @@ export enum EditorOption {
overviewRulerBorder = 79,
overviewRulerLanes = 80,
padding = 81,
parameterHints = 82,
peekWidgetDefaultFocus = 83,
definitionLinkOpensInPeek = 84,
quickSuggestions = 85,
quickSuggestionsDelay = 86,
readOnly = 87,
renameOnType = 88,
renderControlCharacters = 89,
renderFinalNewline = 90,
renderLineHighlight = 91,
renderLineHighlightOnlyWhenFocus = 92,
renderValidationDecorations = 93,
renderWhitespace = 94,
revealHorizontalRightPadding = 95,
roundedSelection = 96,
rulers = 97,
scrollbar = 98,
scrollBeyondLastColumn = 99,
scrollBeyondLastLine = 100,
scrollPredominantAxis = 101,
selectionClipboard = 102,
selectionHighlight = 103,
selectOnLineNumbers = 104,
showFoldingControls = 105,
showUnused = 106,
snippetSuggestions = 107,
smartSelect = 108,
smoothScrolling = 109,
stickyScroll = 110,
stickyTabStops = 111,
stopRenderingLineAfter = 112,
suggest = 113,
suggestFontSize = 114,
suggestLineHeight = 115,
suggestOnTriggerCharacters = 116,
suggestSelection = 117,
tabCompletion = 118,
tabIndex = 119,
unicodeHighlighting = 120,
unusualLineTerminators = 121,
useShadowDOM = 122,
useTabStops = 123,
wordBreak = 124,
wordSeparators = 125,
wordWrap = 126,
wordWrapBreakAfterCharacters = 127,
wordWrapBreakBeforeCharacters = 128,
wordWrapColumn = 129,
wordWrapOverride1 = 130,
wordWrapOverride2 = 131,
wrappingIndent = 132,
wrappingStrategy = 133,
showDeprecated = 134,
inlayHints = 135,
editorClassName = 136,
pixelRatio = 137,
tabFocusMode = 138,
layoutInfo = 139,
wrappingInfo = 140,
defaultColorDecorators = 141
pasteAs = 82,
parameterHints = 83,
peekWidgetDefaultFocus = 84,
definitionLinkOpensInPeek = 85,
quickSuggestions = 86,
quickSuggestionsDelay = 87,
readOnly = 88,
renameOnType = 89,
renderControlCharacters = 90,
renderFinalNewline = 91,
renderLineHighlight = 92,
renderLineHighlightOnlyWhenFocus = 93,
renderValidationDecorations = 94,
renderWhitespace = 95,
revealHorizontalRightPadding = 96,
roundedSelection = 97,
rulers = 98,
scrollbar = 99,
scrollBeyondLastColumn = 100,
scrollBeyondLastLine = 101,
scrollPredominantAxis = 102,
selectionClipboard = 103,
selectionHighlight = 104,
selectOnLineNumbers = 105,
showFoldingControls = 106,
showUnused = 107,
snippetSuggestions = 108,
smartSelect = 109,
smoothScrolling = 110,
stickyScroll = 111,
stickyTabStops = 112,
stopRenderingLineAfter = 113,
suggest = 114,
suggestFontSize = 115,
suggestLineHeight = 116,
suggestOnTriggerCharacters = 117,
suggestSelection = 118,
tabCompletion = 119,
tabIndex = 120,
unicodeHighlighting = 121,
unusualLineTerminators = 122,
useShadowDOM = 123,
useTabStops = 124,
wordBreak = 125,
wordSeparators = 126,
wordWrap = 127,
wordWrapBreakAfterCharacters = 128,
wordWrapBreakBeforeCharacters = 129,
wordWrapColumn = 130,
wordWrapOverride1 = 131,
wordWrapOverride2 = 132,
wrappingIndent = 133,
wrappingStrategy = 134,
showDeprecated = 135,
inlayHints = 136,
editorClassName = 137,
pixelRatio = 138,
tabFocusMode = 139,
layoutInfo = 140,
wrappingInfo = 141,
defaultColorDecorators = 142
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,13 @@
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorCommand, EditorContributionInstantiation, ServicesAccessor, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { editorConfigurationBaseNode } from 'vs/editor/common/config/editorConfigurationSchema';
import { registerEditorFeature } from 'vs/editor/common/editorFeatures';
import { CopyPasteController, changePasteTypeCommandId, pasteWidgetVisibleCtx } from 'vs/editor/contrib/dropOrPasteInto/browser/copyPasteController';
import { DefaultPasteProvidersFeature } from 'vs/editor/contrib/dropOrPasteInto/browser/defaultProviders';
import * as nls from 'vs/nls';
import { ConfigurationScope, Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { Registry } from 'vs/platform/registry/common/platform';

registerEditorContribution(CopyPasteController.ID, CopyPasteController, EditorContributionInstantiation.Eager); // eager because it listens to events on the container dom node of the editor

Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
...editorConfigurationBaseNode,
properties: {
'editor.experimental.pasteActions.enabled': {
type: 'boolean',
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
description: nls.localize('pasteActions', "Enable/disable running edits from extensions on paste."),
default: false,
},
}
});

registerEditorCommand(new class extends EditorCommand {
constructor() {
super({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { UriList, VSDataTransfer, createStringDataTransferItem } from 'vs/base/common/dataTransfer';
import { Disposable } from 'vs/base/common/lifecycle';
import { Mimes } from 'vs/base/common/mime';
import { Schemas } from 'vs/base/common/network';
import { generateUuid } from 'vs/base/common/uuid';
import { toVSDataTransfer } from 'vs/editor/browser/dnd';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
Expand All @@ -23,12 +22,11 @@ import { ITextModel } from 'vs/editor/common/model';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from 'vs/editor/contrib/editorState/browser/editorState';
import { InlineProgressManager } from 'vs/editor/contrib/inlineProgress/browser/inlineProgress';
import { PostEditWidgetManager } from './postEditWidget';
import { localize } from 'vs/nls';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { PostEditWidgetManager } from './postEditWidget';

export const changePasteTypeCommandId = 'editor.changePasteType';

Expand Down Expand Up @@ -65,7 +63,6 @@ export class CopyPasteController extends Disposable implements IEditorContributi
editor: ICodeEditor,
@IInstantiationService instantiationService: IInstantiationService,
@IClipboardService private readonly _clipboardService: IClipboardService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
@ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService,
) {
super();
Expand All @@ -90,13 +87,8 @@ export class CopyPasteController extends Disposable implements IEditorContributi
this._postPasteWidgetManager.clear();
}

private arePasteActionsEnabled(model: ITextModel): boolean {
if (this._configurationService.getValue('editor.experimental.pasteActions.enabled', { resource: model.uri })) {
return true;
}

// TODO: This check is only here to support enabling `ipynb.pasteImagesAsAttachments.enabled` by default
return model.uri.scheme === Schemas.vscodeNotebookCell;
private isPasteAsEnabled(): boolean {
return this._editor.getOption(EditorOption.pasteAs).enabled;
}

private handleCopy(e: ClipboardEvent) {
Expand All @@ -110,7 +102,7 @@ export class CopyPasteController extends Disposable implements IEditorContributi
return;
}

if (!this.arePasteActionsEnabled(model)) {
if (!this.isPasteAsEnabled()) {
return;
}

Expand Down Expand Up @@ -174,7 +166,7 @@ export class CopyPasteController extends Disposable implements IEditorContributi
}

const model = this._editor.getModel();
if (!this.arePasteActionsEnabled(model)) {
if (!this.isPasteAsEnabled()) {
return;
}

Expand Down Expand Up @@ -232,7 +224,8 @@ export class CopyPasteController extends Disposable implements IEditorContributi
}

if (providerEdits.length) {
return this._postPasteWidgetManager.applyEditAndShowIfNeeded(selections[0], { activeEditIndex: 0, allEdits: providerEdits }, tokenSource.token);
const canShowWidget = editor.getOption(EditorOption.pasteAs).showPasteSelector === 'afterPaste';
return this._postPasteWidgetManager.applyEditAndShowIfNeeded(selections[0], { activeEditIndex: 0, allEdits: providerEdits }, canShowWidget, tokenSource.token);
}

await this.applyDefaultPasteHandler(dataTransfer, metadata, tokenSource.token);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { VSDataTransfer } from 'vs/base/common/dataTransfer';
import { Disposable } from 'vs/base/common/lifecycle';
import { toExternalVSDataTransfer } from 'vs/editor/browser/dnd';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { IPosition } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
Expand Down Expand Up @@ -110,7 +111,9 @@ export class DropIntoEditorController extends Disposable implements IEditorContr
if (possibleDropEdits) {
const allEdits = coalesce(possibleDropEdits);
// Pass in the parent token here as it tracks cancelling the entire drop operation.
await this._postDropWidgetManager.applyEditAndShowIfNeeded(Range.fromPositions(position), { activeEditIndex: 0, allEdits }, token);

const canShowWidget = editor.getOption(EditorOption.dropIntoEditor).showDropSelector === 'afterDrop';
await this._postDropWidgetManager.applyEditAndShowIfNeeded(Range.fromPositions(position), { activeEditIndex: 0, allEdits }, canShowWidget, token);
}
} finally {
tokenSource.dispose();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ export class PostEditWidgetManager extends Disposable {
)(() => this.clear()));
}

public async applyEditAndShowIfNeeded(range: Range, edits: EditSet, token: CancellationToken) {
public async applyEditAndShowIfNeeded(range: Range, edits: EditSet, canShowWidget: boolean, token: CancellationToken) {
const model = this._editor.getModel();
if (!model) {
return;
Expand Down Expand Up @@ -192,15 +192,15 @@ export class PostEditWidgetManager extends Disposable {
model.deltaDecorations(editTrackingDecoration, []);
}

if (editResult.isApplied && edits.allEdits.length > 1) {
if (canShowWidget && editResult.isApplied && edits.allEdits.length > 1) {
this.show(editRange ?? range, edits, async (newEditIndex) => {
const model = this._editor.getModel();
if (!model) {
return;
}

await model.undo();
this.applyEditAndShowIfNeeded(range, { activeEditIndex: newEditIndex, allEdits: edits.allEdits }, token);
this.applyEditAndShowIfNeeded(range, { activeEditIndex: newEditIndex, allEdits: edits.allEdits }, canShowWidget, token);
});
}
}
Expand Down
Loading

0 comments on commit 6384b9b

Please sign in to comment.