merge with master

This commit is contained in:
Johannes Rieken 2016-01-11 13:03:39 +01:00
commit 44b8ebe084
189 changed files with 5097 additions and 2734 deletions

View file

@ -57,7 +57,7 @@
},
{
"name": "electron",
"version": "0.35.5",
"version": "0.35.6",
"license": "MIT",
"repositoryURL": "https://github.com/atom/electron",
"isProd": true

View file

@ -1,5 +1,5 @@
environment:
ELECTRON_RUN_AS_NODE: 1
ATOM_SHELL_INTERNAL_RUN_AS_NODE: 1
install:
- ps: Install-Product node 4.1.1 x64

View file

@ -10,7 +10,7 @@
<key>name</key>
<string>keyword.command.dosbatch</string>
<key>match</key>
<string>\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</string>
<string>\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</string>
</dict>
<dict>
<key>name</key>
@ -54,7 +54,7 @@
<key>name</key>
<string>comment.line.rem.dosbatch</string>
<key>match</key>
<string>(?:^|\s)((?i)rem)(?:$|\s.*$)</string>
<string>\b((?i)rem)(?:$|\s.*$)</string>
</dict>
<dict>
<key>name</key>
@ -62,6 +62,72 @@
<key>match</key>
<string>\s*:\s*:.*$</string>
</dict>
<dict>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>variable.parameter.function.begin.shell</string>
</dict>
</dict>
<key>name</key>
<string>variable.parameter.function.dosbatch</string>
<key>match</key>
<string>(?i)(%)(~(?:f|d|p|n|x|s|a|t|z|\$[^:]*:)*)?\d</string>
</dict>
<dict>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>variable.parameter.loop.begin.shell</string>
</dict>
</dict>
<key>name</key>
<string>variable.parameter.loop.dosbatch</string>
<key>match</key>
<string>(?i)(%%)(~(?:f|d|p|n|x|s|a|t|z|\$[^:]*:)*)?[a-z]</string>
</dict>
<dict>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>variable.other.parsetime.begin.shell</string>
</dict>
<key>2</key>
<dict>
<key>name</key>
<string>variable.other.parsetime.end.shell</string>
</dict>
</dict>
<key>name</key>
<string>variable.other.parsetime.dosbatch</string>
<key>match</key>
<string>(%)[^%]+(%)</string>
</dict>
<dict>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>variable.other.delayed.begin.shell</string>
</dict>
<key>2</key>
<dict>
<key>name</key>
<string>variable.other.delayed.end.shell</string>
</dict>
</dict>
<key>name</key>
<string>variable.other.delayed.dosbatch</string>
<key>match</key>
<string>(!)[^!]+(!)</string>
</dict>
<dict>
<key>begin</key>
<string>"</string>
@ -84,7 +150,7 @@
<key>name</key>
<string>string.quoted.double.dosbatch</string>
<key>end</key>
<string>"</string>
<string>"|$</string>
</dict>
<dict>
<key>name</key>

View file

@ -1,6 +1,6 @@
{
"account": "monacobuild",
"container": "debuggers",
"zip": "b5b96dd/node-debug.zip",
"zip": "beaf9dc/node-debug.zip",
"output": ""
}

View file

@ -3,7 +3,7 @@
"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.5",
"version": "0.1.6",
"publisher": "aeschli",
"engines": { "vscode": "*" },
"contributes": {

View file

@ -10,7 +10,7 @@
<key>name</key>
<string>Function declarations</string>
<key>scope</key>
<string>entity.name.function</string>
<string>entity.name.function, entity.method.name</string>
<key>settings</key>
<dict>
<key>foreground</key>
@ -21,7 +21,7 @@
<key>name</key>
<string>Types declaration and references</string>
<key>scope</key>
<string>meta.parameter.type, entity.name.class, new.storage.type, meta.cast, cast.storage.type, heritage.storage.type, annotation.storage.type, var.annotation.storage.type</string>
<string>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</string>
<key>settings</key>
<dict>
<key>foreground</key>

View file

@ -10,7 +10,7 @@
<key>name</key>
<string>Function declarations</string>
<key>scope</key>
<string>entity.name.function</string>
<string>entity.name.function, entity.method.name</string>
<key>settings</key>
<dict>
<key>foreground</key>
@ -21,7 +21,7 @@
<key>name</key>
<string>Types declaration and references</string>
<key>scope</key>
<string>meta.parameter.type, entity.name.class, new.storage.type, meta.cast, cast.storage.type, heritage.storage.type, annotation.storage.type, var.annotation.storage.type</string>
<string>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</string> <key>settings</key>
<key>settings</key>
<dict>
<key>foreground</key>

View file

@ -100,7 +100,7 @@ function registerSupports(modeID: string, host: TypeScriptServiceClientHost, cli
},
{
// e.g. * ...|
beforeText: /^(\t|(\ \ ))*\ \*\ ([^\*]|\*(?!\/))*$/,
beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
action: { indentAction: IndentAction.None, appendText: '* ' }
},
{

View file

@ -47,7 +47,7 @@ function generatePatchedEnv(env:any, stdInPipeName:string, stdOutPipeName:string
newEnv['STDIN_PIPE_NAME'] = stdInPipeName;
newEnv['STDOUT_PIPE_NAME'] = stdOutPipeName;
newEnv['ELECTRON_RUN_AS_NODE'] = '1';
newEnv['ATOM_SHELL_INTERNAL_RUN_AS_NODE'] = '1';
return newEnv;
}

View file

@ -31,7 +31,7 @@ var stdOutPipeName = process.env['STDOUT_PIPE_NAME'];
log('STDIN_PIPE_NAME: ' + stdInPipeName);
log('STDOUT_PIPE_NAME: ' + stdOutPipeName);
log('ELECTRON_RUN_AS_NODE: ' + process.env['ELECTRON_RUN_AS_NODE']);
log('ATOM_SHELL_INTERNAL_RUN_AS_NODE: ' + process.env['ATOM_SHELL_INTERNAL_RUN_AS_NODE']);
// stdout redirection to named pipe
(function() {
@ -136,7 +136,7 @@ log('ELECTRON_RUN_AS_NODE: ' + process.env['ELECTRON_RUN_AS_NODE']);
// Unset the custom environmental variables that should not get inherited
delete process.env['STDIN_PIPE_NAME'];
delete process.env['STDOUT_PIPE_NAME'];
delete process.env['ELECTRON_RUN_AS_NODE'];
delete process.env['ATOM_SHELL_INTERNAL_RUN_AS_NODE'];
require(program);

View file

@ -9,7 +9,8 @@
},
"scripts": {
"compile": "node ./node_modules/vscode/bin/compile -watch -p ./",
"vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../gulpfile.plugins.js compile-plugin:vscode-api-tests ./tsconfig.json"
"vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../gulpfile.plugins.js compile-plugin:vscode-api-tests ./tsconfig.json",
"postinstall": "node ./node_modules/vscode/bin/install"
},
"devDependencies": {
"typescript": "^1.6.2",

View file

@ -1,7 +1,7 @@
{
"name": "Code",
"version": "0.10.6",
"electronVersion": "0.35.5",
"electronVersion": "0.35.6",
"author": {
"name": "Microsoft Corporation"
},

View file

@ -1,6 +1,6 @@
@echo off
setlocal
set VSCODE_DEV=
set ELECTRON_RUN_AS_NODE=1
set ATOM_SHELL_INTERNAL_RUN_AS_NODE=1
"%~dp0..\\Code.exe" "%~dp0code.js" %*
endlocal

View file

@ -1,3 +1,3 @@
delete process.env['ELECTRON_RUN_AS_NODE'];
delete process.env['ATOM_SHELL_INTERNAL_RUN_AS_NODE'];
require('child_process').spawn(require('path').resolve(__dirname, '..', 'Code.exe'), process.argv.slice(2), { detached: true, stdio: 'ignore' });
process.exit(0);

View file

@ -1,6 +1,6 @@
@echo off
set ELECTRON_RUN_AS_NODE=1
set ATOM_SHELL_INTERNAL_RUN_AS_NODE=1
pushd %~dp0\..
.\.build\electron\Code.exe .\node_modules\mocha\bin\_mocha %*

View file

@ -9,11 +9,11 @@ fi
# Unit Tests
if [[ "$OSTYPE" == "darwin"* ]]; then
cd $ROOT ; ulimit -n 4096 ; ELECTRON_RUN_AS_NODE=1 \
cd $ROOT ; ulimit -n 4096 ; ATOM_SHELL_INTERNAL_RUN_AS_NODE=1 \
./.build/electron/Electron.app/Contents/MacOS/Electron \
node_modules/mocha/bin/_mocha $*
else
cd $ROOT ; ELECTRON_RUN_AS_NODE=1 \
cd $ROOT ; ATOM_SHELL_INTERNAL_RUN_AS_NODE=1 \
./.build/electron/electron \
node_modules/mocha/bin/_mocha $*
fi

View file

@ -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;
(<any>e).cancelBubble = true;
}
}
}
@ -1068,10 +1073,14 @@ export function emmet(description: string):HTMLElement {
return result;
};
export function show(element: HTMLElement): void {
element.style.display = null;
export function show(...elements: HTMLElement[]): void {
for (const element of elements) {
element.style.display = null;
}
}
export function hide(element: HTMLElement): void {
element.style.display = 'none';
export function hide(...elements: HTMLElement[]): void {
for (const element of elements) {
element.style.display = 'none';
}
}

View file

@ -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 = [];
}
}
}

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
.monaco-count-badge {
padding: 0.2em 0.4em;
padding: 0.2em 0.5em;
border-radius: 1em;
font-size: 85%;
font-weight: normal;

View file

@ -33,6 +33,28 @@
right: 2px;
}
.monaco-findInput > .controls > .matchCount {
margin-left: 2px;
float: left;
overflow: hidden;
max-width: 30px;
min-width: 20px;
text-align: center;
border-radius: 5px;
padding: 0 4px;
-webkit-box-sizing: border-box;
-o-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
}
.vs .monaco-findInput > .controls > .matchCount {
background: #ddd;
}
.vs .monaco-findInput > .controls > .custom-checkbox.case-sensitive {
background: url('case-sensitive.svg') center center no-repeat;
}
@ -54,6 +76,10 @@
background-color: #333;
}
.vs-dark .monaco-findInput > .controls > .matchCount {
background: #555;
}
.vs-dark .monaco-findInput > .controls > .custom-checkbox.case-sensitive {
background: url('case-sensitive-dark.svg') center center no-repeat;
}

View file

@ -5,20 +5,21 @@
'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';
import {StandardMouseEvent} from 'vs/base/browser/mouseEvent';
export interface IOptions {
export interface IFindInputOptions {
placeholder?:string;
width?:number;
validation?:InputBox.IInputValidator;
validation?:IInputValidator;
label:string;
appendCaseSensitiveLabel?: string;
@ -26,78 +27,72 @@ export interface IOptions {
appendRegexLabel?: string;
}
export interface IMatchCountState {
count: string;
isVisible: boolean;
title: string;
}
const NLS_REGEX_CHECKBOX_LABEL = nls.localize('regexDescription', "Use Regular Expression");
const NLS_WHOLE_WORD_CHECKBOX_LABEL = nls.localize('wordsDescription', "Match Whole Word");
const NLS_CASE_SENSITIVE_CHECKBOX_LABEL = nls.localize('caseDescription', "Match Case");
const NLS_DEFAULT_LABEL = nls.localize('defaultLabel', "input");
export class FindInput {
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;
private matchCount: MatchCount;
public domNode: HTMLElement;
public validationNode: Builder.Builder;
public inputBox:InputBox.InputBox;
public inputBox:InputBox;
constructor(parent:HTMLElement, contextViewProvider: ContextView.IContextViewProvider, options?:IOptions) {
private _onDidOptionChange = this._register(new Emitter<void>());
public onDidOptionChange: Event<void> = this._onDidOptionChange.event;
private _onKeyDown = this._register(new Emitter<StandardKeyboardEvent>());
public onKeyDown: Event<StandardKeyboardEvent> = this._onKeyDown.event;
private _onKeyUp = this._register(new Emitter<StandardKeyboardEvent>());
public onKeyUp: Event<StandardKeyboardEvent> = this._onKeyUp.event;
private _onCaseSensitiveKeyDown = this._register(new Emitter<StandardKeyboardEvent>());
public onCaseSensitiveKeyDown: Event<StandardKeyboardEvent> = 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.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();
@ -105,7 +100,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();
@ -135,6 +130,11 @@ export class FindInput {
}
}
public setMatchCountState(state:IMatchCountState): void {
this.matchCount.setState(state);
this.setInputWidth();
}
public select(): void {
this.inputBox.select();
}
@ -144,29 +144,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();
}
@ -175,46 +175,72 @@ export class FindInput {
}
private setInputWidth(): void {
var w = this.width - this.caseSensitive.width() - this.wholeWords.width() - this.regex.width();
let w = this.width - this.matchCount.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 = 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.matchCount = this._register(new MatchCount({
onClick: (e) => {
this.inputBox.focus();
e.preventDefault();
}
}));
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.setInputWidth();
var controls = document.createElement('div');
let controls = document.createElement('div');
controls.className = 'controls';
controls.appendChild(this.matchCount.domNode);
controls.appendChild(this.caseSensitive.domNode);
controls.appendChild(this.wholeWords.domNode);
controls.appendChild(this.regex.domNode);
@ -226,7 +252,7 @@ export class FindInput {
this.inputBox.validate();
}
public showMessage(message: InputBox.IMessage): void {
public showMessage(message: InputBoxMessage): void {
this.inputBox.showMessage(message);
}
@ -238,3 +264,43 @@ export class FindInput {
this.inputBox.hideMessage();
}
}
interface IMatchCountOpts {
onClick: (e:StandardMouseEvent) => void;
}
class MatchCount extends Widget {
public domNode: HTMLElement;
private isVisible: boolean;
constructor(opts:IMatchCountOpts) {
super();
this.domNode = document.createElement('div');
this.domNode.className = 'matchCount';
this.setState({
isVisible: false,
count: '0',
title: ''
});
this.onclick(this.domNode, opts.onClick);
}
public width(): number {
return this.isVisible ? 30 : 0;
}
public setState(state:IMatchCountState): void {
dom.clearNode(this.domNode);
this.domNode.appendChild(document.createTextNode(state.count));
this.domNode.title = state.title;
this.isVisible = state.isVisible;
if (this.isVisible) {
this.domNode.style.display = 'block';
} else {
this.domNode.style.display = 'none';
}
}
}

View file

@ -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<string>());
public onDidChange: Event<string> = this._onDidChange.event;
private _onDidHeightChange = this._register(new Emitter<number>());
public onDidHeightChange: Event<number> = 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 = <HTMLInputElement> 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 = <any>renderer.renderHtml(renderOptions);
let spanElement:HTMLElement = <any>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();
}

View file

@ -0,0 +1,42 @@
/*---------------------------------------------------------------------------------------------
* 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));
}
protected onchange(domNode:HTMLElement, listener:(e:Event)=>void): void {
this._register(DomUtils.addDisposableListener(domNode, DomUtils.EventType.CHANGE, listener));
}
}

View file

@ -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;

View file

@ -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 extends IDisposable>(t:T): T {
this._toDispose.push(t);
return t;
}
}

View file

@ -190,6 +190,7 @@ export abstract class AbstractProcess<TProgressData> {
} else {
let childProcess: ChildProcess = null;
let closeHandler = (data: any) => {
console.log("Close received");
this.childProcess = null;
this.childProcessPromise = null;
this.handleClose(data, cc, pp, ee);
@ -201,18 +202,6 @@ export abstract class AbstractProcess<TProgressData> {
}
cc(result);
}
let exitHandler = (data: any) => {
this.childProcess = null;
this.childProcessPromise = null;
this.handleExit(data, cc, pp, ee);
let result: SuccessData = {
terminated: this.terminateRequested
};
if (this.shell && Platform.isWindows && Types.isNumber(data)) {
result.cmdCode = <number>data;
}
cc(result);
};
if (this.shell && Platform.isWindows) {
let options: any = Objects.clone(this.options);
options.windowsVerbatimArguments = true;
@ -259,7 +248,6 @@ export abstract class AbstractProcess<TProgressData> {
}
this.childProcess = childProcess;
this.childProcess.on('close', closeHandler);
this.childProcess.on('exit', exitHandler);
this.handleSpawn(childProcess, cc, pp, ee, false);
c(childProcess);
});
@ -275,7 +263,6 @@ export abstract class AbstractProcess<TProgressData> {
});
if (childProcess.pid) {
this.childProcess.on('close', closeHandler);
this.childProcess.on('exit', exitHandler);
this.handleSpawn(childProcess, cc, pp, ee, true);
}
}
@ -290,9 +277,6 @@ export abstract class AbstractProcess<TProgressData> {
protected handleClose(data: any, cc: TValueCallback<SuccessData>, pp: TProgressCallback<TProgressData>, ee: ErrorCallback): void {
// Default is to do nothing.
}
protected handleExit(data: any, cc: TValueCallback<SuccessData>, pp: TProgressCallback<TProgressData>, ee: ErrorCallback): void {
// Default is to do nothing.
}
private static regexp = /^[^"].* .*[^"]/;
private ensureQuotes(value: string) {
@ -396,16 +380,6 @@ export class LineProcess extends AbstractProcess<LineData> {
}
});
}
protected handleExit(data: any, cc: TValueCallback<SuccessData>, pp: TProgressCallback<LineData>, ee: ErrorCallback): void {
if (this.terminateRequested) {
[this.stdoutLineDecoder.end(), this.stderrLineDecoder.end()].forEach((line, index) => {
if (line) {
pp({ line: line, source: index === 0 ? Source.stdout : Source.stderr });
}
});
}
}
}
export class BufferProcess extends AbstractProcess<BufferData> {

View file

@ -6,12 +6,34 @@
'use strict';
import { Url, parse as parseUrl } from 'url';
import { isBoolean } from 'vs/base/common/types';
import HttpProxyAgent = require('http-proxy-agent');
import HttpsProxyAgent = require('https-proxy-agent');
function getAgent(rawRequestURL: string, proxyURL: string, strictSSL: boolean = true): any {
let requestURL = parseUrl(rawRequestURL);
let proxyEndpoint = parseUrl(proxyURL);
function getSystemProxyURI(requestURL: Url): string {
if (requestURL.protocol === 'http:') {
return process.env.HTTP_PROXY || process.env.http_proxy || null;
} else if (requestURL.protocol === 'https:') {
return process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy || null;
}
return null;
}
export interface IOptions {
proxyUrl?: string;
strictSSL?: boolean;
}
export function getProxyAgent(rawRequestURL: string, options: IOptions = {}): any {
const requestURL = parseUrl(rawRequestURL);
const proxyURL = options.proxyUrl || getSystemProxyURI(requestURL);
if (!proxyURL) {
return null;
}
const proxyEndpoint = parseUrl(proxyURL);
if (!/^https?:$/.test(proxyEndpoint.protocol)) {
return null;
@ -24,40 +46,6 @@ function getAgent(rawRequestURL: string, proxyURL: string, strictSSL: boolean =
return new HttpsProxyAgent({
host: proxyEndpoint.hostname,
port: Number(proxyEndpoint.port),
rejectUnauthorized: strictSSL
rejectUnauthorized: isBoolean(options.strictSSL) ? options.strictSSL : true
});
}
function getSystemProxyURI(requestURL: Url): string {
if (requestURL.protocol === 'http:') {
return process.env.HTTP_PROXY || process.env.http_proxy || null;
} else if (requestURL.protocol === 'https:') {
return process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy || null;
}
return null;
}
function getSystemProxyAgent(rawRequestURL: string): any {
let requestURL = parseUrl(rawRequestURL);
let proxyURL = getSystemProxyURI(requestURL);
if (!proxyURL) {
return null;
}
return getAgent(rawRequestURL, proxyURL);
}
export interface IOptions {
proxyUrl?: string;
strictSSL?: boolean;
}
export function getProxyAgent(rawRequestURL: string, options: IOptions = {}): any {
if (!options.proxyUrl) {
return getSystemProxyAgent(rawRequestURL);
}
return getAgent(rawRequestURL, options.proxyUrl, options.strictSSL);
}

View file

@ -48,7 +48,7 @@ function generatePatchedEnv(env:any, stdInPipeName:string, stdOutPipeName:string
newEnv['STDIN_PIPE_NAME'] = stdInPipeName;
newEnv['STDOUT_PIPE_NAME'] = stdOutPipeName;
newEnv['ELECTRON_RUN_AS_NODE'] = '1';
newEnv['ATOM_SHELL_INTERNAL_RUN_AS_NODE'] = '1';
return newEnv;
}

View file

@ -31,7 +31,7 @@ var stdOutPipeName = process.env['STDOUT_PIPE_NAME'];
log('STDIN_PIPE_NAME: ' + stdInPipeName);
log('STDOUT_PIPE_NAME: ' + stdOutPipeName);
log('ELECTRON_RUN_AS_NODE: ' + process.env['ELECTRON_RUN_AS_NODE']);
log('ATOM_SHELL_INTERNAL_RUN_AS_NODE: ' + process.env['ATOM_SHELL_INTERNAL_RUN_AS_NODE']);
// stdout redirection to named pipe
(function() {
@ -136,7 +136,7 @@ log('ELECTRON_RUN_AS_NODE: ' + process.env['ELECTRON_RUN_AS_NODE']);
// Unset the custom environmental variables that should not get inherited
delete process.env['STDIN_PIPE_NAME'];
delete process.env['STDOUT_PIPE_NAME'];
delete process.env['ELECTRON_RUN_AS_NODE'];
delete process.env['ATOM_SHELL_INTERNAL_RUN_AS_NODE'];
require(program);

View file

@ -573,6 +573,41 @@ export interface ICodeEditor extends EditorCommon.ICommonCodeEditor {
* Change the view zones. View zones are lost when a new model is attached to the editor.
*/
changeViewZones(callback: (accessor: IViewZoneChangeAccessor) => void): void;
/**
* Returns the range that is currently centered in the view port.
*/
getCenteredRangeInViewport(): EditorCommon.IEditorRange;
/**
* Get the view zones.
*/
getWhitespaces(): EditorCommon.IEditorWhitespace[];
/**
* Get the horizontal position (left offset) for the column w.r.t to the beginning of the line.
* This method works only if the line `lineNumber` is currently rendered (in the editor's viewport).
* Use this method with caution.
*/
getOffsetForColumn(lineNumber: number, column: number): number;
/**
* Get the vertical position (top offset) for the line w.r.t. to the first line.
*/
getTopForLineNumber(lineNumber: number): number;
/**
* Get the vertical position (top offset) for the position w.r.t. to the first line.
*/
getTopForPosition(lineNumber: number, column: number): number;
/**
* Get the visible position for `position`.
* The result position takes scrolling into account and is relative to the top left corner of the editor.
* Explanation 1: the results of this method will change for the same `position` if the user scrolls the editor.
* Explanation 2: the results of this method will not change if the container of the editor gets repositioned.
* Warning: the results of this method are innacurate for positions that are outside the current editor viewport.
*/
getScrolledVisiblePosition(position: EditorCommon.IPosition): { top: number; left: number; height: number; };
}
/**

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,939 @@
/*---------------------------------------------------------------------------------------------
* 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 {TPromise} from 'vs/base/common/winjs.base';
import * as Objects from 'vs/base/common/objects';
import {onUnexpectedError} from 'vs/base/common/errors';
import EventEmitter = require('vs/base/common/eventEmitter');
import {Cursor} from 'vs/editor/common/controller/cursor';
import {CharacterHardWrappingLineMapperFactory} from 'vs/editor/common/viewModel/characterHardWrappingLineMapper';
import {SplitLinesCollection} from 'vs/editor/common/viewModel/splitLinesCollection';
import {ViewModel} from 'vs/editor/common/viewModel/viewModel';
import * as Timer from 'vs/base/common/timer';
import {CommonEditorRegistry} from 'vs/editor/common/editorCommonExtensions';
import {IActionProvider, IAction, isAction} from 'vs/base/common/actions';
import {CursorMoveHelper} from 'vs/editor/common/controller/cursorMoveHelper';
import {IViewModelHelper} from 'vs/editor/common/controller/oneCursor';
import {IDisposable,disposeAll} from 'vs/base/common/lifecycle';
import {DynamicEditorAction} from 'vs/editor/common/editorAction';
import {ICodeEditorService} from 'vs/editor/common/services/codeEditorService';
import {Range} from 'vs/editor/common/core/range';
import {Position} from 'vs/editor/common/core/position';
import {Selection} from 'vs/editor/common/core/selection';
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';
import {EditorState} from 'vs/editor/common/core/editorState';
import {IKeybindingScopeLocation, IKeybindingService, IKeybindingContextKey} from 'vs/platform/keybinding/common/keybindingService';
import {CommonEditorConfiguration, IIndentationGuesser} from 'vs/editor/common/config/commonEditorConfig';
var EDITOR_ID = 0;
export abstract class CommonCodeEditor extends EventEmitter.EventEmitter implements IActionProvider, EditorCommon.ICommonCodeEditor {
protected domElement: IKeybindingScopeLocation;
protected id:number;
_lifetimeListeners:EventEmitter.ListenerUnbind[];
_lifetimeDispose: IDisposable[];
_configuration:CommonEditorConfiguration;
_telemetryService:ITelemetryService;
protected contributions:{ [key:string]:EditorCommon.IEditorContribution; };
protected forcedWidgetFocusCount:number;
// --- Members logically associated to a model
protected model:EditorCommon.IModel;
protected listenersToRemove:EventEmitter.ListenerUnbind[];
protected hasView: boolean;
protected viewModel:ViewModel;
protected cursor:Cursor;
protected _instantiationService: IInstantiationService;
protected _keybindingService: IKeybindingService;
private _decorationTypeKeysToIds: {[decorationTypeKey:string]:string[];};
private _codeEditorService: ICodeEditorService;
private _editorIdContextKey: IKeybindingContextKey<string>;
protected _editorFocusContextKey: IKeybindingContextKey<boolean>;
private _editorTabMovesFocusKey: IKeybindingContextKey<boolean>;
private _hasMultipleSelectionsKey: IKeybindingContextKey<boolean>;
private _hasNonEmptySelectionKey: IKeybindingContextKey<boolean>;
private _langIdKey: IKeybindingContextKey<string>;
constructor(
domElement: IKeybindingScopeLocation,
options:EditorCommon.ICodeEditorWidgetCreationOptions,
instantiationService: IInstantiationService,
codeEditorService: ICodeEditorService,
keybindingService: IKeybindingService,
telemetryService: ITelemetryService
) {
super();
this.domElement = domElement;
this.id = (++EDITOR_ID);
this._codeEditorService = codeEditorService;
var timerEvent = Timer.start(Timer.Topic.EDITOR, 'CodeEditor.ctor');
this._lifetimeDispose = [];
this._keybindingService = keybindingService.createScoped(domElement);
this._editorIdContextKey = this._keybindingService.createKey('editorId', this.getId());
this._editorFocusContextKey = this._keybindingService.createKey(EditorCommon.KEYBINDING_CONTEXT_EDITOR_FOCUS, undefined);
this._editorTabMovesFocusKey = this._keybindingService.createKey(EditorCommon.KEYBINDING_CONTEXT_EDITOR_TAB_MOVES_FOCUS, false);
this._hasMultipleSelectionsKey = this._keybindingService.createKey(EditorCommon.KEYBINDING_CONTEXT_EDITOR_HAS_MULTIPLE_SELECTIONS, false);
this._hasNonEmptySelectionKey = this._keybindingService.createKey(EditorCommon.KEYBINDING_CONTEXT_EDITOR_HAS_NON_EMPTY_SELECTION, false);
this._langIdKey = this._keybindingService.createKey<string>(EditorCommon.KEYBINDING_CONTEXT_EDITOR_LANGUAGE_ID, undefined);
// listeners that are kept during the whole editor lifetime
this._lifetimeListeners = [];
this._decorationTypeKeysToIds = {};
options = options || {};
var model: EditorCommon.IModel = null;
if (options.model) {
model = options.model;
delete options.model;
}
this._configuration = this._createConfiguration(options, (tabSize:number) => {
if (this.model) {
return this.model.guessIndentation(tabSize);
}
return null;
});
if (this._configuration.editor.tabFocusMode) {
this._editorTabMovesFocusKey.set(true);
}
this._lifetimeListeners.push(this._configuration.addListener(EditorCommon.EventType.ConfigurationChanged, (e) => this.emit(EditorCommon.EventType.ConfigurationChanged, e)));
this.forcedWidgetFocusCount = 0;
this._telemetryService = telemetryService;
this._instantiationService = instantiationService.createChild({
keybindingService: this._keybindingService
});
this._attachModel(model);
// Create editor contributions
this.contributions = {};
timerEvent.stop();
this._codeEditorService.addCodeEditor(this);
}
protected abstract _createConfiguration(options:EditorCommon.ICodeEditorWidgetCreationOptions, indentationGuesser:IIndentationGuesser): CommonEditorConfiguration;
public getId(): string {
return this.getEditorType() + ':' + this.id;
}
public getEditorType(): string {
return EditorCommon.EditorType.ICodeEditor;
}
public destroy(): void {
this.dispose();
}
public dispose(): void {
this._codeEditorService.removeCodeEditor(this);
this._lifetimeDispose = disposeAll(this._lifetimeDispose);
// unbind listeners
while(this._lifetimeListeners.length > 0) {
this._lifetimeListeners.pop()();
}
var contributionId:string;
for (contributionId in this.contributions) {
if (this.contributions.hasOwnProperty(contributionId)) {
this.contributions[contributionId].dispose();
}
}
this.contributions = {};
this._postDetachModelCleanup(this._detachModel());
this._configuration.dispose();
this._keybindingService.dispose();
this.emit(EditorCommon.EventType.Disposed, {});
super.dispose();
}
public captureState(...flags:EditorCommon.CodeEditorStateFlag[]): EditorCommon.ICodeEditorState {
return new EditorState(this, flags);
}
public updateOptions(newOptions:EditorCommon.IEditorOptions): void {
this._configuration.updateOptions(newOptions);
if (this._configuration.editor.tabFocusMode) {
this._editorTabMovesFocusKey.set(true);
} else {
this._editorTabMovesFocusKey.reset();
}
}
public getConfiguration(): EditorCommon.IInternalEditorOptions {
return Objects.clone(this._configuration.editor);
}
public getRawConfiguration(): EditorCommon.IEditorOptions {
return this._configuration.getRawOptions();
}
public getIndentationOptions(): EditorCommon.IInternalIndentationOptions {
return Objects.clone(this._configuration.getIndentationOptions());
}
public normalizeIndentation(str:string): string {
return this._configuration.normalizeIndentation(str);
}
public getValue(options:{ preserveBOM:boolean; lineEnding:string; }=null): string {
if (this.model) {
var preserveBOM:boolean = (options && options.preserveBOM) ? true : false;
var eolPreference = EditorCommon.EndOfLinePreference.TextDefined;
if (options && options.lineEnding && options.lineEnding === '\n') {
eolPreference = EditorCommon.EndOfLinePreference.LF;
} else if (options && options.lineEnding && options.lineEnding === '\r\n') {
eolPreference = EditorCommon.EndOfLinePreference.CRLF;
}
return this.model.getValue(eolPreference, preserveBOM);
}
return '';
}
public setValue(newValue:string): void {
if (this.model) {
this.model.setValue(newValue);
}
}
public getModel(): EditorCommon.IModel {
return this.model;
}
public setModel(model:EditorCommon.IModel = null): void {
if (this.model === model) {
// Current model is the new model
return;
}
var timerEvent = Timer.start(Timer.Topic.EDITOR, 'CodeEditor.setModel');
var detachedModel = this._detachModel();
this._attachModel(model);
var oldModelUrl: string = null;
var newModelUrl: string = null;
if (detachedModel) {
oldModelUrl = detachedModel.getAssociatedResource().toString();
}
if (model) {
newModelUrl = model.getAssociatedResource().toString();
}
var e: EditorCommon.IModelChangedEvent = {
oldModelUrl: oldModelUrl,
newModelUrl: newModelUrl
};
timerEvent.stop();
this.emit(EditorCommon.EventType.ModelChanged, e);
this._postDetachModelCleanup(detachedModel);
}
public abstract getCenteredRangeInViewport(): EditorCommon.IEditorRange;
public getVisibleColumnFromPosition(rawPosition:EditorCommon.IPosition): number {
if (!this.model) {
return rawPosition.column;
}
var position = this.model.validatePosition(rawPosition);
return CursorMoveHelper.visibleColumnFromColumn(this.model, position.lineNumber, position.column, this._configuration.getIndentationOptions().tabSize) + 1;
}
public getPosition(): EditorCommon.IEditorPosition {
if (!this.cursor) {
return null;
}
return this.cursor.getPosition().clone();
}
public setPosition(position:EditorCommon.IPosition, reveal:boolean = false, revealVerticalInCenter:boolean = false, revealHorizontal:boolean = false): void {
if (!this.cursor) {
return;
}
if (!Position.isIPosition(position)) {
throw new Error('Invalid arguments');
}
this.cursor.setSelections('api', [{
selectionStartLineNumber: position.lineNumber,
selectionStartColumn: position.column,
positionLineNumber: position.lineNumber,
positionColumn: position.column
}]);
if (reveal) {
this.revealPosition(position, revealVerticalInCenter, revealHorizontal);
}
}
private _sendRevealRange(range: EditorCommon.IRange, verticalType: EditorCommon.VerticalRevealType, revealHorizontal: boolean): void {
if (!this.model || !this.cursor) {
return;
}
if (!Range.isIRange(range)) {
throw new Error('Invalid arguments');
}
var validatedRange = this.model.validateRange(range);
var revealRangeEvent: EditorCommon.ICursorRevealRangeEvent = {
range: validatedRange,
viewRange: null,
verticalType: verticalType,
revealHorizontal: revealHorizontal
};
this.cursor.emit(EditorCommon.EventType.CursorRevealRange, revealRangeEvent);
}
public revealLine(lineNumber: number): void {
this._sendRevealRange({
startLineNumber: lineNumber,
startColumn: 1,
endLineNumber: lineNumber,
endColumn: 1
}, EditorCommon.VerticalRevealType.Simple, false);
}
public revealLineInCenter(lineNumber: number): void {
this._sendRevealRange({
startLineNumber: lineNumber,
startColumn: 1,
endLineNumber: lineNumber,
endColumn: 1
}, EditorCommon.VerticalRevealType.Center, false);
}
public revealLineInCenterIfOutsideViewport(lineNumber: number): void {
this._sendRevealRange({
startLineNumber: lineNumber,
startColumn: 1,
endLineNumber: lineNumber,
endColumn: 1
}, EditorCommon.VerticalRevealType.CenterIfOutsideViewport, false);
}
public revealPosition(position: EditorCommon.IPosition, revealVerticalInCenter:boolean=false, revealHorizontal:boolean=false): void {
if (!Position.isIPosition(position)) {
throw new Error('Invalid arguments');
}
this._sendRevealRange({
startLineNumber: position.lineNumber,
startColumn: position.column,
endLineNumber: position.lineNumber,
endColumn: position.column
}, revealVerticalInCenter ? EditorCommon.VerticalRevealType.Center : EditorCommon.VerticalRevealType.Simple, revealHorizontal);
}
public revealPositionInCenter(position: EditorCommon.IPosition): void {
if (!Position.isIPosition(position)) {
throw new Error('Invalid arguments');
}
this._sendRevealRange({
startLineNumber: position.lineNumber,
startColumn: position.column,
endLineNumber: position.lineNumber,
endColumn: position.column
}, EditorCommon.VerticalRevealType.Center, true);
}
public revealPositionInCenterIfOutsideViewport(position: EditorCommon.IPosition): void {
if (!Position.isIPosition(position)) {
throw new Error('Invalid arguments');
}
this._sendRevealRange({
startLineNumber: position.lineNumber,
startColumn: position.column,
endLineNumber: position.lineNumber,
endColumn: position.column
}, EditorCommon.VerticalRevealType.CenterIfOutsideViewport, true);
}
public getSelection(): EditorCommon.IEditorSelection {
if (!this.cursor) {
return null;
}
return this.cursor.getSelection().clone();
}
public getSelections(): EditorCommon.IEditorSelection[] {
if (!this.cursor) {
return null;
}
var selections = this.cursor.getSelections();
var result:EditorCommon.IEditorSelection[] = [];
for (var i = 0, len = selections.length; i < len; i++) {
result[i] = selections[i].clone();
}
return result;
}
public setSelection(range:EditorCommon.IRange, reveal?:boolean, revealVerticalInCenter?:boolean, revealHorizontal?:boolean): void;
public setSelection(editorRange:EditorCommon.IEditorRange, reveal?:boolean, revealVerticalInCenter?:boolean, revealHorizontal?:boolean): void;
public setSelection(selection:EditorCommon.ISelection, reveal?:boolean, revealVerticalInCenter?:boolean, revealHorizontal?:boolean): void;
public setSelection(editorSelection:EditorCommon.IEditorSelection, reveal?:boolean, revealVerticalInCenter?:boolean, revealHorizontal?:boolean): void;
public setSelection(something:any, reveal:boolean = false, revealVerticalInCenter:boolean = false, revealHorizontal:boolean = false): void {
var isSelection = Selection.isISelection(something);
var isRange = Range.isIRange(something);
if (!isSelection && !isRange) {
throw new Error('Invalid arguments');
}
if (isSelection) {
this._setSelectionImpl(<EditorCommon.ISelection>something, reveal, revealVerticalInCenter, revealHorizontal);
} else if (isRange) {
// act as if it was an IRange
var selection:EditorCommon.ISelection = {
selectionStartLineNumber: something.startLineNumber,
selectionStartColumn: something.startColumn,
positionLineNumber: something.endLineNumber,
positionColumn: something.endColumn
};
this._setSelectionImpl(selection, reveal, revealVerticalInCenter, revealHorizontal);
}
}
private _setSelectionImpl(sel:EditorCommon.ISelection, reveal:boolean, revealVerticalInCenter:boolean, revealHorizontal:boolean): void {
if (!this.cursor) {
return;
}
var selection = Selection.createSelection(sel.selectionStartLineNumber, sel.selectionStartColumn, sel.positionLineNumber, sel.positionColumn);
this.cursor.setSelections('api', [selection]);
if (reveal) {
this.revealRange(selection, revealVerticalInCenter, revealHorizontal);
}
}
public revealLines(startLineNumber: number, endLineNumber: number): void {
this._sendRevealRange({
startLineNumber: startLineNumber,
startColumn: 1,
endLineNumber: endLineNumber,
endColumn: 1
}, EditorCommon.VerticalRevealType.Simple, false);
}
public revealLinesInCenter(startLineNumber: number, endLineNumber: number): void {
this._sendRevealRange({
startLineNumber: startLineNumber,
startColumn: 1,
endLineNumber: endLineNumber,
endColumn: 1
}, EditorCommon.VerticalRevealType.Center, false);
}
public revealLinesInCenterIfOutsideViewport(startLineNumber: number, endLineNumber: number): void {
this._sendRevealRange({
startLineNumber: startLineNumber,
startColumn: 1,
endLineNumber: endLineNumber,
endColumn: 1
}, EditorCommon.VerticalRevealType.CenterIfOutsideViewport, false);
}
public revealRange(range: EditorCommon.IRange, revealVerticalInCenter:boolean = false, revealHorizontal:boolean = false): void {
this._sendRevealRange(range, revealVerticalInCenter ? EditorCommon.VerticalRevealType.Center : EditorCommon.VerticalRevealType.Simple, revealHorizontal);
}
public revealRangeInCenter(range: EditorCommon.IRange): void {
this._sendRevealRange(range, EditorCommon.VerticalRevealType.Center, true);
}
public revealRangeInCenterIfOutsideViewport(range: EditorCommon.IRange): void {
this._sendRevealRange(range, EditorCommon.VerticalRevealType.CenterIfOutsideViewport, true);
}
public setSelections(ranges: EditorCommon.ISelection[]): void {
if (!this.cursor) {
return;
}
if (!ranges || ranges.length === 0) {
throw new Error('Invalid arguments');
}
for (var i = 0, len = ranges.length; i < len; i++) {
if (!Selection.isISelection(ranges[i])) {
throw new Error('Invalid arguments');
}
}
this.cursor.setSelections('api', ranges);
}
public abstract setScrollTop(newScrollTop:number): void;
public abstract getScrollTop(): number;
public abstract setScrollLeft(newScrollLeft:number): void;
public abstract getScrollLeft(): number;
public abstract getScrollWidth(): number;
public abstract getScrollHeight(): number;
public abstract saveViewState(): EditorCommon.ICodeEditorViewState;
public abstract restoreViewState(state:EditorCommon.IEditorViewState): void;
public onVisible(): void {
}
public onHide(): void {
}
public abstract layout(dimension?:EditorCommon.IDimension): void;
public abstract focus(): void;
public beginForcedWidgetFocus(): void {
this.forcedWidgetFocusCount++;
}
public endForcedWidgetFocus(): void {
this.forcedWidgetFocusCount--;
}
public abstract isFocused(): boolean;
public getContribution(id: string): EditorCommon.IEditorContribution {
return this.contributions[id] || null;
}
public addAction(descriptor:EditorCommon.IActionDescriptor): void {
var action = this._instantiationService.createInstance(DynamicEditorAction, descriptor, this);
this.contributions[action.getId()] = action;
}
public getActions(): IAction[] {
var result: IAction[] = [];
var id: string;
for (id in this.contributions) {
if (this.contributions.hasOwnProperty(id)) {
var contribution = <any>this.contributions[id];
// contribution instanceof IAction
if (isAction(contribution)) {
result.push(<IAction>contribution);
}
}
}
return result;
}
public getAction(id:string): IAction {
var contribution = <any>this.contributions[id];
if (contribution) {
// contribution instanceof IAction
if (isAction(contribution)) {
return <IAction>contribution;
}
}
return null;
}
public trigger(source:string, handlerId:string, payload:any): void {
var candidate = this.getAction(handlerId);
if(candidate !== null) {
if (candidate.enabled) {
this._telemetryService.publicLog('editorActionInvoked', {name: candidate.label} );
TPromise.as(candidate.run()).done(null, onUnexpectedError);
}
} else {
// forward to handler dispatcher
var r = this._configuration.handlerDispatcher.trigger(source, handlerId, payload);
if (!r) {
// console.warn('Returning false from ' + handlerId + ' wont do anything special...');
}
}
}
public executeCommand(source: string, command: EditorCommon.ICommand): boolean {
// forward to handler dispatcher
return this._configuration.handlerDispatcher.trigger(source, EditorCommon.Handler.ExecuteCommand, command);
}
public executeEdits(source: string, edits: EditorCommon.IIdentifiedSingleEditOperation[]): boolean {
if (!this.cursor) {
// no view, no cursor
return false;
}
if (this._configuration.editor.readOnly) {
// read only editor => sorry!
return false;
}
this.model.pushEditOperations(this.cursor.getSelections(), edits, () => {
return this.cursor.getSelections();
});
return true;
}
public executeCommands(source: string, commands: EditorCommon.ICommand[]): boolean {
// forward to handler dispatcher
return this._configuration.handlerDispatcher.trigger(source, EditorCommon.Handler.ExecuteCommands, commands);
}
public changeDecorations(callback:(changeAccessor:EditorCommon.IModelDecorationsChangeAccessor)=>any): any {
if (!this.model) {
// console.warn('Cannot change decorations on editor that is not attached to a model');
// callback will not be called
return null;
}
return this.model.changeDecorations(callback, this.id);
}
public getLineDecorations(lineNumber: number): EditorCommon.IModelDecoration[] {
if (!this.model) {
return null;
}
return this.model.getLineDecorations(lineNumber, this.id, this._configuration.editor.readOnly);
}
public deltaDecorations(oldDecorations:string[], newDecorations:EditorCommon.IModelDeltaDecoration[]): string[] {
if (!this.model) {
return [];
}
if (oldDecorations.length === 0 && newDecorations.length === 0) {
return oldDecorations;
}
return this.model.deltaDecorations(oldDecorations, newDecorations, this.id);
}
public setDecorations(decorationTypeKey: string, ranges:EditorCommon.IRangeWithMessage[]): void {
var opts = this._codeEditorService.resolveDecorationType(decorationTypeKey);
var oldDecorationIds = this._decorationTypeKeysToIds[decorationTypeKey] || [];
this._decorationTypeKeysToIds[decorationTypeKey] = this.deltaDecorations(oldDecorationIds, ranges.map((r) : EditorCommon.IModelDeltaDecoration => {
let decOpts: EditorCommon.IModelDecorationOptions;
if (r.hoverMessage) {
decOpts = Objects.clone(opts);
decOpts.htmlMessage = r.hoverMessage;
} else {
decOpts = opts;
}
return {
range: r.range,
options: decOpts
};
}));
}
public removeDecorations(decorationTypeKey: string): void {
if (this._decorationTypeKeysToIds.hasOwnProperty(decorationTypeKey)) {
this.deltaDecorations(this._decorationTypeKeysToIds[decorationTypeKey], []);
delete this._decorationTypeKeysToIds[decorationTypeKey];
}
}
public addTypingListener(character:string, callback: () => void): EventEmitter.ListenerUnbind {
if (!this.cursor) {
return () => {
// no-op
};
}
this.cursor.addTypingListener(character, callback);
return () => {
if (this.cursor) {
this.cursor.removeTypingListener(character, callback);
}
};
}
public getLayoutInfo(): EditorCommon.IEditorLayoutInfo {
return this._configuration.editor.layoutInfo;
}
_attachModel(model:EditorCommon.IModel): void {
this.model = model ? model : null;
this.listenersToRemove = [];
this.viewModel = null;
this.cursor = null;
if (this.model) {
this._configuration.resetIndentationOptions();
this.domElement.setAttribute('data-mode-id', this.model.getMode().getId());
this._langIdKey.set(this.model.getMode().getId());
this.model.setStopLineTokenizationAfter(this._configuration.editor.stopLineTokenizationAfter);
this._configuration.setIsDominatedByLongLines(this.model.isDominatedByLongLines(this._configuration.editor.longLineBoundary));
this.model.onBeforeAttached();
var hardWrappingLineMapperFactory = new CharacterHardWrappingLineMapperFactory(
this._configuration.editor.wordWrapBreakBeforeCharacters,
this._configuration.editor.wordWrapBreakAfterCharacters,
this._configuration.editor.wordWrapBreakObtrusiveCharacters
);
var linesCollection = new SplitLinesCollection(
this.model,
hardWrappingLineMapperFactory,
this._configuration.getIndentationOptions().tabSize,
this._configuration.editor.wrappingInfo.wrappingColumn,
this._configuration.editor.typicalFullwidthCharacterWidth / this._configuration.editor.typicalHalfwidthCharacterWidth,
EditorCommon.wrappingIndentFromString(this._configuration.editor.wrappingIndent)
);
this.viewModel = new ViewModel(
linesCollection,
this.id,
this._configuration,
this.model,
() => this.getCenteredRangeInViewport()
);
var viewModelHelper:IViewModelHelper = {
viewModel: this.viewModel,
convertModelPositionToViewPosition: (lineNumber:number, column:number) => {
return this.viewModel.convertModelPositionToViewPosition(lineNumber, column);
},
convertModelRangeToViewRange: (modelRange:EditorCommon.IEditorRange) => {
return this.viewModel.convertModelRangeToViewRange(modelRange);
},
convertViewToModelPosition: (lineNumber:number, column:number) => {
return this.viewModel.convertViewPositionToModelPosition(lineNumber, column);
},
validateViewPosition: (viewLineNumber:number, viewColumn:number, modelPosition:EditorCommon.IEditorPosition) => {
return this.viewModel.validateViewPosition(viewLineNumber, viewColumn, modelPosition);
},
validateViewRange: (viewStartLineNumber:number, viewStartColumn:number, viewEndLineNumber:number, viewEndColumn:number, modelRange:EditorCommon.IEditorRange) => {
return this.viewModel.validateViewRange(viewStartLineNumber, viewStartColumn, viewEndLineNumber, viewEndColumn, modelRange);
}
};
this.cursor = new Cursor(
this.id,
this._configuration,
this.model,
viewModelHelper,
this._enableEmptySelectionClipboard()
);
this.viewModel.addEventSource(this.cursor);
this._createView();
this.listenersToRemove.push(this._getViewInternalEventBus().addBulkListener((events) => {
for (var i = 0, len = events.length; i < len; i++) {
var eventType = events[i].getType();
var e = events[i].getData();
switch (eventType) {
case EditorCommon.EventType.ViewFocusGained:
this.emit(EditorCommon.EventType.EditorTextFocus);
// In IE, the focus is not synchronous, so we give it a little help
this.emit(EditorCommon.EventType.EditorFocus, {});
break;
case 'scroll':
this.emit('scroll', e);
break;
case 'scrollSize':
this.emit('scrollSize', e);
break;
case EditorCommon.EventType.ViewFocusLost:
this.emit(EditorCommon.EventType.EditorTextBlur);
break;
case EditorCommon.EventType.ContextMenu:
this.emit(EditorCommon.EventType.ContextMenu, e);
break;
case EditorCommon.EventType.MouseDown:
this.emit(EditorCommon.EventType.MouseDown, e);
break;
case EditorCommon.EventType.MouseUp:
this.emit(EditorCommon.EventType.MouseUp, e);
break;
case EditorCommon.EventType.KeyUp:
this.emit(EditorCommon.EventType.KeyUp, e);
break;
case EditorCommon.EventType.MouseMove:
this.emit(EditorCommon.EventType.MouseMove, e);
break;
case EditorCommon.EventType.MouseLeave:
this.emit(EditorCommon.EventType.MouseLeave, e);
break;
case EditorCommon.EventType.KeyDown:
this.emit(EditorCommon.EventType.KeyDown, e);
break;
case EditorCommon.EventType.ViewLayoutChanged:
this.emit(EditorCommon.EventType.EditorLayout, e);
break;
default:
// console.warn("Unhandled view event: ", e);
}
}
}));
this.listenersToRemove.push(this.model.addBulkListener((events) => {
for (var i = 0, len = events.length; i < len; i++) {
var eventType = events[i].getType();
var e = events[i].getData();
switch (eventType) {
case EditorCommon.EventType.ModelDecorationsChanged:
this.emit(EditorCommon.EventType.ModelDecorationsChanged, e);
break;
case EditorCommon.EventType.ModelModeChanged:
this.domElement.setAttribute('data-mode-id', this.model.getMode().getId());
this._langIdKey.set(this.model.getMode().getId());
this.emit(EditorCommon.EventType.ModelModeChanged, e);
break;
case EditorCommon.EventType.ModelModeSupportChanged:
this.emit(EditorCommon.EventType.ModelModeSupportChanged, e);
break;
case EditorCommon.EventType.ModelContentChanged:
// TODO@Alex
this.emit(EditorCommon.EventType.ModelContentChanged, e);
this.emit('change', {});
break;
case EditorCommon.EventType.ModelDispose:
// Someone might destroy the model from under the editor, so prevent any exceptions by setting a null model
this.setModel(null);
break;
default:
// console.warn("Unhandled model event: ", e);
}
}
}));
var _hasNonEmptySelection = (e: EditorCommon.ICursorSelectionChangedEvent) => {
var allSelections = [e.selection].concat(e.secondarySelections);
return allSelections.some(s => !s.isEmpty());
};
this.listenersToRemove.push(this.cursor.addBulkListener((events) => {
var updateHasMultipleCursors = false,
hasMultipleCursors = false,
updateHasNonEmptySelection = false,
hasNonEmptySelection = false;
for (var i = 0, len = events.length; i < len; i++) {
var eventType = events[i].getType();
var e = events[i].getData();
switch (eventType) {
case EditorCommon.EventType.CursorPositionChanged:
var cursorPositionChangedEvent = <EditorCommon.ICursorPositionChangedEvent>e;
updateHasMultipleCursors = true;
hasMultipleCursors = (cursorPositionChangedEvent.secondaryPositions.length > 0);
this.emit(EditorCommon.EventType.CursorPositionChanged, e);
break;
case EditorCommon.EventType.CursorSelectionChanged:
var cursorSelectionChangedEvent = <EditorCommon.ICursorSelectionChangedEvent>e;
updateHasMultipleCursors = true;
hasMultipleCursors = (cursorSelectionChangedEvent.secondarySelections.length > 0);
updateHasNonEmptySelection = true;
hasNonEmptySelection = _hasNonEmptySelection(cursorSelectionChangedEvent);
this.emit(EditorCommon.EventType.CursorSelectionChanged, e);
break;
default:
// console.warn("Unhandled cursor event: ", e);
}
}
if (updateHasMultipleCursors) {
if (hasMultipleCursors) {
this._hasMultipleSelectionsKey.set(true);
} else {
this._hasMultipleSelectionsKey.reset();
}
}
if (updateHasNonEmptySelection) {
if (hasNonEmptySelection) {
this._hasNonEmptySelectionKey.set(true);
} else {
this._hasNonEmptySelectionKey.reset();
}
}
}));
} else {
this.hasView = false;
}
}
protected abstract _enableEmptySelectionClipboard(): boolean;
protected abstract _createView(): void;
protected abstract _getViewInternalEventBus(): EventEmitter.IEventEmitter;
_postDetachModelCleanup(detachedModel:EditorCommon.IModel): void {
if (detachedModel) {
this._decorationTypeKeysToIds = {};
detachedModel.removeAllDecorationsWithOwnerId(this.id);
}
}
protected _detachModel(): EditorCommon.IModel {
if (this.model) {
this.model.onBeforeDetached();
}
this.hasView = false;
this.listenersToRemove.forEach((element) => {
element();
});
this.listenersToRemove = [];
if (this.cursor) {
this.cursor.dispose();
this.cursor = null;
}
if (this.viewModel) {
this.viewModel.dispose();
this.viewModel = null;
}
var result = this.model;
this.model = null;
this.domElement.removeAttribute('data-mode-id');
return result;
}
}

View file

@ -1758,6 +1758,16 @@ export interface IModel extends IEditableTextModel, ITextModelWithMarkers, IToke
* @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;
/**
* Search the model for the previous match. Loops to the end 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 range where the previous match is. It is null if no previous match has been found.
*/
findPreviousMatch(searchString:string, searchStart:IPosition, isRegex:boolean, matchCase:boolean, wholeWord:boolean): IEditorRange;
/**
* Replace the entire text buffer value contained in this model.
@ -3037,11 +3047,6 @@ export interface ICommonCodeEditor extends IEditor {
*/
setValue(newValue: string): void;
/**
* Returns the range that is currently centered in the view port.
*/
getCenteredRangeInViewport(): IEditorRange;
/**
* Change the scrollTop of the editor's viewport.
*/
@ -3113,42 +3118,11 @@ export interface ICommonCodeEditor extends IEditor {
removeDecorations(decorationTypeKey:string): void;
/**
* Get the horizontal position (left offset) for the column w.r.t to the beginning of the line.
* This method works only if the line `lineNumber` is currently rendered (in the editor's viewport).
* Use this method with caution.
*/
getOffsetForColumn(lineNumber: number, column: number): number;
/**
* Get the vertical position (top offset) for the line w.r.t. to the first line.
*/
getTopForLineNumber(lineNumber: number): number;
/**
* Get the vertical position (top offset) for the position w.r.t. to the first line.
*/
getTopForPosition(lineNumber: number, column: number): number;
/**
* Get the visible position for `position`.
* The result position takes scrolling into account and is relative to the top left corner of the editor.
* Explanation 1: the results of this method will change for the same `position` if the user scrolls the editor.
* Explanation 2: the results of this method will not change if the container of the editor gets repositioned.
* Warning: the results of this method are innacurate for positions that are outside the current editor viewport.
*/
getScrolledVisiblePosition(position: IPosition): { top: number; left: number; height: number; };
/**
* Get the layout info for the editor.
*/
getLayoutInfo(): IEditorLayoutInfo;
/**
* Get the view zones.
*/
getWhitespaces(): IEditorWhitespace[];
/**
* Prevent the editor from sending a widgetFocusLost event,
* set it in a state where it believes that focus is in one of its widgets.

View file

@ -904,6 +904,41 @@ export class TextModel extends OrderGuaranteeEventEmitter implements EditorCommo
return null;
}
public findPreviousMatch(searchString:string, rawSearchStart:EditorCommon.IPosition, isRegex:boolean, matchCase:boolean, wholeWord:boolean): EditorCommon.IEditorRange {
if (this._isDisposed) {
throw new Error('Model.findPreviousMatch: Model is disposed');
}
var regex = Strings.createSafeRegExp(searchString, isRegex, matchCase, wholeWord);
if (!regex) {
return null;
}
var searchStart = this.validatePosition(rawSearchStart),
lineCount = this.getLineCount(),
startLineNumber = searchStart.lineNumber,
text: string,
r: EditorCommon.IEditorRange;
// Look in first line
text = this._lines[startLineNumber - 1].text.substring(0, searchStart.column - 1);
r = this._findLastMatchInLine(regex, text, startLineNumber);
if (r) {
return r;
}
for (var i = 1; i < lineCount; i++) {
var lineIndex = (lineCount + startLineNumber - i - 1) % lineCount;
text = this._lines[lineIndex].text;
r = this._findLastMatchInLine(regex, text, lineIndex + 1);
if (r) {
return r;
}
}
return null;
}
private _doFindMatches(searchRange:EditorCommon.IEditorRange, searchRegex:RegExp, limitResultCount:number): EditorCommon.IEditorRange[] {
var result:EditorCommon.IEditorRange[] = [],
text: string,
@ -942,6 +977,19 @@ export class TextModel extends OrderGuaranteeEventEmitter implements EditorCommo
return new Range(lineNumber, m.index + 1 + deltaOffset, lineNumber, m.index + 1 + m[0].length + deltaOffset);
}
private _findLastMatchInLine(searchRegex:RegExp, text:string, lineNumber:number): EditorCommon.IEditorRange {
let bestResult: EditorCommon.IEditorRange = null;
let m:RegExpExecArray;
while ((m = searchRegex.exec(text))) {
let result = new Range(lineNumber, m.index + 1, lineNumber, m.index + 1 + m[0].length);
if (result.equalsRange(bestResult)) {
break;
}
bestResult = result;
}
return bestResult;
}
private _findMatchesInLine(searchRegex:RegExp, text:string, lineNumber:number, deltaOffset:number, counter:number, result:EditorCommon.IEditorRange[], limitResultCount:number): number {
var m:RegExpExecArray;
// Reset regex to search from the beginning

View file

@ -232,10 +232,16 @@ export class LineCommentCommand implements EditorCommon.ICommand {
}
private _attemptRemoveBlockComment(model:EditorCommon.ITokenizedModel, s:EditorCommon.IEditorSelection, startToken: string, endToken: string): EditorCommon.IIdentifiedSingleEditOperation[] {
var startLineNumber = s.startLineNumber;
var endLineNumber = s.endLineNumber;
var startTokenIndex = model.getLineContent(startLineNumber).lastIndexOf(startToken, s.startColumn - 1 + endToken.length);
var endTokenIndex = model.getLineContent(endLineNumber).indexOf(endToken, s.endColumn - 1 - startToken.length);
let startLineNumber = s.startLineNumber;
let endLineNumber = s.endLineNumber;
let startTokenAllowedBeforeColumn = endToken.length + Math.max(
model.getLineFirstNonWhitespaceColumn(s.startLineNumber),
s.startColumn
);
let startTokenIndex = model.getLineContent(startLineNumber).lastIndexOf(startToken, startTokenAllowedBeforeColumn - 1);
let endTokenIndex = model.getLineContent(endLineNumber).indexOf(endToken, s.endColumn - 1 - startToken.length);
if (startTokenIndex !== -1 && endTokenIndex === -1) {
endTokenIndex = model.getLineContent(startLineNumber).indexOf(endToken, startTokenIndex + startToken.length);

View file

@ -759,6 +759,38 @@ suite('Editor Contrib - Line Comment As Block Comment 2', () => {
new Selection(5, 10, 5, 10)
);
});
test('issue #993: Remove comment does not work consistently in HTML', () => {
testLineCommentCommand(
[
' asd qwe',
' asd qwe',
''
],
new Selection(1, 1, 3, 1),
[
' <!@#asd qwe',
' asd qwe#@!>',
''
],
new Selection(1, 1, 3, 1)
);
testLineCommentCommand(
[
' <!@#asd qwe',
' asd qwe#@!>',
''
],
new Selection(1, 1, 3, 1),
[
' asd qwe',
' asd qwe',
''
],
new Selection(1, 1, 3, 1)
);
});
});

View file

@ -4,187 +4,25 @@
*--------------------------------------------------------------------------------------------*/
'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 {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';
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';
import {CommonFindController, IFindStartOptions, FindStartFocusAction, SelectionHighlighter} from 'vs/editor/contrib/find/common/findController';
enum FindStartFocusAction {
NoFocusChange,
FocusFindInput,
FocusReplaceInput
}
class FindController extends CommonFindController implements IFindController {
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
*/
class FindController implements EditorCommon.IEditorContribution, IFindController {
static ID = 'editor.contrib.findController';
private _editor: EditorBrowser.ICodeEditor;
private _toDispose: IDisposable[];
private _findWidgetVisible: IKeybindingContextKey<boolean>;
private _state: FindReplaceState;
private _widget: FindWidget;
private _model: FindModelBoundToEditorModel;
static getFindController(editor:EditorCommon.ICommonCodeEditor): FindController {
return <FindController>editor.getContribution(FindController.ID);
}
constructor(editor:EditorBrowser.ICodeEditor, @IContextViewService contextViewService: IContextViewService, @IKeybindingService keybindingService: IKeybindingService) {
this._editor = editor;
this._toDispose = [];
this._findWidgetVisible = keybindingService.createKey(CONTEXT_FIND_WIDGET_VISIBLE, false);
super(editor, keybindingService);
this._state = new FindReplaceState();
this._toDispose.push(this._state);
this._toDispose.push(this._state.addChangeListener((e) => this._onStateChanged(e)))
this._widget = new FindWidget(this._editor, this, this._state, contextViewService, keybindingService);
this._toDispose.push(this._widget);
this._model = null;
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._widget = this._register(new FindWidget(editor, this, this._state, contextViewService, keybindingService));
}
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;
}
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._state.change({ isRevealed: false }, false);
this._editor.focus();
}
public toggleCaseSensitive(): void {
this._state.change({ matchCase: !this._state.matchCase }, false);
}
public toggleWholeWords(): void {
this._state.change({ wholeWord: !this._state.wholeWord }, false);
}
public toggleRegex(): void {
this._state.change({ isRegex: !this._state.isRegex }, false);
}
public setSearchString(searchString:string): void {
this._state.change({ searchString: searchString }, false);
}
private _start(opts:IFindStartOptions): void {
this.disposeModel();
if (!this._editor.getModel()) {
// cannot do anything with an editor that doesn't have a model...
return;
}
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());
if (wordAtPosition) {
stateChanges.searchString = wordAtPosition.word;
}
} else {
stateChanges.searchString = this._editor.getModel().getValueInRange(selection);
}
}
}
stateChanges.searchScope = null;
if (opts.seedSearchScopeFromSelection && selection.startLineNumber < selection.endLineNumber) {
// Take search scope into account only if it is more than one line.
stateChanges.searchScope = selection;
}
// Overwrite isReplaceRevealed
if (opts.forceRevealReplace) {
stateChanges.isReplaceRevealed = true;
}
this._state.change(stateChanges, false);
if (!this._model) {
this._model = new FindModelBoundToEditorModel(this._editor, this._state);
}
protected _start(opts:IFindStartOptions): void {
super._start(opts);
if (opts.shouldFocus === FindStartFocusAction.FocusReplaceInput) {
this._widget.focusReplaceInput();
@ -192,444 +30,7 @@ class FindController implements EditorCommon.IEditorContribution, IFindControlle
this._widget.focusFindInput();
}
}
public startFromAction(withReplace:boolean): void {
this._start({
forceRevealReplace: withReplace,
seedSearchStringFromSelection: true,
seedSearchScopeFromSelection: true,
shouldFocus: withReplace ? FindStartFocusAction.FocusReplaceInput : FindStartFocusAction.FocusFindInput,
shouldAnimate: true
});
}
public moveToNextMatch(): boolean {
if (this._model) {
this._model.moveToNextMatch();
return true;
}
return false;
}
public moveToPrevMatch(): boolean {
if (this._model) {
this._model.moveToPrevMatch();
return true;
}
return false;
}
public replace(): boolean {
if (this._model) {
this._model.replace();
return true;
}
return false;
}
public replaceAll(): boolean {
if (this._model) {
this._model.replaceAll();
return true;
}
return false;
}
}
export class StartFindAction extends EditorAction {
constructor(descriptor: EditorCommon.IEditorActionDescriptorData, editor: EditorCommon.ICommonCodeEditor, @INullService ns) {
super(descriptor, editor, Behaviour.WidgetFocus);
}
public run(): TPromise<boolean> {
let controller = FindController.getFindController(this.editor);
controller.startFromAction(false);
return TPromise.as(true);
}
}
export class NextMatchFindAction extends EditorAction {
constructor(descriptor:EditorCommon.IEditorActionDescriptorData, editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
super(descriptor, editor, Behaviour.WidgetFocus);
}
public run(): TPromise<boolean> {
let controller = FindController.getFindController(this.editor);
if (!controller.moveToNextMatch()) {
controller.startFromAction(false);
controller.moveToNextMatch();
}
return TPromise.as(true);
}
}
export class PreviousMatchFindAction extends EditorAction {
constructor(descriptor:EditorCommon.IEditorActionDescriptorData, editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
super(descriptor, editor, Behaviour.WidgetFocus);
}
public run(): TPromise<boolean> {
let controller = FindController.getFindController(this.editor);
if (!controller.moveToPrevMatch()) {
controller.startFromAction(false);
controller.moveToPrevMatch();
}
return TPromise.as(true);
}
}
export class StartFindReplaceAction extends EditorAction {
constructor(descriptor:EditorCommon.IEditorActionDescriptorData, editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
super(descriptor, editor, Behaviour.WidgetFocus | Behaviour.Writeable);
}
public run(): TPromise<boolean> {
let controller = FindController.getFindController(this.editor);
controller.startFromAction(true);
return TPromise.as(true);
}
}
export interface IMultiCursorFindResult {
searchText:string;
isRegex:boolean;
matchCase:boolean;
wholeWord:boolean;
nextMatch: EditorCommon.IEditorSelection;
}
export function multiCursorFind(editor:EditorCommon.ICommonCodeEditor, changeFindSearchString:boolean): IMultiCursorFindResult {
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
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.isRevealed && state.searchString.length > 0) {
// Find widget owns what is searched for
searchText = state.searchString;
} else {
// Selection owns what is searched for
let s = editor.getSelection();
if (s.startLineNumber !== s.endLineNumber) {
// Cannot search for multiline string... yet...
return null;
}
if (s.isEmpty()) {
// selection is empty => expand to current word
let word = editor.getModel().getWordAtPosition(s.getStartPosition());
if (!word) {
return null;
}
searchText = word.word;
nextMatch = Selection.createSelection(s.startLineNumber, word.startColumn, s.startLineNumber, word.endColumn);
} else {
searchText = editor.getModel().getValueInRange(s);
}
if (changeFindSearchString) {
controller.setSearchString(searchText);
}
}
return {
searchText: searchText,
isRegex: isRegex,
matchCase: matchCase,
wholeWord: wholeWord,
nextMatch: nextMatch
};
}
class SelectNextFindMatchAction extends EditorAction {
constructor(descriptor:EditorCommon.IEditorActionDescriptorData, editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
super(descriptor, editor, Behaviour.WidgetFocus);
}
protected _getNextMatch(): EditorCommon.IEditorSelection {
let r = multiCursorFind(this.editor, true);
if (!r) {
return null;
}
if (r.nextMatch) {
return r.nextMatch;
}
let allSelections = this.editor.getSelections();
let lastAddedSelection = allSelections[allSelections.length - 1];
let nextMatch = this.editor.getModel().findNextMatch(r.searchText, lastAddedSelection.getEndPosition(), r.isRegex, r.matchCase, r.wholeWord);
if (!nextMatch) {
return null;
}
return Selection.createSelection(nextMatch.startLineNumber, nextMatch.startColumn, nextMatch.endLineNumber, nextMatch.endColumn);
}
}
class AddSelectionToNextFindMatchAction extends SelectNextFindMatchAction {
static ID = 'editor.action.addSelectionToNextFindMatch';
constructor(descriptor:EditorCommon.IEditorActionDescriptorData, editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
super(descriptor, editor, ns);
}
public run(): TPromise<boolean> {
let nextMatch = this._getNextMatch();
if (!nextMatch) {
return TPromise.as(false);
}
let allSelections = this.editor.getSelections();
this.editor.setSelections(allSelections.concat(nextMatch));
this.editor.revealRangeInCenterIfOutsideViewport(nextMatch);
return TPromise.as(true);
}
}
class MoveSelectionToNextFindMatchAction extends SelectNextFindMatchAction {
static ID = 'editor.action.moveSelectionToNextFindMatch';
constructor(descriptor:EditorCommon.IEditorActionDescriptorData, editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
super(descriptor, editor, ns);
}
public run(): TPromise<boolean> {
let nextMatch = this._getNextMatch();
if (!nextMatch) {
return TPromise.as(false);
}
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);
return TPromise.as(true);
}
}
class SelectHighlightsAction extends EditorAction {
static ID = 'editor.action.selectHighlights';
static COMPAT_ID = 'editor.action.changeAll';
constructor(descriptor:EditorCommon.IEditorActionDescriptorData, editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
let behaviour = Behaviour.WidgetFocus;
if (descriptor.id === SelectHighlightsAction.COMPAT_ID) {
behaviour |= Behaviour.ShowInContextMenu;
}
super(descriptor, editor, behaviour);
}
public getGroupId(): string {
return '2_change/1_changeAll';
}
public run(): TPromise<boolean> {
let r = multiCursorFind(this.editor, true);
if (!r) {
return TPromise.as(false);
}
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)));
}
return TPromise.as(true);
}
}
export class SelectionHighlighter implements EditorCommon.IEditorContribution {
static ID = 'editor.contrib.selectionHighlighter';
private editor: EditorCommon.ICommonCodeEditor;
private decorations: string[];
private toDispose: IDisposable[];
constructor(editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
this.editor = editor;
this.decorations = [];
this.toDispose = [];
this.toDispose.push(editor.addListener2(EditorCommon.EventType.CursorPositionChanged, _ => this._update()));
this.toDispose.push(editor.addListener2(EditorCommon.EventType.ModelChanged, (e) => {
this.removeDecorations();
}));
this.toDispose.push(FindController.getFindController(editor).getState().addChangeListener((e) => this._update()));
}
public getId(): string {
return SelectionHighlighter.ID;
}
private removeDecorations(): void {
if (this.decorations.length > 0) {
this.decorations = this.editor.deltaDecorations(this.decorations, []);
}
}
private _update(): void {
if (!this.editor.getConfiguration().selectionHighlight) {
return;
}
let r = multiCursorFind(this.editor, false);
if (!r) {
this.removeDecorations();
return;
}
let model = this.editor.getModel();
if (r.nextMatch) {
// This is an empty selection
if (OccurrencesRegistry.has(model)) {
// Do not interfere with semantic word highlighting in the no selection case
this.removeDecorations();
return;
}
}
if (/^[ \t]+$/.test(r.searchText)) {
// whitespace only selection
this.removeDecorations();
return;
}
if (r.searchText.length > 200) {
// very long selection
this.removeDecorations();
return;
}
let allMatches = model.findMatches(r.searchText, true, r.isRegex, r.matchCase, r.wholeWord);
allMatches.sort(Range.compareRangesUsingStarts);
let selections = this.editor.getSelections();
selections.sort(Range.compareRangesUsingStarts);
// do not overlap with selection (issue #64 and #512)
let matches: EditorCommon.IEditorRange[] = [];
for (let i = 0, j = 0, len = allMatches.length, lenJ = selections.length; i < len; ) {
let match = allMatches[i];
if (j >= lenJ) {
// finished all editor selections
matches.push(match);
i++;
} else {
let cmp = Range.compareRangesUsingStarts(match, selections[j]);
if (cmp < 0) {
// match is before sel
matches.push(match);
i++;
} else if (cmp > 0) {
// sel is before match
j++;
} else {
// sel is equal to match
i++;
j++;
}
}
}
let decorations = matches.map(r => {
return {
range: r,
options: {
stickiness: EditorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
className: 'selectionHighlight'
}
};
});
this.decorations = this.editor.deltaDecorations(this.decorations, decorations);
}
public dispose(): void {
this.removeDecorations();
this.toDispose = disposeAll(this.toDispose);
}
}
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(SelectHighlightsAction, SelectHighlightsAction.ID, nls.localize('selectAllOccurencesOfFindMatch', "Select All Occurences of Find Match"), {
context: ContextKey.EditorFocus,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_L
}));
// register SelectHighlightsAction again to replace the now removed Change All action
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(SelectHighlightsAction, SelectHighlightsAction.COMPAT_ID, nls.localize('changeAll.label', "Change All Occurrences"), {
context: ContextKey.EditorTextFocus,
primary: KeyMod.CtrlCmd | KeyCode.F2
}));
// register actions
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, 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, 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, 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 }
}));
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(MoveSelectionToNextFindMatchAction, MoveSelectionToNextFindMatchAction.ID, nls.localize('moveSelectionToNextFindMatch', "Move Last Selection To Next Find Match"), {
context: ContextKey.EditorFocus,
primary: KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_D)
}));
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(AddSelectionToNextFindMatchAction, AddSelectionToNextFindMatchAction.ID, nls.localize('addSelectionToNextFindMatch', "Add Selection To Next Find Match"), {
context: ContextKey.EditorFocus,
primary: KeyMod.CtrlCmd | KeyCode.KEY_D
}));
EditorBrowserRegistry.registerEditorContribution(FindController);
EditorBrowserRegistry.registerEditorContribution(SelectionHighlighter);
function registerFindCommand(id:string, callback:(controller:FindController)=>void, keybindings:IKeybindings, needsKey:string = null): void {
CommonEditorRegistry.registerEditorCommand(id, CommonEditorRegistry.commandWeight(5), keybindings, false, needsKey, (ctx, editor, args) => {
callback(FindController.getFindController(editor));
});
}
registerFindCommand(FIND_IDS.CloseFindWidgetCommand, x => x.closeFindWidget(), {
primary: KeyCode.Escape
}, CONTEXT_FIND_WIDGET_VISIBLE);
registerFindCommand(FIND_IDS.ToggleCaseSensitiveCommand, x => x.toggleCaseSensitive(), {
primary: KeyMod.Alt | KeyCode.KEY_C,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C }
});
registerFindCommand(FIND_IDS.ToggleWholeWordCommand, x => x.toggleWholeWords(), {
primary: KeyMod.Alt | KeyCode.KEY_W,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_W }
});
registerFindCommand(FIND_IDS.ToggleRegexCommand, x => x.toggleRegex(), {
primary: KeyMod.Alt | KeyCode.KEY_R,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R }
});

View file

@ -11,16 +11,15 @@ 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 {MATCHES_LIMIT, FIND_IDS} from 'vs/editor/contrib/find/common/findModel';
import {CommonKeybindings} from 'vs/base/common/keyCodes';
import {IKeybindingService} from 'vs/platform/keybinding/common/keybindingService';
import {INewFindReplaceState, FindReplaceStateChangedEvent, FindReplaceState} from 'vs/editor/contrib/find/common/findState';
import {Widget} from 'vs/base/browser/ui/widget';
export interface IFindController {
replace(): void;
@ -38,8 +37,9 @@ const NLS_REPLACE_INPUT_PLACEHOLDER = nls.localize('placeholder.replace', "Repla
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");
const NLS_MATCHES_COUNT_LIMIT_TITLE = nls.localize('title.matchesCountLimit', "Only the first 999 results are highlighted, but all find operations work on the entire text.");
export class FindWidget implements EditorBrowser.IOverlayWidget {
export class FindWidget extends Widget implements EditorBrowser.IOverlayWidget {
private static ID = 'editor.contrib.findWidget';
private static PART_WIDTH = 275;
@ -59,7 +59,7 @@ export class FindWidget implements EditorBrowser.IOverlayWidget {
private _toggleReplaceBtn: SimpleButton;
private _prevBtn: SimpleButton;
private _nextBtn: SimpleButton;
private _toggleSelectionFind: Checkbox;
private _toggleSelectionFind: SimpleCheckbox;
private _closeBtn: SimpleButton;
private _replaceBtn: SimpleButton;
private _replaceAllBtn: SimpleButton;
@ -68,8 +68,6 @@ export class FindWidget implements EditorBrowser.IOverlayWidget {
private _isVisible: boolean;
private _isReplaceVisible: boolean;
private _toDispose: IDisposable[];
private focusTracker: DomUtils.IFocusTracker;
constructor(
@ -79,6 +77,7 @@ export class FindWidget implements EditorBrowser.IOverlayWidget {
contextViewProvider: IContextViewProvider,
keybindingService: IKeybindingService
) {
super();
this._codeEditor = codeEditor;
this._controller = controller;
this._state = state;
@ -88,30 +87,31 @@ export class FindWidget implements EditorBrowser.IOverlayWidget {
this._isVisible = false;
this._isReplaceVisible = false;
this._isReplaceEnabled = false;
this._toDispose = [];
this._toDispose.push(this._state.addChangeListener((e) => this._onStateChanged(e)));
this._register(this._state.addChangeListener((e) => this._onStateChanged(e)));
this._buildDomNode();
this.focusTracker = DomUtils.trackFocus(this._findInput.inputBox.inputElement);
this.focusTracker = this._register(DomUtils.trackFocus(this._findInput.inputBox.inputElement));
this.focusTracker.addFocusListener(() => this._reseedFindScope());
this._toDispose.push(this.focusTracker);
this._toDispose.push({
dispose: () => {
this._findInput.destroy();
let updateCanReplace = () => {
let canReplace = !this._codeEditor.getConfiguration().readOnly;
DomUtils.toggleClass(this._domNode, 'can-replace', canReplace);
if (!canReplace) {
this._state.change({ isReplaceRevealed: false }, false);
}
});
this._toDispose.push(this._replaceInputBox);
};
this._register(this._codeEditor.addListener2(EditorCommon.EventType.ConfigurationChanged, (e:EditorCommon.IConfigurationChangedEvent) => {
if (e.readOnly) {
updateCanReplace();
}
}));
updateCanReplace();
this._codeEditor.addOverlayWidget(this);
}
public dispose(): void {
this._toDispose = disposeAll(this._toDispose);
}
private _reseedFindScope(): void {
let selection = this._codeEditor.getSelection();
if (selection.startLineNumber !== selection.endLineNumber) {
@ -179,15 +179,30 @@ export class FindWidget implements EditorBrowser.IOverlayWidget {
}
if (e.searchScope) {
if (this._state.searchScope) {
this._toggleSelectionFind.checkbox.checked = true;
this._toggleSelectionFind.checked = true;
} else {
this._toggleSelectionFind.checkbox.checked = false;
this._toggleSelectionFind.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);
let showMatchesCount = (this._state.searchString.length > 0);
let matchesCount:string = String(this._state.matchesCount);
let matchesCountTitle = '';
if (this._state.matchesCount >= MATCHES_LIMIT) {
matchesCountTitle = NLS_MATCHES_COUNT_LIMIT_TITLE;
matchesCount += '+';
}
this._findInput.setMatchCountState({
isVisible: showMatchesCount,
count: matchesCount,
title: matchesCountTitle
});
}
}
@ -231,61 +246,70 @@ export class FindWidget implements EditorBrowser.IOverlayWidget {
private _onFindInputKeyDown(e:DomUtils.IKeyboardEvent): void {
let 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(FIND_IDS.NextMatchFindAction).run().done(null, Errors.onUnexpectedError);
handled = true;
} else if (e.equals(CommonKeybindings.SHIFT_ENTER)) {
this._codeEditor.getAction(FIND_IDS.PreviousMatchFindAction).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 {
// getValue() is not updated right away
setTimeout(() => {
this._state.change({ searchString: this._findInput.getValue() }, true);
}, 10);
}
// getValue() is not updated right away
setTimeout(() => {
this._state.change({ searchString: this._findInput.getValue() }, true);
}, 10);
}
private _onReplaceInputKeyDown(e:DomUtils.IKeyboardEvent): void {
let 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._state.change({ replaceString: this._replaceInputBox.value }, false);
}, 10);
}
setTimeout(() => {
this._state.change({ replaceString: this._replaceInputBox.value }, false);
}, 10);
}
// ----- initialization
@ -300,7 +324,7 @@ export class FindWidget implements EditorBrowser.IOverlayWidget {
private _buildFindPart(): HTMLElement {
// Find input
this._findInput = new 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,
@ -321,39 +345,45 @@ export class FindWidget implements EditorBrowser.IOverlayWidget {
return { content: e.message };
}
}
}).on('keydown', (browserEvent:KeyboardEvent) => {
this._onFindInputKeyDown(new StandardKeyboardEvent(browserEvent));
}).on(FindInput.OPTION_CHANGE, () => {
}));
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({
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) => {}
});
this._toDispose.push(this._prevBtn);
}));
// Next button
this._nextBtn = new SimpleButton({
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) => {}
});
this._toDispose.push(this._nextBtn);
}));
let findPart = document.createElement('div');
findPart.className = 'find-part';
@ -362,35 +392,43 @@ export class FindWidget implements EditorBrowser.IOverlayWidget {
findPart.appendChild(this._nextBtn.domNode);
// Toggle selection button
this._toggleSelectionFind = new Checkbox(findPart, NLS_TOGGLE_SELECTION_FIND_TITLE);
this._toggleSelectionFind.disable();
this._toDispose.push(DomUtils.addStandardDisposableListener(this._toggleSelectionFind.checkbox, 'change', (e) => {
if (this._toggleSelectionFind.checkbox.checked) {
this._reseedFindScope();
} else {
this._state.change({ searchScope: null }, true);
this._toggleSelectionFind = this._register(new SimpleCheckbox({
parent: findPart,
title: NLS_TOGGLE_SELECTION_FIND_TITLE,
onChange: () => {
if (this._toggleSelectionFind.checked) {
this._reseedFindScope();
} else {
this._state.change({ searchScope: null }, true);
}
}
}));
this._toggleSelectionFind.disable();
this._codeEditor.addListener(EditorCommon.EventType.CursorSelectionChanged, () => {
this._updateToggleSelectionFindButton();
});
// Close button
this._closeBtn = new SimpleButton({
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 (this._isReplaceVisible) {
this._replaceBtn.focus();
e.preventDefault();
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);
@ -406,7 +444,7 @@ export class FindWidget implements EditorBrowser.IOverlayWidget {
return;
}
if (!this._toggleSelectionFind.checkbox.checked) {
if (!this._toggleSelectionFind.checked) {
let selection = this._codeEditor.getSelection();
if (selection.startLineNumber === selection.endLineNumber) {
@ -422,35 +460,38 @@ export class FindWidget implements EditorBrowser.IOverlayWidget {
let replaceInput = document.createElement('div');
replaceInput.className = 'replace-input';
replaceInput.style.width = FindWidget.REPLACE_INPUT_AREA_WIDTH + 'px';
this._replaceInputBox = new 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({
this._replaceBtn = this._register(new SimpleButton({
label: NLS_REPLACE_BTN_LABEL,
className: 'replace',
onTrigger: () => {
this._controller.replace();
},
onKeyDown: (e) => {}
});
this._toDispose.push(this._replaceBtn);
onKeyDown: (e) => {
if (e.equals(CommonKeybindings.SHIFT_TAB)) {
this._closeBtn.focus();
e.preventDefault();
}
}
}));
// Replace all button
this._replaceAllBtn = new SimpleButton({
this._replaceAllBtn = this._register(new SimpleButton({
label: NLS_REPLACE_ALL_BTN_LABEL,
className: 'replace-all',
onTrigger: () => {
this._controller.replaceAll();
},
onKeyDown: (e) => {}
});
this._toDispose.push(this._replaceAllBtn);
}));
let replacePart = document.createElement('div');
replacePart.className = 'replace-part';
@ -469,28 +510,23 @@ export class FindWidget implements EditorBrowser.IOverlayWidget {
let replacePart = this._buildReplacePart();
// Toggle replace button
this._toggleReplaceBtn = new SimpleButton({
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');
this._domNode.className = 'editor-widget find-widget';
this._domNode.setAttribute('aria-hidden', 'false');
if (!this._codeEditor.getConfiguration().readOnly) {
DomUtils.addClass(this._domNode, 'can-replace');
}
this._domNode.appendChild(this._toggleReplaceBtn.domNode);
this._domNode.appendChild(findPart);
this._domNode.appendChild(replacePart);
@ -550,23 +586,33 @@ export class FindWidget implements EditorBrowser.IOverlayWidget {
}
}
export class Checkbox {
interface ISimpleCheckboxOpts {
parent: HTMLElement;
title: string;
onChange: () => void;
}
class SimpleCheckbox extends Widget {
private static _COUNTER = 0;
private _opts: ISimpleCheckboxOpts;
private _domNode: HTMLElement;
private _checkbox: HTMLInputElement;
private _label: HTMLLabelElement;
constructor(parent: HTMLElement, title: string) {
constructor(opts:ISimpleCheckboxOpts) {
super();
this._opts = opts;
this._domNode = document.createElement('div');
this._domNode.className = 'monaco-checkbox';
this._domNode.title = title;
this._domNode.title = this._opts.title;
this._checkbox = document.createElement('input');
this._checkbox.type = 'checkbox';
this._checkbox.className = 'checkbox';
this._checkbox.id = 'checkbox-' + Checkbox._COUNTER++;
this._checkbox.id = 'checkbox-' + SimpleCheckbox._COUNTER++;
this._label = document.createElement('label');
this._label.className = 'label';
@ -576,15 +622,23 @@ export class Checkbox {
this._domNode.appendChild(this._checkbox);
this._domNode.appendChild(this._label);
parent.appendChild(this._domNode);
this._opts.parent.appendChild(this._domNode);
this.onchange(this._checkbox, (e) => {
this._opts.onChange();
});
}
public get domNode(): HTMLElement {
return this._domNode;
}
public get checkbox(): HTMLInputElement {
return this._checkbox;
public get checked(): boolean {
return this._checkbox.checked;
}
public set checked(newValue:boolean) {
this._checkbox.checked = newValue;
}
public focus(): void {
@ -607,45 +661,44 @@ interface ISimpleButtonOpts {
onKeyDown: (e:DomUtils.IKeyboardEvent)=>void;
}
class SimpleButton implements IDisposable {
class SimpleButton extends Widget {
private _opts: ISimpleButtonOpts;
private _domNode: HTMLElement;
private _toDispose: IDisposable[];
constructor(opts:ISimpleButtonOpts) {
super();
this._opts = opts;
this._domNode = document.createElement('div');
this._domNode.title = this._opts.label;
this._domNode.tabIndex = -1;
this._domNode.tabIndex = 0;
this._domNode.className = 'button ' + this._opts.className;
this._domNode.setAttribute('role', 'button');
this._domNode.setAttribute('aria-label', this._opts.label);
this._toDispose = [];
this._toDispose.push(DomUtils.addStandardDisposableListener(this._domNode, 'click', (e) => {
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._opts.onTrigger();
e.preventDefault();
return;
}
this._opts.onKeyDown(e);
}));
}
public dispose(): void {
this._toDispose = disposeAll(this._toDispose);
});
}
public get domNode(): HTMLElement {
return this._domNode;
}
public isEnabled(): boolean {
return (this._domNode.tabIndex >= 0);
}
public focus(): void {
this._domNode.focus();
}

View file

@ -0,0 +1,678 @@
/*---------------------------------------------------------------------------------------------
* 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 nls from 'vs/nls';
import {TPromise} from 'vs/base/common/winjs.base';
import {CommonEditorRegistry, ContextKey, EditorActionDescriptor} from 'vs/editor/common/editorCommonExtensions';
import {EditorAction, Behaviour} from 'vs/editor/common/editorAction';
import {FindModelBoundToEditorModel, FIND_IDS} from 'vs/editor/contrib/find/common/findModel';
import {Disposable} 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 {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';
export enum FindStartFocusAction {
NoFocusChange,
FocusFindInput,
FocusReplaceInput
}
export interface IFindStartOptions {
forceRevealReplace:boolean;
seedSearchStringFromSelection:boolean;
seedSearchScopeFromSelection:boolean;
shouldFocus:FindStartFocusAction;
shouldAnimate:boolean;
}
const CONTEXT_FIND_WIDGET_VISIBLE = 'findWidgetVisible';
export class CommonFindController extends Disposable implements EditorCommon.IEditorContribution {
static ID = 'editor.contrib.findController';
private _editor: EditorCommon.ICommonCodeEditor;
private _findWidgetVisible: IKeybindingContextKey<boolean>;
protected _state: FindReplaceState;
private _model: FindModelBoundToEditorModel;
static getFindController(editor:EditorCommon.ICommonCodeEditor): CommonFindController {
return <CommonFindController>editor.getContribution(CommonFindController.ID);
}
constructor(editor:EditorCommon.ICommonCodeEditor, @IKeybindingService keybindingService: IKeybindingService) {
super();
this._editor = editor;
this._findWidgetVisible = keybindingService.createKey(CONTEXT_FIND_WIDGET_VISIBLE, false);
this._state = this._register(new FindReplaceState());
this._register(this._state.addChangeListener((e) => this._onStateChanged(e)));
this._model = null;
this._register(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
});
}
}));
}
public dispose(): void {
this.disposeModel();
super.dispose();
}
private disposeModel(): void {
if (this._model) {
this._model.dispose();
this._model = null;
}
}
public getId(): string {
return CommonFindController.ID;
}
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._state.change({ isRevealed: false }, false);
this._editor.focus();
}
public toggleCaseSensitive(): void {
this._state.change({ matchCase: !this._state.matchCase }, false);
}
public toggleWholeWords(): void {
this._state.change({ wholeWord: !this._state.wholeWord }, false);
}
public toggleRegex(): void {
this._state.change({ isRegex: !this._state.isRegex }, false);
}
public setSearchString(searchString:string): void {
this._state.change({ searchString: searchString }, false);
}
public getSelectionSearchString(): string {
let selection = this._editor.getSelection();
if (selection.startLineNumber === selection.endLineNumber) {
if (selection.isEmpty()) {
let wordAtPosition = this._editor.getModel().getWordAtPosition(selection.getStartPosition());
if (wordAtPosition) {
return wordAtPosition.word;
}
} else {
return this._editor.getModel().getValueInRange(selection);
}
}
return null;
}
protected _start(opts:IFindStartOptions): void {
this.disposeModel();
if (!this._editor.getModel()) {
// cannot do anything with an editor that doesn't have a model...
return;
}
let stateChanges: INewFindReplaceState = {
isRevealed: true
};
// Consider editor selection and overwrite the state with it
if (opts.seedSearchStringFromSelection) {
let selectionSearchString = this.getSelectionSearchString();
if (selectionSearchString) {
stateChanges.searchString = selectionSearchString;
}
}
let selection = this._editor.getSelection();
stateChanges.searchScope = null;
if (opts.seedSearchScopeFromSelection && selection.startLineNumber < selection.endLineNumber) {
// Take search scope into account only if it is more than one line.
stateChanges.searchScope = selection;
}
// Overwrite isReplaceRevealed
if (opts.forceRevealReplace) {
stateChanges.isReplaceRevealed = true;
}
this._state.change(stateChanges, false);
if (!this._model) {
this._model = new FindModelBoundToEditorModel(this._editor, this._state);
}
}
public start(opts:IFindStartOptions): void {
this._start(opts);
}
public moveToNextMatch(): boolean {
if (this._model) {
this._model.moveToNextMatch();
return true;
}
return false;
}
public moveToPrevMatch(): boolean {
if (this._model) {
this._model.moveToPrevMatch();
return true;
}
return false;
}
public replace(): boolean {
if (this._model) {
this._model.replace();
return true;
}
return false;
}
public replaceAll(): boolean {
if (this._model) {
this._model.replaceAll();
return true;
}
return false;
}
}
export class StartFindAction extends EditorAction {
constructor(descriptor: EditorCommon.IEditorActionDescriptorData, editor: EditorCommon.ICommonCodeEditor, @INullService ns) {
super(descriptor, editor, Behaviour.WidgetFocus);
}
public run(): TPromise<boolean> {
let controller = CommonFindController.getFindController(this.editor);
controller.start({
forceRevealReplace: false,
seedSearchStringFromSelection: true,
seedSearchScopeFromSelection: true,
shouldFocus: FindStartFocusAction.FocusFindInput,
shouldAnimate: true
});
return TPromise.as(true);
}
}
export abstract class MatchFindAction extends EditorAction {
constructor(descriptor:EditorCommon.IEditorActionDescriptorData, editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
super(descriptor, editor, Behaviour.WidgetFocus);
}
public run(): TPromise<boolean> {
let controller = CommonFindController.getFindController(this.editor);
if (!this._run(controller)) {
controller.start({
forceRevealReplace: false,
seedSearchStringFromSelection: (controller.getState().searchString.length === 0),
seedSearchScopeFromSelection: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: true
});
this._run(controller);
}
return TPromise.as(true);
}
protected abstract _run(controller:CommonFindController): boolean;
}
export class NextMatchFindAction extends MatchFindAction {
protected _run(controller:CommonFindController): boolean {
return controller.moveToNextMatch();
}
}
export class PreviousMatchFindAction extends MatchFindAction {
protected _run(controller:CommonFindController): boolean {
return controller.moveToPrevMatch();
}
}
export abstract class SelectionMatchFindAction extends EditorAction {
constructor(descriptor:EditorCommon.IEditorActionDescriptorData, editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
super(descriptor, editor, Behaviour.WidgetFocus);
}
public run(): TPromise<boolean> {
let controller = CommonFindController.getFindController(this.editor);
let selectionSearchString = controller.getSelectionSearchString();
if (selectionSearchString) {
controller.setSearchString(selectionSearchString);
}
if (!this._run(controller)) {
controller.start({
forceRevealReplace: false,
seedSearchStringFromSelection: false,
seedSearchScopeFromSelection: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: true
});
this._run(controller);
}
return TPromise.as(true);
}
protected abstract _run(controller:CommonFindController): boolean;
}
export class NextSelectionMatchFindAction extends SelectionMatchFindAction {
protected _run(controller:CommonFindController): boolean {
return controller.moveToNextMatch();
}
}
export class PreviousSelectionMatchFindAction extends SelectionMatchFindAction {
protected _run(controller:CommonFindController): boolean {
return controller.moveToPrevMatch();
}
}
export class StartFindReplaceAction extends EditorAction {
constructor(descriptor:EditorCommon.IEditorActionDescriptorData, editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
super(descriptor, editor, Behaviour.WidgetFocus | Behaviour.Writeable);
}
public run(): TPromise<boolean> {
let controller = CommonFindController.getFindController(this.editor);
controller.start({
forceRevealReplace: true,
seedSearchStringFromSelection: (controller.getState().searchString.length === 0),
seedSearchScopeFromSelection: true,
shouldFocus: FindStartFocusAction.FocusReplaceInput,
shouldAnimate: true
});
return TPromise.as(true);
}
}
export interface IMultiCursorFindResult {
searchText:string;
isRegex:boolean;
matchCase:boolean;
wholeWord:boolean;
nextMatch: EditorCommon.IEditorSelection;
}
export function multiCursorFind(editor:EditorCommon.ICommonCodeEditor, changeFindSearchString:boolean): IMultiCursorFindResult {
let controller = CommonFindController.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
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.isRevealed && state.searchString.length > 0) {
// Find widget owns what is searched for
searchText = state.searchString;
} else {
// Selection owns what is searched for
let s = editor.getSelection();
if (s.startLineNumber !== s.endLineNumber) {
// Cannot search for multiline string... yet...
return null;
}
if (s.isEmpty()) {
// selection is empty => expand to current word
let word = editor.getModel().getWordAtPosition(s.getStartPosition());
if (!word) {
return null;
}
searchText = word.word;
nextMatch = Selection.createSelection(s.startLineNumber, word.startColumn, s.startLineNumber, word.endColumn);
} else {
searchText = editor.getModel().getValueInRange(s);
}
if (changeFindSearchString) {
controller.setSearchString(searchText);
}
}
return {
searchText: searchText,
isRegex: isRegex,
matchCase: matchCase,
wholeWord: wholeWord,
nextMatch: nextMatch
};
}
export class SelectNextFindMatchAction extends EditorAction {
constructor(descriptor:EditorCommon.IEditorActionDescriptorData, editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
super(descriptor, editor, Behaviour.WidgetFocus);
}
protected _getNextMatch(): EditorCommon.IEditorSelection {
let r = multiCursorFind(this.editor, true);
if (!r) {
return null;
}
if (r.nextMatch) {
return r.nextMatch;
}
let allSelections = this.editor.getSelections();
let lastAddedSelection = allSelections[allSelections.length - 1];
let nextMatch = this.editor.getModel().findNextMatch(r.searchText, lastAddedSelection.getEndPosition(), r.isRegex, r.matchCase, r.wholeWord);
if (!nextMatch) {
return null;
}
return Selection.createSelection(nextMatch.startLineNumber, nextMatch.startColumn, nextMatch.endLineNumber, nextMatch.endColumn);
}
}
export class AddSelectionToNextFindMatchAction extends SelectNextFindMatchAction {
static ID = FIND_IDS.AddSelectionToNextFindMatchAction;
constructor(descriptor:EditorCommon.IEditorActionDescriptorData, editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
super(descriptor, editor, ns);
}
public run(): TPromise<boolean> {
let nextMatch = this._getNextMatch();
if (!nextMatch) {
return TPromise.as(false);
}
let allSelections = this.editor.getSelections();
this.editor.setSelections(allSelections.concat(nextMatch));
this.editor.revealRangeInCenterIfOutsideViewport(nextMatch);
return TPromise.as(true);
}
}
export class MoveSelectionToNextFindMatchAction extends SelectNextFindMatchAction {
static ID = FIND_IDS.MoveSelectionToNextFindMatchAction;
constructor(descriptor:EditorCommon.IEditorActionDescriptorData, editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
super(descriptor, editor, ns);
}
public run(): TPromise<boolean> {
let nextMatch = this._getNextMatch();
if (!nextMatch) {
return TPromise.as(false);
}
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);
return TPromise.as(true);
}
}
export class SelectHighlightsAction extends EditorAction {
static ID = 'editor.action.selectHighlights';
static COMPAT_ID = 'editor.action.changeAll';
constructor(descriptor:EditorCommon.IEditorActionDescriptorData, editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
let behaviour = Behaviour.WidgetFocus;
if (descriptor.id === SelectHighlightsAction.COMPAT_ID) {
behaviour |= Behaviour.ShowInContextMenu;
}
super(descriptor, editor, behaviour);
}
public getGroupId(): string {
return '2_change/1_changeAll';
}
public run(): TPromise<boolean> {
let r = multiCursorFind(this.editor, true);
if (!r) {
return TPromise.as(false);
}
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)));
}
return TPromise.as(true);
}
}
export class SelectionHighlighter extends Disposable implements EditorCommon.IEditorContribution {
static ID = 'editor.contrib.selectionHighlighter';
private editor: EditorCommon.ICommonCodeEditor;
private decorations: string[];
constructor(editor:EditorCommon.ICommonCodeEditor, @INullService ns) {
super();
this.editor = editor;
this.decorations = [];
this._register(editor.addListener2(EditorCommon.EventType.CursorPositionChanged, _ => this._update()));
this._register(editor.addListener2(EditorCommon.EventType.ModelChanged, (e) => {
this.removeDecorations();
}));
this._register(CommonFindController.getFindController(editor).getState().addChangeListener((e) => this._update()));
}
public getId(): string {
return SelectionHighlighter.ID;
}
private removeDecorations(): void {
if (this.decorations.length > 0) {
this.decorations = this.editor.deltaDecorations(this.decorations, []);
}
}
private _update(): void {
if (!this.editor.getConfiguration().selectionHighlight) {
return;
}
let r = multiCursorFind(this.editor, false);
if (!r) {
this.removeDecorations();
return;
}
let model = this.editor.getModel();
if (r.nextMatch) {
// This is an empty selection
if (OccurrencesRegistry.has(model)) {
// Do not interfere with semantic word highlighting in the no selection case
this.removeDecorations();
return;
}
}
if (/^[ \t]+$/.test(r.searchText)) {
// whitespace only selection
this.removeDecorations();
return;
}
if (r.searchText.length > 200) {
// very long selection
this.removeDecorations();
return;
}
let allMatches = model.findMatches(r.searchText, true, r.isRegex, r.matchCase, r.wholeWord);
allMatches.sort(Range.compareRangesUsingStarts);
let selections = this.editor.getSelections();
selections.sort(Range.compareRangesUsingStarts);
// do not overlap with selection (issue #64 and #512)
let matches: EditorCommon.IEditorRange[] = [];
for (let i = 0, j = 0, len = allMatches.length, lenJ = selections.length; i < len; ) {
let match = allMatches[i];
if (j >= lenJ) {
// finished all editor selections
matches.push(match);
i++;
} else {
let cmp = Range.compareRangesUsingStarts(match, selections[j]);
if (cmp < 0) {
// match is before sel
matches.push(match);
i++;
} else if (cmp > 0) {
// sel is before match
j++;
} else {
// sel is equal to match
i++;
j++;
}
}
}
let decorations = matches.map(r => {
return {
range: r,
options: {
stickiness: EditorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
className: 'selectionHighlight'
}
};
});
this.decorations = this.editor.deltaDecorations(this.decorations, decorations);
}
public dispose(): void {
this.removeDecorations();
super.dispose();
}
}
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(SelectHighlightsAction, SelectHighlightsAction.ID, nls.localize('selectAllOccurencesOfFindMatch', "Select All Occurences of Find Match"), {
context: ContextKey.EditorFocus,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_L
}));
// register SelectHighlightsAction again to replace the now removed Change All action
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(SelectHighlightsAction, SelectHighlightsAction.COMPAT_ID, nls.localize('changeAll.label', "Change All Occurrences"), {
context: ContextKey.EditorTextFocus,
primary: KeyMod.CtrlCmd | KeyCode.F2
}));
// register actions
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(StartFindAction, FIND_IDS.StartFindAction, nls.localize('startFindAction',"Find"), {
context: ContextKey.None,
primary: KeyMod.CtrlCmd | KeyCode.KEY_F
}));
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, 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(NextSelectionMatchFindAction, FIND_IDS.NextSelectionMatchFindAction, nls.localize('nextSelectionMatchFindAction', "Find Next Selection"), {
context: ContextKey.EditorFocus,
primary: KeyMod.CtrlCmd | KeyCode.F3
}));
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(PreviousSelectionMatchFindAction, FIND_IDS.PreviousSelectionMatchFindAction, nls.localize('previousSelectionMatchFindAction', "Find Previous Selection"), {
context: ContextKey.EditorFocus,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F3
}));
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 }
}));
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(MoveSelectionToNextFindMatchAction, MoveSelectionToNextFindMatchAction.ID, nls.localize('moveSelectionToNextFindMatch', "Move Last Selection To Next Find Match"), {
context: ContextKey.EditorFocus,
primary: KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_D)
}));
CommonEditorRegistry.registerEditorAction(new EditorActionDescriptor(AddSelectionToNextFindMatchAction, AddSelectionToNextFindMatchAction.ID, nls.localize('addSelectionToNextFindMatch', "Add Selection To Next Find Match"), {
context: ContextKey.EditorFocus,
primary: KeyMod.CtrlCmd | KeyCode.KEY_D
}));
function registerFindCommand(id:string, callback:(controller:CommonFindController)=>void, keybindings:IKeybindings, needsKey:string = null): void {
CommonEditorRegistry.registerEditorCommand(id, CommonEditorRegistry.commandWeight(5), keybindings, false, needsKey, (ctx, editor, args) => {
callback(CommonFindController.getFindController(editor));
});
}
registerFindCommand(FIND_IDS.CloseFindWidgetCommand, x => x.closeFindWidget(), {
primary: KeyCode.Escape
}, CONTEXT_FIND_WIDGET_VISIBLE);
registerFindCommand(FIND_IDS.ToggleCaseSensitiveCommand, x => x.toggleCaseSensitive(), {
primary: KeyMod.Alt | KeyCode.KEY_C,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C }
});
registerFindCommand(FIND_IDS.ToggleWholeWordCommand, x => x.toggleWholeWords(), {
primary: KeyMod.Alt | KeyCode.KEY_W,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_W }
});
registerFindCommand(FIND_IDS.ToggleRegexCommand, x => x.toggleRegex(), {
primary: KeyMod.Alt | KeyCode.KEY_R,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R }
});

View file

@ -4,123 +4,80 @@
*--------------------------------------------------------------------------------------------*/
'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';
import * as EditorCommon from 'vs/editor/common/editorCommon';
import {IDisposable} from 'vs/base/common/lifecycle';
export class FindDecorations implements Lifecycle.IDisposable {
private editor:EditorCommon.ICommonCodeEditor;
export class FindDecorations implements IDisposable {
private decorations:string[];
private decorationIndex:number;
private findScopeDecorationId:string;
private highlightedDecorationId:string;
private startPosition:EditorCommon.IEditorPosition;
private _editor:EditorCommon.ICommonCodeEditor;
private _decorations:string[];
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();
this._editor = editor;
this._decorations = [];
this._findScopeDecorationId = null;
this._highlightedDecorationId = null;
this._startPosition = this._editor.getPosition();
}
public dispose(): void {
this.editor.deltaDecorations(this._allDecorations(), []);
this._editor.deltaDecorations(this._allDecorations(), []);
this.editor = null;
this.decorations = [];
this.decorationIndex = 0;
this.findScopeDecorationId = null;
this.highlightedDecorationId = null;
this.startPosition = null;
this._editor = null;
this._decorations = [];
this._findScopeDecorationId = null;
this._highlightedDecorationId = null;
this._startPosition = null;
}
public reset(): void {
this.decorations = [];
this.decorationIndex = -1;
this.findScopeDecorationId = null;
this.highlightedDecorationId = null;
this._decorations = [];
this._findScopeDecorationId = null;
this._highlightedDecorationId = null;
}
public getFindScope(): EditorCommon.IEditorRange {
if (this.findScopeDecorationId) {
return this.editor.getModel().getDecorationRange(this.findScopeDecorationId);
if (this._findScopeDecorationId) {
return this._editor.getModel().getDecorationRange(this._findScopeDecorationId);
}
return null;
}
public getStartPosition(): EditorCommon.IEditorPosition {
return this._startPosition;
}
public setStartPosition(newStartPosition:EditorCommon.IEditorPosition): void {
this.startPosition = newStartPosition;
this._setDecorationIndex(-1, false);
this._startPosition = newStartPosition;
this.setCurrentFindMatch(null);
}
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;
public setCurrentFindMatch(nextMatch:EditorCommon.IEditorRange): void {
let newCurrentDecorationId: string = null;
if (nextMatch) {
for (let i = 0, len = this._decorations.length; i < len; i++) {
let range = this._editor.getModel().getDecorationRange(this._decorations[i]);
if (nextMatch.equalsRange(range)) {
newCurrentDecorationId = this._decorations[i];
break;
}
}
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);
}
if (this._highlightedDecorationId !== null || newCurrentDecorationId !== null) {
this._editor.changeDecorations((changeAccessor: EditorCommon.IModelDecorationsChangeAccessor) => {
if (this._highlightedDecorationId !== null) {
changeAccessor.changeDecorationOptions(this._highlightedDecorationId, FindDecorations.createFindMatchDecorationOptions(false));
this._highlightedDecorationId = null;
}
if (newCurrentDecorationId !== null) {
this._highlightedDecorationId = newCurrentDecorationId;
changeAccessor.changeDecorationOptions(this._highlightedDecorationId, FindDecorations.createFindMatchDecorationOptions(true));
}
});
}
}
@ -137,62 +94,26 @@ export class FindDecorations implements Lifecycle.IDisposable {
options: FindDecorations.createFindScopeDecorationOptions()
});
}
let tmpDecorations = this.editor.deltaDecorations(this._allDecorations(), newDecorations);
let tmpDecorations = this._editor.deltaDecorations(this._allDecorations(), newDecorations);
if (findScope) {
this.findScopeDecorationId = tmpDecorations.shift();
this._findScopeDecorationId = tmpDecorations.shift();
} else {
this.findScopeDecorationId = null;
this._findScopeDecorationId = null;
}
this.decorations = tmpDecorations;
this.decorationIndex = -1;
this.highlightedDecorationId = null;
this._decorations = tmpDecorations;
this._highlightedDecorationId = null;
}
private _allDecorations(): string[] {
let result:string[] = [];
result = result.concat(this.decorations);
if (this.findScopeDecorationId) {
result.push(this.findScopeDecorationId);
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,

View file

@ -3,12 +3,12 @@
* 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 * as EditorCommon from 'vs/editor/common/editorCommon';
import * as Strings from 'vs/base/common/strings';
import {ReplaceAllCommand} from './replaceAllCommand';
import {IDisposable, disposeAll} from 'vs/base/common/lifecycle';
import {RunOnceScheduler} from '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';
@ -19,6 +19,10 @@ export const FIND_IDS = {
StartFindAction: 'actions.find',
NextMatchFindAction: 'editor.action.nextMatchFindAction',
PreviousMatchFindAction: 'editor.action.previousMatchFindAction',
NextSelectionMatchFindAction: 'editor.action.nextSelectionMatchFindAction',
PreviousSelectionMatchFindAction: 'editor.action.previousSelectionMatchFindAction',
AddSelectionToNextFindMatchAction: 'editor.action.addSelectionToNextFindMatch',
MoveSelectionToNextFindMatchAction: 'editor.action.moveSelectionToNextFindMatch',
StartFindReplaceAction: 'editor.action.startFindReplaceAction',
CloseFindWidgetCommand: 'closeFindWidget',
ToggleCaseSensitiveCommand: 'toggleFindCaseSensitive',
@ -26,35 +30,37 @@ export const FIND_IDS = {
ToggleRegexCommand: 'toggleFindRegex'
}
export const MATCHES_LIMIT = 999;
export class FindModelBoundToEditorModel {
private editor:EditorCommon.ICommonCodeEditor;
private _editor:EditorCommon.ICommonCodeEditor;
private _state:FindReplaceState;
private _toDispose:Lifecycle.IDisposable[];
private _toDispose:IDisposable[];
private _decorations: FindDecorations;
private _ignoreModelContentChanged:boolean;
private updateDecorationsScheduler:Schedulers.RunOnceScheduler;
private _updateDecorationsScheduler:RunOnceScheduler;
constructor(editor:EditorCommon.ICommonCodeEditor, state:FindReplaceState) {
this.editor = editor;
this._editor = editor;
this._state = state;
this._toDispose = [];
this._decorations = new FindDecorations(editor);
this._toDispose.push(this._decorations);
this.updateDecorationsScheduler = new Schedulers.RunOnceScheduler(() => this.research(false), 100);
this._toDispose.push(this.updateDecorationsScheduler);
this._updateDecorationsScheduler = new RunOnceScheduler(() => this.research(false), 100);
this._toDispose.push(this._updateDecorationsScheduler);
this._toDispose.push(this.editor.addListener2(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') {
this._decorations.setStartPosition(this.editor.getPosition());
this._decorations.setStartPosition(this._editor.getPosition());
}
}));
this._ignoreModelContentChanged = false;
this._toDispose.push(this.editor.addListener2(EditorCommon.EventType.ModelContentChanged, (e:EditorCommon.IModelContentChangedEvent) => {
this._toDispose.push(this._editor.addListener2(EditorCommon.EventType.ModelContentChanged, (e:EditorCommon.IModelContentChangedEvent) => {
if (this._ignoreModelContentChanged) {
return;
}
@ -62,8 +68,8 @@ export class FindModelBoundToEditorModel {
// a model.setValue() was called
this._decorations.reset();
}
this._decorations.setStartPosition(this.editor.getPosition());
this.updateDecorationsScheduler.schedule();
this._decorations.setStartPosition(this._editor.getPosition());
this._updateDecorationsScheduler.schedule();
}));
this._toDispose.push(this._state.addChangeListener((e) => this._onStateChanged(e)));
@ -72,7 +78,7 @@ export class FindModelBoundToEditorModel {
}
public dispose(): void {
this._toDispose = Lifecycle.disposeAll(this._toDispose);
this._toDispose = disposeAll(this._toDispose);
}
private _onStateChanged(e:FindReplaceStateChangedEvent): void {
@ -109,23 +115,167 @@ export class FindModelBoundToEditorModel {
} else {
findScope = this._decorations.getFindScope();
}
if (findScope !== null) {
findScope = new Range(findScope.startLineNumber, 1, findScope.endLineNumber, this._editor.getModel().getLineMaxColumn(findScope.endLineNumber));
}
let findMatches = this._findMatches(findScope);
let findMatches = this._findMatches(findScope, MATCHES_LIMIT);
this._decorations.set(findMatches, findScope);
this._state.change({ matchesCount: findMatches.length }, false);
if (moveCursor) {
this._decorations.moveToFirstAfterStartPosition();
this._moveToNextMatch(this._decorations.getStartPosition());
}
}
private _hasMatches(): boolean {
return (this._state.matchesCount > 0);
}
private _cannotFind(): boolean {
if (!this._hasMatches()) {
let findScope = this._decorations.getFindScope();
if (findScope) {
// Reveal the selection so user is reminded that 'selection find' is on.
this._editor.revealRangeInCenterIfOutsideViewport(findScope);
}
return true;
}
return false;
}
private _moveToPrevMatch(before:EditorCommon.IEditorPosition, isRecursed:boolean = false): void {
if (this._cannotFind()) {
return;
}
let findScope = this._decorations.getFindScope();
let searchRange = FindModelBoundToEditorModel._getSearchRange(this._editor.getModel(), this._state.isReplaceRevealed, findScope);
// ...(----)...|...
if (searchRange.getEndPosition().isBefore(before)) {
before = searchRange.getEndPosition();
}
// ...|...(----)...
if (before.isBefore(searchRange.getStartPosition())) {
before = searchRange.getEndPosition();
}
let {lineNumber,column} = before;
let model = this._editor.getModel();
if (this._state.isRegex) {
// Force advancing to the previous line if searching for $
if (this._state.searchString === '$') {
if (lineNumber === 1) {
lineNumber = model.getLineCount();
} else {
lineNumber--;
}
column = model.getLineMaxColumn(lineNumber);
}
// Force advancing to the previous line if searching for ^ or ^$ and cursor is at the beginning
if (this._state.searchString === '^' || this._state.searchString === '^$') {
if (column === 1) {
if (lineNumber === 1) {
lineNumber = model.getLineCount();
} else {
lineNumber--;
}
column = model.getLineMaxColumn(lineNumber);
}
}
}
let position = new Position(lineNumber, column);
let nextMatch = model.findPreviousMatch(this._state.searchString, position, this._state.isRegex, this._state.matchCase, this._state.wholeWord);
if (!nextMatch) {
// there is precisely one match and selection is on top of it
return;
}
if (!isRecursed && !searchRange.containsRange(nextMatch)) {
return this._moveToPrevMatch(nextMatch.getStartPosition(), true);
}
this._decorations.setCurrentFindMatch(nextMatch);
this._editor.setSelection(nextMatch);
this._editor.revealRangeInCenterIfOutsideViewport(nextMatch);
}
public moveToPrevMatch(): void {
this._decorations.movePrev();
this._moveToPrevMatch(this._editor.getSelection().getStartPosition());
}
public _moveToNextMatch(after:EditorCommon.IEditorPosition, isRecursed:boolean = false): void {
if (this._cannotFind()) {
return;
}
let findScope = this._decorations.getFindScope();
let searchRange = FindModelBoundToEditorModel._getSearchRange(this._editor.getModel(), this._state.isReplaceRevealed, findScope);
// ...(----)...|...
if (searchRange.getEndPosition().isBefore(after)) {
after = searchRange.getStartPosition();
}
// ...|...(----)...
if (after.isBefore(searchRange.getStartPosition())) {
after = searchRange.getStartPosition();
}
let {lineNumber,column} = after;
let model = this._editor.getModel();
if (this._state.isRegex) {
// Force advancing to the next line if searching for ^ or ^$
if (this._state.searchString === '^' || this._state.searchString === '^$') {
if (lineNumber === model.getLineCount()) {
lineNumber = 1;
} else {
lineNumber++;
}
column = 1;
}
// Force advancing to the next line if searching for $ and at the end of the line
if (this._state.searchString === '$') {
if (column === model.getLineMaxColumn(lineNumber)) {
if (lineNumber === model.getLineCount()) {
lineNumber = 1;
} else {
lineNumber++;
}
column = 1;
}
}
}
let position = new Position(lineNumber, column);
let nextMatch = model.findNextMatch(this._state.searchString, position, this._state.isRegex, this._state.matchCase, this._state.wholeWord);
if (!nextMatch) {
// there is precisely one match and selection is on top of it
return;
}
if (!isRecursed && !searchRange.containsRange(nextMatch)) {
return this._moveToNextMatch(nextMatch.getEndPosition(), true);
}
this._decorations.setCurrentFindMatch(nextMatch);
this._editor.setSelection(nextMatch);
this._editor.revealRangeInCenterIfOutsideViewport(nextMatch);
}
public moveToNextMatch(): void {
this._decorations.moveNext();
this._moveToNextMatch(this._editor.getSelection().getEndPosition());
}
private getReplaceString(matchedString:string): string {
@ -138,16 +288,22 @@ export class FindModelBoundToEditorModel {
return matchedString.replace(regexp, parsedReplaceString);
}
private _rangeIsMatch(range:EditorCommon.IEditorRange): boolean {
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);
return (m && m[0].length === selectionText.length);
}
public replace(): void {
if (!this._decorations.hasMatches()) {
if (!this._hasMatches()) {
return;
}
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) {
let selection = this._editor.getSelection();
let selectionText = this._editor.getModel().getValueInRange(selection);
if (this._rangeIsMatch(selection)) {
// selection sits on a find match => replace it!
let replaceString = this.getReplaceString(selectionText);
@ -158,22 +314,22 @@ export class FindModelBoundToEditorModel {
this._decorations.setStartPosition(new Position(selection.startLineNumber, selection.startColumn + replaceString.length));
this.research(true);
} else {
this._decorations.setStartPosition(this.editor.getPosition());
this._decorations.setStartPosition(this._editor.getPosition());
this.moveToNextMatch();
}
}
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);
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.hasMatches()) {
if (!this._hasMatches()) {
return;
}
let model = this.editor.getModel();
let model = this._editor.getModel();
let findScope = this._decorations.getFindScope();
// Get all the ranges (even more than the highlighted ones)
@ -186,14 +342,14 @@ export class FindModelBoundToEditorModel {
replaceStrings.push(this.getReplaceString(model.getValueInRange(ranges[i])));
}
let command = new ReplaceAllCommand.ReplaceAllCommand(ranges, replaceStrings);
let command = new ReplaceAllCommand(ranges, replaceStrings);
this._executeEditorCommand('replaceAll', command);
}
private _executeEditorCommand(source:string, command:EditorCommon.ICommand): void {
try {
this._ignoreModelContentChanged = true;
this.editor.executeCommand(source, command);
this._editor.executeCommand(source, command);
} finally {
this._ignoreModelContentChanged = false;
}

View file

@ -0,0 +1,100 @@
/*---------------------------------------------------------------------------------------------
* 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 assert = require('assert');
import {FindModelBoundToEditorModel, parseReplaceString} from 'vs/editor/contrib/find/common/findModel';
import * as EditorCommon from 'vs/editor/common/editorCommon';
import {withMockCodeEditor} from 'vs/editor/test/common/mocks/mockCodeEditor';
import {Cursor} from 'vs/editor/common/controller/cursor';
import {INewFindReplaceState, FindReplaceStateChangedEvent, FindReplaceState} from 'vs/editor/contrib/find/common/findState';
import {Range} from 'vs/editor/common/core/range';
import {Position} from 'vs/editor/common/core/position';
import {
CommonFindController,
IFindStartOptions,
FindStartFocusAction,
StartFindAction,
NextMatchFindAction
} from 'vs/editor/contrib/find/common/findController';
import {EditOperation} from 'vs/editor/common/core/editOperation';
class TestFindController extends CommonFindController {
public hasFocus: boolean;
protected _start(opts:IFindStartOptions): void {
super._start(opts);
if (opts.shouldFocus !== FindStartFocusAction.NoFocusChange) {
this.hasFocus = true;
}
}
}
suite('FindController', () => {
function fromRange(rng:EditorCommon.IRange): number[] {
return [rng.startLineNumber, rng.startColumn, rng.endLineNumber, rng.endColumn];
}
test('issue #1857: F3, Find Next, acts like "Find Under Cursor"', () => {
withMockCodeEditor([
'ABC',
'ABC',
'XYZ',
'ABC'
], {}, (editor, cursor) => {
// The cursor is at the very top, of the file, at the first ABC
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let findState = findController.getState();
let startFindAction = new StartFindAction({id:'',label:''}, editor, null);
let nextMatchFindAction = new NextMatchFindAction({id:'',label:''}, editor, null);
// I hit Ctrl+F to show the Find dialog
startFindAction.run();
// I type ABC.
findState.change({ searchString: 'A' }, true);
findState.change({ searchString: 'AB' }, true);
findState.change({ searchString: 'ABC' }, true);
// The first ABC is highlighted.
assert.deepEqual(fromRange(editor.getSelection()), [1, 1, 1, 4]);
// I hit Esc to exit the Find dialog.
findController.closeFindWidget();
findController.hasFocus = false;
// The cursor is now at end of the first line, with ABC on that line highlighted.
assert.deepEqual(fromRange(editor.getSelection()), [1, 1, 1, 4]);
// I hit delete to remove it and change the text to XYZ.
editor.executeEdits('test', [EditOperation.delete(new Range(1, 1, 1, 4))]);
editor.executeEdits('test', [EditOperation.insert(new Position(1, 1), 'XYZ')]);
// At this point the text editor looks like this:
// XYZ
// ABC
// XYZ
// ABC
assert.equal(editor.getModel().getLineContent(1), 'XYZ');
// The cursor is at end of the first line.
assert.deepEqual(fromRange(editor.getSelection()), [1, 4, 1, 4]);
// I hit F3 to "Find Next" to find the next occurrence of ABC, but instead it searches for XYZ.
nextMatchFindAction.run();
assert.equal(findState.searchString, 'ABC');
assert.equal(findController.hasFocus, false);
findController.dispose();
startFindAction.dispose();
nextMatchFindAction.dispose();
});
});
});

File diff suppressed because it is too large Load diff

View file

@ -57,8 +57,31 @@
overflow: hidden;
margin-top: 0.1em;
padding-bottom: 0.1em;
}
.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content .docs > .docs-text {
flex: 2;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content .docs > .docs-details {
font-size: 1.4em;
opacity: 0.6;
}
.monaco-editor .suggest-widget .details > .header > .go-back,
.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content .docs > .docs-details {
color: #0035DD;
}
.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content .docs > .docs-details:hover {
opacity: 1;
}
.monaco-editor .suggest-widget .details > .body > .docs,
.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content .docs {
font-family:"Segoe UI", "SFUIText-Regular", "HelveticaNeue", sans-serif, "Droid Sans Fallback";
color: #A21B1B;
}
@ -75,6 +98,10 @@
.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content .type-label {
display: none;
margin-left: 0.8em;
}
.monaco-editor .suggest-widget .details > .body > .type,
.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content .type-label {
font-size: 0.9em;
color: #0035DD;
}
@ -84,7 +111,7 @@
}
.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row.focused > .content .docs {
display: block;
display: flex;
}
.monaco-editor .suggest-widget .monaco-tree .monaco-tree-row > .content .icon {
@ -130,6 +157,41 @@
display: inline-block;
}
.monaco-editor .suggest-widget .details {
display: flex;
flex-flow: column;
height: 100%;
box-sizing: border-box;
padding: 0 10px;
}
.monaco-editor .suggest-widget .details > .header {
padding-top: 2px;
display: flex;
}
.monaco-editor .suggest-widget .details > .header > .title {
flex: 2;
}
.monaco-editor .suggest-widget .details > .header > .go-back {
cursor: pointer;
opacity: 0.6;
}
.monaco-editor .suggest-widget .details > .header > .go-back:hover {
opacity: 1;
}
.monaco-editor .suggest-widget .details > .body {
flex: 2;
overflow-y: scroll;
}
.monaco-editor .suggest-widget .details > .body > p {
margin: 0;
}
/* Dark theme */
.monaco-editor.vs-dark .suggest-widget {
background-color: #2D2D30;
@ -140,10 +202,15 @@
color: #219AE4;
}
.monaco-editor.vs-dark .suggest-widget .details > .body > .docs,
.monaco-editor.vs-dark .suggest-widget .monaco-tree .monaco-tree-row > .content .docs {
color: #C07A7A;
}
.monaco-editor.vs-dark .suggest-widget .monaco-tree .monaco-tree-row > .content .docs > .docs-details,
.monaco-editor.vs-dark .suggest-widget .details > .header > .go-back,
.monaco-editor.vs-dark .suggest-widget .details > .body > .type,
.monaco-editor.vs-dark .suggest-widget .monaco-tree .monaco-tree-row > .content .type-label {
color: #4E94CE;
}
@ -181,10 +248,14 @@
color: #219AE4;
}
.monaco-editor.hc-black .suggest-widget .details > .body > .docs,
.monaco-editor.hc-black .suggest-widget .monaco-tree .monaco-tree-row > .content .docs {
color: #C07A7A;
}
.monaco-editor.hc-black .suggest-widget .monaco-tree .monaco-tree-row > .content .docs > .docs-details,
.monaco-editor.hc-black .suggest-widget .details > .header > .go-back,
.monaco-editor.hc-black .suggest-widget .details > .body > .type,
.monaco-editor.hc-black .suggest-widget .monaco-tree .monaco-tree-row > .content .type-label {
color: #4E94CE;
}

View file

@ -157,7 +157,7 @@ export class SuggestController implements EditorCommon.IEditorContribution {
}
}
public hideSuggestWidget(): void {
public cancelSuggestWidget(): void {
if (this.widget) {
this.widget.cancel();
}
@ -186,6 +186,12 @@ export class SuggestController implements EditorCommon.IEditorContribution {
this.widget.selectPreviousPage();
}
}
public toggleSuggestionDetails(): void {
if (this.widget) {
this.widget.toggleDetails();
}
}
}
export class TriggerSuggestAction extends EditorAction {
@ -219,7 +225,7 @@ CommonEditorRegistry.registerEditorCommand(ACCEPT_SELECTED_SUGGESTION_CMD, weigh
});
CommonEditorRegistry.registerEditorCommand('hideSuggestWidget', weight, { primary: KeyCode.Escape }, true, CONTEXT_SUGGEST_WIDGET_VISIBLE, (ctx, editor, args) => {
const controller = SuggestController.getSuggestController(editor);
controller.hideSuggestWidget();
controller.cancelSuggestWidget();
});
CommonEditorRegistry.registerEditorCommand('selectNextSuggestion', weight, { primary: KeyCode.DownArrow }, true, CONTEXT_SUGGEST_WIDGET_VISIBLE, (ctx, editor, args) => {
const controller = SuggestController.getSuggestController(editor);
@ -237,4 +243,8 @@ CommonEditorRegistry.registerEditorCommand('selectPrevPageSuggestion', weight, {
const controller = SuggestController.getSuggestController(editor);
controller.selectPrevPageSuggestion();
});
CommonEditorRegistry.registerEditorCommand('toggleSuggestionDetails', weight, { primary: KeyMod.CtrlCmd | KeyCode.Space, mac: { primary: KeyMod.WinCtrl | KeyCode.Space } }, true, CONTEXT_SUGGEST_WIDGET_VISIBLE, (ctx, editor, args) => {
const controller = SuggestController.getSuggestController(editor);
controller.toggleSuggestionDetails();
});
EditorBrowserRegistry.registerEditorContribution(SuggestController);

View file

@ -11,7 +11,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { IDisposable, disposeAll } from 'vs/base/common/lifecycle';
import { assign } from 'vs/base/common/objects';
import Event, { Emitter } from 'vs/base/common/event';
import { append, addClass, removeClass, emmet as $ } from 'vs/base/browser/dom';
import { append, addClass, removeClass, toggleClass, emmet as $, hide, show, addDisposableListener } from 'vs/base/browser/dom';
import * as Tree from 'vs/base/parts/tree/common/tree';
import * as TreeImpl from 'vs/base/parts/tree/browser/treeImpl';
import * as TreeDefaults from 'vs/base/parts/tree/browser/treeDefaults';
@ -30,10 +30,7 @@ import { ISuggestResult2 } from '../common/suggest';
import URI from 'vs/base/common/uri';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { onUnexpectedError, isPromiseCanceledError, illegalArgument } from 'vs/base/common/errors';
const defaultCompare: ISuggestionCompare = (a, b) => {
return (a.sortText || a.label).localeCompare((b.sortText || b.label));
}
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
class CompletionItem {
@ -73,6 +70,8 @@ class CompletionItem {
}
}
const defaultCompare: ISuggestionCompare = (a, b) => (a.sortText || a.label).localeCompare((b.sortText || b.label));
class CompletionGroup {
incomplete: boolean;
@ -263,11 +262,22 @@ interface ISuggestionTemplateData {
colorspan: HTMLElement;
highlightedLabel: HighlightedLabel.HighlightedLabel;
typeLabel: HTMLElement;
documentationDetails: HTMLElement;
documentation: HTMLElement;
}
class Renderer implements Tree.IRenderer {
private triggerKeybindingLabel: string;
constructor(
private widget: SuggestWidget,
@IKeybindingService keybindingService: IKeybindingService
) {
const keybindings = keybindingService.lookupKeybindings('editor.action.triggerSuggest');
this.triggerKeybindingLabel = keybindings.length === 0 ? '' : ` (${keybindingService.getLabelFor(keybindings[0])})`;
}
public getHeight(tree: Tree.ITree, element: any): number {
if (element instanceof CompletionItem) {
if ((<CompletionItem>element).suggestion.documentationLabel && tree.isFocused(element)) {
@ -300,7 +310,10 @@ class Renderer implements Tree.IRenderer {
const main = append(text, $('.main'));
data.highlightedLabel = new HighlightedLabel.HighlightedLabel(main);
data.typeLabel = append(main, $('span.type-label'));
data.documentation = append(text, $('.docs'));
const docs = append(text, $('.docs'));
data.documentation = append(docs, $('span.docs-text'));
data.documentationDetails = append(docs, $('span.docs-details.octicon.octicon-info'));
data.documentationDetails.title = nls.localize('readMore', "Read More...{0}", this.triggerKeybindingLabel);
return data;
}
@ -327,6 +340,19 @@ class Renderer implements Tree.IRenderer {
data.highlightedLabel.set(suggestion.label, (<CompletionItem>element).highlights);
data.typeLabel.textContent = suggestion.typeLabel || '';
data.documentation.textContent = suggestion.documentationLabel || '';
if (suggestion.documentationLabel) {
show(data.documentationDetails);
data.documentationDetails.onclick = e => {
e.stopPropagation();
e.preventDefault();
this.widget.toggleDetails();
};
} else {
hide(data.documentationDetails);
data.documentationDetails.onclick = null;
}
}
public disposeTemplate(tree: Tree.ITree, templateId: string, templateData: any): void {
@ -367,11 +393,76 @@ interface ITelemetryData {
enum State {
Hidden,
Triggered,
Loading,
Empty,
Open,
Frozen
Frozen,
Details
}
class SuggestionDetails {
private el: HTMLElement;
private title: HTMLElement;
private back: HTMLElement;
private body: HTMLElement;
private type: HTMLElement;
private docs: HTMLElement;
constructor(container: HTMLElement, private widget: SuggestWidget) {
this.el = append(container, $('.details'));
const header = append(this.el, $('.header'));
this.title = append(header, $('span.title'));
this.back = append(header, $('span.go-back.octicon.octicon-x'));
this.back.title = nls.localize('goback', "Go back");
this.body = append(this.el, $('.body'));
this.type = append(this.body, $('p.type'));
this.docs = append(this.body, $('p.docs'));
addDisposableListener(this.docs, 'mousewheel', e => e.stopPropagation());
}
get element() {
return this.el;
}
render(item: CompletionItem): void {
if (!item) {
this.title.textContent = '';
this.type.textContent = '';
this.docs.textContent = '';
return;
}
this.title.innerText = item.suggestion.label;
this.type.innerText = item.suggestion.typeLabel;
this.docs.innerText = item.suggestion.documentationLabel;
this.back.onclick = e => {
e.preventDefault();
e.stopPropagation();
this.widget.toggleDetails();
};
}
scrollDown(much = 8): void {
this.body.scrollTop += much;
}
scrollUp(much = 8): void {
this.body.scrollTop -= much;
}
pageDown(): void {
this.scrollDown(80);
}
pageUp(): void {
this.scrollUp(80);
}
dispose(): void {
this.el.parentElement.removeChild(this.el);
this.el = null;
}
}
export class SuggestWidget implements EditorBrowser.IContentWidget, IDisposable {
@ -399,6 +490,7 @@ export class SuggestWidget implements EditorBrowser.IContentWidget, IDisposable
private element: HTMLElement;
private messageElement: HTMLElement;
private treeElement: HTMLElement;
private details: SuggestionDetails;
private tree: Tree.ITree;
private renderer: Renderer;
@ -411,7 +503,8 @@ export class SuggestWidget implements EditorBrowser.IContentWidget, IDisposable
private editor: EditorBrowser.ICodeEditor,
private model: SuggestModel,
@IKeybindingService keybindingService: IKeybindingService,
@ITelemetryService telemetryService: ITelemetryService
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService instantiationService: IInstantiationService
) {
this.isAuto = false;
this.oldFocus = null;
@ -430,11 +523,12 @@ export class SuggestWidget implements EditorBrowser.IContentWidget, IDisposable
}
this.messageElement = append(this.element, $('.message'));
this.messageElement.style.display = 'none';
this.treeElement = append(this.element, $('.tree'));
this.details = new SuggestionDetails(this.element, this);
this.renderer = instantiationService.createInstance(Renderer, this);
const configuration = {
renderer: this.renderer = new Renderer(),
renderer: this.renderer,
dataSource: new DataSource(),
controller: new Controller(),
filter: new Filter(() => this.state),
@ -479,7 +573,7 @@ export class SuggestWidget implements EditorBrowser.IContentWidget, IDisposable
private onEditorBlur(): void {
TPromise.timeout(150).done(() => {
if (this.tree && !this.tree.isDOMFocused()) {
if (!this.editor.isFocused()) {
this.setState(State.Hidden);
}
});
@ -553,38 +647,35 @@ export class SuggestWidget implements EditorBrowser.IContentWidget, IDisposable
private setState(state: State): void {
this.state = state;
toggleClass(this.element, 'frozen', state === State.Frozen);
switch (state) {
case State.Hidden:
this.messageElement.style.display = 'none';
this.treeElement.style.display = 'block';
this.hide();
return;
case State.Triggered:
this.messageElement.style.display = 'none';
this.treeElement.style.display = 'block';
hide(this.messageElement, this.details.element);
show(this.treeElement);
this.hide();
return;
case State.Loading:
this.messageElement.innerText = SuggestWidget.LOADING_MESSAGE;
this.messageElement.style.display = 'block';
this.treeElement.style.display = 'none';
removeClass(this.element, 'frozen');
hide(this.treeElement, this.details.element);
show(this.messageElement);
break;
case State.Empty:
this.messageElement.innerText = SuggestWidget.NO_SUGGESTIONS_MESSAGE;
this.messageElement.style.display = 'block';
this.treeElement.style.display = 'none';
removeClass(this.element, 'frozen');
hide(this.treeElement, this.details.element);
show(this.messageElement);
break;
case State.Open:
this.messageElement.style.display = 'none';
this.treeElement.style.display = 'block';
removeClass(this.element, 'frozen');
hide(this.messageElement, this.details.element);
show(this.treeElement);
break;
case State.Frozen:
this.messageElement.style.display = 'none';
this.treeElement.style.display = 'block';
addClass(this.element, 'frozen');
hide(this.messageElement, this.details.element);
show(this.treeElement);
break;
case State.Details:
hide(this.messageElement, this.treeElement);
show(this.details.element);
break;
}
@ -749,7 +840,9 @@ export class SuggestWidget implements EditorBrowser.IContentWidget, IDisposable
switch (this.state) {
case State.Hidden:
return false;
case State.Triggered:
case State.Details:
this.details.pageDown();
return true;
case State.Loading:
return !this.isAuto;
default:
@ -762,7 +855,9 @@ export class SuggestWidget implements EditorBrowser.IContentWidget, IDisposable
switch (this.state) {
case State.Hidden:
return false;
case State.Triggered:
case State.Details:
this.details.scrollDown();
return true;
case State.Loading:
return !this.isAuto;
default:
@ -779,7 +874,9 @@ export class SuggestWidget implements EditorBrowser.IContentWidget, IDisposable
switch (this.state) {
case State.Hidden:
return false;
case State.Triggered:
case State.Details:
this.details.pageUp();
return true;
case State.Loading:
return !this.isAuto;
default:
@ -792,7 +889,9 @@ export class SuggestWidget implements EditorBrowser.IContentWidget, IDisposable
switch (this.state) {
case State.Hidden:
return false;
case State.Triggered:
case State.Details:
this.details.scrollUp();
return true;
case State.Loading:
return !this.isAuto;
default:
@ -809,7 +908,6 @@ export class SuggestWidget implements EditorBrowser.IContentWidget, IDisposable
switch (this.state) {
case State.Hidden:
return false;
case State.Triggered:
case State.Loading:
return !this.isAuto;
default:
@ -823,9 +921,31 @@ export class SuggestWidget implements EditorBrowser.IContentWidget, IDisposable
}
}
public toggleDetails(): void {
if (this.state === State.Details) {
this.setState(State.Open);
this.editor.focus();
return;
}
if (this.state !== State.Open) {
return;
}
const item: CompletionItem = this.tree.getFocus();
if (!item || !item.suggestion.documentationLabel) {
return;
}
this.setState(State.Details);
this.editor.focus();
}
public show(): void {
this._onDidVisibilityChange.fire(true);
this.tree.layout();
this.renderDetails();
this.editor.layoutContentWidget(this);
TPromise.timeout(100).done(() => {
addClass(this.element, 'visible');
@ -839,7 +959,11 @@ export class SuggestWidget implements EditorBrowser.IContentWidget, IDisposable
}
public cancel(): void {
this.model.cancel();
if (this.state === State.Details) {
this.toggleDetails();
} else {
this.model.cancel();
}
}
public getPosition(): EditorBrowser.IContentWidgetPosition {
@ -867,19 +991,19 @@ export class SuggestWidget implements EditorBrowser.IContentWidget, IDisposable
}
private updateWidgetHeight(): void {
const maxHeight = 1000;
let height = 0;
if (this.state === State.Empty || this.state === State.Loading) {
height = 19;
} else if (this.state === State.Details) {
height = 12 * 19;
} else {
const focus = this.tree.getFocus();
const focusHeight = focus ? this.renderer.getHeight(this.tree, focus) : 19;
height += focusHeight;
const suggestionCount = (this.tree.getContentHeight() - focusHeight) / 19;
const maxSuggestions = Math.floor((maxHeight - focusHeight) / 19);
height += Math.min(suggestionCount, 11, maxSuggestions) * 19;
height += Math.min(suggestionCount, 11) * 19;
}
this.element.style.height = height + 'px';
@ -887,6 +1011,14 @@ export class SuggestWidget implements EditorBrowser.IContentWidget, IDisposable
this.editor.layoutContentWidget(this);
}
private renderDetails(): void {
if (this.state !== State.Details) {
this.details.render(null);
} else {
this.details.render(this.tree.getFocus());
}
}
public dispose(): void {
this.state = null;
this.suggestionSupportsAutoAccept = null;
@ -898,6 +1030,8 @@ export class SuggestWidget implements EditorBrowser.IContentWidget, IDisposable
this.element = null;
this.messageElement = null;
this.treeElement = null;
this.details.dispose();
this.details = null;
this.tree.dispose();
this.tree = null;
this.renderer = null;

View file

@ -11,34 +11,7 @@ import {Selection} from 'vs/editor/common/core/selection';
import {Cursor} from 'vs/editor/common/controller/cursor';
import EditorCommon = require('vs/editor/common/editorCommon');
import {IMode} from 'vs/editor/common/modes';
import {CommonEditorConfiguration, ICSSConfig} from 'vs/editor/common/config/commonEditorConfig';
export class MockConfiguration extends CommonEditorConfiguration {
constructor(opts:any) {
super(opts);
}
protected getOuterWidth(): number {
return 100;
}
protected getOuterHeight(): number {
return 100;
}
protected readConfiguration(editorClassName: string, fontFamily: string, fontSize: number, lineHeight: number): ICSSConfig {
// Doesn't really matter
return {
typicalHalfwidthCharacterWidth: 10,
typicalFullwidthCharacterWidth: 20,
maxDigitWidth: 10,
lineHeight: 20,
font: 'mockFont',
fontSize: 20
};
}
}
import {MockConfiguration} from 'vs/editor/test/common/mocks/mockConfiguration';
export function testCommand(
lines: string[],

View file

@ -55,7 +55,7 @@ class DocBlockCommentMode implements Modes.IMode {
},
{
// e.g. * ...|
beforeText: /^(\t|(\ \ ))*\ \*\ ([^\*]|\*(?!\/))*$/,
beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
action: { indentAction: Modes.IndentAction.None, appendText: '* ' }
},
{
@ -591,6 +591,31 @@ suite('Editor Commands - ShiftCommand', () => {
);
});
test('issue #1609: Wrong indentation of block comments', () => {
testShiftCommandInDocBlockCommentMode(
[
'',
'/**',
' * test',
' *',
' * @type {number}',
' */',
'var foo = 0;'
],
new Selection(1,1,7,13),
[
'',
'\t/**',
'\t * test',
'\t *',
'\t * @type {number}',
'\t */',
'\tvar foo = 0;'
],
new Selection(1,1,7,14)
);
});
test('bug #16815:Shift+Tab doesn\'t go back to tabstop', () => {
var repeatStr = (str:string, cnt:number): string => {

View file

@ -13,7 +13,7 @@ import {Cursor} from 'vs/editor/common/controller/cursor';
import EditorCommon = require('vs/editor/common/editorCommon');
import {IMode} from 'vs/editor/common/modes';
import {CommonEditorConfiguration, ICSSConfig} from 'vs/editor/common/config/commonEditorConfig';
import {MockConfiguration} from 'vs/editor/test/common/commands/commandTestUtils';
import {MockConfiguration} from 'vs/editor/test/common/mocks/mockConfiguration';
import {EditOperation} from 'vs/editor/common/core/editOperation';
import {ModelLine, ILineEdit} from 'vs/editor/common/model/modelLine';

View file

@ -5,34 +5,7 @@
'use strict';
import assert = require('assert');
import {CommonEditorConfiguration, ICSSConfig} from 'vs/editor/common/config/commonEditorConfig';
class MockConfiguration extends CommonEditorConfiguration {
constructor(opts:any) {
super(opts);
}
protected getOuterWidth(): number {
return 100;
}
protected getOuterHeight(): number {
return 100;
}
protected readConfiguration(editorClassName: string, fontFamily: string, fontSize: number, lineHeight: number): ICSSConfig {
// Doesn't really matter
return {
typicalHalfwidthCharacterWidth: 10,
typicalFullwidthCharacterWidth: 20,
maxDigitWidth: 10,
lineHeight: 20,
font: 'mockFont',
fontSize: 20
};
}
}
import {MockConfiguration} from 'vs/editor/test/common/mocks/mockConfiguration';
suite('Editor Config - CommonEditorConfig', () => {
test('Configuration.normalizeIndentation', () => {

View file

@ -12,7 +12,7 @@ import Range = require('vs/editor/common/core/range');
import ModelModes = require('vs/editor/test/common/testModes');
import Modes = require('vs/editor/common/modes');
import EditorCommon = require('vs/editor/common/editorCommon');
import {MockConfiguration} from 'vs/editor/test/common/commands/commandTestUtils';
import {MockConfiguration} from 'vs/editor/test/common/mocks/mockConfiguration';
import {EditOperation} from 'vs/editor/common/core/editOperation';
var H = EditorCommon.Handler;

View file

@ -0,0 +1,79 @@
/*---------------------------------------------------------------------------------------------
* 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 {CommonCodeEditor} from 'vs/editor/common/commonCodeEditor';
import {Cursor} from 'vs/editor/common/controller/cursor';
import * as EditorCommon from 'vs/editor/common/editorCommon';
import {EventEmitter, IEventEmitter} from 'vs/base/common/eventEmitter';
import {CommonEditorConfiguration, IIndentationGuesser} from 'vs/editor/common/config/commonEditorConfig';
import {MockConfiguration} from 'vs/editor/test/common/mocks/mockConfiguration';
import {MockKeybindingService} from 'vs/platform/keybinding/test/common/mockKeybindingService';
import {MockCodeEditorService} from 'vs/editor/test/common/mocks/mockCodeEditorService';
import {MockTelemetryService} from 'vs/platform/telemetry/test/common/mockTelemetryService';
import * as InstantiationService from 'vs/platform/instantiation/common/instantiationService';
import {IKeybindingScopeLocation} from 'vs/platform/keybinding/common/keybindingService';
import {Model} from 'vs/editor/common/model/model';
export class MockCodeEditor extends CommonCodeEditor {
protected _createConfiguration(options:EditorCommon.ICodeEditorWidgetCreationOptions, indentationGuesser:IIndentationGuesser): CommonEditorConfiguration {
return new MockConfiguration(options);
}
public getCenteredRangeInViewport(): EditorCommon.IEditorRange { return null; }
public setScrollTop(newScrollTop:number): void { }
public getScrollTop(): number { return 0; }
public setScrollLeft(newScrollLeft:number): void { }
public getScrollLeft(): number { return 0; }
public getScrollWidth(): number { return 0; }
public getScrollHeight(): number { return 0; }
public saveViewState(): EditorCommon.ICodeEditorViewState { return null; }
public restoreViewState(state:EditorCommon.IEditorViewState): void { }
public layout(dimension?:EditorCommon.IDimension): void { }
public focus(): void { }
public isFocused(): boolean { return true; }
protected _enableEmptySelectionClipboard(): boolean { return false; }
protected _createView(): void { }
protected _getViewInternalEventBus(): IEventEmitter { return new EventEmitter(); }
// --- test utils
getCursor(): Cursor {
return this.cursor;
}
public registerAndInstantiateContribution<T extends EditorCommon.IEditorContribution>(ctor:any): T {
let r = <T>this._instantiationService.createInstance(ctor, this);
this.contributions[r.getId()] = r;
return r;
}
}
export class MockScopeLocation implements IKeybindingScopeLocation {
setAttribute(attr:string, value:string): void { }
removeAttribute(attr:string): void { }
}
export function withMockCodeEditor(text:string[], options:EditorCommon.ICodeEditorWidgetCreationOptions, callback:(editor:MockCodeEditor, cursor:Cursor)=>void): void {
let codeEditorService = new MockCodeEditorService();
let keybindingService = new MockKeybindingService();
let telemetryService = new MockTelemetryService();
let instantiationService = InstantiationService.create({
codeEditorService: codeEditorService,
keybindingService: keybindingService,
telemetryService: telemetryService
});
let model = new Model(text.join('\n'), null);
let editor = new MockCodeEditor(new MockScopeLocation(), options, instantiationService, codeEditorService, keybindingService, telemetryService);
editor.setModel(model);
callback(editor, editor.getCursor());
editor.dispose();
model.dispose();
keybindingService.dispose();
telemetryService.dispose();
}

View file

@ -0,0 +1,14 @@
/*---------------------------------------------------------------------------------------------
* 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 {ICommonCodeEditor, IDecorationRenderOptions, IModelDecorationOptions} from 'vs/editor/common/editorCommon';
import {AbstractCodeEditorService} from 'vs/editor/common/services/abstractCodeEditorService';
export class MockCodeEditorService extends AbstractCodeEditorService {
public registerDecorationType(key:string, options: IDecorationRenderOptions): void { }
public removeDecorationType(key:string): void { }
public resolveDecorationType(key:string): IModelDecorationOptions { return null; }
}

View file

@ -0,0 +1,34 @@
/*---------------------------------------------------------------------------------------------
* 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 {CommonEditorConfiguration, ICSSConfig} from 'vs/editor/common/config/commonEditorConfig';
export class MockConfiguration extends CommonEditorConfiguration {
constructor(opts:any) {
super(opts);
}
protected getOuterWidth(): number {
return 100;
}
protected getOuterHeight(): number {
return 100;
}
protected readConfiguration(editorClassName: string, fontFamily: string, fontSize: number, lineHeight: number): ICSSConfig {
// Doesn't really matter
return {
typicalHalfwidthCharacterWidth: 10,
typicalFullwidthCharacterWidth: 20,
maxDigitWidth: 10,
lineHeight: 20,
font: 'mockFont',
fontSize: 20
};
}
}

View file

@ -87,7 +87,7 @@ suite('OnEnter', () => {
action: { indentAction: IndentAction.None, appendText: ' * ' }
},
{
beforeText: /^(\t|(\ \ ))*\ \*\ ([^\*]|\*(?!\/))*$/,
beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
action: { indentAction: IndentAction.None, appendText: '* ' }
},
{
@ -129,7 +129,7 @@ suite('OnEnter', () => {
testIndentAction('*/', '', null, null);
testIndentAction('\t/*', '', null, null);
testIndentAction('\t*', '', null, null);
testIndentAction('\t *', '', null, null);
testIndentAction('\t *', '', IndentAction.None, '* ');
testIndentAction('\t */', '', IndentAction.None, null, 1);
testIndentAction('\t * */', '', null, null);
testIndentAction('\t * * / * / * / */', '', null, null);

View file

@ -91,7 +91,7 @@ export class JSMode extends typescriptMode.TypeScriptMode<javascriptWorker.JavaS
},
{
// e.g. * ...|
beforeText: /^(\t|(\ \ ))*\ \*\ ([^\*]|\*(?!\/))*$/,
beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
action: { indentAction: Modes.IndentAction.None, appendText: '* ' }
},
{

View file

@ -253,7 +253,7 @@ export class TypeScriptMode<W extends typescriptWorker.TypeScriptWorker2> extend
},
{
// e.g. * ...|
beforeText: /^(\t|(\ \ ))*\ \*\ ([^\*]|\*(?!\/))*$/,
beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
action: { indentAction: Modes.IndentAction.None, appendText: '* ' }
},
{

View file

@ -33,15 +33,12 @@ export interface ITextEditorModel extends IEditorModel {
textEditorModel: any;
}
export interface IResourceInput extends ITextInput {
export interface IResourceInput {
/**
* The resource URL of the resource to open.
*/
resource: URI;
}
export interface ITextInput {
/**
* The mime type of the text input if known.

View file

@ -0,0 +1,69 @@
/*---------------------------------------------------------------------------------------------
* 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 {IKeybindingService, IKeybindingContextKey, IKeybindingItem} from 'vs/platform/keybinding/common/keybindingService';
import {Keybinding} from 'vs/base/common/keyCodes';
import {IHTMLContentElement} from 'vs/base/common/htmlContent';
import {TPromise} from 'vs/base/common/winjs.base';
class MockKeybindingContextKey<T> implements IKeybindingContextKey<T> {
private _key: string;
private _defaultValue: T;
private _value: T;
constructor(key: string, defaultValue: T) {
this._key = key;
this._defaultValue = defaultValue;
this._value = this._defaultValue;
}
public set(value: T): void {
this._value = value;
}
public reset(): void {
this._value = this._defaultValue;
}
}
export class MockKeybindingService implements IKeybindingService {
public serviceId = IKeybindingService;
public dispose(): void { }
public executeCommand(commandId: string, args: any): TPromise<any> { return; }
public createKey<T>(key: string, defaultValue: T): IKeybindingContextKey<T> {
return new MockKeybindingContextKey(key, defaultValue);
}
public getLabelFor(keybinding:Keybinding): string {
return keybinding._toUSLabel();
}
public getHTMLLabelFor(keybinding:Keybinding): IHTMLContentElement[] {
return keybinding._toUSHTMLLabel();
}
public getElectronAcceleratorFor(keybinding:Keybinding): string {
return keybinding._toElectronAccelerator();
}
public createScoped(domNode: HTMLElement): IKeybindingService {
return this;
}
public getDefaultKeybindings(): string {
return null;
}
public lookupKeybindings(commandId: string): Keybinding[] {
return [];
}
public customKeybindingsCount(): number {
return 0;
}
}

View file

@ -0,0 +1,11 @@
/*---------------------------------------------------------------------------------------------
* 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 {AbstractTelemetryService} from 'vs/platform/telemetry/common/abstractTelemetryService';
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';
export class MockTelemetryService extends AbstractTelemetryService implements ITelemetryService {
}

View file

@ -11,8 +11,8 @@ import network = require('vs/base/common/network');
import labels = require('vs/base/common/labels');
import {Registry} from 'vs/platform/platform';
import {Action} from 'vs/base/common/actions';
import {IWorkbenchActionRegistry, Extensions} from 'vs/workbench/browser/actionRegistry';
import {StringEditorInput} from 'vs/workbench/browser/parts/editor/stringEditorInput';
import {IWorkbenchActionRegistry, Extensions} from 'vs/workbench/common/actionRegistry';
import {StringEditorInput} from 'vs/workbench/common/editor/stringEditorInput';
import {getDefaultValuesContent} from 'vs/platform/configuration/common/model';
import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService';
import {IWorkspaceContextService} from 'vs/workbench/services/workspace/common/contextService';

View file

@ -9,7 +9,7 @@ import {TimeKeeperRenderer} from 'vs/base/browser/ui/timer/timer';
import {Registry} from 'vs/platform/platform';
import {Action} from 'vs/base/common/actions';
import {SyncActionDescriptor} from 'vs/platform/actions/common/actions';
import {IWorkbenchActionRegistry, Extensions} from 'vs/workbench/browser/actionRegistry';
import {IWorkbenchActionRegistry, Extensions} from 'vs/workbench/common/actionRegistry';
import {INullService} from 'vs/platform/instantiation/common/instantiation';
const ID = 'workbench.action.showPerfBox';

View file

@ -9,7 +9,7 @@ import nls = require('vs/nls');
import {Registry} from 'vs/platform/platform';
import {Action} from 'vs/base/common/actions';
import {SyncActionDescriptor} from 'vs/platform/actions/common/actions';
import {IWorkbenchActionRegistry, Extensions} from 'vs/workbench/browser/actionRegistry';
import {IWorkbenchActionRegistry, Extensions} from 'vs/workbench/common/actionRegistry';
import {IPartService, Position} from 'vs/workbench/services/part/common/partService';
const ID = 'workbench.action.toggleSidebarPosition';

View file

@ -9,7 +9,7 @@ import nls = require('vs/nls');
import {Registry} from 'vs/platform/platform';
import {Action} from 'vs/base/common/actions';
import {SyncActionDescriptor} from 'vs/platform/actions/common/actions';
import {IWorkbenchActionRegistry, Extensions} from 'vs/workbench/browser/actionRegistry';
import {IWorkbenchActionRegistry, Extensions} from 'vs/workbench/common/actionRegistry';
import {IPartService} from 'vs/workbench/services/part/common/partService';
import {KeyMod, KeyCode} from 'vs/base/common/keyCodes';

View file

@ -9,7 +9,7 @@ import nls = require('vs/nls');
import types = require('vs/base/common/types');
import {Action, IAction} from 'vs/base/common/actions';
import {SyncActionDescriptor} from 'vs/platform/actions/common/actions';
import {IWorkbenchActionRegistry, Extensions as ActionExtensions} from 'vs/workbench/browser/actionRegistry';
import {IWorkbenchActionRegistry, Extensions as ActionExtensions} from 'vs/workbench/common/actionRegistry';
import {Registry} from 'vs/platform/platform';
import {BaseEditor} from 'vs/workbench/browser/parts/editor/baseEditor';
import {EditorInput, getUntitledOrFileResource, TextEditorOptions} from 'vs/workbench/common/editor';

View file

@ -9,7 +9,7 @@ import nls = require('vs/nls');
import {Registry} from 'vs/platform/platform';
import {Action} from 'vs/base/common/actions';
import {SyncActionDescriptor} from 'vs/platform/actions/common/actions';
import {IWorkbenchActionRegistry, Extensions} from 'vs/workbench/browser/actionRegistry';
import {IWorkbenchActionRegistry, Extensions} from 'vs/workbench/common/actionRegistry';
import {KeyMod, KeyCode} from 'vs/base/common/keyCodes';
import {IHistoryService} from 'vs/workbench/services/history/common/history';

View file

@ -5,7 +5,7 @@
'use strict';
import nls = require('vs/nls');
import {IWorkbenchActionRegistry, Extensions} from 'vs/workbench/browser/actionRegistry';
import {IWorkbenchActionRegistry, Extensions} from 'vs/workbench/common/actionRegistry';
import {Registry} from 'vs/platform/platform';
import {Action} from 'vs/base/common/actions';
import {Promise} from 'vs/base/common/winjs.base';

View file

@ -7,7 +7,7 @@
import 'vs/css!./media/part';
import {Dimension, Builder} from 'vs/base/browser/builder';
import {WorkbenchComponent} from 'vs/workbench/browser/component';
import {WorkbenchComponent} from 'vs/workbench/common/component';
/**
* Parts are layed out in the workbench and have their own layout that arranges a title,

View file

@ -16,8 +16,8 @@ import {IScrollableElement} from 'vs/base/browser/ui/scrollbar/scrollableElement
import {ScrollableElement} from 'vs/base/browser/ui/scrollbar/impl/scrollableElement';
import {BaseEditor} from 'vs/workbench/browser/parts/editor/baseEditor';
import {EditorInput, EditorOptions} from 'vs/workbench/common/editor';
import {BinaryEditorModel} from 'vs/workbench/browser/parts/editor/binaryEditorModel';
import {DiffEditorModel} from 'vs/workbench/browser/parts/editor/diffEditorModel';
import {BinaryEditorModel} from 'vs/workbench/common/editor/binaryEditorModel';
import {DiffEditorModel} from 'vs/workbench/common/editor/diffEditorModel';
import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService';
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';

View file

@ -12,7 +12,7 @@ import {Dimension, Builder, $} from 'vs/base/browser/builder';
import {ResourceViewer} from 'vs/base/browser/ui/resourceviewer/resourceViewer';
import {EditorModel, EditorInput, EditorOptions} from 'vs/workbench/common/editor';
import {BaseEditor} from 'vs/workbench/browser/parts/editor/baseEditor';
import {BinaryEditorModel} from 'vs/workbench/browser/parts/editor/binaryEditorModel';
import {BinaryEditorModel} from 'vs/workbench/common/editor/binaryEditorModel';
import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService';
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';

View file

@ -8,17 +8,17 @@ import {Registry} from 'vs/platform/platform';
import nls = require('vs/nls');
import {StatusbarItemDescriptor, StatusbarAlignment, IStatusbarRegistry, Extensions as StatusExtensions} from 'vs/workbench/browser/parts/statusbar/statusbar';
import {EditorDescriptor, IEditorRegistry, Extensions as EditorExtensions} from 'vs/workbench/browser/parts/editor/baseEditor';
import {StringEditorInput} from 'vs/workbench/browser/parts/editor/stringEditorInput';
import {StringEditorInput} from 'vs/workbench/common/editor/stringEditorInput';
import {StringEditor} from 'vs/workbench/browser/parts/editor/stringEditor';
import {DiffEditorInput} from 'vs/workbench/browser/parts/editor/diffEditorInput';
import {UntitledEditorInput} from 'vs/workbench/browser/parts/editor/untitledEditorInput';
import {ResourceEditorInput} from 'vs/workbench/browser/parts/editor/resourceEditorInput';
import {DiffEditorInput} from 'vs/workbench/common/editor/diffEditorInput';
import {UntitledEditorInput} from 'vs/workbench/common/editor/untitledEditorInput';
import {ResourceEditorInput} from 'vs/workbench/common/editor/resourceEditorInput';
import {TextDiffEditor} from 'vs/workbench/browser/parts/editor/textDiffEditor';
import {BinaryResourceDiffEditor} from 'vs/workbench/browser/parts/editor/binaryDiffEditor';
import {IFrameEditor} from 'vs/workbench/browser/parts/editor/iframeEditor';
import {IFrameEditorInput} from 'vs/workbench/browser/parts/editor/iframeEditorInput';
import {IFrameEditorInput} from 'vs/workbench/common/editor/iframeEditorInput';
import {ChangeEncodingAction, ChangeEOLAction, ChangeModeAction, EditorStatus} from 'vs/workbench/browser/parts/editor/editorStatus';
import {IWorkbenchActionRegistry, Extensions as ActionExtensions} from 'vs/workbench/browser/actionRegistry';
import {IWorkbenchActionRegistry, Extensions as ActionExtensions} from 'vs/workbench/common/actionRegistry';
import {SyncActionDescriptor} from 'vs/platform/actions/common/actions';
import {SyncDescriptor} from 'vs/platform/instantiation/common/descriptors';
import {KeyMod, KeyCode} from 'vs/base/common/keyCodes';

View file

@ -19,7 +19,7 @@ import {IStatusbarItem} from 'vs/workbench/browser/parts/statusbar/statusbar';
import {Action} from 'vs/base/common/actions';
import {IEditorModesRegistry, Extensions} from 'vs/editor/common/modes/modesRegistry';
import {Registry} from 'vs/platform/platform';
import {UntitledEditorInput} from 'vs/workbench/browser/parts/editor/untitledEditorInput';
import {UntitledEditorInput} from 'vs/workbench/common/editor/untitledEditorInput';
import {IFileEditorInput, EncodingMode, IEncodingSupport, asFileEditorInput, getUntitledOrFileResource} from 'vs/workbench/common/editor';
import {IDisposable, combinedDispose} from 'vs/base/common/lifecycle';
import {ICodeEditor, IDiffEditor} from 'vs/editor/browser/editorBrowser';

View file

@ -14,8 +14,8 @@ import DOM = require('vs/base/browser/dom');
import errors = require('vs/base/common/errors');
import {EditorOptions, EditorInput} from 'vs/workbench/common/editor';
import {EditorInputAction, BaseEditor} from 'vs/workbench/browser/parts/editor/baseEditor';
import {IFrameEditorInput} from 'vs/workbench/browser/parts/editor/iframeEditorInput';
import {IFrameEditorModel} from 'vs/workbench/browser/parts/editor/iframeEditorModel';
import {IFrameEditorInput} from 'vs/workbench/common/editor/iframeEditorInput';
import {IFrameEditorModel} from 'vs/workbench/common/editor/iframeEditorModel';
import {IStorageService, StorageScope} from 'vs/platform/storage/common/storage';
import {Position} from 'vs/platform/editor/common/editor';
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';

View file

@ -5,7 +5,7 @@
'use strict';
import {ICodeEditor} from 'vs/editor/browser/editorBrowser';
import {StringEditorInput} from 'vs/workbench/browser/parts/editor/stringEditorInput';
import {StringEditorInput} from 'vs/workbench/common/editor/stringEditorInput';
import {BaseTextEditor} from 'vs/workbench/browser/parts/editor/textEditor';
import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService';
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';

View file

@ -21,7 +21,7 @@ import {Sash, ISashEvent, IVerticalSashLayoutProvider} from 'vs/base/browser/ui/
import {ProgressBar} from 'vs/base/browser/ui/progressbar/progressbar';
import {BaseEditor} from 'vs/workbench/browser/parts/editor/baseEditor';
import {EditorInput, IInputStatus, TextEditorOptions} from 'vs/workbench/common/editor';
import {DiffEditorInput} from 'vs/workbench/browser/parts/editor/diffEditorInput';
import {DiffEditorInput} from 'vs/workbench/common/editor/diffEditorInput';
import {EventType as BaseEventType} from 'vs/base/common/events';
import {EditorInputEvent, EventType as WorkbenchEventType} from 'vs/workbench/common/events';
import DOM = require('vs/base/browser/dom');

View file

@ -12,9 +12,9 @@ import {IEditorOptions, IEditorViewState} from 'vs/editor/common/editorCommon';
import {DefaultConfig} from 'vs/editor/common/config/defaultConfig';
import {EditorConfiguration} from 'vs/editor/common/config/commonEditorConfig';
import {TextEditorOptions, EditorModel, EditorInput, EditorOptions} from 'vs/workbench/common/editor';
import {BaseTextEditorModel} from 'vs/workbench/browser/parts/editor/textEditorModel';
import {BaseTextEditorModel} from 'vs/workbench/common/editor/textEditorModel';
import {LogEditorInput} from 'vs/workbench/browser/parts/editor/logEditorInput';
import {UntitledEditorInput} from 'vs/workbench/browser/parts/editor/untitledEditorInput';
import {UntitledEditorInput} from 'vs/workbench/common/editor/untitledEditorInput';
import {BaseTextEditor} from 'vs/workbench/browser/parts/editor/textEditor';
import {UntitledEditorEvent, EventType} from 'vs/workbench/common/events';
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';

View file

@ -17,12 +17,12 @@ import {IDiffEditorOptions, IEditorOptions} from 'vs/editor/common/editorCommon'
import {BaseEditor} from 'vs/workbench/browser/parts/editor/baseEditor';
import {BaseTextEditor} from 'vs/workbench/browser/parts/editor/textEditor';
import {TextEditorOptions, TextDiffEditorOptions, EditorModel, EditorInput, EditorOptions} from 'vs/workbench/common/editor';
import {StringEditorInput} from 'vs/workbench/browser/parts/editor/stringEditorInput';
import {ResourceEditorInput} from 'vs/workbench/browser/parts/editor/resourceEditorInput';
import {DiffEditorInput} from 'vs/workbench/browser/parts/editor/diffEditorInput';
import {StringEditorInput} from 'vs/workbench/common/editor/stringEditorInput';
import {ResourceEditorInput} from 'vs/workbench/common/editor/resourceEditorInput';
import {DiffEditorInput} from 'vs/workbench/common/editor/diffEditorInput';
import {DiffNavigator} from 'vs/editor/contrib/diffNavigator/common/diffNavigator';
import {DiffEditorWidget} from 'vs/editor/browser/widget/diffEditorWidget';
import {TextDiffEditorModel} from 'vs/workbench/browser/parts/editor/textDiffEditorModel';
import {TextDiffEditorModel} from 'vs/workbench/common/editor/textDiffEditorModel';
import {DelegatingWorkbenchEditorService} from 'vs/workbench/services/editor/browser/editorService';
import {IFileOperationResult, FileOperationResult} from 'vs/platform/files/common/files';
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';

View file

@ -19,7 +19,7 @@ import {QuickOpenWidget} from 'vs/base/parts/quickopen/browser/quickOpenWidget';
import {ContributableActionProvider} from 'vs/workbench/browser/actionBarRegistry';
import {ITree, IElementCallback} from 'vs/base/parts/tree/common/tree';
import {Registry} from 'vs/platform/platform';
import {WorkbenchComponent} from 'vs/workbench/browser/component';
import {WorkbenchComponent} from 'vs/workbench/common/component';
import {EditorEvent, EventType} from 'vs/workbench/common/events';
import Event, {Emitter} from 'vs/base/common/event';
import {Identifiers} from 'vs/workbench/common/constants';

View file

@ -25,7 +25,7 @@ import {Action, IAction} from 'vs/base/common/actions';
import {Part} from 'vs/workbench/browser/part';
import {EventType as WorkbenchEventType, ViewletEvent} from 'vs/workbench/common/events';
import {Viewlet, EventType as ViewletEventType, IViewletRegistry, Extensions as ViewletExtensions} from 'vs/workbench/browser/viewlet';
import {IWorkbenchActionRegistry, Extensions as ActionExtensions} from 'vs/workbench/browser/actionRegistry';
import {IWorkbenchActionRegistry, Extensions as ActionExtensions} from 'vs/workbench/common/actionRegistry';
import {SyncActionDescriptor} from 'vs/platform/actions/common/actions';
import {WorkbenchProgressService} from 'vs/workbench/services/progress/browser/progressService';
import {IViewletService} from 'vs/workbench/services/viewlet/common/viewletService';

View file

@ -18,7 +18,7 @@ import {IKeybindingService} from 'vs/platform/keybinding/common/keybindingServic
import {IAction} from 'vs/base/common/actions';
import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService';
import {Part} from 'vs/workbench/browser/part';
import {IWorkbenchActionRegistry, Extensions as ActionExtensions} from 'vs/workbench/browser/actionRegistry';
import {IWorkbenchActionRegistry, Extensions as ActionExtensions} from 'vs/workbench/common/actionRegistry';
import {StatusbarAlignment, IStatusbarRegistry, Extensions, IStatusbarItem} from 'vs/workbench/browser/parts/statusbar/statusbar';
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';

View file

@ -15,7 +15,8 @@ import {Registry} from 'vs/platform/platform';
import {Mode, IContext, IAutoFocus, IModel} from 'vs/base/parts/quickopen/common/quickOpen';
import {QuickOpenEntry, IHighlight, QuickOpenEntryGroup, QuickOpenModel} from 'vs/base/parts/quickopen/browser/quickOpenModel';
import {EditorOptions, EditorInput} from 'vs/workbench/common/editor';
import {IFileInput, IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService';
import {IResourceInput} from 'vs/platform/editor/common/editor';
import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService';
import {IQuickOpenService} from 'vs/workbench/services/quickopen/common/quickOpenService';
import {AsyncDescriptor} from 'vs/platform/instantiation/common/descriptors';
@ -194,7 +195,7 @@ export interface IEditorQuickOpenEntry {
/**
* The editor input used for this entry when opening.
*/
getInput(): IFileInput | EditorInput;
getInput(): IResourceInput | EditorInput;
/**
* The editor options used for this entry when opening.
@ -215,7 +216,7 @@ export class EditorQuickOpenEntry extends QuickOpenEntry implements IEditorQuick
return this._editorService;
}
public getInput(): IFileInput | EditorInput {
public getInput(): IResourceInput | EditorInput {
return null;
}
@ -232,7 +233,7 @@ export class EditorQuickOpenEntry extends QuickOpenEntry implements IEditorQuick
if (input instanceof EditorInput) {
this.editorService.openEditor(input, this.getOptions(), sideBySide).done(null, errors.onUnexpectedError);
} else {
this.editorService.openEditor(<IFileInput>input, sideBySide).done(null, errors.onUnexpectedError);
this.editorService.openEditor(<IResourceInput>input, sideBySide).done(null, errors.onUnexpectedError);
}
return true;

View file

@ -14,7 +14,7 @@ import {Dimension, Builder, $} from 'vs/base/browser/builder';
import {IAction, IActionRunner, Action, ActionRunner} from 'vs/base/common/actions';
import {IActionItem, ActionsOrientation} from 'vs/base/browser/ui/actionbar/actionbar';
import {ITree, IFocusEvent, ISelectionEvent} from 'vs/base/parts/tree/common/tree';
import {WorkbenchComponent} from 'vs/workbench/browser/component';
import {WorkbenchComponent} from 'vs/workbench/common/component';
import {ViewletEvent} from 'vs/workbench/common/events';
import {prepareActions} from 'vs/workbench/browser/actionBarRegistry';
import {ToolBar} from 'vs/base/browser/ui/toolbar/toolbar';

View file

@ -36,8 +36,7 @@ import {IViewletRegistry, Extensions as ViewletExtensions} from 'vs/workbench/br
import {QuickOpenController} from 'vs/workbench/browser/parts/quickopen/quickOpenController';
import {getServices} from 'vs/platform/instantiation/common/extensions';
import {AbstractKeybindingService} from 'vs/platform/keybinding/browser/keybindingServiceImpl';
import {UntitledEditorService} from 'vs/workbench/services/untitled/browser/untitledEditorService';
import {IUntitledEditorService} from 'vs/workbench/services/untitled/common/untitledEditorService';
import {IUntitledEditorService, UntitledEditorService} from 'vs/workbench/services/untitled/common/untitledEditorService';
import {WorkbenchEditorService} from 'vs/workbench/services/editor/browser/editorService';
import {Position, Parts, IPartService} from 'vs/workbench/services/part/common/partService';
import {DEFAULT_THEME_ID} from 'vs/platform/theme/common/themes';

View file

@ -29,7 +29,7 @@ exports.collectModules= function(excludes) {
createModuleDescription('vs/workbench/parts/git/electron-browser/gitApp', []),
createModuleDescription('vs/workbench/parts/git/electron-main/askpass', []),
createModuleDescription('vs/workbench/parts/output/browser/outputMode', languageMainExcludes),
createModuleDescription('vs/workbench/parts/output/common/outputMode', languageMainExcludes),
createModuleDescription('vs/workbench/parts/output/common/outputWorker', languageWorkerExcludes),
createModuleDescription('vs/workbench/parts/debug/browser/debugViewlet', excludes),

View file

@ -10,7 +10,7 @@ import types = require('vs/base/common/types');
import URI from 'vs/base/common/uri';
import objects = require('vs/base/common/objects');
import {IEditor, IEditorViewState, IRange} from 'vs/editor/common/editorCommon';
import {IEditorInput, IEditorModel, IEditorOptions, ITextInput} from 'vs/platform/editor/common/editor';
import {IEditorInput, IEditorModel, IEditorOptions, IResourceInput} from 'vs/platform/editor/common/editor';
/**
* A simple bag for input related status that can be shown in the UI
@ -308,23 +308,23 @@ export class TextEditorOptions extends EditorOptions {
private endColumn: number;
private editorViewState: IEditorViewState;
public static from(textInput: ITextInput): TextEditorOptions {
public static from(input: IResourceInput): TextEditorOptions {
let options: TextEditorOptions = null;
if (textInput && textInput.options) {
if (textInput.options.selection || textInput.options.forceOpen || textInput.options.preserveFocus) {
if (input && input.options) {
if (input.options.selection || input.options.forceOpen || input.options.preserveFocus) {
options = new TextEditorOptions();
}
if (textInput.options.selection) {
let selection = textInput.options.selection;
if (input.options.selection) {
let selection = input.options.selection;
options.selection(selection.startLineNumber, selection.startColumn, selection.endLineNumber, selection.endColumn);
}
if (textInput.options.forceOpen) {
if (input.options.forceOpen) {
options.forceOpen = true;
}
if (textInput.options.preserveFocus) {
if (input.options.preserveFocus) {
options.preserveFocus = true;
}
}

View file

@ -9,9 +9,9 @@ import types = require('vs/base/common/types');
import {isBinaryMime} from 'vs/base/common/mime';
import {EventType} from 'vs/base/common/events';
import {EditorModel, IFileEditorInput, EditorInput, IInputStatus, BaseDiffEditorInput} from 'vs/workbench/common/editor';
import {BaseTextEditorModel} from 'vs/workbench/browser/parts/editor/textEditorModel';
import {DiffEditorModel} from 'vs/workbench/browser/parts/editor/diffEditorModel';
import {TextDiffEditorModel} from 'vs/workbench/browser/parts/editor/textDiffEditorModel';
import {BaseTextEditorModel} from 'vs/workbench/common/editor/textEditorModel';
import {DiffEditorModel} from 'vs/workbench/common/editor/diffEditorModel';
import {TextDiffEditorModel} from 'vs/workbench/common/editor/textDiffEditorModel';
/**
* The base editor input for the diff editor. It is made up of two editor inputs, the original version

View file

@ -7,7 +7,6 @@
import {TPromise} from 'vs/base/common/winjs.base';
import {EditorModel, EditorInput} from 'vs/workbench/common/editor';
import {Registry} from 'vs/platform/platform';
import {IEditorRegistry, Extensions} from 'vs/workbench/browser/parts/editor/baseEditor';
/**
* An editor input to use with the IFrameEditor. The resolved IFrameEditorModel can either provide
@ -57,11 +56,6 @@ export abstract class IFrameEditorInput extends EditorInput {
// Create Model and Load
else {
let descriptor = (<IEditorRegistry>Registry.as(Extensions.Editors)).getEditor(this);
if (!descriptor) {
throw new Error('Unable to find an editor in the registry for this input.');
}
let model = this.createModel();
modelPromise = model.load();
}

View file

@ -6,7 +6,7 @@
import {TPromise} from 'vs/base/common/winjs.base';
import {EditorModel, EditorInput} from 'vs/workbench/common/editor';
import {ResourceEditorModel} from 'vs/workbench/browser/parts/editor/resourceEditorModel';
import {ResourceEditorModel} from 'vs/workbench/common/editor/resourceEditorModel';
import URI from 'vs/base/common/uri';
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
import {IModelService} from 'vs/editor/common/services/modelService';

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import {BaseTextEditorModel} from 'vs/workbench/browser/parts/editor/textEditorModel';
import {BaseTextEditorModel} from 'vs/workbench/common/editor/textEditorModel';
import URI from 'vs/base/common/uri';
import {IModeService} from 'vs/editor/common/services/modeService';
import {IModelService} from 'vs/editor/common/services/modelService';

View file

@ -7,7 +7,7 @@
import {TPromise} from 'vs/base/common/winjs.base';
import {MIME_TEXT} from 'vs/base/common/mime';
import {EditorModel, EditorInput} from 'vs/workbench/common/editor';
import {StringEditorModel} from 'vs/workbench/browser/parts/editor/stringEditorModel';
import {StringEditorModel} from 'vs/workbench/common/editor/stringEditorModel';
import URI from 'vs/base/common/uri';
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';

View file

@ -5,7 +5,7 @@
'use strict';
import {TPromise} from 'vs/base/common/winjs.base';
import {BaseTextEditorModel} from 'vs/workbench/browser/parts/editor/textEditorModel';
import {BaseTextEditorModel} from 'vs/workbench/common/editor/textEditorModel';
import {EditorModel} from 'vs/workbench/common/editor';
import URI from 'vs/base/common/uri';
import {Position} from 'vs/editor/common/core/position';

View file

@ -7,8 +7,8 @@
import {TPromise} from 'vs/base/common/winjs.base';
import {IDiffEditorModel} from 'vs/editor/common/editorCommon';
import {EditorModel} from 'vs/workbench/common/editor';
import {BaseTextEditorModel} from 'vs/workbench/browser/parts/editor/textEditorModel';
import {DiffEditorModel} from 'vs/workbench/browser/parts/editor/diffEditorModel';
import {BaseTextEditorModel} from 'vs/workbench/common/editor/textEditorModel';
import {DiffEditorModel} from 'vs/workbench/common/editor/diffEditorModel';
/**
* The base text editor model for the diff editor. It is made up of two text editor models, the original version

View file

@ -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();
}

View file

@ -12,7 +12,7 @@ import labels = require('vs/base/common/labels');
import paths = require('vs/base/common/paths');
import {UntitledEditorInput as AbstractUntitledEditorInput, EditorModel, EncodingMode, IInputStatus} from 'vs/workbench/common/editor';
import {Registry} from 'vs/platform/platform';
import {UntitledEditorModel} from 'vs/workbench/browser/parts/editor/untitledEditorModel';
import {UntitledEditorModel} from 'vs/workbench/common/editor/untitledEditorModel';
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
import {ILifecycleService} from 'vs/platform/lifecycle/common/lifecycle';
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';

View file

@ -6,7 +6,7 @@
import {TPromise} from 'vs/base/common/winjs.base';
import {EditorModel, IEncodingSupport} from 'vs/workbench/common/editor';
import {StringEditorModel} from 'vs/workbench/browser/parts/editor/stringEditorModel';
import {StringEditorModel} from 'vs/workbench/common/editor/stringEditorModel';
import URI from 'vs/base/common/uri';
import {IModelContentChangedEvent, EventType, EndOfLinePreference} from 'vs/editor/common/editorCommon';
import {EventType as WorkbenchEventType, UntitledEditorEvent, ResourceEvent} from 'vs/workbench/common/events';

Some files were not shown because too many files have changed in this diff Show more