mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-11 14:04:49 -05:00
Merge branch 'master' into joh/registerTextDocumentProvider
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
.monaco-count-badge {
|
||||
padding: 0.2em 0.4em;
|
||||
border-radius: 1em;
|
||||
font-size: 80%;
|
||||
font-size: 85%;
|
||||
font-weight: normal;
|
||||
text-align: center;
|
||||
background: #BEBEBE;
|
||||
|
||||
@@ -48,8 +48,7 @@ export class FindInput {
|
||||
private caseSensitive:Checkbox.Checkbox;
|
||||
public domNode: HTMLElement;
|
||||
public validationNode: Builder.Builder;
|
||||
private inputNode:HTMLInputElement;
|
||||
private inputBox:InputBox.InputBox;
|
||||
public inputBox:InputBox.InputBox;
|
||||
|
||||
constructor(parent:HTMLElement, contextViewProvider: ContextView.IContextViewProvider, options?:IOptions) {
|
||||
this.contextViewProvider = contextViewProvider;
|
||||
@@ -64,7 +63,6 @@ export class FindInput {
|
||||
this.wholeWords = null;
|
||||
this.caseSensitive = null;
|
||||
this.domNode = null;
|
||||
this.inputNode = null;
|
||||
this.inputBox = null;
|
||||
this.validationNode = null;
|
||||
|
||||
|
||||
@@ -202,6 +202,34 @@ export function createRegExp(searchString: string, isRegex: boolean, matchCase:
|
||||
return new RegExp(searchString, modifiers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a regular expression only if it is valid and it doesn't lead to endless loop.
|
||||
*/
|
||||
export function createSafeRegExp(searchString:string, isRegex:boolean, matchCase:boolean, wholeWord:boolean): RegExp {
|
||||
if (searchString === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Try to create a RegExp out of the params
|
||||
var regex:RegExp = null;
|
||||
try {
|
||||
regex = createRegExp(searchString, isRegex, matchCase, wholeWord);
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Guard against endless loop RegExps & wrap around try-catch as very long regexes produce an exception when executed the first time
|
||||
try {
|
||||
if (regExpLeadsToEndlessLoop(regex)) {
|
||||
return null;
|
||||
}
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return regex;
|
||||
}
|
||||
|
||||
export function regExpLeadsToEndlessLoop(regexp: RegExp): boolean {
|
||||
// Exit early if it's one of these special cases which are meant to match
|
||||
// against an empty string
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
.monaco-editor.vs .token.constant { color: #dd0000; }
|
||||
.monaco-editor.vs .token.string { color: #A31515; }
|
||||
.monaco-editor.vs .token.string.escape { color: #A31515; }
|
||||
.monaco-editor.vs .token.literal { color: #000000; }
|
||||
.monaco-editor.vs .token.literal.string { color: #A31515; }
|
||||
.monaco-editor.vs .token.literal.hex { color: #e07000; }
|
||||
.monaco-editor.vs .token.number { color: #09885A; }
|
||||
@@ -106,7 +105,6 @@
|
||||
.monaco-editor.vs-dark .token.constant { color: #dd0000; }
|
||||
.monaco-editor.vs-dark .token.string { color: #CE9178; }
|
||||
.monaco-editor.vs-dark .token.string.escape { color: #CE9178; }
|
||||
.monaco-editor.vs-dark .token.literal { color: #e00000; }
|
||||
.monaco-editor.vs-dark .token.literal.hex { color: #e07000; }
|
||||
.monaco-editor.vs-dark .token.number { color: #B5CEA8; }
|
||||
.monaco-editor.vs-dark .token.number.hex { color: #5BB498; }
|
||||
@@ -199,7 +197,6 @@
|
||||
.monaco-editor.hc-black .token.string { color: #CE9178; }
|
||||
.monaco-editor.hc-black .token.string.escape { color: #CE9178; }
|
||||
|
||||
.monaco-editor.hc-black .token.literal { color: #FFFFFF; }
|
||||
.monaco-editor.hc-black .token.literal.hex { color: #FFFFFF; }
|
||||
.monaco-editor.hc-black .token.number { color: #FFFFFF; }
|
||||
.monaco-editor.hc-black .token.number.hex { color: #FFFFFF; }
|
||||
@@ -350,7 +347,6 @@
|
||||
.monaco-editor.vs .token.markup.raw
|
||||
.monaco-editor.vs .token.markup.other*/
|
||||
|
||||
.monaco-editor.vs .token.meta { color: #000000; }
|
||||
.monaco-editor.vs .token.meta.selector { color: #800000; }
|
||||
.monaco-editor.vs .token.meta.tag { color: #800000; }
|
||||
.monaco-editor.vs .token.meta.preprocessor { color: #0000FF; }
|
||||
@@ -478,7 +474,6 @@
|
||||
.monaco-editor.vs-dark .token.markup.raw
|
||||
.monaco-editor.vs-dark .token.markup.other*/
|
||||
|
||||
.monaco-editor.vs-dark .token.meta { color: #D4D4D4; }
|
||||
.monaco-editor.vs-dark .token.meta.selector { color: #D7BA7D; }
|
||||
.monaco-editor.vs-dark .token.meta.tag { color: #808080; } /* gray for html/xml-tag brackets */
|
||||
.monaco-editor.vs-dark .token.meta.preprocessor { color: #569CD6; }
|
||||
@@ -606,7 +601,6 @@
|
||||
.monaco-editor.hc-black .token.markup.raw
|
||||
.monaco-editor.hc-black .token.markup.other*/
|
||||
|
||||
.monaco-editor.hc-black .token.meta { color: #D4D4D4; }
|
||||
.monaco-editor.hc-black .token.meta.selector { color: #D7BA7D; }
|
||||
.monaco-editor.hc-black .token.meta.tag { color: #808080; } /* gray for html/xml-tag brackets */
|
||||
.monaco-editor.hc-black .token.meta.preprocessor { color: #569CD6; }
|
||||
|
||||
@@ -1745,17 +1745,17 @@ export interface IModel extends IEditableTextModel, ITextModelWithMarkers, IToke
|
||||
* @param matchCase Force the matching to match lower/upper case exactly.
|
||||
* @param wholeWord Force the matching to match entire words only.
|
||||
* @param limitResultCount Limit the number of results
|
||||
* @return The ranges where the matches are. It is empty if not matches have been found.
|
||||
* @return The ranges where the matches are. It is empty if no matches have been found.
|
||||
*/
|
||||
findMatches(searchString:string, searchScope:IRange, isRegex:boolean, matchCase:boolean, wholeWord:boolean, limitResultCount?:number): IEditorRange[];
|
||||
/**
|
||||
* Search the model.
|
||||
* Search the model for the next match. Loops to the beginning of the model if needed.
|
||||
* @param searchString The string used to search. If it is a regular expression, set `isRegex` to true.
|
||||
* @param searchStart Start the searching at the specified position.
|
||||
* @param isRegex Used to indicate that `searchString` is a regular expression.
|
||||
* @param matchCase Force the matching to match lower/upper case exactly.
|
||||
* @param wholeWord Force the matching to match entire words only.
|
||||
* @return The ranges where the matches are. It is empty if not matches have been found.
|
||||
* @return The range where the next match is. It is null if no next match has been found.
|
||||
*/
|
||||
findNextMatch(searchString:string, searchStart:IPosition, isRegex:boolean, matchCase:boolean, wholeWord:boolean): IEditorRange;
|
||||
|
||||
|
||||
@@ -849,37 +849,12 @@ export class TextModel extends OrderGuaranteeEventEmitter implements EditorCommo
|
||||
throw new Error('Unknown EOL preference');
|
||||
}
|
||||
|
||||
private _toRegExp(searchString:string, isRegex:boolean, matchCase:boolean, wholeWord:boolean): RegExp {
|
||||
if (searchString === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Try to create a RegExp out of the params
|
||||
var regex:RegExp = null;
|
||||
try {
|
||||
regex = Strings.createRegExp(searchString, isRegex, matchCase, wholeWord);
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Guard against endless loop RegExps & wrap around try-catch as very long regexes produce an exception when executed the first time
|
||||
try {
|
||||
if (Strings.regExpLeadsToEndlessLoop(regex)) {
|
||||
return null;
|
||||
}
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return regex;
|
||||
}
|
||||
|
||||
public findMatches(searchString:string, rawSearchScope:any, isRegex:boolean, matchCase:boolean, wholeWord:boolean, limitResultCount:number = LIMIT_FIND_COUNT): EditorCommon.IEditorRange[] {
|
||||
if (this._isDisposed) {
|
||||
throw new Error('Model.findMatches: Model is disposed');
|
||||
}
|
||||
|
||||
var regex = this._toRegExp(searchString, isRegex, matchCase, wholeWord);
|
||||
var regex = Strings.createSafeRegExp(searchString, isRegex, matchCase, wholeWord);
|
||||
if (!regex) {
|
||||
return [];
|
||||
}
|
||||
@@ -899,7 +874,7 @@ export class TextModel extends OrderGuaranteeEventEmitter implements EditorCommo
|
||||
throw new Error('Model.findNextMatch: Model is disposed');
|
||||
}
|
||||
|
||||
var regex = this._toRegExp(searchString, isRegex, matchCase, wholeWord);
|
||||
var regex = Strings.createSafeRegExp(searchString, isRegex, matchCase, wholeWord);
|
||||
if (!regex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -4,18 +4,16 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {EditorBrowserRegistry} from 'vs/editor/browser/editorBrowserExtensions';
|
||||
import {CommonEditorRegistry, ContextKey, EditorActionDescriptor} from 'vs/editor/common/editorCommonExtensions';
|
||||
import {EditorAction, Behaviour} from 'vs/editor/common/editorAction';
|
||||
import FindWidget = require('./findWidget');
|
||||
import FindModel = require('vs/editor/contrib/find/common/findModel');
|
||||
import nls = require('vs/nls');
|
||||
import EventEmitter = require('vs/base/common/eventEmitter');
|
||||
import EditorBrowser = require('vs/editor/browser/editorBrowser');
|
||||
import Lifecycle = require('vs/base/common/lifecycle');
|
||||
import config = require('vs/editor/common/config/config');
|
||||
import EditorCommon = require('vs/editor/common/editorCommon');
|
||||
import {IFindController, FindWidget} from 'vs/editor/contrib/find/browser/findWidget';
|
||||
import {FindModelBoundToEditorModel, FIND_IDS} from 'vs/editor/contrib/find/common/findModel';
|
||||
import * as EditorBrowser from 'vs/editor/browser/editorBrowser';
|
||||
import {IDisposable, disposeAll} from 'vs/base/common/lifecycle';
|
||||
import * as EditorCommon from 'vs/editor/common/editorCommon';
|
||||
import {Selection} from 'vs/editor/common/core/selection';
|
||||
import {IKeybindingService, IKeybindingContextKey, IKeybindings} from 'vs/platform/keybinding/common/keybindingService';
|
||||
import {IContextViewService} from 'vs/platform/contextview/browser/contextView';
|
||||
@@ -23,255 +21,234 @@ import {INullService} from 'vs/platform/instantiation/common/instantiation';
|
||||
import {KeyMod, KeyCode} from 'vs/base/common/keyCodes';
|
||||
import {Range} from 'vs/editor/common/core/range';
|
||||
import {OccurrencesRegistry} from 'vs/editor/contrib/wordHighlighter/common/wordHighlighter';
|
||||
import {INewFindReplaceState, FindReplaceStateChangedEvent, FindReplaceState} from 'vs/editor/contrib/find/common/findState';
|
||||
|
||||
enum FindStartFocusAction {
|
||||
NoFocusChange,
|
||||
FocusFindInput,
|
||||
FocusReplaceInput
|
||||
}
|
||||
|
||||
interface IFindStartOptions {
|
||||
forceRevealReplace:boolean;
|
||||
seedSearchStringFromSelection:boolean;
|
||||
seedSearchScopeFromSelection:boolean;
|
||||
shouldFocus:FindStartFocusAction;
|
||||
shouldAnimate:boolean;
|
||||
}
|
||||
|
||||
const CONTEXT_FIND_WIDGET_VISIBLE = 'findWidgetVisible';
|
||||
|
||||
/**
|
||||
* The Find controller will survive an editor.setModel(..) call
|
||||
*/
|
||||
export class FindController implements EditorCommon.IEditorContribution, FindWidget.IFindController {
|
||||
class FindController implements EditorCommon.IEditorContribution, IFindController {
|
||||
|
||||
static ID = 'editor.contrib.findController';
|
||||
|
||||
private static _STATE_CHANGED_EVENT = 'stateChanged';
|
||||
|
||||
private editor:EditorBrowser.ICodeEditor;
|
||||
private _editor: EditorBrowser.ICodeEditor;
|
||||
private _toDispose: IDisposable[];
|
||||
private _findWidgetVisible: IKeybindingContextKey<boolean>;
|
||||
|
||||
private model:FindModel.FindModelBoundToEditorModel;
|
||||
private widget:FindWidget.FindWidget;
|
||||
private widgetIsVisible:boolean;
|
||||
private widgetListeners:Lifecycle.IDisposable[];
|
||||
|
||||
private editorListeners:EventEmitter.ListenerUnbind[];
|
||||
private lastState:FindModel.IFindState;
|
||||
private _eventEmitter: EventEmitter.EventEmitter;
|
||||
private _state: FindReplaceState;
|
||||
private _widget: FindWidget;
|
||||
private _model: FindModelBoundToEditorModel;
|
||||
|
||||
static getFindController(editor:EditorCommon.ICommonCodeEditor): FindController {
|
||||
return <FindController>editor.getContribution(FindController.ID);
|
||||
}
|
||||
|
||||
constructor(editor:EditorBrowser.ICodeEditor, @IContextViewService contextViewService: IContextViewService, @IKeybindingService keybindingService: IKeybindingService) {
|
||||
this._editor = editor;
|
||||
this._toDispose = [];
|
||||
this._findWidgetVisible = keybindingService.createKey(CONTEXT_FIND_WIDGET_VISIBLE, false);
|
||||
|
||||
this.editor = editor;
|
||||
this.model = null;
|
||||
this.widgetIsVisible = false;
|
||||
this.lastState = null;
|
||||
this._state = new FindReplaceState();
|
||||
this._toDispose.push(this._state);
|
||||
this._toDispose.push(this._state.addChangeListener((e) => this._onStateChanged(e)))
|
||||
|
||||
this.widget = new FindWidget.FindWidget(this.editor, this, contextViewService, keybindingService);
|
||||
this._widget = new FindWidget(this._editor, this, this._state, contextViewService, keybindingService);
|
||||
this._toDispose.push(this._widget);
|
||||
|
||||
this.widgetListeners = [];
|
||||
this.widgetListeners.push(this.widget.addUserInputEventListener((e) => this.onWidgetUserInput(e)));
|
||||
this.widgetListeners.push(this.widget.addClosedEventListener(() => this.onWidgetClosed()));
|
||||
this._model = null;
|
||||
|
||||
this.editorListeners = [];
|
||||
this.editorListeners.push(this.editor.addListener(EditorCommon.EventType.ModelChanged, () => {
|
||||
this.disposeBindingAndModel();
|
||||
if (this.editor.getModel() && this.lastState && this.widgetIsVisible) {
|
||||
this._start(false, false, false, false);
|
||||
this._toDispose.push(this._editor.addListener2(EditorCommon.EventType.ModelChanged, () => {
|
||||
let shouldRestartFind = (this._editor.getModel() && this._state.isRevealed);
|
||||
|
||||
this.disposeModel();
|
||||
|
||||
if (shouldRestartFind) {
|
||||
this._start({
|
||||
forceRevealReplace: false,
|
||||
seedSearchStringFromSelection: false,
|
||||
seedSearchScopeFromSelection: false,
|
||||
shouldFocus: FindStartFocusAction.NoFocusChange,
|
||||
shouldAnimate: false
|
||||
});
|
||||
}
|
||||
}));
|
||||
this.editorListeners.push(this.editor.addListener(EditorCommon.EventType.Disposed, () => {
|
||||
this.editorListeners.forEach((element:EventEmitter.ListenerUnbind) => {
|
||||
element();
|
||||
});
|
||||
this.editorListeners = [];
|
||||
}));
|
||||
}
|
||||
|
||||
this._eventEmitter = new EventEmitter.EventEmitter([FindController._STATE_CHANGED_EVENT]);
|
||||
public dispose(): void {
|
||||
this.disposeModel();
|
||||
this._toDispose = disposeAll(this._toDispose);
|
||||
}
|
||||
|
||||
private disposeModel(): void {
|
||||
if (this._model) {
|
||||
this._model.dispose();
|
||||
this._model = null;
|
||||
}
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
return FindController.ID;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.widgetListeners = Lifecycle.disposeAll(this.widgetListeners);
|
||||
this.widget.dispose();
|
||||
this.disposeBindingAndModel();
|
||||
this._eventEmitter.dispose();
|
||||
}
|
||||
|
||||
public onStateChanged(listener:()=>void): Lifecycle.IDisposable {
|
||||
return this._eventEmitter.addListener2(FindController._STATE_CHANGED_EVENT, listener);
|
||||
}
|
||||
|
||||
private disposeBindingAndModel(): void {
|
||||
this._findWidgetVisible.reset();
|
||||
this.widget.setModel(null);
|
||||
if (this.model) {
|
||||
this.model.dispose();
|
||||
this.model = null;
|
||||
private _onStateChanged(e:FindReplaceStateChangedEvent): void {
|
||||
if (e.isRevealed) {
|
||||
if (this._state.isRevealed) {
|
||||
this._findWidgetVisible.set(true);
|
||||
} else {
|
||||
this._findWidgetVisible.reset();
|
||||
this.disposeModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getState(): FindReplaceState {
|
||||
return this._state;
|
||||
}
|
||||
|
||||
public closeFindWidget(): void {
|
||||
this.widgetIsVisible = false;
|
||||
this.disposeBindingAndModel();
|
||||
this.editor.focus();
|
||||
this._state.change({ isRevealed: false }, false);
|
||||
this._editor.focus();
|
||||
}
|
||||
|
||||
public toggleCaseSensitive(): void {
|
||||
this.widget.toggleCaseSensitive();
|
||||
this._state.change({ matchCase: !this._state.matchCase }, false);
|
||||
}
|
||||
|
||||
public toggleWholeWords(): void {
|
||||
this.widget.toggleWholeWords();
|
||||
this._state.change({ wholeWord: !this._state.wholeWord }, false);
|
||||
}
|
||||
|
||||
public toggleRegex(): void {
|
||||
this.widget.toggleRegex();
|
||||
}
|
||||
|
||||
private onWidgetClosed(): void {
|
||||
this.widgetIsVisible = false;
|
||||
this.disposeBindingAndModel();
|
||||
}
|
||||
|
||||
public getFindState(): FindModel.IFindState {
|
||||
return this.lastState;
|
||||
this._state.change({ isRegex: !this._state.isRegex }, false);
|
||||
}
|
||||
|
||||
public setSearchString(searchString:string): void {
|
||||
this.widget.setSearchString(searchString);
|
||||
this._state.change({ searchString: searchString }, false);
|
||||
}
|
||||
|
||||
private onWidgetUserInput(e:FindWidget.IUserInputEvent): void {
|
||||
this.lastState = this.widget.getState();
|
||||
if (this.model) {
|
||||
this.model.recomputeMatches(this.lastState, e.jumpToNextMatch);
|
||||
}
|
||||
this._eventEmitter.emit(FindController._STATE_CHANGED_EVENT);
|
||||
}
|
||||
private _start(opts:IFindStartOptions): void {
|
||||
this.disposeModel();
|
||||
|
||||
private _start(forceRevealReplace:boolean, seedSearchStringFromSelection:boolean, seedSearchScopeFromSelection:boolean, shouldFocus:boolean): void {
|
||||
if (!this.model) {
|
||||
this.model = new FindModel.FindModelBoundToEditorModel(this.editor);
|
||||
this.widget.setModel(this.model);
|
||||
}
|
||||
|
||||
this._findWidgetVisible.set(true);
|
||||
|
||||
// Get a default state if none existed before
|
||||
this.lastState = this.lastState || this.widget.getState();
|
||||
|
||||
// Consider editor selection and overwrite the state with it
|
||||
var selection = this.editor.getSelection();
|
||||
|
||||
if (!selection) {
|
||||
// Someone started the find controller with an editor that doesn't have a model...
|
||||
if (!this._editor.getModel()) {
|
||||
// cannot do anything with an editor that doesn't have a model...
|
||||
return;
|
||||
}
|
||||
|
||||
if (seedSearchStringFromSelection) {
|
||||
let stateChanges: INewFindReplaceState = {
|
||||
isRevealed: true
|
||||
};
|
||||
|
||||
// Consider editor selection and overwrite the state with it
|
||||
let selection = this._editor.getSelection();
|
||||
|
||||
if (opts.seedSearchStringFromSelection) {
|
||||
if (selection.startLineNumber === selection.endLineNumber) {
|
||||
if (selection.isEmpty()) {
|
||||
let wordAtPosition = this.editor.getModel().getWordAtPosition(selection.getStartPosition());
|
||||
let wordAtPosition = this._editor.getModel().getWordAtPosition(selection.getStartPosition());
|
||||
if (wordAtPosition) {
|
||||
this.lastState.searchString = wordAtPosition.word;
|
||||
stateChanges.searchString = wordAtPosition.word;
|
||||
}
|
||||
} else {
|
||||
this.lastState.searchString = this.editor.getModel().getValueInRange(selection);
|
||||
stateChanges.searchString = this._editor.getModel().getValueInRange(selection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var searchScope:EditorCommon.IEditorRange = null;
|
||||
if (seedSearchScopeFromSelection && selection.startLineNumber < selection.endLineNumber) {
|
||||
stateChanges.searchScope = null;
|
||||
if (opts.seedSearchScopeFromSelection && selection.startLineNumber < selection.endLineNumber) {
|
||||
// Take search scope into account only if it is more than one line.
|
||||
searchScope = selection;
|
||||
stateChanges.searchScope = selection;
|
||||
}
|
||||
|
||||
// Overwrite isReplaceRevealed
|
||||
if (forceRevealReplace) {
|
||||
this.lastState.isReplaceRevealed = forceRevealReplace;
|
||||
if (opts.forceRevealReplace) {
|
||||
stateChanges.isReplaceRevealed = true;
|
||||
}
|
||||
|
||||
// Start searching
|
||||
this.model.start(this.lastState, searchScope, shouldFocus);
|
||||
this.widgetIsVisible = true;
|
||||
this._state.change(stateChanges, false);
|
||||
|
||||
if (shouldFocus) {
|
||||
if (forceRevealReplace) {
|
||||
this.widget.focusReplaceInput();
|
||||
} else {
|
||||
this.widget.focusFindInput();
|
||||
}
|
||||
if (!this._model) {
|
||||
this._model = new FindModelBoundToEditorModel(this._editor, this._state);
|
||||
}
|
||||
|
||||
if (opts.shouldFocus === FindStartFocusAction.FocusReplaceInput) {
|
||||
this._widget.focusReplaceInput();
|
||||
} else if (opts.shouldFocus === FindStartFocusAction.FocusFindInput) {
|
||||
this._widget.focusFindInput();
|
||||
}
|
||||
}
|
||||
|
||||
public startFromAction(withReplace:boolean): void {
|
||||
this._start(withReplace, true, true, true);
|
||||
this._start({
|
||||
forceRevealReplace: withReplace,
|
||||
seedSearchStringFromSelection: true,
|
||||
seedSearchScopeFromSelection: true,
|
||||
shouldFocus: withReplace ? FindStartFocusAction.FocusReplaceInput : FindStartFocusAction.FocusFindInput,
|
||||
shouldAnimate: true
|
||||
});
|
||||
}
|
||||
|
||||
public next(): boolean {
|
||||
if (this.model) {
|
||||
this.model.next();
|
||||
public moveToNextMatch(): boolean {
|
||||
if (this._model) {
|
||||
this._model.moveToNextMatch();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public prev(): boolean {
|
||||
if (this.model) {
|
||||
this.model.prev();
|
||||
public moveToPrevMatch(): boolean {
|
||||
if (this._model) {
|
||||
this._model.moveToPrevMatch();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public enableSelectionFind(): void {
|
||||
if (this.model) {
|
||||
this.model.setFindScope(this.editor.getSelection());
|
||||
}
|
||||
}
|
||||
|
||||
public disableSelectionFind(): void {
|
||||
if (this.model) {
|
||||
this.model.setFindScope(null);
|
||||
}
|
||||
}
|
||||
|
||||
public replace(): boolean {
|
||||
if (this.model) {
|
||||
this.model.replace();
|
||||
if (this._model) {
|
||||
this._model.replace();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public replaceAll(): boolean {
|
||||
if (this.model) {
|
||||
this.model.replaceAll();
|
||||
if (this._model) {
|
||||
this._model.replaceAll();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class BaseStartFindAction extends EditorAction {
|
||||
export class StartFindAction extends EditorAction {
|
||||
|
||||
constructor(descriptor: EditorCommon.IEditorActionDescriptorData, editor: EditorCommon.ICommonCodeEditor, condition: Behaviour) {
|
||||
super(descriptor, editor, condition || Behaviour.WidgetFocus);
|
||||
}
|
||||
|
||||
_startController(controller:FindController): void {
|
||||
controller.startFromAction(false);
|
||||
constructor(descriptor: EditorCommon.IEditorActionDescriptorData, editor: EditorCommon.ICommonCodeEditor, @INullService ns) {
|
||||
super(descriptor, editor, Behaviour.WidgetFocus);
|
||||
}
|
||||
|
||||
public run(): TPromise<boolean> {
|
||||
|
||||
var controller = FindController.getFindController(this.editor);
|
||||
this._startController(controller);
|
||||
let controller = FindController.getFindController(this.editor);
|
||||
controller.startFromAction(false);
|
||||
return TPromise.as(true);
|
||||
}
|
||||
}
|
||||
|
||||
export class StartFindAction extends BaseStartFindAction {
|
||||
constructor(descriptor: EditorCommon.IEditorActionDescriptorData, editor: EditorCommon.ICommonCodeEditor, @INullService ns) {
|
||||
super(descriptor, editor, Behaviour.WidgetFocus);
|
||||
}
|
||||
}
|
||||
|
||||
export class NextMatchFindAction extends EditorAction {
|
||||
|
||||
constructor(descriptor:EditorCommon.IEditorActionDescriptorData, editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
|
||||
@@ -279,10 +256,10 @@ export class NextMatchFindAction extends EditorAction {
|
||||
}
|
||||
|
||||
public run(): TPromise<boolean> {
|
||||
var controller = FindController.getFindController(this.editor);
|
||||
if (!controller.next()) {
|
||||
let controller = FindController.getFindController(this.editor);
|
||||
if (!controller.moveToNextMatch()) {
|
||||
controller.startFromAction(false);
|
||||
controller.next();
|
||||
controller.moveToNextMatch();
|
||||
}
|
||||
return TPromise.as(true);
|
||||
}
|
||||
@@ -295,27 +272,25 @@ export class PreviousMatchFindAction extends EditorAction {
|
||||
}
|
||||
|
||||
public run(): TPromise<boolean> {
|
||||
var controller = FindController.getFindController(this.editor);
|
||||
if (!controller.prev()) {
|
||||
let controller = FindController.getFindController(this.editor);
|
||||
if (!controller.moveToPrevMatch()) {
|
||||
controller.startFromAction(false);
|
||||
controller.prev();
|
||||
controller.moveToPrevMatch();
|
||||
}
|
||||
return TPromise.as(true);
|
||||
}
|
||||
}
|
||||
|
||||
export class StartFindReplaceAction extends BaseStartFindAction {
|
||||
export class StartFindReplaceAction extends EditorAction {
|
||||
|
||||
constructor(descriptor:EditorCommon.IEditorActionDescriptorData, editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
|
||||
super(descriptor, editor, Behaviour.WidgetFocus | Behaviour.Writeable);
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
return FindModel.START_FIND_REPLACE_ACTION_ID;
|
||||
}
|
||||
|
||||
_startController(controller:FindController): void {
|
||||
public run(): TPromise<boolean> {
|
||||
let controller = FindController.getFindController(this.editor);
|
||||
controller.startFromAction(true);
|
||||
return TPromise.as(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,31 +304,26 @@ export interface IMultiCursorFindResult {
|
||||
}
|
||||
|
||||
export function multiCursorFind(editor:EditorCommon.ICommonCodeEditor, changeFindSearchString:boolean): IMultiCursorFindResult {
|
||||
var controller = FindController.getFindController(editor);
|
||||
var state = controller.getFindState();
|
||||
var searchText: string,
|
||||
isRegex = false,
|
||||
wholeWord = false,
|
||||
matchCase = false,
|
||||
let controller = FindController.getFindController(editor);
|
||||
let state = controller.getState();
|
||||
let searchText: string,
|
||||
nextMatch: EditorCommon.IEditorSelection;
|
||||
|
||||
// In any case, if the find widget was ever opened, the options are taken from it
|
||||
if (state) {
|
||||
isRegex = state.properties.isRegex;
|
||||
wholeWord = state.properties.wholeWord;
|
||||
matchCase = state.properties.matchCase;
|
||||
}
|
||||
let isRegex = state.isRegex;
|
||||
let wholeWord = state.wholeWord;
|
||||
let matchCase = state.matchCase;
|
||||
|
||||
// Find widget owns what we search for if:
|
||||
// - focus is not in the editor (i.e. it is in the find widget)
|
||||
// - and the search widget is visible
|
||||
// - and the search string is non-empty
|
||||
if (!editor.isFocused() && state && state.searchString.length > 0) {
|
||||
if (!editor.isFocused() && state.isRevealed && state.searchString.length > 0) {
|
||||
// Find widget owns what is searched for
|
||||
searchText = state.searchString;
|
||||
} else {
|
||||
// Selection owns what is searched for
|
||||
var s = editor.getSelection();
|
||||
let s = editor.getSelection();
|
||||
|
||||
if (s.startLineNumber !== s.endLineNumber) {
|
||||
// Cannot search for multiline string... yet...
|
||||
@@ -362,7 +332,7 @@ export function multiCursorFind(editor:EditorCommon.ICommonCodeEditor, changeFin
|
||||
|
||||
if (s.isEmpty()) {
|
||||
// selection is empty => expand to current word
|
||||
var word = editor.getModel().getWordAtPosition(s.getStartPosition());
|
||||
let word = editor.getModel().getWordAtPosition(s.getStartPosition());
|
||||
if (!word) {
|
||||
return null;
|
||||
}
|
||||
@@ -391,7 +361,7 @@ class SelectNextFindMatchAction extends EditorAction {
|
||||
}
|
||||
|
||||
protected _getNextMatch(): EditorCommon.IEditorSelection {
|
||||
var r = multiCursorFind(this.editor, true);
|
||||
let r = multiCursorFind(this.editor, true);
|
||||
if (!r) {
|
||||
return null;
|
||||
}
|
||||
@@ -399,10 +369,10 @@ class SelectNextFindMatchAction extends EditorAction {
|
||||
return r.nextMatch;
|
||||
}
|
||||
|
||||
var allSelections = this.editor.getSelections();
|
||||
var lastAddedSelection = allSelections[allSelections.length - 1];
|
||||
let allSelections = this.editor.getSelections();
|
||||
let lastAddedSelection = allSelections[allSelections.length - 1];
|
||||
|
||||
var nextMatch = this.editor.getModel().findNextMatch(r.searchText, lastAddedSelection.getEndPosition(), r.isRegex, r.matchCase, r.wholeWord);
|
||||
let nextMatch = this.editor.getModel().findNextMatch(r.searchText, lastAddedSelection.getEndPosition(), r.isRegex, r.matchCase, r.wholeWord);
|
||||
|
||||
if (!nextMatch) {
|
||||
return null;
|
||||
@@ -420,13 +390,13 @@ class AddSelectionToNextFindMatchAction extends SelectNextFindMatchAction {
|
||||
}
|
||||
|
||||
public run(): TPromise<boolean> {
|
||||
var nextMatch = this._getNextMatch();
|
||||
let nextMatch = this._getNextMatch();
|
||||
|
||||
if (!nextMatch) {
|
||||
return TPromise.as(false);
|
||||
}
|
||||
|
||||
var allSelections = this.editor.getSelections();
|
||||
let allSelections = this.editor.getSelections();
|
||||
this.editor.setSelections(allSelections.concat(nextMatch));
|
||||
this.editor.revealRangeInCenterIfOutsideViewport(nextMatch);
|
||||
|
||||
@@ -442,14 +412,14 @@ class MoveSelectionToNextFindMatchAction extends SelectNextFindMatchAction {
|
||||
}
|
||||
|
||||
public run(): TPromise<boolean> {
|
||||
var nextMatch = this._getNextMatch();
|
||||
let nextMatch = this._getNextMatch();
|
||||
|
||||
if (!nextMatch) {
|
||||
return TPromise.as(false);
|
||||
}
|
||||
|
||||
var allSelections = this.editor.getSelections();
|
||||
var lastAddedSelection = allSelections[allSelections.length - 1];
|
||||
let allSelections = this.editor.getSelections();
|
||||
let lastAddedSelection = allSelections[allSelections.length - 1];
|
||||
this.editor.setSelections(allSelections.slice(0, allSelections.length - 1).concat(nextMatch));
|
||||
this.editor.revealRangeInCenterIfOutsideViewport(nextMatch);
|
||||
|
||||
@@ -458,7 +428,6 @@ class MoveSelectionToNextFindMatchAction extends SelectNextFindMatchAction {
|
||||
}
|
||||
|
||||
class SelectHighlightsAction extends EditorAction {
|
||||
|
||||
static ID = 'editor.action.selectHighlights';
|
||||
static COMPAT_ID = 'editor.action.changeAll';
|
||||
|
||||
@@ -475,12 +444,12 @@ class SelectHighlightsAction extends EditorAction {
|
||||
}
|
||||
|
||||
public run(): TPromise<boolean> {
|
||||
var r = multiCursorFind(this.editor, true);
|
||||
let r = multiCursorFind(this.editor, true);
|
||||
if (!r) {
|
||||
return TPromise.as(false);
|
||||
}
|
||||
|
||||
var matches = this.editor.getModel().findMatches(r.searchText, true, r.isRegex, r.matchCase, r.wholeWord);
|
||||
let matches = this.editor.getModel().findMatches(r.searchText, true, r.isRegex, r.matchCase, r.wholeWord);
|
||||
|
||||
if (matches.length > 0) {
|
||||
this.editor.setSelections(matches.map(m => Selection.createSelection(m.startLineNumber, m.startColumn, m.endLineNumber, m.endColumn)));
|
||||
@@ -490,12 +459,11 @@ class SelectHighlightsAction extends EditorAction {
|
||||
}
|
||||
|
||||
export class SelectionHighlighter implements EditorCommon.IEditorContribution {
|
||||
|
||||
static ID = 'editor.contrib.selectionHighlighter';
|
||||
|
||||
private editor:EditorCommon.ICommonCodeEditor;
|
||||
private decorations:string[];
|
||||
private toDispose:Lifecycle.IDisposable[];
|
||||
private editor: EditorCommon.ICommonCodeEditor;
|
||||
private decorations: string[];
|
||||
private toDispose: IDisposable[];
|
||||
|
||||
constructor(editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
|
||||
this.editor = editor;
|
||||
@@ -506,7 +474,7 @@ export class SelectionHighlighter implements EditorCommon.IEditorContribution {
|
||||
this.toDispose.push(editor.addListener2(EditorCommon.EventType.ModelChanged, (e) => {
|
||||
this.removeDecorations();
|
||||
}));
|
||||
this.toDispose.push(FindController.getFindController(editor).onStateChanged(() => this._update()));
|
||||
this.toDispose.push(FindController.getFindController(editor).getState().addChangeListener((e) => this._update()));
|
||||
}
|
||||
|
||||
public getId(): string {
|
||||
@@ -597,7 +565,7 @@ export class SelectionHighlighter implements EditorCommon.IEditorContribution {
|
||||
|
||||
public dispose(): void {
|
||||
this.removeDecorations();
|
||||
this.toDispose = Lifecycle.disposeAll(this.toDispose);
|
||||
this.toDispose = disposeAll(this.toDispose);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,25 +580,23 @@ CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(SelectHighl
|
||||
primary: KeyMod.CtrlCmd | KeyCode.F2
|
||||
}));
|
||||
|
||||
var CONTEXT_FIND_WIDGET_VISIBLE = 'findWidgetVisible';
|
||||
|
||||
// register actions
|
||||
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(StartFindAction, FindModel.START_FIND_ACTION_ID, nls.localize('startFindAction',"Find"), {
|
||||
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(StartFindAction, FIND_IDS.StartFindAction, 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_ACTION_ID, nls.localize('findNextMatchAction', "Find Next"), {
|
||||
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(NextMatchFindAction, FIND_IDS.NextMatchFindAction, 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_ACTION_ID, nls.localize('findPreviousMatchAction', "Find Previous"), {
|
||||
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(PreviousMatchFindAction, FIND_IDS.PreviousMatchFindAction, 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_ACTION_ID, nls.localize('startReplace', "Replace"), {
|
||||
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(StartFindReplaceAction, FIND_IDS.StartFindReplaceAction, nls.localize('startReplace', "Replace"), {
|
||||
context: ContextKey.None,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.KEY_H,
|
||||
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_F }
|
||||
@@ -652,18 +618,18 @@ function registerFindCommand(id:string, callback:(controller:FindController)=>vo
|
||||
});
|
||||
}
|
||||
|
||||
registerFindCommand(FindModel.CLOSE_FIND_WIDGET_COMMAND_ID, x => x.closeFindWidget(), {
|
||||
registerFindCommand(FIND_IDS.CloseFindWidgetCommand, x => x.closeFindWidget(), {
|
||||
primary: KeyCode.Escape
|
||||
}, CONTEXT_FIND_WIDGET_VISIBLE);
|
||||
registerFindCommand(FindModel.TOGGLE_CASE_SENSITIVE_COMMAND_ID, x => x.toggleCaseSensitive(), {
|
||||
registerFindCommand(FIND_IDS.ToggleCaseSensitiveCommand, 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(), {
|
||||
registerFindCommand(FIND_IDS.ToggleWholeWordCommand, 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(), {
|
||||
registerFindCommand(FIND_IDS.ToggleRegexCommand, x => x.toggleRegex(), {
|
||||
primary: KeyMod.Alt | KeyCode.KEY_R,
|
||||
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R }
|
||||
});
|
||||
|
||||
@@ -6,29 +6,23 @@
|
||||
'use strict';
|
||||
|
||||
import 'vs/css!./findWidget';
|
||||
import nls = require('vs/nls');
|
||||
import Errors = require('vs/base/common/errors');
|
||||
import EventEmitter = require('vs/base/common/eventEmitter');
|
||||
import DomUtils = require('vs/base/browser/dom');
|
||||
import ContextView = require('vs/base/browser/ui/contextview/contextview');
|
||||
import Keyboard = require('vs/base/browser/keyboardEvent');
|
||||
import InputBox = require('vs/base/browser/ui/inputbox/inputBox');
|
||||
import Findinput = require('vs/base/browser/ui/findinput/findInput');
|
||||
import EditorBrowser = require('vs/editor/browser/editorBrowser');
|
||||
import EditorCommon = require('vs/editor/common/editorCommon');
|
||||
import FindModel = require('vs/editor/contrib/find/common/findModel');
|
||||
import Lifecycle = require('vs/base/common/lifecycle');
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as Errors from 'vs/base/common/errors';
|
||||
import * as DomUtils from 'vs/base/browser/dom';
|
||||
import {IContextViewProvider} from 'vs/base/browser/ui/contextview/contextview';
|
||||
import {StandardKeyboardEvent} from 'vs/base/browser/keyboardEvent';
|
||||
import {InputBox, IMessage as InputBoxMessage} from 'vs/base/browser/ui/inputbox/inputBox';
|
||||
import {FindInput} from 'vs/base/browser/ui/findinput/findInput';
|
||||
import * as EditorBrowser from 'vs/editor/browser/editorBrowser';
|
||||
import * as EditorCommon from 'vs/editor/common/editorCommon';
|
||||
import {FIND_IDS} from 'vs/editor/contrib/find/common/findModel';
|
||||
import {disposeAll, IDisposable} from 'vs/base/common/lifecycle';
|
||||
import {CommonKeybindings} from 'vs/base/common/keyCodes';
|
||||
import {IKeybindingService} from 'vs/platform/keybinding/common/keybindingService';
|
||||
import {Keybinding} from 'vs/base/common/keyCodes';
|
||||
|
||||
export interface IUserInputEvent {
|
||||
jumpToNextMatch: boolean;
|
||||
}
|
||||
import {INewFindReplaceState, FindReplaceStateChangedEvent, FindReplaceState} from 'vs/editor/contrib/find/common/findState';
|
||||
|
||||
export interface IFindController {
|
||||
enableSelectionFind(): void;
|
||||
disableSelectionFind(): void;
|
||||
replace(): void;
|
||||
replaceAll(): void;
|
||||
}
|
||||
@@ -45,56 +39,49 @@ 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 {
|
||||
|
||||
private static _USER_CLOSED_EVENT = 'close';
|
||||
private static _USER_INPUT_EVENT = 'userInputEvent';
|
||||
export class FindWidget implements EditorBrowser.IOverlayWidget {
|
||||
|
||||
private static ID = 'editor.contrib.findWidget';
|
||||
private static PART_WIDTH = 275;
|
||||
private static FIND_INPUT_AREA_WIDTH = FindWidget.PART_WIDTH - 54;
|
||||
private static REPLACE_INPUT_AREA_WIDTH = FindWidget.FIND_INPUT_AREA_WIDTH;
|
||||
|
||||
private _codeEditor:EditorBrowser.ICodeEditor;
|
||||
private _codeEditor: EditorBrowser.ICodeEditor;
|
||||
private _state: FindReplaceState;
|
||||
private _controller: IFindController;
|
||||
private _contextViewProvider:ContextView.IContextViewProvider;
|
||||
private _contextViewProvider: IContextViewProvider;
|
||||
private _keybindingService: IKeybindingService;
|
||||
|
||||
private _domNode:HTMLElement;
|
||||
private _findInput:Findinput.FindInput;
|
||||
private _replaceInputBox:InputBox.InputBox;
|
||||
private _domNode: HTMLElement;
|
||||
private _findInput: FindInput;
|
||||
private _replaceInputBox: InputBox;
|
||||
|
||||
private _toggleReplaceBtn:SimpleButton;
|
||||
private _prevBtn:SimpleButton;
|
||||
private _nextBtn:SimpleButton;
|
||||
private _toggleSelectionFind:Checkbox;
|
||||
private _closeBtn:SimpleButton;
|
||||
private _replaceBtn:SimpleButton;
|
||||
private _replaceAllBtn:SimpleButton;
|
||||
private _toggleReplaceBtn: SimpleButton;
|
||||
private _prevBtn: SimpleButton;
|
||||
private _nextBtn: SimpleButton;
|
||||
private _toggleSelectionFind: Checkbox;
|
||||
private _closeBtn: SimpleButton;
|
||||
private _replaceBtn: SimpleButton;
|
||||
private _replaceAllBtn: SimpleButton;
|
||||
|
||||
private _isReplaceEnabled:boolean;
|
||||
private _isVisible:boolean;
|
||||
private _isReplaceVisible:boolean;
|
||||
private _isReplaceEnabled: boolean;
|
||||
private _isVisible: boolean;
|
||||
private _isReplaceVisible: boolean;
|
||||
|
||||
private _toDispose:Lifecycle.IDisposable[];
|
||||
private _toDispose: IDisposable[];
|
||||
|
||||
private _model:FindModel.FindModelBoundToEditorModel;
|
||||
private _modelListenersToDispose:Lifecycle.IDisposable[];
|
||||
|
||||
private focusTracker:DomUtils.IFocusTracker;
|
||||
private focusTracker: DomUtils.IFocusTracker;
|
||||
|
||||
constructor(
|
||||
codeEditor:EditorBrowser.ICodeEditor,
|
||||
controller:IFindController,
|
||||
contextViewProvider:ContextView.IContextViewProvider,
|
||||
keybindingService:IKeybindingService
|
||||
codeEditor: EditorBrowser.ICodeEditor,
|
||||
controller: IFindController,
|
||||
state: FindReplaceState,
|
||||
contextViewProvider: IContextViewProvider,
|
||||
keybindingService: IKeybindingService
|
||||
) {
|
||||
super([
|
||||
FindWidget._USER_INPUT_EVENT,
|
||||
FindWidget._USER_CLOSED_EVENT,
|
||||
]);
|
||||
this._codeEditor = codeEditor;
|
||||
this._controller = controller;
|
||||
this._state = state;
|
||||
this._contextViewProvider = contextViewProvider;
|
||||
this._keybindingService = keybindingService;
|
||||
|
||||
@@ -103,30 +90,34 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
|
||||
this._isReplaceEnabled = false;
|
||||
this._toDispose = [];
|
||||
|
||||
this._model = null;
|
||||
this._modelListenersToDispose = [];
|
||||
this._toDispose.push(this._state.addChangeListener((e) => this._onStateChanged(e)));
|
||||
|
||||
this._buildDomNode();
|
||||
|
||||
this.focusTracker = DomUtils.trackFocus(this._domNode);
|
||||
this.focusTracker = DomUtils.trackFocus(this._findInput.inputBox.inputElement);
|
||||
this.focusTracker.addFocusListener(() => this._reseedFindScope());
|
||||
this._toDispose.push(this.focusTracker);
|
||||
|
||||
this._codeEditor.addOverlayWidget(this);
|
||||
|
||||
this.focusTracker.addFocusListener(() => {
|
||||
var selection = this._codeEditor.getSelection();
|
||||
if (selection.startLineNumber !== selection.endLineNumber) {
|
||||
// Search in selection
|
||||
this._controller.enableSelectionFind();
|
||||
this._toDispose.push({
|
||||
dispose: () => {
|
||||
this._findInput.destroy();
|
||||
}
|
||||
});
|
||||
this._toDispose.push(this._replaceInputBox);
|
||||
|
||||
this._codeEditor.addOverlayWidget(this);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.focusTracker.dispose();
|
||||
this._removeModel();
|
||||
this._findInput.destroy();
|
||||
this._replaceInputBox.dispose();
|
||||
this._toDispose = Lifecycle.disposeAll(this._toDispose);
|
||||
this._toDispose = disposeAll(this._toDispose);
|
||||
}
|
||||
|
||||
private _reseedFindScope(): void {
|
||||
let selection = this._codeEditor.getSelection();
|
||||
if (selection.startLineNumber !== selection.endLineNumber) {
|
||||
// Reseed find scope
|
||||
this._state.change({ searchScope: selection }, true);
|
||||
}
|
||||
}
|
||||
|
||||
// ----- IOverlayWidget API
|
||||
@@ -148,62 +139,60 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
|
||||
return null;
|
||||
}
|
||||
|
||||
public setSearchString(searchString:string): void {
|
||||
this._findInput.setValue(searchString);
|
||||
this._emitUserInputEvent(false);
|
||||
}
|
||||
// ----- React to state changes
|
||||
|
||||
private _setState(state:FindModel.IFindState, selectionFindEnabled:boolean): void {
|
||||
this._findInput.setValue(state.searchString);
|
||||
this._findInput.setCaseSensitive(state.properties.matchCase);
|
||||
this._findInput.setWholeWords(state.properties.wholeWord);
|
||||
this._findInput.setRegex(state.properties.isRegex);
|
||||
this._toggleSelectionFind.checkbox.disabled = !selectionFindEnabled;
|
||||
this._toggleSelectionFind.checkbox.checked = selectionFindEnabled;
|
||||
private _onStateChanged(e:FindReplaceStateChangedEvent): void {
|
||||
if (e.searchString) {
|
||||
this._findInput.setValue(this._state.searchString);
|
||||
|
||||
this._replaceInputBox.value = state.replaceString;
|
||||
if (state.isReplaceRevealed) {
|
||||
this._enableReplace(false);
|
||||
} else {
|
||||
this._disableReplace(false);
|
||||
let findInputIsNonEmpty = (this._state.searchString.length > 0);
|
||||
this._prevBtn.setEnabled(findInputIsNonEmpty);
|
||||
this._nextBtn.setEnabled(findInputIsNonEmpty);
|
||||
this._replaceBtn.setEnabled(findInputIsNonEmpty);
|
||||
this._replaceAllBtn.setEnabled(findInputIsNonEmpty);
|
||||
}
|
||||
if (e.replaceString) {
|
||||
this._replaceInputBox.value = this._state.replaceString;
|
||||
}
|
||||
if (e.isRevealed) {
|
||||
if (this._state.isRevealed) {
|
||||
this._reveal(true);
|
||||
} else {
|
||||
this._hide(true);
|
||||
}
|
||||
}
|
||||
if (e.isReplaceRevealed) {
|
||||
if (this._state.isReplaceRevealed) {
|
||||
this._enableReplace();
|
||||
} else {
|
||||
this._disableReplace();
|
||||
}
|
||||
}
|
||||
if (e.isRegex) {
|
||||
this._findInput.setRegex(this._state.isRegex);
|
||||
}
|
||||
if (e.wholeWord) {
|
||||
this._findInput.setWholeWords(this._state.wholeWord);
|
||||
}
|
||||
if (e.matchCase) {
|
||||
this._findInput.setCaseSensitive(this._state.matchCase);
|
||||
}
|
||||
if (e.searchScope) {
|
||||
if (this._state.searchScope) {
|
||||
this._toggleSelectionFind.checkbox.checked = true;
|
||||
} else {
|
||||
this._toggleSelectionFind.checkbox.checked = false;
|
||||
}
|
||||
this._updateToggleSelectionFindButton();
|
||||
}
|
||||
if (e.searchString || e.matchesCount) {
|
||||
let showRedOutline = (this._state.searchString.length > 0 && this._state.matchesCount === 0);
|
||||
DomUtils.toggleClass(this._domNode, 'no-results', showRedOutline);
|
||||
}
|
||||
this._onFindValueChange();
|
||||
}
|
||||
|
||||
// ----- Public
|
||||
|
||||
public getState(): FindModel.IFindState {
|
||||
var result:FindModel.IFindState = {
|
||||
searchString: this._findInput.getValue(),
|
||||
replaceString: this._replaceInputBox.value,
|
||||
properties: {
|
||||
isRegex: this._findInput.getRegex(),
|
||||
wholeWord: this._findInput.getWholeWords(),
|
||||
matchCase: this._findInput.getCaseSensitive()
|
||||
},
|
||||
isReplaceRevealed: this._isReplaceEnabled
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
public setModel(newFindModel:FindModel.FindModelBoundToEditorModel): void {
|
||||
this._removeModel();
|
||||
if (newFindModel) {
|
||||
// We have a new model! :)
|
||||
this._model = newFindModel;
|
||||
this._modelListenersToDispose.push(this._model.addStartEventListener((e:FindModel.IFindStartEvent) => {
|
||||
this._reveal(e.shouldAnimate);
|
||||
this._setState(e.state, e.selectionFindEnabled);
|
||||
}));
|
||||
this._modelListenersToDispose.push(this._model.addMatchesUpdatedEventListener((e:FindModel.IFindMatchesEvent) => {
|
||||
DomUtils.toggleClass(this._domNode, 'no-results', this._findInput.getValue() !== '' && e.count === 0);
|
||||
}));
|
||||
} else {
|
||||
// No model :(
|
||||
this._hide(false);
|
||||
}
|
||||
}
|
||||
|
||||
public focusFindInput(): void {
|
||||
this._findInput.select();
|
||||
// Edge browser requires focus() in addition to select()
|
||||
@@ -216,14 +205,7 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
|
||||
this._replaceInputBox.focus();
|
||||
}
|
||||
|
||||
private _removeModel(): void {
|
||||
if (this._model !== null) {
|
||||
this._modelListenersToDispose = Lifecycle.disposeAll(this._modelListenersToDispose);
|
||||
this._model = null;
|
||||
}
|
||||
}
|
||||
|
||||
private _enableReplace(sendEvent:boolean): void {
|
||||
private _enableReplace(): void {
|
||||
this._isReplaceEnabled = true;
|
||||
if (!this._codeEditor.getConfiguration().readOnly && !this._isReplaceVisible) {
|
||||
this._replaceInputBox.enable();
|
||||
@@ -233,12 +215,9 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
|
||||
this._toggleReplaceBtn.toggleClass('expand', true);
|
||||
this._toggleReplaceBtn.setExpanded(true);
|
||||
}
|
||||
if (sendEvent) {
|
||||
this._emitUserInputEvent(false);
|
||||
}
|
||||
}
|
||||
|
||||
private _disableReplace(sendEvent:boolean): void {
|
||||
private _disableReplace(): void {
|
||||
this._isReplaceEnabled = false;
|
||||
if (this._isReplaceVisible) {
|
||||
this._replaceInputBox.disable();
|
||||
@@ -248,22 +227,17 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
|
||||
this._toggleReplaceBtn.setExpanded(false);
|
||||
this._isReplaceVisible = false;
|
||||
}
|
||||
if (sendEvent) {
|
||||
this._emitUserInputEvent(false);
|
||||
}
|
||||
}
|
||||
|
||||
// ----- initialization
|
||||
|
||||
private _onFindInputKeyDown(e:DomUtils.IKeyboardEvent): void {
|
||||
|
||||
var handled = false;
|
||||
let handled = false;
|
||||
|
||||
if (e.equals(CommonKeybindings.ENTER)) {
|
||||
this._codeEditor.getAction(FindModel.NEXT_MATCH_FIND_ACTION_ID).run().done(null, Errors.onUnexpectedError);
|
||||
this._codeEditor.getAction(FIND_IDS.NextMatchFindAction).run().done(null, Errors.onUnexpectedError);
|
||||
handled = true;
|
||||
} else if (e.equals(CommonKeybindings.SHIFT_ENTER)) {
|
||||
this._codeEditor.getAction(FindModel.PREVIOUS_MATCH_FIND_ACTION_ID).run().done(null, Errors.onUnexpectedError);
|
||||
this._codeEditor.getAction(FIND_IDS.PreviousMatchFindAction).run().done(null, Errors.onUnexpectedError);
|
||||
handled = true;
|
||||
} else if (e.equals(CommonKeybindings.TAB)) {
|
||||
if (this._isReplaceVisible) {
|
||||
@@ -280,16 +254,16 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
|
||||
if (handled) {
|
||||
e.preventDefault();
|
||||
} else {
|
||||
// getValue() is not updated right away
|
||||
setTimeout(() => {
|
||||
this._onFindValueChange();
|
||||
this._emitUserInputEvent(true);
|
||||
this._state.change({ searchString: this._findInput.getValue() }, true);
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
|
||||
private _onReplaceInputKeyDown(e:DomUtils.IKeyboardEvent): void {
|
||||
|
||||
var handled = false;
|
||||
let handled = false;
|
||||
|
||||
if (e.equals(CommonKeybindings.ENTER)) {
|
||||
this._controller.replace();
|
||||
@@ -309,19 +283,12 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
|
||||
e.preventDefault();
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this._emitUserInputEvent(true);
|
||||
this._state.change({ replaceString: this._replaceInputBox.value }, false);
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
|
||||
private _onFindValueChange(): void {
|
||||
var findInputIsNonEmpty = (this._findInput.getValue().length > 0);
|
||||
|
||||
this._prevBtn.setEnabled(findInputIsNonEmpty);
|
||||
this._nextBtn.setEnabled(findInputIsNonEmpty);
|
||||
this._replaceBtn.setEnabled(findInputIsNonEmpty);
|
||||
this._replaceAllBtn.setEnabled(findInputIsNonEmpty);
|
||||
}
|
||||
// ----- initialization
|
||||
|
||||
private _keybindingLabelFor(actionId:string): string {
|
||||
let keybindings = this._keybindingService.lookupKeybindings(actionId);
|
||||
@@ -333,14 +300,14 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
|
||||
|
||||
private _buildFindPart(): HTMLElement {
|
||||
// Find input
|
||||
this._findInput = new Findinput.FindInput(null, this._contextViewProvider, {
|
||||
this._findInput = new FindInput(null, this._contextViewProvider, {
|
||||
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 => {
|
||||
appendCaseSensitiveLabel: this._keybindingLabelFor(FIND_IDS.ToggleCaseSensitiveCommand),
|
||||
appendWholeWordsLabel: this._keybindingLabelFor(FIND_IDS.ToggleWholeWordCommand),
|
||||
appendRegexLabel: this._keybindingLabelFor(FIND_IDS.ToggleRegexCommand),
|
||||
validation: (value:string): InputBoxMessage => {
|
||||
if (value.length === 0) {
|
||||
return null;
|
||||
}
|
||||
@@ -355,32 +322,40 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
|
||||
}
|
||||
}
|
||||
}).on('keydown', (browserEvent:KeyboardEvent) => {
|
||||
this._onFindInputKeyDown(new Keyboard.StandardKeyboardEvent(browserEvent));
|
||||
}).on(Findinput.FindInput.OPTION_CHANGE, () => {
|
||||
this._emitUserInputEvent(true);
|
||||
this._onFindInputKeyDown(new StandardKeyboardEvent(browserEvent));
|
||||
}).on(FindInput.OPTION_CHANGE, () => {
|
||||
this._state.change({
|
||||
isRegex: this._findInput.getRegex(),
|
||||
wholeWord: this._findInput.getWholeWords(),
|
||||
matchCase: this._findInput.getCaseSensitive()
|
||||
}, true);
|
||||
});
|
||||
|
||||
this._findInput.disable();
|
||||
|
||||
// Previous button
|
||||
this._prevBtn = new SimpleButton(
|
||||
NLS_PREVIOUS_MATCH_BTN_LABEL + this._keybindingLabelFor(FindModel.PREVIOUS_MATCH_FIND_ACTION_ID),
|
||||
'previous'
|
||||
).onTrigger(() => {
|
||||
this._codeEditor.getAction(FindModel.PREVIOUS_MATCH_FIND_ACTION_ID).run().done(null, Errors.onUnexpectedError);
|
||||
this._prevBtn = new SimpleButton({
|
||||
label: NLS_PREVIOUS_MATCH_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.PreviousMatchFindAction),
|
||||
className: 'previous',
|
||||
onTrigger: () => {
|
||||
this._codeEditor.getAction(FIND_IDS.PreviousMatchFindAction).run().done(null, Errors.onUnexpectedError);
|
||||
},
|
||||
onKeyDown: (e) => {}
|
||||
});
|
||||
this._toDispose.push(this._prevBtn);
|
||||
|
||||
// Next button
|
||||
this._nextBtn = new SimpleButton(
|
||||
NLS_NEXT_MATCH_BTN_LABEL + this._keybindingLabelFor(FindModel.NEXT_MATCH_FIND_ACTION_ID),
|
||||
'next'
|
||||
).onTrigger(() => {
|
||||
this._codeEditor.getAction(FindModel.NEXT_MATCH_FIND_ACTION_ID).run().done(null, Errors.onUnexpectedError);
|
||||
this._nextBtn = new SimpleButton({
|
||||
label: NLS_NEXT_MATCH_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.NextMatchFindAction),
|
||||
className: 'next',
|
||||
onTrigger: () => {
|
||||
this._codeEditor.getAction(FIND_IDS.NextMatchFindAction).run().done(null, Errors.onUnexpectedError);
|
||||
},
|
||||
onKeyDown: (e) => {}
|
||||
});
|
||||
this._toDispose.push(this._nextBtn);
|
||||
|
||||
var findPart = document.createElement('div');
|
||||
let findPart = document.createElement('div');
|
||||
findPart.className = 'find-part';
|
||||
findPart.appendChild(this._findInput.domNode);
|
||||
findPart.appendChild(this._prevBtn.domNode);
|
||||
@@ -391,29 +366,28 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
|
||||
this._toggleSelectionFind.disable();
|
||||
this._toDispose.push(DomUtils.addStandardDisposableListener(this._toggleSelectionFind.checkbox, 'change', (e) => {
|
||||
if (this._toggleSelectionFind.checkbox.checked) {
|
||||
this._controller.enableSelectionFind();
|
||||
this._reseedFindScope();
|
||||
} else {
|
||||
this._controller.disableSelectionFind();
|
||||
this._updateToggleSelectionFindButton();
|
||||
this._state.change({ searchScope: null }, true);
|
||||
}
|
||||
}));
|
||||
this._toDispose.push(this._toggleSelectionFind);
|
||||
|
||||
this._codeEditor.addListener(EditorCommon.EventType.CursorSelectionChanged, () => {
|
||||
this._updateToggleSelectionFindButton();
|
||||
});
|
||||
|
||||
// Close button
|
||||
this._closeBtn = new SimpleButton(
|
||||
NLS_CLOSE_BTN_LABEL + this._keybindingLabelFor(FindModel.CLOSE_FIND_WIDGET_COMMAND_ID),
|
||||
'close-fw'
|
||||
).onTrigger(() => {
|
||||
this._hide(true);
|
||||
this._emitClosedEvent();
|
||||
}).onKeyDown((e) => {
|
||||
if (this._isReplaceVisible) {
|
||||
this._replaceBtn.focus();
|
||||
e.preventDefault();
|
||||
this._closeBtn = new SimpleButton({
|
||||
label: NLS_CLOSE_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.CloseFindWidgetCommand),
|
||||
className: 'close-fw',
|
||||
onTrigger: () => {
|
||||
this._state.change({ isRevealed: false }, false);
|
||||
},
|
||||
onKeyDown: (e) => {
|
||||
if (this._isReplaceVisible) {
|
||||
this._replaceBtn.focus();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
});
|
||||
this._toDispose.push(this._closeBtn);
|
||||
@@ -433,7 +407,7 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
|
||||
}
|
||||
|
||||
if (!this._toggleSelectionFind.checkbox.checked) {
|
||||
var selection = this._codeEditor.getSelection();
|
||||
let selection = this._codeEditor.getSelection();
|
||||
|
||||
if (selection.startLineNumber === selection.endLineNumber) {
|
||||
this._toggleSelectionFind.disable();
|
||||
@@ -445,10 +419,10 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
|
||||
|
||||
private _buildReplacePart(): HTMLElement {
|
||||
// Replace input
|
||||
var replaceInput = document.createElement('div');
|
||||
let replaceInput = document.createElement('div');
|
||||
replaceInput.className = 'replace-input';
|
||||
replaceInput.style.width = FindWidget.REPLACE_INPUT_AREA_WIDTH + 'px';
|
||||
this._replaceInputBox = new InputBox.InputBox(replaceInput, null, {
|
||||
this._replaceInputBox = new InputBox(replaceInput, null, {
|
||||
ariaLabel: NLS_REPLACE_INPUT_LABEL,
|
||||
placeholder: NLS_REPLACE_INPUT_PLACEHOLDER
|
||||
});
|
||||
@@ -457,24 +431,28 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
|
||||
this._replaceInputBox.disable();
|
||||
|
||||
// Replace one button
|
||||
this._replaceBtn = new SimpleButton(
|
||||
NLS_REPLACE_BTN_LABEL,
|
||||
'replace'
|
||||
).onTrigger(() => {
|
||||
this._controller.replace();
|
||||
this._replaceBtn = new SimpleButton({
|
||||
label: NLS_REPLACE_BTN_LABEL,
|
||||
className: 'replace',
|
||||
onTrigger: () => {
|
||||
this._controller.replace();
|
||||
},
|
||||
onKeyDown: (e) => {}
|
||||
});
|
||||
this._toDispose.push(this._replaceBtn);
|
||||
|
||||
// Replace all button
|
||||
this._replaceAllBtn = new SimpleButton(
|
||||
NLS_REPLACE_ALL_BTN_LABEL,
|
||||
'replace-all'
|
||||
).onTrigger(() => {
|
||||
this._controller.replaceAll();
|
||||
this._replaceAllBtn = new SimpleButton({
|
||||
label: NLS_REPLACE_ALL_BTN_LABEL,
|
||||
className: 'replace-all',
|
||||
onTrigger: () => {
|
||||
this._controller.replaceAll();
|
||||
},
|
||||
onKeyDown: (e) => {}
|
||||
});
|
||||
this._toDispose.push(this._replaceAllBtn);
|
||||
|
||||
var replacePart = document.createElement('div');
|
||||
let replacePart = document.createElement('div');
|
||||
replacePart.className = 'replace-part';
|
||||
replacePart.appendChild(replaceInput);
|
||||
replacePart.appendChild(this._replaceBtn.domNode);
|
||||
@@ -485,21 +463,19 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
|
||||
|
||||
private _buildDomNode(): void {
|
||||
// Find part
|
||||
var findPart = this._buildFindPart();
|
||||
let findPart = this._buildFindPart();
|
||||
|
||||
// Replace part
|
||||
var replacePart = this._buildReplacePart();
|
||||
let replacePart = this._buildReplacePart();
|
||||
|
||||
// Toggle replace button
|
||||
this._toggleReplaceBtn = new SimpleButton(
|
||||
NLS_TOGGLE_REPLACE_MODE_BTN_LABEL,
|
||||
'toggle left'
|
||||
).onTrigger(() => {
|
||||
if (this._isReplaceVisible) {
|
||||
this._disableReplace(true);
|
||||
} else {
|
||||
this._enableReplace(true);
|
||||
}
|
||||
this._toggleReplaceBtn = new SimpleButton({
|
||||
label: NLS_TOGGLE_REPLACE_MODE_BTN_LABEL,
|
||||
className: 'toggle left',
|
||||
onTrigger: () => {
|
||||
this._state.change({ isReplaceRevealed: !this._isReplaceVisible }, true);
|
||||
},
|
||||
onKeyDown: (e) => {}
|
||||
});
|
||||
this._toggleReplaceBtn.toggleClass('expand', this._isReplaceVisible);
|
||||
this._toggleReplaceBtn.toggleClass('collapse', !this._isReplaceVisible);
|
||||
@@ -532,14 +508,18 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
|
||||
this._toggleSelectionFind.enable();
|
||||
this._closeBtn.setEnabled(true);
|
||||
|
||||
this._onFindValueChange();
|
||||
let findInputIsNonEmpty = (this._state.searchString.length > 0);
|
||||
this._prevBtn.setEnabled(findInputIsNonEmpty);
|
||||
this._nextBtn.setEnabled(findInputIsNonEmpty);
|
||||
this._replaceBtn.setEnabled(findInputIsNonEmpty);
|
||||
this._replaceAllBtn.setEnabled(findInputIsNonEmpty);
|
||||
|
||||
this._isVisible = true;
|
||||
window.setTimeout(() => {
|
||||
setTimeout(() => {
|
||||
DomUtils.addClass(this._domNode, 'visible');
|
||||
if (!animate) {
|
||||
DomUtils.addClass(this._domNode, 'noanimation');
|
||||
window.setTimeout(() => {
|
||||
setTimeout(() => {
|
||||
DomUtils.removeClass(this._domNode, 'noanimation');
|
||||
}, 200);
|
||||
}
|
||||
@@ -568,48 +548,15 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
|
||||
this._codeEditor.layoutOverlayWidget(this);
|
||||
}
|
||||
}
|
||||
|
||||
public addUserInputEventListener(callback:(e:IUserInputEvent)=>void): Lifecycle.IDisposable {
|
||||
return this.addListener2(FindWidget._USER_INPUT_EVENT, callback);
|
||||
}
|
||||
|
||||
private _emitUserInputEvent(jumpToNextMatch:boolean): void {
|
||||
var e:IUserInputEvent = {
|
||||
jumpToNextMatch: jumpToNextMatch
|
||||
};
|
||||
this.emit(FindWidget._USER_INPUT_EVENT, e);
|
||||
}
|
||||
|
||||
public addClosedEventListener(callback:()=>void): Lifecycle.IDisposable {
|
||||
return this.addListener2(FindWidget._USER_CLOSED_EVENT, callback);
|
||||
}
|
||||
|
||||
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 {
|
||||
export class Checkbox {
|
||||
|
||||
private static COUNTER = 0;
|
||||
private _domNode:HTMLElement;
|
||||
private _checkbox:HTMLInputElement;
|
||||
private label:HTMLLabelElement;
|
||||
private static _COUNTER = 0;
|
||||
|
||||
private _domNode: HTMLElement;
|
||||
private _checkbox: HTMLInputElement;
|
||||
private _label: HTMLLabelElement;
|
||||
|
||||
constructor(parent: HTMLElement, title: string) {
|
||||
this._domNode = document.createElement('div');
|
||||
@@ -619,15 +566,15 @@ export class Checkbox implements Lifecycle.IDisposable {
|
||||
this._checkbox = document.createElement('input');
|
||||
this._checkbox.type = 'checkbox';
|
||||
this._checkbox.className = 'checkbox';
|
||||
this._checkbox.id = 'checkbox-' + Checkbox.COUNTER++;
|
||||
this._checkbox.id = 'checkbox-' + Checkbox._COUNTER++;
|
||||
|
||||
this.label = document.createElement('label');
|
||||
this.label.className = 'label';
|
||||
this._label = document.createElement('label');
|
||||
this._label.className = 'label';
|
||||
// Connect the label and the checkbox. Checkbox will get checked when the label recieves a click.
|
||||
this.label.htmlFor = this._checkbox.id;
|
||||
this._label.htmlFor = this._checkbox.id;
|
||||
|
||||
this._domNode.appendChild(this._checkbox);
|
||||
this._domNode.appendChild(this.label);
|
||||
this._domNode.appendChild(this._label);
|
||||
|
||||
parent.appendChild(this._domNode);
|
||||
}
|
||||
@@ -651,50 +598,48 @@ export class Checkbox implements Lifecycle.IDisposable {
|
||||
public disable(): void {
|
||||
this._checkbox.disabled = true;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._domNode = null;
|
||||
this._checkbox = null;
|
||||
this.label = null;
|
||||
}
|
||||
}
|
||||
|
||||
class SimpleButton implements Lifecycle.IDisposable {
|
||||
interface ISimpleButtonOpts {
|
||||
label: string;
|
||||
className: string;
|
||||
onTrigger: ()=>void;
|
||||
onKeyDown: (e:DomUtils.IKeyboardEvent)=>void;
|
||||
}
|
||||
|
||||
private _onTrigger:()=>void;
|
||||
private _onKeyDown:(e:DomUtils.IKeyboardEvent)=>void;
|
||||
private _domNode:HTMLElement;
|
||||
private _toDispose:Lifecycle.IDisposable[];
|
||||
class SimpleButton implements IDisposable {
|
||||
|
||||
constructor(label:string, className:string) {
|
||||
private _opts: ISimpleButtonOpts;
|
||||
private _domNode: HTMLElement;
|
||||
private _toDispose: IDisposable[];
|
||||
|
||||
this._onTrigger = null;
|
||||
this._onKeyDown = null;
|
||||
constructor(opts:ISimpleButtonOpts) {
|
||||
this._opts = opts;
|
||||
|
||||
this._domNode = document.createElement('div');
|
||||
this._domNode.title = label;
|
||||
this._domNode.title = this._opts.label;
|
||||
this._domNode.tabIndex = -1;
|
||||
this._domNode.className = 'button ' + className;
|
||||
this._domNode.className = 'button ' + this._opts.className;
|
||||
this._domNode.setAttribute('role', 'button');
|
||||
this._domNode.setAttribute('aria-label', label);
|
||||
this._domNode.setAttribute('aria-label', this._opts.label);
|
||||
|
||||
this._toDispose = [];
|
||||
this._toDispose.push(DomUtils.addStandardDisposableListener(this._domNode, 'click', (e) => {
|
||||
this._invokeOnTrigger();
|
||||
this._opts.onTrigger();
|
||||
e.preventDefault();
|
||||
}));
|
||||
this._toDispose.push(DomUtils.addStandardDisposableListener(this._domNode, 'keydown', (e) => {
|
||||
if (e.equals(CommonKeybindings.SPACE) || e.equals(CommonKeybindings.ENTER)) {
|
||||
this._invokeOnTrigger();
|
||||
this._opts.onTrigger();
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
this._invokeOnKeyDown(e);
|
||||
this._opts.onKeyDown(e);
|
||||
}));
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._toDispose = Lifecycle.disposeAll(this._toDispose);
|
||||
this._toDispose = disposeAll(this._toDispose);
|
||||
}
|
||||
|
||||
public get domNode(): HTMLElement {
|
||||
@@ -718,26 +663,4 @@ class SimpleButton implements Lifecycle.IDisposable {
|
||||
public toggleClass(className:string, shouldHaveIt:boolean): void {
|
||||
DomUtils.toggleClass(this._domNode, className, shouldHaveIt);
|
||||
}
|
||||
|
||||
public onTrigger(onTrigger:()=>void): SimpleButton {
|
||||
this._onTrigger = onTrigger;
|
||||
return this;
|
||||
}
|
||||
|
||||
public onKeyDown(onKeyDown:(e:DomUtils.IKeyboardEvent)=>void): SimpleButton {
|
||||
this._onKeyDown = onKeyDown;
|
||||
return this;
|
||||
}
|
||||
|
||||
private _invokeOnTrigger(): void {
|
||||
if (this._onTrigger) {
|
||||
this._onTrigger();
|
||||
}
|
||||
}
|
||||
|
||||
private _invokeOnKeyDown(e:DomUtils.IKeyboardEvent): void {
|
||||
if (this._onKeyDown) {
|
||||
this._onKeyDown(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
214
src/vs/editor/contrib/find/common/findDecorations.ts
Normal file
214
src/vs/editor/contrib/find/common/findDecorations.ts
Normal file
@@ -0,0 +1,214 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import EditorCommon = require('vs/editor/common/editorCommon');
|
||||
import Strings = require('vs/base/common/strings');
|
||||
import Events = require('vs/base/common/eventEmitter');
|
||||
import ReplaceAllCommand = require('./replaceAllCommand');
|
||||
import Lifecycle = require('vs/base/common/lifecycle');
|
||||
import Schedulers = require('vs/base/common/async');
|
||||
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 class FindDecorations implements Lifecycle.IDisposable {
|
||||
private editor:EditorCommon.ICommonCodeEditor;
|
||||
|
||||
private decorations:string[];
|
||||
private decorationIndex:number;
|
||||
private findScopeDecorationId:string;
|
||||
private highlightedDecorationId:string;
|
||||
private startPosition:EditorCommon.IEditorPosition;
|
||||
|
||||
constructor(editor:EditorCommon.ICommonCodeEditor) {
|
||||
this.editor = editor;
|
||||
this.decorations = [];
|
||||
this.decorationIndex = 0;
|
||||
this.findScopeDecorationId = null;
|
||||
this.highlightedDecorationId = null;
|
||||
this.startPosition = this.editor.getPosition();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.editor.deltaDecorations(this._allDecorations(), []);
|
||||
|
||||
this.editor = null;
|
||||
this.decorations = [];
|
||||
this.decorationIndex = 0;
|
||||
this.findScopeDecorationId = null;
|
||||
this.highlightedDecorationId = null;
|
||||
this.startPosition = null;
|
||||
}
|
||||
|
||||
public reset(): void {
|
||||
this.decorations = [];
|
||||
this.decorationIndex = -1;
|
||||
this.findScopeDecorationId = null;
|
||||
this.highlightedDecorationId = null;
|
||||
}
|
||||
|
||||
public getFindScope(): EditorCommon.IEditorRange {
|
||||
if (this.findScopeDecorationId) {
|
||||
return this.editor.getModel().getDecorationRange(this.findScopeDecorationId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public setStartPosition(newStartPosition:EditorCommon.IEditorPosition): void {
|
||||
this.startPosition = newStartPosition;
|
||||
this._setDecorationIndex(-1, false);
|
||||
}
|
||||
|
||||
public hasMatches(): boolean {
|
||||
return (this.decorations.length > 0);
|
||||
}
|
||||
|
||||
public setIndexToFirstAfterStartPosition(): void {
|
||||
this._setDecorationIndex(this.indexAfterPosition(this.startPosition), false);
|
||||
}
|
||||
|
||||
public moveToFirstAfterStartPosition(): void {
|
||||
this._setDecorationIndex(this.indexAfterPosition(this.startPosition), true);
|
||||
}
|
||||
|
||||
public movePrev(): void {
|
||||
if (!this.hasMatches()) {
|
||||
this._revealFindScope();
|
||||
return;
|
||||
}
|
||||
if (this.decorationIndex === -1) {
|
||||
this._setDecorationIndex(this.previousIndex(this.indexAfterPosition(this.startPosition)), true);
|
||||
} else {
|
||||
this._setDecorationIndex(this.previousIndex(this.decorationIndex), true);
|
||||
}
|
||||
}
|
||||
|
||||
public moveNext(): void {
|
||||
if (!this.hasMatches()) {
|
||||
this._revealFindScope();
|
||||
return;
|
||||
}
|
||||
if (this.decorationIndex === -1) {
|
||||
this._setDecorationIndex(this.indexAfterPosition(this.startPosition), true);
|
||||
} else {
|
||||
this._setDecorationIndex(this.nextIndex(this.decorationIndex), true);
|
||||
}
|
||||
}
|
||||
|
||||
private _revealFindScope(): void {
|
||||
let findScope = this.getFindScope();
|
||||
if (findScope) {
|
||||
// Reveal the selection so user is reminded that 'selection find' is on.
|
||||
this.editor.revealRangeInCenterIfOutsideViewport(findScope);
|
||||
}
|
||||
}
|
||||
|
||||
private _setDecorationIndex(newIndex:number, moveCursor:boolean): void {
|
||||
this.decorationIndex = newIndex;
|
||||
this.editor.changeDecorations((changeAccessor: EditorCommon.IModelDecorationsChangeAccessor) => {
|
||||
if (this.highlightedDecorationId !== null) {
|
||||
changeAccessor.changeDecorationOptions(this.highlightedDecorationId, FindDecorations.createFindMatchDecorationOptions(false));
|
||||
this.highlightedDecorationId = null;
|
||||
}
|
||||
if (moveCursor && this.decorationIndex >= 0 && this.decorationIndex < this.decorations.length) {
|
||||
this.highlightedDecorationId = this.decorations[this.decorationIndex];
|
||||
changeAccessor.changeDecorationOptions(this.highlightedDecorationId, FindDecorations.createFindMatchDecorationOptions(true));
|
||||
}
|
||||
});
|
||||
if (moveCursor && this.decorationIndex >= 0 && this.decorationIndex < this.decorations.length) {
|
||||
let range = this.editor.getModel().getDecorationRange(this.decorations[this.decorationIndex]);
|
||||
this.editor.setSelection(range);
|
||||
}
|
||||
}
|
||||
|
||||
public set(matches:EditorCommon.IEditorRange[], findScope:EditorCommon.IEditorRange): void {
|
||||
let newDecorations: EditorCommon.IModelDeltaDecoration[] = matches.map((match) => {
|
||||
return {
|
||||
range: match,
|
||||
options: FindDecorations.createFindMatchDecorationOptions(false)
|
||||
};
|
||||
});
|
||||
if (findScope) {
|
||||
newDecorations.unshift({
|
||||
range: findScope,
|
||||
options: FindDecorations.createFindScopeDecorationOptions()
|
||||
});
|
||||
}
|
||||
let tmpDecorations = this.editor.deltaDecorations(this._allDecorations(), newDecorations);
|
||||
|
||||
if (findScope) {
|
||||
this.findScopeDecorationId = tmpDecorations.shift();
|
||||
} else {
|
||||
this.findScopeDecorationId = null;
|
||||
}
|
||||
this.decorations = tmpDecorations;
|
||||
this.decorationIndex = -1;
|
||||
this.highlightedDecorationId = null;
|
||||
}
|
||||
|
||||
private _allDecorations(): string[] {
|
||||
let result:string[] = [];
|
||||
result = result.concat(this.decorations);
|
||||
if (this.findScopeDecorationId) {
|
||||
result.push(this.findScopeDecorationId);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private indexAfterPosition(position:EditorCommon.IEditorPosition): number {
|
||||
if (this.decorations.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
for (let i = 0, len = this.decorations.length; i < len; i++) {
|
||||
let decorationId = this.decorations[i];
|
||||
let r = this.editor.getModel().getDecorationRange(decorationId);
|
||||
if (!r || r.startLineNumber < position.lineNumber) {
|
||||
continue;
|
||||
}
|
||||
if (r.startLineNumber > position.lineNumber) {
|
||||
return i;
|
||||
}
|
||||
if (r.startColumn < position.column) {
|
||||
continue;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private previousIndex(index:number): number {
|
||||
if (this.decorations.length > 0) {
|
||||
return (index - 1 + this.decorations.length) % this.decorations.length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private nextIndex(index:number): number {
|
||||
if (this.decorations.length > 0) {
|
||||
return (index + 1) % this.decorations.length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static createFindMatchDecorationOptions(isCurrent:boolean): EditorCommon.IModelDecorationOptions {
|
||||
return {
|
||||
stickiness: EditorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
className: isCurrent ? 'currentFindMatch' : 'findMatch',
|
||||
overviewRuler: {
|
||||
color: 'rgba(246, 185, 77, 0.7)',
|
||||
darkColor: 'rgba(246, 185, 77, 0.7)',
|
||||
position: EditorCommon.OverviewRulerLane.Center
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static createFindScopeDecorationOptions(): EditorCommon.IModelDecorationOptions {
|
||||
return {
|
||||
className: 'findScope',
|
||||
isWholeLine: true
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -12,458 +12,192 @@ import Schedulers = require('vs/base/common/async');
|
||||
import {Range} from 'vs/editor/common/core/range';
|
||||
import {Position} from 'vs/editor/common/core/position';
|
||||
import {ReplaceCommand} from 'vs/editor/common/commands/replaceCommand';
|
||||
import {FindDecorations} from './findDecorations';
|
||||
import {FindReplaceStateChangedEvent, FindReplaceState} from './findState';
|
||||
|
||||
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;
|
||||
count: number;
|
||||
export const FIND_IDS = {
|
||||
StartFindAction: 'actions.find',
|
||||
NextMatchFindAction: 'editor.action.nextMatchFindAction',
|
||||
PreviousMatchFindAction: 'editor.action.previousMatchFindAction',
|
||||
StartFindReplaceAction: 'editor.action.startFindReplaceAction',
|
||||
CloseFindWidgetCommand: 'closeFindWidget',
|
||||
ToggleCaseSensitiveCommand: 'toggleFindCaseSensitive',
|
||||
ToggleWholeWordCommand: 'toggleFindWholeWord',
|
||||
ToggleRegexCommand: 'toggleFindRegex'
|
||||
}
|
||||
|
||||
export interface IFindProperties {
|
||||
isRegex: boolean;
|
||||
wholeWord: boolean;
|
||||
matchCase: boolean;
|
||||
}
|
||||
|
||||
export interface IFindState {
|
||||
searchString: string;
|
||||
replaceString: string;
|
||||
properties: IFindProperties;
|
||||
isReplaceRevealed: boolean;
|
||||
}
|
||||
|
||||
export interface IFindStartEvent {
|
||||
state: IFindState;
|
||||
selectionFindEnabled: boolean;
|
||||
shouldAnimate: boolean;
|
||||
}
|
||||
|
||||
export class FindModelBoundToEditorModel extends Events.EventEmitter {
|
||||
|
||||
private static _START_EVENT = 'start';
|
||||
private static _MATCHES_UPDATED_EVENT = 'matches';
|
||||
export class FindModelBoundToEditorModel {
|
||||
|
||||
private editor:EditorCommon.ICommonCodeEditor;
|
||||
private startPosition:EditorCommon.IEditorPosition;
|
||||
private searchString:string;
|
||||
private replaceString:string;
|
||||
private searchOnlyEditableRange:boolean;
|
||||
private decorations:string[];
|
||||
private decorationIndex:number;
|
||||
private findScopeDecorationId:string;
|
||||
private highlightedDecorationId:string;
|
||||
private listenersToRemove:Events.ListenerUnbind[];
|
||||
private _state:FindReplaceState;
|
||||
private _toDispose:Lifecycle.IDisposable[];
|
||||
private _decorations: FindDecorations;
|
||||
private _ignoreModelContentChanged:boolean;
|
||||
|
||||
private updateDecorationsScheduler:Schedulers.RunOnceScheduler;
|
||||
private didReplace:boolean;
|
||||
|
||||
private isRegex:boolean;
|
||||
private matchCase:boolean;
|
||||
private wholeWord:boolean;
|
||||
|
||||
constructor(editor:EditorCommon.ICommonCodeEditor) {
|
||||
super([
|
||||
FindModelBoundToEditorModel._MATCHES_UPDATED_EVENT,
|
||||
FindModelBoundToEditorModel._START_EVENT
|
||||
]);
|
||||
constructor(editor:EditorCommon.ICommonCodeEditor, state:FindReplaceState) {
|
||||
this.editor = editor;
|
||||
this.startPosition = null;
|
||||
this.searchString = '';
|
||||
this.replaceString = '';
|
||||
this.searchOnlyEditableRange = false;
|
||||
this.decorations = [];
|
||||
this.decorationIndex = 0;
|
||||
this.findScopeDecorationId = null;
|
||||
this.highlightedDecorationId = null;
|
||||
this.listenersToRemove = [];
|
||||
this.didReplace = false;
|
||||
this._state = state;
|
||||
this._toDispose = [];
|
||||
|
||||
this.isRegex = false;
|
||||
this.matchCase = false;
|
||||
this.wholeWord = false;
|
||||
this._decorations = new FindDecorations(editor);
|
||||
this._toDispose.push(this._decorations);
|
||||
|
||||
this.updateDecorationsScheduler = new Schedulers.RunOnceScheduler(() => {
|
||||
this.updateDecorations(false, false, null);
|
||||
}, 100);
|
||||
this.updateDecorationsScheduler = new Schedulers.RunOnceScheduler(() => this.research(false), 100);
|
||||
this._toDispose.push(this.updateDecorationsScheduler);
|
||||
|
||||
this.listenersToRemove.push(this.editor.addListener(EditorCommon.EventType.CursorPositionChanged, (e:EditorCommon.ICursorPositionChangedEvent) => {
|
||||
this._toDispose.push(this.editor.addListener2(EditorCommon.EventType.CursorPositionChanged, (e:EditorCommon.ICursorPositionChangedEvent) => {
|
||||
if (e.reason === 'explicit' || e.reason === 'undo' || e.reason === 'redo') {
|
||||
if (this.highlightedDecorationId !== null) {
|
||||
this.editor.changeDecorations((changeAccessor: EditorCommon.IModelDecorationsChangeAccessor) => {
|
||||
changeAccessor.changeDecorationOptions(this.highlightedDecorationId, this.createFindMatchDecorationOptions(false));
|
||||
this.highlightedDecorationId = null;
|
||||
});
|
||||
}
|
||||
this.startPosition = this.editor.getPosition();
|
||||
this.decorationIndex = -1;
|
||||
this._decorations.setStartPosition(this.editor.getPosition());
|
||||
}
|
||||
}));
|
||||
|
||||
this.listenersToRemove.push(this.editor.addListener(EditorCommon.EventType.ModelContentChanged, (e:EditorCommon.IModelContentChangedEvent) => {
|
||||
this._ignoreModelContentChanged = false;
|
||||
this._toDispose.push(this.editor.addListener2(EditorCommon.EventType.ModelContentChanged, (e:EditorCommon.IModelContentChangedEvent) => {
|
||||
if (this._ignoreModelContentChanged) {
|
||||
return;
|
||||
}
|
||||
if (e.changeType === EditorCommon.EventType.ModelContentChangedFlush) {
|
||||
// a model.setValue() was called
|
||||
this.decorations = [];
|
||||
this.decorationIndex = -1;
|
||||
this.findScopeDecorationId = null;
|
||||
this.highlightedDecorationId = null;
|
||||
this._decorations.reset();
|
||||
}
|
||||
this.startPosition = this.editor.getPosition();
|
||||
this._decorations.setStartPosition(this.editor.getPosition());
|
||||
this.updateDecorationsScheduler.schedule();
|
||||
}));
|
||||
|
||||
this._toDispose.push(this._state.addChangeListener((e) => this._onStateChanged(e)));
|
||||
|
||||
this.research(false, this._state.searchScope);
|
||||
}
|
||||
|
||||
private removeOldDecorations(changeAccessor:EditorCommon.IModelDecorationsChangeAccessor, removeFindScopeDecoration:boolean): void {
|
||||
let toRemove: string[] = [];
|
||||
var i:number, len:number;
|
||||
for (i = 0, len = this.decorations.length; i < len; i++) {
|
||||
toRemove.push(this.decorations[i]);
|
||||
}
|
||||
this.decorations = [];
|
||||
|
||||
if (removeFindScopeDecoration && this.hasFindScope()) {
|
||||
toRemove.push(this.findScopeDecorationId);
|
||||
this.findScopeDecorationId = null;
|
||||
}
|
||||
|
||||
changeAccessor.deltaDecorations(toRemove, []);
|
||||
public dispose(): void {
|
||||
this._toDispose = Lifecycle.disposeAll(this._toDispose);
|
||||
}
|
||||
|
||||
private createFindMatchDecorationOptions(isCurrent:boolean): EditorCommon.IModelDecorationOptions {
|
||||
return {
|
||||
stickiness: EditorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
className: isCurrent ? 'currentFindMatch' : 'findMatch',
|
||||
overviewRuler: {
|
||||
color: 'rgba(246, 185, 77, 0.7)',
|
||||
darkColor: 'rgba(246, 185, 77, 0.7)',
|
||||
position: EditorCommon.OverviewRulerLane.Center
|
||||
private _onStateChanged(e:FindReplaceStateChangedEvent): void {
|
||||
if (e.searchString || e.isReplaceRevealed || e.isRegex || e.wholeWord || e.matchCase || e.searchScope) {
|
||||
if (e.searchScope) {
|
||||
this.research(e.moveCursor, this._state.searchScope);
|
||||
} else {
|
||||
this.research(e.moveCursor);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private createFindScopeDecorationOptions(): EditorCommon.IModelDecorationOptions {
|
||||
return {
|
||||
className: 'findScope',
|
||||
isWholeLine: true
|
||||
};
|
||||
}
|
||||
|
||||
private addMatchesDecorations(changeAccessor:EditorCommon.IModelDecorationsChangeAccessor, matches:EditorCommon.IEditorRange[]): void {
|
||||
var newDecorations: EditorCommon.IModelDeltaDecoration[] = [];
|
||||
|
||||
var i:number, len:number;
|
||||
for (i = 0, len = matches.length; i < len; i++) {
|
||||
newDecorations[i] = {
|
||||
range: matches[i],
|
||||
options: this.createFindMatchDecorationOptions(false)
|
||||
};
|
||||
}
|
||||
|
||||
this.decorations = changeAccessor.deltaDecorations([], newDecorations);
|
||||
}
|
||||
|
||||
private _getSearchRange(): EditorCommon.IEditorRange {
|
||||
var searchRange:EditorCommon.IEditorRange;
|
||||
private static _getSearchRange(model:EditorCommon.IModel, searchOnlyEditableRange:boolean, findScope:EditorCommon.IEditorRange): EditorCommon.IEditorRange {
|
||||
let searchRange:EditorCommon.IEditorRange;
|
||||
|
||||
if (this.searchOnlyEditableRange) {
|
||||
searchRange = this.editor.getModel().getEditableRange();
|
||||
if (searchOnlyEditableRange) {
|
||||
searchRange = model.getEditableRange();
|
||||
} else {
|
||||
searchRange = this.editor.getModel().getFullModelRange();
|
||||
searchRange = model.getFullModelRange();
|
||||
}
|
||||
|
||||
if (this.hasFindScope()) {
|
||||
// If we have set now or before a find scope, use it for computing the search range
|
||||
searchRange = searchRange.intersectRanges(this.editor.getModel().getDecorationRange(this.findScopeDecorationId));
|
||||
// If we have set now or before a find scope, use it for computing the search range
|
||||
if (findScope) {
|
||||
searchRange = searchRange.intersectRanges(findScope);
|
||||
}
|
||||
|
||||
return searchRange;
|
||||
}
|
||||
|
||||
private updateDecorations(jumpToNextMatch:boolean, resetFindScopeDecoration:boolean, newFindScope:EditorCommon.IEditorRange): void {
|
||||
if (this.didReplace) {
|
||||
this.next();
|
||||
}
|
||||
|
||||
this.editor.changeDecorations((changeAccessor:EditorCommon.IModelDecorationsChangeAccessor) => {
|
||||
this.removeOldDecorations(changeAccessor, resetFindScopeDecoration);
|
||||
|
||||
if (resetFindScopeDecoration && newFindScope) {
|
||||
// Add a decoration to track the find scope
|
||||
let decorations = changeAccessor.deltaDecorations([], [{
|
||||
range: newFindScope,
|
||||
options: this.createFindScopeDecorationOptions()
|
||||
}]);
|
||||
this.findScopeDecorationId = decorations[0];
|
||||
}
|
||||
|
||||
this.addMatchesDecorations(changeAccessor, this._findMatches());
|
||||
});
|
||||
this.highlightedDecorationId = null;
|
||||
|
||||
this.decorationIndex = this.indexAfterPosition(this.startPosition);
|
||||
|
||||
if (!this.didReplace && !jumpToNextMatch) {
|
||||
this.decorationIndex = this.previousIndex(this.decorationIndex);
|
||||
} else if (this.decorations.length > 0) {
|
||||
this.setSelectionToDecoration(this.decorations[this.decorationIndex]);
|
||||
}
|
||||
|
||||
var e:IFindMatchesEvent = {
|
||||
position: this.decorations.length > 0 ? (this.decorationIndex+1) : 0,
|
||||
count: this.decorations.length
|
||||
};
|
||||
|
||||
this._emitMatchesUpdatedEvent(e);
|
||||
|
||||
this.didReplace = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates selection find scope.
|
||||
* Selection find scope just gets removed if passed findScope is null.
|
||||
* Selection find scope does not take columns into account.
|
||||
*/
|
||||
public setFindScope(findScope:EditorCommon.IEditorRange): void {
|
||||
if (findScope === null) {
|
||||
this.updateDecorations(false, true, findScope);
|
||||
private research(moveCursor:boolean, newFindScope?:EditorCommon.IEditorRange): void {
|
||||
let findScope: EditorCommon.IEditorRange = null;
|
||||
if (typeof newFindScope !== 'undefined') {
|
||||
findScope = newFindScope;
|
||||
} else {
|
||||
this.updateDecorations(false, true, new Range(findScope.startLineNumber, 1, findScope.endLineNumber, this.editor.getModel().getLineMaxColumn(findScope.endLineNumber)));
|
||||
findScope = this._decorations.getFindScope();
|
||||
}
|
||||
|
||||
let findMatches = this._findMatches(findScope);
|
||||
this._decorations.set(findMatches, findScope);
|
||||
|
||||
this._state.change({ matchesCount: findMatches.length }, false);
|
||||
|
||||
if (moveCursor) {
|
||||
this._decorations.moveToFirstAfterStartPosition();
|
||||
}
|
||||
}
|
||||
|
||||
public recomputeMatches(newFindData:IFindState, jumpToNextMatch:boolean): void {
|
||||
var somethingChanged = false;
|
||||
if (this.isRegex !== newFindData.properties.isRegex) {
|
||||
this.isRegex = newFindData.properties.isRegex;
|
||||
somethingChanged = true;
|
||||
}
|
||||
if (this.matchCase !== newFindData.properties.matchCase) {
|
||||
this.matchCase = newFindData.properties.matchCase;
|
||||
somethingChanged = true;
|
||||
}
|
||||
if (this.wholeWord !== newFindData.properties.wholeWord) {
|
||||
this.wholeWord = newFindData.properties.wholeWord;
|
||||
somethingChanged = true;
|
||||
}
|
||||
if (newFindData.searchString !== this.searchString) {
|
||||
this.searchString = newFindData.searchString;
|
||||
somethingChanged = true;
|
||||
}
|
||||
this.replaceString = newFindData.replaceString;
|
||||
if (newFindData.isReplaceRevealed !== this.searchOnlyEditableRange) {
|
||||
this.searchOnlyEditableRange = newFindData.isReplaceRevealed;
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
if (somethingChanged) {
|
||||
this.updateDecorations(jumpToNextMatch, false, null);
|
||||
}
|
||||
public moveToPrevMatch(): void {
|
||||
this._decorations.movePrev();
|
||||
}
|
||||
|
||||
public start(newFindData:IFindState, findScope:EditorCommon.IEditorRange, shouldAnimate:boolean): void {
|
||||
this.startPosition = this.editor.getPosition();
|
||||
|
||||
this.isRegex = newFindData.properties.isRegex;
|
||||
this.matchCase = newFindData.properties.matchCase;
|
||||
this.wholeWord = newFindData.properties.wholeWord;
|
||||
this.searchString = newFindData.searchString;
|
||||
this.replaceString = newFindData.replaceString;
|
||||
this.searchOnlyEditableRange = newFindData.isReplaceRevealed;
|
||||
|
||||
this.setFindScope(findScope);
|
||||
this.decorationIndex = this.previousIndex(this.indexAfterPosition(this.startPosition));
|
||||
var e:IFindStartEvent = {
|
||||
state: newFindData,
|
||||
selectionFindEnabled: this.hasFindScope(),
|
||||
shouldAnimate: shouldAnimate
|
||||
};
|
||||
this._emitStartEvent(e);
|
||||
}
|
||||
|
||||
public prev(): void {
|
||||
if (this.decorations.length > 0) {
|
||||
if (this.decorationIndex === -1) {
|
||||
this.decorationIndex = this.indexAfterPosition(this.startPosition);
|
||||
}
|
||||
this.decorationIndex = this.previousIndex(this.decorationIndex);
|
||||
this.setSelectionToDecoration(this.decorations[this.decorationIndex]);
|
||||
} else if (this.hasFindScope()) {
|
||||
// Reveal the selection so user is reminded that 'selection find' is on.
|
||||
this.editor.revealRangeInCenterIfOutsideViewport(this.editor.getModel().getDecorationRange(this.findScopeDecorationId));
|
||||
}
|
||||
}
|
||||
|
||||
public next(): void {
|
||||
if (this.decorations.length > 0) {
|
||||
if (this.decorationIndex === -1) {
|
||||
this.decorationIndex = this.indexAfterPosition(this.startPosition);
|
||||
} else {
|
||||
this.decorationIndex = this.nextIndex(this.decorationIndex);
|
||||
}
|
||||
this.setSelectionToDecoration(this.decorations[this.decorationIndex]);
|
||||
} else if (this.hasFindScope()) {
|
||||
// Reveal the selection so user is reminded that 'selection find' is on.
|
||||
this.editor.revealRangeInCenterIfOutsideViewport(this.editor.getModel().getDecorationRange(this.findScopeDecorationId));
|
||||
}
|
||||
}
|
||||
|
||||
private setSelectionToDecoration(decorationId:string): void {
|
||||
this.editor.changeDecorations((changeAccessor: EditorCommon.IModelDecorationsChangeAccessor) => {
|
||||
if (this.highlightedDecorationId !== null) {
|
||||
changeAccessor.changeDecorationOptions(this.highlightedDecorationId, this.createFindMatchDecorationOptions(false));
|
||||
}
|
||||
changeAccessor.changeDecorationOptions(decorationId, this.createFindMatchDecorationOptions(true));
|
||||
this.highlightedDecorationId = decorationId;
|
||||
});
|
||||
var decorationRange = this.editor.getModel().getDecorationRange(decorationId);
|
||||
if (Range.isIRange(decorationRange)) {
|
||||
this.editor.setSelection(decorationRange);
|
||||
this.editor.revealRangeInCenterIfOutsideViewport(decorationRange);
|
||||
}
|
||||
public moveToNextMatch(): void {
|
||||
this._decorations.moveNext();
|
||||
}
|
||||
|
||||
private getReplaceString(matchedString:string): string {
|
||||
if (!this.isRegex) {
|
||||
return this.replaceString;
|
||||
if (!this._state.isRegex) {
|
||||
return this._state.replaceString;
|
||||
}
|
||||
let regexp = Strings.createRegExp(this.searchString, this.isRegex, this.matchCase, this.wholeWord);
|
||||
let regexp = Strings.createRegExp(this._state.searchString, this._state.isRegex, this._state.matchCase, this._state.wholeWord);
|
||||
// Parse the replace string to support that \t or \n mean the right thing
|
||||
let parsedReplaceString = parseReplaceString(this.replaceString);
|
||||
let parsedReplaceString = parseReplaceString(this._state.replaceString);
|
||||
return matchedString.replace(regexp, parsedReplaceString);
|
||||
}
|
||||
|
||||
public replace(): void {
|
||||
if (this.decorations.length === 0) {
|
||||
if (!this._decorations.hasMatches()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var model = this.editor.getModel();
|
||||
var currentDecorationRange = model.getDecorationRange(this.decorations[this.decorationIndex]);
|
||||
var selection = this.editor.getSelection();
|
||||
let selection = this.editor.getSelection();
|
||||
let selectionText = this.editor.getModel().getValueInRange(selection);
|
||||
let regexp = Strings.createSafeRegExp(this._state.searchString, this._state.isRegex, this._state.matchCase, this._state.wholeWord);
|
||||
let m = selectionText.match(regexp);
|
||||
if (m && m[0].length === selectionText.length) {
|
||||
// selection sits on a find match => replace it!
|
||||
let replaceString = this.getReplaceString(selectionText);
|
||||
|
||||
if (currentDecorationRange !== null &&
|
||||
selection.startColumn === currentDecorationRange.startColumn &&
|
||||
selection.endColumn === currentDecorationRange.endColumn &&
|
||||
selection.startLineNumber === currentDecorationRange.startLineNumber &&
|
||||
selection.endLineNumber === currentDecorationRange.endLineNumber) {
|
||||
let command = new ReplaceCommand(selection, replaceString);
|
||||
|
||||
var matchedString = model.getValueInRange(selection);
|
||||
var replaceString = this.getReplaceString(matchedString);
|
||||
this._executeEditorCommand('replace', command);
|
||||
|
||||
var command = new ReplaceCommand(selection, replaceString);
|
||||
this.editor.executeCommand('replace', command);
|
||||
|
||||
this.startPosition = new Position(selection.startLineNumber, selection.startColumn + replaceString.length);
|
||||
this.decorationIndex = -1;
|
||||
this.didReplace = true;
|
||||
this._decorations.setStartPosition(new Position(selection.startLineNumber, selection.startColumn + replaceString.length));
|
||||
this.research(true);
|
||||
} else {
|
||||
this.next();
|
||||
this._decorations.setStartPosition(this.editor.getPosition());
|
||||
this.moveToNextMatch();
|
||||
}
|
||||
}
|
||||
|
||||
private _findMatches(limitResultCount?:number): EditorCommon.IEditorRange[] {
|
||||
return this.editor.getModel().findMatches(this.searchString, this._getSearchRange(), this.isRegex, this.matchCase, this.wholeWord, limitResultCount);
|
||||
private _findMatches(findScope: EditorCommon.IEditorRange, limitResultCount?:number): EditorCommon.IEditorRange[] {
|
||||
let searchRange = FindModelBoundToEditorModel._getSearchRange(this.editor.getModel(), this._state.isReplaceRevealed, findScope);
|
||||
return this.editor.getModel().findMatches(this._state.searchString, searchRange, this._state.isRegex, this._state.matchCase, this._state.wholeWord, limitResultCount);
|
||||
}
|
||||
|
||||
public replaceAll(): void {
|
||||
if (this.decorations.length === 0) {
|
||||
if (!this._decorations.hasMatches()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let model = this.editor.getModel();
|
||||
let findScope = this._decorations.getFindScope();
|
||||
|
||||
// Get all the ranges (even more than the highlighted ones)
|
||||
let ranges = this._findMatches(Number.MAX_VALUE);
|
||||
let ranges = this._findMatches(findScope, Number.MAX_VALUE);
|
||||
|
||||
// Remove all decorations
|
||||
this.editor.changeDecorations((changeAccessor:EditorCommon.IModelDecorationsChangeAccessor) => {
|
||||
this.removeOldDecorations(changeAccessor, false);
|
||||
});
|
||||
this._decorations.set([], findScope);
|
||||
|
||||
var replaceStrings:string[] = [];
|
||||
for (var i = 0, len = ranges.length; i < len; i++) {
|
||||
let replaceStrings:string[] = [];
|
||||
for (let i = 0, len = ranges.length; i < len; i++) {
|
||||
replaceStrings.push(this.getReplaceString(model.getValueInRange(ranges[i])));
|
||||
}
|
||||
|
||||
var command = new ReplaceAllCommand.ReplaceAllCommand(ranges, replaceStrings);
|
||||
this.editor.executeCommand('replaceAll', command);
|
||||
let command = new ReplaceAllCommand.ReplaceAllCommand(ranges, replaceStrings);
|
||||
this._executeEditorCommand('replaceAll', command);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
this.updateDecorationsScheduler.dispose();
|
||||
this.listenersToRemove.forEach((element) => {
|
||||
element();
|
||||
});
|
||||
this.listenersToRemove = [];
|
||||
if (this.editor.getModel()) {
|
||||
this.editor.changeDecorations((changeAccessor:EditorCommon.IModelDecorationsChangeAccessor) => {
|
||||
this.removeOldDecorations(changeAccessor, true);
|
||||
});
|
||||
private _executeEditorCommand(source:string, command:EditorCommon.ICommand): void {
|
||||
try {
|
||||
this._ignoreModelContentChanged = true;
|
||||
this.editor.executeCommand(source, command);
|
||||
} finally {
|
||||
this._ignoreModelContentChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
public hasFindScope(): boolean {
|
||||
return !!this.findScopeDecorationId;
|
||||
}
|
||||
|
||||
private previousIndex(index:number): number {
|
||||
if (this.decorations.length > 0) {
|
||||
return (index - 1 + this.decorations.length) % this.decorations.length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private nextIndex(index:number): number {
|
||||
if (this.decorations.length > 0) {
|
||||
return (index + 1) % this.decorations.length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private indexAfterPosition(position:EditorCommon.IEditorPosition): number {
|
||||
if (this.decorations.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
for (var i = 0, len = this.decorations.length; i < len; i++) {
|
||||
var decorationId = this.decorations[i];
|
||||
var r = this.editor.getModel().getDecorationRange(decorationId);
|
||||
if (!r || r.startLineNumber < position.lineNumber) {
|
||||
continue;
|
||||
}
|
||||
if (r.startLineNumber > position.lineNumber) {
|
||||
return i;
|
||||
}
|
||||
if (r.startColumn < position.column) {
|
||||
continue;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public addStartEventListener(callback:(e:IFindStartEvent)=>void): Lifecycle.IDisposable {
|
||||
return this.addListener2(FindModelBoundToEditorModel._START_EVENT, callback);
|
||||
}
|
||||
|
||||
private _emitStartEvent(e:IFindStartEvent): void {
|
||||
this.emit(FindModelBoundToEditorModel._START_EVENT, e);
|
||||
}
|
||||
|
||||
public addMatchesUpdatedEventListener(callback:(e:IFindMatchesEvent)=>void): Lifecycle.IDisposable {
|
||||
return this.addListener2(FindModelBoundToEditorModel._MATCHES_UPDATED_EVENT, callback);
|
||||
}
|
||||
|
||||
private _emitMatchesUpdatedEvent(e:IFindMatchesEvent): void {
|
||||
this.emit(FindModelBoundToEditorModel._MATCHES_UPDATED_EVENT, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const BACKSLASH_CHAR_CODE = '\\'.charCodeAt(0);
|
||||
|
||||
167
src/vs/editor/contrib/find/common/findState.ts
Normal file
167
src/vs/editor/contrib/find/common/findState.ts
Normal file
@@ -0,0 +1,167 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as EditorCommon from 'vs/editor/common/editorCommon';
|
||||
import {EventEmitter} from 'vs/base/common/eventEmitter';
|
||||
import {IDisposable} from 'vs/base/common/lifecycle';
|
||||
import {Range} from 'vs/editor/common/core/range';
|
||||
|
||||
export interface FindReplaceStateChangedEvent {
|
||||
moveCursor: boolean;
|
||||
|
||||
searchString: boolean;
|
||||
replaceString: boolean;
|
||||
isRevealed: boolean;
|
||||
isReplaceRevealed: boolean;
|
||||
isRegex: boolean;
|
||||
wholeWord: boolean;
|
||||
matchCase: boolean;
|
||||
searchScope: boolean;
|
||||
matchesCount: boolean;
|
||||
}
|
||||
|
||||
export interface INewFindReplaceState {
|
||||
searchString?: string;
|
||||
replaceString?: string;
|
||||
isRevealed?: boolean;
|
||||
isReplaceRevealed?: boolean;
|
||||
isRegex?: boolean;
|
||||
wholeWord?: boolean;
|
||||
matchCase?: boolean;
|
||||
searchScope?: EditorCommon.IEditorRange;
|
||||
matchesCount?: number;
|
||||
}
|
||||
|
||||
export class FindReplaceState implements IDisposable {
|
||||
|
||||
private static _CHANGED_EVENT = 'changed';
|
||||
|
||||
private _searchString: string;
|
||||
private _replaceString: string;
|
||||
private _isRevealed: boolean;
|
||||
private _isReplaceRevealed: boolean;
|
||||
private _isRegex: boolean;
|
||||
private _wholeWord: boolean;
|
||||
private _matchCase: boolean;
|
||||
private _searchScope: EditorCommon.IEditorRange;
|
||||
private _matchesCount: number;
|
||||
private _eventEmitter: EventEmitter;
|
||||
|
||||
public get searchString(): string { return this._searchString; }
|
||||
public get replaceString(): string { return this._replaceString; }
|
||||
public get isRevealed(): boolean { return this._isRevealed; }
|
||||
public get isReplaceRevealed(): boolean { return this._isReplaceRevealed; }
|
||||
public get isRegex(): boolean { return this._isRegex; }
|
||||
public get wholeWord(): boolean { return this._wholeWord; }
|
||||
public get matchCase(): boolean { return this._matchCase; }
|
||||
public get searchScope(): EditorCommon.IEditorRange { return this._searchScope; }
|
||||
public get matchesCount(): number { return this._matchesCount; }
|
||||
|
||||
constructor() {
|
||||
this._searchString = '';
|
||||
this._replaceString = '';
|
||||
this._isRevealed = false;
|
||||
this._isReplaceRevealed = false;
|
||||
this._isRegex = false;
|
||||
this._wholeWord = false;
|
||||
this._matchCase = false;
|
||||
this._searchScope = null;
|
||||
this._matchesCount = 0;
|
||||
this._eventEmitter = new EventEmitter();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._eventEmitter.dispose();
|
||||
}
|
||||
|
||||
public addChangeListener(listener:(e:FindReplaceStateChangedEvent)=>void): IDisposable {
|
||||
return this._eventEmitter.addListener2(FindReplaceState._CHANGED_EVENT, listener);
|
||||
}
|
||||
|
||||
public change(newState:INewFindReplaceState, moveCursor:boolean): void {
|
||||
let changeEvent:FindReplaceStateChangedEvent = {
|
||||
moveCursor: moveCursor,
|
||||
searchString: false,
|
||||
replaceString: false,
|
||||
isRevealed: false,
|
||||
isReplaceRevealed: false,
|
||||
isRegex: false,
|
||||
wholeWord: false,
|
||||
matchCase: false,
|
||||
searchScope: false,
|
||||
matchesCount: false
|
||||
};
|
||||
let somethingChanged = false;
|
||||
|
||||
if (typeof newState.searchString !== 'undefined') {
|
||||
if (this._searchString !== newState.searchString) {
|
||||
this._searchString = newState.searchString;
|
||||
changeEvent.searchString = true;
|
||||
somethingChanged = true;
|
||||
}
|
||||
}
|
||||
if (typeof newState.replaceString !== 'undefined') {
|
||||
if (this._replaceString !== newState.replaceString) {
|
||||
this._replaceString = newState.replaceString;
|
||||
changeEvent.replaceString = true;
|
||||
somethingChanged = true;
|
||||
}
|
||||
}
|
||||
if (typeof newState.isRevealed !== 'undefined') {
|
||||
if (this._isRevealed !== newState.isRevealed) {
|
||||
this._isRevealed = newState.isRevealed;
|
||||
changeEvent.isRevealed = true;
|
||||
somethingChanged = true;
|
||||
}
|
||||
}
|
||||
if (typeof newState.isReplaceRevealed !== 'undefined') {
|
||||
if (this._isReplaceRevealed !== newState.isReplaceRevealed) {
|
||||
this._isReplaceRevealed = newState.isReplaceRevealed;
|
||||
changeEvent.isReplaceRevealed = true;
|
||||
somethingChanged = true;
|
||||
}
|
||||
}
|
||||
if (typeof newState.isRegex !== 'undefined') {
|
||||
if (this._isRegex !== newState.isRegex) {
|
||||
this._isRegex = newState.isRegex;
|
||||
changeEvent.isRegex = true;
|
||||
somethingChanged = true;
|
||||
}
|
||||
}
|
||||
if (typeof newState.wholeWord !== 'undefined') {
|
||||
if (this._wholeWord !== newState.wholeWord) {
|
||||
this._wholeWord = newState.wholeWord;
|
||||
changeEvent.wholeWord = true;
|
||||
somethingChanged = true;
|
||||
}
|
||||
}
|
||||
if (typeof newState.matchCase !== 'undefined') {
|
||||
if (this._matchCase !== newState.matchCase) {
|
||||
this._matchCase = newState.matchCase;
|
||||
changeEvent.matchCase = true;
|
||||
somethingChanged = true;
|
||||
}
|
||||
}
|
||||
if (typeof newState.searchScope !== 'undefined') {
|
||||
if (!Range.equalsRange(this._searchScope, newState.searchScope)) {
|
||||
this._searchScope = newState.searchScope;
|
||||
changeEvent.searchScope = true;
|
||||
somethingChanged = true;
|
||||
}
|
||||
}
|
||||
if (typeof newState.matchesCount !== 'undefined') {
|
||||
if (this._matchesCount !== newState.matchesCount) {
|
||||
this._matchesCount = newState.matchesCount;
|
||||
changeEvent.matchesCount = true;
|
||||
somethingChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (somethingChanged) {
|
||||
this._eventEmitter.emit(FindReplaceState._CHANGED_EVENT, changeEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
5
src/vs/vscode.d.ts
vendored
5
src/vs/vscode.d.ts
vendored
@@ -3394,10 +3394,11 @@ declare namespace vscode {
|
||||
}
|
||||
}
|
||||
|
||||
// [javascript start marker]
|
||||
// DO NOT MODIFY ABOVE COMMENT
|
||||
// TS 1.6 & node_module
|
||||
// export = vscode;
|
||||
|
||||
// when used for JS*
|
||||
// !!! DO NOT MODIFY ABOVE COMMENT ("when used for JS*") IT IS BEING USED TO DETECT JS* ONLY CHANGES !!!
|
||||
declare module 'vscode' {
|
||||
export = vscode;
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
|
||||
.monaco-workbench .viewlet .collapsible.header .action-label {
|
||||
margin-right: 0.2em;
|
||||
margin-top: 4px;
|
||||
margin-top: 3px;
|
||||
background-repeat: no-repeat;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
|
||||
@@ -34,8 +34,8 @@
|
||||
}
|
||||
|
||||
.explorer-viewlet .monaco-count-badge {
|
||||
padding: 0 6px;
|
||||
display: inline-block;
|
||||
padding: 1px 6px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.explorer-viewlet .explorer-empty-view {
|
||||
|
||||
@@ -9,9 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { Url, parse as parseUrl } from 'url';
|
||||
import { request, IRequestOptions } from 'vs/base/node/request';
|
||||
|
||||
import HttpProxyAgent = require('http-proxy-agent');
|
||||
import HttpsProxyAgent = require('https-proxy-agent');
|
||||
import { getProxyAgent } from 'vs/workbench/node/proxy';
|
||||
|
||||
export interface IXHROptions extends IRequestOptions {
|
||||
responseType?: string;
|
||||
@@ -23,41 +21,18 @@ export interface IXHRResponse {
|
||||
status: number;
|
||||
}
|
||||
|
||||
let proxyConfiguration: string = null;
|
||||
let proxyUrl: string = null;
|
||||
let strictSSL: boolean = true;
|
||||
|
||||
export function configure(proxyURI: string): void {
|
||||
proxyConfiguration = proxyURI;
|
||||
}
|
||||
|
||||
function getProxyURI(uri: Url): string {
|
||||
let proxyURI = proxyConfiguration;
|
||||
if (!proxyURI) {
|
||||
if (uri.protocol === 'http:') {
|
||||
proxyURI = process.env.HTTP_PROXY || process.env.http_proxy || null;
|
||||
} else if (uri.protocol === 'https:') {
|
||||
proxyURI = process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy || null;
|
||||
}
|
||||
}
|
||||
return proxyURI;
|
||||
}
|
||||
|
||||
function getProxyAgent(uri: Url): any {
|
||||
let proxyURI = getProxyURI(uri);
|
||||
if (proxyURI) {
|
||||
let proxyEndpoint = parseUrl(proxyURI);
|
||||
switch (proxyEndpoint.protocol) {
|
||||
case 'http:':
|
||||
case 'https:':
|
||||
return uri.protocol === 'http:' ? new HttpProxyAgent(proxyURI) : new HttpsProxyAgent(proxyURI);
|
||||
}
|
||||
}
|
||||
return void 0;
|
||||
export function configure(_proxyUrl: string, _strictSSL: boolean): void {
|
||||
proxyUrl = _proxyUrl;
|
||||
strictSSL = _strictSSL;
|
||||
}
|
||||
|
||||
export function xhr(options: IXHROptions): TPromise<IXHRResponse> {
|
||||
let endpoint = parseUrl(options.url);
|
||||
const agent = getProxyAgent(options.url, { proxyUrl, strictSSL });
|
||||
options = assign({}, options);
|
||||
options = assign(options, { agent: getProxyAgent(endpoint) });
|
||||
options = assign(options, { agent });
|
||||
|
||||
return request(options).then(result => new TPromise<IXHRResponse>((c, e, p) => {
|
||||
let res = result.res;
|
||||
@@ -88,12 +63,10 @@ export function xhr(options: IXHROptions): TPromise<IXHRResponse> {
|
||||
}
|
||||
});
|
||||
}, err => {
|
||||
let endpoint = parseUrl(options.url);
|
||||
let agent = getProxyAgent(endpoint);
|
||||
let message: string;
|
||||
|
||||
if (agent) {
|
||||
message = 'Unable to to connect to ' + options.url + ' through proxy ' + getProxyURI(endpoint) + '. Error: ' + err.message;
|
||||
message = 'Unable to to connect to ' + options.url + ' through a proxy . Error: ' + err.message;
|
||||
} else {
|
||||
message = 'Unable to to connect to ' + options.url + '. Error: ' + err.message;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
|
||||
|
||||
interface IRawHttpService {
|
||||
xhr(options: http.IXHROptions): TPromise<http.IXHRResponse>;
|
||||
configure(proxy: string): void;
|
||||
configure(proxy: string, strictSSL: boolean): void;
|
||||
}
|
||||
|
||||
interface IXHRFunction {
|
||||
@@ -44,7 +44,7 @@ export class RequestService extends BaseRequestService implements IThreadSynchro
|
||||
// proxy setting updating
|
||||
this.callOnDispose.push(configurationService.addListener(ConfigurationServiceEventTypes.UPDATED, (e: IConfigurationServiceEvent) => {
|
||||
this.rawHttpServicePromise.then((rawHttpService) => {
|
||||
rawHttpService.configure(e.config.http && e.config.http.proxy);
|
||||
rawHttpService.configure(e.config.http && e.config.http.proxy, e.config.http.proxyStrictSSL);
|
||||
});
|
||||
}));
|
||||
}
|
||||
@@ -53,7 +53,7 @@ export class RequestService extends BaseRequestService implements IThreadSynchro
|
||||
private get rawHttpServicePromise(): TPromise<IRawHttpService> {
|
||||
if (!this._rawHttpServicePromise) {
|
||||
this._rawHttpServicePromise = this.configurationService.loadConfiguration().then((configuration: any) => {
|
||||
rawHttpService.configure(configuration.http && configuration.http.proxy);
|
||||
rawHttpService.configure(configuration.http && configuration.http.proxy, configuration.http.proxyStrictSSL);
|
||||
return rawHttpService;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user