diff --git a/extensions/bat/syntaxes/Batch File.tmLanguage b/extensions/bat/syntaxes/Batch File.tmLanguage
index 2a7752e5cfc..0554fa176d3 100644
--- a/extensions/bat/syntaxes/Batch File.tmLanguage
+++ b/extensions/bat/syntaxes/Batch File.tmLanguage
@@ -10,7 +10,7 @@
name
keyword.command.dosbatch
match
- \b(?i)(?:append|assoc|at|attrib|break|cacls|cd|chcp|chdir|chkdsk|chkntfs|cls|cmd|color|comp|compact|convert|copy|date|del|dir|diskcomp|diskcopy|doskey|echo|endlocal|erase|fc|find|findstr|format|ftype|graftabl|help|keyb|label|md|mkdir|mode|more|move|path|pause|popd|print|prompt|pushd|rd|recover|rem|ren|rename|replace|restore|rmdir|set|setlocal|shift|sort|start|subst|time|title|tree|type|ver|verify|vol|xcopy)\b
+ \b(?i)(?:append|assoc|at|attrib|break|cacls|cd|chcp|chdir|chkdsk|chkntfs|cls|cmd|color|comp|compact|convert|copy|date|del|dir|diskcomp|diskcopy|doskey|echo|endlocal|erase|fc|find|findstr|format|ftype|graftabl|help|keyb|label|md|mkdir|mode|more|move|path|pause|popd|print|prompt|pushd|rd|recover|ren|rename|replace|restore|rmdir|set|setlocal|shift|sort|start|subst|time|title|tree|type|ver|verify|vol|xcopy)\b
name
@@ -54,7 +54,7 @@
name
comment.line.rem.dosbatch
match
- (?:^|\s)((?i)rem)(?:$|\s.*$)
+ \b((?i)rem)(?:$|\s.*$)
name
@@ -62,6 +62,72 @@
match
\s*:\s*:.*$
+
+ captures
+
+ 1
+
+ name
+ variable.parameter.function.begin.shell
+
+
+ name
+ variable.parameter.function.dosbatch
+ match
+ (?i)(%)(~(?:f|d|p|n|x|s|a|t|z|\$[^:]*:)*)?\d
+
+
+ captures
+
+ 1
+
+ name
+ variable.parameter.loop.begin.shell
+
+
+ name
+ variable.parameter.loop.dosbatch
+ match
+ (?i)(%%)(~(?:f|d|p|n|x|s|a|t|z|\$[^:]*:)*)?[a-z]
+
+
+ captures
+
+ 1
+
+ name
+ variable.other.parsetime.begin.shell
+
+ 2
+
+ name
+ variable.other.parsetime.end.shell
+
+
+ name
+ variable.other.parsetime.dosbatch
+ match
+ (%)[^%]+(%)
+
+
+ captures
+
+ 1
+
+ name
+ variable.other.delayed.begin.shell
+
+ 2
+
+ name
+ variable.other.delayed.end.shell
+
+
+ name
+ variable.other.delayed.dosbatch
+ match
+ (!)[^!]+(!)
+
begin
"
@@ -84,7 +150,7 @@
name
string.quoted.double.dosbatch
end
- "
+ "|$
name
diff --git a/extensions/php/src/features/completionItemProvider.ts b/extensions/php/src/features/completionItemProvider.ts
index 6eb3b0953db..426649fb314 100644
--- a/extensions/php/src/features/completionItemProvider.ts
+++ b/extensions/php/src/features/completionItemProvider.ts
@@ -14,6 +14,8 @@ export default class PHPCompletionItemProvider implements CompletionItemProvider
public provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken): Promise {
let result: CompletionItem[] = [];
+ var range = document.getWordRangeAtPosition(position);
+ var prefix = range ? document.getText(range) : '';
var added : any = {};
var createNewProposal = function(kind: CompletionItemKind, name: string, entry: phpGlobals.IEntry) : CompletionItem {
@@ -30,39 +32,45 @@ export default class PHPCompletionItemProvider implements CompletionItemProvider
return proposal;
};
+ var matches = (name:string) => {
+ return prefix.length === 0 || name.length > prefix.length && name.substr(0, prefix.length) === prefix;
+ }
+
for (var name in phpGlobals.globalvariables) {
- if (phpGlobals.globalvariables.hasOwnProperty(name)) {
+ if (phpGlobals.globalvariables.hasOwnProperty(name) && matches(name)) {
added[name] = true;
result.push(createNewProposal(CompletionItemKind.Variable, name, phpGlobals.globalvariables[name]));
}
}
for (var name in phpGlobals.globalfunctions) {
- if (phpGlobals.globalfunctions.hasOwnProperty(name)) {
+ if (phpGlobals.globalfunctions.hasOwnProperty(name) && matches(name)) {
added[name] = true;
result.push(createNewProposal(CompletionItemKind.Function, name, phpGlobals.globalfunctions[name]));
}
}
for (var name in phpGlobals.compiletimeconstants) {
- if (phpGlobals.compiletimeconstants.hasOwnProperty(name)) {
+ if (phpGlobals.compiletimeconstants.hasOwnProperty(name) && matches(name)) {
added[name] = true;
result.push(createNewProposal(CompletionItemKind.Field, name, phpGlobals.compiletimeconstants[name]));
}
}
for (var name in phpGlobals.keywords) {
- if (phpGlobals.keywords.hasOwnProperty(name)) {
+ if (phpGlobals.keywords.hasOwnProperty(name) && matches(name)) {
added[name] = true;
result.push(createNewProposal(CompletionItemKind.Keyword, name, phpGlobals.keywords[name]));
}
}
var text = document.getText();
- var variableMatch = /\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/g;
- var match : RegExpExecArray = null;
- while (match = variableMatch.exec(text)) {
- var word = match[0];
- if (!added[word]) {
- added[word] = true;
- result.push(createNewProposal(CompletionItemKind.Variable, word, null));
+ if (matches('$')) {
+ var variableMatch = /\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/g;
+ var match : RegExpExecArray = null;
+ while (match = variableMatch.exec(text)) {
+ var word = match[0];
+ if (!added[word]) {
+ added[word] = true;
+ result.push(createNewProposal(CompletionItemKind.Variable, word, null));
+ }
}
}
var functionMatch = /function\s+([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*\(/g;
diff --git a/extensions/theme-colorful-defaults/package.json b/extensions/theme-colorful-defaults/package.json
new file mode 100644
index 00000000000..a090258fecb
--- /dev/null
+++ b/extensions/theme-colorful-defaults/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "theme-colorful-defaults",
+ "displayName": "Colorful Default Themes - Please provide feedback in issue 1849",
+ "description": "The default VS Code Light and Dark themes with a touch of color. We are considering adding these to the default themes in the January release. Please provide feedback in issue 1849.",
+ "categories": [ "Themes" ],
+ "version": "0.1.6",
+ "publisher": "aeschli",
+ "engines": { "vscode": "*" },
+ "contributes": {
+ "themes": [
+ {
+ "label": "Dark+",
+ "description": "Default dark theme with a touch of color",
+ "uiTheme": "vs-dark",
+ "path": "./themes/dark_plus.tmTheme"
+ },
+ {
+ "label": "Light+",
+ "description": "Default light theme with a touch of color",
+ "uiTheme": "vs",
+ "path": "./themes/light_plus.tmTheme"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/extensions/theme-dark-plus/themes/dark_plus.tmTheme b/extensions/theme-colorful-defaults/themes/dark_plus.tmTheme
similarity index 81%
rename from extensions/theme-dark-plus/themes/dark_plus.tmTheme
rename to extensions/theme-colorful-defaults/themes/dark_plus.tmTheme
index 11c83ebed50..e47345be8a6 100644
--- a/extensions/theme-dark-plus/themes/dark_plus.tmTheme
+++ b/extensions/theme-colorful-defaults/themes/dark_plus.tmTheme
@@ -10,7 +10,7 @@
name
Function declarations
scope
- entity.name.function
+ entity.name.function, entity.method.name
settings
foreground
@@ -21,7 +21,7 @@
name
Types declaration and references
scope
- meta.parameter.type, entity.name.class, new.storage.type, meta.cast, cast.storage.type, heritage.storage.type, annotation.storage.type, var.annotation.storage.type
+ meta.parameter.type, name.class, storage.type.cs, return-type, new.storage.type, meta.cast, cast.storage.type, heritage.storage.type, annotation.storage.type, var.annotation.storage.type
settings
foreground
diff --git a/extensions/theme-dark-plus/themes/light_plus.tmTheme b/extensions/theme-colorful-defaults/themes/light_plus.tmTheme
similarity index 80%
rename from extensions/theme-dark-plus/themes/light_plus.tmTheme
rename to extensions/theme-colorful-defaults/themes/light_plus.tmTheme
index bcfd3eb590a..5881cb3060e 100644
--- a/extensions/theme-dark-plus/themes/light_plus.tmTheme
+++ b/extensions/theme-colorful-defaults/themes/light_plus.tmTheme
@@ -10,7 +10,7 @@
name
Function declarations
scope
- entity.name.function
+ entity.name.function, entity.method.name
settings
foreground
@@ -21,7 +21,7 @@
name
Types declaration and references
scope
- meta.parameter.type, entity.name.class, new.storage.type, meta.cast, cast.storage.type, heritage.storage.type, annotation.storage.type, var.annotation.storage.type
+ meta.parameter.type, name.class, storage.type.cs, return-type, new.storage.type, meta.cast, cast.storage.type, heritage.storage.type, annotation.storage.type, var.annotation.storage.type settings
settings
foreground
diff --git a/extensions/theme-dark-plus/package.json b/extensions/theme-dark-plus/package.json
deleted file mode 100644
index 83d805bda92..00000000000
--- a/extensions/theme-dark-plus/package.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "name": "theme-dark-plus",
- "version": "0.1.0",
- "publisher": "vscode",
- "engines": { "vscode": "*" },
- "contributes": {
- "themes": [
- {
- "label": "Dark+",
- "description": "Dark theme with a touch of color",
- "uiTheme": "vs-dark",
- "path": "./themes/dark_plus.tmTheme"
- },
- {
- "label": "Light+",
- "description": "Light theme with a touch of color",
- "uiTheme": "vs",
- "path": "./themes/light_plus.tmTheme"
- }
- ]
- }
-}
\ No newline at end of file
diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts
index 46f4eb2a2b4..2a16714404e 100644
--- a/src/vs/base/browser/dom.ts
+++ b/src/vs/base/browser/dom.ts
@@ -919,8 +919,13 @@ export var EventType = {
ANIMATION_ITERATION: Browser.isWebKit ? 'webkitAnimationIteration' : 'animationiteration'
};
+export interface EventLike {
+ preventDefault(): void;
+ stopPropagation(): void;
+}
+
export var EventHelper = {
- stop: function (e:Event, cancelBubble?:boolean) {
+ stop: function (e:EventLike, cancelBubble?:boolean) {
if (e.preventDefault) {
e.preventDefault();
} else {
@@ -933,7 +938,7 @@ export var EventHelper = {
e.stopPropagation();
} else {
// IE8
- e.cancelBubble = true;
+ (e).cancelBubble = true;
}
}
}
diff --git a/src/vs/base/browser/ui/checkbox/checkbox.ts b/src/vs/base/browser/ui/checkbox/checkbox.ts
index c017267126b..e197d40836d 100644
--- a/src/vs/base/browser/ui/checkbox/checkbox.ts
+++ b/src/vs/base/browser/ui/checkbox/checkbox.ts
@@ -6,77 +6,78 @@
'use strict';
import 'vs/css!./checkbox';
-import nls = require('vs/nls');
-import Builder = require('vs/base/browser/builder');
-import mouse = require('vs/base/browser/mouseEvent');
-import keyboard = require('vs/base/browser/keyboardEvent');
+
+import * as nls from 'vs/nls';
import {KeyCode} from 'vs/base/common/keyCodes';
+import {Widget} from 'vs/base/browser/ui/widget';
+import {StandardKeyboardEvent} from 'vs/base/browser/keyboardEvent';
-var $ = Builder.$;
+export interface ICheckboxOpts {
+ actionClassName: string;
+ title: string;
+ isChecked: boolean;
+ onChange: () => void;
+ onKeyDown?: (e:StandardKeyboardEvent) => void;
+}
-export class Checkbox {
+export class Checkbox extends Widget {
- private actionClassName: string;
- private title: string;
- public isChecked: boolean;
- private onChange: () => void;
- private listenersToRemove: { (): void; }[];
+ private _opts: ICheckboxOpts;
public domNode: HTMLElement;
- constructor(actionClassName: string, title: string, isChecked: boolean, onChange: () => void) {
- this.actionClassName = actionClassName;
- this.title = title;
- this.isChecked = isChecked;
- this.onChange = onChange;
+ private _checked: boolean;
- this.listenersToRemove = [];
+ constructor(opts:ICheckboxOpts) {
+ super();
+ this._opts = opts;
+ this._checked = this._opts.isChecked;
this.domNode = document.createElement('div');
- this.domNode.title = title;
- this.render();
+ this.domNode.title = this._opts.title;
+ this.domNode.className = this._className();
+ this.domNode.tabIndex = 0;
+ this.domNode.setAttribute('role', 'checkbox');
+ this.domNode.setAttribute('aria-checked', String(this._checked));
+ this.domNode.setAttribute('aria-label', this._opts.title);
- $(this.domNode).attr({
- 'aria-checked': 'false',
- 'aria-label': this.title,
- 'tabindex': 0,
- 'role': 'checkbox'
+ this.onclick(this.domNode, (ev) => {
+ this._checked = !this._checked;
+ this.domNode.className = this._className();
+ this._opts.onChange();
+ ev.preventDefault();
});
- $(this.domNode).on('click', (e: MouseEvent) => {
- var ev = new mouse.StandardMouseEvent(e);
- this.isChecked = !this.isChecked;
- this.render();
- this.onChange();
- ev.preventDefault();
- }, this.listenersToRemove);
-
- $(this.domNode).on('keydown', (browserEvent: KeyboardEvent) => {
- var keyboardEvent = new keyboard.StandardKeyboardEvent(browserEvent);
+ this.onkeydown(this.domNode, (keyboardEvent) => {
if (keyboardEvent.keyCode === KeyCode.Space || keyboardEvent.keyCode === KeyCode.Enter) {
- this.isChecked = !this.isChecked;
- this.render();
- this.onChange();
+ this._checked = !this._checked;
+ this.domNode.className = this._className();
+ this._opts.onChange();
keyboardEvent.preventDefault();
+ return;
}
- }, this.listenersToRemove);
+
+ if (this._opts.onKeyDown) {
+ this._opts.onKeyDown(keyboardEvent);
+ }
+ });
}
public focus(): void {
this.domNode.focus();
}
- private render(): void {
- this.domNode.className = this.className();
+ public get checked(): boolean {
+ return this._checked;
}
- public setChecked(newIsChecked: boolean): void {
- this.isChecked = newIsChecked;
- $(this.domNode).attr('aria-checked', this.isChecked);
- this.render();
+ public set checked(newIsChecked:boolean) {
+ this._checked = newIsChecked;
+ this.domNode.setAttribute('aria-checked', String(this._checked));
+ this.domNode.className = this._className();
}
- private className(): string {
- return 'custom-checkbox ' + this.actionClassName + ' ' + (this.isChecked ? 'checked' : 'unchecked');
+ private _className(): string {
+ return 'custom-checkbox ' + this._opts.actionClassName + ' ' + (this._checked ? 'checked' : 'unchecked');
}
public width(): number {
@@ -85,16 +86,11 @@ export class Checkbox {
public enable(): void {
this.domNode.tabIndex = 0;
+ this.domNode.setAttribute('aria-disabled', String(false));
}
public disable(): void {
this.domNode.tabIndex = -1;
+ this.domNode.setAttribute('aria-disabled', String(true));
}
-
- public destroy(): void {
- this.listenersToRemove.forEach((element) => {
- element();
- });
- this.listenersToRemove = [];
- }
-}
\ No newline at end of file
+}
diff --git a/src/vs/base/browser/ui/countBadge/countBadge.css b/src/vs/base/browser/ui/countBadge/countBadge.css
index c44ae4e8f00..c08164258ec 100644
--- a/src/vs/base/browser/ui/countBadge/countBadge.css
+++ b/src/vs/base/browser/ui/countBadge/countBadge.css
@@ -4,16 +4,14 @@
*--------------------------------------------------------------------------------------------*/
.monaco-count-badge {
- min-width: 18px;
- padding: 0 3px;
- margin: 4px 13px 0 6px;
- border-radius: 8px;
- font-size: 11px;
- line-height: 16px;
+ padding: 0.2em 0.4em;
+ border-radius: 1em;
+ font-size: 85%;
font-weight: normal;
text-align: center;
background: #BEBEBE;
color: #FFF;
+ display: inline;
}
.vs-dark .monaco-count-badge {
diff --git a/src/vs/base/browser/ui/findinput/findInput.ts b/src/vs/base/browser/ui/findinput/findInput.ts
index e05585ac8df..0c82b7d3142 100644
--- a/src/vs/base/browser/ui/findinput/findInput.ts
+++ b/src/vs/base/browser/ui/findinput/findInput.ts
@@ -5,20 +5,20 @@
'use strict';
import 'vs/css!./findInput';
-import nls = require('vs/nls');
-import Builder = require('vs/base/browser/builder');
-import mouse = require('vs/base/browser/mouseEvent');
-import keyboard = require('vs/base/browser/keyboardEvent');
-import InputBox = require('vs/base/browser/ui/inputbox/inputBox');
-import Checkbox = require('vs/base/browser/ui/checkbox/checkbox');
-import ContextView = require('vs/base/browser/ui/contextview/contextview');
-var $ = Builder.$;
+import * as nls from 'vs/nls';
+import * as dom from 'vs/base/browser/dom';
+import {IMessage as InputBoxMessage, IInputValidator, InputBox} from 'vs/base/browser/ui/inputbox/inputBox';
+import {Checkbox} from 'vs/base/browser/ui/checkbox/checkbox';
+import {IContextViewProvider} from 'vs/base/browser/ui/contextview/contextview';
+import {Widget} from 'vs/base/browser/ui/widget';
+import Event, {Emitter} from 'vs/base/common/event';
+import {StandardKeyboardEvent} from 'vs/base/browser/keyboardEvent';
-export interface IOptions {
+export interface IFindInputOptions {
placeholder?:string;
width?:number;
- validation?:InputBox.IInputValidator;
+ validation?:IInputValidator;
label:string;
appendCaseSensitiveLabel?: string;
@@ -31,75 +31,60 @@ const NLS_WHOLE_WORD_CHECKBOX_LABEL = nls.localize('wordsDescription', "Match Wh
const NLS_CASE_SENSITIVE_CHECKBOX_LABEL = nls.localize('caseDescription', "Match Case");
const NLS_DEFAULT_LABEL = nls.localize('defaultLabel', "input");
-export class FindInput {
+export class FindInput extends Widget {
static OPTION_CHANGE:string = 'optionChange';
- private contextViewProvider: ContextView.IContextViewProvider;
- private onOptionChange:(event:Event)=>void;
+ private contextViewProvider: IContextViewProvider;
private width:number;
private placeholder:string;
- private validation:InputBox.IInputValidator;
+ private validation:IInputValidator;
private label:string;
- private listenersToRemove:{():void;}[];
- private regex:Checkbox.Checkbox;
- private wholeWords:Checkbox.Checkbox;
- private caseSensitive:Checkbox.Checkbox;
+ private regex:Checkbox;
+ private wholeWords:Checkbox;
+ private caseSensitive:Checkbox;
public domNode: HTMLElement;
- public validationNode: Builder.Builder;
- private inputNode:HTMLInputElement;
- private inputBox:InputBox.InputBox;
+ public inputBox:InputBox;
- constructor(parent:HTMLElement, contextViewProvider: ContextView.IContextViewProvider, options?:IOptions) {
+ private _onDidOptionChange = this._register(new Emitter());
+ public onDidOptionChange: Event = this._onDidOptionChange.event;
+
+ private _onKeyDown = this._register(new Emitter());
+ public onKeyDown: Event = this._onKeyDown.event;
+
+ private _onKeyUp = this._register(new Emitter());
+ public onKeyUp: Event = this._onKeyUp.event;
+
+ private _onCaseSensitiveKeyDown = this._register(new Emitter());
+ public onCaseSensitiveKeyDown: Event = this._onCaseSensitiveKeyDown.event;
+
+ constructor(parent:HTMLElement, contextViewProvider: IContextViewProvider, options?:IFindInputOptions) {
+ super();
this.contextViewProvider = contextViewProvider;
- this.onOptionChange = null;
this.width = options.width || 100;
this.placeholder = options.placeholder || '';
this.validation = options.validation;
this.label = options.label || NLS_DEFAULT_LABEL;
- this.listenersToRemove = [];
this.regex = null;
this.wholeWords = null;
this.caseSensitive = null;
this.domNode = null;
- this.inputNode = null;
this.inputBox = null;
- this.validationNode = null;
this.buildDomNode(options.appendCaseSensitiveLabel || '', options.appendWholeWordsLabel || '', options.appendRegexLabel || '');
if(Boolean(parent)) {
parent.appendChild(this.domNode);
}
- }
- public destroy(): void {
- this.regex.destroy();
- this.wholeWords.destroy();
- this.caseSensitive.destroy();
- this.listenersToRemove.forEach((element) => {
- element();
- });
- this.listenersToRemove = [];
- }
-
- public on(eventType:string, handler:(event:Event)=>void): FindInput {
- switch(eventType) {
- case 'keydown':
- case 'keyup':
- $(this.inputBox.inputElement).on(eventType, handler);
- break;
- case FindInput.OPTION_CHANGE:
- this.onOptionChange = handler;
- break;
- }
- return this;
+ this.onkeydown(this.inputBox.inputElement, (e) => this._onKeyDown.fire(e));
+ this.onkeyup(this.inputBox.inputElement, (e) => this._onKeyUp.fire(e));
}
public enable(): void {
- $(this.domNode).removeClass('disabled');
+ dom.removeClass(this.domNode, 'disabled');
this.inputBox.enable();
this.regex.enable();
this.wholeWords.enable();
@@ -107,7 +92,7 @@ export class FindInput {
}
public disable(): void {
- $(this.domNode).addClass('disabled');
+ dom.addClass(this.domNode, 'disabled');
this.inputBox.disable();
this.regex.disable();
this.wholeWords.disable();
@@ -146,29 +131,29 @@ export class FindInput {
}
public getCaseSensitive():boolean {
- return this.caseSensitive.isChecked;
+ return this.caseSensitive.checked;
}
public setCaseSensitive(value:boolean): void {
- this.caseSensitive.setChecked(value);
+ this.caseSensitive.checked = value;
this.setInputWidth();
}
public getWholeWords():boolean {
- return this.wholeWords.isChecked;
+ return this.wholeWords.checked;
}
public setWholeWords(value:boolean): void {
- this.wholeWords.setChecked(value);
+ this.wholeWords.checked = value;
this.setInputWidth();
}
public getRegex():boolean {
- return this.regex.isChecked;
+ return this.regex.checked;
}
public setRegex(value:boolean): void {
- this.regex.setChecked(value);
+ this.regex.checked = value;
this.setInputWidth();
}
@@ -177,45 +162,63 @@ export class FindInput {
}
private setInputWidth(): void {
- var w = this.width - this.caseSensitive.width() - this.wholeWords.width() - this.regex.width();
+ let w = this.width - this.caseSensitive.width() - this.wholeWords.width() - this.regex.width();
this.inputBox.width = w;
}
private buildDomNode(appendCaseSensitiveLabel:string, appendWholeWordsLabel: string, appendRegexLabel: string): void {
this.domNode = document.createElement('div');
this.domNode.style.width = this.width + 'px';
- $(this.domNode).addClass('monaco-findInput');
+ dom.addClass(this.domNode, 'monaco-findInput');
- this.inputBox = new InputBox.InputBox(this.domNode, this.contextViewProvider, {
+ this.inputBox = this._register(new InputBox(this.domNode, this.contextViewProvider, {
placeholder: this.placeholder || '',
ariaLabel: this.label || '',
validationOptions: {
validation: this.validation || null,
showMessage: true
}
- });
+ }));
- this.regex = new Checkbox.Checkbox('regex', NLS_REGEX_CHECKBOX_LABEL + appendRegexLabel, false, () => {
- this.onOptionChange(null);
- this.inputBox.focus();
- this.setInputWidth();
- this.validate();
- });
- this.wholeWords = new Checkbox.Checkbox('whole-word', NLS_WHOLE_WORD_CHECKBOX_LABEL + appendWholeWordsLabel, false, () => {
- this.onOptionChange(null);
- this.inputBox.focus();
- this.setInputWidth();
- this.validate();
- });
- this.caseSensitive = new Checkbox.Checkbox('case-sensitive', NLS_CASE_SENSITIVE_CHECKBOX_LABEL + appendCaseSensitiveLabel, false, () => {
- this.onOptionChange(null);
- this.inputBox.focus();
- this.setInputWidth();
- this.validate();
- });
+ this.regex = this._register(new Checkbox({
+ actionClassName: 'regex',
+ title: NLS_REGEX_CHECKBOX_LABEL + appendRegexLabel,
+ isChecked: false,
+ onChange: () => {
+ this._onDidOptionChange.fire();
+ this.inputBox.focus();
+ this.setInputWidth();
+ this.validate();
+ }
+ }));
+ this.wholeWords = this._register(new Checkbox({
+ actionClassName: 'whole-word',
+ title: NLS_WHOLE_WORD_CHECKBOX_LABEL + appendWholeWordsLabel,
+ isChecked: false,
+ onChange: () => {
+ this._onDidOptionChange.fire();
+ this.inputBox.focus();
+ this.setInputWidth();
+ this.validate();
+ }
+ }));
+ this.caseSensitive = this._register(new Checkbox({
+ actionClassName: 'case-sensitive',
+ title: NLS_CASE_SENSITIVE_CHECKBOX_LABEL + appendCaseSensitiveLabel,
+ isChecked: false,
+ onChange: () => {
+ this._onDidOptionChange.fire();
+ this.inputBox.focus();
+ this.setInputWidth();
+ this.validate();
+ },
+ onKeyDown: (e) => {
+ this._onCaseSensitiveKeyDown.fire(e);
+ }
+ }));
this.setInputWidth();
- var controls = document.createElement('div');
+ let controls = document.createElement('div');
controls.className = 'controls';
controls.appendChild(this.caseSensitive.domNode);
controls.appendChild(this.wholeWords.domNode);
@@ -228,7 +231,7 @@ export class FindInput {
this.inputBox.validate();
}
- public showMessage(message: InputBox.IMessage): void {
+ public showMessage(message: InputBoxMessage): void {
this.inputBox.showMessage(message);
}
diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts
index 70051e5d296..2caafdaa65c 100644
--- a/src/vs/base/browser/ui/inputbox/inputBox.ts
+++ b/src/vs/base/browser/ui/inputbox/inputBox.ts
@@ -5,18 +5,19 @@
'use strict';
import 'vs/css!./inputBox';
-import Bal = require('vs/base/browser/browser');
-import dom = require('vs/base/browser/dom');
-import browser = require('vs/base/browser/browserService');
-import htmlcontent = require('vs/base/common/htmlContent');
-import renderer = require('vs/base/browser/htmlContentRenderer');
-import ee = require('vs/base/common/eventEmitter');
-import actions = require('vs/base/common/actions');
-import actionBar = require('vs/base/browser/ui/actionbar/actionbar');
-import lifecycle = require('vs/base/common/lifecycle');
-import contextview = require('vs/base/browser/ui/contextview/contextview');
-var $ = dom.emmet;
+import * as Bal from 'vs/base/browser/browser';
+import * as dom from 'vs/base/browser/dom';
+import * as browser from 'vs/base/browser/browserService';
+import {IHTMLContentElement} from 'vs/base/common/htmlContent';
+import {renderHtml} from 'vs/base/browser/htmlContentRenderer';
+import {IAction} from 'vs/base/common/actions';
+import {ActionBar} from 'vs/base/browser/ui/actionbar/actionbar';
+import {IContextViewProvider, AnchorAlignment} from 'vs/base/browser/ui/contextview/contextview';
+import Event, {Emitter} from 'vs/base/common/event';
+import {Widget} from 'vs/base/browser/ui/widget';
+
+let $ = dom.emmet;
export interface IInputOptions {
placeholder?:string;
@@ -24,7 +25,7 @@ export interface IInputOptions {
type?:string;
validationOptions?:IInputValidationOptions;
flexibleHeight?: boolean;
- actions?:actions.IAction[];
+ actions?:IAction[];
}
export interface IInputValidator {
@@ -53,14 +54,14 @@ export interface IRange {
end: number;
}
-export class InputBox extends ee.EventEmitter {
+export class InputBox extends Widget {
- private contextViewProvider: contextview.IContextViewProvider;
+ private contextViewProvider: IContextViewProvider;
private element: HTMLElement;
private input: HTMLInputElement;
private mirror: HTMLElement;
- private actionbar: actionBar.ActionBar;
+ private actionbar: ActionBar;
private options: IInputOptions;
private message: IMessage;
private placeholder: string;
@@ -69,14 +70,19 @@ export class InputBox extends ee.EventEmitter {
private showValidationMessage: boolean;
private state = 'idle';
private cachedHeight: number;
- private toDispose: lifecycle.IDisposable[];
- constructor(container:HTMLElement, contextViewProvider: contextview.IContextViewProvider, options?: IInputOptions) {
+ private _onDidChange = this._register(new Emitter());
+ public onDidChange: Event = this._onDidChange.event;
+
+ private _onDidHeightChange = this._register(new Emitter());
+ public onDidHeightChange: Event = this._onDidHeightChange.event;
+
+ constructor(container:HTMLElement, contextViewProvider: IContextViewProvider, options?: IInputOptions) {
super();
this.contextViewProvider = contextViewProvider;
this.options = options || Object.create(null);
- this.toDispose = [];
+ // this.toDispose = [];
this.message = null;
this.cachedHeight = null;
this.placeholder = this.options.placeholder || '';
@@ -89,9 +95,9 @@ export class InputBox extends ee.EventEmitter {
this.element = dom.append(container, $('.monaco-inputbox.idle'));
- var tagName = this.options.flexibleHeight ? 'textarea' : 'input';
+ let tagName = this.options.flexibleHeight ? 'textarea' : 'input';
- var wrapper = dom.append(this.element, $('.wrapper'));
+ let wrapper = dom.append(this.element, $('.wrapper'));
this.input = dom.append(wrapper, $(tagName + '.input'));
this.input.setAttribute('autocorrect', 'off');
this.input.setAttribute('autocapitalize', 'off');
@@ -112,22 +118,20 @@ export class InputBox extends ee.EventEmitter {
this.input.setAttribute('placeholder', this.placeholder);
}
- this.toDispose.push(
- dom.addDisposableListener(this.input, dom.EventType.INPUT, () => this.onValueChange()),
- dom.addDisposableListener(this.input, dom.EventType.BLUR, () => this.onBlur()),
- dom.addDisposableListener(this.input, dom.EventType.FOCUS, () => this.onFocus())
- );
+ this.oninput(this.input, () => this.onValueChange());
+ this.onblur(this.input, () => this.onBlur());
+ this.onfocus(this.input, () => this.onFocus());
// Add placeholder shim for IE because IE decides to hide the placeholder on focus (we dont want that!)
if (this.placeholder && Bal.isIE11orEarlier) {
- this.toDispose.push(dom.addDisposableListener(this.input, dom.EventType.CLICK, (e) => {
+ this.onclick(this.input, (e) => {
dom.EventHelper.stop(e, true);
this.input.focus();
- }));
+ });
if (Bal.isIE9) {
- this.toDispose.push(dom.addDisposableListener(this.input, 'keyup', () => this.onValueChange()));
+ this.onkeyup(this.input, () => this.onValueChange());
}
}
@@ -135,7 +139,7 @@ export class InputBox extends ee.EventEmitter {
// Support actions
if (this.options.actions) {
- this.actionbar = new actionBar.ActionBar(this.element);
+ this.actionbar = this._register(new ActionBar(this.element));
this.actionbar.push(this.options.actions, { icon: true, label: false });
}
}
@@ -154,7 +158,7 @@ export class InputBox extends ee.EventEmitter {
}
}
- public setContextViewProvider(contextViewProvider: contextview.IContextViewProvider): void {
+ public setContextViewProvider(contextViewProvider: IContextViewProvider): void {
this.contextViewProvider = contextViewProvider;
}
@@ -244,7 +248,7 @@ export class InputBox extends ee.EventEmitter {
}
public validate(): boolean {
- var result: IMessage = null;
+ let result: IMessage = null;
if (this.validation) {
result = this.validation(this.value);
@@ -273,19 +277,19 @@ export class InputBox extends ee.EventEmitter {
return;
}
- var div: HTMLElement;
- var layout = () => div.style.width = dom.getTotalWidth(this.element) + 'px';
+ let div: HTMLElement;
+ let layout = () => div.style.width = dom.getTotalWidth(this.element) + 'px';
this.state = 'open';
this.contextViewProvider.showContextView({
getAnchor: () => this.element,
- anchorAlignment: contextview.AnchorAlignment.RIGHT,
+ anchorAlignment: AnchorAlignment.RIGHT,
render: (container: HTMLElement) => {
div = dom.append(container, $('.monaco-inputbox-container'));
layout();
- var renderOptions: htmlcontent.IHTMLContentElement = {
+ let renderOptions: IHTMLContentElement = {
tagName: 'span',
className: 'monaco-inputbox-message',
};
@@ -296,7 +300,7 @@ export class InputBox extends ee.EventEmitter {
renderOptions.text = this.message.content;
}
- var spanElement:HTMLElement = renderer.renderHtml(renderOptions);
+ let spanElement:HTMLElement = renderHtml(renderOptions);
dom.addClass(spanElement, this.classForType(this.message.type));
dom.append(div, spanElement);
return null;
@@ -316,13 +320,13 @@ export class InputBox extends ee.EventEmitter {
}
private onValueChange(): void {
- this.emit('change', this.value);
+ this._onDidChange.fire(this.value);
this.validate();
if (this.mirror) {
- var lastCharCode = this.value.charCodeAt(this.value.length - 1);
- var suffix = lastCharCode === 10 ? ' ' : '';
+ let lastCharCode = this.value.charCodeAt(this.value.length - 1);
+ let suffix = lastCharCode === 10 ? ' ' : '';
this.mirror.textContent = this.value + suffix;
this.layout();
}
@@ -338,14 +342,11 @@ export class InputBox extends ee.EventEmitter {
if (previousHeight !== this.cachedHeight) {
this.input.style.height = this.cachedHeight + 'px';
- this.emit('heightchange', this.cachedHeight);
+ this._onDidHeightChange.fire(this.cachedHeight);
}
}
public dispose(): void {
-
- this.toDispose = lifecycle.disposeAll(this.toDispose);
-
this._hideMessage();
this.element = null;
@@ -357,11 +358,7 @@ export class InputBox extends ee.EventEmitter {
this.validation = null;
this.showValidationMessage = null;
this.state = null;
-
- if (this.actionbar) {
- this.actionbar.dispose();
- this.actionbar = null;
- }
+ this.actionbar = null;
super.dispose();
}
diff --git a/src/vs/base/browser/ui/widget.ts b/src/vs/base/browser/ui/widget.ts
new file mode 100644
index 00000000000..5fa39bcfd37
--- /dev/null
+++ b/src/vs/base/browser/ui/widget.ts
@@ -0,0 +1,38 @@
+/*---------------------------------------------------------------------------------------------
+ * 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 {Disposable} from 'vs/base/common/lifecycle';
+import {StandardMouseEvent} from 'vs/base/browser/mouseEvent';
+import {StandardKeyboardEvent} from 'vs/base/browser/keyboardEvent';
+import * as DomUtils from 'vs/base/browser/dom';
+
+export abstract class Widget extends Disposable {
+
+ protected onclick(domNode:HTMLElement, listener:(e:StandardMouseEvent)=>void): void {
+ this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.CLICK, (e:MouseEvent) => listener(new StandardMouseEvent(e))));
+ }
+
+ protected onkeydown(domNode:HTMLElement, listener:(e:StandardKeyboardEvent)=>void): void {
+ this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.KEY_DOWN, (e:KeyboardEvent) => listener(new StandardKeyboardEvent(e))));
+ }
+
+ protected onkeyup(domNode:HTMLElement, listener:(e:StandardKeyboardEvent)=>void): void {
+ this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.KEY_UP, (e:KeyboardEvent) => listener(new StandardKeyboardEvent(e))));
+ }
+
+ protected oninput(domNode:HTMLElement, listener:(e:Event)=>void): void {
+ this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.INPUT, listener));
+ }
+
+ protected onblur(domNode:HTMLElement, listener:(e:Event)=>void): void {
+ this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.BLUR, listener));
+ }
+
+ protected onfocus(domNode:HTMLElement, listener:(e:Event)=>void): void {
+ this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.FOCUS, listener));
+ }
+}
diff --git a/src/vs/base/common/keyCodes.ts b/src/vs/base/common/keyCodes.ts
index f3c75f9aa8a..6db3507409c 100644
--- a/src/vs/base/common/keyCodes.ts
+++ b/src/vs/base/common/keyCodes.ts
@@ -484,6 +484,7 @@ export class CommonKeybindings {
public static WINCTRL_ENTER: number = KeyMod.WinCtrl | KeyCode.Enter;
public static TAB: number = KeyCode.Tab;
+ public static SHIFT_TAB: number = KeyMod.Shift | KeyCode.Tab;
public static ESCAPE: number = KeyCode.Escape;
public static SPACE: number = KeyCode.Space;
public static DELETE: number = KeyCode.Delete;
diff --git a/src/vs/base/common/lifecycle.ts b/src/vs/base/common/lifecycle.ts
index 255646cf962..bc83b7bf657 100644
--- a/src/vs/base/common/lifecycle.ts
+++ b/src/vs/base/common/lifecycle.ts
@@ -69,3 +69,21 @@ export interface CallAll {
* Calls all functions that are being passed to it.
*/
export const cAll: CallAll = callAll;
+
+export abstract class Disposable implements IDisposable {
+
+ private _toDispose: IDisposable[];
+
+ constructor() {
+ this._toDispose = [];
+ }
+
+ public dispose(): void {
+ this._toDispose = disposeAll(this._toDispose);
+ }
+
+ protected _register(t:T): T {
+ this._toDispose.push(t);
+ return t;
+ }
+}
diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts
index b914a83c85f..def07124650 100644
--- a/src/vs/base/common/strings.ts
+++ b/src/vs/base/common/strings.ts
@@ -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
diff --git a/src/vs/editor/browser/widget/media/tokens.css b/src/vs/editor/browser/widget/media/tokens.css
index aa83928a7f0..ff6a96f2023 100644
--- a/src/vs/editor/browser/widget/media/tokens.css
+++ b/src/vs/editor/browser/widget/media/tokens.css
@@ -14,9 +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.comment { color: #008000; }
-.monaco-editor.vs .token.comment.shebang { color: #929292; }
-.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; }
@@ -83,7 +80,6 @@
/* VSXML */
.monaco-editor.vs .token.vs { color: #006400; }
-.monaco-editor.vs .token.comment.vs { color: #aeb9ae; }
.monaco-editor.vs .token.tag.vs { color: #aeb9ae; }
.monaco-editor.vs .token.attribute.name.vs { color: #aeb9ae; }
.monaco-editor.vs .token.attribute.value.vs { color: #2c51cc; }
@@ -109,10 +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.comment { color: #929292; }
-.monaco-editor.vs-dark .token.comment.shebang { color: #929292; }
-.monaco-editor.vs-dark .token.comment.doc { color: #608B4E; }
-.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; }
@@ -204,11 +196,7 @@
.monaco-editor.hc-black .token.constant { color: #dd0000; }
.monaco-editor.hc-black .token.string { color: #CE9178; }
.monaco-editor.hc-black .token.string.escape { color: #CE9178; }
-.monaco-editor.hc-black .token.comment { color: #008000; }
-.monaco-editor.hc-black .token.comment.shebang { color: #929292; }
-.monaco-editor.hc-black .token.comment.doc { color: #608B4E; }
-.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; }
@@ -301,18 +289,7 @@
.monaco-editor.vs .token.builtin.function { color: #0000FF; }
-.monaco-editor.vs .token.comment,
-.monaco-editor.vs .token.comment.block,
-.monaco-editor.vs .token.comment.block.documentation,
-.monaco-editor.vs .token.comment.line { color: #008000; }
-/*
-.monaco-editor.vs .token.comment.line.documentation
-.monaco-editor.vs .token.comment.line.double-slash
-.monaco-editor.vs .token.comment.line.double-dash
-.monaco-editor.vs .token.comment.line.number-sign
-.monaco-editor.vs .token.comment.line.percentage
-.monaco-editor.vs .token.comment.line.character
-*/
+.monaco-editor.vs .token.comment { color: #008000; }
.monaco-editor.vs .token.constant { color: #dd0000; }
.monaco-editor.vs .token.constant.language { color: #0000FF; }
@@ -370,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; }
@@ -441,15 +417,7 @@
.monaco-editor.vs-dark .token.builtin.function { color: #569CD6; }
-.monaco-editor.vs-dark .token.comment,
-.monaco-editor.vs-dark .token.comment.block,
-.monaco-editor.vs-dark .token.comment.block.documentation,
-.monaco-editor.vs-dark .token.comment.line { color: #608B4E; }
-/*.monaco-editor.vs-dark .token.comment.line.double-slash
-.monaco-editor.vs-dark .token.comment.line.double-dash
-.monaco-editor.vs-dark .token.comment.line.number-sign
-.monaco-editor.vs-dark .token.comment.line.percentage
-.monaco-editor.vs-dark .token.comment.line.character*/
+.monaco-editor.vs-dark .token.comment { color: #608B4E; }
.monaco-editor.vs-dark .token.constant { color: #569CD6; }
.monaco-editor.vs-dark .token.constant.language { color: #569CD6; }
@@ -506,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; }
@@ -578,15 +545,7 @@
.monaco-editor.hc-black .token.builtin.function { color: #569CD6; }
-.monaco-editor.hc-black .token.comment,
-.monaco-editor.hc-black .token.comment.block,
-.monaco-editor.hc-black .token.comment.block.documentation,
-.monaco-editor.hc-black .token.comment.line { color: #608B4E; }
-/*.monaco-editor.hc-black .token.comment.line.double-slash
-.monaco-editor.hc-black .token.comment.line.double-dash
-.monaco-editor.hc-black .token.comment.line.number-sign
-.monaco-editor.hc-black .token.comment.line.percentage
-.monaco-editor.hc-black .token.comment.line.character*/
+.monaco-editor.hc-black .token.comment { color: #608B4E; }
.monaco-editor.hc-black .token.constant { color: #569CD6; }
.monaco-editor.hc-black .token.constant.language { color: #569CD6; }
@@ -642,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; }
diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts
index c0301683a91..84a78528980 100644
--- a/src/vs/editor/common/editorCommon.ts
+++ b/src/vs/editor/common/editorCommon.ts
@@ -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;
diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts
index c630f94b648..06b5fb0bda1 100644
--- a/src/vs/editor/common/model/textModel.ts
+++ b/src/vs/editor/common/model/textModel.ts
@@ -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;
}
diff --git a/src/vs/editor/contrib/find/browser/find.ts b/src/vs/editor/contrib/find/browser/find.ts
index e1f248e76ec..25871b5470f 100644
--- a/src/vs/editor/contrib/find/browser/find.ts
+++ b/src/vs/editor/contrib/find/browser/find.ts
@@ -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;
- 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 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 {
-
- 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 {
- 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 {
- 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 {
+ 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 {
- 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 {
- 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 {
- 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 }
});
diff --git a/src/vs/editor/contrib/find/browser/findWidget.ts b/src/vs/editor/contrib/find/browser/findWidget.ts
index e8b6422971c..197d2736c67 100644
--- a/src/vs/editor/contrib/find/browser/findWidget.ts
+++ b/src/vs/editor/contrib/find/browser/findWidget.ts
@@ -6,29 +6,24 @@
'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';
+import {Widget} from 'vs/base/browser/ui/widget';
export interface IFindController {
- enableSelectionFind(): void;
- disableSelectionFind(): void;
replace(): void;
replaceAll(): void;
}
@@ -45,88 +40,71 @@ 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 extends Widget 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 _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,
- ]);
+ super();
this._codeEditor = codeEditor;
this._controller = controller;
+ this._state = state;
this._contextViewProvider = contextViewProvider;
this._keybindingService = keybindingService;
this._isVisible = false;
this._isReplaceVisible = false;
this._isReplaceEnabled = false;
- this._toDispose = [];
- this._model = null;
- this._modelListenersToDispose = [];
+ this._register(this._state.addChangeListener((e) => this._onStateChanged(e)));
this._buildDomNode();
- this.focusTracker = DomUtils.trackFocus(this._domNode);
+ this.focusTracker = this._register(DomUtils.trackFocus(this._findInput.inputBox.inputElement));
+ this.focusTracker.addFocusListener(() => this._reseedFindScope());
this._codeEditor.addOverlayWidget(this);
-
- this.focusTracker.addFocusListener(() => {
- var selection = this._codeEditor.getSelection();
- if (selection.startLineNumber !== selection.endLineNumber) {
- // Search in selection
- this._controller.enableSelectionFind();
- }
- });
}
- public dispose(): void {
- this.focusTracker.dispose();
- this._removeModel();
- this._findInput.destroy();
- this._replaceInputBox.dispose();
- this._toDispose = Lifecycle.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 +126,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 +192,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 +202,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,80 +214,77 @@ 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;
+ switch (e.asKeybinding()) {
+ case CommonKeybindings.ENTER:
+ this._codeEditor.getAction(FIND_IDS.NextMatchFindAction).run().done(null, Errors.onUnexpectedError);
+ e.preventDefault();
+ return;
- if (e.equals(CommonKeybindings.ENTER)) {
- this._codeEditor.getAction(FindModel.NEXT_MATCH_FIND_ACTION_ID).run().done(null, Errors.onUnexpectedError);
- handled = true;
- } else if (e.equals(CommonKeybindings.SHIFT_ENTER)) {
- this._codeEditor.getAction(FindModel.PREVIOUS_MATCH_FIND_ACTION_ID).run().done(null, Errors.onUnexpectedError);
- handled = true;
- } else if (e.equals(CommonKeybindings.TAB)) {
- if (this._isReplaceVisible) {
- this._replaceInputBox.focus();
- } else {
- this._findInput.focusOnCaseSensitive();
- }
- handled = true;
- } else if (e.equals(CommonKeybindings.CTRLCMD_DOWN_ARROW)) {
- this._codeEditor.focus();
- handled = true;
+ case CommonKeybindings.SHIFT_ENTER:
+ this._codeEditor.getAction(FIND_IDS.PreviousMatchFindAction).run().done(null, Errors.onUnexpectedError);
+ e.preventDefault();
+ return;
+
+ case CommonKeybindings.TAB:
+ if (this._isReplaceVisible) {
+ this._replaceInputBox.focus();
+ } else {
+ this._findInput.focusOnCaseSensitive();
+ }
+ e.preventDefault();
+ return;
+
+ case CommonKeybindings.CTRLCMD_DOWN_ARROW:
+ this._codeEditor.focus();
+ e.preventDefault();
+ return;
}
- if (handled) {
- e.preventDefault();
- } else {
- setTimeout(() => {
- this._onFindValueChange();
- this._emitUserInputEvent(true);
- }, 10);
- }
+ // getValue() is not updated right away
+ setTimeout(() => {
+ this._state.change({ searchString: this._findInput.getValue() }, true);
+ }, 10);
}
private _onReplaceInputKeyDown(e:DomUtils.IKeyboardEvent): void {
- var handled = false;
+ switch (e.asKeybinding()) {
+ case CommonKeybindings.ENTER:
+ this._controller.replace();
+ e.preventDefault();
+ return;
- if (e.equals(CommonKeybindings.ENTER)) {
- this._controller.replace();
- handled = true;
- } else if (e.equals(CommonKeybindings.CTRLCMD_ENTER)) {
- this._controller.replaceAll();
- handled = true;
- } else if (e.equals(CommonKeybindings.TAB)) {
- this._findInput.focusOnCaseSensitive();
- handled = true;
- } else if (e.equals(CommonKeybindings.CTRLCMD_DOWN_ARROW)) {
- this._codeEditor.focus();
- handled = true;
+ case CommonKeybindings.CTRLCMD_ENTER:
+ this._controller.replaceAll();
+ e.preventDefault();
+ return;
+
+ case CommonKeybindings.TAB:
+ this._findInput.focusOnCaseSensitive();
+ e.preventDefault();
+ return;
+
+ case CommonKeybindings.SHIFT_TAB:
+ this._findInput.focus();
+ e.preventDefault();
+ return;
+
+ case CommonKeybindings.CTRLCMD_DOWN_ARROW:
+ this._codeEditor.focus();
+ e.preventDefault();
+ return;
}
- if (handled) {
- e.preventDefault();
- } else {
- setTimeout(() => {
- this._emitUserInputEvent(true);
- }, 10);
- }
+ setTimeout(() => {
+ 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 +296,14 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
private _buildFindPart(): HTMLElement {
// Find input
- this._findInput = new Findinput.FindInput(null, this._contextViewProvider, {
+ this._findInput = this._register(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;
}
@@ -354,69 +317,87 @@ export class FindWidget extends EventEmitter.EventEmitter implements EditorBrows
return { content: e.message };
}
}
- }).on('keydown', (browserEvent:KeyboardEvent) => {
- this._onFindInputKeyDown(new Keyboard.StandardKeyboardEvent(browserEvent));
- }).on(Findinput.FindInput.OPTION_CHANGE, () => {
- this._emitUserInputEvent(true);
- });
+ }));
+ this._register(this._findInput.onKeyDown((e) => this._onFindInputKeyDown(e)));
+ this._register(this._findInput.onDidOptionChange(() => {
+ this._state.change({
+ isRegex: this._findInput.getRegex(),
+ wholeWord: this._findInput.getWholeWords(),
+ matchCase: this._findInput.getCaseSensitive()
+ }, true);
+ }));
+ this._register(this._findInput.onCaseSensitiveKeyDown((e) => {
+ if (e.equals(CommonKeybindings.SHIFT_TAB)) {
+ if (this._isReplaceVisible) {
+ this._replaceInputBox.focus();
+ e.preventDefault();
+ }
+ }
+ }));
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._toDispose.push(this._prevBtn);
+ this._prevBtn = this._register(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) => {}
+ }));
// 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._toDispose.push(this._nextBtn);
+ this._nextBtn = this._register(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) => {}
+ }));
- var findPart = document.createElement('div');
+ let findPart = document.createElement('div');
findPart.className = 'find-part';
findPart.appendChild(this._findInput.domNode);
findPart.appendChild(this._prevBtn.domNode);
findPart.appendChild(this._nextBtn.domNode);
// Toggle selection button
- this._toggleSelectionFind = new Checkbox(findPart, NLS_TOGGLE_SELECTION_FIND_TITLE);
+ this._toggleSelectionFind = this._register(new Checkbox(findPart, NLS_TOGGLE_SELECTION_FIND_TITLE));
this._toggleSelectionFind.disable();
- this._toDispose.push(DomUtils.addStandardDisposableListener(this._toggleSelectionFind.checkbox, 'change', (e) => {
+ this._register(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 = this._register(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 (e.equals(CommonKeybindings.TAB)) {
+ if (this._isReplaceVisible) {
+ if (this._replaceBtn.isEnabled()) {
+ this._replaceBtn.focus();
+ } else {
+ this._codeEditor.focus();
+ }
+ e.preventDefault();
+ }
+ }
}
- });
- this._toDispose.push(this._closeBtn);
+ }));
findPart.appendChild(this._closeBtn.domNode);
@@ -433,7 +414,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,36 +426,43 @@ 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 = this._register(new InputBox(replaceInput, null, {
ariaLabel: NLS_REPLACE_INPUT_LABEL,
placeholder: NLS_REPLACE_INPUT_PLACEHOLDER
- });
+ }));
- this._toDispose.push(DomUtils.addStandardDisposableListener(this._replaceInputBox.inputElement, 'keydown', (e) => this._onReplaceInputKeyDown(e)));
+ this._register(DomUtils.addStandardDisposableListener(this._replaceInputBox.inputElement, 'keydown', (e) => this._onReplaceInputKeyDown(e)));
this._replaceInputBox.disable();
// Replace one button
- this._replaceBtn = new SimpleButton(
- NLS_REPLACE_BTN_LABEL,
- 'replace'
- ).onTrigger(() => {
- this._controller.replace();
- });
- this._toDispose.push(this._replaceBtn);
+ this._replaceBtn = this._register(new SimpleButton({
+ label: NLS_REPLACE_BTN_LABEL,
+ className: 'replace',
+ onTrigger: () => {
+ this._controller.replace();
+ },
+ onKeyDown: (e) => {
+ if (e.equals(CommonKeybindings.SHIFT_TAB)) {
+ this._closeBtn.focus();
+ e.preventDefault();
+ }
+ }
+ }));
// Replace all button
- this._replaceAllBtn = new SimpleButton(
- NLS_REPLACE_ALL_BTN_LABEL,
- 'replace-all'
- ).onTrigger(() => {
- this._controller.replaceAll();
- });
- this._toDispose.push(this._replaceAllBtn);
+ this._replaceAllBtn = this._register(new SimpleButton({
+ label: NLS_REPLACE_ALL_BTN_LABEL,
+ className: 'replace-all',
+ onTrigger: () => {
+ this._controller.replaceAll();
+ },
+ onKeyDown: (e) => {}
+ }));
- var replacePart = document.createElement('div');
+ let replacePart = document.createElement('div');
replacePart.className = 'replace-part';
replacePart.appendChild(replaceInput);
replacePart.appendChild(this._replaceBtn.domNode);
@@ -485,26 +473,23 @@ 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 = this._register(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);
this._toggleReplaceBtn.setExpanded(this._isReplaceVisible);
- this._toDispose.push(this._toggleReplaceBtn);
// Widget
this._domNode = document.createElement('div');
@@ -532,14 +517,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,50 +557,18 @@ 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 extends Widget {
- 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) {
+ super();
this._domNode = document.createElement('div');
this._domNode.className = 'monaco-checkbox';
this._domNode.title = title;
@@ -619,15 +576,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,56 +608,53 @@ 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 extends Widget {
- constructor(label:string, className:string) {
+ private _opts: ISimpleButtonOpts;
+ private _domNode: HTMLElement;
- this._onTrigger = null;
- this._onKeyDown = null;
+ constructor(opts:ISimpleButtonOpts) {
+ super();
+ this._opts = opts;
this._domNode = document.createElement('div');
- this._domNode.title = label;
- this._domNode.tabIndex = -1;
- this._domNode.className = 'button ' + className;
+ this._domNode.title = this._opts.label;
+ this._domNode.tabIndex = 0;
+ 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.onclick(this._domNode, (e) => {
+ this._opts.onTrigger();
e.preventDefault();
- }));
- this._toDispose.push(DomUtils.addStandardDisposableListener(this._domNode, 'keydown', (e) => {
+ });
+ this.onkeydown(this._domNode, (e) => {
if (e.equals(CommonKeybindings.SPACE) || e.equals(CommonKeybindings.ENTER)) {
- this._invokeOnTrigger();
+ this._opts.onTrigger();
e.preventDefault();
return;
}
- this._invokeOnKeyDown(e);
- }));
- }
-
- public dispose(): void {
- this._toDispose = Lifecycle.disposeAll(this._toDispose);
+ this._opts.onKeyDown(e);
+ });
}
public get domNode(): HTMLElement {
return this._domNode;
}
+ public isEnabled(): boolean {
+ return (this._domNode.tabIndex >= 0);
+ }
+
public focus(): void {
this._domNode.focus();
}
@@ -718,26 +672,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);
- }
- }
}
diff --git a/src/vs/editor/contrib/find/common/findDecorations.ts b/src/vs/editor/contrib/find/common/findDecorations.ts
new file mode 100644
index 00000000000..faa4688a5a4
--- /dev/null
+++ b/src/vs/editor/contrib/find/common/findDecorations.ts
@@ -0,0 +1,215 @@
+/*---------------------------------------------------------------------------------------------
+ * 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);
+ this.editor.revealRangeInCenterIfOutsideViewport(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
+ };
+ }
+}
diff --git a/src/vs/editor/contrib/find/common/findModel.ts b/src/vs/editor/contrib/find/common/findModel.ts
index 27d105759b2..e24e5f80dd7 100644
--- a/src/vs/editor/contrib/find/common/findModel.ts
+++ b/src/vs/editor/contrib/find/common/findModel.ts
@@ -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);
diff --git a/src/vs/editor/contrib/find/common/findState.ts b/src/vs/editor/contrib/find/common/findState.ts
new file mode 100644
index 00000000000..11452b825ec
--- /dev/null
+++ b/src/vs/editor/contrib/find/common/findState.ts
@@ -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);
+ }
+ }
+}
diff --git a/src/vs/editor/contrib/referenceSearch/browser/referenceSearchWidget.css b/src/vs/editor/contrib/referenceSearch/browser/referenceSearchWidget.css
index be287288ce2..9b79d1302d8 100644
--- a/src/vs/editor/contrib/referenceSearch/browser/referenceSearchWidget.css
+++ b/src/vs/editor/contrib/referenceSearch/browser/referenceSearchWidget.css
@@ -112,6 +112,10 @@
background-color: rgba(234, 92, 0, 0.3);
}
+.monaco-editor .reference-zone-widget .monaco-count-badge {
+ margin-right: 12px;
+}
+
/* dark room */
.monaco-editor.vs-dark .reference-zone-widget .tree .block,
diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts
index 1156a04d295..231bdcbff14 100644
--- a/src/vs/vscode.d.ts
+++ b/src/vs/vscode.d.ts
@@ -3371,6 +3371,7 @@ declare namespace vscode {
// 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;
}
diff --git a/src/vs/workbench/browser/parts/editor/textEditorModel.ts b/src/vs/workbench/browser/parts/editor/textEditorModel.ts
index 954203fda12..fa9019a9fc6 100644
--- a/src/vs/workbench/browser/parts/editor/textEditorModel.ts
+++ b/src/vs/workbench/browser/parts/editor/textEditorModel.ts
@@ -22,6 +22,7 @@ import {IModelService} from 'vs/editor/common/services/modelService';
*/
export abstract class BaseTextEditorModel extends EditorModel implements ITextEditorModel {
private textEditorModelHandle: URI;
+ private createdEditorModel: boolean;
constructor(
@IModelService private modelService: IModelService,
@@ -46,6 +47,7 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd
// To avoid flickering, give the mode at most 50ms to load. If the mode doesn't load in 50ms, proceed creating the model with a mode promise
return Promise.any([Promise.timeout(50), this.getOrCreateMode(this.modeService, mime, firstLineText)]).then(() => {
let model = this.modelService.createModel(value, this.getOrCreateMode(this.modeService, mime, firstLineText), resource);
+ this.createdEditorModel = true;
this.textEditorModelHandle = model.getAssociatedResource();
@@ -124,11 +126,12 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd
}
public dispose(): void {
- if (this.textEditorModelHandle) {
+ if (this.textEditorModelHandle && this.createdEditorModel) {
this.modelService.destroyModel(this.textEditorModelHandle);
}
this.textEditorModelHandle = null;
+ this.createdEditorModel = false;
super.dispose();
}
diff --git a/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css b/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css
index 6a441aaeaa6..ccf818257f1 100644
--- a/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css
+++ b/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css
@@ -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;
diff --git a/src/vs/workbench/parts/debug/browser/breakpointWidget.ts b/src/vs/workbench/parts/debug/browser/breakpointWidget.ts
index 0d3169785b1..f05ceab53af 100644
--- a/src/vs/workbench/parts/debug/browser/breakpointWidget.ts
+++ b/src/vs/workbench/parts/debug/browser/breakpointWidget.ts
@@ -6,27 +6,42 @@
import 'vs/css!../browser/media/breakpointWidget';
import async = require('vs/base/common/async');
import errors = require('vs/base/common/errors');
-import { CommonKeybindings } from 'vs/base/common/keyCodes';
+import { CommonKeybindings, KeyCode } from 'vs/base/common/keyCodes';
import platform = require('vs/base/common/platform');
import lifecycle = require('vs/base/common/lifecycle');
import dom = require('vs/base/browser/dom');
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
+import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions';
import editorbrowser = require('vs/editor/browser/editorBrowser');
import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/browser/zoneWidget';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
+import { IKeybindingService, IKeybindingContextKey } from 'vs/platform/keybinding/common/keybindingService';
import debug = require('vs/workbench/parts/debug/common/debug');
const $ = dom.emmet;
+const CONTEXT_BREAKPOINT_WIDGET_VISIBLE = 'breakpointWidgetVisible';
+const CLOSE_BREAKPOINT_WIDGET_COMMAND_ID = 'closeBreakpointWidget';
export class BreakpointWidget extends ZoneWidget {
+
+ public static INSTANCE: BreakpointWidget;
+
private inputBox: InputBox;
+ private toDispose: lifecycle.IDisposable[];
+ private breakpointWidgetVisible: IKeybindingContextKey;
constructor(editor: editorbrowser.ICodeEditor, private lineNumber: number,
@IContextViewService private contextViewService: IContextViewService,
- @debug.IDebugService private debugService: debug.IDebugService
+ @debug.IDebugService private debugService: debug.IDebugService,
+ @IKeybindingService keybindingService: IKeybindingService
) {
super(editor, { showFrame: true, showArrow: false });
+
+ this.toDispose = [];
this.create();
+ this.breakpointWidgetVisible = keybindingService.createKey(CONTEXT_BREAKPOINT_WIDGET_VISIBLE, false);
+ this.breakpointWidgetVisible.set(true);
+ BreakpointWidget.INSTANCE = this;
}
public fillContainer(container: HTMLElement): void {
@@ -36,15 +51,16 @@ export class BreakpointWidget extends ZoneWidget {
const inputBoxContainer = dom.append(container, $('.inputBoxContainer'));
this.inputBox = new InputBox(inputBoxContainer, this.contextViewService, {
- placeholder: `The breakpoint on line ${ this.lineNumber } will only stop if this condition is true`
+ placeholder: `Breakpoint on line ${ this.lineNumber } will only stop if this condition is true. 'Enter' to accept, 'esc' to cancel.`
});
+ this.toDispose.push(this.inputBox);
+
dom.addClass(this.inputBox.inputElement, platform.isWindows ? 'windows' : platform.isMacintosh ? 'mac' : 'linux');
this.inputBox.value = (breakpoint && breakpoint.condition) ? breakpoint.condition : '';
// Due to an electron bug we have to do the timeout, otherwise we do not get focus
setTimeout(() => this.inputBox.focus(), 0);
let disposed = false;
- const toDispose: [lifecycle.IDisposable] = [this.inputBox, this];
const wrapUp = async.once((success: boolean) => {
if (!disposed) {
disposed = true;
@@ -64,20 +80,29 @@ export class BreakpointWidget extends ZoneWidget {
this.debugService.toggleBreakpoint(raw).done(null, errors.onUnexpectedError);
}
- lifecycle.disposeAll(toDispose);
+ this.dispose();
}
});
- toDispose.push(dom.addStandardDisposableListener(this.inputBox.inputElement, 'keydown', (e: dom.IKeyboardEvent) => {
+ this.toDispose.push(dom.addStandardDisposableListener(this.inputBox.inputElement, 'keydown', (e: dom.IKeyboardEvent) => {
const isEscape = e.equals(CommonKeybindings.ESCAPE);
const isEnter = e.equals(CommonKeybindings.ENTER);
if (isEscape || isEnter) {
wrapUp(isEnter);
}
}));
+ }
- toDispose.push(dom.addDisposableListener(this.inputBox.inputElement, 'blur', () => {
- wrapUp(true);
- }));
+ public dispose(): void {
+ super.dispose();
+ this.breakpointWidgetVisible.reset();
+ BreakpointWidget.INSTANCE = undefined;
+ lifecycle.disposeAll(this.toDispose);
}
}
+
+CommonEditorRegistry.registerEditorCommand(CLOSE_BREAKPOINT_WIDGET_COMMAND_ID, CommonEditorRegistry.commandWeight(8), { primary: KeyCode.Escape, }, false, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, (ctx, editor, args) => {
+ if (BreakpointWidget.INSTANCE) {
+ BreakpointWidget.INSTANCE.dispose();
+ }
+});
diff --git a/src/vs/workbench/parts/debug/browser/debugViewer.ts b/src/vs/workbench/parts/debug/browser/debugViewer.ts
index 7453ab451b5..0d080b51cfe 100644
--- a/src/vs/workbench/parts/debug/browser/debugViewer.ts
+++ b/src/vs/workbench/parts/debug/browser/debugViewer.ts
@@ -846,6 +846,9 @@ export class BreakpointsRenderer implements tree.IRenderer {
data.filePath.textContent = labels.getPathLabel(paths.dirname(breakpoint.source.uri.fsPath), this.contextService);
data.checkbox.checked = breakpoint.enabled;
data.actionBar.context = breakpoint;
+ if (breakpoint.condition) {
+ data.breakpoint.title = breakpoint.condition;
+ }
}
public disposeTemplate(tree: tree.ITree, templateId: string, templateData: any): void {
diff --git a/src/vs/workbench/parts/debug/browser/media/debugHover.css b/src/vs/workbench/parts/debug/browser/media/debugHover.css
index a61dd7d0c91..9f30d9d3dd0 100644
--- a/src/vs/workbench/parts/debug/browser/media/debugHover.css
+++ b/src/vs/workbench/parts/debug/browser/media/debugHover.css
@@ -37,11 +37,25 @@
height: 288px;
}
-/* Disable tree hover highlight in debug hover tree. */
+.monaco-editor .debug-hover-widget .debug-hover-tree .monaco-tree .monaco-tree-row > .content {
+ -webkit-user-select: initial;
+}
+
+/* Disable tree highlight in debug hover tree. */
.monaco-editor .debug-hover-widget .debug-hover-tree .monaco-tree .monaco-tree-rows > .monaco-tree-row:hover:not(.highlighted):not(.selected):not(.focused) {
background-color: inherit;
}
+/* Allign twistie since hover tree is smaller */
+
+.monaco-editor .debug-hover-widget .debug-hover-tree .monaco-tree .monaco-tree-rows > .monaco-tree-row.has-children > .content:before {
+ top: 6px;
+}
+
+.monaco-editor .debug-hover-widget .debug-hover-tree .monaco-tree .monaco-tree-rows > .monaco-tree-row.has-children > .content:after {
+ top: 8px;
+}
+
.monaco-editor .debug-hover-widget .debug-hover-tree .monaco-tree .monaco-tree-rows > .monaco-tree-row {
cursor: default;
}
diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts
index cc30aa2921f..a5570c248e0 100644
--- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts
+++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts
@@ -424,8 +424,12 @@ export class DebugService extends ee.EventEmitter implements debug.IDebugService
}
public editBreakpoint(editor: editorbrowser.ICodeEditor, lineNumber: number): Promise {
- const breakpointWidget = this.instantiationService.createInstance(BreakpointWidget, editor, lineNumber);
- breakpointWidget.show({ lineNumber, column: 1 }, 2);
+ if (BreakpointWidget.INSTANCE) {
+ BreakpointWidget.INSTANCE.dispose();
+ }
+
+ this.instantiationService.createInstance(BreakpointWidget, editor, lineNumber);
+ BreakpointWidget.INSTANCE.show({ lineNumber, column: 1 }, 2);
return Promise.as(true);
}
diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsWorkbenchExtension.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsWorkbenchExtension.ts
index d3613eb8918..2461fc8d927 100644
--- a/src/vs/workbench/parts/extensions/electron-browser/extensionsWorkbenchExtension.ts
+++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsWorkbenchExtension.ts
@@ -63,8 +63,7 @@ export class ExtensionsWorkbenchExtension implements IWorkbenchContribution {
'vs/workbench/parts/extensions/electron-browser/extensionsQuickOpen',
'GalleryExtensionsHandler',
'ext install ',
- nls.localize('galleryExtensionsCommands', "Install Gallery Extensions"),
- true
+ nls.localize('galleryExtensionsCommands', "Install Gallery Extensions")
)
);
@@ -75,8 +74,7 @@ export class ExtensionsWorkbenchExtension implements IWorkbenchContribution {
'vs/workbench/parts/extensions/electron-browser/extensionsQuickOpen',
'OutdatedExtensionsHandler',
'ext update ',
- nls.localize('outdatedExtensionsCommands', "Update Outdated Extensions"),
- true
+ nls.localize('outdatedExtensionsCommands', "Update Outdated Extensions")
)
);
}
diff --git a/src/vs/workbench/parts/extensions/node/extensionsService.ts b/src/vs/workbench/parts/extensions/node/extensionsService.ts
index 5eb0d748d91..84b299148d9 100644
--- a/src/vs/workbench/parts/extensions/node/extensionsService.ts
+++ b/src/vs/workbench/parts/extensions/node/extensionsService.ts
@@ -127,7 +127,7 @@ export class ExtensionsService implements IExtensionsService {
const settings = TPromise.join([
UserSettings.getValue(this.contextService, 'http.proxy'),
- UserSettings.getValue(this.contextService, 'http.proxy.strictSSL')
+ UserSettings.getValue(this.contextService, 'http.proxyStrictSSL')
]);
return settings
diff --git a/src/vs/workbench/parts/files/browser/media/explorerviewlet.css b/src/vs/workbench/parts/files/browser/media/explorerviewlet.css
index 3ae54e985f4..01eadb701c5 100644
--- a/src/vs/workbench/parts/files/browser/media/explorerviewlet.css
+++ b/src/vs/workbench/parts/files/browser/media/explorerviewlet.css
@@ -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 {
diff --git a/src/vs/workbench/parts/files/browser/saveErrorHandler.ts b/src/vs/workbench/parts/files/browser/saveErrorHandler.ts
index 570759eea63..3a563e0af5e 100644
--- a/src/vs/workbench/parts/files/browser/saveErrorHandler.ts
+++ b/src/vs/workbench/parts/files/browser/saveErrorHandler.ts
@@ -13,6 +13,7 @@ import URI from 'vs/base/common/uri';
import {EditorModel} from 'vs/workbench/common/editor';
import {guessMimeTypes} from 'vs/base/common/mime';
import {EditorInputAction} from 'vs/workbench/browser/parts/editor/baseEditor';
+import {IModel} from 'vs/editor/common/editorCommon';
import {ResourceEditorInput} from 'vs/workbench/browser/parts/editor/resourceEditorInput';
import {DiffEditorInput} from 'vs/workbench/browser/parts/editor/diffEditorInput';
import {DiffEditorModel} from 'vs/workbench/browser/parts/editor/diffEditorModel';
@@ -142,6 +143,7 @@ export class FileOnDiskEditorInput extends ResourceEditorInput {
private fileResource: URI;
private lastModified: number;
private mime: string;
+ private createdEditorModel: boolean;
constructor(
fileResource: URI,
@@ -175,6 +177,7 @@ export class FileOnDiskEditorInput extends ResourceEditorInput {
let codeEditorModel = this.modelService.getModel(this.resource);
if (!codeEditorModel) {
this.modelService.createModel(content.value, this.modeService.getOrCreateMode(this.mime), this.resource);
+ this.createdEditorModel = true;
} else {
codeEditorModel.setValue(content.value);
}
@@ -182,6 +185,15 @@ export class FileOnDiskEditorInput extends ResourceEditorInput {
return super.resolve(refresh);
});
}
+
+ public dispose(): void {
+ if (this.createdEditorModel) {
+ this.modelService.destroyModel(this.resource);
+ this.createdEditorModel = false;
+ }
+
+ super.dispose();
+ }
}
// A message with action to resolve a 412 save conflict
diff --git a/src/vs/workbench/parts/git/browser/views/changes/changesView.css b/src/vs/workbench/parts/git/browser/views/changes/changesView.css
index 2a4ca960597..a55218bab2e 100644
--- a/src/vs/workbench/parts/git/browser/views/changes/changesView.css
+++ b/src/vs/workbench/parts/git/browser/views/changes/changesView.css
@@ -65,9 +65,9 @@
cursor: default;
}
-.git-viewlet > .changes-view > .status-view > .monaco-tree .monaco-tree-row .monaco-count-badge {
+.git-viewlet > .changes-view > .status-view > .monaco-tree .monaco-tree-row .count-badge-wrapper {
float: right;
- margin-right: 12px;
+ padding-right: 12px;
}
.git-viewlet > .changes-view > .status-view > .monaco-tree .monaco-tree-row:hover .monaco-count-badge {
@@ -89,21 +89,17 @@
}
.git-viewlet > .changes-view > .status-view > .monaco-tree .monaco-tree-row .file-status .status {
- position: absolute;
- top: 4px;
- height: 12px;
- width: 6px;
- line-height: 12px;
padding: 2px 4px;
font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";
- font-size: 9px;
+ font-size: 70%;
color: white;
text-align: center;
- border-radius: 4px;
+ border-radius: 0.5em;
+ vertical-align: bottom;
}
.git-viewlet > .changes-view > .status-view > .monaco-tree .monaco-tree-row .file-status .name {
- margin-left: 20px;
+ margin-left: 0.4em;
}
.git-viewlet > .changes-view > .status-view > .monaco-tree .monaco-tree-row .file-status.modified .status { background-color: #007ACC; }
diff --git a/src/vs/workbench/parts/git/browser/views/changes/changesView.ts b/src/vs/workbench/parts/git/browser/views/changes/changesView.ts
index 3a80c4f174b..ac6ed6173ce 100644
--- a/src/vs/workbench/parts/git/browser/views/changes/changesView.ts
+++ b/src/vs/workbench/parts/git/browser/views/changes/changesView.ts
@@ -138,7 +138,8 @@ export class ChangesView extends EventEmitter.EventEmitter implements GitView.IV
flexibleHeight: true
});
- this.addEmitter2(this.commitInputBox, 'commitInputBox');
+ this.commitInputBox.onDidChange((value) => this.emit('change', value));
+ this.commitInputBox.onDidHeightChange((value) => this.emit('heightchange', value));
$(this.commitInputBox.inputElement).on('keydown', (e:KeyboardEvent) => {
var keyboardEvent = new Keyboard.StandardKeyboardEvent(e);
@@ -179,7 +180,7 @@ export class ChangesView extends EventEmitter.EventEmitter implements GitView.IV
this.tree.expandAll(this.gitService.getModel().getStatus().getGroups());
this.toDispose.push(this.tree.addListener2('selection', (e) => this.onSelection(e)));
- this.toDispose.push(this.commitInputBox.addListener2('heightchange', () => this.layout()));
+ this.toDispose.push(this.commitInputBox.onDidHeightChange(() => this.layout()));
}
public focus():void {
diff --git a/src/vs/workbench/parts/git/browser/views/changes/changesViewer.ts b/src/vs/workbench/parts/git/browser/views/changes/changesViewer.ts
index d5e83f77474..8ad7d53e44d 100644
--- a/src/vs/workbench/parts/git/browser/views/changes/changesViewer.ts
+++ b/src/vs/workbench/parts/git/browser/views/changes/changesViewer.ts
@@ -269,7 +269,10 @@ export class Renderer implements tree.IRenderer {
data.actionBar = new actionbar.ActionBar(container, { actionRunner: this.actionRunner });
data.actionBar.push(this.actionProvider.getActionsForGroupStatusType(statusType), { icon: true, label: false });
data.actionBar.addListener2('run', e => e.error && this.onError(e.error));
- data.count = new countbadge.CountBadge(container);
+
+ const wrapper = dom.append(container, $('.count-badge-wrapper'));
+ data.count = new countbadge.CountBadge(wrapper);
+
data.root = dom.append(container, $('.status-group'));
switch (statusType) {
diff --git a/src/vs/workbench/parts/git/electron-browser/electronGitService.ts b/src/vs/workbench/parts/git/electron-browser/electronGitService.ts
index 7d5c036dd75..180c3631f71 100644
--- a/src/vs/workbench/parts/git/electron-browser/electronGitService.ts
+++ b/src/vs/workbench/parts/git/electron-browser/electronGitService.ts
@@ -52,7 +52,14 @@ function findGitDarwin(): Promise {
return e('git not found');
}
- return c(gitPath);
+ // make sure git executes
+ exec('git --version', err => {
+ if (err) {
+ return e('git not found');
+ }
+
+ return c(gitPath);
+ });
});
});
});
diff --git a/src/vs/workbench/parts/search/browser/media/searchviewlet.css b/src/vs/workbench/parts/search/browser/media/searchviewlet.css
index e8f7cbfcd4c..e83b7bc0b75 100644
--- a/src/vs/workbench/parts/search/browser/media/searchviewlet.css
+++ b/src/vs/workbench/parts/search/browser/media/searchviewlet.css
@@ -158,6 +158,10 @@
font-style: italic;
}
+.search-viewlet .monaco-count-badge {
+ margin-right: 12px;
+}
+
.vs .search-viewlet input.disabled,
.vs .search-viewlet .file-types.disabled .monaco-inputbox {
background-color: #E1E1E1;
diff --git a/src/vs/workbench/parts/search/browser/searchViewlet.ts b/src/vs/workbench/parts/search/browser/searchViewlet.ts
index 9d01f5296b4..91969b5ff1d 100644
--- a/src/vs/workbench/parts/search/browser/searchViewlet.ts
+++ b/src/vs/workbench/parts/search/browser/searchViewlet.ts
@@ -451,7 +451,7 @@ class PatternInput {
}
public destroy(): void {
- this.pattern.destroy();
+ this.pattern.dispose();
this.listenersToRemove.forEach((element) => {
element();
});
@@ -523,11 +523,11 @@ class PatternInput {
}
public isGlobPattern(): boolean {
- return this.pattern.isChecked;
+ return this.pattern.checked;
}
public setIsGlobPattern(value: boolean): void {
- this.pattern.setChecked(value);
+ this.pattern.checked = value;
this.setInputWidth();
}
@@ -550,15 +550,20 @@ class PatternInput {
}
});
- this.pattern = new Checkbox('pattern', nls.localize('patternDescription', "Use Glob Patterns"), false, () => {
- this.onOptionChange(null);
- this.inputBox.focus();
- this.setInputWidth();
+ this.pattern = new Checkbox({
+ actionClassName: 'pattern',
+ title: nls.localize('patternDescription', "Use Glob Patterns"),
+ isChecked: false,
+ onChange: () => {
+ this.onOptionChange(null);
+ this.inputBox.focus();
+ this.setInputWidth();
- if (this.isGlobPattern()) {
- this.showGlobHelp();
- } else {
- this.inputBox.hideMessage();
+ if (this.isGlobPattern()) {
+ this.showGlobHelp();
+ } else {
+ this.inputBox.hideMessage();
+ }
}
});
@@ -697,8 +702,7 @@ export class SearchViewlet extends Viewlet {
builder = div;
});
- let onKeyUp = (e: KeyboardEvent) => {
- let keyboardEvent = new StandardKeyboardEvent(e);
+ let onStandardKeyUp = (keyboardEvent: StandardKeyboardEvent) => {
if (keyboardEvent.keyCode === KeyCode.Enter) {
this.onQueryChanged(true);
} else if (keyboardEvent.keyCode === KeyCode.Escape) {
@@ -712,6 +716,10 @@ export class SearchViewlet extends Viewlet {
}
};
+ let onKeyUp = (e: KeyboardEvent) => {
+ onStandardKeyUp(new StandardKeyboardEvent(e));
+ };
+
this.queryBox = builder.div({ 'class': 'query-box' }, (div) => {
let options = {
label: nls.localize('label.Search', 'Search Term'),
@@ -733,22 +741,21 @@ export class SearchViewlet extends Viewlet {
}
}
};
- this.findInput = new FindInput(div.getHTMLElement(), this.contextViewService, options)
- .on(dom.EventType.KEY_UP, onKeyUp)
- .on(dom.EventType.KEY_DOWN, (e: KeyboardEvent) => {
- let keyboardEvent = new StandardKeyboardEvent(e);
- if (keyboardEvent.keyCode === KeyCode.DownArrow) {
- dom.EventHelper.stop(e);
- if (this.showsFileTypes()) {
- this.toggleFileTypes(true);
- } else {
- this.selectTreeIfNotSelected(keyboardEvent);
- }
+ this.findInput = new FindInput(div.getHTMLElement(), this.contextViewService, options);
+ this.findInput.onKeyUp(onStandardKeyUp);
+ this.findInput.onKeyDown((keyboardEvent:StandardKeyboardEvent) => {
+ if (keyboardEvent.keyCode === KeyCode.DownArrow) {
+ dom.EventHelper.stop(keyboardEvent);
+ if (this.showsFileTypes()) {
+ this.toggleFileTypes(true);
+ } else {
+ this.selectTreeIfNotSelected(keyboardEvent);
}
- })
- .on(FindInput.OPTION_CHANGE, (e) => {
- this.onQueryChanged(true);
- });
+ }
+ });
+ this.findInput.onDidOptionChange(() => {
+ this.onQueryChanged(true);
+ });
this.findInput.setValue(contentPattern);
this.findInput.setRegex(isRegex);
this.findInput.setCaseSensitive(isCaseSensitive);
diff --git a/src/vs/workbench/services/request/node/rawHttpService.ts b/src/vs/workbench/services/request/node/rawHttpService.ts
index 04495ce01cb..bb854412d4e 100644
--- a/src/vs/workbench/services/request/node/rawHttpService.ts
+++ b/src/vs/workbench/services/request/node/rawHttpService.ts
@@ -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 {
- 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((c, e, p) => {
let res = result.res;
@@ -88,12 +63,10 @@ export function xhr(options: IXHROptions): TPromise {
}
});
}, 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;
}
diff --git a/src/vs/workbench/services/request/node/requestService.ts b/src/vs/workbench/services/request/node/requestService.ts
index 6a6343bbaa5..8288bf2cbdd 100644
--- a/src/vs/workbench/services/request/node/requestService.ts
+++ b/src/vs/workbench/services/request/node/requestService.ts
@@ -23,7 +23,7 @@ import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
interface IRawHttpService {
xhr(options: http.IXHROptions): TPromise;
- 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 {
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;
});
}