提交 b9241a73 编写于 作者: R rebornix

formatOnPaste as editor option instead of actions

上级 3f3ea06f
......@@ -53,6 +53,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;
......@@ -588,6 +589,25 @@ 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, {
startLineNumber: startPosition.lineNumber,
startColumn: startPosition.column,
endLineNumber: endPosition.lineNumber,
endColumn: 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),
snippetSuggestions: opts.snippetSuggestions,
......@@ -666,6 +667,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,
snippetSuggestions: 'bottom',
......
......@@ -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.
......@@ -879,6 +884,7 @@ export class EditorContribOptions {
readonly parameterHints: boolean;
readonly iconsInSuggestions: boolean;
readonly formatOnType: boolean;
readonly formatOnPaste: boolean;
readonly suggestOnTriggerCharacters: boolean;
readonly acceptSuggestionOnEnter: boolean;
readonly snippetSuggestions: 'top' | 'bottom' | 'inline' | 'none';
......@@ -903,6 +909,7 @@ export class EditorContribOptions {
parameterHints: boolean;
iconsInSuggestions: boolean;
formatOnType: boolean;
formatOnPaste: boolean;
suggestOnTriggerCharacters: boolean;
acceptSuggestionOnEnter: boolean;
snippetSuggestions: 'top' | 'bottom' | 'inline' | 'none';
......@@ -923,6 +930,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.snippetSuggestions = source.snippetSuggestions;
......@@ -949,6 +957,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.snippetSuggestions === other.snippetSuggestions
......@@ -3800,6 +3809,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.
*/
......@@ -4098,6 +4114,8 @@ export var EventType = {
WillType: 'willType',
DidType: 'didType',
DidPaste: 'didPaste',
EditorLayout: 'editorLayout',
DiffUpdated: 'diffUpdated'
......
......@@ -12,7 +12,7 @@ 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';
......@@ -151,6 +151,87 @@ 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;
}
var model = this.editor.getModel();
// no support
var [support] = OnTypeFormattingEditProviderRegistry.ordered(model);
if (!support || !support.autoFormatTriggerCharacters) {
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);
this.editor.focus();
});
}
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> {
......@@ -236,41 +317,6 @@ export class FormatSelectionAction extends AbstractFormatAction {
}
}
@editorAction
export class PasteAndFormatAction extends AbstractFormatAction {
constructor() {
super({
id: 'editor.action.pasteAndFormat',
label: nls.localize('pasteAndFormat.label', "Paste and Format"),
alias: 'Paste and Format',
precondition: ContextKeyExpr.and(EditorContextKeys.Writable, ModeContextKeys.hasDocumentSelectionFormattingProvider),
kbOpts: {
kbExpr: EditorContextKeys.TextFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_V)
},
menuOpts: {
group: '1_modification',
order: 1.32
}
});
}
protected _getFormattingEdits(editor: editorCommon.ICommonCodeEditor): TPromise<editorCommon.ISingleEditOperation[]> {
const originalSelectionStart = editor.getSelection().getStartPosition();
editor.focus();
document.execCommand('paste');
// paste doesn't persist selection
const currentCursorPosition = editor.getSelection().getStartPosition();
const pastedContentRange = new Range(currentCursorPosition.lineNumber, currentCursorPosition.column, originalSelectionStart.lineNumber, originalSelectionStart.column);
const model = editor.getModel();
const { tabSize, insertSpaces} = model.getOptions();
return getDocumentRangeFormattingEdits(model, pastedContentRange, { tabSize, insertSpaces });
}
}
// this is the old format action that does both (format document OR format selection)
// and we keep it here such that existing keybinding configurations etc will still work
CommandsRegistry.registerCommand('editor.action.format', accessor => {
......
......@@ -1303,6 +1303,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.
......@@ -1521,6 +1526,7 @@ declare module monaco.editor {
readonly parameterHints: boolean;
readonly iconsInSuggestions: boolean;
readonly formatOnType: boolean;
readonly formatOnPaste: boolean;
readonly suggestOnTriggerCharacters: boolean;
readonly acceptSuggestionOnEnter: boolean;
readonly snippetSuggestions: 'top' | 'bottom' | 'inline' | 'none';
......
......@@ -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.
先完成此消息的编辑!
想要评论请 注册