提交 abbd2a76 编写于 作者: A Alexandru Dima 提交者: GitHub

Merge pull request #18476 from rebornix/FormatOnPaste

fix #13945. support format on paste
......@@ -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;
......@@ -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);
......
......@@ -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),
......@@ -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,
......
......@@ -83,6 +83,7 @@ class ConfigClass implements IConfiguration {
iconsInSuggestions: true,
autoClosingBrackets: true,
formatOnType: false,
formatOnPaste: false,
suggestOnTriggerCharacters: true,
acceptSuggestionOnEnter: true,
acceptSuggestionOnCommitCharacter: true,
......
......@@ -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.
......@@ -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;
......@@ -909,6 +915,7 @@ export class EditorContribOptions {
parameterHints: boolean;
iconsInSuggestions: boolean;
formatOnType: boolean;
formatOnPaste: boolean;
suggestOnTriggerCharacters: boolean;
acceptSuggestionOnEnter: boolean;
acceptSuggestionOnCommitCharacter: boolean;
......@@ -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);
......@@ -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
......@@ -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.
*/
......@@ -4114,6 +4130,8 @@ export var EventType = {
WillType: 'willType',
DidType: 'didType',
DidPaste: 'didPaste',
EditorLayout: 'editorLayout',
DiffUpdated: 'diffUpdated'
......
......@@ -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;
......@@ -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> {
......
......@@ -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.
......@@ -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;
......
......@@ -169,6 +169,7 @@ const configurationValueWhitelist = [
'editor.detectIndentation',
'editor.formatOnType',
'editor.formatOnSave',
'editor.formatOnPaste',
'window.openFilesInNewWindow',
'javascript.validate.enable',
'editor.mouseWheelZoom',
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册