提交 0b90eb2c 编写于 作者: A Alex Dima

Fixes #259: Add shortcuts & commands for find modifiers

上级 a0df9a5a
......@@ -20,8 +20,17 @@ export interface IOptions {
width?:number;
validation?:InputBox.IInputValidator;
label:string;
appendCaseSensitiveLabel?: string;
appendWholeWordsLabel?: string;
appendRegexLabel?: string;
}
const NLS_REGEX_CHECKBOX_LABEL = nls.localize('regexDescription', "Use Regular Expression");
const NLS_WHOLE_WORD_CHECKBOX_LABEL = nls.localize('wordsDescription', "Match Whole Word");
const NLS_CASE_SENSITIVE_CHECKBOX_LABEL = nls.localize('caseDescription', "Match Case");
const NLS_DEFAULT_LABEL = nls.localize('defaultLabel', "input");
export class FindInput {
static OPTION_CHANGE:string = 'optionChange';
......@@ -48,7 +57,7 @@ export class FindInput {
this.width = options.width || 100;
this.placeholder = options.placeholder || '';
this.validation = options.validation;
this.label = options.label || nls.localize('defaultLabel', "input");
this.label = options.label || NLS_DEFAULT_LABEL;
this.listenersToRemove = [];
this.regex = null;
......@@ -59,7 +68,7 @@ export class FindInput {
this.inputBox = null;
this.validationNode = null;
this.buildDomNode();
this.buildDomNode(options.appendCaseSensitiveLabel || '', options.appendWholeWordsLabel || '', options.appendRegexLabel || '');
if(Boolean(parent)) {
parent.appendChild(this.domNode);
......@@ -172,7 +181,7 @@ export class FindInput {
this.inputBox.width = w;
}
private buildDomNode(): void {
private buildDomNode(appendCaseSensitiveLabel:string, appendWholeWordsLabel: string, appendRegexLabel: string): void {
this.domNode = document.createElement('div');
this.domNode.style.width = this.width + 'px';
$(this.domNode).addClass('monaco-findInput');
......@@ -186,19 +195,19 @@ export class FindInput {
}
});
this.regex = new Checkbox.Checkbox('regex', nls.localize('regexDescription', "Use Regular Expression"), false, () => {
this.regex = new Checkbox.Checkbox('regex', NLS_REGEX_CHECKBOX_LABEL + appendRegexLabel, false, () => {
this.onOptionChange(null);
this.inputBox.focus();
this.setInputWidth();
this.validate();
});
this.wholeWords = new Checkbox.Checkbox('whole-word', nls.localize('wordsDescription', "Match Whole Word"), false, () => {
this.wholeWords = new Checkbox.Checkbox('whole-word', NLS_WHOLE_WORD_CHECKBOX_LABEL + appendWholeWordsLabel, false, () => {
this.onOptionChange(null);
this.inputBox.focus();
this.setInputWidth();
this.validate();
});
this.caseSensitive = new Checkbox.Checkbox('case-sensitive', nls.localize('caseDescription', "Match Case"), false, () => {
this.caseSensitive = new Checkbox.Checkbox('case-sensitive', NLS_CASE_SENSITIVE_CHECKBOX_LABEL + appendCaseSensitiveLabel, false, () => {
this.onOptionChange(null);
this.inputBox.focus();
this.setInputWidth();
......
......@@ -236,17 +236,20 @@ function triggerEditorActionGlobal(actionId: string, accessor: ServicesAccessor,
var defaultEditorActionKeybindingOptions:IEditorActionKeybindingOptions = { primary: null, context: ContextKey.EditorTextFocus };
function contextRule(needsTextFocus: boolean, needsKey: string): IKeybindingContextRule[]{
function contextRule(needsTextFocus: boolean, needsKey: string): IKeybindingContextRule[] {
let result: IKeybindingContextRule[] = [];
if (needsTextFocus) {
return [
{ key: EditorCommon.KEYBINDING_CONTEXT_EDITOR_TEXT_FOCUS },
{ key: needsKey }
];
result.push({ key: EditorCommon.KEYBINDING_CONTEXT_EDITOR_TEXT_FOCUS });
} else {
result.push({ key: EditorCommon.KEYBINDING_CONTEXT_EDITOR_FOCUS });
}
if (needsKey) {
result.push({ key: needsKey });
}
return [
{ key: EditorCommon.KEYBINDING_CONTEXT_EDITOR_FOCUS },
{ key: needsKey }
];
return result;
}
function createCommandHandler(commandId: string, handler: IEditorCommandHandler): ICommandHandler {
......
......@@ -17,7 +17,7 @@ import Lifecycle = require('vs/base/common/lifecycle');
import config = require('vs/editor/common/config/config');
import EditorCommon = require('vs/editor/common/editorCommon');
import {Selection} from 'vs/editor/common/core/selection';
import {IKeybindingService, IKeybindingContextKey} from 'vs/platform/keybinding/common/keybindingService';
import {IKeybindingService, IKeybindingContextKey, IKeybindings} from 'vs/platform/keybinding/common/keybindingService';
import {IContextViewService} from 'vs/platform/contextview/browser/contextView';
import {INullService} from 'vs/platform/instantiation/common/instantiation';
import {KeyMod, KeyCode} from 'vs/base/common/keyCodes';
......@@ -29,16 +29,19 @@ export class FindController implements EditorCommon.IEditorContribution, FindWid
static ID = 'editor.contrib.findController';
private static _STATE_CHANGED_EVENT = 'stateChanged';
private editor:EditorBrowser.ICodeEditor;
private _findWidgetVisible: IKeybindingContextKey<boolean>;
private model:FindModel.IFindModel;
private widget:FindWidget.IFindWidget;
private widget:FindWidget.FindWidget;
private widgetIsVisible:boolean;
private widgetListeners:Lifecycle.IDisposable[];
private editorListeners:EventEmitter.ListenerUnbind[];
private lastState:FindModel.IFindState;
private _eventEmitter: EventEmitter.EventEmitter;
static getFindController(editor:EditorCommon.ICommonCodeEditor): FindController {
return <FindController>editor.getContribution(FindController.ID);
......@@ -71,6 +74,8 @@ export class FindController implements EditorCommon.IEditorContribution, FindWid
});
this.editorListeners = [];
}));
this._eventEmitter = new EventEmitter.EventEmitter([FindController._STATE_CHANGED_EVENT]);
}
public getId(): string {
......@@ -84,6 +89,11 @@ export class FindController implements EditorCommon.IEditorContribution, FindWid
this.widget = null;
}
this.disposeBindingAndModel();
this._eventEmitter.dispose();
}
public onStateChanged(listener:()=>void): Lifecycle.IDisposable {
return this._eventEmitter.addListener2(FindController._STATE_CHANGED_EVENT, listener);
}
private disposeBindingAndModel(): void {
......@@ -103,6 +113,51 @@ export class FindController implements EditorCommon.IEditorContribution, FindWid
this.editor.focus();
}
private _ensureHasState(): void {
if (!this.lastState) {
this.lastState = {
isReplaceRevealed: false,
properties: {
isRegex: false,
matchCase: false,
wholeWord: false
},
replaceString: '',
searchString: ''
};
}
}
public toggleCaseSensitive(): void {
if (this.widget) {
this.widget.toggleCaseSensitive();
} else {
this._ensureHasState();
this.lastState.properties.matchCase = !this.lastState.properties.matchCase;
this._eventEmitter.emit(FindController._STATE_CHANGED_EVENT);
}
}
public toggleWholeWords(): void {
if (this.widget) {
this.widget.toggleWholeWords();
} else {
this._ensureHasState();
this.lastState.properties.wholeWord = !this.lastState.properties.wholeWord;
this._eventEmitter.emit(FindController._STATE_CHANGED_EVENT);
}
}
public toggleRegex(): void {
if (this.widget) {
this.widget.toggleRegex();
} else {
this._ensureHasState();
this.lastState.properties.isRegex = !this.lastState.properties.isRegex;
this._eventEmitter.emit(FindController._STATE_CHANGED_EVENT);
}
}
private onWidgetClosed(): void {
this.widgetIsVisible = false;
this.disposeBindingAndModel();
......@@ -114,10 +169,6 @@ export class FindController implements EditorCommon.IEditorContribution, FindWid
public setSearchString(searchString:string): void {
this.widget.setSearchString(searchString);
this.lastState = this.widget.getState();
if (this.model) {
this.model.recomputeMatches(this.lastState, false);
}
}
private onWidgetUserInput(e:FindWidget.IUserInputEvent): void {
......@@ -125,6 +176,7 @@ export class FindController implements EditorCommon.IEditorContribution, FindWid
if (this.model) {
this.model.recomputeMatches(this.lastState, e.jumpToNextMatch);
}
this._eventEmitter.emit(FindController._STATE_CHANGED_EVENT);
}
private _start(forceRevealReplace:boolean, seedSearchStringFromSelection:boolean, seedSearchScopeFromSelection:boolean, shouldFocus:boolean): void {
......@@ -287,7 +339,7 @@ export class StartFindReplaceAction extends BaseStartFindAction {
}
public getId(): string {
return FindModel.START_FIND_REPLACE_ID;
return FindModel.START_FIND_REPLACE_ACTION_ID;
}
_startController(controller:FindController): void {
......@@ -463,19 +515,20 @@ export class SelectionHighlighter implements EditorCommon.IEditorContribution {
private editor:EditorCommon.ICommonCodeEditor;
private model:EditorCommon.IModel;
private decorations:string[];
private toUnhook:EventEmitter.ListenerUnbind[];
private toDispose:Lifecycle.IDisposable[];
constructor(editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
this.editor = editor;
this.model = this.editor.getModel();
this.decorations = [];
this.toUnhook = [];
this.toDispose = [];
this.toUnhook.push(editor.addListener(EditorCommon.EventType.CursorPositionChanged, e => this.onPositionChanged(e)));
this.toUnhook.push(editor.addListener(EditorCommon.EventType.ModelChanged, (e) => {
this.toDispose.push(editor.addListener2(EditorCommon.EventType.CursorPositionChanged, _ => this._update()));
this.toDispose.push(editor.addListener2(EditorCommon.EventType.ModelChanged, (e) => {
this.removeDecorations();
this.model = this.editor.getModel();
}));
this.toDispose.push(FindController.getFindController(editor).onStateChanged(() => this._update()));
}
public getId(): string {
......@@ -488,7 +541,7 @@ export class SelectionHighlighter implements EditorCommon.IEditorContribution {
}
}
private onPositionChanged(e:EditorCommon.ICursorPositionChangedEvent): void {
private _update(): void {
if (!this.editor.getConfiguration().selectionHighlight) {
return;
}
......@@ -541,9 +594,7 @@ export class SelectionHighlighter implements EditorCommon.IEditorContribution {
public dispose(): void {
this.removeDecorations();
while(this.toUnhook.length > 0) {
this.toUnhook.pop()();
}
this.toDispose = Lifecycle.disposeAll(this.toDispose);
}
}
......@@ -556,22 +607,22 @@ CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(SelectHighl
var CONTEXT_FIND_WIDGET_VISIBLE = 'findWidgetVisible';
// register actions
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(StartFindAction, FindModel.START_FIND_ID, nls.localize('startFindAction',"Find"), {
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(StartFindAction, FindModel.START_FIND_ACTION_ID, nls.localize('startFindAction',"Find"), {
context: ContextKey.None,
primary: KeyMod.CtrlCmd | KeyCode.KEY_F,
secondary: [KeyMod.CtrlCmd | KeyCode.F3]
}));
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(NextMatchFindAction, FindModel.NEXT_MATCH_FIND_ID, nls.localize('findNextMatchAction', "Find Next"), {
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(NextMatchFindAction, FindModel.NEXT_MATCH_FIND_ACTION_ID, nls.localize('findNextMatchAction', "Find Next"), {
context: ContextKey.EditorFocus,
primary: KeyCode.F3,
mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] }
}));
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(PreviousMatchFindAction, FindModel.PREVIOUS_MATCH_FIND_ID, nls.localize('findPreviousMatchAction', "Find Previous"), {
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(PreviousMatchFindAction, FindModel.PREVIOUS_MATCH_FIND_ACTION_ID, nls.localize('findPreviousMatchAction', "Find Previous"), {
context: ContextKey.EditorFocus,
primary: KeyMod.Shift | KeyCode.F3,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3] }
}));
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(StartFindReplaceAction, FindModel.START_FIND_REPLACE_ID, nls.localize('startReplace', "Replace"), {
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(StartFindReplaceAction, FindModel.START_FIND_REPLACE_ACTION_ID, nls.localize('startReplace', "Replace"), {
context: ContextKey.None,
primary: KeyMod.CtrlCmd | KeyCode.KEY_H,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_F }
......@@ -586,6 +637,25 @@ CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(AddSelectio
}));
EditorBrowserRegistry.registerEditorContribution(FindController);
EditorBrowserRegistry.registerEditorContribution(SelectionHighlighter);
CommonEditorRegistry.registerEditorCommand('closeFindWidget', CommonEditorRegistry.commandWeight(5), { primary: KeyCode.Escape }, false, CONTEXT_FIND_WIDGET_VISIBLE, (ctx, editor, args) => {
FindController.getFindController(editor).closeFindWidget();
});
\ No newline at end of file
function registerFindCommand(id:string, callback:(controller:FindController)=>void, keybindings:IKeybindings, needsKey:string = null): void {
CommonEditorRegistry.registerEditorCommand(id, CommonEditorRegistry.commandWeight(5), keybindings, false, needsKey, (ctx, editor, args) => {
callback(FindController.getFindController(editor));
});
}
registerFindCommand(FindModel.CLOSE_FIND_WIDGET_COMMAND_ID, x => x.closeFindWidget(), {
primary: KeyCode.Escape
}, CONTEXT_FIND_WIDGET_VISIBLE);
registerFindCommand(FindModel.TOGGLE_CASE_SENSITIVE_COMMAND_ID, x => x.toggleCaseSensitive(), {
primary: KeyMod.Alt | KeyCode.KEY_C,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C }
});
registerFindCommand(FindModel.TOGGLE_WHOLE_WORD_COMMAND_ID, x => x.toggleWholeWords(), {
primary: KeyMod.Alt | KeyCode.KEY_W,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_W }
});
registerFindCommand(FindModel.TOGGLE_REGEX_COMMAND_ID, x => x.toggleRegex(), {
primary: KeyMod.Alt | KeyCode.KEY_R,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R }
});
......@@ -26,17 +26,6 @@ export interface IUserInputEvent {
jumpToNextMatch: boolean;
}
export interface IFindWidget {
dispose(): void;
setModel(newFindModel:FindModel.IFindModel): void;
setSearchString(searchString:string): void;
getState(): FindModel.IFindState;
addUserInputEventListener(callback:(e:IUserInputEvent)=>void): Lifecycle.IDisposable;
addClosedEventListener(callback:()=>void): Lifecycle.IDisposable;
}
export interface IFindController {
enableSelectionFind(): void;
disableSelectionFind(): void;
......@@ -56,7 +45,7 @@ const NLS_REPLACE_BTN_LABEL = nls.localize('label.replaceButton', "Replace");
const NLS_REPLACE_ALL_BTN_LABEL = nls.localize('label.replaceAllButton', "Replace All");
const NLS_TOGGLE_REPLACE_MODE_BTN_LABEL = nls.localize('label.toggleReplaceButton', "Toggle Replace mode");
export class FindWidget extends EventEmitter.EventEmitter implements EditorBrowser.IOverlayWidget, IFindWidget {
export class FindWidget extends EventEmitter.EventEmitter implements EditorBrowser.IOverlayWidget {
private static _USER_CLOSED_EVENT = 'close';
private static _USER_INPUT_EVENT = 'userInputEvent';
......@@ -161,6 +150,7 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
public setSearchString(searchString:string): void {
this._findInput.setValue(searchString);
this._emitUserInputEvent(false);
}
private _setState(state:FindModel.IFindState, selectionFindEnabled:boolean): void {
......@@ -263,10 +253,10 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
var handled = false;
if (e.equals(CommonKeybindings.ENTER)) {
this._codeEditor.getAction(FindModel.NEXT_MATCH_FIND_ID).run().done(null, Errors.onUnexpectedError);
this._codeEditor.getAction(FindModel.NEXT_MATCH_FIND_ACTION_ID).run().done(null, Errors.onUnexpectedError);
handled = true;
} else if (e.equals(CommonKeybindings.SHIFT_ENTER)) {
this._codeEditor.getAction(FindModel.PREVIOUS_MATCH_FIND_ID).run().done(null, Errors.onUnexpectedError);
this._codeEditor.getAction(FindModel.PREVIOUS_MATCH_FIND_ACTION_ID).run().done(null, Errors.onUnexpectedError);
handled = true;
} else if (e.equals(CommonKeybindings.TAB)) {
if (this._isReplaceVisible) {
......@@ -334,6 +324,9 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
width: FindWidget.FIND_INPUT_AREA_WIDTH,
label: NLS_FIND_INPUT_LABEL,
placeholder: NLS_FIND_INPUT_PLACEHOLDER,
appendCaseSensitiveLabel: this._keybindingLabelFor(FindModel.TOGGLE_CASE_SENSITIVE_COMMAND_ID),
appendWholeWordsLabel: this._keybindingLabelFor(FindModel.TOGGLE_WHOLE_WORD_COMMAND_ID),
appendRegexLabel: this._keybindingLabelFor(FindModel.TOGGLE_REGEX_COMMAND_ID),
validation: (value:string): InputBox.IMessage => {
if (value.length === 0) {
return null;
......@@ -358,19 +351,19 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
// Previous button
this._prevBtn = new SimpleButton(
NLS_PREVIOUS_MATCH_BTN_LABEL + this._keybindingLabelFor(FindModel.PREVIOUS_MATCH_FIND_ID),
NLS_PREVIOUS_MATCH_BTN_LABEL + this._keybindingLabelFor(FindModel.PREVIOUS_MATCH_FIND_ACTION_ID),
'previous'
).onTrigger(() => {
this._codeEditor.getAction(FindModel.PREVIOUS_MATCH_FIND_ID).run().done(null, Errors.onUnexpectedError);
this._codeEditor.getAction(FindModel.PREVIOUS_MATCH_FIND_ACTION_ID).run().done(null, Errors.onUnexpectedError);
});
this._toDispose.push(this._prevBtn);
// Next button
this._nextBtn = new SimpleButton(
NLS_NEXT_MATCH_BTN_LABEL + this._keybindingLabelFor(FindModel.NEXT_MATCH_FIND_ID),
NLS_NEXT_MATCH_BTN_LABEL + this._keybindingLabelFor(FindModel.NEXT_MATCH_FIND_ACTION_ID),
'next'
).onTrigger(() => {
this._codeEditor.getAction(FindModel.NEXT_MATCH_FIND_ID).run().done(null, Errors.onUnexpectedError);
this._codeEditor.getAction(FindModel.NEXT_MATCH_FIND_ACTION_ID).run().done(null, Errors.onUnexpectedError);
});
this._toDispose.push(this._nextBtn);
......@@ -399,7 +392,7 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
// Close button
this._closeBtn = new SimpleButton(
NLS_CLOSE_BTN_LABEL + ' (Escape)',
NLS_CLOSE_BTN_LABEL + this._keybindingLabelFor(FindModel.CLOSE_FIND_WIDGET_COMMAND_ID),
'close-fw'
).onTrigger(() => {
this._hide(true);
......@@ -581,6 +574,21 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
private _emitClosedEvent(): void {
this.emit(FindWidget._USER_CLOSED_EVENT);
}
public toggleCaseSensitive(): void {
this._findInput.setCaseSensitive(!this._findInput.getCaseSensitive());
this._emitUserInputEvent(false);
}
public toggleWholeWords(): void {
this._findInput.setWholeWords(!this._findInput.getWholeWords());
this._emitUserInputEvent(false);
}
public toggleRegex(): void {
this._findInput.setRegex(!this._findInput.getRegex());
this._emitUserInputEvent(false);
}
}
export class Checkbox implements Lifecycle.IDisposable {
......
......@@ -13,11 +13,14 @@ import {Range} from 'vs/editor/common/core/range';
import {Position} from 'vs/editor/common/core/position';
import {ReplaceCommand} from 'vs/editor/common/commands/replaceCommand';
export var START_FIND_ID = 'actions.find';
export var NEXT_MATCH_FIND_ID = 'editor.action.nextMatchFindAction';
export var PREVIOUS_MATCH_FIND_ID = 'editor.action.previousMatchFindAction';
export var START_FIND_REPLACE_ID = 'editor.action.startFindReplaceAction';
export const START_FIND_ACTION_ID = 'actions.find';
export const NEXT_MATCH_FIND_ACTION_ID = 'editor.action.nextMatchFindAction';
export const PREVIOUS_MATCH_FIND_ACTION_ID = 'editor.action.previousMatchFindAction';
export const START_FIND_REPLACE_ACTION_ID = 'editor.action.startFindReplaceAction';
export const CLOSE_FIND_WIDGET_COMMAND_ID = 'closeFindWidget';
export const TOGGLE_CASE_SENSITIVE_COMMAND_ID = 'toggleFindCaseSensitive';
export const TOGGLE_WHOLE_WORD_COMMAND_ID = 'toggleFindWholeWord';
export const TOGGLE_REGEX_COMMAND_ID = 'toggleFindRegex';
export interface IFindMatchesEvent {
position: number;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册