Skip to content

Commit

Permalink
Merge pull request #18476 from rebornix/FormatOnPaste
Browse files Browse the repository at this point in the history
fix #13945. support format on paste
  • Loading branch information
alexdima committed Jan 19, 2017
2 parents 84df32c + 487c172 commit abbd2a7
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 1 deletion.
15 changes: 15 additions & 0 deletions src/vs/editor/common/commonCodeEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export abstract class CommonCodeEditor extends EventEmitter implements editorCom
public readonly onDidDispose: Event<void> = fromEventEmitter<void>(this, editorCommon.EventType.Disposed);
public readonly onWillType: Event<string> = fromEventEmitter<string>(this, editorCommon.EventType.WillType);
public readonly onDidType: Event<string> = fromEventEmitter<string>(this, editorCommon.EventType.DidType);
public readonly onDidPaste: Event<Range> = fromEventEmitter<Range>(this, editorCommon.EventType.DidPaste);

protected domElement: IContextKeyServiceTarget;

Expand Down Expand Up @@ -604,6 +605,20 @@ export abstract class CommonCodeEditor extends EventEmitter implements editorCom
return;
}

if (handlerId === editorCommon.Handler.Paste) {
if (!this.cursor || typeof payload.text !== 'string' || payload.text.length === 0) {
// nothing to do
return;
}
const startPosition = this.cursor.getSelection().getStartPosition();
this.cursor.trigger(source, handlerId, payload);
const endPosition = this.cursor.getSelection().getStartPosition();
if (source === 'keyboard') {
this.emit(editorCommon.EventType.DidPaste, new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column));
}
return;
}

let candidate = this.getAction(handlerId);
if (candidate !== null) {
TPromise.as(candidate.run()).done(null, onUnexpectedError);
Expand Down
6 changes: 6 additions & 0 deletions src/vs/editor/common/config/commonEditorConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ class InternalEditorOptionsHelper {
parameterHints: toBoolean(opts.parameterHints),
iconsInSuggestions: toBoolean(opts.iconsInSuggestions),
formatOnType: toBoolean(opts.formatOnType),
formatOnPaste: toBoolean(opts.formatOnPaste),
suggestOnTriggerCharacters: toBoolean(opts.suggestOnTriggerCharacters),
acceptSuggestionOnEnter: toBoolean(opts.acceptSuggestionOnEnter),
acceptSuggestionOnCommitCharacter: toBoolean(opts.acceptSuggestionOnCommitCharacter),
Expand Down Expand Up @@ -668,6 +669,11 @@ const editorConfiguration: IConfigurationNode = {
'default': DefaultConfig.editor.formatOnType,
'description': nls.localize('formatOnType', "Controls if the editor should automatically format the line after typing")
},
'editor.formatOnPaste': {
'type': 'boolean',
'default': DefaultConfig.editor.formatOnPaste,
'description': nls.localize('formatOnPaste', "Controls if the editor should automatically format the pasted content")
},
'editor.suggestOnTriggerCharacters': {
'type': 'boolean',
'default': DefaultConfig.editor.suggestOnTriggerCharacters,
Expand Down
1 change: 1 addition & 0 deletions src/vs/editor/common/config/defaultConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ class ConfigClass implements IConfiguration {
iconsInSuggestions: true,
autoClosingBrackets: true,
formatOnType: false,
formatOnPaste: false,
suggestOnTriggerCharacters: true,
acceptSuggestionOnEnter: true,
acceptSuggestionOnCommitCharacter: true,
Expand Down
18 changes: 18 additions & 0 deletions src/vs/editor/common/editorCommon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,11 @@ export interface IEditorOptions {
* Defaults to false.
*/
formatOnType?: boolean;
/**
* Enable format on paste.
* Defaults to false.
*/
formatOnPaste?: boolean;
/**
* Enable the suggestion box to pop-up on trigger characters.
* Defaults to true.
Expand Down Expand Up @@ -884,6 +889,7 @@ export class EditorContribOptions {
readonly parameterHints: boolean;
readonly iconsInSuggestions: boolean;
readonly formatOnType: boolean;
readonly formatOnPaste: boolean;
readonly suggestOnTriggerCharacters: boolean;
readonly acceptSuggestionOnEnter: boolean;
readonly acceptSuggestionOnCommitCharacter: boolean;
Expand All @@ -909,6 +915,7 @@ export class EditorContribOptions {
parameterHints: boolean;
iconsInSuggestions: boolean;
formatOnType: boolean;
formatOnPaste: boolean;
suggestOnTriggerCharacters: boolean;
acceptSuggestionOnEnter: boolean;
acceptSuggestionOnCommitCharacter: boolean;
Expand All @@ -930,6 +937,7 @@ export class EditorContribOptions {
this.parameterHints = Boolean(source.parameterHints);
this.iconsInSuggestions = Boolean(source.iconsInSuggestions);
this.formatOnType = Boolean(source.formatOnType);
this.formatOnPaste = Boolean(source.formatOnPaste);
this.suggestOnTriggerCharacters = Boolean(source.suggestOnTriggerCharacters);
this.acceptSuggestionOnEnter = Boolean(source.acceptSuggestionOnEnter);
this.acceptSuggestionOnCommitCharacter = Boolean(source.acceptSuggestionOnCommitCharacter);
Expand Down Expand Up @@ -957,6 +965,7 @@ export class EditorContribOptions {
&& this.parameterHints === other.parameterHints
&& this.iconsInSuggestions === other.iconsInSuggestions
&& this.formatOnType === other.formatOnType
&& this.formatOnPaste === other.formatOnPaste
&& this.suggestOnTriggerCharacters === other.suggestOnTriggerCharacters
&& this.acceptSuggestionOnEnter === other.acceptSuggestionOnEnter
&& this.acceptSuggestionOnCommitCharacter === other.acceptSuggestionOnCommitCharacter
Expand Down Expand Up @@ -3816,6 +3825,13 @@ export interface ICommonCodeEditor extends IEditor {
*/
onDidType(listener: (text: string) => void): IDisposable;

/**
* An event emitted when users paste text in the editor.
* @event
* @internal
*/
onDidPaste(listener: (range: Range) => void): IDisposable;

/**
* Returns true if this editor or one of its widgets has keyboard focus.
*/
Expand Down Expand Up @@ -4114,6 +4130,8 @@ export var EventType = {
WillType: 'willType',
DidType: 'didType',

DidPaste: 'didPaste',

EditorLayout: 'editorLayout',

DiffUpdated: 'diffUpdated'
Expand Down
83 changes: 82 additions & 1 deletion src/vs/editor/contrib/format/common/formatActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ import { TPromise } from 'vs/base/common/winjs.base';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { editorAction, ServicesAccessor, EditorAction, commonEditorContribution } from 'vs/editor/common/editorCommonExtensions';
import { OnTypeFormattingEditProviderRegistry } from 'vs/editor/common/modes';
import { OnTypeFormattingEditProviderRegistry, DocumentRangeFormattingEditProviderRegistry } from 'vs/editor/common/modes';
import { getOnTypeFormattingEdits, getDocumentFormattingEdits, getDocumentRangeFormattingEdits } from '../common/format';
import { EditOperationsCommand } from './formatCommand';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService';
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
import { CharacterSet } from 'vs/editor/common/core/characterClassifier';
import { Range } from 'vs/editor/common/core/range';

import ModeContextKeys = editorCommon.ModeContextKeys;
import EditorContextKeys = editorCommon.EditorContextKeys;
Expand Down Expand Up @@ -150,6 +151,86 @@ class FormatOnType implements editorCommon.IEditorContribution {
}
}

@commonEditorContribution
class FormatOnPaste implements editorCommon.IEditorContribution {

private static ID = 'editor.contrib.formatOnPaste';

private editor: editorCommon.ICommonCodeEditor;
private workerService: IEditorWorkerService;
private callOnDispose: IDisposable[];
private callOnModel: IDisposable[];

constructor(editor: editorCommon.ICommonCodeEditor, @IEditorWorkerService workerService: IEditorWorkerService) {
this.editor = editor;
this.workerService = workerService;
this.callOnDispose = [];
this.callOnModel = [];

this.callOnDispose.push(editor.onDidChangeConfiguration(() => this.update()));
this.callOnDispose.push(editor.onDidChangeModel(() => this.update()));
this.callOnDispose.push(editor.onDidChangeModelLanguage(() => this.update()));
this.callOnDispose.push(DocumentRangeFormattingEditProviderRegistry.onDidChange(this.update, this));
}

private update(): void {

// clean up
this.callOnModel = dispose(this.callOnModel);

// we are disabled
if (!this.editor.getConfiguration().contribInfo.formatOnPaste) {
return;
}

// no model
if (!this.editor.getModel()) {
return;
}

let model = this.editor.getModel();

// no support
let [support] = DocumentRangeFormattingEditProviderRegistry.ordered(model);
if (!support || !support.provideDocumentRangeFormattingEdits) {
return;
}

this.callOnModel.push(this.editor.onDidPaste((range: Range) => {
this.trigger(range);
}));
}

private trigger(range: Range): void {
if (this.editor.getSelections().length > 1) {
return;
}

const model = this.editor.getModel();
const { tabSize, insertSpaces } = model.getOptions();
const state = this.editor.captureState(editorCommon.CodeEditorStateFlag.Value, editorCommon.CodeEditorStateFlag.Position);

getDocumentRangeFormattingEdits(model, range, { tabSize, insertSpaces }).then(edits => {
return this.workerService.computeMoreMinimalEdits(model.uri, edits, []);
}).then(edits => {
if (!state.validate(this.editor) || isFalsyOrEmpty(edits)) {
return;
}
const command = new EditOperationsCommand(edits, this.editor.getSelection());
this.editor.executeCommand(this.getId(), command);
});
}

public getId(): string {
return FormatOnPaste.ID;
}

public dispose(): void {
this.callOnDispose = dispose(this.callOnDispose);
this.callOnModel = dispose(this.callOnModel);
}
}

export abstract class AbstractFormatAction extends EditorAction {

public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): TPromise<void> {
Expand Down
6 changes: 6 additions & 0 deletions src/vs/monaco.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1310,6 +1310,11 @@ declare module monaco.editor {
* Defaults to false.
*/
formatOnType?: boolean;
/**
* Enable format on paste.
* Defaults to false.
*/
formatOnPaste?: boolean;
/**
* Enable the suggestion box to pop-up on trigger characters.
* Defaults to true.
Expand Down Expand Up @@ -1533,6 +1538,7 @@ declare module monaco.editor {
readonly parameterHints: boolean;
readonly iconsInSuggestions: boolean;
readonly formatOnType: boolean;
readonly formatOnPaste: boolean;
readonly suggestOnTriggerCharacters: boolean;
readonly acceptSuggestionOnEnter: boolean;
readonly acceptSuggestionOnCommitCharacter: boolean;
Expand Down
1 change: 1 addition & 0 deletions src/vs/platform/telemetry/common/telemetryUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ const configurationValueWhitelist = [
'editor.detectIndentation',
'editor.formatOnType',
'editor.formatOnSave',
'editor.formatOnPaste',
'window.openFilesInNewWindow',
'javascript.validate.enable',
'editor.mouseWheelZoom',
Expand Down

0 comments on commit abbd2a7

Please sign in to comment.